package org.eclipse.iee.editor.core.pad.common.text; import java.awt.Rectangle; import java.util.List; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.text.CaretInfo; import org.eclipse.iee.editor.core.bindings.IObservableValue; import org.eclipse.iee.editor.core.bindings.IObserver; import org.eclipse.iee.editor.core.container.EditorManager; import org.eclipse.iee.editor.core.container.ITextEditor; import org.eclipse.iee.editor.core.container.IView; import org.eclipse.swt.events.KeyEvent; import com.google.common.base.Optional; import com.google.common.collect.Lists; public abstract class AbstractTextEditor<T> implements ITextEditor<T>, IAdaptable { private Optional<? extends IObservableValue<T>> fModel = Optional.absent(); private Optional<ITextEditor<?>> fParent = Optional.absent(); private List<ITextEditor<?>> fChildren = Lists.newArrayList(); private Optional<EditorManager> fManager = Optional.absent(); private IView fView; private IObserver<T> fObserver = new IObserver<T>() { @Override public void valueChanged(T oldValue, T newValue) { onValueChanged(oldValue, newValue); } }; public AbstractTextEditor() { } @Override public IView getView() { if (fView == null) { fView = createView(); } return fView; } abstract protected IView createView(); @Override public Optional<IEditorLocation> getTextLocation(int x, int y) { return Optional.absent(); } @Override public Optional<IEditorLocation> getNext(ITextEditor<?> textPart) { int indexOf = getChildren().indexOf(textPart); if (indexOf < 0) { throw new IllegalArgumentException("Editor is not a child of this container"); } else if (indexOf + 1 == getChildren().size()) { if (getParent().isPresent()) { return getParent().get().getNext(this); } else { return Optional.<IEditorLocation> absent(); } } else { return getChildren().get(indexOf + 1).getStart(); } } @Override public Optional<IEditorLocation> getPrevious(ITextEditor<?> textPart) { int indexOf = getChildren().indexOf(textPart); if (indexOf < 0) { throw new IllegalArgumentException("Editor is not a child of this container"); } else if (indexOf - 1 < 0) { if (getParent().isPresent()) { return getParent().get().getPrevious(this); } else { return Optional.<IEditorLocation> absent(); } } else { return getChildren().get(indexOf - 1).getEnd(); } } @Override public Optional<IEditorLocation> getAbove(IEditorLocation textLocation) { ITextEditor<?> editor = textLocation.getEditor(); return getAbove(textLocation, editor); } public Optional<IEditorLocation> getAbove(IEditorLocation textLocation, ITextEditor<?> editor) { CaretInfo caretInfo = textLocation.getCaretInfo(); int y = getView().getBounds().y - 1; Optional<ITextEditor<?>> editorAt = getManager().get().getEditorAt(new Point(caretInfo.getX(), y)); if (editorAt.isPresent()) { return editorAt.get().getTextLocation(caretInfo.getX(), y); } else if (getParent().isPresent()) { return getAbove(textLocation, getParent().get()); } return Optional.<IEditorLocation> absent(); } @Override public Optional<IEditorLocation> getBelow(IEditorLocation textLocation) { ITextEditor<?> editor = textLocation.getEditor(); return getBelow(textLocation, editor); } private Optional<IEditorLocation> getBelow(IEditorLocation textLocation, ITextEditor<?> editor) { CaretInfo caretInfo = textLocation.getCaretInfo(); Rectangle bounds = getView().getBounds(); int y = bounds.y + bounds.height + 1; Optional<ITextEditor<?>> editorAt = getManager().get().getEditorAt(new Point(caretInfo.getX(), y)); if (editorAt.isPresent()) { return editorAt.get().getTextLocation(caretInfo.getX(), y); } else if (getParent().isPresent()) { return getBelow(textLocation, getParent().get()); } return Optional.<IEditorLocation> absent(); } @Override public Optional<IEditorLocation> getStart() { if (getChildren().size() > 0) { return getChildren().get(0).getStart(); } return Optional.<IEditorLocation> absent(); } @Override public Optional<IEditorLocation> getEnd() { if (getChildren().size() > 0) { return getChildren().get(getChildren().size() - 1).getEnd(); } return Optional.<IEditorLocation> absent(); } @Override public Optional<IEditorLocation> getLineStart(int x, int y) { return getLineStart(x, y, true); } @Override public Optional<IEditorLocation> getLineStart(int x, int y, boolean askParent) { if (askParent && getParent().isPresent()) { return getParent().get().getLineStart(x, y, true); } else { List<ITextEditor<?>> children = getChildren(); Optional<IEditorLocation> lineStart = Optional.absent(); for (ITextEditor<?> iTextEditor : children) { Optional<IEditorLocation> tmp = iTextEditor.getLineStart(x, y, false); if (tmp.isPresent()) { CaretInfo caretInfo = tmp.get().getCaretInfo(); if (caretInfo.getX() < x) { x = caretInfo.getX(); lineStart = tmp; } } } return lineStart; } } @Override public Optional<IEditorLocation> getLineEnd(int x, int y) { return getLineEnd(x, y, true); } @Override public Optional<IEditorLocation> getLineEnd(int x, int y, boolean askParent) { if (askParent && getParent().isPresent()) { return getParent().get().getLineEnd(x, y, true); } else { List<ITextEditor<?>> children = getChildren(); Optional<IEditorLocation> lineStart = Optional.absent(); for (ITextEditor<?> iTextEditor : children) { Optional<IEditorLocation> tmp = iTextEditor.getLineEnd(x, y, false); if (tmp.isPresent()) { CaretInfo caretInfo = tmp.get().getCaretInfo(); if (caretInfo.getX() > x) { x = caretInfo.getX(); lineStart = tmp; } } } return lineStart; } } @Override public T getModel() { return fModel.isPresent() ? fModel.get().getValue() : null; } public void setParent(Optional<ITextEditor<?>> parent) { fParent = parent; } @Override public Optional<ITextEditor<?>> getParent() { return fParent; } public ITextEditor<?> addEditor(ITextEditor<?> child) { child.setParent(Optional.<ITextEditor<?>> of(this)); fChildren.add(child); if (getManager().isPresent()) { getManager().get().addEditor(child); } return child; } public void attach(EditorManager manager) { setManager(Optional.of(manager)); T model = getModel(); if (model != null) { manager.registerModel(model, this); } getManager().get().registerVisual(this, getView()); for (ITextEditor<?> iTextEditor : fChildren) { iTextEditor.attach(manager); } } public void removeEditor(ITextEditor<?> child) { if (getManager().isPresent()) { getManager().get().removeEditor(child); } fChildren.remove(child); child.setParent(Optional.<ITextEditor<?>> absent()); } public void detach(EditorManager manager) { for (ITextEditor<?> iTextEditor : fChildren) { iTextEditor.detach(manager); } manager.unregisterVisual(getView()); T model = getModel(); if (model != null) { manager.unregisterModel(model); } setManager(Optional.<EditorManager> absent()); } @Override public void setValue(Optional<? extends IObservableValue<T>> value) { IObservableValue<T> oldValue = getValue().isPresent() ? getValue().get() : null; fModel = value; if (oldValue != value) { T old = null; if (oldValue != null) { old = oldValue.getValue(); if (old != null) { if (fManager.isPresent()) { fManager.get().unregisterModel(old); } doUnbindValue(old); } oldValue.removeObserver(fObserver); } if (value.isPresent()) { value.get().addObserver(fObserver); T newV = value.get().getValue(); if (newV != null) { if (fManager.isPresent()) { fManager.get().registerModel(newV, this); } doBindValue(newV); } onValueChanged(old, newV); } } } public List<ITextEditor<?>> getChildren() { return fChildren; } protected void doBindValue(T value) { } protected void doUnbindValue(T oldValue) { } protected void onValueChanged(T oldValue, T newValue) { } @Override public Optional<? extends IObservableValue<T>> getValue() { return fModel; } @Override public void selectBetween(IEditorLocation start, IEditorLocation end) { List<ITextEditor<?>> children = getChildren(); Optional<ITextEditor<?>> startContainer = getChildContaining(start.getEditor()); int startindex = startContainer.isPresent() ? children.indexOf(startContainer.get()) : 0; Optional<ITextEditor<?>> endContainer = getChildContaining(end.getEditor()); int endindex = endContainer.isPresent() ? children.indexOf(endContainer.get()) + 1 : children.size(); for(int i = startindex; i < endindex; i++) { children.get(i).selectBetween(start, end); } } private Optional<ITextEditor<?>> getChildContaining(ITextEditor<?> editor) { Optional<ITextEditor<?>> t = Optional.<ITextEditor<?>> of(editor); if (t.get().getParent().isPresent() && t.get().getParent().get() == this) { return t; } while(t.get().getParent().isPresent() && t.get().getParent().get() != this) { t = t.get().getParent(); } if (!t.get().getParent().isPresent()) { return Optional.absent(); } return t; } @Override public void unselectBetween(IEditorLocation start, IEditorLocation end) { List<ITextEditor<?>> children = getChildren(); Optional<ITextEditor<?>> startContainer = getChildContaining(start.getEditor()); int startindex = startContainer.isPresent() ? children.indexOf(startContainer.get()) : 0; Optional<ITextEditor<?>> endContainer = getChildContaining(end.getEditor()); int endindex = endContainer.isPresent() ? children.indexOf(endContainer.get()) + 1 : children.size(); for(int i = startindex; i < endindex; i++) { children.get(i).unselectBetween(start, end); } } public void dispose() { if (fModel.isPresent()) { if (fModel.get().getValue() != null) { doUnbindValue(fModel.get().getValue()); } fModel.get().removeObserver(fObserver); } for (ITextEditor<?> child : fChildren) { child.dispose(); } doDispose(); } protected void doDispose() { } @Override public Object getAdapter(Class adapter) { if (getModel() == null) { return null; } if (adapter.isAssignableFrom(getModel().getClass())) { return adapter; } return null; } protected Optional<EditorManager> getManager() { return fManager; } protected void setManager(Optional<EditorManager> manager) { fManager = manager; } @Override public boolean handleKey(KeyEvent e) { if (getParent().isPresent()) { return getParent().get().handleKey(e); } return false; } }