diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java index b2b2a6c3b7cc..74721efa3a89 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java @@ -1397,10 +1397,12 @@ public abstract class ChooseByNameBase { public static final String NON_PREFIX_SEPARATOR = "non-prefix matches:"; public static Component renderNonPrefixSeparatorComponent(Color backgroundColor) { - final TitledSeparator separator = new TitledSeparator(); - separator.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 0)); - separator.setBackground(backgroundColor); - return separator; + final JPanel panel = new JPanel(new BorderLayout()); + final JSeparator separator = new JSeparator(SwingConstants.HORIZONTAL); + panel.setPreferredSize(new Dimension(0, 3)); + panel.add(separator, BorderLayout.SOUTH); + panel.setBackground(backgroundColor); + return panel; } private class CalcElementsThread implements Runnable { diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNamePopup.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNamePopup.java index ed7052f56ee9..4eecc38d4267 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNamePopup.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNamePopup.java @@ -31,31 +31,44 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.statistics.StatisticsInfo; import com.intellij.psi.statistics.StatisticsManager; import com.intellij.ui.ScreenUtil; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; +import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNamePopupComponent{ +public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNamePopupComponent { private static final Key CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY = new Key("ChooseByNamePopup"); private Component myOldFocusOwner = null; private boolean myShowListForEmptyPattern = false; private final boolean myMayRequestCurrentWindow; private final ChooseByNamePopup myOldPopup; + private ActionMap myActionMap; + private InputMap myInputMap; + private String myAdText; - protected ChooseByNamePopup(@Nullable final Project project, final ChooseByNameModel model, ChooseByNameItemProvider provider, final ChooseByNamePopup oldPopup, - @Nullable final String predefinedText, boolean mayRequestOpenInCurrentWindow, int initialIndex) { + protected ChooseByNamePopup(@Nullable final Project project, + final ChooseByNameModel model, + ChooseByNameItemProvider provider, + final ChooseByNamePopup oldPopup, + @Nullable final String predefinedText, + boolean mayRequestOpenInCurrentWindow, + int initialIndex) { super(project, model, provider, oldPopup != null ? oldPopup.getEnteredText() : predefinedText, initialIndex); myOldPopup = oldPopup; if (oldPopup != null) { //inherit old focus owner myOldFocusOwner = oldPopup.myPreviouslyFocusedComponent; } myMayRequestCurrentWindow = mayRequestOpenInCurrentWindow; + myAdText = myMayRequestCurrentWindow ? "Press " + + KeymapUtil.getKeystrokeText(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.SHIFT_MASK)) + + " to open in current window" : null; } public String getEnteredText() { @@ -79,10 +92,18 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP } rebuildList(myInitialIndex, 0, null, ModalityState.current(), null); } - if (myOldFocusOwner != null){ + if (myOldFocusOwner != null) { myPreviouslyFocusedComponent = myOldFocusOwner; myOldFocusOwner = null; } + + if (myInputMap != null && myActionMap != null) { + for (KeyStroke keyStroke : myInputMap.keys()) { + Object key = myInputMap.get(keyStroke); + myTextField.getInputMap().put(keyStroke, key); + myTextField.getActionMap().put(key, myActionMap.get(key)); + } + } } @Override @@ -94,7 +115,7 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP return true; } - protected boolean isShowListForEmptyPattern(){ + protected boolean isShowListForEmptyPattern() { return myShowListForEmptyPattern; } @@ -130,13 +151,13 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP myListScrollPane.setVisible(true); myListScrollPane.setBorder(null); - String adText = myMayRequestCurrentWindow ? "Press " + KeymapUtil.getKeystrokeText(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.SHIFT_MASK)) + " to open in current window" : null; + String adText = getAdText(); if (myDropdownPopup == null) { ComponentPopupBuilder builder = JBPopupFactory.getInstance().createComponentPopupBuilder(myListScrollPane, myListScrollPane); builder.setFocusable(false) .setRequestFocus(false) .setCancelKeyEnabled(false) - .setFocusOwners(new JComponent[] {myTextField}) + .setFocusOwners(new JComponent[]{myTextField}) .setBelongsToGlobalPopupStack(false) .setModalContext(false) .setAdText(adText) @@ -151,7 +172,8 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP myDropdownPopup.setLocation(preferredBounds.getLocation()); myDropdownPopup.setSize(preferredBounds.getSize()); myDropdownPopup.show(layeredPane); - } else { + } + else { myDropdownPopup.setLocation(preferredBounds.getLocation()); // in 'focus follows mouse' mode, to avoid focus escaping to editor, don't reduce popup size when list size is reduced @@ -170,12 +192,12 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP } } - protected void close(final boolean isOk) { - if (checkDisposed()){ + public void close(final boolean isOk) { + if (checkDisposed()) { return; } - if (isOk){ + if (isOk) { myModel.saveInitialCheckBoxState(myCheckBox.isSelected()); final List chosenElements = getChosenElements(); @@ -197,7 +219,7 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP return; } - if (!chosenElements.isEmpty()){ + if (!chosenElements.isEmpty()) { final String enteredText = getEnteredText(); if (enteredText.indexOf('*') >= 0) { FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.popup.wildcards"); @@ -214,7 +236,7 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP } } } - else{ + else { return; } } @@ -226,7 +248,7 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP } cleanupUI(isOk); - myActionListener.onClose (); + myActionListener.onClose(); } @Nullable @@ -238,7 +260,8 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP if (myTextPopup != null) { if (ok) { myTextPopup.closeOk(null); - } else { + } + else { myTextPopup.cancel(); } myTextPopup = null; @@ -247,7 +270,8 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP if (myDropdownPopup != null) { if (ok) { myDropdownPopup.closeOk(null); - } else { + } + else { myDropdownPopup.cancel(); } myDropdownPopup = null; @@ -266,10 +290,13 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP public static ChooseByNamePopup createPopup(final Project project, final ChooseByNameModel model, final PsiElement context, @Nullable final String predefinedText, boolean mayRequestOpenInCurrentWindow, final int initialIndex) { - return createPopup(project,model,new DefaultChooseByNameItemProvider(context),predefinedText,mayRequestOpenInCurrentWindow,initialIndex); + return createPopup(project, model, new DefaultChooseByNameItemProvider(context), predefinedText, mayRequestOpenInCurrentWindow, + initialIndex); } - public static ChooseByNamePopup createPopup(final Project project, final ChooseByNameModel model, final ChooseByNameItemProvider provider) { + public static ChooseByNamePopup createPopup(final Project project, + final ChooseByNameModel model, + final ChooseByNameItemProvider provider) { return createPopup(project, model, provider, null); } @@ -333,20 +360,21 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP private int getLineOrColumn(final boolean line) { final Matcher matcher = patternToDetectLinesAndColumns.matcher(getEnteredText()); if (matcher.matches()) { - final int groupNumber = line ? 2:3; + final int groupNumber = line ? 2 : 3; try { - if(groupNumber <= matcher.groupCount()) { + if (groupNumber <= matcher.groupCount()) { final String group = matcher.group(groupNumber); if (group != null) return Integer.parseInt(group) - 1; } if (!line && getLineOrColumn(true) != -1) return 0; } - catch (NumberFormatException ignored) {} + catch (NumberFormatException ignored) { + } } return -1; } - + @Nullable public String getPathToAnonymous() { final Matcher matcher = patternToDetectAnonymousClasses.matcher(getEnteredText()); @@ -361,7 +389,7 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP } } - return null; + return null; } public int getColumnPosition() { @@ -378,4 +406,19 @@ public class ChooseByNamePopup extends ChooseByNameBase implements ChooseByNameP String name = getEnteredText().substring(index + 1).trim(); return StringUtil.isEmpty(name) ? null : name; } + + public void registerAction(@NonNls String aActionName, KeyStroke keyStroke, Action aAction) { + if (myInputMap == null) myInputMap = new InputMap(); + if (myActionMap == null) myActionMap = new ActionMap(); + myInputMap.put(keyStroke, aActionName); + myActionMap.put(aActionName, aAction); + } + + public String getAdText() { + return myAdText; + } + + public void setAdText(final String adText) { + myAdText = adText; + } } diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index 3a4bdab93ebb..a59e204a8d3c 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -283,7 +283,7 @@ public class UIUtil { public static void setEnabled(Component component, boolean enabled, boolean recursively) { component.setEnabled(enabled); if (component instanceof JLabel) { - Color color = enabled ? getLabelForeground() : UIManager.getColor("Label.disabledForeground"); + Color color = enabled ? getLabelForeground() : getLabelDisabledForeground(); if (color != null) { component.setForeground(color); } @@ -416,6 +416,10 @@ public class UIUtil { return UIManager.getColor("Label.foreground"); } + public static Color getLabelDisabledForeground() { + return UIManager.getColor("Label.disabledForeground"); + } + public static Icon getOptionPanelWarningIcon() { return UIManager.getIcon("OptionPane.warningIcon"); } diff --git a/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskManager.java b/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskManager.java index 35f821dd92a9..92bb4ca45f23 100644 --- a/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskManager.java +++ b/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskManager.java @@ -39,9 +39,9 @@ public abstract class TaskManager { * @return up-to-date issues retrieved from repositories * @see #getCachedIssues() */ - public abstract List getIssues(String query); + public abstract List getIssues(@Nullable String query); - public abstract List getIssues(String query, boolean forceRequest); + public abstract List getIssues(@Nullable String query, boolean forceRequest); /** * Returns already cached issues. @@ -53,7 +53,9 @@ public abstract class TaskManager { public abstract Task updateIssue(String id); public abstract LocalTask[] getLocalTasks(); - + + public abstract List getLocalTasks(String query); + public abstract LocalTask addTask(Task issue); public abstract LocalTask createLocalTask(String summary); diff --git a/plugins/tasks/tasks-core/src/META-INF/plugin.xml b/plugins/tasks/tasks-core/src/META-INF/plugin.xml index 48e19c22d032..44c5fd3904ed 100644 --- a/plugins/tasks/tasks-core/src/META-INF/plugin.xml +++ b/plugins/tasks/tasks-core/src/META-INF/plugin.xml @@ -21,12 +21,15 @@ - - - + + + + + + diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/GotoTaskAction.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/GotoTaskAction.java new file mode 100644 index 000000000000..58ab8489ee50 --- /dev/null +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/GotoTaskAction.java @@ -0,0 +1,188 @@ +package com.intellij.tasks.actions; + +import com.intellij.ide.actions.GotoActionBase; +import com.intellij.ide.util.gotoByName.ChooseByNameBase; +import com.intellij.ide.util.gotoByName.ChooseByNameItemProvider; +import com.intellij.ide.util.gotoByName.ChooseByNamePopup; +import com.intellij.ide.util.gotoByName.SimpleChooseByNameModel; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.IdeActions; +import com.intellij.openapi.keymap.KeymapUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Ref; +import com.intellij.psi.PsiManager; +import com.intellij.tasks.LocalTask; +import com.intellij.tasks.Task; +import com.intellij.tasks.TaskManager; +import com.intellij.tasks.doc.TaskPsiElement; +import com.intellij.tasks.impl.TaskUtil; +import com.intellij.util.ArrayUtil; +import com.intellij.util.Function; +import com.intellij.util.Processor; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Evgeny Zakrevsky + */ +public class GotoTaskAction extends GotoActionBase { + public static final CreateNewTaskAction CREATE_NEW_TASK_ACTION = new CreateNewTaskAction(); + + public GotoTaskAction() { + getTemplatePresentation().setText("Goto Task..."); + } + + @Override + protected void gotoActionPerformed(final AnActionEvent e) { + final Project project = e.getProject(); + if (project == null) return; + final Ref shiftPressed = Ref.create(false); + + ChooseByNamePopup popup = ChooseByNamePopup.createPopup(project, new GotoTaskPopupModel(project), new ChooseByNameItemProvider() { + @Override + public List filterNames(ChooseByNameBase base, String[] names, String pattern) { + return ContainerUtil.emptyList(); + } + + @Override + public void filterElements(ChooseByNameBase base, + String pattern, + boolean everywhere, + Computable cancelled, + Processor consumer) { + Object[] elements = base.getModel().getElementsByName("", false, pattern); + for (Object element : elements) { + if (!consumer.process(element)) return; + } + } + }, "", false, 0); + popup.setShowListForEmptyPattern(true); + popup.setSearchInAnyPlace(true); + popup.setAdText("Press SHIFT to merge with current context
Pressing " + KeymapUtil + .getFirstKeyboardShortcutText(ActionManager.getInstance().getAction(IdeActions.ACTION_QUICK_JAVADOC)) + " would show task description and comments"); + popup.registerAction("shiftPressed", KeyStroke.getKeyStroke("shift pressed SHIFT"), new AbstractAction() { + public void actionPerformed(ActionEvent e) { + shiftPressed.set(true); + } + }); + popup.registerAction("shiftReleased", KeyStroke.getKeyStroke("released SHIFT"), new AbstractAction() { + public void actionPerformed(ActionEvent e) { + shiftPressed.set(false); + } + }); + + showNavigationPopup(new GotoActionCallback() { + @Override + public void elementChosen(ChooseByNamePopup popup, Object element) { + TaskManager taskManager = TaskManager.getManager(project); + if (element instanceof TaskPsiElement) { + Task task = ((TaskPsiElement)element).getTask(); + LocalTask localTask = taskManager.findTask(task.getId()); + if (localTask != null) { + final boolean createChangelist = + taskManager.isVcsEnabled() && !taskManager.getOpenChangelists(localTask).isEmpty(); + taskManager.activateTask(localTask, !shiftPressed.get(), createChangelist); + } + else { + (new SimpleOpenTaskDialog(project, task)).show(); + + } + } + else if (element == CREATE_NEW_TASK_ACTION) { + popup.close(false); + Task task = taskManager.createLocalTask(CREATE_NEW_TASK_ACTION.getTaskName()); + SimpleOpenTaskDialog simpleOpenTaskDialog = new SimpleOpenTaskDialog(project, task); + simpleOpenTaskDialog.showAndGetOk(); + } + } + }, null, popup); + + } + + private static class GotoTaskPopupModel extends SimpleChooseByNameModel { + private ListCellRenderer myListCellRenderer; + private final Project myProject; + + + protected GotoTaskPopupModel(@NotNull Project project) { + super(project, "Enter task name:", null); + myProject = project; + myListCellRenderer = new TaskCellRenderer(project); + } + + @Override + public String[] getNames() { + return ArrayUtil.EMPTY_STRING_ARRAY; + } + + @Override + protected Object[] getElementsByName(String name, String pattern) { + List tasks = new ArrayList(); + tasks.addAll(TaskManager.getManager(myProject).getLocalTasks(pattern)); + tasks.addAll(ContainerUtil.filter(TaskManager.getManager(myProject).getIssues(pattern), new Condition() { + @Override + public boolean value(Task task) { + return TaskManager.getManager(myProject).findTask(task.getId()) == null; + } + })); + + List taskPsiElements = ContainerUtil.map(tasks, new Function() { + @Override + public TaskPsiElement fun(Task task) { + return new TaskPsiElement(PsiManager.getInstance(myProject), task); + } + }); + TaskPsiElement[] result2 = new TaskPsiElement[taskPsiElements.size()]; + ArrayUtil.copy(taskPsiElements, result2, 0); + + final boolean foundTaskListEmpty = taskPsiElements.size() == 0; + Object[] result = new Object[taskPsiElements.size() + 1 + (foundTaskListEmpty ? 0 : 1)]; + result[0] = CREATE_NEW_TASK_ACTION; + CREATE_NEW_TASK_ACTION.setTaskName(pattern); + if (!foundTaskListEmpty) { + result[1] = ChooseByNameBase.NON_PREFIX_SEPARATOR; + } + ArrayUtil.copy(taskPsiElements, result, foundTaskListEmpty ? 1 : 2); + return result; + } + + @Override + public ListCellRenderer getListCellRenderer() { + return myListCellRenderer; + } + + @Override + public String getElementName(Object element) { + if (element instanceof TaskPsiElement) { + return TaskUtil.getTrimmedSummary(((TaskPsiElement)element).getTask()); + } else if (element == CREATE_NEW_TASK_ACTION) { + return "Create New Task \"" + CREATE_NEW_TASK_ACTION.getActionText() + "\"..."; + } + return null; + } + } + + public static class CreateNewTaskAction { + private String taskName; + + public String getActionText() { + return "Create New Task \'" + taskName + "\'"; + } + + public void setTaskName(final String taskName) { + this.taskName = taskName; + } + + public String getTaskName() { + return taskName; + } + } +} diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/OpenTaskAction.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/OpenTaskAction.java index 07e170f45694..3ef65d3aac4a 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/OpenTaskAction.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/OpenTaskAction.java @@ -30,14 +30,14 @@ public class OpenTaskAction extends BaseTaskAction { private final static Logger LOG = Logger.getInstance("#com.intellij.tasks.actions.OpenTaskAction"); public OpenTaskAction() { - super("Open _New Task..."); + super("_Open Task..."); } public void actionPerformed(AnActionEvent e) { Project project = getProject(e); if (project == null) return; - ActivateTaskDialog dialog = new ActivateTaskDialog(project); + OpenTaskDialog dialog = new OpenTaskDialog(project); dialog.show(); if (dialog.isOK()) { Task task = dialog.getSelectedTask(); diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/ActivateTaskDialog.form b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/OpenTaskDialog.form similarity index 98% rename from plugins/tasks/tasks-core/src/com/intellij/tasks/actions/ActivateTaskDialog.form rename to plugins/tasks/tasks-core/src/com/intellij/tasks/actions/OpenTaskDialog.form index 35be48a0f962..e5f6d3d67693 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/ActivateTaskDialog.form +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/OpenTaskDialog.form @@ -1,5 +1,5 @@ -
+ diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/ActivateTaskDialog.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/OpenTaskDialog.java similarity index 98% rename from plugins/tasks/tasks-core/src/com/intellij/tasks/actions/ActivateTaskDialog.java rename to plugins/tasks/tasks-core/src/com/intellij/tasks/actions/OpenTaskDialog.java index 7713c2848108..0954036c05c4 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/ActivateTaskDialog.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/OpenTaskDialog.java @@ -57,7 +57,7 @@ import java.util.List; /** * @author Dmitry Avdeev */ -public class ActivateTaskDialog extends DialogWrapper { +public class OpenTaskDialog extends DialogWrapper { private JPanel myPanel; @@ -77,7 +77,7 @@ public class ActivateTaskDialog extends DialogWrapper { private AsyncProcessIcon myUpdateIcon; private JLabel myUpdateLabel; - protected ActivateTaskDialog(Project project) { + protected OpenTaskDialog(Project project) { super(project, true); myProject = project; @@ -260,7 +260,7 @@ public class ActivateTaskDialog extends DialogWrapper { @NonNls protected String getDimensionServiceKey() { - return "ActivateTaskDialog"; + return "OpenTaskDialog"; } @Override @@ -305,7 +305,7 @@ public class ActivateTaskDialog extends DialogWrapper { @Override protected String getQuickDocHotKeyAdvertisementTail(@NotNull String shortcut) { - return " task description and comments"; + return "task description and comments"; } @NotNull diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/SimpleOpenTaskDialog.form b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/SimpleOpenTaskDialog.form new file mode 100644 index 000000000000..dd07871fdafe --- /dev/null +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/SimpleOpenTaskDialog.form @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/SimpleOpenTaskDialog.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/SimpleOpenTaskDialog.java new file mode 100644 index 000000000000..05d16a09290c --- /dev/null +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/SimpleOpenTaskDialog.java @@ -0,0 +1,142 @@ +/* + * Copyright 2000-2009 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.intellij.tasks.actions; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.options.binding.BindControl; +import com.intellij.openapi.options.binding.ControlBinder; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.vcs.changes.ChangeListManager; +import com.intellij.tasks.*; +import com.intellij.tasks.impl.TaskManagerImpl; +import com.intellij.tasks.impl.TaskUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.util.Iterator; + +/** + * @author Dmitry Avdeev + */ +public class SimpleOpenTaskDialog extends DialogWrapper { + private final static Logger LOG = Logger.getInstance("#com.intellij.tasks.actions.SimpleOpenTaskDialog"); + + private JPanel myPanel; + @BindControl(value = "clearContext", instant = true) + private JCheckBox myClearContext; + @BindControl(value = "createChangelist", instant = true) + private JCheckBox myCreateChangelist; + private JCheckBox myMarkAsInProgressBox; + private JLabel myTaskNameLabel; + + private final Project myProject; + private final Task myTask; + + public SimpleOpenTaskDialog(@NotNull final Project project, @NotNull final Task task) { + super(project, false); + myProject = project; + myTask = task; + TaskManagerImpl taskManager = (TaskManagerImpl)TaskManager.getManager(myProject); + setTitle("Open Task"); + myTaskNameLabel.setText(TaskUtil.getTrimmedSummary(task)); + + TaskManagerImpl manager = (TaskManagerImpl)TaskManager.getManager(project); + ControlBinder binder = new ControlBinder(manager.getState()); + binder.bindAnnotations(this); + binder.reset(); + + TaskRepository repository = task.getRepository(); + if (repository == null || !repository.getRepositoryType().getPossibleTaskStates().contains(TaskState.IN_PROGRESS)) { + myMarkAsInProgressBox.setVisible(false); + } + + // refresh change lists + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + for (Iterator it = taskManager.getOpenChangelists(task).iterator(); it.hasNext(); ) { + ChangeListInfo changeListInfo = it.next(); + if (changeListManager.getChangeList(changeListInfo.id) == null) { + it.remove(); + } + } + + boolean vcsEnabled = manager.isVcsEnabled(); + if (!vcsEnabled) { + myCreateChangelist.setEnabled(false); + myCreateChangelist.setSelected(false); + } + else if (!taskManager.getOpenChangelists(task).isEmpty()) { + myCreateChangelist.setSelected(true); + myCreateChangelist.setEnabled(false); + } + else { + myCreateChangelist.setSelected(taskManager.getState().createChangelist); + myCreateChangelist.setEnabled(true); + } + init(); + getPreferredFocusedComponent(); + } + + @Override + protected void doOKAction() { + TaskManagerImpl taskManager = (TaskManagerImpl)TaskManager.getManager(myProject); + taskManager.getState().markAsInProgress = isMarkAsInProgress(); + TaskRepository repository = myTask.getRepository(); + if (isMarkAsInProgress() && repository != null) { + try { + repository.setTaskState(myTask, TaskState.IN_PROGRESS); + } + catch (Exception ex) { + Messages.showErrorDialog(myProject, "Could not set state for " + myTask.getId(), "Error"); + LOG.warn(ex); + } + } + taskManager.activateTask(myTask, isClearContext(), isCreateChangelist()); + if (myTask.getType() == TaskType.EXCEPTION && AnalyzeTaskStacktraceAction.hasTexts(myTask)) { + AnalyzeTaskStacktraceAction.analyzeStacktrace(myTask, myProject); + } + super.doOKAction(); + } + + private boolean isClearContext() { + return myClearContext.isSelected(); + } + + private boolean isCreateChangelist() { + return myCreateChangelist.isSelected(); + } + + private boolean isMarkAsInProgress() { + return myMarkAsInProgressBox.isSelected() && myMarkAsInProgressBox.isVisible(); + } + + @NonNls + protected String getDimensionServiceKey() { + return "SimpleOpenTaskDialog"; + } + + @Override + public JComponent getPreferredFocusedComponent() { + return null; + } + + protected JComponent createCenterPanel() { + return myPanel; + } +} diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/SwitchTaskAction.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/SwitchTaskAction.java index 0802c9f100a4..f095c5071ee8 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/SwitchTaskAction.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/SwitchTaskAction.java @@ -96,7 +96,7 @@ public class SwitchTaskAction extends BaseTaskAction { DefaultActionGroup group = new DefaultActionGroup(); final TaskManager manager = TaskManager.getManager(project); - group.add(new OpenTaskAction()); + group.add(new GotoTaskAction()); group.addSeparator(); diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/TaskCellRenderer.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/TaskCellRenderer.java new file mode 100644 index 000000000000..5add4fcaf8b5 --- /dev/null +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/TaskCellRenderer.java @@ -0,0 +1,81 @@ +package com.intellij.tasks.actions; + +import com.intellij.ide.util.gotoByName.ChooseByNameBase; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.IconLoader; +import com.intellij.tasks.Task; +import com.intellij.tasks.TaskManager; +import com.intellij.tasks.doc.TaskPsiElement; +import com.intellij.tasks.impl.TaskUtil; +import com.intellij.ui.LayeredIcon; +import com.intellij.ui.SimpleColoredComponent; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.ui.speedSearch.SpeedSearchUtil; +import com.intellij.util.text.Matcher; +import com.intellij.util.text.MatcherHolder; +import com.intellij.util.ui.EmptyIcon; +import com.intellij.util.ui.UIUtil; + +import javax.swing.*; +import java.awt.*; + +/** + * @author Evgeny Zakrevsky + */ +public class TaskCellRenderer extends DefaultListCellRenderer implements MatcherHolder { + private static final Color REMOTE_TASK_BG_COLOR = new Color(240, 240, 255); + private Matcher myMatcher; + private final Project myProject; + + + public TaskCellRenderer(Project project) { + super(); + myProject = project; + } + + + public Component getListCellRendererComponent(JList list, Object value, int index, boolean sel, boolean focus) { + final JPanel panel = new JPanel(new BorderLayout()); + panel.setBackground(UIUtil.getListBackground(sel)); + panel.setForeground(UIUtil.getListForeground(sel)); + if (value instanceof TaskPsiElement) { + final Task task = ((TaskPsiElement)value).getTask(); + final SimpleColoredComponent c = new SimpleColoredComponent(); + final boolean isLocalTask = TaskManager.getManager(myProject).findTask(task.getId()) != null; + final boolean isOld = TaskManager.getManager(myProject).getOpenChangelists(task).isEmpty(); + + final Color bg = sel ? UIUtil.getListSelectionBackground() : isLocalTask ? UIUtil.getListBackground() : REMOTE_TASK_BG_COLOR; + panel.setBackground(bg); + SimpleTextAttributes attr = getAttributes(sel, task.isClosed()); + + c.setIcon(isLocalTask && isOld ? IconLoader.getTransparentIcon(task.getIcon(), 0.5f) : task.getIcon()); + SpeedSearchUtil.appendColoredFragmentForMatcher(TaskUtil.getTrimmedSummary(task), c, attr, myMatcher, bg, sel); + panel.add(c, BorderLayout.CENTER); + } + else if ("...".equals(value)){ + final SimpleColoredComponent c = new SimpleColoredComponent(); + c.setIcon(EmptyIcon.ICON_16); + c.append((String)value); + panel.add(c, BorderLayout.CENTER); + } else if (GotoTaskAction.CREATE_NEW_TASK_ACTION == value) { + final SimpleColoredComponent c = new SimpleColoredComponent(); + c.setIcon(LayeredIcon.create(IconLoader.getIcon("/icons/unknown.png"), IconLoader.getIcon("/actions/new.png"))); + c.append(GotoTaskAction.CREATE_NEW_TASK_ACTION.getActionText()); + panel.add(c, BorderLayout.CENTER); + } else if (ChooseByNameBase.NON_PREFIX_SEPARATOR == value) { + panel.add(ChooseByNameBase.renderNonPrefixSeparatorComponent(UIUtil.getListBackground()), BorderLayout.CENTER); + } + + return panel; + } + + private static SimpleTextAttributes getAttributes(final boolean selected, final boolean taskClosed) { + return new SimpleTextAttributes(taskClosed ? SimpleTextAttributes.STYLE_STRIKEOUT : SimpleTextAttributes.STYLE_PLAIN, + taskClosed ? UIUtil.getLabelDisabledForeground() : UIUtil.getListForeground(selected)); + } + + @Override + public void setPatternMatcher(Matcher matcher) { + myMatcher = matcher; + } +} diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskChangelistSupport.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskChangelistSupport.java index f41a6b130681..913ad450d504 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskChangelistSupport.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskChangelistSupport.java @@ -20,7 +20,7 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.changes.LocalChangeList; import com.intellij.openapi.vcs.changes.ui.EditChangelistSupport; -import com.intellij.tasks.actions.ActivateTaskDialog; +import com.intellij.tasks.actions.OpenTaskDialog; import com.intellij.ui.EditorTextField; import com.intellij.ui.TextFieldWithAutoCompletionContributor; import com.intellij.util.Consumer; @@ -42,8 +42,8 @@ public class TaskChangelistSupport implements EditChangelistSupport { public void installSearch(EditorTextField name, final EditorTextField comment) { Document document = name.getDocument(); - final ActivateTaskDialog.MyTextFieldWithAutoCompletionListProvider completionProvider = - new ActivateTaskDialog.MyTextFieldWithAutoCompletionListProvider(myProject); + final OpenTaskDialog.MyTextFieldWithAutoCompletionListProvider completionProvider = + new OpenTaskDialog.MyTextFieldWithAutoCompletionListProvider(myProject); TextFieldWithAutoCompletionContributor.installCompletion(document, myProject, completionProvider, false); } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskManagerImpl.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskManagerImpl.java index 54ce3deca2e3..f8aab44d9baf 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskManagerImpl.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskManagerImpl.java @@ -297,6 +297,17 @@ public class TaskManagerImpl extends TaskManager implements ProjectComponent, Pe } } + @Override + public List getLocalTasks(final String query) { + List tasks = new ArrayList(); + for (LocalTask localTask : getLocalTasks()) { + if (TaskUtil.getTrimmedSummary(localTask).toLowerCase().contains(query.toLowerCase())) { + tasks.add(localTask); + } + } + return tasks; + } + @Override public LocalTask addTask(Task issue) { LocalTaskImpl task = issue instanceof LocalTaskImpl ? (LocalTaskImpl)issue : new LocalTaskImpl(issue); @@ -378,11 +389,11 @@ public class TaskManagerImpl extends TaskManager implements ProjectComponent, Pe changeList = myChangeListManager.addChangeList(name, comment); } else { + getOpenChangelists(task).add(new ChangeListInfo(changeList)); changeList.setComment(comment); } myChangeListManager.setDefaultChangeList(changeList); task.setAssociatedChangelistId(changeList.getId()); - getOpenChangelists(task).add(new ChangeListInfo(changeList)); } private LocalTask doActivate(Task origin, boolean explicitly) { diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskUtil.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskUtil.java index 57e8fe7a80de..e48e04525c85 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskUtil.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskUtil.java @@ -1,72 +1,71 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.intellij.tasks.impl; - -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.tasks.LocalTask; -import com.intellij.tasks.Task; -import com.intellij.tasks.TaskRepository; -import org.jetbrains.annotations.Nullable; - -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * @author Dmitry Avdeev - */ -public class TaskUtil { - - private static final Pattern DATE_PATTERN = Pattern.compile("(\\d\\d\\d\\d[/-]\\d\\d[/-]\\d\\d).*(\\d\\d:\\d\\d:\\d\\d).*"); - private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); - - public static String formatTask(Task task, String format) { - return format.replace("{id}", task.getId()).replace("{number}", task.getNumber()) - .replace("{project}", task.getProject()).replace("{summary}", task.getSummary()); - } - - @Nullable - public static String getChangeListComment(Task task) { - final TaskRepository repository = task.getRepository(); - if (repository == null || !repository.isShouldFormatCommitMessage()) { - return null; - } - return formatTask(task, repository.getCommitMessageFormat()); - } - - public static String getTrimmedSummary(LocalTask task) { - String text; - if (task.isIssue()) { - text = task.getId() + ": " + task.getSummary(); - } else { - text = task.getSummary(); - } - return StringUtil.first(text, 60, true); - } - - @Nullable - public static Date parseDate(String date) throws ParseException { - final Matcher m = DATE_PATTERN.matcher(date); - if (m.find()) { - return DATE_FORMAT.parse(m.group(1).replace('-', '/') + " " + m.group(2)); - } - return null; - } -} +/* + * Copyright 2000-2009 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.intellij.tasks.impl; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.tasks.Task; +import com.intellij.tasks.TaskRepository; +import org.jetbrains.annotations.Nullable; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Dmitry Avdeev + */ +public class TaskUtil { + + private static final Pattern DATE_PATTERN = Pattern.compile("(\\d\\d\\d\\d[/-]\\d\\d[/-]\\d\\d).*(\\d\\d:\\d\\d:\\d\\d).*"); + private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + + public static String formatTask(Task task, String format) { + return format.replace("{id}", task.getId()).replace("{number}", task.getNumber()) + .replace("{project}", task.getProject()).replace("{summary}", task.getSummary()); + } + + @Nullable + public static String getChangeListComment(Task task) { + final TaskRepository repository = task.getRepository(); + if (repository == null || !repository.isShouldFormatCommitMessage()) { + return null; + } + return formatTask(task, repository.getCommitMessageFormat()); + } + + public static String getTrimmedSummary(Task task) { + String text; + if (task.isIssue()) { + text = task.getId() + ": " + task.getSummary(); + } else { + text = task.getSummary(); + } + return StringUtil.first(text, 60, true); + } + + @Nullable + public static Date parseDate(String date) throws ParseException { + final Matcher m = DATE_PATTERN.matcher(date); + if (m.find()) { + return DATE_FORMAT.parse(m.group(1).replace('-', '/') + " " + m.group(2)); + } + return null; + } +} diff --git a/plugins/tasks/tasks-core/src/icons/web.png b/plugins/tasks/tasks-core/src/icons/web.png new file mode 100644 index 000000000000..020daa377c52 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/web.png differ diff --git a/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskCompletionTest.java b/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskCompletionTest.java index 51cd69711c72..898d933eca41 100644 --- a/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskCompletionTest.java +++ b/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskCompletionTest.java @@ -6,7 +6,7 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.ui.CommitMessage; import com.intellij.psi.PsiFile; -import com.intellij.tasks.actions.ActivateTaskDialog; +import com.intellij.tasks.actions.OpenTaskDialog; import com.intellij.tasks.impl.LocalTaskImpl; import com.intellij.tasks.impl.TaskManagerImpl; import com.intellij.testFramework.MapDataContext; @@ -84,7 +84,7 @@ public class TaskCompletionTest extends LightCodeInsightFixtureTestCase { Document document = myFixture.getDocument(psiFile); final Project project = getProject(); TextFieldWithAutoCompletionContributor.installCompletion(document, project, - new ActivateTaskDialog.MyTextFieldWithAutoCompletionListProvider(project), + new OpenTaskDialog.MyTextFieldWithAutoCompletionListProvider(project), false); document.putUserData(CommitMessage.DATA_CONTEXT_KEY, new MapDataContext()); }