/******************************************************************************* * Copyright (c) 2001, 2006 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation * Jens Lukowski/Innoopract - initial renaming/restructuring * *******************************************************************************/ package org.eclipse.wst.sse.ui.internal.openon; import com.ibm.icu.util.StringTokenizer; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.PreferenceConverter; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextInputListener; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.ITextViewerExtension5; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.Region; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.wst.sse.ui.internal.Logger; import org.eclipse.wst.sse.ui.internal.util.EditorUtility; /** * @deprecated Use org.eclipse.jface.text.hyperlink.HyperlinkManager */ public class OpenFileHyperlinkTracker implements KeyListener, MouseListener, MouseMoveListener, FocusListener, PaintListener, IPropertyChangeListener, IDocumentListener, ITextInputListener { /** The session is active. */ private boolean fActive; /** The currently active style range. */ private IRegion fActiveRegion; /** Preference key for browser-like links to be enabled */ private String fBrowserLikeLinksKeyModifierKey; /** The link color. */ private Color fColor; /** The hand cursor. */ private Cursor fCursor; /** The key modifier mask. */ private int fKeyModifierMask; /** Preference key for hyperlink underline color */ private String fLinkColorKey; /** The preference store */ private IPreferenceStore fPreferenceStore; /** The currently active style range as position. */ private Position fRememberedPosition; /** The text viewer this hyperlink tracker is associated with */ private ITextViewer fTextViewer; /** * */ public OpenFileHyperlinkTracker(ITextViewer textViewer) { fTextViewer = textViewer; } private void activateCursor(ITextViewer viewer) { StyledText text = viewer.getTextWidget(); if (text == null || text.isDisposed()) return; Display display = text.getDisplay(); if (fCursor == null) fCursor = new Cursor(display, SWT.CURSOR_HAND); text.setCursor(fCursor); } private int computeStateMask(String modifiers) { if (modifiers == null) return -1; if (modifiers.length() == 0) return SWT.NONE; int stateMask = 0; StringTokenizer modifierTokenizer = new StringTokenizer(modifiers, ",;.:+-* "); //$NON-NLS-1$ while (modifierTokenizer.hasMoreTokens()) { int modifier = EditorUtility.findLocalizedModifier(modifierTokenizer.nextToken()); if (modifier == 0 || (stateMask & modifier) == modifier) return -1; stateMask = stateMask | modifier; } return stateMask; } /** * Creates a color from the information stored in the given preference * store. Returns <code>null</code> if there is no such information * available. */ private Color createColor(IPreferenceStore store, String key, Display display) { RGB rgb = null; if (store.contains(key)) { if (store.isDefault(key)) rgb = PreferenceConverter.getDefaultColor(store, key); else rgb = PreferenceConverter.getColor(store, key); } return EditorUtility.getColor(rgb); } public void deactivate() { deactivate(false); } public void deactivate(boolean redrawAll) { if (!fActive) return; repairRepresentation(redrawAll); fActive = false; } /* * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent) */ public void documentAboutToBeChanged(DocumentEvent event) { if (fActive && fActiveRegion != null) { fRememberedPosition = new Position(fActiveRegion.getOffset(), fActiveRegion.getLength()); try { event.getDocument().addPosition(fRememberedPosition); } catch (BadLocationException x) { fRememberedPosition = null; } } } /* * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent) */ public void documentChanged(DocumentEvent event) { if (fRememberedPosition != null) { if (!fRememberedPosition.isDeleted()) { event.getDocument().removePosition(fRememberedPosition); fActiveRegion = new Region(fRememberedPosition.getOffset(), fRememberedPosition.getLength()); fRememberedPosition = null; ITextViewer viewer = getTextViewer(); if (viewer != null) { StyledText widget = viewer.getTextWidget(); if (widget != null && !widget.isDisposed()) { widget.getDisplay().asyncExec(new Runnable() { public void run() { deactivate(); } }); } } } else { fActiveRegion = null; fRememberedPosition = null; deactivate(); } } } /* * @see org.eclipse.swt.events.FocusListener#focusGained(org.eclipse.swt.events.FocusEvent) */ public void focusGained(FocusEvent e) { } /* * @see org.eclipse.swt.events.FocusListener#focusLost(org.eclipse.swt.events.FocusEvent) */ public void focusLost(FocusEvent event) { deactivate(); } private int getCurrentTextOffset() { try { StyledText text = getTextViewer().getTextWidget(); if (text == null || text.isDisposed()) return -1; Display display = text.getDisplay(); Point absolutePosition = display.getCursorLocation(); Point relativePosition = text.toControl(absolutePosition); int widgetOffset = text.getOffsetAtLocation(relativePosition); if (getTextViewer() instanceof ITextViewerExtension5) { ITextViewerExtension5 extension = (ITextViewerExtension5) getTextViewer(); return extension.widgetOffset2ModelOffset(widgetOffset); } else { return widgetOffset + getTextViewer().getVisibleRegion().getOffset(); } } catch (IllegalArgumentException e) { return -1; } } private Point getMaximumLocation(StyledText text, int offset, int length) { Point maxLocation = new Point(Integer.MIN_VALUE, Integer.MIN_VALUE); for (int i = 0; i <= length; i++) { Point location = text.getLocationAtOffset(offset + i); if (location.x > maxLocation.x) maxLocation.x = location.x; if (location.y > maxLocation.y) maxLocation.y = location.y; } return maxLocation; } private Point getMinimumLocation(StyledText text, int offset, int length) { Point minLocation = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); for (int i = 0; i <= length; i++) { Point location = text.getLocationAtOffset(offset + i); if (location.x < minLocation.x) minLocation.x = location.x; if (location.y < minLocation.y) minLocation.y = location.y; } return minLocation; } private IPreferenceStore getNewPreferenceStore() { return fPreferenceStore; } private ITextViewer getTextViewer() { return fTextViewer; } private void highlightRegion(ITextViewer viewer, IRegion region) { if (region.equals(fActiveRegion)) return; repairRepresentation(); StyledText text = viewer.getTextWidget(); if (text == null || text.isDisposed()) return; // Underline int offset = 0; int length = 0; if (viewer instanceof ITextViewerExtension5) { ITextViewerExtension5 extension = (ITextViewerExtension5) viewer; IRegion widgetRange = extension.modelRange2WidgetRange(new Region(region.getOffset(), region.getLength())); if (widgetRange == null) return; offset = widgetRange.getOffset(); length = widgetRange.getLength(); } else { offset = region.getOffset() - viewer.getVisibleRegion().getOffset(); length = region.getLength(); } // need clearBackground to be true for paint event to be fired text.redrawRange(offset, length, true); fActiveRegion = region; } private boolean includes(IRegion region, IRegion position) { return position.getOffset() >= region.getOffset() && position.getOffset() + position.getLength() <= region.getOffset() + region.getLength(); } /* * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, * org.eclipse.jface.text.IDocument) */ public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { if (oldInput == null) return; deactivate(); oldInput.removeDocumentListener(this); } /* * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, * org.eclipse.jface.text.IDocument) */ public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { if (newInput == null) return; newInput.addDocumentListener(this); } public void install(IPreferenceStore store) { fPreferenceStore = store; ITextViewer textViewer = getTextViewer(); if (textViewer == null) return; StyledText text = textViewer.getTextWidget(); if (text == null || text.isDisposed()) return; updateColor(textViewer); textViewer.addTextInputListener(this); IDocument document = textViewer.getDocument(); if (document != null) document.addDocumentListener(this); text.addKeyListener(this); text.addMouseListener(this); text.addMouseMoveListener(this); text.addFocusListener(this); text.addPaintListener(this); updateKeyModifierMask(); fPreferenceStore.addPropertyChangeListener(this); } /* * @see org.eclipse.swt.events.KeyListener#keyPressed(org.eclipse.swt.events.KeyEvent) */ public void keyPressed(KeyEvent event) { if (fActive) { deactivate(); return; } if (event.keyCode != fKeyModifierMask) { deactivate(); return; } fActive = true; // removed for #25871 // // ISourceViewer viewer= getSourceViewer(); // if (viewer == null) // return; // // IRegion region= getCurrentTextRegion(viewer); // if (region == null) // return; // // highlightRegion(viewer, region); // activateCursor(viewer); } /* * @see org.eclipse.swt.events.KeyListener#keyReleased(org.eclipse.swt.events.KeyEvent) */ public void keyReleased(KeyEvent event) { if (!fActive) return; deactivate(); } /* * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent) */ public void mouseDoubleClick(MouseEvent e) { } /* * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent) */ public void mouseDown(MouseEvent event) { if (!fActive) return; if (event.stateMask != fKeyModifierMask) { deactivate(); return; } if (event.button != 1) { deactivate(); return; } } /* * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent) */ public void mouseMove(MouseEvent event) { if (event.widget instanceof Control && !((Control) event.widget).isFocusControl()) { deactivate(); return; } if (!fActive) { if (event.stateMask != fKeyModifierMask) return; // modifier was already pressed fActive = true; } ITextViewer viewer = getTextViewer(); if (viewer == null) { deactivate(); return; } StyledText text = viewer.getTextWidget(); if (text == null || text.isDisposed()) { deactivate(); return; } if ((event.stateMask & SWT.BUTTON1) != 0 && text.getSelectionCount() != 0) { deactivate(); return; } IRegion region = null; int offset = getCurrentTextOffset(); IOpenOn openOn = OpenOnProvider.getInstance().getOpenOn(getTextViewer().getDocument(), offset); if (openOn != null) { region = openOn.getOpenOnRegion(getTextViewer().getDocument(), offset); } if (region == null || region.getLength() == 0) { repairRepresentation(); return; } highlightRegion(viewer, region); activateCursor(viewer); } /* * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent) */ public void mouseUp(MouseEvent e) { if (!fActive) return; if (e.button != 1) { deactivate(); return; } boolean wasActive = fCursor != null; IRegion previousRegion = fActiveRegion; deactivate(); if (wasActive) { IOpenOn openOn = OpenOnProvider.getInstance().getOpenOn(getTextViewer().getDocument(), previousRegion.getOffset()); if (openOn != null) { openOn.openOn(getTextViewer().getDocument(), previousRegion); } } } /* * @see PaintListener#paintControl(PaintEvent) */ public void paintControl(PaintEvent event) { if (fActiveRegion == null) return; ITextViewer viewer = getTextViewer(); if (viewer == null) return; StyledText text = viewer.getTextWidget(); if (text == null || text.isDisposed()) return; int offset = 0; int length = 0; if (viewer instanceof ITextViewerExtension5) { ITextViewerExtension5 extension = (ITextViewerExtension5) viewer; IRegion widgetRange = extension.modelRange2WidgetRange(fActiveRegion); if (widgetRange == null) return; offset = widgetRange.getOffset(); length = widgetRange.getLength(); } else { IRegion region = viewer.getVisibleRegion(); if (!includes(region, fActiveRegion)) return; offset = fActiveRegion.getOffset() - region.getOffset(); length = fActiveRegion.getLength(); } // support for bidi Point minLocation = getMinimumLocation(text, offset, length); Point maxLocation = getMaximumLocation(text, offset, length); int x1 = minLocation.x; int x2 = minLocation.x + maxLocation.x - minLocation.x - 1; int y = minLocation.y + text.getLineHeight() - 1; GC gc = event.gc; if (fColor != null && !fColor.isDisposed()) gc.setForeground(fColor); gc.drawLine(x1, y, x2, y); } /* * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event) { if (event.getProperty().equals(fLinkColorKey)) { ITextViewer viewer = getTextViewer(); if (viewer != null) updateColor(viewer); } else if (event.getProperty().equals(fBrowserLikeLinksKeyModifierKey)) { updateKeyModifierMask(); } } private void repairRepresentation() { repairRepresentation(false); } private void repairRepresentation(boolean redrawAll) { if (fActiveRegion == null) return; int offset = fActiveRegion.getOffset(); int length = fActiveRegion.getLength(); fActiveRegion = null; ITextViewer viewer = getTextViewer(); if (viewer != null) { resetCursor(viewer); // Remove underline if (viewer instanceof ITextViewerExtension5) { ITextViewerExtension5 extension = (ITextViewerExtension5) viewer; offset = extension.modelOffset2WidgetOffset(offset); } else { offset -= viewer.getVisibleRegion().getOffset(); } try { StyledText text = viewer.getTextWidget(); // need clearBackground to be true for paint event to be fired text.redrawRange(offset, length, true); } catch (IllegalArgumentException x) { Logger.logException(x); } } } private void resetCursor(ITextViewer viewer) { StyledText text = viewer.getTextWidget(); if (text != null && !text.isDisposed()) text.setCursor(null); if (fCursor != null) { fCursor.dispose(); fCursor = null; } } public void setHyperlinkPreferenceKeys(String linkColorKey, String browserLikeLinksKeyModifierKey) { fLinkColorKey = linkColorKey; fBrowserLikeLinksKeyModifierKey = browserLikeLinksKeyModifierKey; } public void uninstall() { if (fCursor != null) { fCursor.dispose(); fCursor = null; } ITextViewer textViewer = getTextViewer(); if (textViewer == null) return; textViewer.removeTextInputListener(this); IDocument document = textViewer.getDocument(); if (document != null) document.removeDocumentListener(this); IPreferenceStore preferenceStore = getNewPreferenceStore(); if (preferenceStore != null) preferenceStore.removePropertyChangeListener(this); StyledText text = textViewer.getTextWidget(); if (text == null || text.isDisposed()) return; text.removeKeyListener(this); text.removeMouseListener(this); text.removeMouseMoveListener(this); text.removeFocusListener(this); text.removePaintListener(this); } private void updateColor(ITextViewer viewer) { StyledText text = viewer.getTextWidget(); if (text == null || text.isDisposed()) return; Display display = text.getDisplay(); fColor = createColor(getNewPreferenceStore(), fLinkColorKey, display); } private void updateKeyModifierMask() { String modifiers = getNewPreferenceStore().getString(fBrowserLikeLinksKeyModifierKey); fKeyModifierMask = computeStateMask(modifiers); } }