getProjectUsers(boolean isForce) {
BacklogClient backlogClient = repository.createBacklogClient();
if (backlogClient == null) {
return Collections.emptyList();
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/Installer.java b/src/main/java/com/junichi11/netbeans/modules/backlog/Installer.java
deleted file mode 100644
index abc90dd..0000000
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/Installer.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
- *
- * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
- * Other names may be trademarks of their respective owners.
- *
- * The contents of this file are subject to the terms of either the GNU
- * General Public License Version 2 only ("GPL") or the Common
- * Development and Distribution License("CDDL") (collectively, the
- * "License"). You may not use this file except in compliance with the
- * License. You can obtain a copy of the License at
- * http://www.netbeans.org/cddl-gplv2.html
- * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
- * specific language governing permissions and limitations under the
- * License. When distributing the software, include this License Header
- * Notice in each file and include the License file at
- * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the GPL Version 2 section of the License file that
- * accompanied this code. If applicable, add the following below the
- * License Header, with the fields enclosed by brackets [] replaced by
- * your own identifying information:
- * "Portions Copyrighted [year] [name of copyright owner]"
- *
- * If you wish your version of this file to be governed by only the CDDL
- * or only the GPL Version 2, indicate your decision by adding
- * "[Contributor] elects to include this software in this distribution
- * under the [CDDL or GPL Version 2] license." If you do not indicate a
- * single choice of license, a recipient has the option to distribute
- * your version of this file under either the CDDL, the GPL Version 2 or
- * to extend the choice of license to its licensees as provided above.
- * However, if you add GPL Version 2 code and therefore, elected the GPL
- * Version 2 license, then the option applies only if the new code is
- * made subject to such option by the copyright holder.
- *
- * Contributor(s):
- *
- * Portions Copyrighted 2014 Sun Microsystems, Inc.
- */
-package com.junichi11.netbeans.modules.backlog;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import org.openide.modules.Dependency;
-import org.openide.modules.ModuleInfo;
-import org.openide.modules.ModuleInstall;
-import org.openide.modules.Modules;
-
-/**
- * Fixes up module system dependencies.
- *
- * First add dependencies on whatever API modules you want to use, even if you
- * are not (yet) their “friend”. Setting
- * {@code warn } allows you to still compile
- * against them and declare a regular specification version dependency, but be
- * careful since this also disables checks about transitive dependency usage.
- *
- * You can also add dependencies on modules with no exported packages; declare a
- * specification version dependency and use the same flag to suppress checks.
- * The same applies to modules exporting friend packages from which you
- * also/instead want to use implementation packages. In either case be very
- * careful and preferably catch {@link LinkageError} to be defensive.
- *
- * Then add a module installer and extend this class rather than
- * {@link ModuleInstall} directly. Override {@link #friends} and/or
- * {@link #siblings}. When {@link #validate} is called, module system errors
- * will be suppressed.
- *
- * Original source code : https://bitbucket.org/jglick/yenta
- */
-public class Installer extends ModuleInstall {
-
- private static final long serialVersionUID = 8470550717876369223L;
-
- /**
- * Specifies the modules with whom you would like to be friends. These
- * modules must be among your declared dependencies and they must export
- * friend packages. For each such module, if you are not already a friend,
- * you will be treated as one, so you will be able to access friend (but not
- * private) packages.
- *
- * @return a set of module code name bases (default implementation is empty)
- */
- protected Set friends() {
- Set set = new HashSet<>();
- set.add("org.netbeans.modules.bugtracking.commons"); // NOI18N
- set.add("org.netbeans.modules.team.commons"); // NOI18N
- return set;
- }
-
- /**
- * Specifies the modules from whom you need complete access. These modules
- * must be among your declared dependencies. For each such module, you will
- * be able to access all packages, as with an implementation dependency. Be
- * careful to defend against unexpected signature changes!
- *
- * @return a set of module code name bases (default implementation is empty)
- */
- protected Set siblings() {
- return Collections.emptySet();
- }
-
- /**
- * @inheritDoc
- * @throws IllegalStateException if {@link #friends} and {@link #siblings} are misconfigured or if the module system cannot be manipulated
- */
- @Override
- public void validate() throws IllegalStateException {
- Set friends = friends();
- Set siblings = siblings();
- if (friends.isEmpty() && siblings.isEmpty()) {
- throw new IllegalStateException("Must specify some friends and/or siblings");
- }
- ModuleInfo me = Modules.getDefault().ownerOf(getClass());
- if (me == null) {
- throw new IllegalStateException("No apparent module owning " + getClass());
- }
- try {
- Object manager = me.getClass().getMethod("getManager").invoke(me);
- for (String m : friends) {
- if (siblings.contains(m)) {
- throw new IllegalStateException("Cannot specify the same module " + m + " in both friends and siblings");
- }
- Object data = data(findDependency(manager, m));
- Field friendNamesF = Class.forName("org.netbeans.ModuleData", true, data.getClass().getClassLoader()).getDeclaredField("friendNames");
- friendNamesF.setAccessible(true);
- Set> names = (Set>) friendNamesF.get(data);
- Set newNames = new HashSet<>(names);
- newNames.add(me.getCodeNameBase());
- friendNamesF.set(data, newNames);
- }
- for (String m : siblings) {
- ModuleInfo dep = findDependency(manager, m);
- String implVersion = dep.getImplementationVersion();
- if (implVersion == null) {
- throw new IllegalStateException("No implementation version found in " + m);
- }
- Object data = data(me);
- Field dependenciesF = Class.forName("org.netbeans.ModuleData", true, data.getClass().getClassLoader()).getDeclaredField("dependencies");
- dependenciesF.setAccessible(true);
- Dependency[] dependencies = (Dependency[]) dependenciesF.get(data);
- boolean found = false;
- for (int i = 0; i < dependencies.length; i++) {
- if (dependencies[i].getName().replaceFirst("/.+$", "").equals(m)) {
- Set nue = Dependency.create(Dependency.TYPE_MODULE, dependencies[i].getName() + " = " + implVersion);
- if (nue.size() != 1) {
- throw new IllegalStateException("Could not recreate dependency from " + dependencies[i] + " based on " + implVersion);
- }
- dependencies[i] = nue.iterator().next();
- found = true;
- }
- }
- if (!found) {
- throw new IllegalStateException("Did not find dependency on " + m);
- }
- // StandardModule.classLoaderUp skips adding a parent if the dep seemed to offer us nothing, and this has already been called.
- Object[] publicPackages = (Object[]) dep.getClass().getMethod("getPublicPackages").invoke(dep);
- if (publicPackages != null && publicPackages.length == 0) {
- me.getClassLoader().getClass().getMethod("append", ClassLoader[].class).invoke(me.getClassLoader(), (Object) new ClassLoader[]{dep.getClassLoader()});
- }
- }
- } catch (IllegalStateException x) {
- throw x;
- } catch (Exception x) {
- throw new IllegalStateException(x);
- }
- }
-
- private ModuleInfo findDependency(/*ModuleManager*/Object manager, String m) throws Exception {
- Object dep = manager.getClass().getMethod("get", String.class).invoke(manager, m);
- if (dep == null) {
- throw new IllegalStateException("No such dependency " + m);
- }
- return (ModuleInfo) dep;
- }
-
- private Object data(ModuleInfo module) throws Exception {
- Method dataM = Class.forName("org.netbeans.Module", true, module.getClass().getClassLoader()).getDeclaredMethod("data");
- dataM.setAccessible(true);
- return dataM.invoke(module);
- }
-
-}
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/BacklogIssue.java b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/BacklogIssue.java
index 078fd86..008d708 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/BacklogIssue.java
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/BacklogIssue.java
@@ -41,6 +41,7 @@
*/
package com.junichi11.netbeans.modules.backlog.issue;
+import com.junichi11.netbeans.modules.backlog.BacklogConfig;
import com.junichi11.netbeans.modules.backlog.BacklogConnector;
import com.junichi11.netbeans.modules.backlog.repository.BacklogRepository;
import static com.junichi11.netbeans.modules.backlog.utils.BacklogUtils.DEFAULT_DATE_FORMAT;
@@ -55,6 +56,8 @@
import com.nulabinc.backlog4j.Resolution;
import com.nulabinc.backlog4j.ResponseList;
import com.nulabinc.backlog4j.User;
+import com.nulabinc.backlog4j.api.option.AddIssueCommentNotificationParams;
+import com.nulabinc.backlog4j.api.option.AddIssueCommentParams;
import com.nulabinc.backlog4j.api.option.CreateIssueParams;
import com.nulabinc.backlog4j.api.option.UpdateIssueCommentParams;
import com.nulabinc.backlog4j.api.option.UpdateIssueParams;
@@ -74,6 +77,7 @@
import java.util.logging.Logger;
import javax.swing.JTable;
import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.modules.bugtracking.api.Repository;
import org.netbeans.modules.bugtracking.api.RepositoryManager;
import org.netbeans.modules.bugtracking.api.Util;
@@ -100,6 +104,7 @@ public final class BacklogIssue {
private IssueNode node;
private String subtaskParentIssueKey;
private IssueScheduleInfo scheduleInfo;
+ private final List comments = Collections.synchronizedList(new ArrayList());
private final BacklogRepository repository;
private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
@@ -120,6 +125,7 @@ public final class BacklogIssue {
public static final String PROP_COMMENT_DELETED = "backlog.comment.deleted"; // NOI18N
public static final String PROP_COMMENT_QUOTE = "backlog.comment.quote"; // NOI18N
public static final String PROP_COMMENT_EDITED = "backlog.comment.edited"; // NOI18N
+ public static final String PROP_COMMENT_NOTIFY = "backlog.comment.notify"; // NOI18N
private static final Logger LOGGER = Logger.getLogger(BacklogIssue.class.getName());
public BacklogIssue(BacklogRepository repository) {
@@ -425,10 +431,15 @@ public void refresh() {
return;
}
try {
- issue = backlogClient.getIssue(id);
+ setIssue(backlogClient.getIssue(id));
} catch (BacklogAPIException ex) {
LOGGER.log(Level.WARNING, ex.getMessage());
}
+ fireStatusChange();
+ }
+
+ public void refreshIssue(Issue issue) {
+ setIssue(issue);
}
/**
@@ -437,8 +448,53 @@ public void refresh() {
* @return status
*/
public Status getStatus() {
- // TODO
- return Status.SEEN;
+ return BacklogConfig.getInstance().getStatus(this);
+ }
+
+ public void setStatus(Status status) {
+ BacklogConfig.getInstance().setStatus(this, status);
+ fireStatusChange();
+ }
+
+ public List getIssueComments() {
+ if (issue == null) {
+ return Collections.emptyList();
+ }
+ return comments;
+ }
+
+ private void refreshIssueComments() {
+ if (issue == null) {
+ return;
+ }
+ comments.clear();
+ BacklogClient backlogClient = repository.createBacklogClient();
+ if (backlogClient != null) {
+ try {
+ ResponseList issueComments = backlogClient.getIssueComments(issue.getId());
+ comments.addAll(issueComments);
+ } catch (BacklogAPIException ex) {
+ LOGGER.log(Level.WARNING, ex.getMessage());
+ }
+ }
+ }
+
+ public long getLastUpdatedTime() {
+ Date updated = this.getUpdated();
+ if (updated != null) {
+ long time = updated.getTime();
+ for (IssueComment issueComment : getIssueComments()) {
+ Date commentUpdated = issueComment.getUpdated();
+ if (commentUpdated != null) {
+ long commentTime = commentUpdated.getTime();
+ if (time < commentTime) {
+ time = commentTime;
+ }
+ }
+ }
+ return time;
+ }
+ return -1L;
}
/**
@@ -522,7 +578,7 @@ public Issue addIssue(CreateIssueParams issueParams) {
fireChange();
fireDataChange();
fireScheduleChange();
-// fireStatusChange();
+ fireStatusChange();
((BacklogIssueController) getController()).setChanged(false);
}
subtaskParentIssueKey = null;
@@ -618,12 +674,59 @@ public Attachment deleteIssueAttachment(long attachmentId) {
return attachment;
}
+ /**
+ * Add IssueComment.
+ *
+ * @param content the added issue comment
+ * @param userIds user identifers
+ * @return IssueComment if adding is successful, otherwise {@code null}
+ */
+ @CheckForNull
+ public IssueComment addIssueComment(String content, List userIds) {
+ BacklogClient backlogClient = repository.createBacklogClient();
+ if (backlogClient == null) {
+ return null;
+ }
+ IssueComment issueComment = null;
+ try {
+ AddIssueCommentParams addIssueCommentParams = new AddIssueCommentParams(issue.getId(), content);
+ addIssueCommentParams.notifiedUserIds(userIds);
+ issueComment = backlogClient.addIssueComment(addIssueCommentParams);
+ } catch (BacklogAPIException ex) {
+ LOGGER.log(Level.WARNING, ex.getMessage());
+ }
+ return issueComment;
+ }
+
+ /**
+ * Add an issue comment notification.
+ *
+ * @param comment the issue comment
+ * @param userIds user identifers
+ * @return IssueComment if adding is successful, otherwise {@code null}
+ */
+ @CheckForNull
+ public IssueComment addIssueCommentNotification(@NonNull IssueComment comment, List userIds) {
+ BacklogClient backlogClient = repository.createBacklogClient();
+ if (backlogClient == null) {
+ return null;
+ }
+ IssueComment issueComment = null;
+ try {
+ AddIssueCommentNotificationParams params = new AddIssueCommentNotificationParams(issue.getId(), comment.getId(), userIds);
+ issueComment = backlogClient.addIssueCommentNotification(params);
+ } catch (BacklogAPIException ex) {
+ LOGGER.log(Level.WARNING, ex.getMessage());
+ }
+ return issueComment;
+ }
+
/**
* Update IssueComment.
*
* @param comment IssueComment
* @param content new content
- * @return Updated IssueComment if update is successful, otherwise
+ * @return Updated IssueComment if updating is successful, otherwise
* {@code null}
*/
@CheckForNull
@@ -659,6 +762,9 @@ public List getSubissueIds() {
private void setIssue(Issue issue) {
this.issue = issue;
this.summary = issue.getSummary();
+ // XXX many requests may be posted for getting comments
+ // Use notification?
+// refreshIssueComments();
}
/**
@@ -767,10 +873,8 @@ private IssueScheduleInfo createScheduleInfo() {
int interval = (int) ((due - start) / (1000 * 60 * 60 * 24));
return new IssueScheduleInfo(startDate, interval);
}
- } else {
- if (dueDate != null) {
- return new IssueScheduleInfo(dueDate, 1);
- }
+ } else if (dueDate != null) {
+ return new IssueScheduleInfo(dueDate, 1);
}
return null;
}
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/BacklogIssueStatusProvider.java b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/BacklogIssueStatusProvider.java
index b828674..3518094 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/BacklogIssueStatusProvider.java
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/BacklogIssueStatusProvider.java
@@ -59,7 +59,15 @@ public Status getStatus(BacklogIssue issue) {
}
@Override
- public void setSeenIncoming(BacklogIssue i, boolean bln) {
+ public void setSeenIncoming(BacklogIssue issue, boolean seen) {
+ Status status = getStatus(issue);
+ if (!seen) {
+ issue.setStatus(Status.INCOMING_NEW);
+ return;
+ }
+ if (status != Status.SEEN && seen) {
+ issue.setStatus(Status.SEEN);
+ }
}
@Override
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/BacklogIssuePanel.form b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/BacklogIssuePanel.form
index 34db3e0..4b8bd31 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/BacklogIssuePanel.form
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/BacklogIssuePanel.form
@@ -93,7 +93,11 @@
-
+
+
+
+
+
@@ -112,11 +116,11 @@
-
+
-
+
@@ -130,16 +134,18 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+
@@ -149,6 +155,8 @@
+
+
@@ -212,6 +220,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -299,39 +321,25 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
@@ -346,7 +354,7 @@
-
+
@@ -387,7 +395,7 @@
-
+
@@ -397,23 +405,22 @@
-
+
+
+
+
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
@@ -429,7 +436,14 @@
-
+
+
+
+
+
+
+
+
@@ -506,11 +520,17 @@
-
-
+
+
+
+
+
+
+
+
+
+
-
-
@@ -898,6 +918,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/BacklogIssuePanel.java b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/BacklogIssuePanel.java
index 026653e..5421718 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/BacklogIssuePanel.java
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/BacklogIssuePanel.java
@@ -41,6 +41,7 @@
*/
package com.junichi11.netbeans.modules.backlog.issue.ui;
+import com.junichi11.netbeans.modules.backlog.BacklogConfig;
import com.junichi11.netbeans.modules.backlog.BacklogData;
import com.junichi11.netbeans.modules.backlog.issue.BacklogAttachment;
import com.junichi11.netbeans.modules.backlog.issue.BacklogIssue;
@@ -53,6 +54,7 @@
import static com.junichi11.netbeans.modules.backlog.utils.BacklogUtils.DEFAULT_DATE_FORMAT;
import static com.junichi11.netbeans.modules.backlog.utils.BacklogUtils.DEFAULT_DATE_FORMAT_WITH_TIME;
import com.junichi11.netbeans.modules.backlog.utils.StringUtils;
+import com.junichi11.netbeans.modules.backlog.utils.UiUtils;
import com.nulabinc.backlog4j.Attachment;
import com.nulabinc.backlog4j.BacklogAPIException;
import com.nulabinc.backlog4j.BacklogClient;
@@ -62,6 +64,7 @@
import com.nulabinc.backlog4j.IssueComment;
import com.nulabinc.backlog4j.IssueType;
import com.nulabinc.backlog4j.Milestone;
+import com.nulabinc.backlog4j.Notification;
import com.nulabinc.backlog4j.Priority;
import com.nulabinc.backlog4j.Project;
import com.nulabinc.backlog4j.Resolution;
@@ -79,7 +82,10 @@
import com.nulabinc.backlog4j.internal.json.UserJSONImpl;
import com.nulabinc.backlog4j.internal.json.VersionJSONImpl;
import java.awt.Component;
+import java.awt.EventQueue;
import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
@@ -88,6 +94,8 @@
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
@@ -106,11 +114,14 @@
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.netbeans.modules.bugtracking.issuetable.IssueTable;
import org.netbeans.modules.bugtracking.issuetable.QueryTableCellRenderer;
+import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.awt.HtmlBrowser;
@@ -155,12 +166,20 @@ public class BacklogIssuePanel extends javax.swing.JPanel implements PropertyCha
private final DefaultListModel categoryListModel = new DefaultListModel<>();
private final DefaultListModel versionListModel = new DefaultListModel<>();
private final DefaultListModel milestoneListModel = new DefaultListModel<>();
+ private final DefaultListModel notificationUserListModel = new DefaultListModel<>();
private final ChangeSupport changeSupport = new ChangeSupport(this);
// icon
private static final Icon ERROR_ICON = BacklogImage.ERROR_16.getIcon();
private static final Icon ICON = BacklogImage.ICON_32.getIcon();
+ // manage templates options
+ private static final String TEMPLATES_ADD_OPTION = Bundle.BacklogIssuePanel_manage_templates_add_option();
+ private static final String TEMPLATES_EDIT_OPTION = Bundle.BacklogIssuePanel_manage_templates_edit_option();
+ private static final String TEMPLATES_DUPLICATE_OPTION = Bundle.BacklogIssuePanel_manage_templates_duplicate_option();
+ private static final String TEMPLATES_REMOVE_OPTION = Bundle.BacklogIssuePanel_manage_templates_remove_option();
+ private static final String TEMPLATES_CLOSE_OPTION = Bundle.BacklogIssuePanel_manage_templates_close_option();
+
private static final int MAX_COMMENT_COUNT = 100;
/**
@@ -214,6 +233,7 @@ private void init() {
versionList.setCellRenderer(new AttributesListCellRenderer(versionList.getCellRenderer()));
categoryList.setCellRenderer(new AttributesListCellRenderer(categoryList.getCellRenderer()));
milestoneList.setCellRenderer(new AttributesListCellRenderer(milestoneList.getCellRenderer()));
+ notificationUserList.setCellRenderer(new AttributesListCellRenderer(notificationUserList.getCellRenderer(), repositoryId));
resolutionComboBox.setRenderer(new AttributesListCellRenderer(resolutionComboBox.getRenderer()));
// attachments
@@ -338,6 +358,7 @@ private void setHeader() {
Issue existingIssue = issue.getIssue();
setHeaderIssueKey(existingIssue.getIssueKey() + " " + existingIssue.getSummary());
setDateLabel(headerCreatedDateLabel, existingIssue.getCreated(), true);
+ setDateLabel(headerUpdatedDateLabel, existingIssue.getUpdated(), true);
setDateLabel(headerStartDateViewLabel, existingIssue.getStartDate(), false);
setDateLabel(headerDueDateViewLabel, existingIssue.getDueDate(), false);
User createdUser = existingIssue.getCreatedUser();
@@ -665,13 +686,21 @@ private void setCategories(BacklogData data, boolean force) {
}
private void setUsers(BacklogData data) {
- List users = data.getUsers();
+ // asssignee, notification user
+ List users = data.getProjectUsers();
assigneeComboBoxModel.removeAllElements();
assigneeComboBoxModel.addElement(new UserJSONImpl());
+ notificationUserListModel.removeAllElements();
+ notificationUserListModel.addElement(new UserJSONImpl());
+ User myself = data.getMyself();
for (User user : users) {
assigneeComboBoxModel.addElement(user);
+ if (!user.equals(myself)) {
+ notificationUserListModel.addElement(user);
+ }
}
assigneeComboBox.setModel(assigneeComboBoxModel);
+ notificationUserList.setModel(notificationUserListModel);
}
private void setVersions(BacklogData data, boolean force) {
@@ -828,6 +857,24 @@ public String getComment() {
return commentTextArea.getText();
}
+ public List getNotificationUsers() {
+ return notificationUserList.getSelectedValuesList();
+ }
+
+ public List getNotificationUserIds() {
+ List users = getNotificationUsers();
+ if (users.size() == 1 && StringUtils.isEmpty(users.get(0).getName())) {
+ return Collections.emptyList();
+ }
+ List ids = new ArrayList<>();
+ for (User user : users) {
+ if (!StringUtils.isEmpty(user.getName())) {
+ ids.add(user.getId());
+ }
+ }
+ return ids;
+ }
+
void fireChange() {
changeSupport.fireChange();
}
@@ -866,6 +913,8 @@ private void initComponents() {
headerIssueKeyLabel = new javax.swing.JLabel();
headerCreatedLabel = new javax.swing.JLabel();
headerCreatedDateLabel = new javax.swing.JLabel();
+ headerUpdatedLabel = new javax.swing.JLabel();
+ headerUpdatedDateLabel = new javax.swing.JLabel();
headerStartDateLabel = new javax.swing.JLabel();
headerStartDateViewLabel = new javax.swing.JLabel();
headerDueDateLabel = new javax.swing.JLabel();
@@ -878,21 +927,21 @@ private void initComponents() {
jSeparator3 = new javax.swing.JSeparator();
mainScrollPane = new javax.swing.JScrollPane();
mainPanel = new javax.swing.JPanel();
- priorityComboBox = new javax.swing.JComboBox();
+ priorityComboBox = new javax.swing.JComboBox<>();
descriptionScrollPane = new javax.swing.JScrollPane();
descriptionEditorPane = new javax.swing.JEditorPane();
estimatedHoursLabel = new javax.swing.JLabel();
priorityLabel = new javax.swing.JLabel();
- resolutionComboBox = new javax.swing.JComboBox();
+ resolutionComboBox = new javax.swing.JComboBox<>();
jSeparator2 = new javax.swing.JSeparator();
versionLabel = new javax.swing.JLabel();
milestoneLabel = new javax.swing.JLabel();
statusLabel = new javax.swing.JLabel();
categoryScrollPane = new javax.swing.JScrollPane();
- categoryList = new javax.swing.JList();
+ categoryList = new javax.swing.JList<>();
resolutionLabel = new javax.swing.JLabel();
assigneeLabel = new javax.swing.JLabel();
- assigneeComboBox = new javax.swing.JComboBox();
+ assigneeComboBox = new javax.swing.JComboBox<>();
acturalHoursLabel = new javax.swing.JLabel();
estimatedHoursTextField = new javax.swing.JTextField();
actualHoursTextField = new javax.swing.JTextField();
@@ -905,11 +954,11 @@ private void initComponents() {
attributeLabel = new javax.swing.JLabel();
descriptionLabel = new javax.swing.JLabel();
versionScrollPane = new javax.swing.JScrollPane();
- versionList = new javax.swing.JList();
+ versionList = new javax.swing.JList<>();
categoryLabel = new javax.swing.JLabel();
milestoneScrollPane = new javax.swing.JScrollPane();
- milestoneList = new javax.swing.JList();
- issueTypeComboBox = new javax.swing.JComboBox();
+ milestoneList = new javax.swing.JList<>();
+ issueTypeComboBox = new javax.swing.JComboBox<>();
commentLabel = new javax.swing.JLabel();
commentScrollPane = new javax.swing.JScrollPane();
commentTextArea = new javax.swing.JTextArea();
@@ -920,11 +969,16 @@ private void initComponents() {
addVersionButton = new javax.swing.JButton();
addMilestoneButton = new javax.swing.JButton();
assignToMyselfLinkButton = new org.netbeans.modules.bugtracking.commons.LinkButton();
- statusComboBox = new javax.swing.JComboBox();
+ statusComboBox = new javax.swing.JComboBox<>();
hoursEstimatedLabel = new javax.swing.JLabel();
hoursActualLabel = new javax.swing.JLabel();
attachmentsCollapsibleSectionPanel = new org.netbeans.modules.bugtracking.commons.CollapsibleSectionPanel();
subtaskingCollapsibleSectionPanel = new org.netbeans.modules.bugtracking.commons.CollapsibleSectionPanel();
+ notificationLabel = new javax.swing.JLabel();
+ notificationUserScrollPane = new javax.swing.JScrollPane();
+ notificationUserList = new javax.swing.JList<>();
+ insertTemplateButton = new javax.swing.JButton();
+ manageTemplatesButton = new javax.swing.JButton();
dummyMainCommentsPanel.setLayout(new java.awt.BorderLayout());
@@ -978,6 +1032,10 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
org.openide.awt.Mnemonics.setLocalizedText(headerCreatedDateLabel, org.openide.util.NbBundle.getMessage(BacklogIssuePanel.class, "BacklogIssuePanel.headerCreatedDateLabel.text")); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(headerUpdatedLabel, org.openide.util.NbBundle.getMessage(BacklogIssuePanel.class, "BacklogIssuePanel.headerUpdatedLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(headerUpdatedDateLabel, org.openide.util.NbBundle.getMessage(BacklogIssuePanel.class, "BacklogIssuePanel.headerUpdatedDateLabel.text")); // NOI18N
+
org.openide.awt.Mnemonics.setLocalizedText(headerStartDateLabel, org.openide.util.NbBundle.getMessage(BacklogIssuePanel.class, "BacklogIssuePanel.headerStartDateLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(headerStartDateViewLabel, org.openide.util.NbBundle.getMessage(BacklogIssuePanel.class, "BacklogIssuePanel.headerStartDateViewLabel.text")); // NOI18N
@@ -1019,6 +1077,10 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(headerCreatedDateLabel)
.addGap(18, 18, 18)
+ .addComponent(headerUpdatedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(headerUpdatedDateLabel)
+ .addGap(18, 18, 18)
.addComponent(headerStartDateLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(headerStartDateViewLabel)
@@ -1036,11 +1098,11 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(addSubtaskLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(jSeparator3, javax.swing.GroupLayout.PREFERRED_SIZE, 6, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jSeparator3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(refreshLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(jSeparator5, javax.swing.GroupLayout.PREFERRED_SIZE, 6, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jSeparator5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(showOnBrowserLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap())
@@ -1050,14 +1112,15 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
.addGroup(headerPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(headerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(headerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
- .addComponent(showOnBrowserLinkButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
- .addComponent(refreshLinkButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
- .addComponent(jSeparator5)
- .addComponent(jSeparator3)
- .addComponent(addSubtaskLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
- .addComponent(headerIssueKeyLabel))
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(headerIssueKeyLabel)
+ .addGroup(headerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(headerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(jSeparator5)
+ .addComponent(jSeparator3)
+ .addComponent(addSubtaskLinkButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(refreshLinkButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addComponent(showOnBrowserLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(headerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(headerCreatedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(headerCreatedDateLabel)
@@ -1066,7 +1129,9 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
.addComponent(headerCreatedByLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(headerCreatedUserLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(headerStartDateLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addComponent(headerStartDateViewLabel))
+ .addComponent(headerStartDateViewLabel)
+ .addComponent(headerUpdatedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(headerUpdatedDateLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(headerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(errorHeaderLabel)
@@ -1175,6 +1240,28 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
subtaskingCollapsibleSectionPanel.setExpanded(false);
subtaskingCollapsibleSectionPanel.setLabel(org.openide.util.NbBundle.getMessage(BacklogIssuePanel.class, "BacklogIssuePanel.subtaskingCollapsibleSectionPanel.label")); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(notificationLabel, org.openide.util.NbBundle.getMessage(BacklogIssuePanel.class, "BacklogIssuePanel.notificationLabel.text")); // NOI18N
+
+ notificationUserScrollPane.setViewportView(notificationUserList);
+
+ insertTemplateButton.setIcon(new javax.swing.ImageIcon("/home/junichi11/NetBeansProjects/netbeans-backlog-plugin/src/main/resources/com/junichi11/netbeans/modules/backlog/resources/template_16.png")); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(insertTemplateButton, org.openide.util.NbBundle.getMessage(BacklogIssuePanel.class, "BacklogIssuePanel.insertTemplateButton.text")); // NOI18N
+ insertTemplateButton.setToolTipText(org.openide.util.NbBundle.getMessage(BacklogIssuePanel.class, "BacklogIssuePanel.insertTemplateButton.toolTipText")); // NOI18N
+ insertTemplateButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ insertTemplateButtonActionPerformed(evt);
+ }
+ });
+
+ manageTemplatesButton.setIcon(new javax.swing.ImageIcon("/home/junichi11/NetBeansProjects/netbeans-backlog-plugin/src/main/resources/com/junichi11/netbeans/modules/backlog/resources/manage_template_16.png")); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(manageTemplatesButton, org.openide.util.NbBundle.getMessage(BacklogIssuePanel.class, "BacklogIssuePanel.manageTemplatesButton.text")); // NOI18N
+ manageTemplatesButton.setToolTipText(org.openide.util.NbBundle.getMessage(BacklogIssuePanel.class, "BacklogIssuePanel.manageTemplatesButton.toolTipText")); // NOI18N
+ manageTemplatesButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ manageTemplatesButtonActionPerformed(evt);
+ }
+ });
+
javax.swing.GroupLayout mainPanelLayout = new javax.swing.GroupLayout(mainPanel);
mainPanel.setLayout(mainPanelLayout);
mainPanelLayout.setHorizontalGroup(
@@ -1182,29 +1269,20 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
.addGroup(mainPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(mainPanelLayout.createSequentialGroup()
- .addComponent(subtaskingCollapsibleSectionPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
- .addContainerGap())
- .addGroup(mainPanelLayout.createSequentialGroup()
- .addComponent(attachmentsCollapsibleSectionPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
- .addContainerGap())
- .addGroup(mainPanelLayout.createSequentialGroup()
- .addComponent(jSeparator1)
- .addContainerGap())
+ .addComponent(subtaskingCollapsibleSectionPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jSeparator1)
.addGroup(mainPanelLayout.createSequentialGroup()
.addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(summaryLabel)
+ .addComponent(descriptionLabel)
.addGroup(mainPanelLayout.createSequentialGroup()
- .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(summaryLabel)
- .addComponent(descriptionLabel))
+ .addComponent(insertTemplateButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(descriptionScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
- .addComponent(summaryTextField)))
- .addGroup(mainPanelLayout.createSequentialGroup()
- .addComponent(attributeLabel)
- .addGap(0, 0, Short.MAX_VALUE)))
- .addContainerGap())
+ .addComponent(manageTemplatesButton)))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(descriptionScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(summaryTextField)))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, mainPanelLayout.createSequentialGroup()
.addGap(12, 12, 12)
.addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@@ -1217,7 +1295,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
.addComponent(typeLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(dueDatePicker, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(dueDatePicker, javax.swing.GroupLayout.DEFAULT_SIZE, 197, Short.MAX_VALUE)
.addComponent(startDatePicker, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(milestoneScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addComponent(versionScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
@@ -1252,23 +1330,24 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
.addComponent(assigneeComboBox, javax.swing.GroupLayout.Alignment.TRAILING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(mainPanelLayout.createSequentialGroup()
.addComponent(assignToMyselfLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addGap(0, 0, Short.MAX_VALUE))
+ .addGap(0, 87, Short.MAX_VALUE))
.addGroup(mainPanelLayout.createSequentialGroup()
.addComponent(actualHoursTextField)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(hoursActualLabel)))))
- .addContainerGap())
+ .addComponent(hoursActualLabel))))))
+ .addComponent(commentsCollapsibleSectionPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jSeparator2, javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(attachmentsCollapsibleSectionPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(attributeLabel)
.addGroup(mainPanelLayout.createSequentialGroup()
.addComponent(commentLabel)
.addGap(18, 18, 18)
.addComponent(commentScrollPane)
- .addGap(12, 12, 12))
- .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, mainPanelLayout.createSequentialGroup()
- .addComponent(commentsCollapsibleSectionPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
- .addContainerGap())
- .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, mainPanelLayout.createSequentialGroup()
- .addComponent(jSeparator2)
- .addContainerGap())))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(notificationLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(notificationUserScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)))
+ .addContainerGap())
);
mainPanelLayout.setVerticalGroup(
mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@@ -1281,7 +1360,12 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
.addComponent(summaryLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(descriptionLabel)
+ .addGroup(mainPanelLayout.createSequentialGroup()
+ .addComponent(descriptionLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(insertTemplateButton)
+ .addComponent(manageTemplatesButton)))
.addComponent(descriptionScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
@@ -1344,10 +1428,14 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
.addComponent(attachmentsCollapsibleSectionPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(commentLabel)
- .addComponent(commentScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
- .addGap(18, 18, 18)
- .addComponent(commentsCollapsibleSectionPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGroup(mainPanelLayout.createSequentialGroup()
+ .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(commentLabel)
+ .addComponent(commentScrollPane)
+ .addComponent(notificationUserScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
+ .addGap(18, 18, 18)
+ .addComponent(commentsCollapsibleSectionPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addComponent(notificationLabel))
.addContainerGap())
);
@@ -1474,6 +1562,69 @@ public void run() {
});
}//GEN-LAST:event_addSubtaskLinkButtonActionPerformed
+ @NbBundle.Messages("BacklogIssuePanel.insert.template.title=Insert Template")
+ private void insertTemplateButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_insertTemplateButtonActionPerformed
+ assert EventQueue.isDispatchThread();
+ String[] templateNames = BacklogConfig.getInstance().getTemplateNames();
+ InsertTemplatePanel insertTemplatePanel = new InsertTemplatePanel();
+ insertTemplatePanel.setTemplates(templateNames);
+ NotifyDescriptor.Confirmation message = new NotifyDescriptor.Confirmation(
+ insertTemplatePanel,
+ Bundle.BacklogIssuePanel_insert_template_title(),
+ NotifyDescriptor.OK_CANCEL_OPTION,
+ NotifyDescriptor.PLAIN_MESSAGE);
+ if (DialogDisplayer.getDefault().notify(message) == NotifyDescriptor.OK_OPTION) {
+ String selectedTemplateName = insertTemplatePanel.getSelectedTemplateName();
+ String template = BacklogConfig.getInstance().getTemplate(selectedTemplateName);
+ if (template == null || template.isEmpty()) {
+ return;
+ }
+
+ // insert a template to a caret position
+ int caretPosition = descriptionEditorPane.getCaretPosition();
+ Document document = descriptionEditorPane.getDocument();
+ try {
+ document.insertString(caretPosition, template, null);
+ } catch (BadLocationException ex) {
+ LOGGER.log(Level.WARNING, "Can''t insert a template to " + caretPosition, ex); // NOI18N
+ }
+ }
+
+ }//GEN-LAST:event_insertTemplateButtonActionPerformed
+
+ @NbBundle.Messages({
+ "BacklogIssuePanel.manage.templates.title=Manage Templates",
+ "BacklogIssuePanel.manage.templates.add.option=Add",
+ "BacklogIssuePanel.manage.templates.remove.option=Remove",
+ "BacklogIssuePanel.manage.templates.edit.option=Edit",
+ "BacklogIssuePanel.manage.templates.duplicate.option=Duplicate",
+ "BacklogIssuePanel.manage.templates.close.option=Close"
+ })
+ private void manageTemplatesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_manageTemplatesButtonActionPerformed
+ assert EventQueue.isDispatchThread();
+ final ManageTemplatesPanel manageTemplatesPanel = new ManageTemplatesPanel();
+ final DialogDescriptor descriptor = new DialogDescriptor(
+ manageTemplatesPanel, // message
+ Bundle.BacklogIssuePanel_manage_templates_title(), // title
+ true, // modal
+ null, // options
+ null, // initial value
+ DialogDescriptor.RIGHT_ALIGN,
+ null, // help
+ null // action listener
+ );
+ descriptor.setOptions(new String[]{
+ TEMPLATES_ADD_OPTION,
+ TEMPLATES_EDIT_OPTION,
+ TEMPLATES_DUPLICATE_OPTION,
+ TEMPLATES_REMOVE_OPTION,
+ TEMPLATES_CLOSE_OPTION
+ });
+ descriptor.setClosingOptions(new String[]{TEMPLATES_CLOSE_OPTION});
+ descriptor.setButtonListener(new ManageTemplateButtonListener(descriptor, manageTemplatesPanel));
+ DialogDisplayer.getDefault().notify(descriptor);
+ }//GEN-LAST:event_manageTemplatesButtonActionPerformed
+
@NbBundle.Messages({
"BacklogIssuePanel.label.select.file=Select File",
"BacklogIssuePanel.message.uploading.attachments=Uploading files"
@@ -1501,11 +1652,11 @@ public void run() {
ProgressHandle handle = ProgressHandleFactory.createHandle(
Bundle.BacklogIssuePanel_message_uploading_attachments(),
new Cancellable() {
- @Override
- public boolean cancel() {
- return true;
- }
- });
+ @Override
+ public boolean cancel() {
+ return true;
+ }
+ });
try {
handle.start(attachments.length);
int progressCount = 0;
@@ -1600,13 +1751,18 @@ public void run() {
})
private void updateIssue() {
UpdateIssueParams issueParams = createUpdateIssueParams();
- final Issue updateIssue = issue.updateIssue(issueParams);
- final boolean hasComment = isCommentUpdated();
+ // TODO check changes
+ final Issue updatedIssue = issue.updateIssue(issueParams);
+
+ final boolean hasComment = hasComment();
+ if (hasComment) {
+ IssueComment addedComment = issue.addIssueComment(getComment(), getNotificationUserIds());
+ }
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
- if (updateIssue != null) {
+ if (updatedIssue != null) {
unsubmittedAttachmentsPanel.removeAllAttachments();
StatusDisplayer.getDefault().setStatusText(Bundle.BacklogIssuePanel_message_update_issue_success());
} else {
@@ -1741,11 +1897,11 @@ private UpdateIssueParams createUpdateIssueParams() {
}
// comment
- String comment = getComment();
- if (!StringUtils.isEmpty(comment)) {
- issueParams = issueParams.comment(comment);
- }
-
+ // sepalate
+// String comment = getComment();
+// if (!StringUtils.isEmpty(comment)) {
+// issueParams = issueParams.comment(comment);
+// }
// check status
// Can't change to the same status
Issue existingIssue = issue.getIssue();
@@ -1790,7 +1946,7 @@ private UpdateIssueParams createUpdateIssueParams() {
return issueParams;
}
- private boolean isCommentUpdated() {
+ private boolean hasComment() {
String comment = getComment();
return !StringUtils.isEmpty(comment);
}
@@ -1859,8 +2015,11 @@ private static String getBacklogIssueUrlFormat(String backlogDomain) {
private javax.swing.JPanel headerPanel;
private javax.swing.JLabel headerStartDateLabel;
private javax.swing.JLabel headerStartDateViewLabel;
+ private javax.swing.JLabel headerUpdatedDateLabel;
+ private javax.swing.JLabel headerUpdatedLabel;
private javax.swing.JLabel hoursActualLabel;
private javax.swing.JLabel hoursEstimatedLabel;
+ private javax.swing.JButton insertTemplateButton;
private javax.swing.JComboBox issueTypeComboBox;
private javax.swing.JSeparator jSeparator1;
private javax.swing.JSeparator jSeparator2;
@@ -1871,9 +2030,13 @@ private static String getBacklogIssueUrlFormat(String backlogDomain) {
private javax.swing.JPanel mainPanel;
private javax.swing.JScrollPane mainScrollPane;
private javax.swing.JPanel mainSubtaskTablePanel;
+ private javax.swing.JButton manageTemplatesButton;
private javax.swing.JLabel milestoneLabel;
private javax.swing.JList milestoneList;
private javax.swing.JScrollPane milestoneScrollPane;
+ private javax.swing.JLabel notificationLabel;
+ private javax.swing.JList notificationUserList;
+ private javax.swing.JScrollPane notificationUserScrollPane;
private javax.swing.JComboBox priorityComboBox;
private javax.swing.JLabel priorityLabel;
private org.netbeans.modules.bugtracking.commons.LinkButton refreshLinkButton;
@@ -1907,6 +2070,9 @@ public void propertyChange(PropertyChangeEvent event) {
case BacklogIssue.PROP_COMMENT_EDITED:
editComment(commentsPanel.getEditedComment());
break;
+ case BacklogIssue.PROP_COMMENT_NOTIFY:
+ notifyComment(commentsPanel.getNotifyComment());
+ break;
case AttachmentPanel.PROP_ATTACHMENT_DELETED:
Attachment attachment = (Attachment) event.getOldValue();
deleteAttachment(attachment);
@@ -1958,6 +2124,48 @@ public void run() {
});
}
+ @NbBundle.Messages("BacklogIssuePanel.no.notification.user=There are no users that can be notified.")
+ private void notifyComment(final IssueComment comment) {
+ SwingUtilities.invokeLater(new Runnable() {
+
+ @Override
+ public void run() {
+ // users
+ List allUsers = getAllNotificationUsers(comment);
+ if (allUsers.isEmpty()) {
+ UiUtils.showPlainDialog(Bundle.BacklogIssuePanel_no_notification_user());
+ return;
+ }
+
+ // show dialog
+ List userIds = NotifyCommentPanel.showDialog(allUsers, issue.getRepository());
+ if (userIds.isEmpty()) {
+ return;
+ }
+ IssueComment updatedIssueComment = issue.addIssueCommentNotification(comment, userIds);
+ if (updatedIssueComment != null) {
+ update(true);
+ }
+ }
+ });
+ }
+
+ private List getAllNotificationUsers(IssueComment comment) {
+ BacklogRepository repository = issue.getRepository();
+ BacklogData data = BacklogData.create(repository);
+ User myself = data.getMyself();
+ List notifications = comment.getNotifications();
+ List allUsers = new ArrayList<>(data.getProjectUsers());
+ allUsers.remove(myself);
+ for (Notification notification : notifications) {
+ User user = notification.getUser();
+ if (user != null) {
+ allUsers.remove(user);
+ }
+ }
+ return allUsers;
+ }
+
private void deleteAttachment(Attachment attachment) {
Attachment a = issue.deleteIssueAttachment(attachment.getId());
if (a != null) {
@@ -1996,4 +2204,149 @@ private void processUpdate() {
fireChange();
}
}
+
+ //~ inner class
+ private static class ManageTemplateButtonListener implements ActionListener {
+
+ private final DialogDescriptor descriptor;
+ private final ManageTemplatesPanel manageTemplatesPanel;
+
+ public ManageTemplateButtonListener(DialogDescriptor descriptor, ManageTemplatesPanel manageTemplatesPanel) {
+ this.descriptor = descriptor;
+ this.manageTemplatesPanel = manageTemplatesPanel;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ Object value = descriptor.getValue();
+ if (value == TEMPLATES_ADD_OPTION) {
+ add();
+ } else if (value == TEMPLATES_EDIT_OPTION) {
+ edit();
+ } else if (value == TEMPLATES_DUPLICATE_OPTION) {
+ duplicate();
+ } else if (value == TEMPLATES_REMOVE_OPTION) {
+ remove();
+ }
+ }
+
+ @NbBundle.Messages("ManageTemplateButtonListener.add.title=Add Template")
+ private void add() {
+ showDialog(TEMPLATES_ADD_OPTION, Bundle.ManageTemplateButtonListener_add_title());
+ }
+
+ @NbBundle.Messages("ManageTemplateButtonListener.edit.title=Edit Template")
+ private void edit() {
+ showDialog(TEMPLATES_EDIT_OPTION, Bundle.ManageTemplateButtonListener_edit_title());
+ }
+
+ @NbBundle.Messages("ManageTemplateButtonListener.duplicate.title=Duplicate Template")
+ private void duplicate() {
+ showDialog(TEMPLATES_DUPLICATE_OPTION, Bundle.ManageTemplateButtonListener_duplicate_title());
+ }
+
+ @NbBundle.Messages({
+ "# {0} - name",
+ "ManageTemplateButtonListener.remove.message=Do you really want to remove {0}? \n(In case of default, it is just initialized without removing.)"
+ })
+ private void remove() {
+ String selectedTemplateName = manageTemplatesPanel.getSelectedTemplateName();
+ if (selectedTemplateName == null || selectedTemplateName.isEmpty()) {
+ return;
+ }
+ if (UiUtils.showQuestionDialog(Bundle.ManageTemplateButtonListener_remove_message(selectedTemplateName))) {
+ BacklogConfig.getInstance().removeTemplate(selectedTemplateName);
+ manageTemplatesPanel.resetTemplateNameList();
+ }
+ }
+
+ private void showDialog(String option, String title) {
+ if (!option.equals(TEMPLATES_ADD_OPTION)
+ && !option.equals(TEMPLATES_EDIT_OPTION)
+ && !option.equals(TEMPLATES_DUPLICATE_OPTION)) {
+ return;
+ }
+
+ // create panel
+ final TemplatePanel templatePanel = new TemplatePanel();
+ String selectedTemplateName = manageTemplatesPanel.getSelectedTemplateName();
+ if (!option.equals(TEMPLATES_ADD_OPTION)) {
+ if (selectedTemplateName == null || selectedTemplateName.isEmpty()) {
+ return;
+ }
+ templatePanel.setTemplateNameEditable(!option.equals(TEMPLATES_EDIT_OPTION));
+ templatePanel.setTemplateName(selectedTemplateName);
+ templatePanel.setTemplate(BacklogConfig.getInstance().getTemplate(selectedTemplateName));
+ }
+ final NotifyDescriptor.Confirmation notify = new NotifyDescriptor.Confirmation(
+ templatePanel,
+ title,
+ NotifyDescriptor.OK_CANCEL_OPTION,
+ NotifyDescriptor.PLAIN_MESSAGE);
+
+ // add listener
+ ChangeListener listener = null;
+ if (option.equals(TEMPLATES_ADD_OPTION) || option.equals(TEMPLATES_DUPLICATE_OPTION)) {
+ final List existingNames = new ArrayList<>(Arrays.asList(BacklogConfig.getInstance().getTemplateNames()));
+ listener = new TemplatePanelChangeListener(templatePanel, notify, existingNames);
+ templatePanel.addChangeListener(listener);
+ templatePanel.fireChange();
+ }
+
+ // show dialog
+ if (DialogDisplayer.getDefault().notify(notify) == NotifyDescriptor.OK_OPTION) {
+ String templateName = templatePanel.getTemplateName();
+ if (templateName != null && !templateName.isEmpty()) {
+ String template = templatePanel.getTemplate();
+ BacklogConfig.getInstance().setTemplate(templateName, template);
+ if (option.equals(TEMPLATES_EDIT_OPTION)) {
+ manageTemplatesPanel.setSelectedTemplateName(selectedTemplateName);
+ } else {
+ manageTemplatesPanel.resetTemplateNameList();
+ }
+ }
+ }
+
+ if (listener != null) {
+ templatePanel.removeChangeListener(listener);
+ }
+ }
+ }
+
+ private static class TemplatePanelChangeListener implements ChangeListener {
+
+ private final TemplatePanel templatePanel;
+ private final NotifyDescriptor.Confirmation notify;
+ private final List existingNames;
+
+ public TemplatePanelChangeListener(TemplatePanel templatePanel, NotifyDescriptor.Confirmation notify, List existingNames) {
+ this.templatePanel = templatePanel;
+ this.notify = notify;
+ this.existingNames = existingNames;
+ }
+
+ @Override
+ @NbBundle.Messages({
+ "TemplatePanelChangeListener.invalid.empty=Name must be set.",
+ "TemplatePanelChangeListener.invalid.existing=It already exisits."
+ })
+ public void stateChanged(ChangeEvent e) {
+ // validate
+ String templateName = templatePanel.getTemplateName();
+ if ((templateName == null || templateName.isEmpty())) {
+ notify.setValid(false);
+ templatePanel.setErrorMessage(Bundle.TemplatePanelChangeListener_invalid_empty());
+ return;
+ }
+ if (existingNames.contains(templateName)) {
+ notify.setValid(false);
+ templatePanel.setErrorMessage(Bundle.TemplatePanelChangeListener_invalid_existing());
+ return;
+ }
+
+ // everything ok
+ notify.setValid(true);
+ templatePanel.setErrorMessage(" "); // NOI18N
+ }
+ }
}
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/Bundle.properties b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/Bundle.properties
index acf4f9e..f3a187e 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/Bundle.properties
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/Bundle.properties
@@ -28,6 +28,8 @@ BacklogIssuePanel.headerCreatedByLabel.text=Created by:
BacklogIssuePanel.headerIssueKeyLabel.text=ISSUE_KEY
BacklogIssuePanel.headerCreatedLabel.text=Created:
BacklogIssuePanel.headerCreatedDateLabel.text=-
+BacklogIssuePanel.headerUpdatedLabel.text=Updated:
+BacklogIssuePanel.headerUpdatedDateLabel.text=-
BacklogIssuePanel.headerCreatedUserLinkButton.text=-
SubmitPanel.errorLabel.text=ERROR
SubmitPanel.submitButton.text=Submit
@@ -59,3 +61,14 @@ CommentPanel.quoteLinkButton.text=Quote
CommentPanel.deleteLinkButton.text=Delete
BacklogIssuePanel.subtaskingCollapsibleSectionPanel.label=Subtasking
BacklogIssuePanel.addSubtaskLinkButton.text=Add subtask
+BacklogIssuePanel.notificationLabel.text=Notify:
+CommentPanel.notificationSentToLabel.text=Notification sent to:
+CommentPanel.notifyLinkButton.text=Notify
+BacklogIssuePanel.insertTemplateButton.toolTipText=Insert Template
+BacklogIssuePanel.insertTemplateButton.text=
+BacklogIssuePanel.manageTemplatesButton.toolTipText=Manage Templates
+BacklogIssuePanel.manageTemplatesButton.text=
+InsertTemplatePanel.templatesComboBox.toolTipText=
+TemplatePanel.nameLabel.text=Name:
+TemplatePanel.nameTextField.text=
+TemplatePanel.errorLabel.text=ERROR
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/CommentPanel.form b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/CommentPanel.form
index 704e64d..99aba36 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/CommentPanel.form
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/CommentPanel.form
@@ -16,13 +16,13 @@
-
-
-
-
+
+
+
+
-
+
@@ -31,6 +31,8 @@
+
+
@@ -38,8 +40,13 @@
+
+
+
+
+
-
+
@@ -56,12 +63,17 @@
+
-
+
+
+
+
+
@@ -144,5 +156,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/CommentPanel.java b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/CommentPanel.java
index 5538a8b..914ae7c 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/CommentPanel.java
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/CommentPanel.java
@@ -47,9 +47,12 @@
import com.junichi11.netbeans.modules.backlog.utils.BacklogUtils;
import com.junichi11.netbeans.modules.backlog.utils.UiUtils;
import com.nulabinc.backlog4j.IssueComment;
+import com.nulabinc.backlog4j.Notification;
import com.nulabinc.backlog4j.User;
import java.util.Date;
+import java.util.List;
import javax.swing.Icon;
+import javax.swing.JLabel;
import org.openide.util.NbBundle;
/**
@@ -58,11 +61,17 @@
*/
public class CommentPanel extends javax.swing.JPanel {
+ public enum Status {
+ Quote,
+ Edited,
+ Deleted,
+ Notify,
+ None
+ }
+
private static final long serialVersionUID = 4522741935313865234L;
private IssueComment comment;
- private boolean isQuote;
- private boolean isEdited;
- private boolean isDeleted;
+ private Status status = Status.None;
/**
* Creates new form CommentPanel
@@ -74,12 +83,23 @@ private CommentPanel() {
public CommentPanel(BacklogRepository repository, IssueComment comment) {
this.comment = comment;
initComponents();
+
setUser(repository, comment.getCreatedUser());
setCreatedDate(comment.getCreated());
setUpdatedDate(comment.getUpdated());
setContent(comment.getContent());
- // XXX delete comment is still not supported by api v2
+ setNotifications(comment, repository);
+
+ // disable
+ BacklogData cache = BacklogData.create(repository);
+ User myself = cache.getMyself();
+ if (!comment.getCreatedUser().equals(myself)) {
+ notifyLinkButton.setEnabled(false);
+ editLinkButton.setEnabled(false);
+ }
+
+ // TODO delete comment is still not supported by api v2
deleteLinkButton.setEnabled(false);
}
@@ -108,12 +128,26 @@ private void setUpdatedDate(Date date) {
private void setContent(String content) {
if (content == null) {
- contentTextPane.setText("");
+ contentTextPane.setText(""); // NOI18N
} else if (content.isEmpty()) {
// TODO show change log?
contentTextPane.setText(""); // NOI18N
} else {
- contentTextPane.setText(content); // NOI18N
+ contentTextPane.setText(content);
+ }
+ }
+
+ private void setNotifications(IssueComment comment, BacklogRepository repository) {
+ List notifications = comment.getNotifications();
+ BacklogData data = BacklogData.create(repository);
+ for (Notification notification : notifications) {
+ User user = notification.getUser();
+ Icon userIcon = data.getUserIcon(user);
+ if (userIcon != null) {
+ JLabel userLabel = new JLabel(userIcon);
+ userLabel.setToolTipText(user.getName());
+ notificationUsersPanel.add(userLabel);
+ }
}
}
@@ -125,22 +159,12 @@ public String getSelectedText() {
return contentTextPane.getSelectedText();
}
- public boolean isQuote() {
- return isQuote;
- }
-
- public boolean isEdited() {
- return isEdited;
- }
-
- public boolean isDeleted() {
- return isDeleted;
+ public Status getStatus() {
+ return status;
}
void resetProperties() {
- isQuote = false;
- isEdited = false;
- isDeleted = false;
+ status = Status.None;
}
/**
@@ -162,6 +186,9 @@ private void initComponents() {
contentTextPane = new javax.swing.JTextPane();
quoteLinkButton = new org.netbeans.modules.bugtracking.commons.LinkButton();
deleteLinkButton = new org.netbeans.modules.bugtracking.commons.LinkButton();
+ notifyLinkButton = new org.netbeans.modules.bugtracking.commons.LinkButton();
+ notificationSentToLabel = new javax.swing.JLabel();
+ notificationUsersPanel = new javax.swing.JPanel();
org.openide.awt.Mnemonics.setLocalizedText(createdLabel, org.openide.util.NbBundle.getMessage(CommentPanel.class, "CommentPanel.createdLabel.text")); // NOI18N
@@ -197,17 +224,28 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
}
});
+ org.openide.awt.Mnemonics.setLocalizedText(notifyLinkButton, org.openide.util.NbBundle.getMessage(CommentPanel.class, "CommentPanel.notifyLinkButton.text")); // NOI18N
+ notifyLinkButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ notifyLinkButtonActionPerformed(evt);
+ }
+ });
+
+ org.openide.awt.Mnemonics.setLocalizedText(notificationSentToLabel, org.openide.util.NbBundle.getMessage(CommentPanel.class, "CommentPanel.notificationSentToLabel.text")); // NOI18N
+
+ notificationUsersPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.RIGHT));
+
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addGroup(layout.createSequentialGroup()
.addContainerGap()
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
- .addComponent(contentTextPane)
- .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(contentTextPane, javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(layout.createSequentialGroup()
.addComponent(userLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 139, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 89, Short.MAX_VALUE)
.addComponent(createdLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(createdDateLabel)
@@ -216,12 +254,18 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(updatedDateLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(notifyLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(quoteLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(editLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(deleteLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
- .addComponent(jSeparator1, javax.swing.GroupLayout.Alignment.LEADING))
+ .addComponent(jSeparator1)
+ .addComponent(notificationUsersPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addGap(0, 0, Short.MAX_VALUE)
+ .addComponent(notificationSentToLabel)))
.addContainerGap())
);
layout.setVerticalGroup(
@@ -236,17 +280,22 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
.addComponent(userLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(editLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(quoteLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addComponent(deleteLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addComponent(deleteLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(notifyLinkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(contentTextPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(notificationSentToLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(notificationUsersPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap(24, Short.MAX_VALUE))
);
}// //GEN-END:initComponents
private void quoteLinkButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_quoteLinkButtonActionPerformed
- isQuote = true;
+ status = Status.Quote;
firePropertyChange(BacklogIssue.PROP_COMMENT_QUOTE, null, null);
}//GEN-LAST:event_quoteLinkButtonActionPerformed
@@ -257,15 +306,20 @@ private void deleteLinkButtonActionPerformed(java.awt.event.ActionEvent evt) {//
if (!UiUtils.showQuestionDialog(Bundle.CommentPanel_message_delete_issue())) {
return;
}
- isDeleted = true;
+ status = Status.Deleted;
firePropertyChange(BacklogIssue.PROP_COMMENT_DELETED, null, null);
}//GEN-LAST:event_deleteLinkButtonActionPerformed
private void editLinkButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editLinkButtonActionPerformed
- isEdited = true;
+ status = Status.Edited;
firePropertyChange(BacklogIssue.PROP_COMMENT_EDITED, null, null);
}//GEN-LAST:event_editLinkButtonActionPerformed
+ private void notifyLinkButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_notifyLinkButtonActionPerformed
+ status = Status.Notify;
+ firePropertyChange(BacklogIssue.PROP_COMMENT_NOTIFY, null, null);
+ }//GEN-LAST:event_notifyLinkButtonActionPerformed
+
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTextPane contentTextPane;
private javax.swing.JLabel createdDateLabel;
@@ -273,6 +327,9 @@ private void editLinkButtonActionPerformed(java.awt.event.ActionEvent evt) {//GE
private org.netbeans.modules.bugtracking.commons.LinkButton deleteLinkButton;
private org.netbeans.modules.bugtracking.commons.LinkButton editLinkButton;
private javax.swing.JSeparator jSeparator1;
+ private javax.swing.JLabel notificationSentToLabel;
+ private javax.swing.JPanel notificationUsersPanel;
+ private org.netbeans.modules.bugtracking.commons.LinkButton notifyLinkButton;
private org.netbeans.modules.bugtracking.commons.LinkButton quoteLinkButton;
private javax.swing.JLabel updatedDateLabel;
private javax.swing.JLabel updatedLabel;
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/CommentsPanel.java b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/CommentsPanel.java
index f73da1c..20b5bff 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/CommentsPanel.java
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/CommentsPanel.java
@@ -63,6 +63,7 @@ public class CommentsPanel extends javax.swing.JPanel implements PropertyChangeL
private CommentPanel quoteCommentPanel;
private CommentPanel deletedCommentPanel;
private CommentPanel editedCommentPanel;
+ private CommentPanel notifyCommentPanel;
/**
* Creates new form UnsubmittedAttachmentsPanel
@@ -127,10 +128,15 @@ public IssueComment getEditedComment() {
return editedCommentPanel == null ? null : editedCommentPanel.getComment();
}
+ public IssueComment getNotifyComment() {
+ return notifyCommentPanel == null ? null : notifyCommentPanel.getComment();
+ }
+
public void resetChangedPanels() {
quoteCommentPanel = null;
deletedCommentPanel = null;
editedCommentPanel = null;
+ notifyCommentPanel = null;
}
/**
@@ -159,6 +165,9 @@ public void propertyChange(PropertyChangeEvent event) {
case BacklogIssue.PROP_COMMENT_EDITED:
fireEditedPropertyChanged();
break;
+ case BacklogIssue.PROP_COMMENT_NOTIFY:
+ fireNotifyPropertyChanged();
+ break;
default:
break;
}
@@ -168,7 +177,7 @@ private void fireQuotePropertyChanged() {
synchronized (commentPanels) {
quoteCommentPanel = null;
for (CommentPanel comment : commentPanels) {
- if (comment.isQuote()) {
+ if (comment.getStatus() == CommentPanel.Status.Quote) {
quoteCommentPanel = comment;
comment.resetProperties();
firePropertyChange(BacklogIssue.PROP_COMMENT_QUOTE, null, null);
@@ -182,7 +191,7 @@ private void fireDeletedPropertyChanged() {
synchronized (commentPanels) {
deletedCommentPanel = null;
for (CommentPanel comment : commentPanels) {
- if (comment.isDeleted()) {
+ if (comment.getStatus() == CommentPanel.Status.Deleted) {
deletedCommentPanel = comment;
comment.resetProperties();
firePropertyChange(BacklogIssue.PROP_COMMENT_DELETED, null, null);
@@ -196,7 +205,7 @@ private void fireEditedPropertyChanged() {
synchronized (commentPanels) {
editedCommentPanel = null;
for (CommentPanel comment : commentPanels) {
- if (comment.isEdited()) {
+ if (comment.getStatus() == CommentPanel.Status.Edited) {
editedCommentPanel = comment;
comment.resetProperties();
firePropertyChange(BacklogIssue.PROP_COMMENT_EDITED, null, null);
@@ -206,4 +215,18 @@ private void fireEditedPropertyChanged() {
}
}
+ private void fireNotifyPropertyChanged() {
+ synchronized (commentPanels) {
+ notifyCommentPanel = null;
+ for (CommentPanel comment : commentPanels) {
+ if (comment.getStatus() == CommentPanel.Status.Notify) {
+ notifyCommentPanel = comment;
+ comment.resetProperties();
+ firePropertyChange(BacklogIssue.PROP_COMMENT_NOTIFY, null, null);
+ break;
+ }
+ }
+ }
+ }
+
}
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/InsertTemplatePanel.form b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/InsertTemplatePanel.form
new file mode 100644
index 0000000..262db5e
--- /dev/null
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/InsertTemplatePanel.form
@@ -0,0 +1,46 @@
+
+
+
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/InsertTemplatePanel.java b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/InsertTemplatePanel.java
new file mode 100644
index 0000000..735a3ed
--- /dev/null
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/InsertTemplatePanel.java
@@ -0,0 +1,98 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2015 Sun Microsystems, Inc.
+ */
+package com.junichi11.netbeans.modules.backlog.issue.ui;
+
+/**
+ *
+ * @author junichi11
+ */
+public class InsertTemplatePanel extends javax.swing.JPanel {
+
+ private static final long serialVersionUID = -5815122403054262852L;
+
+ /**
+ * Creates new form InsertTemplatePanel
+ */
+ public InsertTemplatePanel() {
+ initComponents();
+ }
+
+ public void setTemplates(String[] templates) {
+ for (String template : templates) {
+ templatesComboBox.addItem(template);
+ }
+ }
+
+ public String getSelectedTemplateName() {
+ return (String) templatesComboBox.getSelectedItem();
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ templatesComboBox = new javax.swing.JComboBox();
+
+ templatesComboBox.setToolTipText(org.openide.util.NbBundle.getMessage(InsertTemplatePanel.class, "InsertTemplatePanel.templatesComboBox.toolTipText")); // NOI18N
+ templatesComboBox.setPreferredSize(new java.awt.Dimension(200, 27));
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(templatesComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(templatesComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ );
+ }// //GEN-END:initComponents
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JComboBox templatesComboBox;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/ManageTemplatesPanel.form b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/ManageTemplatesPanel.form
new file mode 100644
index 0000000..1f8c9fb
--- /dev/null
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/ManageTemplatesPanel.form
@@ -0,0 +1,76 @@
+
+
+
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/ManageTemplatesPanel.java b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/ManageTemplatesPanel.java
new file mode 100644
index 0000000..9988370
--- /dev/null
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/ManageTemplatesPanel.java
@@ -0,0 +1,155 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2015 Sun Microsystems, Inc.
+ */
+package com.junichi11.netbeans.modules.backlog.issue.ui;
+
+import com.junichi11.netbeans.modules.backlog.BacklogConfig;
+import javax.swing.DefaultListModel;
+import javax.swing.JPanel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+/**
+ *
+ * @author junichi11
+ */
+public class ManageTemplatesPanel extends JPanel {
+
+ private static final long serialVersionUID = -8286266826160163754L;
+
+ private final DefaultListModel listModel = new DefaultListModel<>();
+
+ /**
+ * Creates new form ManageTemplatesPanel
+ */
+ public ManageTemplatesPanel() {
+ initComponents();
+ init();
+ }
+
+ private void init() {
+ resetTemplateNameList();
+ templateNameList.setModel(listModel);
+ templateNameList.addListSelectionListener(new ListSelectionListener() {
+
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ String selectedValue = templateNameList.getSelectedValue();
+ if (selectedValue != null) {
+ // show template
+ setTemplate(selectedValue);
+ }
+ }
+ });
+ }
+
+ public void resetTemplateNameList() {
+ listModel.clear();
+ templateEditorPane.setText(""); // NOI18N
+ BacklogConfig config = BacklogConfig.getInstance();
+ String[] templateNames = config.getTemplateNames();
+ for (String templateName : templateNames) {
+ listModel.addElement(templateName);
+ }
+ }
+
+ public String getSelectedTemplateName() {
+ return templateNameList.getSelectedValue();
+ }
+
+ public void setSelectedTemplateName(String name) {
+ templateNameList.setSelectedValue(name, true);
+ setTemplate(name);
+ }
+
+ private void setTemplate(String templateName) {
+ String template = BacklogConfig.getInstance().getTemplate(templateName);
+ templateEditorPane.setText(template);
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ templateScrollPane = new javax.swing.JScrollPane();
+ templateEditorPane = new javax.swing.JEditorPane();
+ templateNameScrollPane = new javax.swing.JScrollPane();
+ templateNameList = new javax.swing.JList();
+
+ templateEditorPane.setEditable(false);
+ templateScrollPane.setViewportView(templateEditorPane);
+
+ templateNameScrollPane.setViewportView(templateNameList);
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(templateNameScrollPane)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(templateScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(templateScrollPane)
+ .addComponent(templateNameScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 276, Short.MAX_VALUE))
+ .addContainerGap())
+ );
+ }// //GEN-END:initComponents
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JEditorPane templateEditorPane;
+ private javax.swing.JList templateNameList;
+ private javax.swing.JScrollPane templateNameScrollPane;
+ private javax.swing.JScrollPane templateScrollPane;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/NotifyCommentPanel.form b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/NotifyCommentPanel.form
new file mode 100644
index 0000000..c8ec442
--- /dev/null
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/NotifyCommentPanel.form
@@ -0,0 +1,50 @@
+
+
+
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/NotifyCommentPanel.java b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/NotifyCommentPanel.java
new file mode 100644
index 0000000..30c54ba
--- /dev/null
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/NotifyCommentPanel.java
@@ -0,0 +1,136 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2015 Sun Microsystems, Inc.
+ */
+package com.junichi11.netbeans.modules.backlog.issue.ui;
+
+import com.junichi11.netbeans.modules.backlog.repository.BacklogRepository;
+import com.junichi11.netbeans.modules.backlog.ui.AttributesListCellRenderer;
+import com.junichi11.netbeans.modules.backlog.utils.StringUtils;
+import com.nulabinc.backlog4j.User;
+import com.nulabinc.backlog4j.internal.json.UserJSONImpl;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.swing.DefaultListModel;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author junichi11
+ */
+public class NotifyCommentPanel extends javax.swing.JPanel {
+
+ private final DefaultListModel notificationUserListModel = new DefaultListModel<>();
+
+ /**
+ * Creates new form NotifyCommentPanel
+ */
+ private NotifyCommentPanel(List users, BacklogRepository repository) {
+ initComponents();
+ // set notificatoin user list
+ notificationUserList.setCellRenderer(new AttributesListCellRenderer(notificationUserList.getCellRenderer(), repository.getID()));
+
+ notificationUserListModel.addElement(new UserJSONImpl());
+ for (User user : users) {
+ notificationUserListModel.addElement(user);
+ }
+ notificationUserList.setModel(notificationUserListModel);
+ }
+
+ @NbBundle.Messages("NotifyCommentPanel.dialog.title=Notify comment to")
+ public static List showDialog(List users, BacklogRepository repository) {
+ NotifyCommentPanel panel = new NotifyCommentPanel(users, repository);
+ NotifyDescriptor.Confirmation confirmation = new NotifyDescriptor.Confirmation(
+ panel,
+ Bundle.NotifyCommentPanel_dialog_title(),
+ NotifyDescriptor.OK_CANCEL_OPTION,
+ NotifyDescriptor.PLAIN_MESSAGE
+ );
+ if (DialogDisplayer.getDefault().notify(confirmation) == NotifyDescriptor.OK_OPTION) {
+ return panel.getNotificationUserIds();
+ }
+ return Collections.emptyList();
+ }
+
+ List getNotificationUserIds() {
+ List selectedUsers = notificationUserList.getSelectedValuesList();
+ List userIds = new ArrayList<>();
+ for (User selectedUser : selectedUsers) {
+ if (!StringUtils.isEmpty(selectedUser.getName())) {
+ userIds.add(selectedUser.getId());
+ }
+ }
+ return userIds;
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ notificationUserScrollPane = new javax.swing.JScrollPane();
+ notificationUserList = new javax.swing.JList();
+
+ notificationUserScrollPane.setViewportView(notificationUserList);
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(notificationUserScrollPane)
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(notificationUserScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ );
+ }// //GEN-END:initComponents
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JList notificationUserList;
+ private javax.swing.JScrollPane notificationUserScrollPane;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/TemplatePanel.form b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/TemplatePanel.form
new file mode 100644
index 0000000..adae4d8
--- /dev/null
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/TemplatePanel.form
@@ -0,0 +1,88 @@
+
+
+
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/TemplatePanel.java b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/TemplatePanel.java
new file mode 100644
index 0000000..0f81421
--- /dev/null
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/issue/ui/TemplatePanel.java
@@ -0,0 +1,207 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2015 Sun Microsystems, Inc.
+ */
+package com.junichi11.netbeans.modules.backlog.issue.ui;
+
+import com.junichi11.netbeans.modules.backlog.utils.BacklogImage;
+import javax.swing.UIManager;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import org.openide.util.ChangeSupport;
+
+/**
+ *
+ * @author junichi11
+ */
+public class TemplatePanel extends javax.swing.JPanel {
+
+ private static final long serialVersionUID = 3434944709638210871L;
+
+ private final ChangeSupport changeSupport = new ChangeSupport(this);
+
+ /**
+ * Creates new form TemplatePanel
+ */
+ public TemplatePanel() {
+ initComponents();
+ init();
+ }
+
+ private void init() {
+ // add listener
+ nameTextField.getDocument().addDocumentListener(new DefaultDocumentListener());
+
+ // error
+ errorLabel.setForeground(UIManager.getColor("nb.errorForeground")); // NOI18N
+ setErrorMessage(""); // NOI18N
+ }
+
+ public void setTemplateNameEditable(boolean isEditable) {
+ nameTextField.setEditable(isEditable);
+ }
+
+ public void setTemplateName(String name) {
+ nameTextField.setText(name);
+ }
+
+ public String getTemplateName() {
+ return nameTextField.getText().trim();
+ }
+
+ public void setTemplate(String template) {
+ templateEditorPane.setText(template);
+ }
+
+ public String getTemplate() {
+ return templateEditorPane.getText();
+ }
+
+ public void setErrorMessage(String errorMessage) {
+ if (errorMessage == null || errorMessage.trim().isEmpty()) {
+ errorMessage = ""; // NOI18N
+ errorLabel.setIcon(null);
+ } else {
+ errorLabel.setIcon(BacklogImage.ERROR_16.getIcon());
+ }
+ errorLabel.setText(errorMessage);
+ }
+
+ public void addChangeListener(ChangeListener listener) {
+ changeSupport.addChangeListener(listener);
+ }
+
+ public void removeChangeListener(ChangeListener listener) {
+ changeSupport.removeChangeListener(listener);
+ }
+
+ void fireChange() {
+ changeSupport.fireChange();
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ nameLabel = new javax.swing.JLabel();
+ nameTextField = new javax.swing.JTextField();
+ templateScrollPane = new javax.swing.JScrollPane();
+ templateEditorPane = new javax.swing.JEditorPane();
+ errorLabel = new javax.swing.JLabel();
+
+ org.openide.awt.Mnemonics.setLocalizedText(nameLabel, org.openide.util.NbBundle.getMessage(TemplatePanel.class, "TemplatePanel.nameLabel.text")); // NOI18N
+
+ nameTextField.setText(org.openide.util.NbBundle.getMessage(TemplatePanel.class, "TemplatePanel.nameTextField.text")); // NOI18N
+
+ templateScrollPane.setViewportView(templateEditorPane);
+
+ org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(TemplatePanel.class, "TemplatePanel.errorLabel.text")); // NOI18N
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(templateScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 426, Short.MAX_VALUE)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(nameLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(nameTextField))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(errorLabel)
+ .addGap(0, 0, Short.MAX_VALUE)))
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(nameLabel)
+ .addComponent(nameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(templateScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 220, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(errorLabel)
+ .addContainerGap())
+ );
+ }// //GEN-END:initComponents
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JLabel errorLabel;
+ private javax.swing.JLabel nameLabel;
+ private javax.swing.JTextField nameTextField;
+ private javax.swing.JEditorPane templateEditorPane;
+ private javax.swing.JScrollPane templateScrollPane;
+ // End of variables declaration//GEN-END:variables
+
+ private class DefaultDocumentListener implements DocumentListener {
+
+ public DefaultDocumentListener() {
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ processUpdate();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ processUpdate();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ processUpdate();
+ }
+
+ private void processUpdate() {
+ fireChange();
+ }
+ }
+}
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/options/BacklogOptions.java b/src/main/java/com/junichi11/netbeans/modules/backlog/options/BacklogOptions.java
index 62416d4..40b0e5b 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/options/BacklogOptions.java
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/options/BacklogOptions.java
@@ -55,6 +55,7 @@ public final class BacklogOptions {
private static final String ASSIGNED_TO_ME = "assigned.to.me"; // NOI18N
private static final String CREATED_BY_ME = "created.by.me"; // NOI18N
private static final String QUERY_MAX_ISSUE_COUNT = "query.max.issue.count"; // NOI18N
+ private static final String NOTIFICATIONS = "notifications"; // NOI18N
private static final BacklogOptions INSTANCE = new BacklogOptions();
private BacklogOptions() {
@@ -88,6 +89,14 @@ public void setMaxIssueCountForDefaultQuery(int value) {
getPreferences().putInt(QUERY_MAX_ISSUE_COUNT, value);
}
+ public void setNotificationsQuery(boolean isEnabled) {
+ getPreferences().putBoolean(NOTIFICATIONS, isEnabled);
+ }
+
+ public boolean isNotificationsQuery() {
+ return getPreferences().getBoolean(NOTIFICATIONS, false);
+ }
+
private Preferences getPreferences() {
return NbPreferences.forModule(BacklogOptions.class).node(PREFERENCES_PATH);
}
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/options/BacklogOptionsPanel.form b/src/main/java/com/junichi11/netbeans/modules/backlog/options/BacklogOptionsPanel.form
index 3fadc40..a9ccd32 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/options/BacklogOptionsPanel.form
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/options/BacklogOptionsPanel.form
@@ -29,6 +29,7 @@
+
@@ -48,6 +49,8 @@
+
+
@@ -75,6 +78,13 @@
+
+
+
+
+
+
+
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/options/BacklogOptionsPanel.java b/src/main/java/com/junichi11/netbeans/modules/backlog/options/BacklogOptionsPanel.java
index ca3288b..c1438ea 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/options/BacklogOptionsPanel.java
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/options/BacklogOptionsPanel.java
@@ -55,6 +55,9 @@ final class BacklogOptionsPanel extends javax.swing.JPanel {
initComponents();
maxIssueSpinnerNumberModel = new SpinnerNumberModel(20, 20, 500, 10);
maxIssueCountSpinner.setModel(maxIssueSpinnerNumberModel);
+
+ // XXX another way should be used
+ notificationsCheckBox.setVisible(false);
}
/**
@@ -69,6 +72,7 @@ private void initComponents() {
assignedToMeCheckBox = new javax.swing.JCheckBox();
createdByMeCheckBox = new javax.swing.JCheckBox();
maxIssueCountSpinner = new javax.swing.JSpinner();
+ notificationsCheckBox = new javax.swing.JCheckBox();
org.openide.awt.Mnemonics.setLocalizedText(defaultQueriesLabel, org.openide.util.NbBundle.getMessage(BacklogOptionsPanel.class, "BacklogOptionsPanel.defaultQueriesLabel.text")); // NOI18N
@@ -77,6 +81,7 @@ private void initComponents() {
org.openide.awt.Mnemonics.setLocalizedText(createdByMeCheckBox, org.openide.util.NbBundle.getMessage(BacklogOptionsPanel.class, "BacklogOptionsPanel.createdByMeCheckBox.text")); // NOI18N
maxIssueCountSpinner.setToolTipText(org.openide.util.NbBundle.getMessage(BacklogOptionsPanel.class, "BacklogOptionsPanel.maxIssueCountSpinner.toolTipText")); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(notificationsCheckBox, org.openide.util.NbBundle.getMessage(BacklogOptionsPanel.class, "BacklogOptionsPanel.notificationsCheckBox.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
@@ -93,7 +98,8 @@ private void initComponents() {
.addGap(12, 12, 12)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(createdByMeCheckBox)
- .addComponent(assignedToMeCheckBox))))
+ .addComponent(assignedToMeCheckBox)
+ .addComponent(notificationsCheckBox))))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
@@ -107,6 +113,8 @@ private void initComponents() {
.addComponent(assignedToMeCheckBox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(createdByMeCheckBox)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(notificationsCheckBox)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
}// //GEN-END:initComponents
@@ -116,6 +124,7 @@ void load() {
assignedToMeCheckBox.setSelected(options.isAssignedToMeQuery());
createdByMeCheckBox.setSelected(options.isCreatedByMeQuery());
setMaxIssueCount(options.getMaxIssueCountForDefaultQuery());
+ notificationsCheckBox.setSelected(options.isNotificationsQuery());
}
void store() {
@@ -123,6 +132,7 @@ void store() {
options.setAssignedToMeQuery(isAssignedToMeQuery());
options.setCreatedByMeQuery(isCreatedByMeQuery());
options.setMaxIssueCountForDefaultQuery(getMaxIssueCount());
+ options.setNotificationsQuery(isNotificationsQuery());
}
boolean valid() {
@@ -146,10 +156,15 @@ private void setMaxIssueCount(int count) {
maxIssueSpinnerNumberModel.setValue(count);
}
+ private boolean isNotificationsQuery() {
+ return notificationsCheckBox.isSelected();
+ }
+
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox assignedToMeCheckBox;
private javax.swing.JCheckBox createdByMeCheckBox;
private javax.swing.JLabel defaultQueriesLabel;
private javax.swing.JSpinner maxIssueCountSpinner;
+ private javax.swing.JCheckBox notificationsCheckBox;
// End of variables declaration//GEN-END:variables
}
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/query/BacklogQuery.java b/src/main/java/com/junichi11/netbeans/modules/backlog/query/BacklogQuery.java
index d6c07f3..6636296 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/query/BacklogQuery.java
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/query/BacklogQuery.java
@@ -135,14 +135,16 @@ public void setSaved(boolean isSaved) {
/**
* Get all BacklogIssues for this query.
*
+ * @param isRefresh {@code true} if clear an issue cache, otherwise
+ * {@code false}
* @return BacklogIssues
*/
- public Collection getAllIssues() {
+ public Collection getAllIssues(boolean isRefresh) {
GetIssuesParams issuesParams = createGetIssuesParams();
if (issuesParams == null) {
return Collections.emptyList();
}
- return getAllIssues(getGetIssuesParams(issuesParams), getMaxIssueCount());
+ return getAllIssues(getGetIssuesParams(issuesParams), getMaxIssueCount(), isRefresh);
}
/**
@@ -151,18 +153,20 @@ public Collection getAllIssues() {
* @param issuesParams GetIssuesParams
* @return BacklogIssues
*/
- public Collection getAllIssues(GetIssuesParams issuesParams, int maxIssueCount) {
- return repository.getIssues(issuesParams, maxIssueCount, true);
+ public Collection getAllIssues(GetIssuesParams issuesParams, int maxIssueCount, boolean isRefresh) {
+ return repository.getIssues(issuesParams, maxIssueCount, true, isRefresh);
}
/**
* Get BacklogIssues for GetIssuesParams.
*
* @param issuesParams GetIssuesParams
+ * @param isRefresh {@code true} if clear an issue cache, otherwise
+ * {@code false}
* @return BacklogIssues
*/
- public Collection getIssues(GetIssuesParams issuesParams) {
- return repository.getIssues(issuesParams, 100, false);
+ public Collection getIssues(GetIssuesParams issuesParams, boolean isRefresh) {
+ return repository.getIssues(issuesParams, 100, false, isRefresh);
}
/**
@@ -547,7 +551,7 @@ public void refresh() {
if (issueContainer != null) {
issueContainer.refreshingStarted();
issueContainer.clear();
- for (BacklogIssue issue : getAllIssues()) {
+ for (BacklogIssue issue : getAllIssues(true)) {
issueContainer.add(issue);
}
}
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/query/BacklogQueryController.java b/src/main/java/com/junichi11/netbeans/modules/backlog/query/BacklogQueryController.java
index dcb1157..1d6d025 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/query/BacklogQueryController.java
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/query/BacklogQueryController.java
@@ -211,11 +211,11 @@ public void run() {
ProgressHandle handle = ProgressHandleFactory.createHandle(
Bundle.BacklogQueryController_label_searching_issues(),
new Cancellable() {
- @Override
- public boolean cancel() {
- return isCancel.getAndSet(true);
- }
- }
+ @Override
+ public boolean cancel() {
+ return isCancel.getAndSet(true);
+ }
+ }
);
final int maxIssueCount = getPanel().getMaxIssueCount();
@@ -235,7 +235,7 @@ public boolean cancel() {
GetIssuesParams clonedParams = support.newGetIssuesParams()
.count(count)
.offset(i * count);
- issues.addAll(query.getIssues(clonedParams));
+ issues.addAll(query.getIssues(clonedParams, false));
handle.progress(i + 1);
}
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/query/NotificationsQuery.java b/src/main/java/com/junichi11/netbeans/modules/backlog/query/NotificationsQuery.java
new file mode 100644
index 0000000..b96974e
--- /dev/null
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/query/NotificationsQuery.java
@@ -0,0 +1,234 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2015 Sun Microsystems, Inc.
+ */
+package com.junichi11.netbeans.modules.backlog.query;
+
+import com.junichi11.netbeans.modules.backlog.BacklogConnector;
+import com.junichi11.netbeans.modules.backlog.BacklogData;
+import com.junichi11.netbeans.modules.backlog.issue.BacklogIssue;
+import com.junichi11.netbeans.modules.backlog.query.ui.NotificationPanel;
+import com.junichi11.netbeans.modules.backlog.repository.BacklogRepository;
+import com.junichi11.netbeans.modules.backlog.utils.UiUtils;
+import com.nulabinc.backlog4j.Issue;
+import com.nulabinc.backlog4j.IssueComment;
+import com.nulabinc.backlog4j.Notification;
+import com.nulabinc.backlog4j.Project;
+import com.nulabinc.backlog4j.User;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.swing.Icon;
+import javax.swing.JLabel;
+import javax.swing.SwingUtilities;
+import org.netbeans.modules.bugtracking.api.Repository;
+import org.netbeans.modules.bugtracking.api.RepositoryManager;
+import org.netbeans.modules.bugtracking.api.Util;
+import org.netbeans.modules.bugtracking.spi.IssueStatusProvider.Status;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.openide.awt.NotificationDisplayer;
+import org.openide.awt.NotificationDisplayer.Priority;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author junichi11
+ */
+public final class NotificationsQuery extends BacklogQuery implements DefaultQuery {
+
+ private final List notifications = Collections.synchronizedList(new ArrayList());
+ private final Set alreadyNotifiedIds = Collections.synchronizedSet(new HashSet());
+
+ public NotificationsQuery(BacklogRepository repository) {
+ super(repository);
+ }
+
+ @NbBundle.Messages("NotificationsQuery.displayName=Notifications")
+ @Override
+ public String getDisplayName() {
+ return Bundle.NotificationsQuery_displayName();
+ }
+
+ @Override
+ public String getTooltip() {
+ return getDisplayName();
+ }
+
+ @Override
+ @NbBundle.Messages({
+ "# {0} - content",
+ "NotificationsQuery.notification.comment=Comment: {0}",
+ "NotificationsQuery.notification.marked.all.as.read=Marked all notifications as read."
+ })
+ public Collection getAllIssues(boolean isRefresh) {
+ final BacklogRepository repository = getRepository();
+ List issues = new ArrayList<>();
+ if (isRefresh) {
+ notifications.clear();
+ notifications.addAll(repository.getNotifications());
+ }
+ for (final Notification notification : notifications) {
+ if (notification.isAlreadyRead() || notification.isResourceAlreadyRead()) {
+ continue;
+ }
+ final BacklogIssue issue = repository.getIssue(notification, isRefresh);
+ if (!issues.contains(issue)) {
+ Status status = issue.getStatus();
+ if (status == Status.SEEN) {
+ issue.setStatus(Status.INCOMING_MODIFIED);
+ }
+ issues.add(issue);
+ }
+ // icon
+ BacklogData data = BacklogData.create(repository);
+ Icon senderIcon = data.getUserIcon(notification.getSender());
+
+ IssueComment comment = notification.getComment();
+ String id = String.valueOf(notification.getId());
+ if (!alreadyNotifiedIds.contains(id)) {
+ // show notification
+ alreadyNotifiedIds.add(id);
+ NotificationPanel notificationPanel = new NotificationPanel(comment.getContent(), notification.getId());
+ notificationPanel.addPropertyChangeListener(new PropertyChangeListener() {
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (evt.getPropertyName().equals(NotificationPanel.PROPERTY_MARK_AS_READ)) {
+ Repository repo = RepositoryManager.getInstance().getRepository(BacklogConnector.ID, repository.getID());
+ Util.openIssue(repo, issue.getKeyId());
+ repository.markAsReadNotification(notification.getId());
+ } else if (evt.getPropertyName().equals(NotificationPanel.PROPERTY_MARK_ALL_AS_READ)) {
+ repository.resetNotificationCount();
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(Bundle.NotificationsQuery_notification_marked_all_as_read()));
+ }
+ });
+ }
+ }
+ });
+ NotificationDisplayer.getDefault().notify(
+ getTitle(notification),
+ senderIcon,
+ new JLabel(getTitle(notification)),
+ notificationPanel,
+ Priority.NORMAL,
+ NotificationDisplayer.Category.INFO
+ );
+ }
+ }
+ return issues;
+ }
+
+ @NbBundle.Messages({
+ "# {0} - title",
+ "NotificationsQuery.notification.title=Backlog: {0}",
+ "NotificationsQuery.reason.assigned=Assigned",
+ "NotificationsQuery.reason.commented=Commented",
+ "NotificationsQuery.reason.fileAttached=File Attached",
+ "NotificationsQuery.reason.issueCreated=Issue Created",
+ "NotificationsQuery.reason.issueUpdated=Issue Updated ",
+ "NotificationsQuery.reason.projectUserAdded=Project User Added",
+ "NotificationsQuery.reason.other=Other",
+ "# {0} - sender",
+ "NotificationsQuery.notification.sender=Sender:{0}"
+ })
+ private static String getTitle(Notification notification) {
+ StringBuilder sb = new StringBuilder();
+ Project project = notification.getProject();
+ Issue issue = notification.getIssue();
+ sb.append(project.getProjectKey()).append("-").append(issue.getKeyId()).append(" "); // NOI18N
+ Notification.Reason reason = notification.getReason();
+ switch (reason) {
+ case Assigned:
+ sb.append(Bundle.NotificationsQuery_reason_assigned());
+ break;
+ case Commented:
+ sb.append(Bundle.NotificationsQuery_reason_commented());
+ break;
+ case FileAttached:
+ sb.append(Bundle.NotificationsQuery_reason_fileAttached());
+ break;
+ case IssueCreated:
+ sb.append(Bundle.NotificationsQuery_reason_issueCreated());
+ break;
+ case IssueUpdated:
+ sb.append(Bundle.NotificationsQuery_reason_issueUpdated());
+ break;
+ case ProjectUserAdded:
+ sb.append(Bundle.NotificationsQuery_reason_projectUserAdded());
+ break;
+ case Other:
+ sb.append(Bundle.NotificationsQuery_reason_other());
+ break;
+ default:
+ throw new AssertionError();
+ }
+ User sender = notification.getSender();
+ if (sender != null) {
+ sb.append(" ").append(Bundle.NotificationsQuery_notification_sender(sender.getName())); // NOI18N
+ }
+ return Bundle.NotificationsQuery_notification_title(sb.toString());
+ }
+
+ @Override
+ public boolean canRename() {
+ return false;
+ }
+
+ @Override
+ public boolean canRemove() {
+ // XXX delete action is not set to disable
+ return false;
+ }
+
+ @Override
+ public void remove() {
+ // XXX delete action is not set to disable
+ UiUtils.showOptions();
+ }
+
+}
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/query/ui/Bundle.properties b/src/main/java/com/junichi11/netbeans/modules/backlog/query/ui/Bundle.properties
index a40f1b5..f133498 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/query/ui/Bundle.properties
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/query/ui/Bundle.properties
@@ -30,3 +30,6 @@ DatePanel.startDateLabel.text=Start date
DatePanel.startDateHyphenLabel.text=-
BacklogQueryPanel.issueCountLabel.text=ISSUE COUNT
BacklogQueryPanel.maxIssueCountSpinner.toolTipText=Maximum count
+NotificationPanel.commentLabel.text=Comment
+NotificationPanel.markAllAsReadButton.text=Mark All As Read
+NotificationPanel.markAsReadButton.text=Mark As Read & Open
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/query/ui/GeneralPanel.java b/src/main/java/com/junichi11/netbeans/modules/backlog/query/ui/GeneralPanel.java
index 7468c24..6689cba 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/query/ui/GeneralPanel.java
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/query/ui/GeneralPanel.java
@@ -170,8 +170,8 @@ private void setAttributes() {
setCategory(data.getCategories());
setVersion(data.getVersions());
setMilestone(data.getVersions());
- setAssignee(data.getUsers());
- setResisteredBy(data.getUsers());
+ setAssignee(data.getProjectUsers());
+ setResisteredBy(data.getProjectUsers());
setResolution(data.getResolutions());
setFile();
}
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/query/ui/NotificationPanel.form b/src/main/java/com/junichi11/netbeans/modules/backlog/query/ui/NotificationPanel.form
new file mode 100644
index 0000000..a900052
--- /dev/null
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/query/ui/NotificationPanel.form
@@ -0,0 +1,89 @@
+
+
+
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/query/ui/NotificationPanel.java b/src/main/java/com/junichi11/netbeans/modules/backlog/query/ui/NotificationPanel.java
new file mode 100644
index 0000000..55e47d6
--- /dev/null
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/query/ui/NotificationPanel.java
@@ -0,0 +1,164 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2015 Sun Microsystems, Inc.
+ */
+package com.junichi11.netbeans.modules.backlog.query.ui;
+
+/**
+ *
+ * @author junichi11
+ */
+public class NotificationPanel extends javax.swing.JPanel {
+
+ public static final String PROPERTY_MARK_AS_READ = "notification.mark.as.read"; // NOI18N
+ public static final String PROPERTY_MARK_ALL_AS_READ = "notification.mark.all.as.read"; // NOI18N
+ private final String comment;
+ private final long id;
+
+ /**
+ * Creates new form NotificationPanel
+ */
+ public NotificationPanel() {
+ this("", -1); // NOI18N
+ }
+
+ public NotificationPanel(String comment, long id) {
+ this.comment = comment;
+ this.id = id;
+ initComponents();
+ setComment(comment);
+ }
+
+ public long getNotificationId() {
+ return id;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ private void setComment(String comment) {
+ commentTextPane.setText(comment);
+ }
+
+ void markAsReadPressed() {
+ firePropertyChange(PROPERTY_MARK_AS_READ, null, null);
+ }
+
+ void markAllAsReadPressed() {
+ firePropertyChange(PROPERTY_MARK_ALL_AS_READ, null, null);
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ markAsReadButton = new javax.swing.JButton();
+ markAllAsReadButton = new javax.swing.JButton();
+ commentLabel = new javax.swing.JLabel();
+ commentScrollPane = new javax.swing.JScrollPane();
+ commentTextPane = new javax.swing.JTextPane();
+
+ setOpaque(false);
+
+ org.openide.awt.Mnemonics.setLocalizedText(markAsReadButton, org.openide.util.NbBundle.getMessage(NotificationPanel.class, "NotificationPanel.markAsReadButton.text")); // NOI18N
+ markAsReadButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ markAsReadButtonActionPerformed(evt);
+ }
+ });
+
+ org.openide.awt.Mnemonics.setLocalizedText(markAllAsReadButton, org.openide.util.NbBundle.getMessage(NotificationPanel.class, "NotificationPanel.markAllAsReadButton.text")); // NOI18N
+ markAllAsReadButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ markAllAsReadButtonActionPerformed(evt);
+ }
+ });
+
+ org.openide.awt.Mnemonics.setLocalizedText(commentLabel, org.openide.util.NbBundle.getMessage(NotificationPanel.class, "NotificationPanel.commentLabel.text")); // NOI18N
+
+ commentTextPane.setEditable(false);
+ commentScrollPane.setViewportView(commentTextPane);
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(markAsReadButton)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(markAllAsReadButton))
+ .addComponent(commentLabel)
+ .addComponent(commentScrollPane)
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(commentLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(commentScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 51, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(markAsReadButton)
+ .addComponent(markAllAsReadButton)))
+ );
+ }// //GEN-END:initComponents
+
+ private void markAsReadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_markAsReadButtonActionPerformed
+ markAsReadPressed();
+ }//GEN-LAST:event_markAsReadButtonActionPerformed
+
+ private void markAllAsReadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_markAllAsReadButtonActionPerformed
+ markAllAsReadPressed();
+ }//GEN-LAST:event_markAllAsReadButtonActionPerformed
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JLabel commentLabel;
+ private javax.swing.JScrollPane commentScrollPane;
+ private javax.swing.JTextPane commentTextPane;
+ private javax.swing.JButton markAllAsReadButton;
+ private javax.swing.JButton markAsReadButton;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/repository/BacklogRepository.java b/src/main/java/com/junichi11/netbeans/modules/backlog/repository/BacklogRepository.java
index 573ebf1..6bba926 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/repository/BacklogRepository.java
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/repository/BacklogRepository.java
@@ -49,6 +49,7 @@
import com.junichi11.netbeans.modules.backlog.query.BacklogQuery;
import com.junichi11.netbeans.modules.backlog.query.CreatedByMeQuery;
import com.junichi11.netbeans.modules.backlog.query.DefaultQuery;
+import com.junichi11.netbeans.modules.backlog.query.NotificationsQuery;
import com.junichi11.netbeans.modules.backlog.query.GetIssuesParamsSupport;
import com.junichi11.netbeans.modules.backlog.utils.BacklogImage;
import com.junichi11.netbeans.modules.backlog.utils.BacklogUtils;
@@ -57,6 +58,7 @@
import com.nulabinc.backlog4j.BacklogClient;
import com.nulabinc.backlog4j.BacklogClientFactory;
import com.nulabinc.backlog4j.Issue;
+import com.nulabinc.backlog4j.Notification;
import com.nulabinc.backlog4j.Project;
import com.nulabinc.backlog4j.ResponseList;
import com.nulabinc.backlog4j.api.option.GetIssuesCountParams;
@@ -80,6 +82,7 @@
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.modules.bugtracking.api.Repository;
import org.netbeans.modules.bugtracking.api.RepositoryManager;
import org.netbeans.modules.bugtracking.api.Util;
@@ -114,7 +117,9 @@ public final class BacklogRepository {
// default queries
private BacklogQuery assignedToMeQuery;
private BacklogQuery createdByMeQuery;
+ private final BacklogQuery notificationQuery = new NotificationsQuery(this);
+ // key id, issue
private final Map issueCache = Collections.synchronizedMap(new HashMap());
// XXX for subtask
private BacklogIssue subtaskParentIssue;
@@ -233,11 +238,15 @@ public BacklogIssue createIssue(String summary, String description) {
* @param issue an issue
* @return backlog issue
*/
- public synchronized BacklogIssue createIssue(Issue issue) {
+ public synchronized BacklogIssue createIssue(Issue issue, boolean isRefresh) {
// use cache
String keyId = String.valueOf(issue.getKeyId());
BacklogIssue backlogIssue = issueCache.get(keyId);
if (backlogIssue != null) {
+ // #27
+ if (isRefresh) {
+ backlogIssue.refreshIssue(issue);
+ }
return backlogIssue;
}
backlogIssue = new BacklogIssue(this, issue);
@@ -295,7 +304,7 @@ public List getIssues(String... keyIds) {
try {
Issue issue = client.getIssue(issueKey);
if (issue != null) {
- backlogIssue = createIssue(issue);
+ backlogIssue = createIssue(issue, false);
backlogIssues.add(backlogIssue);
}
} catch (BacklogAPIException ex) {
@@ -309,10 +318,12 @@ public List getIssues(String... keyIds) {
* Get BacklogIssues.
*
* @param issuesParams GetIssuesParams
+ * @param isRefresh {@code true} if clear an issue cache, otherwise
+ * {@code false}
* @return BacklogIssues
*/
- public Collection getIssues(GetIssuesParams issuesParams) {
- return getIssues(issuesParams, 100, false);
+ public Collection getIssues(GetIssuesParams issuesParams, boolean isRefresh) {
+ return getIssues(issuesParams, 100, false, isRefresh);
}
/**
@@ -321,8 +332,8 @@ public Collection getIssues(GetIssuesParams issuesParams) {
* @param issuesParams GetIssuesParams
* @return BacklogIssues
*/
- public Collection getAllIssues(GetIssuesParams issuesParams, int maxCount) {
- return getIssues(issuesParams, maxCount, true);
+ public Collection getAllIssues(GetIssuesParams issuesParams, int maxCount, boolean isRefresh) {
+ return getIssues(issuesParams, maxCount, true, isRefresh);
}
/**
@@ -331,7 +342,7 @@ public Collection getAllIssues(GetIssuesParams issuesParams, int m
* @param issuesParams GetIssuesParams
* @return BacklogIssues
*/
- public Collection getIssues(GetIssuesParams issuesParams, int maxCount, boolean isAll) {
+ public Collection getIssues(GetIssuesParams issuesParams, int maxCount, boolean isAll, boolean isRefresh) {
Project p = getProject();
if (p == null || issuesParams == null) {
return Collections.emptyList();
@@ -356,7 +367,7 @@ public Collection getIssues(GetIssuesParams issuesParams, int maxC
}
ResponseList issues = backlogClient.getIssues(issuesParams);
for (Issue issue : issues) {
- backlogIssues.add(createIssue(issue));
+ backlogIssues.add(createIssue(issue, isRefresh));
if (++total == maxCount) {
break;
}
@@ -372,6 +383,83 @@ public Collection getIssues(GetIssuesParams issuesParams, int maxC
return backlogIssues;
}
+ /**
+ * Get Notifications.
+ *
+ * @return Notifications
+ */
+ public Collection getNotifications() {
+ Project p = getProject();
+ if (p == null) {
+ return Collections.emptyList();
+ }
+ BacklogClient backlogClient = createBacklogClient();
+ if (backlogClient == null) {
+ return Collections.emptyList();
+ }
+ List notifications = new ArrayList<>();
+ try {
+ ResponseList responses = backlogClient.getNotifications();
+ notifications.addAll(responses);
+ } catch (BacklogAPIException ex) {
+ LOGGER.log(Level.INFO, ex.getMessage());
+ }
+ return notifications;
+ }
+
+ /**
+ * Mark as read a notification.
+ *
+ * @param id notification identifer
+ */
+ public void markAsReadNotification(long id) {
+ Project p = getProject();
+ if (p == null) {
+ return;
+ }
+ BacklogClient backlogClient = createBacklogClient();
+ if (backlogClient == null) {
+ return;
+ }
+ try {
+ backlogClient.markAsReadNotification(id);
+ } catch (BacklogAPIException ex) {
+ LOGGER.log(Level.INFO, ex.getMessage());
+ }
+ }
+
+ /**
+ * Reset notification count.
+ *
+ */
+ public void resetNotificationCount() {
+ Project p = getProject();
+ if (p == null) {
+ return;
+ }
+ BacklogClient backlogClient = createBacklogClient();
+ if (backlogClient == null) {
+ return;
+ }
+ try {
+ backlogClient.resetNotificationCount();
+ } catch (BacklogAPIException ex) {
+ LOGGER.log(Level.INFO, ex.getMessage());
+ }
+ }
+
+ /**
+ * Get BacklogIssue for Notification.
+ *
+ * @param notification Notification
+ * @param isRefresh
+ * @return BacklogIssue
+ */
+ public BacklogIssue getIssue(@NonNull Notification notification, boolean isRefresh) {
+ Issue issue = notification.getIssue();
+ return createIssue(issue, isRefresh);
+ }
+
/**
* Get an issue count.
*
@@ -421,7 +509,7 @@ public BacklogIssue getIssue(String issueKey) {
try {
Issue issue = backlogClient.getIssue(issueKey);
if (issue != null) {
- return createIssue(issue);
+ return createIssue(issue, false);
}
} catch (BacklogAPIException ex) {
LOGGER.log(Level.INFO, ex.getMessage());
@@ -444,7 +532,7 @@ public BacklogIssue getIssue(long issueId) {
try {
Issue issue = backlogClient.getIssue(issueId);
if (issue != null) {
- return createIssue(issue);
+ return createIssue(issue, false);
}
} catch (BacklogAPIException ex) {
LOGGER.log(Level.INFO, ex.getMessage());
@@ -503,7 +591,7 @@ public List getBacklogSubissues(BacklogIssue parentIssue) {
List subissues = getSubissues(parentIssue);
ArrayList backlogSubissues = new ArrayList<>(subissues.size());
for (Issue subissue : subissues) {
- backlogSubissues.add(createIssue(subissue));
+ backlogSubissues.add(createIssue(subissue, false));
}
return backlogSubissues;
}
@@ -564,6 +652,9 @@ public Collection getQueries() {
if (options.isCreatedByMeQuery()) {
addQuery(getCreatedByMeQuery());
}
+ if (options.isNotificationsQuery()) {
+ addQuery(getNotificationQuery());
+ }
// add user queries
String[] queryNames = BacklogConfig.getInstance().getQueryNames(this);
@@ -628,6 +719,7 @@ public void optionsChanged() {
BacklogOptions options = BacklogOptions.getInstance();
setDefaultQuery(getAssignedToMeQuery(), options.isAssignedToMeQuery());
setDefaultQuery(getCreatedByMeQuery(), options.isCreatedByMeQuery());
+ setDefaultQuery(getNotificationQuery(), options.isNotificationsQuery());
fireQueryListChanged();
}
@@ -636,10 +728,8 @@ private void setDefaultQuery(BacklogQuery query, boolean isEnabled) {
if (!getQueries().contains(query)) {
getQueries().add(query);
}
- } else {
- if (getQueries().contains(query)) {
- getQueries().remove(query);
- }
+ } else if (getQueries().contains(query)) {
+ getQueries().remove(query);
}
}
@@ -704,7 +794,7 @@ public Collection simpleSearch(String criteria) {
GetIssuesParams issuesParams;
issuesParams = new GetIssuesParams(Collections.singletonList(p.getId()))
.keyword(criteria);
- issues.addAll(getIssues(issuesParams));
+ issues.addAll(getIssues(issuesParams, false));
return issues;
}
@@ -857,6 +947,15 @@ private BacklogQuery getCreatedByMeQuery() {
return createdByMeQuery;
}
+ /**
+ * Get NotificationQuery.
+ *
+ * @return CreatedByMeQuery
+ */
+ private BacklogQuery getNotificationQuery() {
+ return notificationQuery;
+ }
+
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/repository/ui/BacklogRepositoryPanel.form b/src/main/java/com/junichi11/netbeans/modules/backlog/repository/ui/BacklogRepositoryPanel.form
index 7ba35bd..001c0c8 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/repository/ui/BacklogRepositoryPanel.form
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/repository/ui/BacklogRepositoryPanel.form
@@ -16,44 +16,37 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
@@ -83,7 +76,6 @@
-
diff --git a/src/main/java/com/junichi11/netbeans/modules/backlog/repository/ui/BacklogRepositoryPanel.java b/src/main/java/com/junichi11/netbeans/modules/backlog/repository/ui/BacklogRepositoryPanel.java
index 22d8b45..f4a5e54 100644
--- a/src/main/java/com/junichi11/netbeans/modules/backlog/repository/ui/BacklogRepositoryPanel.java
+++ b/src/main/java/com/junichi11/netbeans/modules/backlog/repository/ui/BacklogRepositoryPanel.java
@@ -274,36 +274,31 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addGap(0, 0, Short.MAX_VALUE)
+ .addComponent(setDisplayNameButton)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(connectButton))
.addGroup(layout.createSequentialGroup()
- .addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
- .addGap(0, 0, Short.MAX_VALUE)
- .addComponent(setDisplayNameButton)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(connectButton))
+ .addComponent(spaceIdLabel)
+ .addComponent(apiKeyLabel)
+ .addComponent(nameLabel)
+ .addComponent(projectLabel)
+ .addComponent(backlogLabel))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(spaceIdLabel)
- .addComponent(apiKeyLabel)
- .addComponent(nameLabel)
- .addComponent(projectLabel)
- .addComponent(backlogLabel))
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(layout.createSequentialGroup()
- .addComponent(backlogComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addGap(0, 0, Short.MAX_VALUE))
- .addComponent(spaceIdTextField, javax.swing.GroupLayout.Alignment.TRAILING)
- .addComponent(apiKeyTextField)
- .addComponent(nameTextField)
- .addComponent(projectComboBox, 0, 452, Short.MAX_VALUE))))
- .addContainerGap())
+ .addComponent(backlogComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(0, 0, Short.MAX_VALUE))
+ .addComponent(spaceIdTextField, javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(apiKeyTextField)
+ .addComponent(nameTextField)
+ .addComponent(projectComboBox, 0, 476, Short.MAX_VALUE)))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
- .addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(backlogLabel)
.addComponent(backlogComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
@@ -326,8 +321,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(nameLabel)
- .addComponent(nameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
- .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addComponent(nameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
);
}// //GEN-END:initComponents
diff --git a/src/main/nbm/manifest.mf b/src/main/nbm/manifest.mf
index ec33a6f..046f258 100644
--- a/src/main/nbm/manifest.mf
+++ b/src/main/nbm/manifest.mf
@@ -1,3 +1,2 @@
Manifest-Version: 1.0
-OpenIDE-Module-Install: com/junichi11/netbeans/modules/backlog/Installer.class
OpenIDE-Module-Localizing-Bundle: com/junichi11/netbeans/modules/backlog/Bundle.properties
diff --git a/src/main/resources/com/junichi11/netbeans/modules/backlog/Bundle_ja_JP.properties b/src/main/resources/com/junichi11/netbeans/modules/backlog/Bundle_ja_JP.properties
index f0a3efa..20b708f 100644
--- a/src/main/resources/com/junichi11/netbeans/modules/backlog/Bundle_ja_JP.properties
+++ b/src/main/resources/com/junichi11/netbeans/modules/backlog/Bundle_ja_JP.properties
@@ -43,3 +43,6 @@
#OpenIDE-Module-Short-Description=
#OpenIDE-Module-Long-Description=
OpenIDE-Module-Display-Category=\u30d9\u30fc\u30b9IDE
+
+# BacklogConfig
+BacklogConfig.default.template=#### \u6982\u8981\u8aac\u660e\n\n#### \u518d\u73fe\u624b\u9806\n\n1. \n2. \n3. \n\n#### \u5b9f\u969b\u306e\u7d50\u679c\n\n#### \u671f\u5f85\u3055\u308c\u308b\u7d50\u679c\n
diff --git a/src/main/resources/com/junichi11/netbeans/modules/backlog/issue/ui/Bundle_ja_JP.properties b/src/main/resources/com/junichi11/netbeans/modules/backlog/issue/ui/Bundle_ja_JP.properties
index 1c55da0..1f19ede 100644
--- a/src/main/resources/com/junichi11/netbeans/modules/backlog/issue/ui/Bundle_ja_JP.properties
+++ b/src/main/resources/com/junichi11/netbeans/modules/backlog/issue/ui/Bundle_ja_JP.properties
@@ -70,6 +70,8 @@ BacklogIssuePanel.header.new.subtask={0}\u306e\u65b0\u898f\u5b50\u8ab2\u984c
BacklogIssuePanel.headerCreatedByLabel.text=\u767b\u9332\u8005:
BacklogIssuePanel.headerCreatedDateLabel.text=-
BacklogIssuePanel.headerCreatedLabel.text=\u767b\u9332\u65e5:
+BacklogIssuePanel.headerUpdatedDateLabel.text=-
+BacklogIssuePanel.headerUpdatedLabel.text=\u66f4\u65b0\u65e5:
BacklogIssuePanel.headerCreatedUserLinkButton.text=-
BacklogIssuePanel.headerDueDateLabel.text=\u671f\u9650\u65e5:
BacklogIssuePanel.headerDueDateViewLabel.text=-
@@ -89,6 +91,8 @@ BacklogIssuePanel.message.update.issue.fail=\u8ab2\u984c\u3092\u66f4\u65b0\u3067
BacklogIssuePanel.message.update.issue.success=\u8ab2\u984c\u3092\u66f4\u65b0\u3057\u307e\u3057\u305f
BacklogIssuePanel.message.uploading.attachments=\u30d5\u30a1\u30a4\u30eb\u3092\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u4e2d...
BacklogIssuePanel.milestoneLabel.text=\u30de\u30a4\u30eb\u30b9\u30c8\u30fc\u30f3:
+BacklogIssuePanel.no.notification.user=\u304a\u77e5\u3089\u305b\u3067\u304d\u308b\u30e6\u30fc\u30b6\u306f\u3044\u307e\u305b\u3093\u3002
+BacklogIssuePanel.notificationLabel.text=\u304a\u77e5\u3089\u305b:
BacklogIssuePanel.priorityLabel.text=\u512a\u5148\u5ea6*:
BacklogIssuePanel.refreshLinkButton.text=\u30ea\u30d5\u30ec\u30c3\u30b7\u30e5
BacklogIssuePanel.resolutionLabel.text=\u5b8c\u4e86\u7406\u7531:
@@ -104,6 +108,28 @@ BacklogIssuePanel.summaryLabel.text=\u4ef6\u540d*:
BacklogIssuePanel.summaryTextField.text=
BacklogIssuePanel.typeLabel.text=\u7a2e\u5225*:
BacklogIssuePanel.versionLabel.text=\u767a\u751f\u30d0\u30fc\u30b8\u30e7\u30f3:
+BacklogIssuePanel.insertTemplateButton.toolTipText=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u633f\u5165
+BacklogIssuePanel.insertTemplateButton.text=
+BacklogIssuePanel.insert.template.title=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u633f\u5165
+BacklogIssuePanel.manageTemplatesButton.toolTipText=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u7ba1\u7406
+BacklogIssuePanel.manageTemplatesButton.text=
+BacklogIssuePanel.manage.templates.title=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u7ba1\u7406
+BacklogIssuePanel.manage.templates.add.option=\u8ffd\u52a0
+BacklogIssuePanel.manage.templates.remove.option=\u524a\u9664
+BacklogIssuePanel.manage.templates.edit.option=\u7de8\u96c6
+BacklogIssuePanel.manage.templates.duplicate.option=\u8907\u88fd
+BacklogIssuePanel.manage.templates.close.option=\u9589\u3058\u308b
+InsertTemplatePanel.templatesComboBox.toolTipText=
+TemplatePanel.nameLabel.text=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u540d:
+TemplatePanel.nameTextField.text=
+TemplatePanel.errorLabel.text=ERROR
+
+ManageTemplateButtonListener.add.title=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u8ffd\u52a0
+ManageTemplateButtonListener.edit.title=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u7de8\u96c6
+ManageTemplateButtonListener.duplicate.title=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u8907\u88fd
+ManageTemplateButtonListener.remove.message={0}\u3092\u672c\u5f53\u306b\u524a\u9664\u3057\u307e\u3059\u304b?\n(default\u306e\u5834\u5408\u306f\u524a\u9664\u3055\u308c\u305a\u306b\u521d\u671f\u5316\u3055\u308c\u307e\u3059\u3002)
+TemplatePanelChangeListener.invalid.empty=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u540d\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002
+TemplatePanelChangeListener.invalid.existing=\u305d\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u540d\u306f\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3002
CommentPanel.contentTextPane.text=
CommentPanel.createdDateLabel.text=-
@@ -111,6 +137,8 @@ CommentPanel.createdLabel.text=\u767b\u9332\u65e5:
CommentPanel.deleteLinkButton.text=\u524a\u9664
CommentPanel.editLinkButton.text=\u7de8\u96c6
CommentPanel.message.delete.issue=\u672c\u5f53\u306b\u30b3\u30e1\u30f3\u30c8\u3092\u524a\u9664\u3057\u307e\u3059\u304b?
+CommentPanel.notificationSentToLabel.text=\u304a\u77e5\u3089\u305b\u3057\u305f\u4eba:
+CommentPanel.notifyLinkButton.text=\u304a\u77e5\u3089\u305b
CommentPanel.quoteLinkButton.text=\u5f15\u7528
CommentPanel.updatedDateLabel.text=-
CommentPanel.updatedLabel.text=\u66f4\u65b0\u65e5:
@@ -118,5 +146,7 @@ CommentPanel.userLinkButton.text=USER
EditIssuePanel.dialg.title=\u30b3\u30e1\u30f3\u30c8\u3092\u7de8\u96c6
+NotifyCommentPanel.dialog.title=\u30b3\u30e1\u30f3\u30c8\u3092\u304a\u77e5\u3089\u305b\u3057\u305f\u3044\u30e6\u30fc\u30b6
+
SubmitPanel.errorLabel.text=ERROR
SubmitPanel.submitButton.text=\u767b\u9332
diff --git a/src/main/resources/com/junichi11/netbeans/modules/backlog/options/Bundle.properties b/src/main/resources/com/junichi11/netbeans/modules/backlog/options/Bundle.properties
index 461ac87..e3c0bb6 100644
--- a/src/main/resources/com/junichi11/netbeans/modules/backlog/options/Bundle.properties
+++ b/src/main/resources/com/junichi11/netbeans/modules/backlog/options/Bundle.properties
@@ -42,3 +42,4 @@ BacklogOptionsPanel.assignedToMeCheckBox.text=Assigned To Me
BacklogOptionsPanel.createdByMeCheckBox.text=Created By Me
BacklogOptionsPanel.defaultQueriesLabel.text=Default queries:
BacklogOptionsPanel.maxIssueCountSpinner.toolTipText=Maximum count
+BacklogOptionsPanel.notificationsCheckBox.text=Notifications
diff --git a/src/main/resources/com/junichi11/netbeans/modules/backlog/options/Bundle_ja_JP.properties b/src/main/resources/com/junichi11/netbeans/modules/backlog/options/Bundle_ja_JP.properties
index 1d6268f..6d03d40 100644
--- a/src/main/resources/com/junichi11/netbeans/modules/backlog/options/Bundle_ja_JP.properties
+++ b/src/main/resources/com/junichi11/netbeans/modules/backlog/options/Bundle_ja_JP.properties
@@ -40,5 +40,6 @@
BacklogOptionsPanel.assignedToMeCheckBox.text=\u62c5\u5f53
BacklogOptionsPanel.createdByMeCheckBox.text=\u767b\u9332
+BacklogOptionsPanel.notificationsCheckBox.text=\u304a\u77e5\u3089\u305b
BacklogOptionsPanel.defaultQueriesLabel.text=\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u554f\u5408\u305b:
BacklogOptionsPanel.maxIssueCountSpinner.toolTipText=\u6700\u5927\u4ef6\u6570
diff --git a/src/main/resources/com/junichi11/netbeans/modules/backlog/query/Bundle_ja_JP.properties b/src/main/resources/com/junichi11/netbeans/modules/backlog/query/Bundle_ja_JP.properties
index 08c86b4..8db4231 100644
--- a/src/main/resources/com/junichi11/netbeans/modules/backlog/query/Bundle_ja_JP.properties
+++ b/src/main/resources/com/junichi11/netbeans/modules/backlog/query/Bundle_ja_JP.properties
@@ -48,3 +48,16 @@ BacklogQueryController.message.error.already.exists=\u3059\u3067\u306b\u5b58\u57
BacklogQueryController.message.error.empty.name=\u554f\u5408\u305b\u540d\u3092\u8a2d\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044
BacklogQueryController.message.saved=\u554f\u5408\u305b\u3092\u4fdd\u5b58\u3057\u307e\u3057\u305f
CreatedByMeQuery.displayName=\u767b\u9332
+
+NotificationsQuery.displayName=\u304a\u77e5\u3089\u305b
+NotificationsQuery.notification.comment=\u30b3\u30e1\u30f3\u30c8: {0}
+NotificationsQuery.notification.marked.all.as.read=\u5168\u3066\u65e2\u8aad\u306b\u3057\u307e\u3057\u305f\u3002
+NotificationsQuery.notification.title=Backlog: {0}
+NotificationsQuery.reason.assigned=\u62c5\u5f53\u306b\u3055\u308c\u307e\u3057\u305f
+NotificationsQuery.reason.commented=\u30b3\u30e1\u30f3\u30c8\u3055\u308c\u307e\u3057\u305f
+NotificationsQuery.reason.fileAttached=\u30d5\u30a1\u30a4\u30eb\u304c\u6dfb\u4ed8\u3055\u308c\u307e\u3057\u305f
+NotificationsQuery.reason.issueCreated=\u8ab2\u984c\u304c\u4f5c\u6210\u3055\u308c\u307e\u3057\u305f
+NotificationsQuery.reason.issueUpdated=\u8ab2\u984c\u304c\u66f4\u65b0\u3055\u308c\u307e\u3057\u305f
+NotificationsQuery.reason.projectUserAdded=\u30e6\u30fc\u30b6\u304c\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f
+NotificationsQuery.reason.other=\u305d\u306e\u4ed6
+
diff --git a/src/main/resources/com/junichi11/netbeans/modules/backlog/query/ui/Bundle_ja_JP.properties b/src/main/resources/com/junichi11/netbeans/modules/backlog/query/ui/Bundle_ja_JP.properties
index 99df1bc..860f834 100644
--- a/src/main/resources/com/junichi11/netbeans/modules/backlog/query/ui/Bundle_ja_JP.properties
+++ b/src/main/resources/com/junichi11/netbeans/modules/backlog/query/ui/Bundle_ja_JP.properties
@@ -70,6 +70,9 @@ GeneralPanel.registeredByMeLinkButton.text=\u79c1\u3092\u9078\u629e
GeneralPanel.resolutionLabel.text=\u5b8c\u4e86\u7406\u7531
GeneralPanel.statusLabel.text=\u72b6\u614b
GeneralPanel.versionLabel.text=\u767a\u751f\u30d0\u30fc\u30b8\u30e7\u30f3
+NotificationPanel.commentLabel.text=\u30b3\u30e1\u30f3\u30c8
+NotificationPanel.markAllAsReadButton.text=\u5168\u3066\u65e2\u8aad\u306b\u3059\u308b
+NotificationPanel.markAsReadButton.text=\u65e2\u8aad\u306b\u3057\u3066\u958b\u304f
UnassignedUser.name=\u672a\u8a2d\u5b9a
NoCategory.name=\u672a\u8a2d\u5b9a
diff --git a/src/main/resources/com/junichi11/netbeans/modules/backlog/resources/manage_template_16.png b/src/main/resources/com/junichi11/netbeans/modules/backlog/resources/manage_template_16.png
new file mode 100644
index 0000000..2e85b04
Binary files /dev/null and b/src/main/resources/com/junichi11/netbeans/modules/backlog/resources/manage_template_16.png differ
diff --git a/src/main/resources/com/junichi11/netbeans/modules/backlog/resources/template_16.png b/src/main/resources/com/junichi11/netbeans/modules/backlog/resources/template_16.png
new file mode 100644
index 0000000..7d1d8c6
Binary files /dev/null and b/src/main/resources/com/junichi11/netbeans/modules/backlog/resources/template_16.png differ