aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorangelozerr2018-06-04 12:50:02 -0400
committerLars Vogel2018-06-22 02:41:29 -0400
commit883e4bcb4c17378f820408e38311de62afbdf4a9 (patch)
tree03f00fc90e7718f70038b217950255f145ef054c
parentd868122644e5c227a2f2ac9579634ace561ad8a1 (diff)
downloadeclipse.platform.text-883e4bcb4c17378f820408e38311de62afbdf4a9.tar.gz
eclipse.platform.text-883e4bcb4c17378f820408e38311de62afbdf4a9.tar.xz
eclipse.platform.text-883e4bcb4c17378f820408e38311de62afbdf4a9.zip
Bug 321410 - [EditorMgmt][navigation] Provide Mini-map of text in editorI20180625-1545
Change-Id: If88c7bf0fdd81e2c3540c0cf967288a21410afc6 Signed-off-by: angelozerr <angelo.zerr@gmail.com>
-rw-r--r--org.eclipse.ui.workbench.texteditor/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.ui.workbench.texteditor/icons/full/eview16/minimap.pngbin0 -> 332 bytes
-rw-r--r--org.eclipse.ui.workbench.texteditor/plugin.properties2
-rw-r--r--org.eclipse.ui.workbench.texteditor/plugin.xml13
-rw-r--r--org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapMessages.java32
-rw-r--r--org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapMessages.properties15
-rw-r--r--org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapPage.java58
-rw-r--r--org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapView.java79
-rw-r--r--org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapWidget.java461
-rw-r--r--org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MultiPageMinimapPage.java93
10 files changed, 754 insertions, 0 deletions
diff --git a/org.eclipse.ui.workbench.texteditor/META-INF/MANIFEST.MF b/org.eclipse.ui.workbench.texteditor/META-INF/MANIFEST.MF
index 18a9f968e..d31331309 100644
--- a/org.eclipse.ui.workbench.texteditor/META-INF/MANIFEST.MF
+++ b/org.eclipse.ui.workbench.texteditor/META-INF/MANIFEST.MF
@@ -15,6 +15,7 @@ Export-Package:
org.eclipse.ui.internal.texteditor.quickdiff.compare.equivalence;x-internal:=true,
org.eclipse.ui.internal.texteditor.rulers;x-internal:=true,
org.eclipse.ui.internal.texteditor.spelling;x-internal:=true,
+ org.eclipse.ui.internal.views.minimap;x-internal:=true,
org.eclipse.ui.texteditor; texteditor="split"; mandatory:="texteditor",
org.eclipse.ui.texteditor.link,
org.eclipse.ui.texteditor.quickdiff,
diff --git a/org.eclipse.ui.workbench.texteditor/icons/full/eview16/minimap.png b/org.eclipse.ui.workbench.texteditor/icons/full/eview16/minimap.png
new file mode 100644
index 000000000..02c4b79e1
--- /dev/null
+++ b/org.eclipse.ui.workbench.texteditor/icons/full/eview16/minimap.png
Binary files differ
diff --git a/org.eclipse.ui.workbench.texteditor/plugin.properties b/org.eclipse.ui.workbench.texteditor/plugin.properties
index 4f2184330..77a35f5b3 100644
--- a/org.eclipse.ui.workbench.texteditor/plugin.properties
+++ b/org.eclipse.ui.workbench.texteditor/plugin.properties
@@ -193,3 +193,5 @@ SpellingEngine= Spelling Engine
blockSelectionModeFont.label= Text Editor Block Selection Font
blockSelectionModeFont.description= The block selection mode font is used by text editors in block (column) mode. A monospace font should be used.
+
+MinimapView.name=Minimap \ No newline at end of file
diff --git a/org.eclipse.ui.workbench.texteditor/plugin.xml b/org.eclipse.ui.workbench.texteditor/plugin.xml
index d51f0cc2d..44ed2d60f 100644
--- a/org.eclipse.ui.workbench.texteditor/plugin.xml
+++ b/org.eclipse.ui.workbench.texteditor/plugin.xml
@@ -1338,4 +1338,17 @@
</command>
</menuContribution>
</extension>
+
+ <extension
+ point="org.eclipse.ui.views">
+ <view
+ category="org.eclipse.ui"
+ class="org.eclipse.ui.internal.views.minimap.MinimapView"
+ icon="icons/full/eview16/minimap.png"
+ id="org.eclipse.ui.views.minimap.MinimapView"
+ name="%MinimapView.name"
+ allowMultiple="false"
+ restorable="true">
+ </view>
+ </extension>
</plugin>
diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapMessages.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapMessages.java
new file mode 100644
index 000000000..bee839256
--- /dev/null
+++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapMessages.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Angelo ZERR.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Angelo Zerr <angelo.zerr@gmail.com> - [minimap] Initialize minimap view - Bug 535450
+ *******************************************************************************/
+package org.eclipse.ui.internal.views.minimap;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * MinimapMessages is the message class for the messages used in the minimap.
+ *
+ */
+public class MinimapMessages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.ui.internal.views.minimap.MinimapMessages";//$NON-NLS-1$
+
+ // ==============================================================================
+ // Minimap View
+ // ==============================================================================
+ /** */
+ public static String MinimapViewNoMinimap;
+
+ static {
+ // load message values from bundle file
+ NLS.initializeMessages(BUNDLE_NAME, MinimapMessages.class);
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapMessages.properties b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapMessages.properties
new file mode 100644
index 000000000..ef07d1d34
--- /dev/null
+++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapMessages.properties
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2018 Angelo ZERR.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Angelo Zerr <angelo.zerr@gmail.com> - [minimap] Initialize minimap view - Bug 535450
+###############################################################################
+
+# ==============================================================================
+# Minimap View
+# ==============================================================================
+MinimapViewNoMinimap=A minimap is not available. \ No newline at end of file
diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapPage.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapPage.java
new file mode 100644
index 000000000..68a7a193d
--- /dev/null
+++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapPage.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Angelo ZERR.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Angelo Zerr <angelo.zerr@gmail.com> - [minimap] Initialize minimap view - Bug 535450
+ *******************************************************************************/
+package org.eclipse.ui.internal.views.minimap;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+import org.eclipse.jface.text.ITextOperationTarget;
+import org.eclipse.jface.text.source.ISourceViewer;
+
+import org.eclipse.ui.part.Page;
+
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * Minimap page which displays scaled content of the given text editor.
+ *
+ */
+public class MinimapPage extends Page {
+
+ private final ISourceViewer fEditorViewer;
+ private MinimapWidget fMinimapWidget;
+
+ public MinimapPage(ITextEditor textEditor) {
+ fEditorViewer = (ISourceViewer) textEditor.getAdapter(ITextOperationTarget.class);
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ // Create minimap styled text
+ fMinimapWidget = new MinimapWidget(parent, fEditorViewer);
+ fMinimapWidget.install();
+ }
+
+ @Override
+ public Control getControl() {
+ return fMinimapWidget.getControl();
+ }
+
+ @Override
+ public void setFocus() {
+ fMinimapWidget.getControl().setFocus();
+ }
+
+ @Override
+ public void dispose() {
+ fMinimapWidget.uninstall();
+ super.dispose();
+ }
+}
diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapView.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapView.java
new file mode 100644
index 000000000..4b1ebe54a
--- /dev/null
+++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapView.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Angelo ZERR.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Angelo Zerr <angelo.zerr@gmail.com> - [minimap] Initialize minimap view - Bug 535450
+ *******************************************************************************/
+package org.eclipse.ui.internal.views.minimap;
+
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.part.IPage;
+import org.eclipse.ui.part.MessagePage;
+import org.eclipse.ui.part.MultiPageEditorPart;
+import org.eclipse.ui.part.Page;
+import org.eclipse.ui.part.PageBook;
+import org.eclipse.ui.part.PageBookView;
+import org.eclipse.ui.part.PageSite;
+
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * Minimap view used to display content of current text editor with scale.
+ *
+ */
+public class MinimapView extends PageBookView {
+
+ private String defaultText;
+
+ public MinimapView() {
+ this.defaultText = MinimapMessages.MinimapViewNoMinimap;
+ }
+
+ @Override
+ protected IPage createDefaultPage(PageBook book) {
+ MessagePage page = new MessagePage();
+ this.initPage(page);
+ page.createControl(book);
+ page.setMessage(this.defaultText);
+ return page;
+ }
+
+ @Override
+ protected PageRec doCreatePage(IWorkbenchPart part) {
+ Page page = createMinimapPage(part);
+ PageSite site = new PageSite(this.getViewSite());
+ page.init(site);
+ page.createControl(this.getPageBook());
+ return new PageBookView.PageRec(part, page);
+ }
+
+ private Page createMinimapPage(IWorkbenchPart part) {
+ if (part instanceof MultiPageEditorPart) {
+ return new MultiPageMinimapPage((MultiPageEditorPart) part);
+ }
+ return new MinimapPage((ITextEditor) part);
+ }
+
+ @Override
+ protected void doDestroyPage(IWorkbenchPart part, PageRec rec) {
+ IPage page = rec.page;
+ page.dispose();
+ rec.dispose();
+ }
+
+ @Override
+ protected IWorkbenchPart getBootstrapPart() {
+ IWorkbenchPage page = this.getSite().getPage();
+ return page != null ? page.getActiveEditor() : null;
+ }
+
+ @Override
+ protected boolean isImportant(IWorkbenchPart part) {
+ return part instanceof ITextEditor || part instanceof MultiPageEditorPart;
+ }
+}
diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapWidget.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapWidget.java
new file mode 100644
index 000000000..2fd40a493
--- /dev/null
+++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MinimapWidget.java
@@ -0,0 +1,461 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Angelo ZERR.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Angelo Zerr <angelo.zerr@gmail.com> - [minimap] Initialize minimap view - Bug 535450
+ *******************************************************************************/
+package org.eclipse.ui.internal.views.minimap;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CaretEvent;
+import org.eclipse.swt.custom.CaretListener;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.custom.TextChangeListener;
+import org.eclipse.swt.custom.TextChangedEvent;
+import org.eclipse.swt.custom.TextChangingEvent;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseWheelListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextPresentationListener;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension4;
+import org.eclipse.jface.text.ITextViewerExtension5;
+import org.eclipse.jface.text.IViewportListener;
+import org.eclipse.jface.text.JFaceTextUtil;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.jface.text.TextViewer;
+
+/**
+ * Minimap widget which displays scaled content of the given text editor.
+ *
+ */
+public class MinimapWidget {
+
+ private static final float SCALE = 0.2f;
+
+ private final ITextViewer fEditorViewer;
+
+ private final StyledText fMinimapTextWidget;
+
+ /**
+ * Editor tracker used to track text changed and styles changes of the
+ * editor content.
+ */
+ class EditorTracker implements TextChangeListener, ControlListener, ITextPresentationListener, IViewportListener {
+
+ private Map<Font, Font> fScaledFonts;
+
+ private static final int RESETED = -1;
+
+ private int scaledClientAreaHeight = RESETED;
+
+ @Override
+ public void textSet(TextChangedEvent event) {
+ synchText();
+ }
+
+ @Override
+ public void textChanging(TextChangingEvent event) {
+ fMinimapTracker.replaceTextRange(event);
+ }
+
+ @Override
+ public void textChanged(TextChangedEvent event) {
+ // Do nothing
+ }
+
+ @Override
+ public void applyTextPresentation(TextPresentation presentation) {
+ addPresentation(presentation);
+ }
+
+ private StyleRange modelStyleRange2WidgetStyleRange(StyleRange range) {
+ IRegion region = modelRange2WidgetRange(new Region(range.start, range.length));
+ if (region != null) {
+ StyleRange result = (StyleRange) range.clone();
+ result.start = region.getOffset();
+ result.length = region.getLength();
+ return result;
+ }
+ return null;
+ }
+
+ private IRegion modelRange2WidgetRange(IRegion region) {
+ if (fEditorViewer instanceof ITextViewerExtension5) {
+ ITextViewerExtension5 extension = (ITextViewerExtension5) fEditorViewer;
+ return extension.modelRange2WidgetRange(region);
+ }
+ if (fEditorViewer instanceof TextViewer) {
+ return ((TextViewer) fEditorViewer).modelRange2WidgetRange(region);
+ }
+ IRegion visibleRegion = fEditorViewer.getVisibleRegion();
+ int start = region.getOffset() - visibleRegion.getOffset();
+ int end = start + region.getLength();
+ if (end > visibleRegion.getLength())
+ end = visibleRegion.getLength();
+
+ return new Region(start, end - start);
+ }
+
+ private void addPresentation(TextPresentation presentation) {
+
+ StyleRange range = presentation.getDefaultStyleRange();
+ if (range != null) {
+
+ range = modelStyleRange2WidgetStyleRange(range);
+ if (range != null) {
+ updateStyle(range);
+ fMinimapTextWidget.setStyleRange(range);
+ }
+ List<StyleRange> ranges = new ArrayList<>(presentation.getDenumerableRanges());
+ Iterator<StyleRange> e = presentation.getNonDefaultStyleRangeIterator();
+ while (e.hasNext()) {
+ range = e.next();
+ range = modelStyleRange2WidgetStyleRange(range);
+ if (range != null) {
+ updateStyle(range);
+ ranges.add(range);
+ }
+ }
+
+ if (!ranges.isEmpty()) {
+ fMinimapTextWidget.replaceStyleRanges(0, 0, ranges.toArray(new StyleRange[ranges.size()]));
+ }
+
+ } else {
+ IRegion region = modelRange2WidgetRange(presentation.getCoverage());
+ if (region == null)
+ return;
+
+ List<StyleRange> list = new ArrayList<>(presentation.getDenumerableRanges());
+ Iterator<StyleRange> e = presentation.getAllStyleRangeIterator();
+ while (e.hasNext()) {
+ range = e.next();
+ range = modelStyleRange2WidgetStyleRange(range);
+ if (range != null) {
+ updateStyle(range);
+ list.add(range);
+ }
+ }
+
+ if (!list.isEmpty()) {
+ StyleRange[] ranges = new StyleRange[list.size()];
+ list.toArray(ranges);
+ fMinimapTextWidget.replaceStyleRanges(region.getOffset(), region.getLength(), ranges);
+ }
+ }
+ }
+
+ void updateStyle(StyleRange range) {
+ // scale the font
+ range.font = getScaledFont(range.font);
+ // remove GlyphMetrics (created by code mining)
+ range.metrics = null;
+ }
+
+ Font getScaledFont(Font editorFont) {
+ if (editorFont == null) {
+ return null;
+ }
+ Font scaledFont = fScaledFonts.get(editorFont);
+ if (scaledFont != null) {
+ return scaledFont;
+ }
+ FontData[] fontData = editorFont.getFontData();
+ FontData fontDatum = fontData[0];
+
+ scaledFont = new Font(editorFont.getDevice(), fontDatum.getName(),
+ Math.round(fontDatum.getHeight() * getScale()), fontDatum.getStyle());
+ fScaledFonts.put(editorFont, scaledFont);
+ return scaledFont;
+ }
+
+ @Override
+ public void controlMoved(ControlEvent e) {
+ // Do nothing
+ }
+
+ @Override
+ public void controlResized(ControlEvent e) {
+ scaledClientAreaHeight = RESETED;
+ updateMinimap();
+ }
+
+ @Override
+ public void viewportChanged(int verticalOffset) {
+ updateMinimap();
+ }
+
+ void updateMinimap() {
+ StyledText editorTextWidget = fEditorViewer.getTextWidget();
+ int editorTopIndex = JFaceTextUtil.getPartialTopIndex(editorTextWidget);
+ int editorBottomIndex = JFaceTextUtil.getPartialBottomIndex(editorTextWidget);
+ fMinimapTracker.updateMinimap(editorTopIndex, editorBottomIndex);
+ }
+
+ int getScaledClientAreaHeight() {
+ if (scaledClientAreaHeight == RESETED) {
+ scaledClientAreaHeight = Math.round(fEditorViewer.getTextWidget().getClientArea().height * getScale());
+ }
+ return scaledClientAreaHeight;
+ }
+
+ void install() {
+ StyledText editorTextWidget = fEditorViewer.getTextWidget();
+ fScaledFonts = new HashMap<>();
+ // Compute scaled font
+ Font scaledFont = getScaledFont(editorTextWidget.getFont());
+ fMinimapTextWidget.setFont(scaledFont);
+ // track changed content of styled text of the editor
+ editorTextWidget.getContent().addTextChangeListener(this);
+ // track changed styles of styled text of the editor
+ fMinimapTextWidget.setBackground(editorTextWidget.getBackground());
+ fMinimapTextWidget.setForeground(editorTextWidget.getForeground());
+ if (fEditorViewer instanceof ITextViewerExtension4) {
+ ((ITextViewerExtension4) fEditorViewer).addTextPresentationListener(this);
+ }
+ // track changed of vertical bar scroll to update highlight
+ // Viewport.
+ fEditorViewer.addViewportListener(this);
+ editorTextWidget.addControlListener(this);
+ synchTextAndStyles();
+ }
+
+ void synchTextAndStyles() {
+ synchText();
+ synchStyles();
+ }
+
+ private void synchStyles() {
+ StyledText editorTextWidget = fEditorViewer.getTextWidget();
+ StyleRange[] ranges = editorTextWidget.getStyleRanges();
+ if (ranges != null) {
+ for (StyleRange range : ranges) {
+ updateStyle(range);
+ }
+ }
+ fMinimapTextWidget.setStyleRanges(ranges);
+ }
+
+ private void synchText() {
+ StyledText editorTextWidget = fEditorViewer.getTextWidget();
+ fMinimapTextWidget.setText(editorTextWidget.getText());
+ }
+
+ void uninstall() {
+ StyledText editorTextWidget = fEditorViewer.getTextWidget();
+ // untrack changed content of styled text of the editor
+ if (editorTextWidget.getContent() != null) {
+ editorTextWidget.getContent().removeTextChangeListener(this);
+ }
+ // untrack changed styles of styled text of the editor
+ if (fEditorViewer instanceof ITextViewerExtension4) {
+ ((ITextViewerExtension4) fEditorViewer).removeTextPresentationListener(this);
+ }
+ // track changed of vertical bar scroll to update highlight
+ // Viewport.
+ fEditorViewer.removeViewportListener(this);
+ editorTextWidget.removeControlListener(this);
+ fScaledFonts.values().forEach(Font::dispose);
+ }
+ }
+
+ /**
+ * Minimap tracker.
+ *
+ */
+ class MinimapTracker implements CaretListener, SelectionListener, PaintListener, MouseWheelListener {
+
+ private static final int NB_LINES_SCROLL = 10;
+
+ private int fEditorTopIndex;
+
+ private int fTopIndexY;
+
+ private boolean fTextChanging;
+
+ @Override
+ public void caretMoved(CaretEvent event) {
+ if (fTextChanging) {
+ // When editor content changed, it updates the minimap styled
+ // text content which update caret.
+ // In this case, ignore it to avoid set the top index of the
+ // editor viewer.
+ return;
+ }
+ updateEditorTopIndex();
+ }
+
+ private void updateEditorTopIndex() {
+ int caretOffset = fMinimapTextWidget.getCaretOffset();
+ int lineAtOffset = fMinimapTextWidget.getLineAtOffset(caretOffset);
+ int newTopIndex = lineAtOffset;
+ if (fEditorViewer instanceof ITextViewerExtension5) {
+ // adjust offset according folded content
+ newTopIndex = ((ITextViewerExtension5) fEditorViewer).widgetLine2ModelLine(lineAtOffset);
+ }
+ fEditorViewer.setTopIndex(newTopIndex);
+ }
+
+ public void updateMinimap(int editorTopIndex, int editorBottomIndex) {
+ if (editorTopIndex != fEditorTopIndex) {
+ fEditorTopIndex = editorTopIndex;
+ // Update the position of minimap styled text
+ fMinimapTextWidget.setRedraw(false);
+ int newMinimapTopIndex = editorTopIndex;
+ if (editorTopIndex != 0
+ && editorBottomIndex != fMinimapTextWidget.getLineAtOffset(fMinimapTextWidget.getCharCount())) {
+ // center the draw of square of editor client area
+ int minimapTopIndex = JFaceTextUtil.getPartialTopIndex(fMinimapTextWidget);
+ int minimapBottomIndex = JFaceTextUtil.getPartialBottomIndex(fMinimapTextWidget);
+ int minimapVisibleLineCount = minimapBottomIndex - minimapTopIndex;
+ int editorVisibleLineCount = editorBottomIndex - editorTopIndex;
+ newMinimapTopIndex = Math.max(0,
+ editorTopIndex + editorVisibleLineCount - minimapVisibleLineCount / 2);
+ }
+ fMinimapTextWidget.setTopIndex(newMinimapTopIndex);
+ fTopIndexY = fMinimapTextWidget.getLinePixel(fEditorTopIndex);
+ fMinimapTextWidget.setRedraw(true);
+ }
+ }
+
+ public void replaceTextRange(TextChangingEvent event) {
+ fTextChanging = true;
+ int start = event.start;
+ int length = event.replaceCharCount;
+ String text = event.newText;
+ if (event.newLineCount > 0 || event.replaceLineCount > 0) {
+ Rectangle clientArea = fMinimapTextWidget.getClientArea();
+ fMinimapTextWidget.setRedraw(false);
+ fMinimapTextWidget.replaceTextRange(start, length, text);
+ fMinimapTextWidget.redraw(0, fTopIndexY, clientArea.width, clientArea.height, false);
+ fMinimapTextWidget.setRedraw(true);
+ } else {
+ fMinimapTextWidget.replaceTextRange(start, length, text);
+ }
+ fTextChanging = false;
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ // Do nothing
+ }
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ // hack to avoid drawing selection of styled text while mouse move
+ // of highlight viewport
+ fMinimapTextWidget.setSelection(fMinimapTextWidget.getCaretOffset());
+ }
+
+ @Override
+ public void paintControl(PaintEvent event) {
+ GC gc = event.gc;
+ int clientAreaHeight = fEditorTracker.getScaledClientAreaHeight();
+ int clientAreaWidth = fMinimapTextWidget.getClientArea().width;
+ gc.setBackground(fMinimapTextWidget.getSelectionBackground());
+ Rectangle rect = new Rectangle(0, fTopIndexY, clientAreaWidth, clientAreaHeight);
+ gc.drawRectangle(rect.x, rect.y, Math.max(1, rect.width - 1), Math.max(1, rect.height - 1));
+ gc.setAdvanced(true);
+ if (gc.getAdvanced()) {
+ gc.setAlpha(20);
+ gc.fillRectangle(rect);
+ gc.setAdvanced(false);
+ }
+ }
+
+ @Override
+ public void mouseScrolled(MouseEvent e) {
+ int caretOffset = fMinimapTextWidget.getOffsetAtPoint(new Point(0, fTopIndexY));
+ int lineIndex = fMinimapTextWidget.getLineAtOffset(caretOffset);
+ if (e.count > 0) {
+ lineIndex = Math.max(0, lineIndex - NB_LINES_SCROLL);
+ } else {
+ lineIndex = Math.min(fMinimapTextWidget.getCharCount(), lineIndex + NB_LINES_SCROLL);
+ }
+ caretOffset = fMinimapTextWidget.getOffsetAtLine(lineIndex);
+ fMinimapTextWidget.setCaretOffset(caretOffset);
+ }
+
+ void install() {
+ fMinimapTextWidget.addCaretListener(this);
+ fMinimapTextWidget.addSelectionListener(this);
+ fMinimapTextWidget.addPaintListener(this);
+ fMinimapTextWidget.addMouseWheelListener(this);
+ }
+
+ void uninstall() {
+ if (!fMinimapTextWidget.isDisposed()) {
+ fMinimapTextWidget.removeCaretListener(this);
+ fMinimapTextWidget.removeSelectionListener(this);
+ fMinimapTextWidget.removePaintListener(this);
+ fMinimapTextWidget.removeMouseWheelListener(this);
+ }
+ }
+
+ }
+
+ private final EditorTracker fEditorTracker;
+
+ private final MinimapTracker fMinimapTracker;
+
+ public MinimapWidget(Composite parent, ITextViewer viewer) {
+ fEditorViewer = viewer;
+
+ // Create minimap styled text
+ fMinimapTextWidget = new StyledText(parent, SWT.MULTI | SWT.READ_ONLY);
+ fMinimapTextWidget.setEditable(false);
+ fMinimapTextWidget.setCursor(fMinimapTextWidget.getDisplay().getSystemCursor(SWT.CURSOR_ARROW));
+
+ // Initialize trackers
+ fEditorTracker = new EditorTracker();
+ fMinimapTracker = new MinimapTracker();
+ }
+
+ /**
+ * Install minimap widget.
+ */
+ public void install() {
+ fEditorTracker.install();
+ fMinimapTracker.install();
+ }
+
+ public void uninstall() {
+ fEditorTracker.uninstall();
+ fMinimapTracker.uninstall();
+ }
+
+ public Control getControl() {
+ return fMinimapTextWidget;
+ }
+
+ float getScale() {
+ return SCALE;
+ }
+}
diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MultiPageMinimapPage.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MultiPageMinimapPage.java
new file mode 100644
index 000000000..6fc5febc3
--- /dev/null
+++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/views/minimap/MultiPageMinimapPage.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Angelo ZERR.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Angelo Zerr <angelo.zerr@gmail.com> - [minimap] Initialize minimap view - Bug 535450
+ *******************************************************************************/
+package org.eclipse.ui.internal.views.minimap;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+
+import org.eclipse.jface.dialogs.IPageChangedListener;
+
+import org.eclipse.ui.part.MultiPageEditorPart;
+import org.eclipse.ui.part.Page;
+import org.eclipse.ui.part.PageBook;
+
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * Minimap with multi page editor which shows a minimap for the current page
+ * which is an {@link ITextEditor}.
+ *
+ */
+public class MultiPageMinimapPage extends Page {
+
+ private final MultiPageEditorPart fMultiPageEditor;
+ private final Map<Object, Control> fTextWidgetMap;
+ private final IPageChangedListener fPageChangedListener;
+ private PageBook fPageBook;
+ private Label fErrorLabel;
+
+ public MultiPageMinimapPage(MultiPageEditorPart multiPageEditor) {
+ this.fMultiPageEditor = multiPageEditor;
+ this.fTextWidgetMap = new HashMap<>();
+ this.fPageChangedListener = e -> {
+ Object selectedPage = multiPageEditor.getSelectedPage();
+ // Find from cache the minimap for the selected page
+ Control textWidget = fTextWidgetMap.get(selectedPage);
+ if (textWidget != null) {
+ fPageBook.showPage(textWidget);
+ return;
+ }
+
+ if (selectedPage instanceof ITextEditor) {
+ // Create and show a minimap page for the given text editor page
+ ITextEditor textEditor = (ITextEditor) selectedPage;
+ MinimapPage minimapPage = new MinimapPage(textEditor);
+ minimapPage.createControl(fPageBook);
+ textWidget = minimapPage.getControl();
+ fTextWidgetMap.put(selectedPage, textWidget);
+ fPageBook.showPage(textWidget);
+ } else {
+ fTextWidgetMap.put(selectedPage, fErrorLabel);
+ fPageBook.showPage(fErrorLabel);
+ }
+ };
+ multiPageEditor.addPageChangedListener(fPageChangedListener);
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ fPageBook = new PageBook(parent, SWT.NORMAL);
+ fErrorLabel = new Label(fPageBook, SWT.NORMAL);
+ fErrorLabel.setText(MinimapMessages.MinimapViewNoMinimap);
+ fPageChangedListener.pageChanged(null);
+ }
+
+ @Override
+ public Control getControl() {
+ return fPageBook;
+ }
+
+ @Override
+ public void setFocus() {
+ fPageBook.setFocus();
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ fMultiPageEditor.removePageChangedListener(fPageChangedListener);
+ }
+}