blob: e4e8659b0321f419ccb9d0e0e8a5cf82a5a71340 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2004 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);
}
}