package org.eclipse.iee.editor.core.container;
import java.util.Hashtable;
import org.eclipse.iee.editor.core.pad.Pad;
import org.eclipse.iee.editor.core.pad.common.text.IEditorLocation;
import org.eclipse.iee.editor.core.pad.common.ui.IMenuContributor;
import org.eclipse.iee.editor.core.pad.common.ui.SelectionModel;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CaretEvent;
import org.eclipse.swt.custom.CaretListener;
import org.eclipse.swt.custom.ST;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Menu;
import com.google.common.base.Optional;
public class UserInteractionManager {
private final ISourceViewer fSourceViewer;
private final ContainerManager fContainerManager;
private Hashtable<Integer, Integer> fKeyActionMap = new Hashtable<Integer, Integer>();
public UserInteractionManager(ContainerManager containerManager) {
fContainerManager = containerManager;
fSourceViewer = containerManager.getSourceViewer();
fContainerManager.selectEditor(null);
initListeners();
createKeyBindings();
}
public void moveCaretTo(int offset) {
fContainerManager.selectEditor(null);
fContainerManager.activateEditor(null);
ITextViewerExtension5 ext5 = getExt5();
fSourceViewer.getTextWidget().setCaretOffset(ext5.modelOffset2WidgetOffset(offset));
}
private ITextViewerExtension5 getExt5() {
return (ITextViewerExtension5) fSourceViewer;
}
public void focusOnMainEditor() {
fSourceViewer.getTextWidget().forceFocus();
}
public void updateCaretSelection() {
}
protected void initListeners() {
/* 1) Disallow modification within Container's text region */
final StyledText textWidget = fSourceViewer.getTextWidget();
textWidget.addVerifyListener(new VerifyListener() {
@Override
public void verifyText(VerifyEvent e) {
int start = getExt5().widgetOffset2ModelOffset(e.start);
int end = getExt5().widgetOffset2ModelOffset(e.end);
Container atStart = fContainerManager
.getContainerHavingOffset(start);
Container atEnd = fContainerManager
.getContainerHavingOffset(end);
/* Text replaced */
if ((atStart != null && start != atStart.getPosition()
.getOffset())
|| (atEnd != null && e.end != atEnd.getPosition()
.getOffset())) {
e.doit = false;
return;
}
/* XXX Visibility */
// fContainerManager.updateContainerVisibility(false);
}
});
textWidget.addVerifyKeyListener(new VerifyKeyListener () {
@Override
public void verifyKey(VerifyEvent e) {
Optional<IEditorLocation> position = fContainerManager.getCursonPosition();
int action = getAction(e);
if (position.isPresent()) {
if (!position.get().getEditor().handleKey(e)) {
if (action == SWT.NULL && e.character != 0) {
IEditorLocation replace = fContainerManager.getSelectionModel().replace(String.valueOf(e.character));
fContainerManager.setCursorPosition(replace);
} else {
doAction(action);
}
}
e.doit = false;
} else {
if (action == SWT.NULL) {
if (e.character == SWT.DEL) {
action = ST.DELETE_NEXT;
}
}
if (action != SWT.NULL) {
int caretOffset = getExt5().widgetOffset2ModelOffset(textWidget.getCaretOffset());
switch (action) {
case ST.COLUMN_PREVIOUS:
case ST.SELECT_COLUMN_PREVIOUS:
e.doit = caretPositionChange(caretOffset, false);
break;
case ST.COLUMN_NEXT:
case ST.SELECT_COLUMN_NEXT:
e.doit = caretPositionChange(caretOffset, true);
break;
case ST.DELETE_NEXT:
Container container = fContainerManager
.getContainerHavingOffset(caretOffset);
if (container != null) {
container.destroy();
}
case ST.DELETE_PREVIOUS:
Container prevContainer = fContainerManager
.getContainerHavingOffset(caretOffset - 1);
if (prevContainer != null) {
prevContainer.destroy();
}
}
}
}
}
});
/*
* If caret is inside Container's text region, moving it to the end of
* line
*/
textWidget.addCaretListener(new CaretListener() {
@Override
public void caretMoved(CaretEvent e) {
Point selection = fSourceViewer.getSelectedRange();
if (selection.y == 0) {
int caretOffset = getExt5().widgetOffset2ModelOffset(e.caretOffset);
Container container = fContainerManager
.getContainerHavingOffset(caretOffset);
if (container != null) {
Position position = container.getPosition();
if (caretOffset != position.getOffset()) {
/* Move caret to the Pad's border */
textWidget.setCaretOffset(getExt5().modelOffset2WidgetOffset(position.getOffset()
+ position.getLength()));
fContainerManager.selectEditor(null);
}
}
}
}
});
textWidget.addMenuDetectListener(new MenuDetectListener() {
@Override
public void menuDetected(MenuDetectEvent e) {
Point control = textWidget.toControl(e.x, e.y);
Optional<ITextEditor<?>> editor = fContainerManager.getEditorAt(control.x, control.y);
while (editor.isPresent() && !(editor.get() instanceof IMenuContributor)) {
editor = editor.get().getParent();
}
if (editor.isPresent()) {
MenuManager menuManager = new MenuManager();
Menu menu = menuManager.createContextMenu(textWidget);
((IMenuContributor) editor.get()).contribute(menuManager);
menuManager.update(false);
menu.setLocation(e.x, e.y);
menu.setVisible(true);
e.doit = false;
}
}
});
textWidget.addFocusListener(new FocusListener() {
@Override
public void focusLost(FocusEvent e) {
}
@Override
public void focusGained(FocusEvent e) {
fContainerManager.activateEditor(null);
}
});
}
private boolean caretPositionChange(int x, boolean caretMovesForward) {
Point selection = fSourceViewer.getSelectedRange();
if (selection.y == 0) {
Container container = fContainerManager.getContainerHavingOffset(caretMovesForward ? x : x - 1);
if (container != null) {
Position position = container.getPosition();
if (caretMovesForward) {
Pad<?, ?> pad = container.getPad();
Optional<IEditorLocation> start = pad.getStart();
fContainerManager.activateEditor(pad);
if (start.isPresent()) {
fContainerManager.setCursorPosition(start.get());
} else {
fSourceViewer.getTextWidget().setCaretOffset(getExt5().modelOffset2WidgetOffset(position.getOffset()
+ position.getLength()));
}
} else {
Pad<?, ?> pad = container.getPad();
Optional<IEditorLocation> end = pad.getEnd();
fContainerManager.activateEditor(pad);
if (end.isPresent()) {
fContainerManager.setCursorPosition(end.get());
} else {
fSourceViewer.getTextWidget().setCaretOffset(getExt5().modelOffset2WidgetOffset(position.getOffset()));
}
}
return false;
}
}
return true;
}
private int getAction(KeyEvent event) {
int action;
if (event.keyCode!= 0) {
// special key pressed (e.g., F1)
action = getKeyBinding(event.keyCode | event.stateMask);
} else {
// character key pressed
action = getKeyBinding(event.character | event.stateMask);
if (action == SWT.NULL) {
if ((event.stateMask & SWT.CTRL) != 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 = getKeyBinding(c | event.stateMask);
}
}
}
return action;
}
private int getKeyBinding(int key) {
Integer action = (Integer) fKeyActionMap.get(new Integer(key));
return action == null ? SWT.NULL : action.intValue();
}
/**
* Creates default key bindings.
*/
void createKeyBindings() {
int nextKey = fContainerManager.isMirrored() ? SWT.ARROW_LEFT : SWT.ARROW_RIGHT;
int previousKey = fContainerManager.isMirrored() ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT;
// Navigation
setKeyBinding(SWT.ARROW_UP, ST.LINE_UP);
setKeyBinding(SWT.ARROW_DOWN, ST.LINE_DOWN);
setKeyBinding(SWT.HOME, ST.LINE_START);
setKeyBinding(SWT.END, ST.LINE_END);
setKeyBinding(SWT.HOME | SWT.MOD1, ST.TEXT_START);
setKeyBinding(SWT.END | SWT.MOD1, ST.TEXT_END);
setKeyBinding(nextKey | SWT.MOD1, ST.WORD_NEXT);
setKeyBinding(previousKey | SWT.MOD1, ST.WORD_PREVIOUS);
setKeyBinding(SWT.PAGE_UP, ST.PAGE_UP);
setKeyBinding(SWT.PAGE_DOWN, ST.PAGE_DOWN);
setKeyBinding(SWT.PAGE_UP | SWT.MOD1, ST.WINDOW_START);
setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1, ST.WINDOW_END);
setKeyBinding(nextKey, ST.COLUMN_NEXT);
setKeyBinding(previousKey, ST.COLUMN_PREVIOUS);
// Selection
setKeyBinding(SWT.ARROW_UP | SWT.MOD2, ST.SELECT_LINE_UP);
setKeyBinding(SWT.ARROW_DOWN | SWT.MOD2, ST.SELECT_LINE_DOWN);
setKeyBinding(SWT.HOME | SWT.MOD2, ST.SELECT_LINE_START);
setKeyBinding(SWT.END | SWT.MOD2, ST.SELECT_LINE_END);
setKeyBinding(SWT.HOME | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_START);
setKeyBinding(SWT.END | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_END);
setKeyBinding(nextKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_NEXT);
setKeyBinding(previousKey | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_PREVIOUS);
setKeyBinding(SWT.PAGE_UP | SWT.MOD2, ST.SELECT_PAGE_UP);
setKeyBinding(SWT.PAGE_DOWN | SWT.MOD2, ST.SELECT_PAGE_DOWN);
setKeyBinding(SWT.PAGE_UP | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_START);
setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_END);
setKeyBinding(nextKey | SWT.MOD2, ST.SELECT_COLUMN_NEXT);
setKeyBinding(previousKey | SWT.MOD2, ST.SELECT_COLUMN_PREVIOUS);
// Modification
// Cut, Copy, Paste
setKeyBinding('X' | SWT.MOD1, ST.CUT);
setKeyBinding('C' | SWT.MOD1, ST.COPY);
setKeyBinding('V' | SWT.MOD1, ST.PASTE);
// Cut, Copy, Paste Wordstar style
setKeyBinding(SWT.DEL | SWT.MOD2, ST.CUT);
setKeyBinding(SWT.INSERT | SWT.MOD1, ST.COPY);
setKeyBinding(SWT.INSERT | SWT.MOD2, ST.PASTE);
setKeyBinding(SWT.BS | SWT.MOD2, ST.DELETE_PREVIOUS);
setKeyBinding(SWT.BS, ST.DELETE_PREVIOUS);
setKeyBinding(SWT.DEL, ST.DELETE_NEXT);
setKeyBinding(SWT.BS | SWT.MOD1, ST.DELETE_WORD_PREVIOUS);
setKeyBinding(SWT.DEL | SWT.MOD1, ST.DELETE_WORD_NEXT);
// Miscellaneous
setKeyBinding(SWT.INSERT, ST.TOGGLE_OVERWRITE);
}
public void setKeyBinding(int key, int action) {
int modifierValue = key & SWT.MODIFIER_MASK;
char keyChar = (char)(key & SWT.KEY_MASK);
if (Character.isLetter(keyChar)) {
char ch = Character.toUpperCase(keyChar);
int newKey = ch | modifierValue;
if (action == SWT.NULL) {
fKeyActionMap.remove(new Integer(newKey));
} else {
fKeyActionMap.put(new Integer(newKey), new Integer(action));
}
ch = Character.toLowerCase(keyChar);
newKey = ch | modifierValue;
if (action == SWT.NULL) {
fKeyActionMap.remove(new Integer(newKey));
} else {
fKeyActionMap.put(new Integer(newKey), new Integer(action));
}
} else {
if (action == SWT.NULL) {
fKeyActionMap.remove(new Integer(key));
} else {
fKeyActionMap.put(new Integer(key), new Integer(action));
}
}
}
private void doAction(int action) {
switch (action) {
case ST.DELETE_PREVIOUS:
doDeletePrevious();
break;
case ST.DELETE_NEXT:
doDeleteNext();
break;
case ST.COLUMN_NEXT:
doColumnNext(false);
break;
case ST.SELECT_COLUMN_NEXT:
doColumnNext(true);
break;
case ST.COLUMN_PREVIOUS:
doColumnPrevious(false);
break;
case ST.SELECT_COLUMN_PREVIOUS:
doColumnPrevious(true);
break;
default:
break;
}
}
private void doColumnNext(boolean selection) {
Optional<IEditorLocation> position = getCursonPosition();
Optional<IEditorLocation> next = position.get().getNext();
if (next.isPresent()) {
if (!selection) {
fContainerManager.setCursorPosition(next.get());
} else {
fContainerManager.setSelectionEnd(next.get());
}
} else if (!selection) {
ITextEditor<?> editor = getPad(position.get());
if (editor != null) {
((Pad) editor).moveCaretToContainerTail();
}
}
}
private ITextEditor<?> getPad(IEditorLocation position) {
ITextEditor<?> editor = position.getEditor();
while (editor != null && !(editor instanceof Pad)) {
editor = editor.getParent().isPresent() ? editor.getParent().get() : null;
}
return editor;
}
private void doColumnPrevious(boolean selection) {
Optional<IEditorLocation> position = getCursonPosition();
Optional<IEditorLocation> previous = position.get().getPrevious();
if (previous.isPresent()) {
if (!selection) {
fContainerManager.setCursorPosition(previous.get());
} else {
fContainerManager.setSelectionEnd(previous.get());
}
} else if (!selection) {
ITextEditor<?> editor = getPad(position.get());
if (editor != null) {
((Pad) editor).moveCaretToCurrentPad();
}
}
}
private void doDeletePrevious() {
SelectionModel selectionModel = fContainerManager.getSelectionModel();
if (selectionModel.isEmpty()) {
Optional<IEditorLocation> start = getCursonPosition();
Optional<IEditorLocation> previous = start.get().getPrevious();
if (previous.isPresent()) {
fContainerManager.setCursorPosition(new SelectionModel(previous.get(), start.get()).replace(""));
}
} else {
fContainerManager.setCursorPosition(selectionModel.replace(""));
}
}
private void doDeleteNext() {
SelectionModel selectionModel = fContainerManager.getSelectionModel();
if (selectionModel.isEmpty()) {
Optional<IEditorLocation> start = getCursonPosition();
Optional<IEditorLocation> next = start.get().getNext();
if (next.isPresent()) {
fContainerManager.setCursorPosition(new SelectionModel(start.get(), next.get()).replace(""));
}
} else {
fContainerManager.setCursorPosition(selectionModel.replace(""));
}
}
private Optional<IEditorLocation> getCursonPosition() {
return fContainerManager.getCursonPosition();
}
}