/**
* Copyright 2004-2016 Riccardo Solmi. All rights reserved.
* This file is part of the Whole Platform.
*
* The Whole Platform 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 3 of the License, or
* (at your option) any later version.
*
* The Whole Platform 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 the Whole Platform. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whole.lang.ui.tools;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.gef.DragTracker;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.SharedCursors;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.tools.SelectionTool;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ST;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.widgets.Display;
import org.whole.lang.model.IEntity;
import org.whole.lang.reflect.IEditorKit;
import org.whole.lang.ui.commands.ITextCommand;
import org.whole.lang.ui.commands.TextTransactionCommand;
import org.whole.lang.ui.editparts.AbstractPart;
import org.whole.lang.ui.editparts.IEntityPart;
import org.whole.lang.ui.editpolicies.TextualHilightEditPolicy;
import org.whole.lang.ui.requests.TextualRequest;
import org.whole.lang.ui.util.AnimableRunnable;
/**
* @author Enrico Persiani
*/
public class TextualSelectionTool extends SelectionTool implements PositionConstants {
static final boolean IS_COCOA = "cocoa".equals(SWT.getPlatform());
protected TextTransactionCommand transactionCommand;
protected boolean mirrored;
protected int tripleClickButton;
protected boolean animationState;
public TextualSelectionTool() {
setDefaultCursor(SharedCursors.IBEAM);
setUnloadWhenFinished(false);
}
protected EditPart getFocusedPart() {
EditPartViewer viewer = getCurrentViewer();
return viewer != null ? viewer.getFocusEditPart() : null;
}
protected IEditorKit getEditorKit() {
EditPart target = getFocusedPart();
if (target == null || !(target instanceof IEntityPart))
return null;
return ((IEntityPart) target).getModelEntity().wGetEditorKit();
}
protected void resetTransactionCommand() {
transactionCommand = null;
}
protected void performSelectionLocationUpdate(TextualHilightEditPolicy editPolicy) {
resetTransactionCommand();
editPolicy.updateHilight(getLocation());
}
@Override
public void setViewer(EditPartViewer viewer) {
super.setViewer(viewer);
mirrored = viewer != null && (viewer.getControl().getStyle() & SWT.MIRRORED) != 0;
}
@Override
public void activate() {
super.activate();
animationState = AnimableRunnable.enableAnimation(false);
refreshCursor();
AbstractPart.setSyncPropertyChange(true);
}
@Override
public void deactivate() {
AbstractPart.setSyncPropertyChange(false);
resetTransactionCommand();
AnimableRunnable.enableAnimation(animationState);
super.deactivate();
}
@Override
public void mouseDown(MouseEvent e, EditPartViewer viewer) {
super.mouseDown(e, viewer);
EditPart targetEditPart = getTargetEditPart();
if (targetEditPart == null || e.button != 1)
return;
EditPolicy editPolicy = targetEditPart.getEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE);
if (editPolicy instanceof TextualHilightEditPolicy)
performSelectionLocationUpdate((TextualHilightEditPolicy) editPolicy);
}
@Override
public void mouseUp(MouseEvent e, EditPartViewer viewer) {
if (tripleClickButton < 0)
tripleClickButton = -tripleClickButton;
else if (tripleClickButton > 0) {
DragTracker dragTracker = getDragTracker();
if (dragTracker instanceof TextualDragTracker)
((TextualDragTracker) dragTracker).mouseTripleClick(e, viewer);
}
super.mouseUp(e, viewer);
}
@Override
public void mouseDoubleClick(MouseEvent e, EditPartViewer viewer) {
super.mouseDoubleClick(e, viewer);
tripleClickButton = -e.button;
Display.getCurrent().timerExec(500, new Runnable() {
public void run() {
tripleClickButton = 0;
}
});
}
@Override
protected boolean handleKeyDown(KeyEvent event) {
if (isInState(STATE_INITIAL)) {
handleModelKeyPressed(event);
if (event.doit)
doKeyDown(event);
else
resetTransactionCommand();
}
if (event.doit) {
//TODO better tracking of focus change in graphical viewer
EditPart editPart = getTargetEditPart();
boolean handleKeyDown = super.handleKeyDown(event);
EditPart focusEditPart = getCurrentViewer().getFocusEditPart();
if (!focusEditPart.equals(editPart))
setTargetEditPart(focusEditPart);
return handleKeyDown;
}
return true;
}
@Override
protected boolean handleKeyUp(KeyEvent event) {
handleModelKeyReleased(event);
if (event.doit)
return super.handleKeyUp(event);
resetTransactionCommand();
return true;
}
protected void doKeyDown(KeyEvent event) {
int action = 0;
if (event.keyCode != 0)
action = lookupAction(event.keyCode | event.stateMask);
else {
action = lookupAction(event.character | event.stateMask);
// check for control characters
if (action == 0 && (event.stateMask & SWT.CTRL) != 0 && event.character >= 0 && event.character <= 31) {
// get the character from the CTRL+char sequence, the control
// key subtracts 64 from the value of the key that it modifies
int c = event.character + 64;
action = lookupAction(c | event.stateMask);
}
}
if (action == 0)
handleTyping(event);
else
doAction(action, event);
}
protected void doAction(int action, KeyEvent event) {
event.doit = false;
switch (action) {
case ST.DELETE_PREVIOUS:
event.doit = !handleBackspace();
break;
case ST.DELETE_NEXT:
event.doit = !handleDelete();
break;
case ST.LINE_UP:
event.doit = !handleCaretMove(-1, VERTICAL);
break;
case ST.LINE_DOWN:
event.doit = !handleCaretMove(1, VERTICAL);
break;
case ST.COLUMN_PREVIOUS:
event.doit = !handleCaretMove(-1, HORIZONTAL);
break;
case ST.COLUMN_NEXT:
event.doit = !handleCaretMove(1, HORIZONTAL);
break;
case ST.TOGGLE_OVERWRITE:
toggleOverwrite();
break;
case SWT.TAB:
handleTyping(event);
break;
case SWT.LF:
handleTyping(event);
break;
default:
event.doit = true;
break;
}
}
protected void handleTyping(KeyEvent event) {
boolean ignore = false;
if (IS_COCOA) {
// Ignore accelerator key combinations (we do not want to
// insert a character in the text in this instance). Do not
// ignore COMMAND+ALT combinations since that key sequence
// produces characters on the mac.
ignore = (event.stateMask ^ SWT.COMMAND) == 0
|| (event.stateMask ^ (SWT.COMMAND | SWT.SHIFT)) == 0;
} else {
// Ignore accelerator key combinations (we do not want to
// insert a character in the text in this instance). Don't
// ignore CTRL+ALT combinations since that is the Alt Gr
// key on some keyboards.
ignore = (event.stateMask ^ SWT.ALT) == 0 || (event.stateMask ^ SWT.CTRL) == 0
|| (event.stateMask ^ (SWT.ALT | SWT.SHIFT)) == 0
|| (event.stateMask ^ (SWT.CTRL | SWT.SHIFT)) == 0;
}
// -ignore anything below SPACE except for line delimiter keys and tab.
// -ignore DEL
if (!ignore && event.character > 31 && event.character != SWT.DEL
|| event.character == SWT.CR || event.character == SWT.LF
|| event.character == '\t') {
doInsertContent(event.character);
event.doit = false;
}
}
protected int lookupAction(int i) {
switch (i) {
//Left and Right
case SWT.ARROW_LEFT:
return mirrored ? ST.COLUMN_NEXT : ST.COLUMN_PREVIOUS;
case SWT.ARROW_RIGHT:
return mirrored ? ST.COLUMN_PREVIOUS : ST.COLUMN_NEXT;
case SWT.ARROW_RIGHT | SWT.SHIFT:
return mirrored ? ST.SELECT_COLUMN_PREVIOUS : ST.SELECT_COLUMN_NEXT;
case SWT.ARROW_LEFT | SWT.SHIFT:
return mirrored ? ST.SELECT_COLUMN_NEXT : ST.SELECT_COLUMN_PREVIOUS;
case SWT.ARROW_RIGHT | SWT.CONTROL:
return mirrored ? ST.WORD_PREVIOUS : ST.WORD_NEXT;
case SWT.ARROW_RIGHT | SWT.CONTROL | SWT.SHIFT:
return mirrored ? ST.SELECT_WORD_PREVIOUS : ST.SELECT_WORD_NEXT;
case SWT.ARROW_LEFT| SWT.CONTROL:
return mirrored ? ST.WORD_NEXT : ST.WORD_PREVIOUS;
case SWT.ARROW_LEFT| SWT.CONTROL | SWT.SHIFT:
return mirrored ? ST.SELECT_WORD_NEXT : ST.SELECT_WORD_PREVIOUS;
case ST.LINE_END:
case ST.TOGGLE_OVERWRITE:
case ST.SELECT_LINE_END:
case ST.LINE_START:
case ST.SELECT_LINE_START:
case ST.PAGE_UP:
case ST.PAGE_DOWN:
case ST.SELECT_PAGE_UP:
case ST.SELECT_PAGE_DOWN:
case ST.LINE_UP:
case ST.LINE_DOWN:
case ST.SELECT_LINE_UP:
case ST.SELECT_LINE_DOWN:
case ST.TEXT_END:
case ST.SELECT_TEXT_END:
case ST.TEXT_START:
case ST.SELECT_TEXT_START:
case ST.DELETE_PREVIOUS:
case ST.DELETE_NEXT:
case ST.WINDOW_START:
case ST.WINDOW_END:
case ST.SELECT_WINDOW_START:
case ST.SELECT_WINDOW_END:
case SWT.TAB | SWT.SHIFT:
case SWT.TAB:
return i;
case SWT.LF:
case SWT.CR:
return SWT.LF;
default:
break;
}
return 0;
}
protected boolean handleCaretMove(int positions, int direction) {
EditPart target = getFocusedPart();
if (target == null)
return false;
EditPolicy editPolicy = target.getEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE);
if (editPolicy instanceof TextualHilightEditPolicy) {
if (direction == HORIZONTAL && ((TextualHilightEditPolicy) editPolicy).moveCaretHorizontally(positions)) {
resetTransactionCommand();
return true;
}
if (direction == VERTICAL && ((TextualHilightEditPolicy) editPolicy).moveCaretVertically(positions)) {
resetTransactionCommand();
return true;
}
}
return false;
}
protected boolean handleTextRequest(TextualRequest edit) {
EditPart target = getFocusedPart();
if (target == null)
return false;
Command command = target.getCommand(edit);
if (command == null || !command.canExecute())
return false;
if (command instanceof ITextCommand) {
ITextCommand textCommand = (ITextCommand) command;
if (transactionCommand == null || !transactionCommand.canMerge(textCommand) || !getDomain().getCommandStack().isDirty()) {
transactionCommand = new TextTransactionCommand();
transactionCommand.setModel((IEntity) target.getModel());
transactionCommand.merge(textCommand);
executeCommand(transactionCommand);
} else
transactionCommand.merge(textCommand);
} else {
resetTransactionCommand();
executeCommand(command);
}
return true;
}
protected void handleModelKeyPressed(KeyEvent event) {
getCurrentViewer().getKeyHandler().keyPressed(event);
}
protected void handleModelKeyReleased(KeyEvent event) {
getCurrentViewer().getKeyHandler().keyReleased(event);
}
protected boolean handleBackspace() {
return handleTextRequest(TextualRequest.createBackspaceRequest());
}
protected boolean handleDelete() {
return handleTextRequest(TextualRequest.createDeleteRequest());
}
private boolean overwrite = false;
public boolean toggleOverwrite() {
return overwrite = !overwrite;
}
public boolean isOverwrite() {
return overwrite;
}
protected boolean doInsertContent(char ch) {
return doInsertContent(ch == SWT.CR ? "\n" : Character.toString(ch));
}
protected boolean doInsertContent(String content) {
return handleTextRequest(isOverwrite() ? TextualRequest.createOverwriteRequest(content) : TextualRequest.createInsertRequest(content));
}
@Override
protected Cursor calculateCursor() {
return getDefaultCursor();
}
}