/*
* Copyright (C) 2006 SQL Explorer Development Team
* http://sourceforge.net/projects/eclipsesql
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package net.sourceforge.sqlexplorer.plugin.editors;
import java.util.ArrayList;
import net.sourceforge.sqlexplorer.dbstructure.nodes.INode;
import net.sourceforge.sqlexplorer.dbstructure.nodes.TableNode;
import net.sourceforge.sqlexplorer.plugin.SQLExplorerPlugin;
import net.sourceforge.sqlexplorer.plugin.views.DatabaseStructureView;
import net.sourceforge.sqlexplorer.sessiontree.model.utility.Dictionary;
import net.sourceforge.sqlexplorer.sqleditor.SQLTextViewer;
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.ITextViewerExtension2;
import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.custom.StyleRange;
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;
class MouseClickListener implements KeyListener, MouseListener,
MouseMoveListener, FocusListener, PaintListener,
IPropertyChangeListener, IDocumentListener, ITextInputListener {
private SQLEditor editor;
@SuppressWarnings("unused")
private INode activeTableNode;
private boolean fActive;
/** The currently active style range. */
private IRegion fActiveRegion;
/** The link color. */
private Color fColor;
/** The hand cursor. */
private Cursor fCursor;
/** The key modifier mask. */
private int fKeyModifierMask = SWT.CTRL;
/** The currently active style range as position. */
private Position fRememberedPosition;
private ISourceViewer sourceViewer;
public MouseClickListener(SQLEditor editor) {
super();
this.editor = editor;
}
private void activateCursor(ISourceViewer 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);
}
public void deactivate() {
deactivate(false);
}
public void deactivate(boolean redrawAll) {
if (!fActive)
return;
repairRepresentation(redrawAll);
fActive = false;
}
/*
* (non-Javadoc)
*
* @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;
}
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
*/
public void documentChanged(DocumentEvent event) {
if (fRememberedPosition != null && !fRememberedPosition.isDeleted()) {
event.getDocument().removePosition(fRememberedPosition);
fActiveRegion = new Region(fRememberedPosition.getOffset(),
fRememberedPosition.getLength());
}
fRememberedPosition = null;
if (sourceViewer != null) {
StyledText widget = sourceViewer.getTextWidget();
if (widget != null && !widget.isDisposed()) {
widget.getDisplay().asyncExec(new Runnable() {
public void run() {
deactivate();
}
});
}
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.events.FocusListener#focusGained(org.eclipse.swt.events.FocusEvent)
*/
public void focusGained(FocusEvent e) {
}
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.events.FocusListener#focusLost(org.eclipse.swt.events.FocusEvent)
*/
public void focusLost(FocusEvent e) {
deactivate();
}
private int getCurrentTextOffset(ISourceViewer viewer) {
try {
StyledText text = viewer.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 (viewer instanceof ITextViewerExtension5) {
ITextViewerExtension5 extension = (ITextViewerExtension5) viewer;
return extension.widgetOffset2ModelOffset(widgetOffset);
} else {
return widgetOffset + viewer.getVisibleRegion().getOffset();
}
} catch (IllegalArgumentException e) {
return -1;
}
}
private IRegion getCurrentTextRegion(ISourceViewer viewer) {
if (viewer == null)
return null;
Dictionary dictionary = ((SQLTextViewer) viewer).dictionary;
if (dictionary == null)
return null;
int offset = getCurrentTextOffset(viewer);
if (offset == -1)
return null;
try {
IRegion reg = selectWord(viewer.getDocument(), offset);
if (reg == null)
return null;
String selection = viewer.getDocument().get(reg.getOffset(),
reg.getLength());
if (selection == null)
return null;
Object obj = dictionary.getByTableName(selection.toLowerCase());
if (obj == null)
return null;
else {
if (!(obj instanceof ArrayList))
return null;
ArrayList ls = (ArrayList) obj;
if (ls.isEmpty())
return null;
Object node = ((ArrayList) obj).get(0);
if (node instanceof TableNode)
activeTableNode = (INode) node;
else
return null;
}
return reg;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
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 void highlightRegion(ISourceViewer viewer, IRegion region) {
if (region.equals(fActiveRegion))
return;
repairRepresentation();
StyledText text = viewer.getTextWidget();
if (text == null || text.isDisposed())
return;
// highlight region
int offset = 0;
int length = 0;
if (viewer instanceof ITextViewerExtension5) {
ITextViewerExtension5 extension = (ITextViewerExtension5) viewer;
IRegion widgetRange = extension.modelRange2WidgetRange(region);
if (widgetRange == null)
return;
offset = widgetRange.getOffset();
length = widgetRange.getLength();
} else {
offset = region.getOffset()
- viewer.getVisibleRegion().getOffset();
length = region.getLength();
}
StyleRange oldStyleRange = text.getStyleRangeAtOffset(offset);
Color foregroundColor = fColor;
Color backgroundColor = oldStyleRange == null ? text
.getBackground() : oldStyleRange.background;
StyleRange styleRange = new StyleRange(offset, length,
foregroundColor, backgroundColor);
text.setStyleRange(styleRange);
// underline
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();
}
/*
* (non-Javadoc)
*
* @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);
}
/*
* (non-Javadoc)
*
* @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(ISourceViewer sourceViewer) {
this.sourceViewer = sourceViewer;
if (sourceViewer == null)
return;
StyledText text = sourceViewer.getTextWidget();
if (text == null || text.isDisposed())
return;
updateColor(sourceViewer);
sourceViewer.addTextInputListener(this);
IDocument document = sourceViewer.getDocument();
if (document != null)
document.addDocumentListener(this);
text.addKeyListener(this);
text.addMouseListener(this);
text.addMouseMoveListener(this);
text.addFocusListener(this);
text.addPaintListener(this);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.events.KeyListener#keyPressed(org.eclipse.swt.events.KeyEvent)
*/
public void keyPressed(KeyEvent event) {
editor.updateCursorPosition();
if (fActive) {
deactivate();
return;
}
if (event.keyCode != fKeyModifierMask) {
deactivate();
return;
}
fActive = true;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.events.KeyListener#keyReleased(org.eclipse.swt.events.KeyEvent)
*/
public void keyReleased(KeyEvent e) {
editor.updateCursorPosition();
if (!fActive)
return;
deactivate();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
*/
public void mouseDoubleClick(MouseEvent e) {
editor.updateCursorPosition();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
*/
public void mouseDown(MouseEvent event) {
editor.updateCursorPosition();
if (!fActive)
return;
if (event.stateMask != fKeyModifierMask) {
deactivate();
return;
}
if (event.button != 1) {
deactivate();
return;
}
}
/*
* (non-Javadoc)
*
* @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;
}
if (sourceViewer == null) {
deactivate();
return;
}
StyledText text = sourceViewer.getTextWidget();
if (text == null || text.isDisposed()) {
deactivate();
return;
}
if ((event.stateMask & SWT.BUTTON1) != 0
&& text.getSelectionCount() != 0) {
deactivate();
return;
}
IRegion region = getCurrentTextRegion(sourceViewer);
if (region == null || region.getLength() == 0) {
repairRepresentation();
return;
}
highlightRegion(sourceViewer, region);
activateCursor(sourceViewer);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
*/
public void mouseUp(MouseEvent e) {
editor.updateCursorPosition();
if (!fActive)
return;
if (e.button != 1) {
deactivate();
return;
}
boolean wasActive = fCursor != null;
deactivate();
if (wasActive) {
BusyIndicator.showWhile(Display.getCurrent(), new Runnable() {
public void run() {
try {
DatabaseStructureView structureView = SQLExplorerPlugin.getDefault().getDatabaseStructureView();
if (structureView != null) {
editor.getEditorSite()
.getWorkbenchWindow().getActivePage()
.bringToTop(structureView);
}
} catch (Exception e1) {
SQLExplorerPlugin
.error("Error selecting table", e1);
}
}
});
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
*/
public void paintControl(PaintEvent event) {
if (fActiveRegion == null)
return;
if (sourceViewer == null)
return;
StyledText text = sourceViewer.getTextWidget();
if (text == null || text.isDisposed())
return;
int offset = 0;
int length = 0;
if (sourceViewer instanceof ITextViewerExtension5) {
ITextViewerExtension5 extension = (ITextViewerExtension5) sourceViewer;
IRegion widgetRange = extension
.modelRange2WidgetRange(new Region(offset, length));
if (widgetRange == null)
return;
offset = widgetRange.getOffset();
length = widgetRange.getLength();
} else {
IRegion region = sourceViewer.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);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent event) {
// noop
}
private void repairRepresentation() {
repairRepresentation(false);
}
private void repairRepresentation(boolean redrawAll) {
if (fActiveRegion == null)
return;
if (sourceViewer != null) {
resetCursor(sourceViewer);
int offset = fActiveRegion.getOffset();
int length = fActiveRegion.getLength();
// remove style
if (!redrawAll && sourceViewer instanceof ITextViewerExtension2)
((ITextViewerExtension2) sourceViewer)
.invalidateTextPresentation(offset, length);
else
sourceViewer.invalidateTextPresentation();
// remove underline
if (sourceViewer instanceof ITextViewerExtension5) {
ITextViewerExtension5 extension = (ITextViewerExtension5) sourceViewer;
offset = extension.modelOffset2WidgetOffset(offset);
} else {
offset -= sourceViewer.getVisibleRegion().getOffset();
}
StyledText text = sourceViewer.getTextWidget();
try {
text.redrawRange(offset, length, true);
} catch (IllegalArgumentException x) {
x.printStackTrace();
// JavaPlugin.log(x);
}
}
fActiveRegion = null;
}
private void resetCursor(ISourceViewer viewer) {
StyledText text = viewer.getTextWidget();
if (text != null && !text.isDisposed())
text.setCursor(null);
if (fCursor != null) {
fCursor.dispose();
fCursor = null;
}
}
private IRegion selectWord(IDocument document, int anchor) {
try {
int offset = anchor;
char c;
while (offset >= 0) {
c = document.getChar(offset);
if (!Character.isJavaIdentifierPart(c))
break;
--offset;
}
int start = offset;
offset = anchor;
int length = document.getLength();
while (offset < length) {
c = document.getChar(offset);
if (!Character.isJavaIdentifierPart(c))
break;
++offset;
}
int end = offset;
if (start == end)
return new Region(start, 0);
else
return new Region(start + 1, end - start - 1);
} catch (BadLocationException x) {
return null;
}
}
public void uninstall() {
if (fColor != null) {
fColor.dispose();
fColor = null;
}
if (fCursor != null) {
fCursor.dispose();
fCursor = null;
}
if (sourceViewer == null)
return;
sourceViewer.removeTextInputListener(this);
IDocument document = sourceViewer.getDocument();
if (document != null)
document.removeDocumentListener(this);
StyledText text = sourceViewer.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(ISourceViewer viewer) {
if (fColor != null)
fColor.dispose();
StyledText text = viewer.getTextWidget();
if (text == null || text.isDisposed())
return;
Display display = text.getDisplay();
fColor = display.getSystemColor(SWT.COLOR_BLUE);
}
}