Tasks&Contexts: New choose popup for search and opening tasks.

This commit is contained in:
Evgeny Zakrevsky
2012-08-08 13:25:16 +04:00
parent 5e2fc205fa
commit 0108163b05
18 changed files with 688 additions and 120 deletions
@@ -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 {
@@ -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<ChooseByNamePopup> CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY = new Key<ChooseByNamePopup>("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<Object> 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;
}
}
@@ -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");
}
@@ -39,9 +39,9 @@ public abstract class TaskManager {
* @return up-to-date issues retrieved from repositories
* @see #getCachedIssues()
*/
public abstract List<Task> getIssues(String query);
public abstract List<Task> getIssues(@Nullable String query);
public abstract List<Task> getIssues(String query, boolean forceRequest);
public abstract List<Task> 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<LocalTask> getLocalTasks(String query);
public abstract LocalTask addTask(Task issue);
public abstract LocalTask createLocalTask(String summary);
@@ -21,12 +21,15 @@
<actions>
<group id="task.actions">
<action id="tasks.activate" class="com.intellij.tasks.actions.OpenTaskAction" text="_Open Task...">
<keyboard-shortcut keymap="$default" first-keystroke="alt shift N"/>
</action>
<!--<action id="tasks.activate" class="com.intellij.tasks.actions.OpenTaskAction" text="_Open Task...">-->
<!--<keyboard-shortcut keymap="$default" first-keystroke="alt shift G"/>-->
<!--</action>-->
<action id="tasks.switch" class="com.intellij.tasks.actions.SwitchTaskAction" text="_Switch Task...">
<keyboard-shortcut keymap="$default" first-keystroke="alt shift T"/>
</action>
<action id="tasks.goto" class="com.intellij.tasks.actions.GotoTaskAction">
<keyboard-shortcut keymap="$default" first-keystroke="alt shift N"/>
</action>
<separator/>
<action id="tasks.create.changelist" class="com.intellij.tasks.actions.CreateChangelistAction" text="Create Change_list..."/>
<action id="tasks.show.task.description" class="com.intellij.tasks.actions.ShowTaskDescription" text="Show Description"/>
@@ -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<Boolean> shiftPressed = Ref.create(false);
ChooseByNamePopup popup = ChooseByNamePopup.createPopup(project, new GotoTaskPopupModel(project), new ChooseByNameItemProvider() {
@Override
public List<String> filterNames(ChooseByNameBase base, String[] names, String pattern) {
return ContainerUtil.emptyList();
}
@Override
public void filterElements(ChooseByNameBase base,
String pattern,
boolean everywhere,
Computable<Boolean> cancelled,
Processor<Object> 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("<html>Press SHIFT to merge with current context<br/>Pressing " + KeymapUtil
.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction(IdeActions.ACTION_QUICK_JAVADOC)) + " would show task description and comments</html>");
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<Object>() {
@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<Task> tasks = new ArrayList<Task>();
tasks.addAll(TaskManager.getManager(myProject).getLocalTasks(pattern));
tasks.addAll(ContainerUtil.filter(TaskManager.getManager(myProject).getIssues(pattern), new Condition<Task>() {
@Override
public boolean value(Task task) {
return TaskManager.getManager(myProject).findTask(task.getId()) == null;
}
}));
List<TaskPsiElement> taskPsiElements = ContainerUtil.map(tasks, new Function<Task, TaskPsiElement>() {
@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;
}
}
}
@@ -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();
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.tasks.actions.ActivateTaskDialog">
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.tasks.actions.OpenTaskDialog">
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
@@ -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
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.tasks.actions.SimpleOpenTaskDialog">
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="504" height="108"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<grid id="35df8" layout-manager="GridLayoutManager" row-count="1" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<hspacer id="666fc">
<constraints>
<grid row="0" column="3" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<component id="fedb6" class="javax.swing.JCheckBox" binding="myClearContext" default-binding="true">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<focusable value="false"/>
<selected value="true"/>
<text value="&amp;Clear current context"/>
</properties>
</component>
<component id="5ef71" class="javax.swing.JCheckBox" binding="myCreateChangelist" default-binding="true">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<focusable value="false"/>
<selected value="true"/>
<text value="Create change&amp;list"/>
</properties>
</component>
<component id="d4c2" class="javax.swing.JCheckBox" binding="myMarkAsInProgressBox" default-binding="true">
<constraints>
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<focusable value="false"/>
<text value="Mark as 'In &amp;Progress'"/>
</properties>
</component>
</children>
</grid>
<grid id="29a80" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="9276f" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Open task:"/>
</properties>
</component>
<hspacer id="b92e0">
<constraints>
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<component id="e8faa" class="javax.swing.JLabel" binding="myTaskNameLabel">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Task description"/>
</properties>
</component>
</children>
</grid>
<vspacer id="8bd9b">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
</children>
</grid>
</form>
@@ -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<ChangeListInfo> 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;
}
}
@@ -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();
@@ -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;
}
}
@@ -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);
}
@@ -297,6 +297,17 @@ public class TaskManagerImpl extends TaskManager implements ProjectComponent, Pe
}
}
@Override
public List<LocalTask> getLocalTasks(final String query) {
List<LocalTask> tasks = new ArrayList<LocalTask>();
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) {
@@ -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;
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

@@ -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());
}