From 1ac3cfbf20ee9f4e4d902d3bba0b83577d0b1898 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 7 Jun 2012 20:43:00 +0400 Subject: [PATCH 01/20] [merge] Don't draw these weird horizontal lines at the gutter. --- .../openapi/diff/impl/incrementalMerge/ChangeType.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java index 49f2423b8dc6..927333625d6f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java @@ -16,7 +16,6 @@ package com.intellij.openapi.diff.impl.incrementalMerge; import com.intellij.openapi.diff.ex.DiffFragment; -import com.intellij.openapi.diff.impl.highlighting.LineRenderer; import com.intellij.openapi.diff.impl.util.TextDiffType; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.markup.*; @@ -104,7 +103,6 @@ public class ChangeType { highlighter.setLineSeparatorPlacement(SeparatorPlacement.TOP); highlighter.setLineSeparatorColor(separatorColor); highlighter.setLineSeparatorRenderer(separatorRenderer); - highlighter.setLineMarkerRenderer(LineRenderer.top()); } if (text.charAt(length - 1) == '\n') { @@ -116,7 +114,6 @@ public class ChangeType { highlighter.setLineSeparatorPlacement(SeparatorPlacement.BOTTOM); highlighter.setLineSeparatorColor(separatorColor); highlighter.setLineSeparatorRenderer(separatorRenderer); - highlighter.setLineMarkerRenderer(LineRenderer.bottom()); } return highlighter; } From 374026b418fe091144d33b4453f398c68265ffba Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 7 Jun 2012 20:46:32 +0400 Subject: [PATCH 02/20] javadoc --- .../intellij/openapi/diff/impl/settings/DiffPreviewPanel.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/lang-impl/src/com/intellij/openapi/diff/impl/settings/DiffPreviewPanel.java b/platform/lang-impl/src/com/intellij/openapi/diff/impl/settings/DiffPreviewPanel.java index d7ee52134f17..c9ef205ff2f5 100644 --- a/platform/lang-impl/src/com/intellij/openapi/diff/impl/settings/DiffPreviewPanel.java +++ b/platform/lang-impl/src/com/intellij/openapi/diff/impl/settings/DiffPreviewPanel.java @@ -40,6 +40,9 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; +/** + * The panel from the Settings, that allows to see changes to diff/merge coloring scheme right away. + */ public class DiffPreviewPanel implements PreviewPanel { private final MergePanel2.AsComponent myMergePanelComponent; private final JPanel myPanel = new JPanel(new BorderLayout()); From acc269784454797d47db49538bc861ba5c1cc2c0 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sat, 9 Jun 2012 12:32:04 +0400 Subject: [PATCH 03/20] @Nullable --- .../src/com/intellij/openapi/actionSystem/ToggleAction.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/ToggleAction.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/ToggleAction.java index 9a1235e3ec6d..f09383c7bcea 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/ToggleAction.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/ToggleAction.java @@ -15,6 +15,8 @@ */ package com.intellij.openapi.actionSystem; +import org.jetbrains.annotations.Nullable; + import javax.swing.*; /** @@ -26,11 +28,11 @@ public abstract class ToggleAction extends AnAction implements Toggleable { public ToggleAction(){ } - public ToggleAction(final String text){ + public ToggleAction(@Nullable final String text){ super(text); } - public ToggleAction(final String text, final String description, final Icon icon){ + public ToggleAction(@Nullable final String text, @Nullable final String description, @Nullable final Icon icon){ super(text, description, icon); } From 62ee77a2d30a99a8567396c1c0b943bbec3d6142 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sat, 9 Jun 2012 18:15:15 +0400 Subject: [PATCH 04/20] [merge tool] Make possible to hide/show line numbers, soft wraps and use other editor settings. * Introduce an action in the merge tool toolbar to change settings of all 3 editors. * Save the settings in MergeToolSettings, workspace-level. * Since all 4 settings are similar, hold them in a separate enum MergeToolEditorSetting with the #apply() method which would apply the setting to the passed editor. * Get rid of EditorPlace.ViewProperty and all related methods and classes. Instead use more simple way to initialize settings (MergePanel2#initEditorSettings) and to change them (#setHighlighterSettings and other - for DiffPreviewPanel * Remove EditorPlace.ComponentState - no need in the additional level of abstraction since the DiffEditorState is the only usage. * Make some small cleanups and refactorings. --- .../diff/impl/settings/DiffPreviewPanel.java | 8 +- .../impl/incrementalMerge/ui/EditorPlace.java | 41 +---- .../impl/incrementalMerge/ui/MergePanel2.java | 163 ++++++++++-------- .../ui/MergeToolEditorSetting.java | 73 ++++++++ .../ui/MergeToolSettings.java | 114 ++++++++++++ .../ui/MergeToolSettingsAction.java | 126 ++++++++++++++ .../src/META-INF/VcsExtensions.xml | 2 + 7 files changed, 412 insertions(+), 115 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergeToolEditorSetting.java create mode 100644 platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergeToolSettings.java create mode 100644 platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergeToolSettingsAction.java diff --git a/platform/lang-impl/src/com/intellij/openapi/diff/impl/settings/DiffPreviewPanel.java b/platform/lang-impl/src/com/intellij/openapi/diff/impl/settings/DiffPreviewPanel.java index c9ef205ff2f5..5a4544f2c0ca 100644 --- a/platform/lang-impl/src/com/intellij/openapi/diff/impl/settings/DiffPreviewPanel.java +++ b/platform/lang-impl/src/com/intellij/openapi/diff/impl/settings/DiffPreviewPanel.java @@ -54,10 +54,8 @@ public class DiffPreviewPanel implements PreviewPanel { myPanel.add(myMergePanelComponent, BorderLayout.CENTER); myMergePanelComponent.setToolbarEnabled(false); MergePanel2 mergePanel = getMergePanel(); - mergePanel.setEditorProperty(MergePanel2.LINE_NUMBERS, Boolean.FALSE); - mergePanel.setEditorProperty(MergePanel2.LINE_MARKERS_AREA, Boolean.FALSE); - mergePanel.setEditorProperty(MergePanel2.ADDITIONAL_LINES, 1); - mergePanel.setEditorProperty(MergePanel2.ADDITIONAL_COLUMNS, 1); + mergePanel.setShowLineNumbers(false); + mergePanel.setAdditionalLinesAndColumns(1, 1); mergePanel.setScrollToFirstDiff(false); for (int i = 0; i < MergePanel2.EDITORS_COUNT; i++) { @@ -105,7 +103,7 @@ public class DiffPreviewPanel implements PreviewPanel { public void setColorScheme(final EditorColorsScheme highlighterSettings) { getMergePanel().setColorScheme(highlighterSettings); - getMergePanel().setEditorProperty(MergePanel2.HIGHLIGHTER_SETTINGS, highlighterSettings); + getMergePanel().setHighlighterSettings(highlighterSettings); } private class EditorMouseListener extends EditorMouseMotionAdapter { diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java index 7624bc5fd8e8..e90bf7de94a4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java @@ -20,7 +20,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorFactory; -import com.intellij.openapi.editor.ex.EditorEx; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; @@ -28,11 +28,11 @@ import java.util.ArrayList; public class EditorPlace extends JComponent implements Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.incrementalMerge.ui.EditorPlace"); - private final ComponentState myState; + private final MergePanel2.DiffEditorState myState; private final ArrayList myListeners = new ArrayList(); private Editor myEditor = null; - public EditorPlace(ComponentState state) { + public EditorPlace(MergePanel2.DiffEditorState state) { myState = state; setLayout(new BorderLayout()); } @@ -109,25 +109,10 @@ public class EditorPlace extends JComponent implements Disposable { return myState.getDocument(); } - public ComponentState getState() { + public MergePanel2.DiffEditorState getState() { return myState; } - public abstract static class ComponentState { - private Document myDocument; - public abstract Editor createEditor(); - - public void setDocument(Document document) { - myDocument = document; - } - - public Document getDocument() { - return myDocument; - } - - public abstract void updateValue(Editor editor, ViewProperty property, T value); - } - public interface EditorListener { void onEditorCreated(EditorPlace place); void onEditorReleased(Editor releasedEditor); @@ -137,27 +122,11 @@ public class EditorPlace extends JComponent implements Disposable { return EditorFactory.getInstance(); } + @Nullable public JComponent getContentComponent() { return myEditor == null ? null : myEditor.getContentComponent(); } - public abstract static class ViewProperty { - private final T myDefault; - - protected ViewProperty(T aDefault) { - myDefault = aDefault; - } - - public void updateEditor(Editor editor, T value, ComponentState state) { - if (editor == null) return; - if (value == null) value = myDefault; - EditorEx editorEx = (EditorEx)editor; - doUpdateEditor(editorEx, value, state); - } - - protected abstract void doUpdateEditor(EditorEx editorEx, T value, ComponentState state); - } - public void dispose() { removeEditor(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java index 1c3404c1f372..2ebac7c61ddb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java @@ -21,6 +21,7 @@ import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.IdeActions; import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.*; import com.intellij.openapi.diff.actions.NextDiffAction; @@ -50,11 +51,10 @@ import com.intellij.openapi.ui.DialogBuilder; import com.intellij.openapi.ui.LabeledComponent; import com.intellij.openapi.util.Disposer; import com.intellij.ui.EditorNotificationPanel; -import com.intellij.util.containers.HashMap; -import com.intellij.util.containers.HashSet; import com.intellij.util.diff.FilesTooBigForDiffException; import gnu.trove.TIntHashSet; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; @@ -109,65 +109,51 @@ public class MergePanel2 implements DiffViewer { myEditorsPanels[i].setComponent(editorPlace); } FontSizeSynchronizer.attachTo(editorPlaces); - myPanel = new DiffPanelOuterComponent(TextDiffType.MERGE_TYPES, TOOLBAR); + myPanel = new DiffPanelOuterComponent(TextDiffType.MERGE_TYPES, createToolbar()); myPanel.insertDiffComponent(new ThreePanels(myEditorsPanels, myDividers), new MyScrollingPanel()); myProvider = new MyDataProvider(); myPanel.setDataProvider(myProvider); myBuilder = builder; } - private static final EditorPlace.ViewProperty EDITOR_SCHEME = new EditorPlace.ViewProperty(null) { - public void doUpdateEditor(EditorEx editorEx, EditorColorsScheme scheme, EditorPlace.ComponentState state) { - if (scheme != null) editorEx.setColorsScheme(scheme); - } - }; - public static final EditorPlace.ViewProperty LINE_NUMBERS = new EditorPlace.ViewProperty(Boolean.TRUE) { - public void doUpdateEditor(EditorEx editorEx, Boolean aBoolean, EditorPlace.ComponentState state) { - editorEx.getSettings().setLineNumbersShown(aBoolean.booleanValue()); - } - }; - public static final EditorPlace.ViewProperty LINE_MARKERS_AREA = new EditorPlace.ViewProperty(Boolean.TRUE) { - public void doUpdateEditor(EditorEx editorEx, Boolean aBoolean, EditorPlace.ComponentState state) { - editorEx.getSettings().setLineMarkerAreaShown(aBoolean.booleanValue()); - } - }; - public static final EditorPlace.ViewProperty ADDITIONAL_LINES = new EditorPlace.ViewProperty(null) { - public void doUpdateEditor(EditorEx editorEx, Integer integer, EditorPlace.ComponentState state) { - if (integer != null) editorEx.getSettings().setAdditionalLinesCount(integer.intValue()); - } - }; - public static final EditorPlace.ViewProperty ADDITIONAL_COLUMNS = new EditorPlace.ViewProperty(null) { - public void doUpdateEditor(EditorEx editorEx, Integer integer, EditorPlace.ComponentState state) { - if (integer != null) editorEx.getSettings().setAdditionalColumnsCount(integer.intValue()); - } - }; - public static final EditorPlace.ViewProperty HIGHLIGHTER_SETTINGS = - new EditorPlace.ViewProperty(null) { - public void doUpdateEditor(EditorEx editorEx, EditorColorsScheme settings, EditorPlace.ComponentState state) { - if (settings == null) settings = EditorColorsManager.getInstance().getGlobalScheme(); - DiffEditorState editorState = (DiffEditorState)state; - editorEx.setHighlighter( - EditorHighlighterFactory.getInstance().createEditorHighlighter(editorState.getFileType(), settings, editorState.getProject())); + @NotNull + private DiffRequest.ToolbarAddons createToolbar() { + return new DiffRequest.ToolbarAddons() { + public void customize(DiffToolbar toolbar) { + ActionManager actionManager = ActionManager.getInstance(); + toolbar.addAction(actionManager.getAction(IdeActions.ACTION_COPY)); + toolbar.addAction(actionManager.getAction(IdeActions.ACTION_FIND)); + toolbar.addAction(PreviousDiffAction.find()); + toolbar.addAction(NextDiffAction.find()); + toolbar.addSeparator(); + toolbar.addAction(new OpenPartialDiffAction(1, 0, AllIcons.Diff.LeftDiff)); + toolbar.addAction(new OpenPartialDiffAction(1, 2, AllIcons.Diff.RightDiff)); + toolbar.addAction(new OpenPartialDiffAction(0, 2, AllIcons.Diff.BranchDiff)); + toolbar.addSeparator(); + toolbar.addAction(new ApplyNonConflicts()); + toolbar.addSeparator(); + toolbar.addAction(new MergeToolSettingsAction(getEditors())); } }; - private static final Collection ALL_PROPERTIES = Arrays.asList - (ADDITIONAL_COLUMNS, ADDITIONAL_LINES, EDITOR_SCHEME, HIGHLIGHTER_SETTINGS, LINE_MARKERS_AREA, LINE_NUMBERS); + } - private static final DiffRequest.ToolbarAddons TOOLBAR = new DiffRequest.ToolbarAddons() { - public void customize(DiffToolbar toolbar) { - ActionManager actionManager = ActionManager.getInstance(); - toolbar.addAction(actionManager.getAction(IdeActions.ACTION_COPY)); - toolbar.addAction(actionManager.getAction(IdeActions.ACTION_FIND)); - toolbar.addAction(PreviousDiffAction.find()); - toolbar.addAction(NextDiffAction.find()); - toolbar.addSeparator(); - toolbar.addAction(new OpenPartialDiffAction(1, 0, AllIcons.Diff.LeftDiff)); - toolbar.addAction(new OpenPartialDiffAction(1, 2, AllIcons.Diff.RightDiff)); - toolbar.addAction(new OpenPartialDiffAction(0, 2, AllIcons.Diff.BranchDiff)); - toolbar.addSeparator(); - toolbar.addAction(new ApplyNonConflicts()); + @NotNull + private Collection getEditors() { + Collection editors = new ArrayList(3); + for (EditorPlace place : getEditorPlaces()) { + editors.add(place.getEditor()); } - }; + return editors; + } + + @NotNull + private Collection getEditorPlaces() { + Collection editorPlaces = new ArrayList(3); + for (LabeledComponent editorsPanel : myEditorsPanels) { + editorPlaces.add((EditorPlace) editorsPanel.getComponent()); + } + return editorPlaces; + } public void setScrollToFirstDiff(final boolean scrollToFirstDiff) { myScrollToFirstDiff = scrollToFirstDiff; @@ -208,6 +194,7 @@ public class MergePanel2 implements DiffViewer { Editor left = getEditor(0); Editor base = getEditor(1); Editor right = getEditor(2); + myMergeList.setMarkups(left, base, right); EditingSides[] sides = {new MyEditingSides(FragmentSide.SIDE1), new MyEditingSides(FragmentSide.SIDE2)}; myScrollSupport.install(sides); @@ -227,6 +214,39 @@ public class MergePanel2 implements DiffViewer { } } + public void setHighlighterSettings(@Nullable EditorColorsScheme settings) { + if (settings == null) { + settings = EditorColorsManager.getInstance().getGlobalScheme(); + } + for (EditorPlace place : getEditorPlaces()) { + Editor editor = place.getEditor(); + DiffEditorState editorState = place.getState(); + ((EditorEx)editor).setHighlighter(EditorHighlighterFactory.getInstance(). + createEditorHighlighter(editorState.getFileType(), settings, editorState.getProject())); + } + } + + public void setAdditionalLinesAndColumns(int lines, int columns) { + for (Editor editor : getEditors()) { + editor.getSettings().setAdditionalLinesCount(lines); + editor.getSettings().setAdditionalColumnsCount(columns); + } + } + + public void setShowLineNumbers(boolean show) { + for (Editor editor : getEditors()) { + MergeToolEditorSetting.LINE_NUMBERS.apply(editor, show); + } + } + + private static void initEditorSettings(@NotNull Editor editor) { + Project project = editor.getProject(); + MergeToolSettings settings = project == null ? null : ServiceManager.getService(project, MergeToolSettings.class); + for (MergeToolEditorSetting property : MergeToolEditorSetting.values()) { + property.apply(editor, settings == null ? property.getDefault() : settings.getPreference(property)); + } + } + private void disposeMergeList() { if (myMergeList == null) return; if (myStatusUpdater != null) { @@ -360,14 +380,23 @@ public class MergePanel2 implements DiffViewer { } } - private class DiffEditorState extends EditorPlace.ComponentState { - private final HashMap myProperties = new HashMap(); + class DiffEditorState { private final int myIndex; + private Document myDocument; private DiffEditorState(int index) { myIndex = index; } + public void setDocument(Document document) { + myDocument = document; + } + + public Document getDocument() { + return myDocument; + } + + @Nullable public Editor createEditor() { Document document = getDocument(); if (document == null) return null; @@ -381,20 +410,11 @@ public class MergePanel2 implements DiffViewer { if (myIndex != 1) ((EditorMarkupModel)editor.getMarkupModel()).setErrorStripeVisible(true); editor.getSettings().setFoldingOutlineShown(false); editor.getFoldingModel().setFoldingEnabled(false); - HashSet notProcessedDefaults = new HashSet(ALL_PROPERTIES); - for (EditorPlace.ViewProperty viewProperty : myProperties.keySet()) { - notProcessedDefaults.remove(viewProperty); - viewProperty.updateEditor(editor, myProperties.get(viewProperty), this); - } - for (EditorPlace.ViewProperty viewProperty : notProcessedDefaults) { - viewProperty.updateEditor(editor, null, this); - } - return editor; - } + editor.getSettings().setLineMarkerAreaShown(false); - public void updateValue(Editor editor, EditorPlace.ViewProperty property, T value) { - myProperties.put(property, value); - property.updateEditor(editor, value, this); + initEditorSettings(editor); + + return editor; } public FileType getFileType() { @@ -484,15 +504,10 @@ public class MergePanel2 implements DiffViewer { return myMergeList; } - public void setEditorProperty(EditorPlace.ViewProperty property, T value) { - for (int i = 0; i < EDITORS_COUNT; i++) { - EditorPlace editorPlace = getEditorPlace(i); - editorPlace.getState().updateValue(editorPlace.getEditor(), property, value); - } - } - public void setColorScheme(EditorColorsScheme scheme) { - setEditorProperty(EDITOR_SCHEME, scheme); + for (Editor editor : getEditors()) { + ((EditorEx)editor).setColorsScheme(scheme); + } myPanel.setColorScheme(scheme); } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergeToolEditorSetting.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergeToolEditorSetting.java new file mode 100644 index 000000000000..2c81e18db057 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergeToolEditorSetting.java @@ -0,0 +1,73 @@ +/* + * Copyright 2000-2012 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.diff.impl.incrementalMerge.ui; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ex.EditorEx; + +/** + *

+ * Properties of the editors used in the merge tool. Most of them are customizable by the user. + *

+ * + * @author Kirill Likhodedov + * @see MergeToolSettings + */ +enum MergeToolEditorSetting { + WHITESPACES(false) { + @Override + protected void apply(Editor editor, boolean state) { + editor.getSettings().setWhitespacesShown(state); + editor.getComponent().repaint(); + } + }, + LINE_NUMBERS(false) { + @Override + protected void apply(Editor editor, boolean state) { + editor.getSettings().setLineNumbersShown(state); + editor.getComponent().repaint(); + } + }, + INDENT_LINES(false) { + @Override + protected void apply(Editor editor, boolean state) { + editor.getSettings().setIndentGuidesShown(state); + editor.getComponent().repaint(); + } + }, + SOFT_WRAPS(false) { + @Override + protected void apply(Editor editor, boolean state) { + editor.getSettings().setUseSoftWraps(state); + if (editor instanceof EditorEx) { + ((EditorEx)editor).reinitSettings(); + } + } + }; + + private final boolean myDefault; + + MergeToolEditorSetting(boolean aDefault) { + myDefault = aDefault; + } + + protected abstract void apply(Editor editor, boolean state); + + boolean getDefault() { + return myDefault; + } + +} diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergeToolSettings.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergeToolSettings.java new file mode 100644 index 000000000000..69d30c78112b --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergeToolSettings.java @@ -0,0 +1,114 @@ +/* + * Copyright 2000-2012 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.diff.impl.incrementalMerge.ui; + +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.components.StoragePathMacros; +import org.jetbrains.annotations.NotNull; + +/** + * Workspace-wide settings for merge tool UI customization. + * + * @author Kirill Likhodedov + */ +@State(name = "MergeToolSettings", storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)}) +public class MergeToolSettings implements PersistentStateComponent { + + private State myState = new State(); + + public static class State { + public boolean WHITESPACES = false; + public boolean LINE_NUMBERS = true; + public boolean INDENT_LINES = false; + public boolean SOFT_WRAPS = false; + } + + @Override + public State getState() { + return myState; + } + + @Override + public void loadState(State state) { + myState = state; + } + + public void setPreference(@NotNull MergeToolEditorSetting setting, boolean state) { + switch (setting) { + case WHITESPACES: + setShowWhiteSpaces(state); + break; + case LINE_NUMBERS: + setShowLineNumbers(state); + break; + case INDENT_LINES: + setShowIndentLines(state); + break; + case SOFT_WRAPS: + setUseSoftWraps(state); + break; + } + } + + public boolean getPreference(@NotNull MergeToolEditorSetting setting) { + switch (setting) { + case WHITESPACES: + return isShowWhitespaces(); + case LINE_NUMBERS: + return isShowLineNumbers(); + case INDENT_LINES: + return isShowIndentLines(); + case SOFT_WRAPS: + return isUseSoftWraps(); + } + return false; + } + + public void setShowLineNumbers(boolean state) { + myState.LINE_NUMBERS = state; + } + + public boolean isShowLineNumbers() { + return myState.LINE_NUMBERS; + } + + public void setShowWhiteSpaces(boolean state) { + myState.WHITESPACES = state; + } + + public boolean isShowWhitespaces() { + return myState.WHITESPACES; + } + + public void setShowIndentLines(boolean state) { + myState.INDENT_LINES = state; + } + + public boolean isShowIndentLines() { + return myState.INDENT_LINES; + } + + public void setUseSoftWraps(boolean state) { + myState.SOFT_WRAPS = state; + } + + public boolean isUseSoftWraps() { + return myState.SOFT_WRAPS; + } + +} diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergeToolSettingsAction.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergeToolSettingsAction.java new file mode 100644 index 000000000000..3fff9cd07cae --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergeToolSettingsAction.java @@ -0,0 +1,126 @@ +/* + * Copyright 2000-2012 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.diff.impl.incrementalMerge.ui; + +import com.intellij.icons.AllIcons; +import com.intellij.idea.ActionsBundle; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.wm.impl.content.ToolWindowContentUi; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.util.Collection; + +/** + * The "gear" action allowing to configure merge tool visual preferences, such as displaying whitespaces, line numbers and soft wraps. + * + * @author Kirill Likhodedov + * @see MergeToolSettings + */ +class MergeToolSettingsAction extends AnAction { + + private final Collection myEditors; + private final ActionGroup myActionGroup; + + MergeToolSettingsAction(@NotNull Collection editors) { + super(AllIcons.General.Gear); + myEditors = editors; + myActionGroup = new MergeToolActionGroup(); + } + + @Override + public void actionPerformed(AnActionEvent e) { + InputEvent inputEvent = e.getInputEvent(); + ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(ToolWindowContentUi.POPUP_PLACE, myActionGroup); + int x = 0; + int y = 0; + if (inputEvent instanceof MouseEvent) { + x = ((MouseEvent)inputEvent).getX(); + y = ((MouseEvent)inputEvent).getY(); + } + popupMenu.getComponent().show(inputEvent.getComponent(), x, y); + } + + private class MergeToolActionGroup extends ActionGroup { + @NotNull + @Override + public AnAction[] getChildren(@Nullable AnActionEvent e) { + return new AnAction[] { + new MergeToolToggleAction("EditorToggleShowWhitespaces", MergeToolEditorSetting.WHITESPACES, myEditors), + new MergeToolToggleAction("EditorToggleShowLineNumbers", MergeToolEditorSetting.LINE_NUMBERS, myEditors), + new MergeToolToggleAction("EditorToggleShowIndentLines", MergeToolEditorSetting.INDENT_LINES, myEditors), + new MergeToolToggleAction("EditorToggleUseSoftWraps", MergeToolEditorSetting.SOFT_WRAPS, myEditors) + }; + } + } + + /** + * Common class for all actions toggling merge tool editor settings. + */ + private static class MergeToolToggleAction extends ToggleAction { + + private final MergeToolEditorSetting mySetting; + private final Collection myEditors; + + private MergeToolToggleAction(String actionId, MergeToolEditorSetting setting, Collection editors) { + super(ActionsBundle.actionText(actionId), ActionsBundle.actionDescription(actionId), null); + mySetting = setting; + myEditors = editors; + } + + @Override + public boolean isSelected(AnActionEvent e) { + return getPreference(e, mySetting); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + setPreference(e, mySetting, state); + for (Editor editor : myEditors) { + mySetting.apply(editor, state); + } + } + + private static void setPreference(AnActionEvent event, MergeToolEditorSetting preference, boolean state) { + MergeToolSettings settings = getSettings(event.getProject()); + if (settings != null) { + settings.setPreference(preference, state); + } + } + + private static boolean getPreference(AnActionEvent event, MergeToolEditorSetting preference) { + MergeToolSettings settings = getSettings(event.getProject()); + if (settings != null) { + return settings.getPreference(preference); + } + return false; + } + + @Nullable + private static MergeToolSettings getSettings(@Nullable Project project) { + if (project != null) { + return ServiceManager.getService(project, MergeToolSettings.class); + } + return null; + } + } + +} diff --git a/platform/platform-resources/src/META-INF/VcsExtensions.xml b/platform/platform-resources/src/META-INF/VcsExtensions.xml index 733ca23c4ee6..43bb478254cf 100644 --- a/platform/platform-resources/src/META-INF/VcsExtensions.xml +++ b/platform/platform-resources/src/META-INF/VcsExtensions.xml @@ -10,6 +10,8 @@ serviceImplementation="com.intellij.openapi.diff.impl.dir.DirDiffManagerImpl"/> + + From 8cfda8e54241370344bc0c8d311614bf4288355a Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sat, 9 Jun 2012 18:34:12 +0400 Subject: [PATCH 05/20] [merge tool] Don't show the default gutter popup. REASON: * The settings are available from the new gear-button on the toolbar. * The settings can't be possibly available without the gutter (which is probably in the future + if line numbers are hidden). * Default popup contains "show annotate" (not applicable for merge tool), but doesn't contain "use soft wrap" (totally applicable and useful). * Default popup affects only the editor of the selected gutter, but it means no sense to disable, for example, line numbers in one merge editor, but enable it in the other. Changing the behavior of the popup, on the other hand, can confuse the user. DETAILS: * Introduce EditorGutterComponentEx#setShowDefaultGutterPopup and call it when initializing editors. --- .../diff/impl/incrementalMerge/ui/MergePanel2.java | 2 +- .../openapi/editor/ex/EditorGutterComponentEx.java | 2 ++ .../editor/impl/EditorGutterComponentImpl.java | 14 +++++++++++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java index 2ebac7c61ddb..a26ad70fd08c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java @@ -411,7 +411,7 @@ public class MergePanel2 implements DiffViewer { editor.getSettings().setFoldingOutlineShown(false); editor.getFoldingModel().setFoldingEnabled(false); editor.getSettings().setLineMarkerAreaShown(false); - + editor.getGutterComponentEx().setShowDefaultGutterPopup(false); initEditorSettings(editor); return editor; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorGutterComponentEx.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorGutterComponentEx.java index 1d936db0345e..43090c6dfe42 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorGutterComponentEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorGutterComponentEx.java @@ -56,4 +56,6 @@ public abstract class EditorGutterComponentEx extends JComponent implements Edit public abstract Point getPoint(GutterIconRenderer renderer); public abstract void setLineNumberConvertor(final Convertor lineNumberConvertor); + + public abstract void setShowDefaultGutterPopup(boolean show); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java index b5403375405b..08d0a0f0d559 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java @@ -91,6 +91,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse private String myLastGutterToolTip = null; private int myLastPreferredHeight = -1; private Convertor myLineNumberConvertor; + private boolean myShowDefaultGutterPopup = true; @SuppressWarnings("unchecked") public EditorGutterComponentImpl(EditorImpl editor) { @@ -1343,6 +1344,11 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse myLineNumberConvertor = lineNumberConvertor; } + @Override + public void setShowDefaultGutterPopup(boolean show) { + myShowDefaultGutterPopup = show; + } + private void invokePopup(MouseEvent e) { final ActionManager actionManager = ActionManager.getInstance(); if (myEditor.getMouseEventArea(e) == EditorMouseEventArea.ANNOTATIONS_AREA) { @@ -1381,9 +1387,11 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse } } else { - ActionGroup group = (ActionGroup)CustomActionsSchema.getInstance().getCorrectedAction(IdeActions.GROUP_EDITOR_GUTTER); - ActionPopupMenu popupMenu = actionManager.createActionPopupMenu(ActionPlaces.UNKNOWN, group); - popupMenu.getComponent().show(this, e.getX(), e.getY()); + if (myShowDefaultGutterPopup) { + ActionGroup group = (ActionGroup)CustomActionsSchema.getInstance().getCorrectedAction(IdeActions.GROUP_EDITOR_GUTTER); + ActionPopupMenu popupMenu = actionManager.createActionPopupMenu(ActionPlaces.UNKNOWN, group); + popupMenu.getComponent().show(this, e.getX(), e.getY()); + } e.consume(); } } From ec2301a24e63cc6d585930aa86399c4e3ed1f9ec Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Tue, 26 Jun 2012 19:55:51 +0400 Subject: [PATCH 06/20] [merge tool] fix setting the EditorColorsScheme on editor creation --- .../impl/incrementalMerge/ui/MergePanel2.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java index a26ad70fd08c..0f9f61309915 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java @@ -179,7 +179,9 @@ public class MergePanel2 implements DiffViewer { if (myData == null) return; DiffContent[] contents = myData.getContents(); for (int i = 0; i < EDITORS_COUNT; i++) { - getEditorPlace(i).setDocument(contents[i].getDocument()); + EditorPlace editorPlace = getEditorPlace(i); + editorPlace.setDocument(contents[i].getDocument()); + setHighlighterSettings(null, editorPlace); } tryInitView(); } @@ -215,15 +217,19 @@ public class MergePanel2 implements DiffViewer { } public void setHighlighterSettings(@Nullable EditorColorsScheme settings) { + for (EditorPlace place : getEditorPlaces()) { + setHighlighterSettings(settings, place); + } + } + + private void setHighlighterSettings(@Nullable EditorColorsScheme settings, @NotNull EditorPlace place) { if (settings == null) { settings = EditorColorsManager.getInstance().getGlobalScheme(); } - for (EditorPlace place : getEditorPlaces()) { - Editor editor = place.getEditor(); - DiffEditorState editorState = place.getState(); - ((EditorEx)editor).setHighlighter(EditorHighlighterFactory.getInstance(). - createEditorHighlighter(editorState.getFileType(), settings, editorState.getProject())); - } + Editor editor = place.getEditor(); + DiffEditorState editorState = place.getState(); + ((EditorEx)editor).setHighlighter(EditorHighlighterFactory.getInstance(). + createEditorHighlighter(editorState.getFileType(), settings, editorState.getProject())); } public void setAdditionalLinesAndColumns(int lines, int columns) { From 06c3d141860f44493a181976ef5c41bf545edc82 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 28 Jun 2012 19:17:18 +0400 Subject: [PATCH 07/20] [merge tool] return the line markers area (with apply and ignore buttons), which was occasionally removed when rewriting merge tool settings. --- .../openapi/diff/impl/incrementalMerge/ui/MergePanel2.java | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java index 0f9f61309915..22f6ff8eaa3e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java @@ -251,6 +251,7 @@ public class MergePanel2 implements DiffViewer { for (MergeToolEditorSetting property : MergeToolEditorSetting.values()) { property.apply(editor, settings == null ? property.getDefault() : settings.getPreference(property)); } + editor.getSettings().setLineMarkerAreaShown(true); } private void disposeMergeList() { From 92c0ddcbaa5fbf5ebb06656b8881a3c7174bb8db Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 5 Jul 2012 20:12:43 +0400 Subject: [PATCH 08/20] UIUtil: add drawLine taking colors as params --- .../util/src/com/intellij/util/ui/UIUtil.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index 997e600f0a4a..a4b9f04ec17e 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -273,6 +273,24 @@ public class UIUtil { g.drawLine(x1, y1, x2, y2); } + public static void drawLine(Graphics2D g, int x1, int y1, int x2, int y2, @Nullable Color bgColor, @Nullable Color fgColor) { + Color oldFg = g.getColor(); + Color oldBg = g.getBackground(); + if (fgColor != null) { + g.setColor(fgColor); + } + if (bgColor != null) { + g.setBackground(bgColor); + } + drawLine(g, x1, y1, x2, y2); + if (fgColor != null) { + g.setColor(oldFg); + } + if (bgColor != null) { + g.setBackground(oldBg); + } + } + @NotNull public static String[] splitText(String text, FontMetrics fontMetrics, int widthLimit, char separator) { ArrayList lines = new ArrayList(); From e05c690fe8c57ddb7da8dccc1566e25344d6e529 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 5 Jul 2012 20:14:01 +0400 Subject: [PATCH 09/20] TrafficLightRenderer: don't refresh error stipes if they are not displayed, add a method to know this to EditorMarkupModel. --- .../daemon/impl/TrafficLightRenderer.java | 20 ++++++++++--------- .../openapi/editor/ex/EditorMarkupModel.java | 2 ++ .../editor/impl/EditorMarkupModelImpl.java | 5 +++++ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.java index 258115b3a101..e1d055d7af62 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.java @@ -117,15 +117,17 @@ public class TrafficLightRenderer implements ErrorStripeRenderer, Disposable { public static void setOrRefreshErrorStripeRenderer(@NotNull EditorMarkupModel editorMarkupModel, @NotNull Project project, Document document, PsiFile file) { ApplicationManager.getApplication().assertIsDispatchThread(); - ErrorStripeRenderer renderer = editorMarkupModel.getErrorStripeRenderer(); - if (renderer instanceof TrafficLightRenderer) { - ((TrafficLightRenderer)renderer).refresh(); - ((EditorMarkupModelImpl)editorMarkupModel).repaintVerticalScrollBar(); - } - else { - renderer = new TrafficLightRenderer(project, document, file); - Disposer.register(((EditorImpl)editorMarkupModel.getEditor()).getDisposable(), (Disposable)renderer); - editorMarkupModel.setErrorStripeRenderer(renderer); + if (editorMarkupModel.isErrorStripeVisible()) { + ErrorStripeRenderer renderer = editorMarkupModel.getErrorStripeRenderer(); + if (renderer instanceof TrafficLightRenderer) { + ((TrafficLightRenderer)renderer).refresh(); + ((EditorMarkupModelImpl)editorMarkupModel).repaintVerticalScrollBar(); + } + else { + renderer = new TrafficLightRenderer(project, document, file); + Disposer.register(((EditorImpl)editorMarkupModel.getEditor()).getDisposable(), (Disposable)renderer); + editorMarkupModel.setErrorStripeRenderer(renderer); + } } } 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 0cdae1ae1a7a..0f70bf4010cd 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 @@ -40,4 +40,6 @@ public interface EditorMarkupModel extends MarkupModel { ErrorStripTooltipRendererProvider getErrorStripTooltipRendererProvider(); void setMinMarkHeight(int minMarkHeight); + + boolean isErrorStripeVisible(); } 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 dbd917cb1c98..5201b146675c 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 @@ -777,6 +777,11 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark myMinMarkHeight = minMarkHeight; } + @Override + public boolean isErrorStripeVisible() { + return getErrorPanel() != null; + } + private static class BasicTooltipRendererProvider implements ErrorStripTooltipRendererProvider { @Override public TooltipRenderer calcTooltipRenderer(@NotNull final Collection highlighters) { From 8d7e6a2c3aeb1dded01d30bf25420e677d54a8a3 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 5 Jul 2012 20:15:42 +0400 Subject: [PATCH 10/20] EditorEx: getVerticalScrollbarOrientation Already implemented in EditorImpl, pass up to the interface. Will be used in the merge tool. --- .../src/com/intellij/injected/editor/EditorWindow.java | 5 +++++ .../src/com/intellij/openapi/editor/ex/EditorEx.java | 3 +++ .../src/com/intellij/openapi/editor/impl/EditorImpl.java | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java index ff69f00bccb2..539bce6566bd 100644 --- a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java +++ b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java @@ -697,6 +697,11 @@ public class EditorWindow extends UserDataHolderBase implements EditorEx { myDelegate.setVerticalScrollbarOrientation(type); } + @Override + public int getVerticalScrollbarOrientation() { + return myDelegate.getVerticalScrollbarOrientation(); + } + @Override public void setVerticalScrollbarVisible(final boolean b) { myDelegate.setVerticalScrollbarVisible(b); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java index 899134dc93ba..c9f161cc7d36 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java @@ -73,6 +73,9 @@ public interface EditorEx extends Editor { void setVerticalScrollbarOrientation(@MagicConstant(intValues = {VERTICAL_SCROLLBAR_LEFT, VERTICAL_SCROLLBAR_RIGHT}) int type); + @MagicConstant(intValues = {VERTICAL_SCROLLBAR_LEFT, VERTICAL_SCROLLBAR_RIGHT}) + int getVerticalScrollbarOrientation(); + void setVerticalScrollbarVisible(boolean b); void setHorizontalScrollbarVisible(boolean b); 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 c6a1c0747dac..4cb2ef64c531 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 @@ -4622,7 +4622,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } } - int getVerticalScrollbarOrientation() { + public int getVerticalScrollbarOrientation() { return myScrollBarOrientation; } From d5848cd75dbba10a771c50ae93f3fe28b2edd296 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sat, 7 Jul 2012 15:48:38 +0400 Subject: [PATCH 11/20] [diff/merge] IDEA-86287 Fix diff dividers position in the merge tool. Use different offsets for merge and diff (because of different layouts of the tools), specify the offset in the DividerPolygon constructor. --- .../intellij/openapi/diff/impl/DiffPanelImpl.java | 9 ++++++--- .../diff/impl/splitter/DiffDividerPaint.java | 6 ++++-- .../openapi/diff/impl/splitter/DividerPolygon.java | 14 ++++++++++---- .../openapi/diff/impl/util/DiffDivider.java | 3 ++- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java index db78e75c285a..918b3f1a6609 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java @@ -17,7 +17,10 @@ package com.intellij.openapi.diff.impl; import com.intellij.ide.actions.EditSourceAction; import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.CommonShortcuts; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; @@ -68,13 +71,13 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.awt.event.MouseListener; -import java.security.InvalidParameterException; import java.util.Iterator; import java.util.LinkedList; import java.util.List; public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSidesContainer { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.DiffPanelImpl"); + public static final int DIFF_DIVIDER_POLYGONS_OFFSET = 3; private final DiffSplitterI mySplitter; private final DiffPanelOuterComponent myPanel; @@ -127,7 +130,7 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid if (horizontal) { mySplitter = new DiffSplitter(myLeftSide.getComponent(), myRightSide.getComponent(), - new DiffDividerPaint(this, FragmentSide.SIDE1), myData); + new DiffDividerPaint(this, FragmentSide.SIDE1, DIFF_DIVIDER_POLYGONS_OFFSET), myData); } else { mySplitter = new HorizontalDiffSplitter(myLeftSide.getComponent(), myRightSide.getComponent()); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DiffDividerPaint.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DiffDividerPaint.java index bf1e435f7b93..dd9b4b470ea1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DiffDividerPaint.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DiffDividerPaint.java @@ -24,10 +24,12 @@ import java.awt.*; public class DiffDividerPaint { private final EditingSides mySides; private final FragmentSide myLeftSide; + private final int myDiffDividerPolygonsOffset; - public DiffDividerPaint(EditingSides sides, FragmentSide leftSide) { + public DiffDividerPaint(EditingSides sides, FragmentSide leftSide, int diffDividerPolygonsOffset) { mySides = sides; myLeftSide = leftSide; + myDiffDividerPolygonsOffset = diffDividerPolygonsOffset; } public void paint(Graphics g, JComponent component) { @@ -36,7 +38,7 @@ public class DiffDividerPaint { int height = component.getHeight(); int editorHeight = mySides.getEditor(myLeftSide).getComponent().getHeight(); Graphics2D gg = (Graphics2D)g.create(0, height - editorHeight, width, editorHeight); - DividerPolygon.paintPolygons(DividerPolygon.createVisiblePolygons(mySides, myLeftSide), gg, width); + DividerPolygon.paintPolygons(DividerPolygon.createVisiblePolygons(mySides, myLeftSide, myDiffDividerPolygonsOffset), gg, width); gg.dispose(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java index 023ec4e9aaf2..84603a84e88d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java @@ -26,6 +26,9 @@ import com.intellij.util.ui.UIUtil; import java.awt.*; import java.util.ArrayList; +/** + * A polygon, which is drawn between editors in merge or diff dialogs, and which indicates the change flow from one editor to another. + */ public class DividerPolygon { public static final int OFFSET = 3; private final Color myColor; @@ -86,7 +89,7 @@ public class DividerPolygon { //g.setComposite(composite); } - public static ArrayList createVisiblePolygons(EditingSides sides, FragmentSide left) { + public static ArrayList createVisiblePolygons(EditingSides sides, FragmentSide left, int diffDividerPolygonsOffset) { Editor editor1 = sides.getEditor(left); Editor editor2 = sides.getEditor(left.otherSide()); LineBlocks lineBlocks = sides.getLineBlocks(); @@ -100,7 +103,7 @@ public class DividerPolygon { Trapezium trapezium = lineBlocks.getTrapezium(i); final TextDiffType type = lineBlocks.getType(i); Color color = type.getPolygonColor(editor1); - polygons.add(createPolygon(transformations, trapezium, color, left)); + polygons.add(createPolygon(transformations, trapezium, color, left, diffDividerPolygonsOffset)); } return polygons; } @@ -110,7 +113,8 @@ public class DividerPolygon { return new FoldingTransformation(editor); } - private static DividerPolygon createPolygon(Transformation[] transformations, Trapezium trapezium, Color color, FragmentSide left) { + private static DividerPolygon createPolygon(Transformation[] transformations, Trapezium trapezium, Color color, FragmentSide left, + int diffDividerPolygonsOffset) { Interval base1 = trapezium.getBase(left); Interval base2 = trapezium.getBase(left.otherSide()); Transformation leftTransform = transformations[left.getIndex()]; @@ -119,7 +123,9 @@ public class DividerPolygon { int end1 = leftTransform.transform(base1.getEnd()); int start2 = rightTransform.transform(base2.getStart()); int end2 = rightTransform.transform(base2.getEnd()); - return new DividerPolygon(start1 - OFFSET, start2 - OFFSET, end1 - OFFSET, end2 - OFFSET, color); + return new DividerPolygon(start1 - diffDividerPolygonsOffset, start2 - diffDividerPolygonsOffset, + end1 - diffDividerPolygonsOffset, end2 - diffDividerPolygonsOffset, + ColorUtil.toAlpha(color, TRANSPARENCY)); } static Interval getVisibleInterval(Editor editor) { diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/DiffDivider.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/DiffDivider.java index 15ade80ec853..e9c3168e401c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/DiffDivider.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/DiffDivider.java @@ -28,6 +28,7 @@ import javax.swing.*; import java.awt.*; public class DiffDivider extends JComponent { + public static final int MERGE_DIVIDER_POLYGONS_OFFSET = 1; private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.util.DiffDivider"); private final Editor[] myEditors = new Editor[2]; private final FragmentSide myLeftSide; @@ -72,7 +73,7 @@ public class DiffDivider extends JComponent { public void listenEditors(@NotNull EditingSides sides) { stopListenEditors(); - myPaint = new DiffDividerPaint(sides, myLeftSide); + myPaint = new DiffDividerPaint(sides, myLeftSide, MERGE_DIVIDER_POLYGONS_OFFSET); myEditors[0] = sides.getEditor(FragmentSide.SIDE1); myEditors[1] = sides.getEditor(FragmentSide.SIDE2); if (myEditors[0] == null || myEditors[1] == null) { From 5614eacb5fee68803e82c8fffc058110ff875789 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sat, 7 Jul 2012 16:06:58 +0400 Subject: [PATCH 12/20] [merge tool] Make diff markers flow above gutters and scrollbars, thus connecting editor highlightings and diff dividers. EditorPlace is a container for editor, that is put into the merge tool dialog. * Override its paint() method, and paint above gutters and scrollbars. Right and Left columns need to paint only above the gutter, while central needs to paint above the gutter for the left-side change, and above the scrollbar - for the right-side change. * To calculate the coordinates of the painted rectangles, use the existing method: DividerPolygon.createVisiblePolygons. It is calculated when painting DiffDividers anyway, so this extra calculation can be avoided, but for the code simplicity and because there are no performance problems with it, it's decided to leave as is for now. * Since the markers are painted above gutters, line numbers and scrollbars, they need to be semi-transparent. Make DividerPolygons' colors have the same transparency (this will affect the diff tool as well, but it looks fine). * It is not possible to have a semi-transparent background highlightings in the editor, but it looks not very good to have different colors for text background and the markers on dividers and gutters. To fix this emulate transparency by taking the color in between of the diff type color and editor's background, considering the transparency coefficient. This is done in the TextDiffType. This should be tested in other color schemes. * Introduce the MergePanelColumn simply enumerating 3 columns of the merge tool. The goal is to use it everywhere in the merge tool code, instead of referencing columns by digital indexes and manipulating FragmentSides. Use it in the EditorPlace. --- .../impl/incrementalMerge/ui/EditorPlace.java | 178 +++++++++++++++++- .../impl/incrementalMerge/ui/MergePanel2.java | 41 +++- .../incrementalMerge/ui/MergePanelColumn.java | 31 +++ .../diff/impl/splitter/DividerPolygon.java | 25 ++- .../openapi/diff/impl/util/TextDiffType.java | 27 ++- 5 files changed, 289 insertions(+), 13 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanelColumn.java diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java index e90bf7de94a4..278dca08ebd9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java @@ -17,26 +17,120 @@ package com.intellij.openapi.diff.impl.incrementalMerge.ui; import com.intellij.openapi.Disposable; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.diff.impl.EditingSides; +import com.intellij.openapi.diff.impl.highlighting.FragmentSide; +import com.intellij.openapi.diff.impl.splitter.DividerPolygon; +import com.intellij.openapi.diff.impl.util.DiffDivider; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.event.CaretEvent; +import com.intellij.openapi.editor.event.CaretListener; +import com.intellij.openapi.editor.event.VisibleAreaEvent; +import com.intellij.openapi.editor.event.VisibleAreaListener; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.ex.EditorGutterComponentEx; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.util.ArrayList; +/** + * The container for an {@link Editor}, which is added then to {@link com.intellij.openapi.diff.impl.util.ThreePanels}. + */ public class EditorPlace extends JComponent implements Disposable { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.incrementalMerge.ui.EditorPlace"); - private final MergePanel2.DiffEditorState myState; - private final ArrayList myListeners = new ArrayList(); - private Editor myEditor = null; + private static final Logger LOG = Logger.getInstance(EditorPlace.class); - public EditorPlace(MergePanel2.DiffEditorState state) { + @NotNull private final MergePanel2.DiffEditorState myState; + @NotNull private final MergePanelColumn myColumn; + @NotNull private final SideInfo mySideInfo; + @NotNull private final ArrayList myListeners = new ArrayList(); + + @Nullable private Editor myEditor; + + private final VisibleAreaListener myVisibleAreaListener = new VisibleAreaListener() { + public void visibleAreaChanged(VisibleAreaEvent e) { + repaint(); + } + }; + + public EditorPlace(@NotNull MergePanel2.DiffEditorState state, @NotNull MergePanelColumn column, @NotNull MergePanel2 mergePanel) { myState = state; + myColumn = column; + mySideInfo = SideInfo.convertFromColumn(mergePanel, myColumn); + setLayout(new BorderLayout()); } + @Override + public void paint(Graphics g) { + super.paint(g); + + if (myEditor != null) { + Graphics2D g2 = (Graphics2D)g; + drawAbove(true, g2, mySideInfo.createPolygonsForGutter(), mySideInfo.isTakeLeftSideOfPolygonForGutter()); + drawAbove(false, g2, mySideInfo.createPolygonsForScrollbar(), mySideInfo.isTakeLeftSideOfPolygonForScrollbar()); + } + } + + private void drawAbove(boolean gutter, Graphics2D g2, ArrayList polygons, boolean takeLeftSideOfPolygon) { + for (DividerPolygon polygon : polygons) { + int startY = takeLeftSideOfPolygon ? polygon.getTopLeftY() :polygon.getTopRightY(); + int endY = takeLeftSideOfPolygon ? polygon.getBottomLeftY() : polygon.getBottomRightY(); + int height = endY - startY; + + if (height == 0) { // draw at least a one-pixel line (e.g. for insertion or deletion), as it is done in highlighters + height = 1; + } + + if (gutter) { + drawAboveGutter(g2, startY, height, polygon.getColor()); + } + else { + drawAboveScrollBar(g2, startY, height, polygon.getColor()); + } + } + } + + private void drawAboveGutter(@NotNull Graphics2D g, int startY, int height, @NotNull Color color) { + EditorGutterComponentEx gutter = ((EditorEx)myEditor).getGutterComponentEx(); + g.setColor(color); + if (((EditorEx)myEditor).getVerticalScrollbarOrientation() == EditorEx.VERTICAL_SCROLLBAR_RIGHT) { + // scrollbar is at the right => the gutter is at the left (central editor case) + int startX = gutter.getX(); + g.fillRect(startX, startY, gutter.getWidth() + 1, height); + + drawFramingLines(g, startX, startY, startX + gutter.getWidth(), startY + height); + } + else { + JComponent editorComponent = myEditor.getComponent(); + int startX = editorComponent.getX() + editorComponent.getWidth() - gutter.getWidth() - 1; + g.fillRect(startX, startY, gutter.getWidth() + 1, height); + + drawFramingLines(g, startX, startY, startX + gutter.getWidth(), startY + height); + } + } + + private void drawAboveScrollBar(@NotNull Graphics2D g, int startY, int height, @NotNull Color color) { + // painting only above the central scrollbar, because painting on edge scrollbars is not needed, and there are error stripes + if (myColumn == MergePanelColumn.BASE) { + g.setColor(color); + JScrollBar scrollBar = ((EditorEx)myEditor).getScrollPane().getVerticalScrollBar(); + int startX = scrollBar.getX(); + g.fillRect(startX, startY, scrollBar.getWidth(), height); + + drawFramingLines(g, startX, startY, startX + scrollBar.getWidth(), startY + height); + } + } + + private static void drawFramingLines(@NotNull Graphics2D g, int startX, int topY, int endX, int bottomY) { + UIUtil.drawLine(g, startX, topY, endX, topY, null, DividerPolygon.FRAMING_LINE_COLOR); + UIUtil.drawLine(g, startX, bottomY, endX, bottomY, null, DividerPolygon.FRAMING_LINE_COLOR); + } + public void addNotify() { if (myEditor != null) { super.addNotify(); @@ -52,6 +146,14 @@ public class EditorPlace extends JComponent implements Disposable { myEditor = myState.createEditor(); if (myEditor == null) return; add(myEditor.getComponent(), BorderLayout.CENTER); + myEditor.getScrollingModel().addVisibleAreaListener(myVisibleAreaListener); + myEditor.getCaretModel().addCaretListener(new CaretListener() { + @Override + public void caretPositionChanged(CaretEvent e) { + repaint(); + } + }); + repaint(); fireEditorCreated(); } @@ -86,6 +188,7 @@ public class EditorPlace extends JComponent implements Disposable { private void removeEditor() { if (myEditor != null) { + myEditor.getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener); Editor releasedEditor = myEditor; remove(myEditor.getComponent()); getEditorFactory().releaseEditor(myEditor); @@ -94,6 +197,7 @@ public class EditorPlace extends JComponent implements Disposable { } } + @Nullable public Editor getEditor() { return myEditor; } @@ -109,6 +213,7 @@ public class EditorPlace extends JComponent implements Disposable { return myState.getDocument(); } + @NotNull public MergePanel2.DiffEditorState getState() { return myState; } @@ -130,4 +235,67 @@ public class EditorPlace extends JComponent implements Disposable { public void dispose() { removeEditor(); } + + /** + * Helper structure to encapsulate the legacy-style ({@link EditingSides}, {@link FragmentSide}) information about merge columns. + */ + private static class SideInfo { + @NotNull private final EditingSides myEditingSidesForGutter; + @NotNull private final FragmentSide myFragmentSideForGutter; + private final boolean myTakeLeftSideOfPolygonForGutter; + @NotNull private final EditingSides myEditingSidesForScrollbar; + @NotNull private final FragmentSide myFragmentSideForScrollbar; + private final boolean myTakeLeftSideOfPolygonForScrollbar; + + private SideInfo(@NotNull EditingSides editingSidesForGutter, @NotNull FragmentSide fragmentSideForGutter, + boolean takeLeftSideOfPolygonForGutter, + @NotNull EditingSides editingSidesForScrollbar, @NotNull FragmentSide fragmentSideForScrollbar, + boolean takeLeftSideOfPolygonForScrollbar) { + myEditingSidesForGutter = editingSidesForGutter; + myFragmentSideForGutter = fragmentSideForGutter; + myTakeLeftSideOfPolygonForGutter = takeLeftSideOfPolygonForGutter; + myEditingSidesForScrollbar = editingSidesForScrollbar; + myFragmentSideForScrollbar = fragmentSideForScrollbar; + myTakeLeftSideOfPolygonForScrollbar = takeLeftSideOfPolygonForScrollbar; + } + + public SideInfo(EditingSides side, FragmentSide fragmentSide, boolean takeLeftSideOfPolygon) { + this(side, fragmentSide, takeLeftSideOfPolygon, side, fragmentSide, takeLeftSideOfPolygon); + } + + public ArrayList createPolygonsForGutter() { + return DividerPolygon.createVisiblePolygons(myEditingSidesForGutter, myFragmentSideForGutter, + DiffDivider.MERGE_DIVIDER_POLYGONS_OFFSET); + } + + public ArrayList createPolygonsForScrollbar() { + return DividerPolygon.createVisiblePolygons(myEditingSidesForScrollbar, myFragmentSideForScrollbar, + DiffDivider.MERGE_DIVIDER_POLYGONS_OFFSET); + } + + public boolean isTakeLeftSideOfPolygonForGutter() { + return myTakeLeftSideOfPolygonForGutter; + } + + public boolean isTakeLeftSideOfPolygonForScrollbar() { + return myTakeLeftSideOfPolygonForScrollbar; + } + + @NotNull + private static SideInfo convertFromColumn(@NotNull MergePanel2 mergePanel, @NotNull MergePanelColumn column) { + switch (column) { + case LEFT: + return new SideInfo(mergePanel.getFirstEditingSide(), FragmentSide.SIDE2, true); + case BASE: + return new SideInfo(mergePanel.getFirstEditingSide(), FragmentSide.SIDE2, false, + mergePanel.getSecondEditingSide(), FragmentSide.SIDE1, true); + case RIGHT: + return new SideInfo(mergePanel.getSecondEditingSide(), FragmentSide.SIDE1, false); + default: + throw new IllegalStateException("Incorrect column value: " + column); + } + } + + } + } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java index 22f6ff8eaa3e..cf93ea82def7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java @@ -100,7 +100,7 @@ public class MergePanel2 implements DiffViewer { } }; for (int i = 0; i < EDITORS_COUNT; i++) { - EditorPlace editorPlace = new EditorPlace(new DiffEditorState(i)); + EditorPlace editorPlace = new EditorPlace(new DiffEditorState(i), indexToColumn(i), this); Disposer.register(parent, editorPlace); editorPlaces.add(editorPlace); editorPlace.addListener(placeListener); @@ -116,6 +116,20 @@ public class MergePanel2 implements DiffViewer { myBuilder = builder; } + /** + * Convert legacy-style editor (or panel) number to the {@link MergePanelColumn}. + * @param i 0, 1 or 2 + * @return Left, base or right, respectively. + */ + private static MergePanelColumn indexToColumn(int i) { + switch (i) { + case 0: return MergePanelColumn.LEFT; + case 1: return MergePanelColumn.BASE; + case 2: return MergePanelColumn.RIGHT; + default: throw new IllegalStateException("Incorrect value for a merge column: " + i); + } + } + @NotNull private DiffRequest.ToolbarAddons createToolbar() { return new DiffRequest.ToolbarAddons() { @@ -159,6 +173,10 @@ public class MergePanel2 implements DiffViewer { myScrollToFirstDiff = scrollToFirstDiff; } + /** + * @deprecated Because it references by index. + */ + @Deprecated public Editor getEditor(int index) { return getEditorPlace(index).getEditor(); } @@ -167,10 +185,18 @@ public class MergePanel2 implements DiffViewer { return myData == null ? FileTypes.PLAIN_TEXT : getContentType(myData); } + /** + * @deprecated Because it references by index. + */ + @Deprecated public String getVersionTitle(int index) { return myEditorsPanels[index].getRawText(); } + /** + * @deprecated Because it references by index. + */ + @Deprecated public EditorPlace getEditorPlace(int index) { return (EditorPlace)myEditorsPanels[index].getComponent(); } @@ -198,7 +224,7 @@ public class MergePanel2 implements DiffViewer { Editor right = getEditor(2); myMergeList.setMarkups(left, base, right); - EditingSides[] sides = {new MyEditingSides(FragmentSide.SIDE1), new MyEditingSides(FragmentSide.SIDE2)}; + EditingSides[] sides = {getFirstEditingSide(), getSecondEditingSide()}; myScrollSupport.install(sides); for (int i = 0; i < myDividers.length; i++) { myDividers[i].listenEditors(sides[i]); @@ -216,6 +242,16 @@ public class MergePanel2 implements DiffViewer { } } + @NotNull + EditingSides getFirstEditingSide() { + return new MyEditingSides(FragmentSide.SIDE1); + } + + @NotNull + EditingSides getSecondEditingSide() { + return new MyEditingSides(FragmentSide.SIDE2); + } + public void setHighlighterSettings(@Nullable EditorColorsScheme settings) { for (EditorPlace place : getEditorPlaces()) { setHighlighterSettings(settings, place); @@ -418,6 +454,7 @@ public class MergePanel2 implements DiffViewer { editor.getSettings().setFoldingOutlineShown(false); editor.getFoldingModel().setFoldingEnabled(false); editor.getSettings().setLineMarkerAreaShown(false); + editor.getSettings().setFoldingOutlineShown(false); editor.getGutterComponentEx().setShowDefaultGutterPopup(false); initEditorSettings(editor); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanelColumn.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanelColumn.java new file mode 100644 index 000000000000..88d7a9f260f0 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanelColumn.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2012 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.diff.impl.incrementalMerge.ui; + +import com.intellij.openapi.diff.impl.highlighting.FragmentSide; + +/** + * Represents one of three columns in the 3-way merge. + * To be used when specific column code is needed. + * + * Little by little, this should substitute all legacy code, that references merge columns by digital indexes from 1 to 3 or from 0 to 2, + * as well as by {@link FragmentSide FragmentSides}. + * + * @author Kirill Likhodedov + */ +public enum MergePanelColumn { + LEFT, BASE, RIGHT +} diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java index 84603a84e88d..7ec483c19d5a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java @@ -21,6 +21,7 @@ import com.intellij.openapi.diff.impl.util.TextDiffType; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.util.Comparing; +import com.intellij.ui.ColorUtil; import com.intellij.util.ui.UIUtil; import java.awt.*; @@ -30,7 +31,9 @@ import java.util.ArrayList; * A polygon, which is drawn between editors in merge or diff dialogs, and which indicates the change flow from one editor to another. */ public class DividerPolygon { - public static final int OFFSET = 3; + public static final int TRANSPARENCY = 150; + public static final Color FRAMING_LINE_COLOR = Color.LIGHT_GRAY; + private final Color myColor; private final int myStart1; private final int myStart2; @@ -52,7 +55,7 @@ public class DividerPolygon { private void paint(Graphics2D g, int width) { g.setColor(myColor); g.fill(new Polygon(new int[]{0, 0, width, width}, new int[]{myStart1, myEnd1, myEnd2, myStart2}, 4)); - g.setColor(Color.GRAY); + g.setColor(FRAMING_LINE_COLOR); UIUtil.drawLine(g, 0, myStart1, width, myStart2); UIUtil.drawLine(g, 0, myEnd1, width, myEnd2); } @@ -74,10 +77,26 @@ public class DividerPolygon { return "<" + myStart1 + ", " + myEnd1 + " : " + myStart2 + ", " + myEnd2 + "> " + myColor; } - Color getColor() { + public Color getColor() { return myColor; } + public int getTopLeftY() { + return myStart1; + } + + public int getTopRightY() { + return myStart2; + } + + public int getBottomLeftY() { + return myEnd1; + } + + public int getBottomRightY() { + return myEnd2; + } + public static void paintPolygons(ArrayList polygons, Graphics2D g, int width) { g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java index 1d24fc30a1e7..00ae5234ff18 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java @@ -18,6 +18,7 @@ package com.intellij.openapi.diff.impl.util; import com.intellij.openapi.diff.DiffBundle; import com.intellij.openapi.diff.DiffColors; import com.intellij.openapi.diff.ex.DiffStatusBar; +import com.intellij.openapi.diff.impl.splitter.DividerPolygon; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.colors.TextAttributesKey; @@ -101,16 +102,20 @@ public class TextDiffType implements DiffStatusBar.LegendTypeDescriptor { return myAttributesKey; } + @Nullable public TextAttributes getTextAttributes(EditorColorsScheme scheme) { TextAttributes originalAttrs = scheme.getAttributes(myAttributesKey); + if (originalAttrs == null) { + return null; + } + TextAttributes overridingAttributes = new TextAttributes(); if (!myApplied) { - return originalAttrs; + overridingAttributes.setBackgroundColor(getMiddle(originalAttrs.getBackgroundColor(), scheme.getDefaultBackground())); } else { - TextAttributes overridingAttributes = new TextAttributes(); overridingAttributes.setBackgroundColor(scheme.getDefaultBackground()); - return TextAttributes.merge(originalAttrs, overridingAttributes); } + return TextAttributes.merge(originalAttrs, overridingAttributes); } @Nullable @@ -135,4 +140,20 @@ public class TextDiffType implements DiffStatusBar.LegendTypeDescriptor { public TextDiffTypeEnum getType() { return myType; } + + @NotNull + public static Color getMiddle(@NotNull Color highlight, @NotNull Color background) { + return new Color(avg(highlight.getRed(), background.getRed()), + avg(highlight.getGreen(), background.getGreen()), + avg(highlight.getBlue(), background.getBlue())); + } + + private static int avg(int highlight, int background) { + // transparency can't be used in the editor + // => emulating transparency, so that editor highlighting would be the same color as diff dividers + double coeff = DividerPolygon.TRANSPARENCY / 255.0; + double addendum = 1 - coeff; + return (int)(highlight * coeff + background * addendum); + } + } From 41472605d64fbeb353323783bff753855619f5d1 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Tue, 10 Jul 2012 17:46:45 +0400 Subject: [PATCH 13/20] [merge tool] "All changes resolved" message: show based on the component, not on the project. May fix some focus issues. --- .../openapi/diff/impl/mergeTool/MergeRequestImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/mergeTool/MergeRequestImpl.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/mergeTool/MergeRequestImpl.java index 6798ec93fbca..9e889122d631 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/mergeTool/MergeRequestImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/mergeTool/MergeRequestImpl.java @@ -236,7 +236,7 @@ public class MergeRequestImpl extends MergeRequest { } } - private class AllResolvedListener implements ChangeCounter.Listener, Runnable { + private static class AllResolvedListener implements ChangeCounter.Listener, Runnable { private final MergePanel2 myMergePanel; private final DialogWrapper myDialogWrapper; private boolean myWasInvoked = false; @@ -255,7 +255,7 @@ public class MergeRequestImpl extends MergeRequest { myWasInvoked = true; ChangeCounter.getOrCreate(myMergePanel.getMergeList()).removeListener(this); int doApply = Messages - .showOkCancelDialog(getProject(), DiffBundle.message("merge.all.changes.have.processed.save.and.finish.confirmation.text"), + .showOkCancelDialog(getWholePanel(), DiffBundle.message("merge.all.changes.have.processed.save.and.finish.confirmation.text"), DiffBundle.message("all.changes.processed.dialog.title"), DiffBundle.message("merge.save.and.finish.button"), DiffBundle.message("merge.continue.button"), Messages.getQuestionIcon()); From 51a8f15e9e1616e1dd44dbf4ebcd28c2184bba99 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Tue, 10 Jul 2012 19:52:03 +0400 Subject: [PATCH 14/20] [merge tool] better dividers for applied changes Let DividerPolygon know whether it is applied or not. Draw applied polygon as a rectangle instead of a filled square (actually via 2 lines). Make applied polygon non-transparent, to enhance its visibility. Draw applied changed markers above EditorPlaces as bold dotted lines instead of a filled square. --- .../impl/incrementalMerge/ui/EditorPlace.java | 45 +++++++++++++------ .../diff/impl/splitter/DividerPolygon.java | 31 +++++++++---- .../openapi/diff/impl/util/TextDiffType.java | 4 ++ 3 files changed, 58 insertions(+), 22 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java index 278dca08ebd9..dbf67e7af0bf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java @@ -87,42 +87,61 @@ public class EditorPlace extends JComponent implements Disposable { } if (gutter) { - drawAboveGutter(g2, startY, height, polygon.getColor()); + drawAboveGutter(g2, startY, height, polygon.getColor(), polygon.isApplied()); } else { - drawAboveScrollBar(g2, startY, height, polygon.getColor()); + drawAboveScrollBar(g2, startY, height, polygon.getColor(), polygon.isApplied()); } } } - private void drawAboveGutter(@NotNull Graphics2D g, int startY, int height, @NotNull Color color) { + private void drawAboveGutter(@NotNull Graphics2D g, int startY, int height, @NotNull Color color, boolean applied) { EditorGutterComponentEx gutter = ((EditorEx)myEditor).getGutterComponentEx(); g.setColor(color); if (((EditorEx)myEditor).getVerticalScrollbarOrientation() == EditorEx.VERTICAL_SCROLLBAR_RIGHT) { // scrollbar is at the right => the gutter is at the left (central editor case) int startX = gutter.getX(); - g.fillRect(startX, startY, gutter.getWidth() + 1, height); - - drawFramingLines(g, startX, startY, startX + gutter.getWidth(), startY + height); + if (!applied) { + g.fillRect(startX, startY, gutter.getWidth() + 1, height); + drawFramingLines(g, startX, startY, startX + gutter.getWidth(), startY + height); + } + else { + drawBoldDottedFramingLines(g, startX, gutter.getWidth() + 1, startY, height, color); + } } else { JComponent editorComponent = myEditor.getComponent(); int startX = editorComponent.getX() + editorComponent.getWidth() - gutter.getWidth() - 1; - g.fillRect(startX, startY, gutter.getWidth() + 1, height); - - drawFramingLines(g, startX, startY, startX + gutter.getWidth(), startY + height); + if (!applied) { + g.fillRect(startX, startY, gutter.getWidth() + 1, height); + drawFramingLines(g, startX, startY, startX + gutter.getWidth(), startY + height); + } + else { + int endX = startX + gutter.getWidth() + 1; + drawBoldDottedFramingLines(g, startX, endX, startY, height, color); + } } } - private void drawAboveScrollBar(@NotNull Graphics2D g, int startY, int height, @NotNull Color color) { + private static void drawBoldDottedFramingLines(Graphics2D g, int startX, int endX, int startY, int height, Color color) { + UIUtil.drawBoldDottedLine(g, startX, endX, startY, null, color, false); + UIUtil.drawBoldDottedLine(g, startX, endX, startY + height, null, color, false); + } + + private void drawAboveScrollBar(@NotNull Graphics2D g, int startY, int height, @NotNull Color color, boolean applied) { // painting only above the central scrollbar, because painting on edge scrollbars is not needed, and there are error stripes if (myColumn == MergePanelColumn.BASE) { g.setColor(color); JScrollBar scrollBar = ((EditorEx)myEditor).getScrollPane().getVerticalScrollBar(); int startX = scrollBar.getX(); - g.fillRect(startX, startY, scrollBar.getWidth(), height); - - drawFramingLines(g, startX, startY, startX + scrollBar.getWidth(), startY + height); + int endX = startX + scrollBar.getWidth(); + if (!applied) { + g.fillRect(startX, startY, scrollBar.getWidth(), height); + drawFramingLines(g, startX, startY, endX, startY + height); + } + else { + drawBoldDottedFramingLines(g, startX, endX, startY, height, color); + } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java index 7ec483c19d5a..330107e164a6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java @@ -39,8 +39,10 @@ public class DividerPolygon { private final int myStart2; private final int myEnd1; private final int myEnd2; + private final boolean myApplied; - public DividerPolygon(int start1, int start2, int end1, int end2, Color color) { + public DividerPolygon(int start1, int start2, int end1, int end2, Color color, boolean applied) { + myApplied = applied; myStart1 = advance(start1); myStart2 = advance(start2); myEnd1 = advance(end1); @@ -53,11 +55,18 @@ public class DividerPolygon { } private void paint(Graphics2D g, int width) { - g.setColor(myColor); - g.fill(new Polygon(new int[]{0, 0, width, width}, new int[]{myStart1, myEnd1, myEnd2, myStart2}, 4)); - g.setColor(FRAMING_LINE_COLOR); - UIUtil.drawLine(g, 0, myStart1, width, myStart2); - UIUtil.drawLine(g, 0, myEnd1, width, myEnd2); + if (!myApplied) { + g.setColor(myColor); + g.fill(new Polygon(new int[]{0, 0, width, width}, new int[]{myStart1, myEnd1, myEnd2, myStart2}, 4)); + g.setColor(FRAMING_LINE_COLOR); + UIUtil.drawLine(g, 0, myStart1, width, myStart2); + UIUtil.drawLine(g, 0, myEnd1, width, myEnd2); + } + else { + g.setColor(myColor); + UIUtil.drawLine(g, 0, myStart1 + 1, width, myStart2 + 1); + UIUtil.drawLine(g, 0, myEnd1 + 1, width, myEnd2 + 1); + } } public int hashCode() { @@ -97,6 +106,10 @@ public class DividerPolygon { return myEnd2; } + public boolean isApplied() { + return myApplied; + } + public static void paintPolygons(ArrayList polygons, Graphics2D g, int width) { g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); @@ -122,7 +135,7 @@ public class DividerPolygon { Trapezium trapezium = lineBlocks.getTrapezium(i); final TextDiffType type = lineBlocks.getType(i); Color color = type.getPolygonColor(editor1); - polygons.add(createPolygon(transformations, trapezium, color, left, diffDividerPolygonsOffset)); + polygons.add(createPolygon(transformations, trapezium, color, left, diffDividerPolygonsOffset, type.isApplied())); } return polygons; } @@ -133,7 +146,7 @@ public class DividerPolygon { } private static DividerPolygon createPolygon(Transformation[] transformations, Trapezium trapezium, Color color, FragmentSide left, - int diffDividerPolygonsOffset) { + int diffDividerPolygonsOffset, boolean applied) { Interval base1 = trapezium.getBase(left); Interval base2 = trapezium.getBase(left.otherSide()); Transformation leftTransform = transformations[left.getIndex()]; @@ -144,7 +157,7 @@ public class DividerPolygon { int end2 = rightTransform.transform(base2.getEnd()); return new DividerPolygon(start1 - diffDividerPolygonsOffset, start2 - diffDividerPolygonsOffset, end1 - diffDividerPolygonsOffset, end2 - diffDividerPolygonsOffset, - ColorUtil.toAlpha(color, TRANSPARENCY)); + applied ? color : ColorUtil.toAlpha(color, TRANSPARENCY), applied); } static Interval getVisibleInterval(Editor editor) { diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java index 00ae5234ff18..bab6f5544c08 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java @@ -53,6 +53,10 @@ public class TextDiffType implements DiffStatusBar.LegendTypeDescriptor { private final String myDisplayName; private final boolean myApplied; + public boolean isApplied() { + return myApplied; + } + public static TextDiffType create(@Nullable final TextDiffTypeEnum type) { if (TextDiffTypeEnum.INSERT.equals(type)) { return INSERT; From e473464eaf280856cbe12f343822db04d154aa03 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Wed, 11 Jul 2012 15:46:01 +0400 Subject: [PATCH 15/20] [merge tool] Double-lined separators for insertion/deletion highlighters. + refactoring: reusing LineSeparatorRenderer instances. --- .../impl/incrementalMerge/ChangeType.java | 47 +++++++++++++------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java index 927333625d6f..d6aa85c96a6a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java @@ -37,6 +37,9 @@ public class ChangeType { private final TextDiffType myDiffType; private final boolean myApplied; + private static final EmptyRangeLineSeparatorRenderer EMPTY_RANGE_LINE_SEPARATOR_RENDERER = new EmptyRangeLineSeparatorRenderer(); + private final LineSeparatorRenderer myStandardLineSeparatorRenderer = new StandardLineSeparatorRenderer(); + private ChangeType(TextDiffType diffType, boolean applied) { myApplied = applied; if (applied) { @@ -81,18 +84,6 @@ public class ChangeType { private RangeHighlighter addBlock(String text, ChangeSide changeSide, ChangeHighlighterHolder markup, TextDiffType diffType) { EditorColorsScheme colorScheme = markup.getEditor().getColorsScheme(); Color separatorColor = getSeparatorColor(diffType.getLegendColor(colorScheme)); - LineSeparatorRenderer separatorRenderer = new LineSeparatorRenderer() { - @Override - public void drawLine(Graphics g, int x1, int x2, int y) { - Graphics2D g2 = (Graphics2D) g; - if (myApplied) { - UIUtil.drawBoldDottedLine(g2, x1, x2, y, g2.getBackground(), g2.getColor(), false); - } - else { - UIUtil.drawDottedLine(g2, x1, y, x2, y, g2.getBackground(), g2.getColor()); - } - } - }; int length = text.length(); int start = changeSide.getStart(); @@ -102,7 +93,7 @@ public class ChangeType { if (highlighter != null) { highlighter.setLineSeparatorPlacement(SeparatorPlacement.TOP); highlighter.setLineSeparatorColor(separatorColor); - highlighter.setLineSeparatorRenderer(separatorRenderer); + highlighter.setLineSeparatorRenderer(myStandardLineSeparatorRenderer); } if (text.charAt(length - 1) == '\n') { @@ -113,7 +104,7 @@ public class ChangeType { if (highlighter != null) { highlighter.setLineSeparatorPlacement(SeparatorPlacement.BOTTOM); highlighter.setLineSeparatorColor(separatorColor); - highlighter.setLineSeparatorRenderer(separatorRenderer); + highlighter.setLineSeparatorRenderer(myStandardLineSeparatorRenderer); } return highlighter; } @@ -125,6 +116,7 @@ public class ChangeType { return null; } highlighter.setLineSeparatorPlacement(placement); + highlighter.setLineSeparatorRenderer(EMPTY_RANGE_LINE_SEPARATOR_RENDERER); return highlighter; } @@ -153,4 +145,31 @@ public class ChangeType { } + /** + * Insertion or deletion change have an empty range on one of the sides (inserting to, deleting from). + * A solid line is used to indicate this change in the target/original editor. + */ + private static class EmptyRangeLineSeparatorRenderer implements LineSeparatorRenderer { + @Override + public void drawLine(Graphics g, int x1, int x2, int y) { + g.drawLine(x1, y, x2, y); + g.drawLine(x1, y + 1, x2, y + 1); + } + } + + /** + * Draws standard separators around an ordinary change which contains some non-empty text range. + */ + private class StandardLineSeparatorRenderer implements LineSeparatorRenderer { + @Override + public void drawLine(Graphics g, int x1, int x2, int y) { + Graphics2D g2 = (Graphics2D) g; + if (myApplied) { + UIUtil.drawBoldDottedLine(g2, x1, x2, y, g2.getBackground(), g2.getColor(), false); + } + else { + UIUtil.drawDottedLine(g2, x1, y, x2, y, g2.getBackground(), g2.getColor()); + } + } + } } From 36574f915ec62a07615434f9d9691368fb416a84 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Wed, 11 Jul 2012 15:46:42 +0400 Subject: [PATCH 16/20] [merge tool] draw insertion/deletion one-line markers with the diff color instead of framing color --- .../impl/incrementalMerge/ui/EditorPlace.java | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java index dbf67e7af0bf..e0e980b7cb54 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java @@ -102,8 +102,13 @@ public class EditorPlace extends JComponent implements Disposable { // scrollbar is at the right => the gutter is at the left (central editor case) int startX = gutter.getX(); if (!applied) { - g.fillRect(startX, startY, gutter.getWidth() + 1, height); - drawFramingLines(g, startX, startY, startX + gutter.getWidth(), startY + height); + if (height > 2) { + g.fillRect(startX, startY, gutter.getWidth() + 1, height); + drawFramingLines(g, startX, startY, startX + gutter.getWidth(), startY + height, DividerPolygon.FRAMING_LINE_COLOR); + } + else { + drawFramingLines(g, startX, startY, startX + gutter.getWidth(), startY + height, color); + } } else { drawBoldDottedFramingLines(g, startX, gutter.getWidth() + 1, startY, height, color); @@ -114,7 +119,7 @@ public class EditorPlace extends JComponent implements Disposable { int startX = editorComponent.getX() + editorComponent.getWidth() - gutter.getWidth() - 1; if (!applied) { g.fillRect(startX, startY, gutter.getWidth() + 1, height); - drawFramingLines(g, startX, startY, startX + gutter.getWidth(), startY + height); + drawFramingLines(g, startX, startY, startX + gutter.getWidth(), startY + height, DividerPolygon.FRAMING_LINE_COLOR); } else { int endX = startX + gutter.getWidth() + 1; @@ -136,8 +141,13 @@ public class EditorPlace extends JComponent implements Disposable { int startX = scrollBar.getX(); int endX = startX + scrollBar.getWidth(); if (!applied) { - g.fillRect(startX, startY, scrollBar.getWidth(), height); - drawFramingLines(g, startX, startY, endX, startY + height); + if (height > 2) { + g.fillRect(startX, startY, scrollBar.getWidth(), height); + drawFramingLines(g, startX, startY, endX, startY + height, DividerPolygon.FRAMING_LINE_COLOR); + } + else { + drawFramingLines(g, startX, startY, endX, startY + height, color); + } } else { drawBoldDottedFramingLines(g, startX, endX, startY, height, color); @@ -145,9 +155,9 @@ public class EditorPlace extends JComponent implements Disposable { } } - private static void drawFramingLines(@NotNull Graphics2D g, int startX, int topY, int endX, int bottomY) { - UIUtil.drawLine(g, startX, topY, endX, topY, null, DividerPolygon.FRAMING_LINE_COLOR); - UIUtil.drawLine(g, startX, bottomY, endX, bottomY, null, DividerPolygon.FRAMING_LINE_COLOR); + private static void drawFramingLines(@NotNull Graphics2D g, int startX, int topY, int endX, int bottomY, @NotNull Color color) { + UIUtil.drawLine(g, startX, topY, endX, topY, null, color); + UIUtil.drawLine(g, startX, bottomY, endX, bottomY, null, color); } public void addNotify() { From d820edc992ad32e1634b13482dc2cdcb861c74b3 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Mon, 16 Jul 2012 19:01:23 +0400 Subject: [PATCH 17/20] [merge tool] Fix line separator coloring for applied changes It is incorrect to use g.getColor() to draw the separator. Take value from the TextDiffType. As a result, move the LineSeparatorRenderer inside addBlock(), because we need the editor color scheme. --- .../impl/incrementalMerge/ChangeType.java | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java index d6aa85c96a6a..89aa052bcb30 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java @@ -38,7 +38,6 @@ public class ChangeType { private final boolean myApplied; private static final EmptyRangeLineSeparatorRenderer EMPTY_RANGE_LINE_SEPARATOR_RENDERER = new EmptyRangeLineSeparatorRenderer(); - private final LineSeparatorRenderer myStandardLineSeparatorRenderer = new StandardLineSeparatorRenderer(); private ChangeType(TextDiffType diffType, boolean applied) { myApplied = applied; @@ -81,7 +80,7 @@ public class ChangeType { } @Nullable - private RangeHighlighter addBlock(String text, ChangeSide changeSide, ChangeHighlighterHolder markup, TextDiffType diffType) { + private RangeHighlighter addBlock(String text, ChangeSide changeSide, final ChangeHighlighterHolder markup, TextDiffType diffType) { EditorColorsScheme colorScheme = markup.getEditor().getColorsScheme(); Color separatorColor = getSeparatorColor(diffType.getLegendColor(colorScheme)); @@ -90,10 +89,23 @@ public class ChangeType { int end = start + length; RangeHighlighter highlighter = markup.addRangeHighlighter(start, end, LAYER, diffType, HighlighterTargetArea.EXACT_RANGE, myApplied); + LineSeparatorRenderer lineSeparatorRenderer = new LineSeparatorRenderer() { + @Override + public void drawLine(Graphics g, int x1, int x2, int y) { + Graphics2D g2 = (Graphics2D)g; + if (myApplied) { + UIUtil.drawBoldDottedLine(g2, x1, x2, y, null, myDiffType.getPolygonColor(markup.getEditor()), false); + } + else { + UIUtil.drawDottedLine(g2, x1, y, x2, y, null, g2.getColor()); + } + } + }; + if (highlighter != null) { highlighter.setLineSeparatorPlacement(SeparatorPlacement.TOP); highlighter.setLineSeparatorColor(separatorColor); - highlighter.setLineSeparatorRenderer(myStandardLineSeparatorRenderer); + highlighter.setLineSeparatorRenderer(lineSeparatorRenderer); } if (text.charAt(length - 1) == '\n') { @@ -104,7 +116,7 @@ public class ChangeType { if (highlighter != null) { highlighter.setLineSeparatorPlacement(SeparatorPlacement.BOTTOM); highlighter.setLineSeparatorColor(separatorColor); - highlighter.setLineSeparatorRenderer(myStandardLineSeparatorRenderer); + highlighter.setLineSeparatorRenderer(lineSeparatorRenderer); } return highlighter; } @@ -156,20 +168,4 @@ public class ChangeType { g.drawLine(x1, y + 1, x2, y + 1); } } - - /** - * Draws standard separators around an ordinary change which contains some non-empty text range. - */ - private class StandardLineSeparatorRenderer implements LineSeparatorRenderer { - @Override - public void drawLine(Graphics g, int x1, int x2, int y) { - Graphics2D g2 = (Graphics2D) g; - if (myApplied) { - UIUtil.drawBoldDottedLine(g2, x1, x2, y, g2.getBackground(), g2.getColor(), false); - } - else { - UIUtil.drawDottedLine(g2, x1, y, x2, y, g2.getBackground(), g2.getColor()); - } - } - } } From 9cf8a86ffe0bc3aa34a710d6e063085b22c64ada Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Mon, 16 Jul 2012 19:17:35 +0400 Subject: [PATCH 18/20] [merge tool] Better gutter markers, solve EditorPlace repaint problems on dialog init, and on change apply Problems: * On dialog show, vertical scrollbars are independently repainted from the Animator in the ButtonlessScrollbarUI => after unknown period of time after dialog init, the scrollbar gets repainted, thus overdrawing the change markers. * On change apply, the document gets modified, editor highlightings change, and gutter is repainted, thus also overdrawing the change markers. Subscribing to merge list events is not sufficient, since document is updated from one of such listeners, and the order of listeners calling is undefined. * Since the change markers are drawn with a transparent color above the gutter, line numbers and apply/ignore actions look pale, and even seem disabled. Solution: * Don't draw above the gutter, instead implement a LineMarkerRenderer and draw gutter background. * Repaint markers drawn above the scrollbar on each scrollbar repaint. (btw, doing the same with the gutter doesn't work, because redrawing the gutter so often makes the scrolling process unacceptably jittering). Details: * EditorEx.registerScrollBarRepaintCallback to be called on each scrollbar repaint. Register the callback in the EditorPlace, and repaint just the scrollbar. * Remove all drawing above the gutter from EditorPlace, leave only drawing above the scrollbar. It simplifies the code, btw, by removing the SideInfo helper, and many if-else branches. * Add a LineMarkerRenderer to the RangeHighlighters, which would draw the needed marker in the gutter. * Modify the EditorGutterComponentImpl so that line numbers are painted after line markers, but line numbers background is painted as usual. That makes possible to fill the entire gutter with a color (from a LineMarkerRenderer), but still draw the line numbers above it. * Playing with transparency is no more needed, so remove it from the DividerPolygon. --- .../injected/editor/EditorWindow.java | 5 + .../intellij/openapi/diff/impl/DiffUtil.java | 7 + .../ChangeHighlighterHolder.java | 56 ++++- .../impl/incrementalMerge/ui/EditorPlace.java | 212 ++++-------------- .../impl/incrementalMerge/ui/MergePanel2.java | 2 +- .../diff/impl/splitter/DividerPolygon.java | 5 +- .../openapi/diff/impl/util/TextDiffType.java | 13 +- .../intellij/openapi/editor/ex/EditorEx.java | 15 ++ .../impl/EditorGutterComponentImpl.java | 14 +- .../openapi/editor/impl/EditorImpl.java | 23 +- .../util/src/com/intellij/util/ui/UIUtil.java | 15 ++ 11 files changed, 186 insertions(+), 181 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java index 539bce6566bd..a722bf38c31b 100644 --- a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java +++ b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java @@ -789,6 +789,11 @@ public class EditorWindow extends UserDataHolderBase implements EditorEx { myDelegate.setPurePaintingMode(enabled); } + @Override + public void registerScrollBarRepaintCallback(@Nullable RepaintCallback callback) { + myDelegate.registerScrollBarRepaintCallback(callback); + } + @Override public void setPrefixTextAndAttributes(@Nullable String prefixText, @Nullable TextAttributes attributes) { myDelegate.setPrefixTextAndAttributes(prefixText, attributes); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffUtil.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffUtil.java index 10f92e976815..407c50ecd4d3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffUtil.java @@ -32,10 +32,12 @@ import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.FrameWrapper; import com.intellij.util.ImageLoader; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.awt.*; public class DiffUtil { private DiffUtil() { @@ -94,4 +96,9 @@ public class DiffUtil { editor.getGutterComponentEx().revalidateMarkup(); return editor; } + + public static void drawBoldDottedFramingLines(Graphics2D g, int startX, int endX, int startY, int bottomY, Color color) { + UIUtil.drawBoldDottedLine(g, startX, endX, startY, null, color, false); + UIUtil.drawBoldDottedLine(g, startX, endX, bottomY, null, color, false); + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeHighlighterHolder.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeHighlighterHolder.java index b9da081a35e4..f9c4bdd93412 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeHighlighterHolder.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeHighlighterHolder.java @@ -17,13 +17,16 @@ package com.intellij.openapi.diff.impl.incrementalMerge; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.diff.impl.DiffUtil; +import com.intellij.openapi.diff.impl.splitter.DividerPolygon; import com.intellij.openapi.diff.impl.util.GutterActionRenderer; import com.intellij.openapi.diff.impl.util.TextDiffType; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.markup.HighlighterTargetArea; -import com.intellij.openapi.editor.markup.MarkupModel; -import com.intellij.openapi.editor.markup.RangeHighlighter; -import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.ex.EditorGutterComponentEx; +import com.intellij.openapi.editor.ex.RangeHighlighterEx; +import com.intellij.openapi.editor.markup.*; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -78,6 +81,7 @@ class ChangeHighlighterHolder { RangeHighlighter highlighter = getMarkupModel().addLineHighlighter(line, layer, null); highlighter.setLineSeparatorColor(diffType.getTextBackground(myEditor)); highlighterCreated(highlighter, diffType.getTextAttributes(myEditor), applied); + highlighter.setLineMarkerRenderer(new ChangesColoringLineMarkerRenderer(diffType)); return highlighter; } @@ -88,6 +92,7 @@ class ChangeHighlighterHolder { TextAttributes attributes = type.getTextAttributes(myEditor); RangeHighlighter highlighter = getMarkupModel().addRangeHighlighter(start, end, layer, attributes, targetArea); highlighterCreated(highlighter, attributes, applied); + highlighter.setLineMarkerRenderer(new ChangesColoringLineMarkerRenderer(type)); return highlighter; } @@ -131,7 +136,7 @@ class ChangeHighlighterHolder { if (myMainHighlighter != null && myActions != null && myActions.length > 0) { myActionHighlighters = new RangeHighlighter[myActions.length]; for (int i = 0; i < myActionHighlighters.length; i++) { - RangeHighlighter highlighter = cloneMainHighlighter(myMainHighlighter); + RangeHighlighterEx highlighter = (RangeHighlighterEx)cloneMainHighlighter(myMainHighlighter); highlighter.setGutterIconRenderer(new GutterActionRenderer(myActions[i])); myActionHighlighters[i] = highlighter; } @@ -148,4 +153,45 @@ class ChangeHighlighterHolder { removeHighlighters(); setHighlighter(changeSide, type); } + + /** + * Expands the change highlighters to the editor's gutter. + */ + private class ChangesColoringLineMarkerRenderer implements LineMarkerRenderer { + private final TextDiffType myDiffType; + + public ChangesColoringLineMarkerRenderer(@NotNull TextDiffType diffType) { + myDiffType = diffType; + } + + @Override + public void paint(Editor editor, Graphics g, Rectangle range) { + Color color = myDiffType.getPolygonColor(myEditor); + if (color == null) { + return; + } + + EditorGutterComponentEx gutter = ((EditorEx)editor).getGutterComponentEx(); + Graphics2D g2 = (Graphics2D)g; + int x = 0; + int y = range.y; + int width = gutter.getWidth(); + int height = range.height; + + if (!myDiffType.isApplied()) { + if (height > 2) { + g.setColor(color); + g.fillRect(x, y, width, height); + UIUtil.drawFramingLines(g2, x, x + width, y - 1, y + height - 1, DividerPolygon.FRAMING_LINE_COLOR); + } + else { + // insertion or deletion, when a range is null, and the text highlighter is a 2 pixel line + UIUtil.drawFramingLines(g2, x, x + width, y - 1, y + height, color); + } + } + else { + DiffUtil.drawBoldDottedFramingLines(g2, x, x + width, y - 1, y + height - 1, color); + } + } + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java index e0e980b7cb54..aaa5c98e9702 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java @@ -17,19 +17,14 @@ package com.intellij.openapi.diff.impl.incrementalMerge.ui; import com.intellij.openapi.Disposable; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.diff.impl.EditingSides; +import com.intellij.openapi.diff.impl.DiffUtil; import com.intellij.openapi.diff.impl.highlighting.FragmentSide; import com.intellij.openapi.diff.impl.splitter.DividerPolygon; import com.intellij.openapi.diff.impl.util.DiffDivider; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorFactory; -import com.intellij.openapi.editor.event.CaretEvent; -import com.intellij.openapi.editor.event.CaretListener; -import com.intellij.openapi.editor.event.VisibleAreaEvent; -import com.intellij.openapi.editor.event.VisibleAreaListener; import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.editor.ex.EditorGutterComponentEx; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -41,26 +36,19 @@ import java.util.ArrayList; /** * The container for an {@link Editor}, which is added then to {@link com.intellij.openapi.diff.impl.util.ThreePanels}. */ -public class EditorPlace extends JComponent implements Disposable { +public class EditorPlace extends JComponent implements Disposable, EditorEx.RepaintCallback { private static final Logger LOG = Logger.getInstance(EditorPlace.class); @NotNull private final MergePanel2.DiffEditorState myState; @NotNull private final MergePanelColumn myColumn; - @NotNull private final SideInfo mySideInfo; + @NotNull private final MergePanel2 myMergePanel; @NotNull private final ArrayList myListeners = new ArrayList(); - - @Nullable private Editor myEditor; - - private final VisibleAreaListener myVisibleAreaListener = new VisibleAreaListener() { - public void visibleAreaChanged(VisibleAreaEvent e) { - repaint(); - } - }; + @Nullable private EditorEx myEditor; public EditorPlace(@NotNull MergePanel2.DiffEditorState state, @NotNull MergePanelColumn column, @NotNull MergePanel2 mergePanel) { myState = state; myColumn = column; - mySideInfo = SideInfo.convertFromColumn(mergePanel, myColumn); + myMergePanel = mergePanel; setLayout(new BorderLayout()); } @@ -68,98 +56,64 @@ public class EditorPlace extends JComponent implements Disposable { @Override public void paint(Graphics g) { super.paint(g); + paintThis(g); + } + public void call(Graphics g) { + repaintScrollbar(); + } + + private void repaintScrollbar() { + if (myEditor == null || myColumn != MergePanelColumn.BASE) { + return; // we draw above the scrollbar only in the central column + } + Component editorComponent = myEditor.getComponent(); + JScrollBar scrollBar = myEditor.getScrollPane().getVerticalScrollBar(); + repaint(editorComponent.getWidth() - scrollBar.getWidth(), 0, scrollBar.getWidth(), scrollBar.getHeight()); + } + + private void paintThis(Graphics g) { if (myEditor != null) { - Graphics2D g2 = (Graphics2D)g; - drawAbove(true, g2, mySideInfo.createPolygonsForGutter(), mySideInfo.isTakeLeftSideOfPolygonForGutter()); - drawAbove(false, g2, mySideInfo.createPolygonsForScrollbar(), mySideInfo.isTakeLeftSideOfPolygonForScrollbar()); - } - } + ArrayList polygons = DividerPolygon.createVisiblePolygons(myMergePanel.getSecondEditingSide(), FragmentSide.SIDE1, + DiffDivider.MERGE_DIVIDER_POLYGONS_OFFSET); + for (DividerPolygon polygon : polygons) { + int startY = polygon.getTopLeftY(); + int endY = polygon.getBottomLeftY(); + int height = endY - startY; - private void drawAbove(boolean gutter, Graphics2D g2, ArrayList polygons, boolean takeLeftSideOfPolygon) { - for (DividerPolygon polygon : polygons) { - int startY = takeLeftSideOfPolygon ? polygon.getTopLeftY() :polygon.getTopRightY(); - int endY = takeLeftSideOfPolygon ? polygon.getBottomLeftY() : polygon.getBottomRightY(); - int height = endY - startY; + if (height == 0) { // draw at least a one-pixel line (e.g. for insertion or deletion), as it is done in highlighters + height = 1; + } - if (height == 0) { // draw at least a one-pixel line (e.g. for insertion or deletion), as it is done in highlighters - height = 1; - } - - if (gutter) { - drawAboveGutter(g2, startY, height, polygon.getColor(), polygon.isApplied()); - } - else { - drawAboveScrollBar(g2, startY, height, polygon.getColor(), polygon.isApplied()); + drawPolygonAboveScrollBar((Graphics2D)g, startY, height, polygon.getColor(), polygon.isApplied()); } } } - private void drawAboveGutter(@NotNull Graphics2D g, int startY, int height, @NotNull Color color, boolean applied) { - EditorGutterComponentEx gutter = ((EditorEx)myEditor).getGutterComponentEx(); + private void drawPolygonAboveScrollBar(@NotNull Graphics2D g, int startY, int height, @NotNull Color color, boolean applied) { + // painting only above the central scrollbar, because painting on edge scrollbars is not needed, and there are error stripes + if (myColumn != MergePanelColumn.BASE) { + return; + } + g.setColor(color); - if (((EditorEx)myEditor).getVerticalScrollbarOrientation() == EditorEx.VERTICAL_SCROLLBAR_RIGHT) { - // scrollbar is at the right => the gutter is at the left (central editor case) - int startX = gutter.getX(); - if (!applied) { - if (height > 2) { - g.fillRect(startX, startY, gutter.getWidth() + 1, height); - drawFramingLines(g, startX, startY, startX + gutter.getWidth(), startY + height, DividerPolygon.FRAMING_LINE_COLOR); - } - else { - drawFramingLines(g, startX, startY, startX + gutter.getWidth(), startY + height, color); - } + JScrollBar scrollBar = myEditor.getScrollPane().getVerticalScrollBar(); + int startX = scrollBar.getX(); + int endX = startX + scrollBar.getWidth() - 1; + if (!applied) { + if (height > 2) { + g.fillRect(startX, startY, scrollBar.getWidth(), height); + UIUtil.drawFramingLines(g, startX, endX, startY, startY + height, DividerPolygon.FRAMING_LINE_COLOR); } else { - drawBoldDottedFramingLines(g, startX, gutter.getWidth() + 1, startY, height, color); + UIUtil.drawFramingLines(g, startX, endX, startY, startY + height, color); } } else { - JComponent editorComponent = myEditor.getComponent(); - int startX = editorComponent.getX() + editorComponent.getWidth() - gutter.getWidth() - 1; - if (!applied) { - g.fillRect(startX, startY, gutter.getWidth() + 1, height); - drawFramingLines(g, startX, startY, startX + gutter.getWidth(), startY + height, DividerPolygon.FRAMING_LINE_COLOR); - } - else { - int endX = startX + gutter.getWidth() + 1; - drawBoldDottedFramingLines(g, startX, endX, startY, height, color); - } + DiffUtil.drawBoldDottedFramingLines(g, startX, endX, startY, startY + height, color); } } - private static void drawBoldDottedFramingLines(Graphics2D g, int startX, int endX, int startY, int height, Color color) { - UIUtil.drawBoldDottedLine(g, startX, endX, startY, null, color, false); - UIUtil.drawBoldDottedLine(g, startX, endX, startY + height, null, color, false); - } - - private void drawAboveScrollBar(@NotNull Graphics2D g, int startY, int height, @NotNull Color color, boolean applied) { - // painting only above the central scrollbar, because painting on edge scrollbars is not needed, and there are error stripes - if (myColumn == MergePanelColumn.BASE) { - g.setColor(color); - JScrollBar scrollBar = ((EditorEx)myEditor).getScrollPane().getVerticalScrollBar(); - int startX = scrollBar.getX(); - int endX = startX + scrollBar.getWidth(); - if (!applied) { - if (height > 2) { - g.fillRect(startX, startY, scrollBar.getWidth(), height); - drawFramingLines(g, startX, startY, endX, startY + height, DividerPolygon.FRAMING_LINE_COLOR); - } - else { - drawFramingLines(g, startX, startY, endX, startY + height, color); - } - } - else { - drawBoldDottedFramingLines(g, startX, endX, startY, height, color); - } - } - } - - private static void drawFramingLines(@NotNull Graphics2D g, int startX, int topY, int endX, int bottomY, @NotNull Color color) { - UIUtil.drawLine(g, startX, topY, endX, topY, null, color); - UIUtil.drawLine(g, startX, bottomY, endX, bottomY, null, color); - } - public void addNotify() { if (myEditor != null) { super.addNotify(); @@ -175,13 +129,8 @@ public class EditorPlace extends JComponent implements Disposable { myEditor = myState.createEditor(); if (myEditor == null) return; add(myEditor.getComponent(), BorderLayout.CENTER); - myEditor.getScrollingModel().addVisibleAreaListener(myVisibleAreaListener); - myEditor.getCaretModel().addCaretListener(new CaretListener() { - @Override - public void caretPositionChanged(CaretEvent e) { - repaint(); - } - }); + myEditor.registerScrollBarRepaintCallback(this); + repaint(); fireEditorCreated(); } @@ -217,7 +166,7 @@ public class EditorPlace extends JComponent implements Disposable { private void removeEditor() { if (myEditor != null) { - myEditor.getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener); + myEditor.registerScrollBarRepaintCallback(null); Editor releasedEditor = myEditor; remove(myEditor.getComponent()); getEditorFactory().releaseEditor(myEditor); @@ -264,67 +213,4 @@ public class EditorPlace extends JComponent implements Disposable { public void dispose() { removeEditor(); } - - /** - * Helper structure to encapsulate the legacy-style ({@link EditingSides}, {@link FragmentSide}) information about merge columns. - */ - private static class SideInfo { - @NotNull private final EditingSides myEditingSidesForGutter; - @NotNull private final FragmentSide myFragmentSideForGutter; - private final boolean myTakeLeftSideOfPolygonForGutter; - @NotNull private final EditingSides myEditingSidesForScrollbar; - @NotNull private final FragmentSide myFragmentSideForScrollbar; - private final boolean myTakeLeftSideOfPolygonForScrollbar; - - private SideInfo(@NotNull EditingSides editingSidesForGutter, @NotNull FragmentSide fragmentSideForGutter, - boolean takeLeftSideOfPolygonForGutter, - @NotNull EditingSides editingSidesForScrollbar, @NotNull FragmentSide fragmentSideForScrollbar, - boolean takeLeftSideOfPolygonForScrollbar) { - myEditingSidesForGutter = editingSidesForGutter; - myFragmentSideForGutter = fragmentSideForGutter; - myTakeLeftSideOfPolygonForGutter = takeLeftSideOfPolygonForGutter; - myEditingSidesForScrollbar = editingSidesForScrollbar; - myFragmentSideForScrollbar = fragmentSideForScrollbar; - myTakeLeftSideOfPolygonForScrollbar = takeLeftSideOfPolygonForScrollbar; - } - - public SideInfo(EditingSides side, FragmentSide fragmentSide, boolean takeLeftSideOfPolygon) { - this(side, fragmentSide, takeLeftSideOfPolygon, side, fragmentSide, takeLeftSideOfPolygon); - } - - public ArrayList createPolygonsForGutter() { - return DividerPolygon.createVisiblePolygons(myEditingSidesForGutter, myFragmentSideForGutter, - DiffDivider.MERGE_DIVIDER_POLYGONS_OFFSET); - } - - public ArrayList createPolygonsForScrollbar() { - return DividerPolygon.createVisiblePolygons(myEditingSidesForScrollbar, myFragmentSideForScrollbar, - DiffDivider.MERGE_DIVIDER_POLYGONS_OFFSET); - } - - public boolean isTakeLeftSideOfPolygonForGutter() { - return myTakeLeftSideOfPolygonForGutter; - } - - public boolean isTakeLeftSideOfPolygonForScrollbar() { - return myTakeLeftSideOfPolygonForScrollbar; - } - - @NotNull - private static SideInfo convertFromColumn(@NotNull MergePanel2 mergePanel, @NotNull MergePanelColumn column) { - switch (column) { - case LEFT: - return new SideInfo(mergePanel.getFirstEditingSide(), FragmentSide.SIDE2, true); - case BASE: - return new SideInfo(mergePanel.getFirstEditingSide(), FragmentSide.SIDE2, false, - mergePanel.getSecondEditingSide(), FragmentSide.SIDE1, true); - case RIGHT: - return new SideInfo(mergePanel.getSecondEditingSide(), FragmentSide.SIDE1, false); - default: - throw new IllegalStateException("Incorrect column value: " + column); - } - } - - } - } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java index cf93ea82def7..44d0195afef9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java @@ -440,7 +440,7 @@ public class MergePanel2 implements DiffViewer { } @Nullable - public Editor createEditor() { + public EditorEx createEditor() { Document document = getDocument(); if (document == null) return null; Project project = myData.getProject(); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java index 330107e164a6..bf809557a2c3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java @@ -21,7 +21,6 @@ import com.intellij.openapi.diff.impl.util.TextDiffType; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.util.Comparing; -import com.intellij.ui.ColorUtil; import com.intellij.util.ui.UIUtil; import java.awt.*; @@ -65,7 +64,9 @@ public class DividerPolygon { else { g.setColor(myColor); UIUtil.drawLine(g, 0, myStart1 + 1, width, myStart2 + 1); + UIUtil.drawLine(g, 0, myStart1 + 2, width, myStart2 + 2); UIUtil.drawLine(g, 0, myEnd1 + 1, width, myEnd2 + 1); + UIUtil.drawLine(g, 0, myEnd1, width, myEnd2); } } @@ -157,7 +158,7 @@ public class DividerPolygon { int end2 = rightTransform.transform(base2.getEnd()); return new DividerPolygon(start1 - diffDividerPolygonsOffset, start2 - diffDividerPolygonsOffset, end1 - diffDividerPolygonsOffset, end2 - diffDividerPolygonsOffset, - applied ? color : ColorUtil.toAlpha(color, TRANSPARENCY), applied); + color, applied); } static Interval getVisibleInterval(Editor editor) { diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java index bab6f5544c08..fd2ca0e3a24b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java @@ -124,11 +124,18 @@ public class TextDiffType implements DiffStatusBar.LegendTypeDescriptor { @Nullable public Color getPolygonColor(Editor editor) { - return getLegendColor(editor.getColorsScheme()); + if (isApplied()) { + return getLegendColor(editor.getColorsScheme()); + } + else { + TextAttributes attributes = getTextAttributes(editor.getColorsScheme()); + return attributes == null ? null : attributes.getBackgroundColor(); + } } - public TextAttributes getTextAttributes(Editor editor1) { - return getTextAttributes(editor1.getColorsScheme()); + @Nullable + public TextAttributes getTextAttributes(Editor editor) { + return getTextAttributes(editor.getColorsScheme()); } @Nullable diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java index c9f161cc7d36..b5e04cbabe43 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java @@ -237,4 +237,19 @@ public interface EditorEx extends Editor { * @param enabled 'pure painting mode' status to use */ void setPurePaintingMode(boolean enabled); + + /** + * Allows to register a callback that will be called one each repaint of the editor vertical scrollbar. + * This is needed to allow a parent component draw above the scrollbar components (e.g. in the merge tool), + * otherwise the drawings are cleared once the scrollbar gets repainted (which may happen suddenly, because the scrollbar UI uses the + * {@link com.intellij.util.ui.Animator} to draw itself. + * @param callback callback which will be called from the {@link javax.swing.JComponent#paint(java.awt.Graphics)} method of + * the editor vertical scrollbar. + */ + void registerScrollBarRepaintCallback(@Nullable RepaintCallback callback); + + interface RepaintCallback { + void call(Graphics g); + } + } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java index 08d0a0f0d559..56d12ea41b30 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java @@ -66,8 +66,10 @@ import javax.swing.plaf.ComponentUI; import java.awt.*; import java.awt.event.*; import java.awt.geom.AffineTransform; -import java.util.*; +import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.TreeMap; class EditorGutterComponentImpl extends EditorGutterComponentEx implements MouseListener, MouseMotionListener { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.EditorGutterComponentImpl"); @@ -185,7 +187,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse } UISettings.setupAntialiasing(g); - paintLineNumbers(g, clip); + paintLineNumbersBackground(g, clip); paintAnnotations(g, clip); Object hint = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING); @@ -197,6 +199,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse paintFoldingBackground(g, clip, firstVisibleOffset, lastVisibleOffset); paintLineMarkers(g, clip, firstVisibleOffset, lastVisibleOffset); paintFoldingTree(g, clip, firstVisibleOffset, lastVisibleOffset); + paintLineNumbers(g, clip); } finally { g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, hint); @@ -322,13 +325,18 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse private void paintLineNumbers(Graphics g, Rectangle clip) { if (isLineNumbersShown()) { - paintBackground(g, clip, getLineNumberAreaOffset(), getLineNumberAreaWidth()); int x = getLineNumberAreaOffset() + getLineNumberAreaWidth() - 2; UIUtil.drawVDottedLine((Graphics2D)g, x, clip.y, clip.y + clip.height, null, getOutlineColor(false)); doPaintLineNumbers(g, clip); } } + private void paintLineNumbersBackground(Graphics g, Rectangle clip) { + if (isLineNumbersShown()) { + paintBackground(g, clip, getLineNumberAreaOffset(), getLineNumberAreaWidth()); + } + } + @Override public Color getBackground() { if (myBackgroundColor == null) { 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 4cb2ef64c531..48407568ae9f 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 @@ -75,10 +75,7 @@ import com.intellij.ui.LightweightHint; import com.intellij.ui.SideBorder; import com.intellij.ui.components.JBScrollBar; import com.intellij.ui.components.JBScrollPane; -import com.intellij.util.Alarm; -import com.intellij.util.IJSwingUtilities; -import com.intellij.util.Processor; -import com.intellij.util.Producer; +import com.intellij.util.*; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Convertor; import com.intellij.util.containers.HashMap; @@ -528,6 +525,11 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi myPurePaintingMode = enabled; } + @Override + public void registerScrollBarRepaintCallback(@Nullable RepaintCallback callback) { + myVerticalScrollBar.registerRepaintCallback(callback); + } + @Override public boolean isViewer() { return myIsViewer || myIsRendererMode; @@ -4381,6 +4383,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi class MyScrollBar extends JBScrollBar implements IdeGlassPane.TopComponent { @NonNls private static final String APPLE_LAF_AQUA_SCROLL_BAR_UI_CLASS = "apple.laf.AquaScrollBarUI"; private ScrollBarUI myPersistentUI; + @Nullable private RepaintCallback myRepaintCallback; private MyScrollBar(@JdkConstants.AdjustableOrientation int orientation) { super(orientation); @@ -4402,6 +4405,14 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi super.setUI(myPersistentUI); } + @Override + public void paint(Graphics g) { + super.paint(g); + if (myRepaintCallback != null) { + myRepaintCallback.call(g); + } + } + /** * This is helper method. It returns height of the top (decrease) scroll bar * button. Please note, that it's possible to return real height only if scroll bar @@ -4472,6 +4483,10 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi Rectangle vr = vp.getViewRect(); return myEditorComponent.getScrollableBlockIncrement(vr, SwingConstants.VERTICAL, direction); } + + public void registerRepaintCallback(@Nullable RepaintCallback callback) { + myRepaintCallback = callback; + } } private MyEditable getViewer() { diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index a4b9f04ec17e..eb34c5348389 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -113,6 +113,21 @@ public class UIUtil { MacUIUtil.getInvertedTextCursor(): Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR); } + /** + * Draws two horizontal lines, the first at {@code topY}, the second at {@code bottomY}. + * The purpose of this method (and the ground of the name) is to draw two lines framing a horizontal filled rectangle. + * @param g Graphics context to draw with. + * @param startX x-start point. + * @param endX x-end point. + * @param topY y-coordinate of the first line. + * @param bottomY y-coordinate of the second line. + * @param color color of the lines. + */ + public static void drawFramingLines(@NotNull Graphics2D g, int startX, int endX, int topY, int bottomY, @NotNull Color color) { + drawLine(g, startX, topY, endX, topY, null, color); + drawLine(g, startX, bottomY, endX, bottomY, null, color); + } + public enum FontSize {NORMAL, SMALL, MINI} public enum ComponentStyle {REGULAR, SMALL, MINI} public enum FontColor {NORMAL, BRIGHTER} From 82efc5150796d27bb471ed702522bd441d9685a2 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Mon, 16 Jul 2012 19:21:23 +0400 Subject: [PATCH 19/20] EditorGutterComponentImpl: fix calculation of the space needed for foldings Seems to be a misprint in the old code: if the folding outline is not shown, no need to reserve place for it. --- .../openapi/editor/impl/EditorGutterComponentImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java index 56d12ea41b30..cefdd6d53c23 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java @@ -295,7 +295,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse doPaintFoldingTree((Graphics2D)g, clip, firstVisibleOffset, lastVisibleOffset); } else { - UIUtil.drawVDottedLine((Graphics2D)g, clip.x + clip.width -1, clip.y, clip.y + clip.height, null, getOutlineColor(false)); + UIUtil.drawVDottedLine((Graphics2D)g, clip.x + clip.width - 1, clip.y, clip.y + clip.height, null, getOutlineColor(false)); } } @@ -561,7 +561,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse }); myLineMarkerAreaWidth = myIconsAreaWidth + FREE_PAINTERS_AREA_WIDTH + - (isFoldingOutlineShown() ? 0 : getFoldingAnchorWidth() / 2); + (isFoldingOutlineShown() ? getFoldingAnchorWidth() / 2 : 0); } private void paintGutterRenderers(final Graphics g, int firstVisibleOffset, int lastVisibleOffset) { From 7870427927511f4417647e41c40a4c38485c4edf Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Tue, 17 Jul 2012 14:28:35 +0400 Subject: [PATCH 20/20] [merge tool] Remove middle-color hack in TextDiffType It was used to make the highlighting look the same as the semi-transparent colors drawn above the gutters. Since another logic without transparency is used now, there is no need to change the diff color defined in the settings. --- .../openapi/diff/impl/util/TextDiffType.java | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java index fd2ca0e3a24b..f1d5bcaae932 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java @@ -18,7 +18,6 @@ package com.intellij.openapi.diff.impl.util; import com.intellij.openapi.diff.DiffBundle; import com.intellij.openapi.diff.DiffColors; import com.intellij.openapi.diff.ex.DiffStatusBar; -import com.intellij.openapi.diff.impl.splitter.DividerPolygon; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.colors.TextAttributesKey; @@ -113,10 +112,7 @@ public class TextDiffType implements DiffStatusBar.LegendTypeDescriptor { return null; } TextAttributes overridingAttributes = new TextAttributes(); - if (!myApplied) { - overridingAttributes.setBackgroundColor(getMiddle(originalAttrs.getBackgroundColor(), scheme.getDefaultBackground())); - } - else { + if (myApplied) { overridingAttributes.setBackgroundColor(scheme.getDefaultBackground()); } return TextAttributes.merge(originalAttrs, overridingAttributes); @@ -152,19 +148,4 @@ public class TextDiffType implements DiffStatusBar.LegendTypeDescriptor { return myType; } - @NotNull - public static Color getMiddle(@NotNull Color highlight, @NotNull Color background) { - return new Color(avg(highlight.getRed(), background.getRed()), - avg(highlight.getGreen(), background.getGreen()), - avg(highlight.getBlue(), background.getBlue())); - } - - private static int avg(int highlight, int background) { - // transparency can't be used in the editor - // => emulating transparency, so that editor highlighting would be the same color as diff dividers - double coeff = DividerPolygon.TRANSPARENCY / 255.0; - double addendum = 1 - coeff; - return (int)(highlight * coeff + background * addendum); - } - }