diff options
author | Tor Norbye <tnorbye@google.com> | 2014-08-20 17:01:23 -0700 |
---|---|---|
committer | Tor Norbye <tnorbye@google.com> | 2014-08-20 17:01:23 -0700 |
commit | 1aa2e09bdbd413eacb677e9fa4b50630530d0656 (patch) | |
tree | 2f4cc6d69645bd460aa253fdecb606d764fbd25d /platform/platform-impl/src/com/intellij/openapi | |
parent | 02cf98d65c798d368fcec43ed64a001d513bdd4f (diff) | |
download | idea-1aa2e09bdbd413eacb677e9fa4b50630530d0656.tar.gz |
Snapshot idea/138.1696 from git://git.jetbrains.org/idea/community.git
Change-Id: I50c97b83a815ce635e49a38380ba5b8765e4b16a
Diffstat (limited to 'platform/platform-impl/src/com/intellij/openapi')
44 files changed, 1661 insertions, 273 deletions
diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/AbbreviationManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/AbbreviationManagerImpl.java index a7b2e7bc9d02..0d116c7a927f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/AbbreviationManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/AbbreviationManagerImpl.java @@ -38,40 +38,33 @@ import java.util.*; )} ) public class AbbreviationManagerImpl extends AbbreviationManager implements - ExportableApplicationComponent, PersistentStateComponent<Element> { + ExportableComponent, PersistentStateComponent<Element> { private final Map<String, List<String>> myAbbreviation2ActionId = new THashMap<String, List<String>>(); private final Map<String, LinkedHashSet<String>> myActionId2Abbreviations = new THashMap<String, LinkedHashSet<String>>(); private final Map<String, LinkedHashSet<String>> myPluginsActionId2Abbreviations = new THashMap<String, LinkedHashSet<String>>(); - @Override - public void initComponent() { - - } - - @Override - public void disposeComponent() { - - } - - @NotNull - @Override - public String getComponentName() { - return "AbbreviationManager"; - } - @Nullable @Override public Element getState() { final Element actions = new Element("actions"); - final Element abbreviations = new Element("abbreviations"); - actions.addContent(abbreviations); + if (myActionId2Abbreviations.isEmpty()) { + return actions; + } + + Element abbreviations = null; for (String key : myActionId2Abbreviations.keySet()) { final LinkedHashSet<String> abbrs = myActionId2Abbreviations.get(key); final LinkedHashSet<String> pluginAbbrs = myPluginsActionId2Abbreviations.get(key); if (abbrs == pluginAbbrs || (abbrs != null && abbrs.equals(pluginAbbrs))) { continue; } + if (abbrs != null) { + if (abbreviations == null) { + abbreviations = new Element("abbreviations"); + actions.addContent(abbreviations); + } + final Element action = new Element("action"); action.setAttribute("id", key); abbreviations.addContent(action); diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java index 78043075b166..723956ef9daa 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java @@ -49,6 +49,7 @@ import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.IdeFrame; import com.intellij.util.ArrayUtil; import com.intellij.util.ObjectUtils; +import com.intellij.util.ReflectionUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import com.intellij.util.messages.MessageBusConnection; @@ -66,7 +67,6 @@ import javax.swing.*; import javax.swing.Timer; import java.awt.*; import java.awt.event.*; -import java.lang.reflect.Constructor; import java.util.*; import java.util.List; @@ -259,9 +259,8 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat Object obj; String className = stub.getClassName(); try { - Constructor<?> constructor = Class.forName(className, true, stub.getLoader()).getDeclaredConstructor(); - constructor.setAccessible(true); - obj = constructor.newInstance(); + Class<?> aClass = Class.forName(className, true, stub.getLoader()); + obj = ReflectionUtil.newInstance(aClass); } catch (ClassNotFoundException e) { PluginId pluginId = stub.getPluginId(); diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionToolbarImpl.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionToolbarImpl.java index a0b495726f30..529d5ba1c62f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionToolbarImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionToolbarImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -65,6 +65,8 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.actionSystem.impl.ActionToolbarImpl"); private static final List<ActionToolbarImpl> ourToolbars = new LinkedList<ActionToolbarImpl>(); + private static final String RIGHT_ALIGN_KEY = "RIGHT_ALIGN"; + public static void updateAllToolbarsImmediately() { for (ActionToolbarImpl toolbar : new ArrayList<ActionToolbarImpl>(ourToolbars)) { toolbar.updateActionsImmediately(); @@ -107,7 +109,7 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar { private final ActionButtonLook myButtonLook = null; private final ActionButtonLook myMinimalButtonLook = new InplaceActionButtonLook(); private final DataManager myDataManager; - protected final ActionManagerEx myActionManager; + @NotNull protected final ActionManagerEx myActionManager; private Rectangle myAutoPopupRec; @@ -135,7 +137,7 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar { @NotNull final ActionGroup actionGroup, final boolean horizontal, DataManager dataManager, - ActionManagerEx actionManager, + @NotNull ActionManagerEx actionManager, KeymapManagerEx keymapManager) { this(place, actionGroup, horizontal, false, dataManager, actionManager, keymapManager, false); } @@ -144,7 +146,7 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar { final boolean horizontal, final boolean decorateButtons, DataManager dataManager, - ActionManagerEx actionManager, + @NotNull ActionManagerEx actionManager, KeymapManagerEx keymapManager) { this(place, actionGroup, horizontal, decorateButtons, dataManager, actionManager, keymapManager, false); } @@ -154,7 +156,7 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar { final boolean horizontal, final boolean decorateButtons, DataManager dataManager, - ActionManagerEx actionManager, + @NotNull ActionManagerEx actionManager, KeymapManagerEx keymapManager, boolean updateActionsNow) { super(null); @@ -272,11 +274,16 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar { } private void fillToolBar(final List<AnAction> actions, boolean layoutSecondaries) { + final List<AnAction> rightAligned = new ArrayList<AnAction>(); if (myAddSeparatorFirst) { add(new MySeparator()); } for (int i = 0; i < actions.size(); i++) { final AnAction action = actions.get(i); + if (action instanceof RightAlignedToolbarAction) { + rightAligned.add(action); + continue; + } // if (action instanceof Separator && isNavBar()) { // continue; // } @@ -311,18 +318,23 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar { add(mySecondaryActionsButton); } - if ((ActionPlaces.MAIN_TOOLBAR.equals(myPlace) || ActionPlaces.NAVIGATION_BAR_TOOLBAR.equals(myPlace))) { - final AnAction searchEverywhereAction = ActionManager.getInstance().getAction("SearchEverywhere"); - if (searchEverywhereAction != null) { - try { - final CustomComponentAction searchEveryWhereAction = (CustomComponentAction)searchEverywhereAction; - final JComponent searchEverywhere = searchEveryWhereAction.createCustomComponent(searchEverywhereAction.getTemplatePresentation()); - searchEverywhere.putClientProperty("SEARCH_EVERYWHERE", Boolean.TRUE); - add(searchEverywhere); - } - catch (Exception ignore) {} - } - } + for (AnAction action : rightAligned) { + JComponent button = action instanceof CustomComponentAction ? getCustomComponent(action) : createToolbarButton(action); + button.putClientProperty(RIGHT_ALIGN_KEY, Boolean.TRUE); + add(button); + } + //if ((ActionPlaces.MAIN_TOOLBAR.equals(myPlace) || ActionPlaces.NAVIGATION_BAR_TOOLBAR.equals(myPlace))) { + // final AnAction searchEverywhereAction = ActionManager.getInstance().getAction("SearchEverywhere"); + // if (searchEverywhereAction != null) { + // try { + // final CustomComponentAction searchEveryWhereAction = (CustomComponentAction)searchEverywhereAction; + // final JComponent searchEverywhere = searchEveryWhereAction.createCustomComponent(searchEverywhereAction.getTemplatePresentation()); + // searchEverywhere.putClientProperty("SEARCH_EVERYWHERE", Boolean.TRUE); + // add(searchEverywhere); + // } + // catch (Exception ignore) {} + // } + //} } private JComponent getCustomComponent(AnAction action) { @@ -730,14 +742,18 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar { throw new IllegalStateException("unknown layoutPolicy: " + myLayoutPolicy); } + if (getComponentCount() > 0 && size2Fit.width < Integer.MAX_VALUE) { - final Component component = getComponent(getComponentCount() - 1); - if (component instanceof JComponent && ((JComponent)component).getClientProperty("SEARCH_EVERYWHERE") == Boolean.TRUE) { - int max = 0; - for (int i = 0; i < bounds.size() - 2; i++) { - max = Math.max(max, bounds.get(i).height); + int maxHeight = 0; + for (int i = 0; i < bounds.size() - 2; i++) { + maxHeight = Math.max(maxHeight, bounds.get(i).height); + } + + for (int i = getComponentCount() - 1, j = 1; i > 0; i--, j++) { + final Component component = getComponent(i); + if (component instanceof JComponent && ((JComponent)component).getClientProperty(RIGHT_ALIGN_KEY) == Boolean.TRUE) { + bounds.set(bounds.size() - j, new Rectangle(size2Fit.width - j * 25, 0, 25, maxHeight)); } - bounds.set(bounds.size() - 1, new Rectangle(size2Fit.width - 25, 0, 25, max)); } } } @@ -1185,7 +1201,7 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar { final ActionGroup actionGroup, final boolean horizontal, final DataManager dataManager, - final ActionManagerEx actionManager, + @NotNull ActionManagerEx actionManager, final KeymapManagerEx keymapManager, JComponent parent) { super(place, actionGroup, horizontal, false, dataManager, actionManager, keymapManager, true); diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java index 7c8acbc4c25c..46fae517170d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java @@ -781,7 +781,7 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App } @Override - public void restart(boolean exitConfirmed) { + public void restart(final boolean exitConfirmed) { exit(false, exitConfirmed, true, true); } @@ -801,7 +801,7 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App exiting = true; try { - if (!force && getDefaultModalityState() != ModalityState.NON_MODAL) { + if (!force && !exitConfirmed && getDefaultModalityState() != ModalityState.NON_MODAL) { return; } @@ -1067,8 +1067,7 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App private static Thread getEventQueueThread() { EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue(); try { - Method method = EventQueue.class.getDeclaredMethod("getDispatchThread"); - method.setAccessible(true); + Method method = ReflectionUtil.getDeclaredMethod(EventQueue.class, "getDispatchThread"); return (Thread)method.invoke(eventQueue); } catch (Exception e) { diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/ModalityInvokatorImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/ModalityInvokatorImpl.java index ab2be68b70bd..de7a7e76aa70 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/ModalityInvokatorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/ModalityInvokatorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -27,23 +27,27 @@ import com.intellij.openapi.util.Condition; import org.jetbrains.annotations.NotNull; public class ModalityInvokatorImpl implements ModalityInvokator { + @NotNull @Override - public ActionCallback invokeLater(Runnable runnable) { + public ActionCallback invokeLater(@NotNull Runnable runnable) { return invokeLater(runnable, ApplicationManager.getApplication().getDisposed()); } + @NotNull @Override - public ActionCallback invokeLater(final Runnable runnable, @NotNull final Condition expired) { + public ActionCallback invokeLater(@NotNull final Runnable runnable, @NotNull final Condition expired) { return LaterInvocator.invokeLater(runnable, expired); } + @NotNull @Override - public ActionCallback invokeLater(final Runnable runnable, @NotNull final ModalityState state, @NotNull final Condition expired) { + public ActionCallback invokeLater(@NotNull final Runnable runnable, @NotNull final ModalityState state, @NotNull final Condition expired) { return LaterInvocator.invokeLater(runnable, state, expired); } + @NotNull @Override - public ActionCallback invokeLater(Runnable runnable, @NotNull ModalityState state) { + public ActionCallback invokeLater(@NotNull Runnable runnable, @NotNull ModalityState state) { return invokeLater(runnable, state, ApplicationManager.getApplication().getDisposed()); } }
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/EditorLinePainter.java b/platform/platform-impl/src/com/intellij/openapi/editor/EditorLinePainter.java new file mode 100644 index 000000000000..420a0fdf23a7 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/EditorLinePainter.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2014 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.openapi.editor; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * @author Konstantin Bulenkov + */ +public abstract class EditorLinePainter { + public static final ExtensionPointName<EditorLinePainter> EP_NAME = ExtensionPointName.create("com.intellij.editor.linePainter"); + + public abstract Collection<LineExtensionInfo> getLineExtensions(@NotNull Project project, @NotNull VirtualFile file, int lineNumber); +} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/LineExtensionInfo.java b/platform/platform-impl/src/com/intellij/openapi/editor/LineExtensionInfo.java new file mode 100644 index 000000000000..f84502097b10 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/LineExtensionInfo.java @@ -0,0 +1,71 @@ +/* + * Copyright 2000-2014 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.openapi.editor; + +import com.intellij.openapi.editor.markup.EffectType; +import org.intellij.lang.annotations.JdkConstants; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; + +/** + * @author Konstantin Bulenkov + */ +public class LineExtensionInfo { + @NotNull private final String myText; + @Nullable private final Color myColor; + @Nullable private final EffectType myEffectType; + @Nullable private final Color myEffectColor; + @JdkConstants.FontStyle private final int myFontType; + + public LineExtensionInfo(@NotNull String text, + @Nullable Color color, + @Nullable EffectType effectType, + @Nullable Color effectColor, + @JdkConstants.FontStyle int fontType) { + myText = text; + myColor = color; + myEffectType = effectType; + myEffectColor = effectColor; + myFontType = fontType; + } + + @NotNull + public String getText() { + return myText; + } + + @Nullable + public Color getColor() { + return myColor; + } + + @Nullable + public EffectType getEffectType() { + return myEffectType; + } + + @Nullable + public Color getEffectColor() { + return myEffectColor; + } + + @JdkConstants.FontStyle + public int getFontType() { + return myFontType; + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/EnterAction.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/EnterAction.java index cc7b2177cd91..ea2107632122 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/EnterAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/EnterAction.java @@ -30,6 +30,8 @@ import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.actionSystem.EditorAction; import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler; import com.intellij.util.ui.MacUIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class EnterAction extends EditorAction { public EnterAction() { @@ -43,13 +45,13 @@ public class EnterAction extends EditorAction { } @Override - public void executeWriteAction(Editor editor, DataContext dataContext) { + public void executeWriteAction(Editor editor, @Nullable Caret caret, DataContext dataContext) { CommandProcessor.getInstance().setCurrentCommandName(EditorBundle.message("typing.command.name")); insertNewLineAtCaret(editor); } @Override - public boolean isEnabled(Editor editor, DataContext dataContext) { + public boolean isEnabledForCaret(@NotNull Editor editor, @NotNull Caret caret, DataContext dataContext) { return !editor.isOneLineMode(); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/IndentSelectionAction.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/IndentSelectionAction.java index 92f025685891..76eff2bd7a54 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/IndentSelectionAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/IndentSelectionAction.java @@ -57,7 +57,7 @@ public class IndentSelectionAction extends EditorAction { @Override public void executeWriteAction(Editor editor, @Nullable Caret caret, DataContext dataContext) { Project project = CommonDataKeys.PROJECT.getData(dataContext); - if (isEnabled(editor, dataContext)) { + if (isEnabled(editor, caret, dataContext)) { indentSelection(editor, project); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/SplitLineAction.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/SplitLineAction.java index 7716a9001b7e..1583bd423ff7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/SplitLineAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/SplitLineAction.java @@ -18,10 +18,7 @@ package com.intellij.openapi.editor.actions; import com.intellij.ide.DataManager; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.IdeActions; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.RangeMarker; -import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.actionSystem.EditorAction; import com.intellij.openapi.editor.actionSystem.EditorActionHandler; import com.intellij.openapi.editor.actionSystem.EditorActionManager; @@ -30,6 +27,7 @@ import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.ide.CopyPasteManager; import com.intellij.openapi.util.Key; import com.intellij.util.text.CharArrayUtil; +import org.jetbrains.annotations.NotNull; /** * @author max @@ -48,13 +46,13 @@ public class SplitLineAction extends EditorAction { } @Override - public boolean isEnabled(Editor editor, DataContext dataContext) { - return getEnterHandler().isEnabled(editor, dataContext) && + public boolean isEnabledForCaret(@NotNull Editor editor, @NotNull Caret caret, DataContext dataContext) { + return getEnterHandler().isEnabled(editor, caret, dataContext) && !((EditorEx)editor).isEmbeddedIntoDialogWrapper(); } @Override - public void executeWriteAction(Editor editor, DataContext dataContext) { + public void executeWriteAction(Editor editor, Caret caret, DataContext dataContext) { CopyPasteManager.getInstance().stopKillRings(); final Document document = editor.getDocument(); final RangeMarker rangeMarker = @@ -77,7 +75,7 @@ public class SplitLineAction extends EditorAction { } else { DataManager.getInstance().saveInDataContext(dataContext, SPLIT_LINE_KEY, true); try { - getEnterHandler().execute(editor, dataContext); + getEnterHandler().execute(editor, caret, dataContext); } finally { DataManager.getInstance().saveInDataContext(dataContext, SPLIT_LINE_KEY, null); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/StartNewLineAction.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/StartNewLineAction.java index 0302e484d00d..b2f43bf4458f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/StartNewLineAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/StartNewLineAction.java @@ -17,6 +17,7 @@ package com.intellij.openapi.editor.actions; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.IdeActions; +import com.intellij.openapi.editor.Caret; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.editor.actionSystem.EditorAction; @@ -24,6 +25,7 @@ import com.intellij.openapi.editor.actionSystem.EditorActionHandler; import com.intellij.openapi.editor.actionSystem.EditorActionManager; import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler; import com.intellij.openapi.ide.CopyPasteManager; +import org.jetbrains.annotations.NotNull; /** * @author max @@ -39,12 +41,12 @@ public class StartNewLineAction extends EditorAction { } @Override - public boolean isEnabled(Editor editor, DataContext dataContext) { - return getEnterHandler().isEnabled(editor, dataContext); + public boolean isEnabledForCaret(@NotNull Editor editor, @NotNull Caret caret, DataContext dataContext) { + return getEnterHandler().isEnabled(editor, caret, dataContext); } @Override - public void executeWriteAction(Editor editor, DataContext dataContext) { + public void executeWriteAction(Editor editor, Caret caret, DataContext dataContext) { CopyPasteManager.getInstance().stopKillRings(); if (editor.getDocument().getLineCount() != 0) { editor.getSelectionModel().removeSelection(); @@ -53,7 +55,7 @@ public class StartNewLineAction extends EditorAction { editor.getCaretModel().moveToOffset(lineEndOffset); } - getEnterHandler().execute(editor, dataContext); + getEnterHandler().execute(editor, caret, dataContext); } private static EditorActionHandler getEnterHandler() { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/StartNewLineBeforeAction.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/StartNewLineBeforeAction.java index fc2ae4031ca3..4d8c6ff2f465 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/StartNewLineBeforeAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/StartNewLineBeforeAction.java @@ -42,8 +42,8 @@ public class StartNewLineBeforeAction extends EditorAction { } @Override - public boolean isEnabled(Editor editor, DataContext dataContext) { - return getHandler(IdeActions.ACTION_EDITOR_ENTER).isEnabled(editor, dataContext); + public boolean isEnabledForCaret(@NotNull Editor editor, @NotNull Caret caret, DataContext dataContext) { + return getHandler(IdeActions.ACTION_EDITOR_ENTER).isEnabled(editor, caret, dataContext); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/UnselectWordAtCaretAction.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/UnselectWordAtCaretAction.java index a1ae088a89e2..2d7e3945ee6e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/UnselectWordAtCaretAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/UnselectWordAtCaretAction.java @@ -24,11 +24,13 @@ */ package com.intellij.openapi.editor.actions; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.editor.Caret; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.actionSystem.EditorAction; import com.intellij.openapi.editor.actionSystem.EditorActionHandler; -import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.project.DumbAware; +import org.jetbrains.annotations.Nullable; public class UnselectWordAtCaretAction extends EditorAction implements DumbAware { public UnselectWordAtCaretAction() { @@ -42,7 +44,7 @@ public class UnselectWordAtCaretAction extends EditorAction implements DumbAware } @Override - public void execute(Editor editor, DataContext dataContext) { + public void doExecute(Editor editor, @Nullable Caret caret, DataContext dataContext) { } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorMarkupModel.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorMarkupModel.java index 5da1077baf5c..71dee99c3514 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorMarkupModel.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorMarkupModel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -28,7 +28,7 @@ public interface EditorMarkupModel extends MarkupModel { void setErrorStripeVisible(boolean val); - void setErrorStripeRenderer(ErrorStripeRenderer renderer); + void setErrorStripeRenderer(@NotNull ErrorStripeRenderer renderer); ErrorStripeRenderer getErrorStripeRenderer(); void addErrorMarkerListener(@NotNull ErrorStripeListener listener, @NotNull Disposable parent); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHeaderComponent.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHeaderComponent.java index fc1f9048d31f..31b3a56d2317 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHeaderComponent.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHeaderComponent.java @@ -46,13 +46,8 @@ public class EditorHeaderComponent extends JPanel { paintGradient(g, this); } - @NotNull - protected JBColor getBaseBackgroundColor() { - return new JBColor(getBackground(), JBColor.background()); - } - private void paintGradient(Graphics g, JComponent c) { - Color GRADIENT_C1 = getBaseBackgroundColor(); + Color GRADIENT_C1 = isBackgroundSet() ? getBackground() : new JBColor(getBackground(), JBColor.background()); Color GRADIENT_C2 = new JBColor(new Color(Math.max(0, GRADIENT_C1.getRed() - 0x18), Math.max(0, GRADIENT_C1.getGreen() - 0x18), Math.max(0, GRADIENT_C1.getBlue() - 0x18)), Gray._75); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index c0ffbd10aca4..6160ea58dcdf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -2765,8 +2765,25 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (hEnd >= lEnd) { FoldRegion collapsedFolderAt = myFoldingModel.getCollapsedRegionAtOffset(start); if (collapsedFolderAt == null) { - drawStringWithSoftWraps(g, chars, start, lEnd - lIterator.getSeparatorLength(), position, clip, effectColor, - effectType, fontType, currentColor, logicalPosition); + int i = drawStringWithSoftWraps(g, chars, start, lEnd - lIterator.getSeparatorLength(), position, clip, effectColor, + effectType, fontType, currentColor, logicalPosition); + final VirtualFile file = getVirtualFile(); + if (myProject != null && file != null && !isOneLineMode()) { + for (EditorLinePainter painter : EditorLinePainter.EP_NAME.getExtensions()) { + Collection<LineExtensionInfo> extensions = painter.getLineExtensions(myProject, file, lIterator.getLineNumber()); + if (extensions != null && !extensions.isEmpty()) { + for (LineExtensionInfo info : extensions) { + drawStringWithSoftWraps(g, info.getText(), 0, info.getText().length(), position, clip, + info.getEffectColor() == null ? effectColor : info.getEffectColor(), + info.getEffectType() == null ? effectType : info.getEffectType(), + info.getFontType(), + info.getColor() == null ? currentColor : info.getColor(), + logicalPosition); + } + } + } + } + position.x = 0; if (position.y > clip.y + clip.height) { break; @@ -6749,10 +6766,17 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi public Insets getBorderInsets(Component c) { Container splitters = SwingUtilities.getAncestorOfClass(EditorsSplitters.class, c); boolean thereIsSomethingAbove = !SystemInfo.isMac || UISettings.getInstance().SHOW_MAIN_TOOLBAR || UISettings.getInstance().SHOW_NAVIGATION_BAR || - myProject != null && !ToolWindowManagerEx.getInstanceEx(myProject).getIdsOn(ToolWindowAnchor.TOP).isEmpty(); + toolWindowIsNotEmpty(); return splitters == null ? super.getBorderInsets(c) : new Insets(thereIsSomethingAbove ? 1 : 0, 0, 0, 0); } + public boolean toolWindowIsNotEmpty() { + if (myProject == null) return false; + ToolWindowManagerEx m = ToolWindowManagerEx.getInstanceEx(myProject); + if (m == null) return false; + return !m.getIdsOn(ToolWindowAnchor.TOP).isEmpty(); + } + @Override public boolean isBorderOpaque() { return true; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java index 6ec29e58dc0b..20c08ec4ec99 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java @@ -368,7 +368,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } @Override - public void setErrorStripeRenderer(ErrorStripeRenderer renderer) { + public void setErrorStripeRenderer(@NotNull ErrorStripeRenderer renderer) { assertIsDispatchThread(); if (myErrorStripeRenderer instanceof Disposable) { Disposer.dispose((Disposable)myErrorStripeRenderer); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileTextFieldImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileTextFieldImpl.java index 63843a932076..64f56eb750eb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileTextFieldImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileTextFieldImpl.java @@ -285,7 +285,7 @@ public abstract class FileTextFieldImpl implements FileLookup, Disposable, FileT myList = new JBList(); myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - myList.setCellRenderer(new GroupedItemsListRenderer(new ListItemDescriptor() { + myList.setCellRenderer(new GroupedItemsListRenderer(new ListItemDescriptorAdapter() { public String getTextFor(final Object value) { final LookupFile file = (LookupFile)value; @@ -298,10 +298,6 @@ public abstract class FileTextFieldImpl implements FileLookup, Disposable, FileT } - public String getTooltipFor(final Object value) { - return null; - } - public Icon getIconFor(final Object value) { final LookupFile file = (LookupFile)value; return file.getIcon(); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java index 9520834b4518..30eb78cf706f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -16,6 +16,7 @@ package com.intellij.openapi.fileEditor.ex; import com.intellij.openapi.Disposable; +import com.intellij.openapi.editor.Caret; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.EditorDataProvider; import com.intellij.openapi.fileEditor.FileEditor; @@ -164,9 +165,9 @@ public abstract class FileEditorManagerEx extends FileEditorManager implements B public abstract boolean isInsideChange(); @Nullable - public final Object getData(@NotNull String dataId, @NotNull Editor editor, @NotNull VirtualFile file) { + public final Object getData(@NotNull String dataId, @NotNull Editor editor, @NotNull Caret caret) { for (final EditorDataProvider dataProvider : myDataProviders) { - final Object o = dataProvider.getData(dataId, editor, file); + final Object o = dataProvider.getData(dataId, editor, caret); if (o != null) return o; } return null; diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java index 091ff1171157..cce6de27545d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java @@ -621,6 +621,7 @@ public class FileDocumentManagerImpl extends FileDocumentManager implements Virt DocumentEx documentEx = (DocumentEx)document; documentEx.setReadOnly(false); LoadTextUtil.setCharsetWasDetectedFromBytes(file, null); + file.setBOM(null); // reset BOM in case we had one and the external change stripped it away documentEx.replaceText(LoadTextUtil.loadText(file), file.getModificationStamp()); documentEx.setReadOnly(!wasWritable); } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java index c7fa51f770d1..bab6d2b2fa4f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java @@ -1636,7 +1636,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec */ private final class MyVirtualFileListener extends VirtualFileAdapter { @Override - public void beforeFileDeletion(VirtualFileEvent e) { + public void beforeFileDeletion(@NotNull VirtualFileEvent e) { assertDispatchThread(); boolean moveFocus = moveFocusOnDelete(); @@ -1651,7 +1651,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } @Override - public void propertyChanged(VirtualFilePropertyEvent e) { + public void propertyChanged(@NotNull VirtualFilePropertyEvent e) { if (VirtualFile.PROP_NAME.equals(e.getPropertyName())) { assertDispatchThread(); final VirtualFile file = e.getFile(); @@ -1681,7 +1681,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } @Override - public void fileMoved(VirtualFileMoveEvent e) { + public void fileMoved(@NotNull VirtualFileMoveEvent e) { final VirtualFile file = e.getFile(); final VirtualFile[] openFiles = getOpenFiles(); for (final VirtualFile openFile : openFiles) { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/HistoryEntry.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/HistoryEntry.java index 5f3dd9adad22..51d044866717 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/HistoryEntry.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/HistoryEntry.java @@ -24,18 +24,19 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.containers.HashMap; import org.jdom.Element; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.Map; final class HistoryEntry{ - static final String TAG = "entry"; + @NonNls static final String TAG = "entry"; private static final String FILE_ATTR = "file"; - private static final String PROVIDER_ELEMENT = "provider"; - private static final String EDITOR_TYPE_ID_ATTR = "editor-type-id"; - private static final String SELECTED_ATTR_VALUE = "selected"; - private static final String STATE_ELEMENT = "state"; + @NonNls private static final String PROVIDER_ELEMENT = "provider"; + @NonNls private static final String EDITOR_TYPE_ID_ATTR = "editor-type-id"; + @NonNls private static final String SELECTED_ATTR_VALUE = "selected"; + @NonNls private static final String STATE_ELEMENT = "state"; public final VirtualFile myFile; /** @@ -53,7 +54,7 @@ final class HistoryEntry{ } } - public HistoryEntry(Project project, Element e) throws InvalidDataException { + public HistoryEntry(@NotNull Project project, @NotNull Element e) throws InvalidDataException { myFile = getVirtualFile(e); myProvider2State = new HashMap<FileEditorProvider, FileEditorState>(); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorComponent.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorComponent.java index 288227eb51bd..de11bd5b9a12 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorComponent.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorComponent.java @@ -36,7 +36,6 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.impl.EditorHistoryManager; -import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl; import com.intellij.openapi.fileTypes.FileTypeEvent; import com.intellij.openapi.fileTypes.FileTypeListener; import com.intellij.openapi.fileTypes.FileTypeManager; @@ -278,7 +277,7 @@ class TextEditorComponent extends JBLoadingPanel implements DataProvider { if (e == null) return null; if (!myProject.isDisposed()) { - final Object o = ((FileEditorManagerImpl)FileEditorManager.getInstance(myProject)).getData(dataId, e, myFile); + final Object o = FileEditorManager.getInstance(myProject).getData(dataId, e, e.getCaretModel().getCurrentCaret()); if (o != null) return o; } diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ModifierKeyDoubleClickHandler.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ModifierKeyDoubleClickHandler.java index 13a8aaecb724..8a9031986ef8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ModifierKeyDoubleClickHandler.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ModifierKeyDoubleClickHandler.java @@ -24,6 +24,7 @@ import com.intellij.openapi.actionSystem.ex.ActionManagerEx; import com.intellij.openapi.keymap.KeymapManager; import com.intellij.openapi.util.Clock; import com.intellij.openapi.util.Couple; +import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.util.containers.ConcurrentHashMap; import gnu.trove.TIntIntHashMap; @@ -63,6 +64,9 @@ public class ModifierKeyDoubleClickHandler { public void registerAction(@NotNull String actionId, int modifierKeyCode, int actionKeyCode) { + if (SystemInfo.isMac && modifierKeyCode == KeyEvent.VK_CONTROL) { + modifierKeyCode = KeyEvent.VK_META; + } final MyDispatcher dispatcher = new MyDispatcher(actionId, modifierKeyCode, actionKeyCode); IdeEventQueue.EventDispatcher oldDispatcher = myDispatchers.put(actionId, dispatcher); IdeEventQueue.getInstance().addDispatcher(dispatcher, null); diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTree.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTree.java index 1b0c17f3ab4e..44ec15003de9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTree.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTree.java @@ -21,6 +21,7 @@ import com.intellij.ide.ui.UISettings; import com.intellij.ide.ui.search.SearchUtil; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.QuickList; +import com.intellij.openapi.actionSystem.impl.ActionMenu; import com.intellij.openapi.keymap.KeyMapBundle; import com.intellij.openapi.keymap.Keymap; import com.intellij.openapi.keymap.KeymapUtil; @@ -40,12 +41,15 @@ import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; import com.intellij.util.ui.tree.WideSelectionTreeUI; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.*; import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; import java.util.ArrayList; import java.util.Enumeration; import java.util.Set; @@ -91,7 +95,7 @@ public class ActionsTree { } } } - + } }; myTree.setRootVisible(false); @@ -99,6 +103,36 @@ public class ActionsTree { myTree.putClientProperty(WideSelectionTreeUI.STRIPED_CLIENT_PROPERTY, Boolean.TRUE); myTree.setCellRenderer(new KeymapsRenderer()); + myTree.addMouseMotionListener(new MouseMotionAdapter() { + @Override + public void mouseMoved(MouseEvent e) { + String description = getDescription(e); + if (description != null) { + ActionMenu.showDescriptionInStatusBar(true, myTree, description); + } + else { + ActionMenu.showDescriptionInStatusBar(false, myTree, null); + } + } + + @Nullable + private String getDescription(@NotNull MouseEvent e) { + TreePath path = myTree.getPathForLocation(e.getX(), e.getY()); + if (path == null) return null; + + DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent(); + if (node == null) return null; + + Object userObject = node.getUserObject(); + if (!(userObject instanceof String)) return null; + + String actionId = (String)userObject; + AnAction action = ActionManager.getInstance().getActionOrStub(actionId); + if (action == null) return null; + + return action.getTemplatePresentation().getDescription(); + } + }); myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); @@ -447,6 +481,7 @@ public class ActionsTree { Icon icon = null; String text; boolean bound = false; + setToolTipText(null); if (value instanceof DefaultMutableTreeNode) { Object userObject = ((DefaultMutableTreeNode)value).getUserObject(); @@ -474,6 +509,7 @@ public class ActionsTree { if (actionIcon != null) { icon = actionIcon; } + setToolTipText(action.getTemplatePresentation().getDescription()); } else { text = actionId; @@ -521,7 +557,7 @@ public class ActionsTree { } } } - + private void paintRowData(Tree tree, Object data, Rectangle bounds, Graphics2D g) { Shortcut[] shortcuts = null; Set<String> abbreviations = null; @@ -552,7 +588,7 @@ public class ActionsTree { Color c2 = new Color(208, 200, 66); g.translate(0, bounds.y - 1); - + for (Shortcut shortcut : shortcuts) { int width = metrics.stringWidth(KeymapUtil.getShortcutText(shortcut)); UIUtil.drawSearchMatch(g, x, x + width, bounds.height, c1, c2); diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableExtensionPointUtil.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableExtensionPointUtil.java index c06dbee44c1a..429a55b2b809 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableExtensionPointUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableExtensionPointUtil.java @@ -71,7 +71,7 @@ public class ConfigurableExtensionPointUtil { idToConfigurable.put(parentId, parent.addChild(wrapper)); } else { - LOG.error("Can't find parent for " + parentId + " (" + wrapper + ")"); + LOG.debug("Can't find parent for " + parentId + " (" + wrapper + ")"); } } } @@ -79,8 +79,9 @@ public class ConfigurableExtensionPointUtil { for (final Iterator<String> iterator = idToConfigurable.keySet().iterator(); iterator.hasNext(); ) { final String key = iterator.next(); final ConfigurableWrapper wrapper = idToConfigurable.get(key); - if (wrapper.getParentId() != null) { - iterator.remove(); + final String parentId = wrapper.getParentId(); + if (parentId != null && idToConfigurable.containsKey(parentId)) { + iterator.remove(); // remove only processed parents } } ContainerUtil.addAll(result, idToConfigurable.values()); diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java index f465e2d0fecb..f1411cf3551d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java @@ -18,9 +18,11 @@ package com.intellij.openapi.options.ex; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurableGroup; import com.intellij.openapi.options.OptionsBundle; +import com.intellij.openapi.options.SearchableConfigurable; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.Map.Entry; public final class MixedConfigurableGroup implements ConfigurableGroup { @@ -66,6 +68,16 @@ public final class MixedConfigurableGroup implements ConfigurableGroup { } list.add(configurable); } + ArrayList<Configurable> buildList = map.get("build"); + if (buildList != null) { + NodeConfigurable buildTools = new NodeConfigurable("build.tools"); + buildTools.add(find("MavenSettings", buildList.iterator())); + buildTools.add(find("reference.settingsdialog.project.gradle", buildList.iterator())); + buildTools.add(find("reference.settingsdialog.project.gant", buildList.iterator())); + if (buildTools.getConfigurables() != null) { + buildList.add(0, buildTools); + } + } ArrayList<ConfigurableGroup> groups = new ArrayList<ConfigurableGroup>(map.size()); groups.add(new MixedConfigurableGroup("appearance", map)); groups.add(new MixedConfigurableGroup("editor", map)); @@ -80,4 +92,18 @@ public final class MixedConfigurableGroup implements ConfigurableGroup { groups.add(other); return groups.toArray(new ConfigurableGroup[groups.size()]); } + + private static Configurable find(String id, Iterator<Configurable> iterator) { + while (iterator.hasNext()) { + Configurable configurable = iterator.next(); + if (configurable instanceof SearchableConfigurable) { + SearchableConfigurable sc = (SearchableConfigurable)configurable; + if (id.equals(sc.getId())) { + iterator.remove(); + return configurable; + } + } + } + return null; + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java new file mode 100644 index 000000000000..6571c7a63746 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java @@ -0,0 +1,71 @@ +/* + * Copyright 2000-2014 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.openapi.options.ex; + +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.OptionsBundle; +import com.intellij.openapi.options.SearchableConfigurable; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; + +public final class NodeConfigurable extends SearchableConfigurable.Parent.Abstract { + private final ArrayList<Configurable> myConfigurables = new ArrayList<Configurable>(); + private final String myId; + + public NodeConfigurable(@NotNull String id) { + myId = id; + } + + public void add(Configurable configurable) { + if (configurable != null) { + super.disposeUIResources(); + myConfigurables.add(configurable); + } + } + + @Override + public void disposeUIResources() { + super.disposeUIResources(); + myConfigurables.clear(); + } + + @NotNull + @Override + public String getId() { + return myId; + } + + @Nullable + @Override + public String getHelpTopic() { + return myId; + } + + @Nls + @Override + public String getDisplayName() { + return OptionsBundle.message("node.configurable." + myId + ".display.name"); + } + + @Override + protected Configurable[] buildConfigurables() { + int size = myConfigurables.size(); + return size == 0 ? null : myConfigurables.toArray(new Configurable[size]); + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsEditor.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsEditor.java index 07e5a93670e6..271539f038a0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsEditor.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsEditor.java @@ -49,6 +49,7 @@ import com.intellij.ui.navigation.History; import com.intellij.ui.navigation.Place; import com.intellij.ui.speedSearch.ElementFilter; import com.intellij.ui.treeStructure.SimpleNode; +import com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.update.Activatable; import com.intellij.util.ui.update.MergingUpdateQueue; @@ -87,6 +88,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat private final History myHistory = new History(this); private final OptionsTree myTree; + private final SettingsTreeView myTreeView; private final MySearchField mySearch; private final Splitter myMainSplitter; //[back/forward] JComponent myToolbarComponent; @@ -126,7 +128,12 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat mySearch = new MySearchField() { @Override protected void onTextKeyEvent(final KeyEvent e) { - myTree.processTextEvent(e); + if (myTreeView != null) { + myTreeView.myTree.processKeyEvent(e); + } + else { + myTree.processTextEvent(e); + } } }; @@ -144,12 +151,12 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat } }); - myTree = new OptionsTree(myProject, groups, getContext()) { + final KeyListener listener = new KeyListener() { @Override - protected void onTreeKeyEvent(final KeyEvent e) { + public void keyTyped(KeyEvent event) { myFilterDocumentWasChanged = false; try { - mySearch.keyEventToTextField(e); + mySearch.keyEventToTextField(event); } finally { if (myFilterDocumentWasChanged && !isFilterFieldVisible()) { @@ -157,10 +164,33 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat } } } + + @Override + public void keyPressed(KeyEvent event) { + keyTyped(event); + } + + @Override + public void keyReleased(KeyEvent event) { + keyTyped(event); + } }; + if (Registry.is("ide.file.settings.tree.new")) { + myTreeView = new SettingsTreeView(listener, getContext(), groups); + myTree = null; + } + else { + myTreeView = null; + myTree = new OptionsTree(myProject, groups, getContext()) { + @Override + protected void onTreeKeyEvent(final KeyEvent e) { + listener.keyTyped(e); + } + }; + } - getContext().addColleague(myTree); - Disposer.register(this, myTree); + getContext().addColleague(myTreeView != null ? myTreeView : myTree); + Disposer.register(this, myTreeView != null ? myTreeView : myTree); mySearch.addDocumentListener(new DocumentAdapter() { @Override protected void textChanged(DocumentEvent e) { @@ -198,7 +228,8 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat @Override public Dimension getMinimumSize() { Dimension dimension = super.getMinimumSize(); - dimension.width = Math.max(myTree.getMinimumSize().width, mySearchWrapper.getPreferredSize().width); + JComponent component = myTreeView != null ? myTreeView : myTree; + dimension.width = Math.max(component.getMinimumSize().width, mySearchWrapper.getPreferredSize().width); return dimension; } }; @@ -211,7 +242,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat */ myLeftSide.add(mySearchWrapper, BorderLayout.NORTH); - myLeftSide.add(myTree, BorderLayout.CENTER); + myLeftSide.add(myTreeView != null ? myTreeView : myTree, BorderLayout.CENTER); setLayout(new BorderLayout()); @@ -233,9 +264,19 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat mySpotlightUpdate = new MergingUpdateQueue("OptionsSpotlight", 200, false, this, this, this); if (preselectedConfigurable != null) { - myTree.select(preselectedConfigurable); + if (myTreeView != null) { + myTreeView.select(preselectedConfigurable); + } + else { + myTree.select(preselectedConfigurable); + } } else { - myTree.selectFirst(); + if (myTreeView != null) { + myTreeView.selectFirst(); + } + else { + myTree.selectFirst(); + } } Toolkit.getDefaultToolkit().addAWTEventListener(this, @@ -295,12 +336,16 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat @Deprecated @Nullable public <T extends Configurable> T findConfigurable(Class<T> configurableClass) { - return myTree.findConfigurable(configurableClass); + return myTreeView != null + ? myTreeView.findConfigurable(configurableClass) + : myTree.findConfigurable(configurableClass); } @Nullable public SearchableConfigurable findConfigurableById(@NotNull String configurableId) { - return myTree.findConfigurableById(configurableId); + return myTreeView != null + ? myTreeView.findConfigurableById(configurableId) + : myTree.findConfigurableById(configurableId); } public ActionCallback clearSearchAndSelect(Configurable configurable) { @@ -318,7 +363,9 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat public ActionCallback select(Configurable configurable, final String text) { myFilter.refilterFor(text, false, true); - return myTree.select(configurable); + return myTreeView != null + ? myTreeView.select(configurable) + : myTree.select(configurable); } private float readProportion(final float defaultValue, final String propertyName) { @@ -367,7 +414,10 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat myOwnDetails.setContent(myContentWrapper); myOwnDetails.setBannerMinHeight(mySearchWrapper.getHeight()); myOwnDetails.setText(getBannerText(configurable)); - if (Registry.is("ide.file.settings.order.new")) { + if (myTreeView != null) { + myOwnDetails.forProject(myTreeView.findConfigurableProject(configurable)); + } + else if (Registry.is("ide.file.settings.order.new")) { myOwnDetails.forProject(myTree.getConfigurableProject(configurable)); } @@ -385,7 +435,8 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat checkModified(oldConfigurable); checkModified(configurable); - if (myTree.myBuilder.getSelectedElements().size() == 0) { + FilteringTreeBuilder builder = myTreeView != null ? myTreeView.myBuilder : myTree.myBuilder; + if (builder.getSelectedElements().size() == 0) { select(configurable).notify(result); } else { result.setDone(); @@ -507,6 +558,9 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat } private String[] getBannerText(Configurable configurable) { + if (myTreeView != null) { + return myTreeView.getPathNames(configurable); + } final List<Configurable> list = myTree.getPathToRoot(configurable); final String[] result = new String[list.size()]; int add = 0; @@ -795,7 +849,12 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat getContext().fireErrorsChanged(errors, null); if (!errors.isEmpty()) { - myTree.select(errors.keySet().iterator().next()); + if (myTreeView != null) { + myTreeView.select(errors.keySet().iterator().next()); + } + else { + myTree.select(errors.keySet().iterator().next()); + } } } @@ -835,7 +894,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat return myFiltered.contains(node.getConfigurable()) || isChildOfNameHit(node); } - return true; + return SettingsTreeView.isFiltered(myFiltered, myHits, value); } private boolean isChildOfNameHit(OptionsTree.EditorNode node) { @@ -929,7 +988,8 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat myLastSelected = current; } - final ActionCallback callback = fireUpdate(adjustSelection ? myTree.findNodeFor(toSelect) : null, adjustSelection, now); + SimpleNode node = !adjustSelection ? null : myTreeView != null ? myTreeView.findNode(toSelect) : myTree.findNodeFor(toSelect); + final ActionCallback callback = fireUpdate(node, adjustSelection, now); myFilterDocumentWasChanged = true; @@ -965,7 +1025,12 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat myFilter.refilterFor(filter, false, true).doWhenDone(new Runnable() { @Override public void run() { - myTree.select(config).notifyWhenDone(result); + if (myTreeView != null) { + myTreeView.select(config).notifyWhenDone(result); + } + else { + myTree.select(config).notifyWhenDone(result); + } } }); diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java index e0ab24815ece..175b994122ef 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java @@ -23,6 +23,7 @@ import com.intellij.openapi.options.ConfigurableGroup; import com.intellij.openapi.options.OptionsBundle; import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.options.ex.ConfigurableWrapper; +import com.intellij.openapi.options.ex.NodeConfigurable; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Disposer; @@ -116,10 +117,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl } }); - final JScrollPane scrolls = ScrollPaneFactory.createScrollPane(myTree); - scrolls.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); - - add(scrolls, BorderLayout.CENTER); + add(new StickySeparator(myTree), BorderLayout.CENTER); mySelection = new MergingUpdateQueue("OptionsTree", 150, false, this, this, this).setRestartTimerOnAdd(true); myTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { @@ -290,7 +288,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl } class Renderer extends GroupedElementsRenderer.Tree { - + private GroupSeparator mySeparator; private JLabel myProjectIcon; private JLabel myHandle; @@ -298,7 +296,8 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl protected void layout() { myRendererComponent.setOpaqueActive(false); - myRendererComponent.add(mySeparatorComponent, BorderLayout.NORTH); + mySeparator = new GroupSeparator(); + myRendererComponent.add(Registry.is("ide.file.settings.order.new") ? mySeparator : mySeparatorComponent, BorderLayout.NORTH); final NonOpaquePanel content = new NonOpaquePanel(new BorderLayout()); myHandle = new JLabel("", SwingConstants.CENTER); @@ -325,7 +324,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl JComponent result; Color fg = UIUtil.getTreeTextForeground(); - + mySeparator.configure(null, false); final Base base = extractNode(value); if (base instanceof EditorNode) { @@ -335,6 +334,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl final DefaultMutableTreeNode prevValue = ((DefaultMutableTreeNode)value).getPreviousSibling(); if (prevValue == null || prevValue instanceof LoadingNode) { group = editor.getGroup(); + mySeparator.configure(group, false); } else { final Base prevBase = extractNode(prevValue); @@ -342,6 +342,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl final EditorNode prevEditor = (EditorNode)prevBase; if (prevEditor.getGroup() != editor.getGroup()) { group = editor.getGroup(); + mySeparator.configure(group, true); } } } @@ -404,8 +405,25 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl myTextLabel.setBorder(new EmptyBorder(1,2,1,0)); } - Project project = getConfigurableProject(base); - if (project != null && Registry.is("ide.file.settings.order.new")) { + Project project = null; + if (base != null && Registry.is("ide.file.settings.order.new")) { + SimpleNode parent = base.getParent(); + if (parent == myRoot) { + project = getConfigurableProject(base); // show icon for top-level nodes + if (base.getConfigurable() instanceof NodeConfigurable) { // special case for custom subgroups (build.tools) + Configurable[] configurables = ((NodeConfigurable)base.getConfigurable()).getConfigurables(); + if (configurables != null) { // assume that all configurables have the same project + project = getConfigurableProject(configurables[0]); + } + } + } + else if (parent instanceof Base && ((Base)parent).getConfigurable() instanceof NodeConfigurable) { + if (((Base)base.getParent()).getConfigurable() instanceof NodeConfigurable) { + project = getConfigurableProject(base); // special case for custom subgroups + } + } + } + if (project != null) { myProjectIcon.setBackground(selected ? getSelectionBackground() : getBackground()); myProjectIcon.setIcon(selected ? AllIcons.General.ProjectConfigurableSelected : AllIcons.General.ProjectConfigurable); myProjectIcon.setVisible(true); @@ -903,4 +921,97 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl } return getConfigurableProject(node.getParent()); } + + private static final class GroupSeparator extends JLabel { + public static final int SPACE = 10; + + public GroupSeparator() { + setFont(UIUtil.getLabelFont()); + setFont(getFont().deriveFont(Font.BOLD)); + } + + public void configure(ConfigurableGroup group, boolean isSpaceNeeded) { + if (group == null) { + setVisible(false); + } + else { + setVisible(true); + int bottom = UIUtil.isUnderNativeMacLookAndFeel() ? 1 : 3; + int top = isSpaceNeeded + ? bottom + SPACE + : bottom; + setBorder(BorderFactory.createEmptyBorder(top, 3, bottom, 3)); + setText(group.getDisplayName()); + } + } + } + + private static final class StickySeparator extends JComponent { + private final SimpleTree myTree; + private final JScrollPane myScroller; + private final GroupSeparator mySeparator; + + public StickySeparator(SimpleTree tree) { + myTree = tree; + myScroller = ScrollPaneFactory.createScrollPane(myTree); + myScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + mySeparator = new GroupSeparator(); + add(myScroller); + } + + @Override + public void doLayout() { + myScroller.setBounds(0, 0, getWidth(), getHeight()); + } + + @Override + public void paint(Graphics g) { + super.paint(g); + + if (Registry.is("ide.file.settings.order.new")) { + ConfigurableGroup group = getGroup(GroupSeparator.SPACE + mySeparator.getFont().getSize()); + if (group != null && group == getGroup(-GroupSeparator.SPACE)) { + mySeparator.configure(group, false); + + Rectangle bounds = myScroller.getViewport().getBounds(); + int height = mySeparator.getPreferredSize().height; + if (bounds.height > height) { + bounds.height = height; + } + g.setColor(myTree.getBackground()); + if (g instanceof Graphics2D) { + int h = bounds.height / 3; + int y = bounds.y + bounds.height - h; + g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height - h); + ((Graphics2D)g).setPaint(UIUtil.getGradientPaint( + 0, y, g.getColor(), + 0, y + h, ColorUtil.toAlpha(g.getColor(), 0))); + g.fillRect(bounds.x, y, bounds.width, h); + } + else { + g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); + } + mySeparator.setSize(bounds.width - 1, bounds.height); + mySeparator.paint(g.create(bounds.x + 1, bounds.y, bounds.width - 1, bounds.height)); + } + } + } + + private ConfigurableGroup getGroup(int offset) { + TreePath path = myTree.getClosestPathForLocation(-myTree.getX(), -myTree.getY() + offset); + SimpleNode node = myTree.getNodeFor(path); + if (node instanceof FilteringTreeStructure.FilteringNode) { + Object delegate = ((FilteringTreeStructure.FilteringNode)node).getDelegate(); + while (delegate instanceof EditorNode) { + EditorNode editor = (EditorNode)delegate; + ConfigurableGroup group = editor.getGroup(); + if (group != null) { + return group; + } + delegate = editor.getParent(); + } + } + return null; + } + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java new file mode 100644 index 000000000000..9269da7ab967 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java @@ -0,0 +1,864 @@ +/* + * Copyright 2000-2014 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.openapi.options.newEditor; + +import com.intellij.icons.AllIcons; +import com.intellij.ide.ui.search.ConfigurableHit; +import com.intellij.ide.util.treeView.NodeDescriptor; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.options.*; +import com.intellij.openapi.options.ex.ConfigurableWrapper; +import com.intellij.openapi.options.ex.NodeConfigurable; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.ActionCallback; +import com.intellij.openapi.util.Disposer; +import com.intellij.ui.*; +import com.intellij.ui.treeStructure.*; +import com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder; +import com.intellij.ui.treeStructure.filtered.FilteringTreeStructure; +import com.intellij.util.ArrayUtil; +import com.intellij.util.ui.GraphicsUtil; +import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.tree.TreeUtil; +import com.intellij.util.ui.update.MergingUpdateQueue; +import com.intellij.util.ui.update.Update; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.util.List; +import javax.swing.*; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeExpansionListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.plaf.TreeUI; +import javax.swing.plaf.basic.BasicTreeUI; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; + +/** + * @author Sergey.Malenkov + */ +final class SettingsTreeView extends JComponent implements Disposable, OptionsEditorColleague { + final SimpleTree myTree; + final FilteringTreeBuilder myBuilder; + + private final OptionsEditorContext myContext; + private final MyRoot myRoot; + private final JScrollPane myScroller; + private JLabel mySeparator; + private final MyRenderer myRenderer = new MyRenderer(); + private final IdentityHashMap<Configurable, MyNode> myConfigurableToNodeMap = new IdentityHashMap<Configurable, MyNode>(); + private final MergingUpdateQueue myQueue = new MergingUpdateQueue("OptionsTree", 150, false, this, this, this).setRestartTimerOnAdd(true); + + private Configurable myQueuedConfigurable; + + SettingsTreeView(final KeyListener listener, OptionsEditorContext context, ConfigurableGroup... groups) { + myContext = context; + myRoot = new MyRoot(groups); + + myTree = new MyTree(); + myTree.getInputMap().clear(); + TreeUtil.installActions(myTree); + + myTree.setOpaque(true); + myTree.setBorder(BorderFactory.createEmptyBorder(0, 1, 0, 0)); + + myTree.setRowHeight(-1); + myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + + myTree.setCellRenderer(myRenderer); + myTree.setRootVisible(false); + myTree.setShowsRootHandles(false); + + myScroller = ScrollPaneFactory.createScrollPane(myTree); + myScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + add(myScroller); + + myTree.addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + myBuilder.revalidateTree(); + } + + @Override + public void componentMoved(ComponentEvent e) { + myBuilder.revalidateTree(); + } + + @Override + public void componentShown(ComponentEvent e) { + myBuilder.revalidateTree(); + } + }); + + myTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { + public void valueChanged(TreeSelectionEvent event) { + MyNode node = extractNode(event.getNewLeadSelectionPath()); + select(node == null ? null : node.myConfigurable); + } + }); + + myTree.addKeyListener(new KeyListener() { + public void keyTyped(KeyEvent event) { + if (listener != null && isValid(event)) { + listener.keyTyped(event); + } + } + + public void keyPressed(KeyEvent event) { + if (listener != null && isValid(event)) { + listener.keyPressed(event); + } + } + + public void keyReleased(KeyEvent event) { + if (listener != null && isValid(event)) { + listener.keyReleased(event); + } + } + + private boolean isValid(KeyEvent event) { + return null == myTree.getInputMap().get(KeyStroke.getKeyStrokeForEvent(event)); + } + }); + myBuilder = new MyBuilder(new SimpleTreeStructure.Impl(myRoot)); + myBuilder.setFilteringMerge(300, null); + Disposer.register(this, myBuilder); + } + + @NotNull + String[] getPathNames(Configurable configurable) { + ArrayDeque<String> path = new ArrayDeque<String>(); + MyNode node = myConfigurableToNodeMap.get(configurable); + while (node != null) { + path.push(node.myDisplayName); + SimpleNode parent = node.getParent(); + node = parent instanceof MyNode + ? (MyNode)parent + : null; + } + return ArrayUtil.toStringArray(path); + } + + @Nullable + SimpleNode findNode(Configurable toSelect) { + return myConfigurableToNodeMap.get(toSelect); + } + + @Nullable + SearchableConfigurable findConfigurableById(@NotNull String id) { + for (Configurable configurable : myConfigurableToNodeMap.keySet()) { + if (configurable instanceof SearchableConfigurable) { + SearchableConfigurable searchable = (SearchableConfigurable)configurable; + if (id.equals(searchable.getId())) { + return searchable; + } + } + } + return null; + } + + @Nullable + <T extends UnnamedConfigurable> T findConfigurable(@NotNull Class<T> type) { + for (UnnamedConfigurable configurable : myConfigurableToNodeMap.keySet()) { + if (configurable instanceof ConfigurableWrapper) { + ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; + configurable = wrapper.getConfigurable(); + } + if (type.isInstance(configurable)) { + return type.cast(configurable); + } + } + return null; + } + + @Nullable + Project findConfigurableProject(@Nullable Configurable configurable) { + if (configurable instanceof ConfigurableWrapper) { + ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; + return wrapper.getExtensionPoint().getProject(); + } + return findConfigurableProject(myConfigurableToNodeMap.get(configurable)); + } + + @Nullable + private static Project findConfigurableProject(@Nullable MyNode node) { + if (node != null) { + Configurable configurable = node.myConfigurable; + if (configurable instanceof ConfigurableWrapper) { + ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; + return wrapper.getExtensionPoint().getProject(); + } + SimpleNode parent = node.getParent(); + if (parent instanceof MyNode) { + return findConfigurableProject((MyNode)parent); + } + } + return null; + } + + @Nullable + private ConfigurableGroup findConfigurableGroupAt(int x, int y) { + TreePath path = myTree.getClosestPathForLocation(x - myTree.getX(), y - myTree.getY()); + while (path != null) { + MyNode node = extractNode(path); + if (node == null) { + return null; + } + if (node.myComposite instanceof ConfigurableGroup) { + return (ConfigurableGroup)node.myComposite; + } + path = path.getParentPath(); + } + return null; + } + + @Nullable + private static MyNode extractNode(@Nullable Object object) { + if (object instanceof TreePath) { + TreePath path = (TreePath)object; + object = path.getLastPathComponent(); + } + if (object instanceof DefaultMutableTreeNode) { + DefaultMutableTreeNode node = (DefaultMutableTreeNode)object; + object = node.getUserObject(); + } + if (object instanceof FilteringTreeStructure.FilteringNode) { + FilteringTreeStructure.FilteringNode node = (FilteringTreeStructure.FilteringNode)object; + object = node.getDelegate(); + } + return object instanceof MyNode + ? (MyNode)object + : null; + } + + static boolean isFiltered(Set<Configurable> configurables, ConfigurableHit hits, SimpleNode value) { + if (value instanceof MyNode && !configurables.contains(((MyNode)value).myConfigurable)) { + if (hits != null) { + configurables = hits.getNameFullHits(); + while (value != null) { + if (value instanceof MyNode) { + if (configurables.contains(((MyNode)value).myConfigurable)) { + return true; + } + } + value = value.getParent(); + } + } + return false; + } + return true; + } + + @Override + public void doLayout() { + myScroller.setBounds(0, 0, getWidth(), getHeight()); + } + + @Override + public void paint(Graphics g) { + super.paint(g); + + if (mySeparator == null) { + mySeparator = new JLabel(); + mySeparator.setFont(UIUtil.getLabelFont()); + mySeparator.setFont(getFont().deriveFont(Font.BOLD)); + } + ConfigurableGroup group = findConfigurableGroupAt(0, 5 + mySeparator.getFont().getSize()); + if (group != null && group == findConfigurableGroupAt(0, -5)) { + int offset = UIUtil.isUnderNativeMacLookAndFeel() ? 1 : 3; + mySeparator.setBorder(BorderFactory.createEmptyBorder(offset, 18, offset, 3)); + mySeparator.setText(group.getDisplayName()); + + Rectangle bounds = myScroller.getViewport().getBounds(); + int height = mySeparator.getPreferredSize().height; + if (bounds.height > height) { + bounds.height = height; + } + g.setColor(myTree.getBackground()); + if (g instanceof Graphics2D) { + int h = bounds.height / 4; + int y = bounds.y + bounds.height - h; + g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height - h); + ((Graphics2D)g).setPaint(UIUtil.getGradientPaint( + 0, y, g.getColor(), + 0, y + h, ColorUtil.toAlpha(g.getColor(), 0))); + g.fillRect(bounds.x, y, bounds.width, h + h); + } + else { + g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); + } + mySeparator.setSize(bounds.width - 1, bounds.height); + mySeparator.paint(g.create(bounds.x + 1, bounds.y, bounds.width - 1, bounds.height)); + } + } + + void selectFirst() { + for (ConfigurableGroup eachGroup : myRoot.myGroups) { + Configurable[] kids = eachGroup.getConfigurables(); + if (kids.length > 0) { + select(kids[0]); + return; + } + } + } + + ActionCallback select(@Nullable final Configurable configurable) { + if (myBuilder.isSelectionBeingAdjusted()) { + return new ActionCallback.Rejected(); + } + final ActionCallback callback = new ActionCallback(); + myQueuedConfigurable = configurable; + myQueue.queue(new Update(this) { + public void run() { + if (configurable == myQueuedConfigurable) { + if (configurable == null) { + fireSelected(null, callback); + } + else { + myBuilder.getReady(this).doWhenDone(new Runnable() { + @Override + public void run() { + if (configurable != myQueuedConfigurable) return; + + MyNode editorNode = myConfigurableToNodeMap.get(configurable); + FilteringTreeStructure.FilteringNode editorUiNode = myBuilder.getVisibleNodeFor(editorNode); + if (editorUiNode == null) return; + + if (!myBuilder.getSelectedElements().contains(editorUiNode)) { + myBuilder.select(editorUiNode, new Runnable() { + public void run() { + fireSelected(configurable, callback); + } + }); + } + else { + myBuilder.scrollSelectionToVisible(new Runnable() { + public void run() { + fireSelected(configurable, callback); + } + }, false); + } + } + }); + } + } + } + + @Override + public void setRejected() { + super.setRejected(); + callback.setRejected(); + } + }); + return callback; + } + + private void fireSelected(Configurable configurable, ActionCallback callback) { + myContext.fireSelected(configurable, this).doWhenProcessed(callback.createSetDoneRunnable()); + } + + @Override + public void dispose() { + myQueuedConfigurable = null; + } + + @Override + public ActionCallback onSelected(@Nullable Configurable configurable, Configurable oldConfigurable) { + return select(configurable); + } + + @Override + public ActionCallback onModifiedAdded(Configurable configurable) { + myTree.repaint(); + return new ActionCallback.Done(); + } + + @Override + public ActionCallback onModifiedRemoved(Configurable configurable) { + myTree.repaint(); + return new ActionCallback.Done(); + } + + @Override + public ActionCallback onErrorsChanged() { + return new ActionCallback.Done(); + } + + private final class MyRoot extends CachingSimpleNode { + private final ConfigurableGroup[] myGroups; + + private MyRoot(ConfigurableGroup[] groups) { + super(null); + myGroups = groups; + } + + @Override + protected SimpleNode[] buildChildren() { + if (myGroups == null || myGroups.length == 0) { + return NO_CHILDREN; + } + SimpleNode[] result = new SimpleNode[myGroups.length]; + for (int i = 0; i < myGroups.length; i++) { + result[i] = new MyNode(this, myGroups[i]); + } + return result; + } + } + + private final class MyNode extends CachingSimpleNode { + private final Configurable.Composite myComposite; + private final Configurable myConfigurable; + private final String myDisplayName; + + private MyNode(CachingSimpleNode parent, Configurable configurable) { + super(parent); + myComposite = configurable instanceof Configurable.Composite ? (Configurable.Composite)configurable : null; + myConfigurable = configurable; + String name = configurable.getDisplayName(); + myDisplayName = name != null ? name.replace("\n", " ") : "{ " + configurable.getClass().getSimpleName() + " }"; + + myConfigurableToNodeMap.put(configurable, this); + } + + private MyNode(CachingSimpleNode parent, ConfigurableGroup group) { + super(parent); + myComposite = group; + myConfigurable = null; + String name = group.getDisplayName(); + myDisplayName = name != null ? name.replace("\n", " ") : "{ " + group.getClass().getSimpleName() + " }"; + } + + @Override + protected SimpleNode[] buildChildren() { + if (myComposite == null) { + return NO_CHILDREN; + } + Configurable[] configurables = myComposite.getConfigurables(); + if (configurables == null || configurables.length == 0) { + return NO_CHILDREN; + } + SimpleNode[] result = new SimpleNode[configurables.length]; + for (int i = 0; i < configurables.length; i++) { + result[i] = new MyNode(this, configurables[i]); + if (myConfigurable != null) { + myContext.registerKid(myConfigurable, configurables[i]); + } + } + return result; + } + + @Override + public boolean isAlwaysLeaf() { + return myComposite == null; + } + + @Override + public int getWeight() { + return WeightBasedComparator.UNDEFINED_WEIGHT; + } + } + + private final class MyRenderer extends GroupedElementsRenderer.Tree { + private JLabel myNodeIcon; + private JLabel myProjectIcon; + + protected JComponent createItemComponent() { + myTextLabel = new ErrorLabel(); + return myTextLabel; + } + + @Override + protected void layout() { + myNodeIcon = new JLabel(" ", SwingConstants.RIGHT); + myProjectIcon = new JLabel(" ", SwingConstants.LEFT); + myProjectIcon.setOpaque(true); + myRendererComponent.add(BorderLayout.NORTH, mySeparatorComponent); + myRendererComponent.add(BorderLayout.CENTER, myComponent); + myRendererComponent.add(BorderLayout.WEST, myNodeIcon); + myRendererComponent.add(BorderLayout.EAST, myProjectIcon); + } + + public Component getTreeCellRendererComponent(JTree tree, + Object value, + boolean selected, + boolean expanded, + boolean leaf, + int row, + boolean focused) { + myTextLabel.setOpaque(selected); + myTextLabel.setFont(UIUtil.getLabelFont()); + + String text; + boolean hasSeparatorAbove = false; + int preferredForcedWidth = -1; + + MyNode node = extractNode(value); + if (node == null) { + text = value.toString(); + } + else { + text = node.myDisplayName; + // show groups in bold + if (myRoot == node.getParent()) { + hasSeparatorAbove = node != myRoot.getChildAt(0); + myTextLabel.setFont(myTextLabel.getFont().deriveFont(Font.BOLD)); + } + TreePath path = tree.getPathForRow(row); + if (path == null) { + if (value instanceof DefaultMutableTreeNode) { + path = new TreePath(((DefaultMutableTreeNode)value).getPath()); + } + } + int forcedWidth = 2000; + if (path != null && tree.isVisible()) { + Rectangle visibleRect = tree.getVisibleRect(); + + int nestingLevel = tree.isRootVisible() ? path.getPathCount() - 1 : path.getPathCount() - 2; + + int left = UIUtil.getTreeLeftChildIndent(); + int right = UIUtil.getTreeRightChildIndent(); + + Insets treeInsets = tree.getInsets(); + + int indent = (left + right) * nestingLevel + (treeInsets != null ? treeInsets.left + treeInsets.right : 0); + + forcedWidth = visibleRect.width > 0 ? visibleRect.width - indent : forcedWidth; + } + preferredForcedWidth = forcedWidth - 4; + } + Component result = configureComponent(text, null, null, null, selected, hasSeparatorAbove, null, preferredForcedWidth); + // update font color for modified configurables + if (!selected && node != null) { + Configurable configurable = node.myConfigurable; + if (configurable != null) { + if (myContext.getErrors().containsKey(configurable)) { + myTextLabel.setForeground(JBColor.RED); + } + else if (myContext.getModified().contains(configurable)) { + myTextLabel.setForeground(JBColor.BLUE); + } + } + } + // configure project icon + Project project = null; + if (node != null) { + SimpleNode parent = node.getParent(); + if (parent instanceof MyNode) { + if (myRoot == parent.getParent()) { + project = findConfigurableProject(node); // show icon for top-level nodes + if (node.myConfigurable instanceof NodeConfigurable) { // special case for custom subgroups (build.tools) + Configurable[] configurables = ((NodeConfigurable)node.myConfigurable).getConfigurables(); + if (configurables != null) { // assume that all configurables have the same project + project = findConfigurableProject(configurables[0]); + } + } + } + else if (((MyNode)parent).myConfigurable instanceof NodeConfigurable) { + if (((MyNode)node.getParent()).myConfigurable instanceof NodeConfigurable) { + project = findConfigurableProject(node); // special case for custom subgroups + } + } + } + } + if (project != null) { + myProjectIcon.setIcon(selected + ? AllIcons.General.ProjectConfigurableSelected + : AllIcons.General.ProjectConfigurable); + myProjectIcon.setToolTipText(OptionsBundle.message(project.isDefault() + ? "configurable.default.project.tooltip" + : "configurable.current.project.tooltip")); + myProjectIcon.setBackground(myTextLabel.getBackground()); + myProjectIcon.setVisible(true); + } + else { + myProjectIcon.setVisible(false); + } + // configure node icon + if (value instanceof DefaultMutableTreeNode) { + DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)value; + TreePath treePath = new TreePath(treeNode.getPath()); + myNodeIcon.setIcon(myTree.getHandleIcon(treeNode, treePath)); + } + else { + myNodeIcon.setIcon(null); + } + return result; + } + + + public boolean isUnderHandle(Point point) { + Point handlePoint = SwingUtilities.convertPoint(myRendererComponent, point, myNodeIcon); + Rectangle bounds = myNodeIcon.getBounds(); + return bounds.x < handlePoint.x && bounds.getMaxX() >= handlePoint.x; + } + } + + private final class MyTree extends SimpleTree { + @Override + public String getToolTipText(MouseEvent event) { + if (event != null) { + Component component = getDeepestRendererComponentAt(event.getX(), event.getY()); + if (component instanceof JLabel) { + JLabel label = (JLabel)component; + if (label.getIcon() != null) { + String text = label.getToolTipText(); + if (text != null) { + return text; + } + } + } + } + return super.getToolTipText(event); + } + + @Override + protected boolean paintNodes() { + return false; + } + + @Override + protected boolean highlightSingleNode() { + return false; + } + + @Override + public void setUI(TreeUI ui) { + TreeUI actualUI = ui; + if (!(ui instanceof MyTreeUi)) { + actualUI = new MyTreeUi(); + } + super.setUI(actualUI); + } + + @Override + protected boolean isCustomUI() { + return true; + } + + @Override + protected void configureUiHelper(TreeUIHelper helper) { + } + + @Override + public boolean getScrollableTracksViewportWidth() { + return true; + } + + + @Override + public void processKeyEvent(KeyEvent e) { + TreePath path = myTree.getSelectionPath(); + if (path != null) { + if (e.getKeyCode() == KeyEvent.VK_LEFT) { + if (isExpanded(path)) { + collapsePath(path); + return; + } + } + else if (e.getKeyCode() == KeyEvent.VK_RIGHT) { + if (isCollapsed(path)) { + expandPath(path); + return; + } + } + } + super.processKeyEvent(e); + } + + @Override + protected void processMouseEvent(MouseEvent e) { + MyTreeUi ui = (MyTreeUi)myTree.getUI(); + boolean toggleNow = MouseEvent.MOUSE_RELEASED == e.getID() + && UIUtil.isActionClick(e, MouseEvent.MOUSE_RELEASED) + && !ui.isToggleEvent(e); + + if (toggleNow || MouseEvent.MOUSE_PRESSED == e.getID()) { + TreePath path = getPathForLocation(e.getX(), e.getY()); + if (path != null) { + Rectangle bounds = getPathBounds(path); + if (bounds != null && path.getLastPathComponent() instanceof DefaultMutableTreeNode) { + DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent(); + boolean selected = isPathSelected(path); + boolean expanded = isExpanded(path); + Component comp = + myRenderer.getTreeCellRendererComponent(this, node, selected, expanded, node.isLeaf(), getRowForPath(path), isFocusOwner()); + + comp.setBounds(bounds); + comp.validate(); + + Point point = new Point(e.getX() - bounds.x, e.getY() - bounds.y); + if (myRenderer.isUnderHandle(point)) { + if (toggleNow) { + ui.toggleExpandState(path); + } + e.consume(); + return; + } + } + } + } + + super.processMouseEvent(e); + } + + private final class MyTreeUi extends BasicTreeUI { + + @Override + public void toggleExpandState(TreePath path) { + super.toggleExpandState(path); + } + + @Override + public boolean isToggleEvent(MouseEvent event) { + return super.isToggleEvent(event); + } + + @Override + protected boolean shouldPaintExpandControl(TreePath path, + int row, + boolean isExpanded, + boolean hasBeenExpanded, + boolean isLeaf) { + return false; + } + + @Override + protected void paintHorizontalPartOfLeg(Graphics g, + Rectangle clipBounds, + Insets insets, + Rectangle bounds, + TreePath path, + int row, + boolean isExpanded, + boolean hasBeenExpanded, + boolean isLeaf) { + + } + + @Override + protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, TreePath path) { + } + + @Override + public void paint(Graphics g, JComponent c) { + GraphicsUtil.setupAntialiasing(g); + super.paint(g, c); + } + } + } + + private final class MyBuilder extends FilteringTreeBuilder { + + List<Object> myToExpandOnResetFilter; + boolean myRefilteringNow; + boolean myWasHoldingFilter; + + public MyBuilder(SimpleTreeStructure structure) { + super(myTree, myContext.getFilter(), structure, new WeightBasedComparator(false)); + myTree.addTreeExpansionListener(new TreeExpansionListener() { + public void treeExpanded(TreeExpansionEvent event) { + invalidateExpansions(); + } + + public void treeCollapsed(TreeExpansionEvent event) { + invalidateExpansions(); + } + }); + } + + private void invalidateExpansions() { + if (!myRefilteringNow) { + myToExpandOnResetFilter = null; + } + } + + @Override + protected boolean isSelectable(Object object) { + return object instanceof MyNode; + } + + @Override + public boolean isAutoExpandNode(NodeDescriptor nodeDescriptor) { + return myContext.isHoldingFilter(); + } + + @Override + public boolean isToEnsureSelectionOnFocusGained() { + return false; + } + + @Override + protected ActionCallback refilterNow(Object preferredSelection, boolean adjustSelection) { + final List<Object> toRestore = new ArrayList<Object>(); + if (myContext.isHoldingFilter() && !myWasHoldingFilter && myToExpandOnResetFilter == null) { + myToExpandOnResetFilter = myBuilder.getUi().getExpandedElements(); + } + else if (!myContext.isHoldingFilter() && myWasHoldingFilter && myToExpandOnResetFilter != null) { + toRestore.addAll(myToExpandOnResetFilter); + myToExpandOnResetFilter = null; + } + + myWasHoldingFilter = myContext.isHoldingFilter(); + + ActionCallback result = super.refilterNow(preferredSelection, adjustSelection); + myRefilteringNow = true; + return result.doWhenDone(new Runnable() { + public void run() { + myRefilteringNow = false; + if (!myContext.isHoldingFilter() && getSelectedElements().isEmpty()) { + restoreExpandedState(toRestore); + } + } + }); + } + + private void restoreExpandedState(List<Object> toRestore) { + TreePath[] selected = myTree.getSelectionPaths(); + if (selected == null) { + selected = new TreePath[0]; + } + + List<TreePath> toCollapse = new ArrayList<TreePath>(); + + for (int eachRow = 0; eachRow < myTree.getRowCount(); eachRow++) { + if (!myTree.isExpanded(eachRow)) continue; + + TreePath eachVisiblePath = myTree.getPathForRow(eachRow); + if (eachVisiblePath == null) continue; + + Object eachElement = myBuilder.getElementFor(eachVisiblePath.getLastPathComponent()); + if (toRestore.contains(eachElement)) continue; + + + for (TreePath eachSelected : selected) { + if (!eachVisiblePath.isDescendant(eachSelected)) { + toCollapse.add(eachVisiblePath); + } + } + } + + for (TreePath each : toCollapse) { + myTree.collapsePath(each); + } + } + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java index 27bb8eb5f245..6a37bb2fb69e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java @@ -17,7 +17,6 @@ package com.intellij.openapi.progress.impl; import com.intellij.concurrency.JobScheduler; import com.intellij.openapi.Disposable; -import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.application.ex.ApplicationEx; @@ -46,7 +45,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -public class ProgressManagerImpl extends ProgressManager implements Disposable{ +public class ProgressManagerImpl extends ProgressManager implements Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.progress.impl.ProgressManagerImpl"); private final AtomicInteger myCurrentUnsafeProgressCount = new AtomicInteger(0); private final AtomicInteger myCurrentModalProgressCount = new AtomicInteger(0); @@ -55,7 +54,7 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable{ private static final boolean DISABLED = "disabled".equals(System.getProperty("idea.ProcessCanceledException")); private final ScheduledFuture<?> myCheckCancelledFuture; - public ProgressManagerImpl(Application application) { + public ProgressManagerImpl() { if (DISABLED) { myCheckCancelledFuture = null; } @@ -64,7 +63,6 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable{ @Override public void run() { ourNeedToCheckCancel = true; - ProgressIndicatorProvider.ourNeedToCheckCancel = true; } }, 0, 10, TimeUnit.MILLISECONDS); } @@ -86,7 +84,6 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable{ if (ourLockedCheckCounter > 10) { ourLockedCheckCounter = 0; ourNeedToCheckCancel = true; - ProgressIndicatorProvider.ourNeedToCheckCancel = true; } } else { @@ -99,7 +96,6 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable{ public static void canceled() { ourNeedToCheckCancel = true; - ProgressIndicatorProvider.ourNeedToCheckCancel = true; } private static class NonCancelableIndicator extends EmptyProgressIndicator implements NonCancelableSection { @@ -371,6 +367,14 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable{ public static Future<?> runProcessWithProgressAsynchronously(@NotNull final Task.Backgroundable task, @NotNull final ProgressIndicator progressIndicator, @Nullable final Runnable continuation) { + return runProcessWithProgressAsynchronously(task, progressIndicator, continuation, ModalityState.NON_MODAL); + } + + @NotNull + public static Future<?> runProcessWithProgressAsynchronously(@NotNull final Task.Backgroundable task, + @NotNull final ProgressIndicator progressIndicator, + @Nullable final Runnable continuation, + @NotNull final ModalityState modalityState) { if (progressIndicator instanceof Disposable) { Disposer.register(ApplicationManager.getApplication(), (Disposable)progressIndicator); } @@ -397,7 +401,7 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable{ public void run() { task.onCancel(); } - }, ModalityState.NON_MODAL); + }, modalityState); } else { final Task.NotificationInfo notificationInfo = task.notifyFinished(); @@ -412,7 +416,7 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable{ public void run() { task.onSuccess(); } - }, ModalityState.NON_MODAL); + }, modalityState); } } }; @@ -493,10 +497,6 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable{ @Override public void dispose() { - stopCheckCanceled(); - } - - private void stopCheckCanceled() { if (myCheckCancelledFuture != null) myCheckCancelledFuture.cancel(false); } diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressIndicatorUtils.java b/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressIndicatorUtils.java index b98f19b25d17..0c18cb7706d9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressIndicatorUtils.java +++ b/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressIndicatorUtils.java @@ -61,7 +61,7 @@ public class ProgressIndicatorUtils { runWithWriteActionPriority(new ProgressIndicatorBase(), task); } - public static void runWithWriteActionPriority(final ProgressIndicator progressIndicator, final ReadTask task) { + private static void surroundWithListener(@NotNull final ProgressIndicator progressIndicator, @NotNull Runnable runnable) { final ApplicationAdapter listener = new ApplicationAdapter() { @Override public void beforeWriteActionStart(Object action) { @@ -69,45 +69,70 @@ public class ProgressIndicatorUtils { } }; final Application application = ApplicationManager.getApplication(); + application.addApplicationListener(listener); try { - application.addApplicationListener(listener); - ProgressManager.getInstance().runProcess(new Runnable(){ - @Override - public void run() { - // This read action can possible last for a long time, we want it to stop immediately on the first write access. - // For this purpose we launch it under empty progress and invoke progressIndicator#cancel on write access to avoid possible write lock delays. - try { - application.runReadAction(new Runnable() { - @Override - public void run() { - task.computeInReadAction(progressIndicator); - } - }); - } - catch (ProcessCanceledException ignore) { - } - finally { - if (progressIndicator.isCanceled()) { - task.onCanceled(progressIndicator); - } - } - } - }, progressIndicator); + runnable.run(); } finally { application.removeApplicationListener(listener); } } + + public static void runWithWriteActionPriority(@NotNull final ProgressIndicator progressIndicator, @NotNull final ReadTask task) { + surroundWithListener(progressIndicator, new Runnable() { + @Override + public void run() { + runUnderProgress(progressIndicator, task); + } + }); + } + + private static void runUnderProgress(@NotNull final ProgressIndicator progressIndicator, @NotNull final ReadTask task) { + ProgressManager.getInstance().runProcess(new Runnable() { + @Override + public void run() { + // This read action can possible last for a long time, we want it to stop immediately on the first write access. + // For this purpose we launch it under empty progress and invoke progressIndicator#cancel on write access to avoid possible write lock delays. + try { + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + task.computeInReadAction(progressIndicator); + } + }); + } + catch (ProcessCanceledException ignore) { + } + finally { + if (progressIndicator.isCanceled()) { + task.onCanceled(progressIndicator); + } + } + } + }, progressIndicator); + } + public static void scheduleWithWriteActionPriority(@NotNull final ReadTask task) { scheduleWithWriteActionPriority(new ProgressIndicatorBase(), task); } - public static void scheduleWithWriteActionPriority(final ProgressIndicator indicator, final ReadTask task) { - ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + public static void scheduleWithWriteActionPriority(@NotNull final ProgressIndicator indicator, @NotNull final ReadTask task) { + // we have to attach listeners in EDT to avoid "fire write action started while attach listeners from another thread" race condition + ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - runWithWriteActionPriority(indicator, task); + surroundWithListener(indicator, new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @Override + public void run() { + runUnderProgress(indicator, task); + } + }); + } + }); } }); } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java index a20df87718cc..308edb2d81a6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java @@ -392,7 +392,7 @@ public class DumbServiceImpl extends DumbService implements Disposable { } private class AppIconProgress extends ProgressIndicatorBase { - double lastFraction; + private double lastFraction; @Override public void setFraction(final double fraction) { @@ -408,16 +408,18 @@ public class DumbServiceImpl extends DumbService implements Disposable { @Override public void finish(@NotNull TaskInfo task) { - UIUtil.invokeLaterIfNeeded(new Runnable() { - @Override - public void run() { - AppIcon appIcon = AppIcon.getInstance(); - if (appIcon.hideProgress(myProject, "indexUpdate")) { - appIcon.requestAttention(myProject, false); - appIcon.setOkBadge(myProject, true); + if (lastFraction != 0) { // we should call setProgress at least once before + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + AppIcon appIcon = AppIcon.getInstance(); + if (appIcon.hideProgress(myProject, "indexUpdate")) { + appIcon.requestAttention(myProject, false); + appIcon.setOkBadge(myProject, true); + } } - } - }); + }); + } } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java index d1abc0816845..d1573e2adc7f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java @@ -745,16 +745,6 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTra @Override public void windowOpened(WindowEvent e) { - if (!isModal()) { - DialogWrapper wrapper = getDialogWrapper(); - if (wrapper != null) { - JComponent component = wrapper.getPreferredFocusedComponent(); - if (component != null) { - // request focus for non-modal dialog (i.e. TipDialog) - IdeFocusManager.findInstance().requestFocus(component, true); - } - } - } if (!SystemInfo.isMacOSLion) return; Window window = e.getWindow(); if (window instanceof Dialog) { @@ -774,14 +764,9 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTra queue.getKeyEventDispatcher().resetState(); } - // if (myProject != null) { - // Project project = myProject.get(); - //if (project != null && !project.isDisposed() && project.isInitialized()) { - // // IdeFocusManager.findInstanceByComponent(this).requestFocus(new MyFocusCommand(dialogWrapper), true); - //} - // } } + // Workaround for switching workspaces on dialog show if (SystemInfo.isMac && myProject != null && Registry.is("ide.mac.fix.dialog.showing") && !dialogWrapper.isModalProgress()) { final IdeFrame frame = WindowManager.getInstance().getIdeFrame(myProject.get()); AppIcon.getInstance().requestFocus(frame); diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginDownloader.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginDownloader.java index 9739d75be7e7..c93e69438766 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginDownloader.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginDownloader.java @@ -61,6 +61,7 @@ public class PluginDownloader { private String myFileName; private String myPluginName; + private BuildNumber myBuildNumber; private File myFile; private File myOldFile; @@ -74,12 +75,18 @@ public class PluginDownloader { myPluginVersion = pluginVersion; } - public PluginDownloader(String pluginId, String pluginUrl, String pluginVersion, String fileName, String pluginName) { + public PluginDownloader(String pluginId, + String pluginUrl, + String pluginVersion, + String fileName, + String pluginName, + BuildNumber buildNumber) { myPluginId = pluginId; myPluginUrl = pluginUrl; myPluginVersion = pluginVersion; myFileName = fileName; myPluginName = pluginName; + myBuildNumber = buildNumber; } @SuppressWarnings("UnusedDeclaration") @@ -89,7 +96,7 @@ public class PluginDownloader { } public boolean prepareToInstall(ProgressIndicator pi) throws IOException { - return prepareToInstall(pi, null); + return prepareToInstall(pi, myBuildNumber); } public boolean prepareToInstall(@Nullable ProgressIndicator pi, @Nullable BuildNumber forBuildNumber) throws IOException { @@ -289,9 +296,15 @@ public class PluginDownloader { } private URLConnection openConnection(final String url) throws IOException { - final URLConnection connection = ApplicationManager.getApplication() != null - ? HttpConfigurable.getInstance().openConnection(url) - : new URL(url).openConnection(); + final URLConnection connection; + if (ApplicationManager.getApplication() != null) { + connection = HttpConfigurable.getInstance().openConnection(url); + } + else { + connection = new URL(url).openConnection(); + connection.setConnectTimeout(HttpConfigurable.CONNECTION_TIMEOUT); + connection.setReadTimeout(HttpConfigurable.CONNECTION_TIMEOUT); + } if (connection instanceof HttpURLConnection) { final int responseCode = ((HttpURLConnection)connection).getResponseCode(); if (responseCode != HttpURLConnection.HTTP_OK) { @@ -385,9 +398,14 @@ public class PluginDownloader { } public static PluginDownloader createDownloader(IdeaPluginDescriptor descriptor) throws UnsupportedEncodingException { + return createDownloader(descriptor, null); + } + + public static PluginDownloader createDownloader(IdeaPluginDescriptor descriptor, + BuildNumber buildNumber) throws UnsupportedEncodingException { PluginDownloader downloader = new PluginDownloader(descriptor.getPluginId().getIdString(), - UpdateChecker.getDownloadUrl(descriptor), - descriptor.getVersion(), null, descriptor.getName()); + UpdateChecker.getDownloadUrl(descriptor, buildNumber), + descriptor.getVersion(), null, descriptor.getName(), buildNumber); downloader.setDescriptor(descriptor); return downloader; } diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.java index 354f89c22820..83858cc4fb40 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.java @@ -239,13 +239,13 @@ public final class UpdateChecker { updateSettings.myOutdatedPlugins.clear(); if (!toUpdate.isEmpty()) { try { - final List<IdeaPluginDescriptor> process = RepositoryHelper.loadPluginsFromRepository(indicator); + final List<IdeaPluginDescriptor> process = RepositoryHelper.loadPluginsFromRepository(indicator, buildNumber); for (IdeaPluginDescriptor loadedPlugin : process) { final PluginId pluginId = loadedPlugin.getPluginId(); final String idString = pluginId.getIdString(); if (!toUpdate.containsKey(idString)) continue; if (!downloaded.containsKey(pluginId)) { - prepareToInstall(PluginDownloader.createDownloader(loadedPlugin), buildNumber, downloaded, incompatiblePlugins, true, indicator); + prepareToInstall(PluginDownloader.createDownloader(loadedPlugin, buildNumber), buildNumber, downloaded, incompatiblePlugins, true, indicator); } } } @@ -376,7 +376,7 @@ public final class UpdateChecker { final List<IdeaPluginDescriptor> descriptors = RepositoryHelper.loadPluginsFromDescription(inputStream, indicator); for (IdeaPluginDescriptor descriptor : descriptors) { ((PluginNode)descriptor).setRepositoryName(host); - prepareToInstall(PluginDownloader.createDownloader(descriptor), buildNumber, downloaded, incompatiblePlugins, collectToUpdate, + prepareToInstall(PluginDownloader.createDownloader(descriptor, buildNumber), buildNumber, downloaded, incompatiblePlugins, collectToUpdate, indicator); } @@ -424,7 +424,7 @@ public final class UpdateChecker { if (progressIndicator != null) { progressIndicator.setText2(finalPluginUrl); } - final PluginDownloader downloader = new PluginDownloader(pluginId, finalPluginUrl, pluginVersion, null, null); + final PluginDownloader downloader = new PluginDownloader(pluginId, finalPluginUrl, pluginVersion, null, null, buildNumber); prepareToInstall(downloader, buildNumber, downloaded, incompatiblePlugins, collectToUpdate, indicator); } catch (IOException e) { @@ -624,11 +624,15 @@ public final class UpdateChecker { urlToCheck = url; } - HttpURLConnection connection = ApplicationManager.getApplication() != null ? - HttpConfigurable.getInstance().openHttpConnection(urlToCheck) : - (HttpURLConnection)new URL(urlToCheck).openConnection(); - connection.setReadTimeout(HttpConfigurable.CONNECTION_TIMEOUT); - connection.setConnectTimeout(HttpConfigurable.CONNECTION_TIMEOUT); + HttpURLConnection connection; + if (ApplicationManager.getApplication() != null) { + connection = HttpConfigurable.getInstance().openHttpConnection(urlToCheck); + } + else { + connection = (HttpURLConnection)new URL(urlToCheck).openConnection(); + connection.setReadTimeout(HttpConfigurable.CONNECTION_TIMEOUT); + connection.setConnectTimeout(HttpConfigurable.CONNECTION_TIMEOUT); + } connection.connect(); inputStreams[0] = connection.getInputStream(); @@ -862,7 +866,7 @@ public final class UpdateChecker { } } - static String getDownloadUrl(IdeaPluginDescriptor descriptor) throws UnsupportedEncodingException { + static String getDownloadUrl(IdeaPluginDescriptor descriptor, @Nullable BuildNumber buildNumber) throws UnsupportedEncodingException { String url = null; if (descriptor instanceof PluginNode) { url = ((PluginNode)descriptor).getDownloadUrl(); @@ -881,11 +885,13 @@ public final class UpdateChecker { String uuid = ApplicationManager.getApplication() == null ? UUID.randomUUID().toString() : getInstallationUID(PropertiesComponent.getInstance()); - String buildNumber = ApplicationManager.getApplication() != null - ? ApplicationInfo.getInstance().getApiVersion() - : ApplicationInfoImpl.getShadowInstance().getBuild().asString(); + String buildNumberAsString = buildNumber != null + ? buildNumber.asString() + : ApplicationManager.getApplication() != null + ? ApplicationInfo.getInstance().getApiVersion() + : ApplicationInfoImpl.getShadowInstance().getBuild().asString(); url = RepositoryHelper.getDownloadUrl() + URLEncoder.encode(descriptor.getPluginId().getIdString(), "UTF8") + - "&build=" + buildNumber + "&uuid=" + URLEncoder.encode(uuid, "UTF8"); + "&build=" + buildNumberAsString + "&uuid=" + URLEncoder.encode(uuid, "UTF8"); } return url; } diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdatesXmlLoader.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdatesXmlLoader.java index d1010f52e1b1..9c6c6a2020e5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdatesXmlLoader.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdatesXmlLoader.java @@ -32,6 +32,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; +import java.net.URLConnection; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; @@ -67,7 +68,10 @@ public class UpdatesXmlLoader { URL requestUrl = prepareRequestUrl(updateUrl); - final InputStream inputStream = requestUrl.openStream(); + URLConnection connection = requestUrl.openConnection(); + connection.setConnectTimeout(HttpConfigurable.CONNECTION_TIMEOUT); + connection.setReadTimeout(HttpConfigurable.CONNECTION_TIMEOUT); + final InputStream inputStream = connection.getInputStream(); Reader reader = new InputStreamReader(inputStream); try { return new UpdatesInfo(JDOMUtil.loadDocument(inputStream).getRootElement()); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/ex/ToolWindowManagerEx.java b/platform/platform-impl/src/com/intellij/openapi/wm/ex/ToolWindowManagerEx.java index fbe213f20bb0..2bff2a10d5cd 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/ex/ToolWindowManagerEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/ex/ToolWindowManagerEx.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -28,7 +28,7 @@ import javax.swing.*; import java.util.List; public abstract class ToolWindowManagerEx extends ToolWindowManager { - public abstract void initToolWindow(ToolWindowEP bean); + public abstract void initToolWindow(@NotNull ToolWindowEP bean); public static ToolWindowManagerEx getInstanceEx(final Project project){ return (ToolWindowManagerEx)getInstance(project); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java index c302cce7572e..867c404073d3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java @@ -203,9 +203,6 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { public ActionCallback requestFocus(@NotNull final FocusCommand command, final boolean forced) { assertDispatchThread(); - if (!forced && !command.canFocusChangeFrom(getFocusOwner())) { - return ActionCallback.REJECTED; - } if (isInternalMode) { recordCommand(command, new Throwable(), forced); } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeGlassPaneImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeGlassPaneImpl.java index 71f964504102..1613953287b6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeGlassPaneImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeGlassPaneImpl.java @@ -24,8 +24,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.ui.Divider; import com.intellij.openapi.ui.Painter; import com.intellij.openapi.ui.impl.GlassPaneDialogWrapperPeer; -import com.intellij.openapi.ui.popup.Balloon; -import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Weighted; import com.intellij.openapi.wm.IdeGlassPane; @@ -287,8 +285,6 @@ public class IdeGlassPaneImpl extends JPanel implements IdeGlassPaneEx, IdeEvent return false; } - private MouseEvent myLastRedispatchedEvent = null; - private boolean preprocess(final MouseEvent e, final boolean motion, JRootPane eventRootPane) { try { final MouseEvent event = convertEvent(e, eventRootPane); @@ -297,25 +293,6 @@ public class IdeGlassPaneImpl extends JPanel implements IdeGlassPaneEx, IdeEvent return false; } - Component c = SwingUtilities.getDeepestComponentAt(e.getComponent(), e.getX(), e.getY()); - Balloon balloon = JBPopupFactory.getInstance().getParentBalloonFor(c); - if (balloon != null && myLastRedispatchedEvent != e) { - if (e.getID() == MouseEvent.MOUSE_PRESSED - && IdeTooltipManager.getInstance().hasCurrent() - && IdeTooltipManager.getInstance().hideCurrent(event, null, null, false)) { - //noinspection SSBasedInspection - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - myLastRedispatchedEvent = e; - IdeEventQueue.getInstance().dispatchEvent(e); - } - }); - } - return false; - } - myLastRedispatchedEvent = null; - for (EventListener each : mySortedMouseListeners) { if (motion && each instanceof MouseMotionListener) { fireMouseMotion((MouseMotionListener)each, event); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java index 8b78b1632f79..7b9a7494c640 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java @@ -87,6 +87,7 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { return tw; } + @NotNull @Override public ToolWindow registerToolWindow(@NotNull String id, @NotNull JComponent component, @@ -96,11 +97,13 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { return doRegisterToolWindow(id, parentDisposable); } + @NotNull @Override public ToolWindow registerToolWindow(@NotNull String id, @NotNull JComponent component, @NotNull ToolWindowAnchor anchor) { return doRegisterToolWindow(id, null); } + @NotNull @Override public ToolWindow registerToolWindow(@NotNull String id, @NotNull JComponent component, @@ -111,19 +114,22 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { return doRegisterToolWindow(id, parentDisposable); } + @NotNull @Override public ToolWindow registerToolWindow(@NotNull String id, @NotNull JComponent component, @NotNull ToolWindowAnchor anchor, - Disposable parentDisposable) { + @NotNull Disposable parentDisposable) { return doRegisterToolWindow(id, parentDisposable); } + @NotNull @Override public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @NotNull final ToolWindowAnchor anchor) { return doRegisterToolWindow(id, null); } + @NotNull @Override public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @@ -132,6 +138,7 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { return doRegisterToolWindow(id, null); } + @NotNull @Override public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @NotNull final ToolWindowAnchor anchor, final Disposable parentDisposable, final boolean dumbAware) { @@ -152,6 +159,7 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { return false; } + @NotNull @Override public String[] getToolWindowIds() { return ArrayUtil.EMPTY_STRING_ARRAY; @@ -168,9 +176,10 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { } @Override - public void invokeLater(Runnable runnable) { + public void invokeLater(@NotNull Runnable runnable) { } + @NotNull @Override public IdeFocusManager getFocusManager() { return IdeFocusManagerHeadless.INSTANCE; @@ -190,7 +199,7 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { } @Override - public void initToolWindow(ToolWindowEP bean) { + public void initToolWindow(@NotNull ToolWindowEP bean) { } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowImpl.java index efcdd148d57b..1fa2bd95defe 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowImpl.java @@ -72,6 +72,7 @@ public final class ToolWindowImpl implements ToolWindowEx { private boolean myPlaceholderMode; private ToolWindowFactory myContentFactory; + @NotNull private ActionCallback myActivation = new ActionCallback.Done(); private final BusyObject.Impl myShowing = new BusyObject.Impl() { @Override @@ -115,18 +116,22 @@ public final class ToolWindowImpl implements ToolWindowEx { myChangeSupport.addPropertyChangeListener(l); } + @Override public final void removePropertyChangeListener(final PropertyChangeListener l) { myChangeSupport.removePropertyChangeListener(l); } + @Override public final void activate(final Runnable runnable) { activate(runnable, true); } + @Override public void activate(@Nullable final Runnable runnable, final boolean autoFocusContents) { activate(runnable, autoFocusContents, true); } + @Override public void activate(@Nullable final Runnable runnable, boolean autoFocusContents, boolean forced) { ApplicationManager.getApplication().assertIsDispatchThread(); @@ -136,6 +141,7 @@ public final class ToolWindowImpl implements ToolWindowEx { myToolWindowManager.activateToolWindow(myId, forced, autoFocusContents); getActivation().doWhenDone(new Runnable() { + @Override public void run() { myToolWindowManager.invokeLater(new Runnable() { @Override @@ -150,6 +156,7 @@ public final class ToolWindowImpl implements ToolWindowEx { }); } + @Override public final boolean isActive() { ApplicationManager.getApplication().assertIsDispatchThread(); if (myToolWindowManager.isEditorComponentActive()) return false; @@ -182,11 +189,13 @@ public final class ToolWindowImpl implements ToolWindowEx { return result; } + @Override public final void show(final Runnable runnable) { ApplicationManager.getApplication().assertIsDispatchThread(); myToolWindowManager.showToolWindow(myId); if (runnable != null) { getActivation().doWhenDone(new Runnable() { + @Override public void run() { myToolWindowManager.invokeLater(runnable); } @@ -194,6 +203,7 @@ public final class ToolWindowImpl implements ToolWindowEx { } } + @Override public final void hide(@Nullable final Runnable runnable) { ApplicationManager.getApplication().assertIsDispatchThread(); myToolWindowManager.hideToolWindow(myId, false); @@ -202,14 +212,17 @@ public final class ToolWindowImpl implements ToolWindowEx { } } + @Override public final boolean isVisible() { return myToolWindowManager.isToolWindowVisible(myId); } + @Override public final ToolWindowAnchor getAnchor() { return myToolWindowManager.getToolWindowAnchor(myId); } + @Override public final void setAnchor(final ToolWindowAnchor anchor, @Nullable final Runnable runnable) { ApplicationManager.getApplication().assertIsDispatchThread(); myToolWindowManager.setToolWindowAnchor(myId, anchor); @@ -218,11 +231,13 @@ public final class ToolWindowImpl implements ToolWindowEx { } } + @Override public boolean isSplitMode() { ApplicationManager.getApplication().assertIsDispatchThread(); return myToolWindowManager.isSplitMode(myId); } + @Override public void setContentUiType(ToolWindowContentUiType type, @Nullable Runnable runnable) { ApplicationManager.getApplication().assertIsDispatchThread(); myToolWindowManager.setContentUiType(myId, type); @@ -231,15 +246,18 @@ public final class ToolWindowImpl implements ToolWindowEx { } } + @Override public void setDefaultContentUiType(@NotNull ToolWindowContentUiType type) { myToolWindowManager.setDefaultContentUiType(this, type); } + @Override public ToolWindowContentUiType getContentUiType() { ApplicationManager.getApplication().assertIsDispatchThread(); return myToolWindowManager.getContentUiType(myId); } + @Override public void setSplitMode(final boolean isSideTool, @Nullable final Runnable runnable) { ApplicationManager.getApplication().assertIsDispatchThread(); myToolWindowManager.setSideTool(myId, isSideTool); @@ -248,20 +266,24 @@ public final class ToolWindowImpl implements ToolWindowEx { } } + @Override public final void setAutoHide(final boolean state) { ApplicationManager.getApplication().assertIsDispatchThread(); myToolWindowManager.setToolWindowAutoHide(myId, state); } + @Override public final boolean isAutoHide() { ApplicationManager.getApplication().assertIsDispatchThread(); return myToolWindowManager.isToolWindowAutoHide(myId); } + @Override public final ToolWindowType getType() { return myToolWindowManager.getToolWindowType(myId); } + @Override public final void setType(final ToolWindowType type, @Nullable final Runnable runnable) { ApplicationManager.getApplication().assertIsDispatchThread(); myToolWindowManager.setToolWindowType(myId, type); @@ -270,19 +292,23 @@ public final class ToolWindowImpl implements ToolWindowEx { } } + @Override public final ToolWindowType getInternalType() { ApplicationManager.getApplication().assertIsDispatchThread(); return myToolWindowManager.getToolWindowInternalType(myId); } + @Override public void stretchWidth(int value) { myToolWindowManager.stretchWidth(this, value); } + @Override public void stretchHeight(int value) { myToolWindowManager.stretchHeight(this, value); } + @Override public InternalDecorator getDecorator() { return myDecorator; } @@ -297,6 +323,7 @@ public final class ToolWindowImpl implements ToolWindowEx { getDecorator().setTitleActions(actions); } + @Override public final void setAvailable(final boolean available, final Runnable runnable) { ApplicationManager.getApplication().assertIsDispatchThread(); final Boolean oldAvailable = myAvailable ? Boolean.TRUE : Boolean.FALSE; @@ -307,6 +334,7 @@ public final class ToolWindowImpl implements ToolWindowEx { } } + @Override public void installWatcher(ContentManager contentManager) { new ContentManagerWatcher(this, contentManager); } @@ -316,14 +344,17 @@ public final class ToolWindowImpl implements ToolWindowEx { * <code>ContentManager</code> class. Otherwise it delegates the functionality to the * passed content manager. */ + @Override public final boolean isAvailable() { return myAvailable && myComponent != null; } + @Override public final JComponent getComponent() { return myComponent; } + @Override public ContentManager getContentManager() { return myContentManager; } @@ -332,6 +363,7 @@ public final class ToolWindowImpl implements ToolWindowEx { return myContentUI; } + @Override public final Icon getIcon() { ApplicationManager.getApplication().assertIsDispatchThread(); return myIcon; @@ -342,17 +374,20 @@ public final class ToolWindowImpl implements ToolWindowEx { return myId; } + @Override public final String getTitle() { ApplicationManager.getApplication().assertIsDispatchThread(); return getSelectedContent().getDisplayName(); } + @Override @NotNull public final String getStripeTitle() { ApplicationManager.getApplication().assertIsDispatchThread(); return ObjectUtils.notNull(myStripeTitle, myId); } + @Override public final void setIcon(final Icon icon) { ApplicationManager.getApplication().assertIsDispatchThread(); final Icon oldIcon = getIcon(); @@ -364,6 +399,7 @@ public final class ToolWindowImpl implements ToolWindowEx { myChangeSupport.firePropertyChange(PROP_ICON, oldIcon, icon); } + @Override public final void setTitle(String title) { ApplicationManager.getApplication().assertIsDispatchThread(); String oldTitle = getTitle(); @@ -371,6 +407,7 @@ public final class ToolWindowImpl implements ToolWindowEx { myChangeSupport.firePropertyChange(PROP_TITLE, oldTitle, title); } + @Override public final void setStripeTitle(@NotNull String stripeTitle) { ApplicationManager.getApplication().assertIsDispatchThread(); String oldTitle = myStripeTitle; @@ -415,18 +452,22 @@ public final class ToolWindowImpl implements ToolWindowEx { return myDecorator != null ? myDecorator.createPopupGroup() : null; } + @Override public void setDefaultState(@Nullable final ToolWindowAnchor anchor, @Nullable final ToolWindowType type, @Nullable final Rectangle floatingBounds) { myToolWindowManager.setDefaultState(this, anchor, type, floatingBounds); } + @Override public void setToHideOnEmptyContent(final boolean hideOnEmpty) { myHideOnEmptyContent = hideOnEmpty; } + @Override public boolean isToHideOnEmptyContent() { return myHideOnEmptyContent; } + @Override public boolean isDisposed() { return myContentManager.isDisposed(); } @@ -439,12 +480,15 @@ public final class ToolWindowImpl implements ToolWindowEx { myPlaceholderMode = placeholderMode; } + @Override + @NotNull public ActionCallback getActivation() { return myActivation; } - public ActionCallback setActivation(ActionCallback activation) { - if (myActivation != null && !myActivation.isProcessed() && !myActivation.equals(activation)) { + @NotNull + public ActionCallback setActivation(@NotNull ActionCallback activation) { + if (!myActivation.isProcessed() && !myActivation.equals(activation)) { myActivation.setRejected(); } @@ -467,6 +511,7 @@ public final class ToolWindowImpl implements ToolWindowEx { } } + @Override public void showContentPopup(InputEvent inputEvent) { myContentUI.toggleContentPopup(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java index 555e0666f72e..95b3d54f8030 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java @@ -363,6 +363,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements return IdeFocusManager.getInstance(project); } + @NotNull public Project getProject() { return myProject; } @@ -482,21 +483,14 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements } @Override - public void initToolWindow(ToolWindowEP bean) { - ToolWindowAnchor toolWindowAnchor; - try { - toolWindowAnchor = ToolWindowAnchor.fromText(bean.anchor); - } - catch (Exception e) { - LOG.error(e); - return; - } + public void initToolWindow(@NotNull ToolWindowEP bean) { JLabel label = new JLabel("Initializing...", SwingConstants.CENTER); label.setOpaque(true); final Color treeBg = UIManager.getColor("Tree.background"); label.setBackground(ColorUtil.toAlpha(treeBg, 180)); final Color treeFg = UIUtil.getTreeForeground(); label.setForeground(ColorUtil.toAlpha(treeFg, 180)); + ToolWindowAnchor toolWindowAnchor = ToolWindowAnchor.fromText(bean.anchor); final ToolWindowFactory factory = bean.getToolWindowFactory(); final ToolWindowImpl toolWindow = (ToolWindowImpl)registerToolWindow(bean.id, label, toolWindowAnchor, myProject, DumbService.isDumbAware(factory), @@ -747,7 +741,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements myActiveStack.push(id); } - if (autoFocusContents) { + if (autoFocusContents && ApplicationManager.getApplication().isActive()) { appendRequestFocusInToolWindowCmd(id, commandsList, forcedFocusRequest); } } @@ -826,6 +820,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements appendApplyWindowInfoCmd(info, commandsList); } + @NotNull @Override public String[] getToolWindowIds() { final WindowInfoImpl[] infos = myLayout.getInfos(); @@ -1087,6 +1082,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements appendApplyWindowInfoCmd(toBeShownInfo, commandsList); } + @NotNull @Override public ToolWindow registerToolWindow(@NotNull final String id, @NotNull final JComponent component, @@ -1094,6 +1090,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements return registerToolWindow(id, component, anchor, false); } + @NotNull @Override public ToolWindow registerToolWindow(@NotNull final String id, @NotNull JComponent component, @@ -1102,6 +1099,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements return registerToolWindow(id, component, anchor, parentDisposable, false, false); } + @NotNull @Override public ToolWindow registerToolWindow(@NotNull String id, @NotNull JComponent component, @@ -1111,6 +1109,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements return registerToolWindow(id, component, anchor, parentDisposable, canWorkInDumbMode, false); } + @NotNull @Override public ToolWindow registerToolWindow(@NotNull final String id, @NotNull JComponent component, @@ -1120,6 +1119,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements return registerDisposable(id, parentDisposable, registerToolWindow(id, component, anchor, false, canCloseContents, canWorkInDumbMode)); } + @NotNull private ToolWindow registerToolWindow(@NotNull final String id, @NotNull final JComponent component, @NotNull final ToolWindowAnchor anchor, @@ -1127,11 +1127,13 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements return registerToolWindow(id, component, anchor, false, false, canWorkInDumbMode); } + @NotNull @Override public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @NotNull final ToolWindowAnchor anchor) { return registerToolWindow(id, null, anchor, false, canCloseContent, false); } + @NotNull @Override public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @@ -1141,15 +1143,18 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements } + @NotNull @Override public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @NotNull final ToolWindowAnchor anchor, @NotNull final Disposable parentDisposable, final boolean canWorkInDumbMode) { - return registerDisposable(id, parentDisposable, registerToolWindow(id, null, anchor, false, canCloseContent, canWorkInDumbMode)); + ToolWindow window = registerToolWindow(id, null, anchor, false, canCloseContent, canWorkInDumbMode); + return registerDisposable(id, parentDisposable, window); } + @NotNull private ToolWindow registerToolWindow(@NotNull final String id, @Nullable final JComponent component, @NotNull final ToolWindowAnchor anchor, @@ -1216,7 +1221,8 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements return toolWindow; } - private ToolWindow registerDisposable(final String id, @NotNull final Disposable parentDisposable, final ToolWindow window) { + @NotNull + private ToolWindow registerDisposable(@NotNull final String id, @NotNull final Disposable parentDisposable, @NotNull ToolWindow window) { Disposer.register(parentDisposable, new Disposable() { @Override public void dispose() { @@ -1353,12 +1359,13 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements } @Override - public void invokeLater(final Runnable runnable) { + public void invokeLater(@NotNull final Runnable runnable) { List<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>(); commandList.add(new InvokeLaterCmd(runnable, myWindowManager.getCommandProcessor())); execute(commandList); } + @NotNull @Override public IdeFocusManager getFocusManager() { return IdeFocusManager.getInstance(myProject); |