/* ******************************************************************************
* Copyright (c) 2006-2012 XMind Ltd. and others.
*
* This file is a part of XMind 3. XMind releases 3 and
* above are dual-licensed under the Eclipse Public License (EPL),
* which is available at http://www.eclipse.org/legal/epl-v10.html
* and the GNU Lesser General Public License (LGPL),
* which is available at http://www.gnu.org/licenses/lgpl.html
* See http://www.xmind.net/license.html for details.
*
* Contributors:
* XMind Ltd. - initial API and implementation
*******************************************************************************/
package org.xmind.ui.texteditor;
import org.eclipse.draw2d.Viewport;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.TextStyle;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ScrollBar;
import org.xmind.gef.GraphicalViewer;
import org.xmind.gef.IGraphicalViewer;
import org.xmind.gef.IZoomListener;
import org.xmind.gef.ZoomManager;
import org.xmind.gef.ZoomObject;
public abstract class FloatingTextEditorHelperBase implements
IFloatingTextEditorListener, IZoomListener, Listener {
private final static int GAP = 20;
private FloatingTextEditor editor = null;
private IGraphicalViewer viewer = null;
private ZoomManager zoomManager = null;
private int minWidth = -1;
private int maxWidth = -1;
private int prefWidth = -1;
private boolean extendsBidirectionalHorizontal;
private int expansion = 100;
public FloatingTextEditorHelperBase() {
this(false);
}
public FloatingTextEditorHelperBase(boolean extendsBidirectionalHorizontal) {
this.extendsBidirectionalHorizontal = extendsBidirectionalHorizontal;
}
public boolean isExtendsBidirectionalHorizontal() {
return extendsBidirectionalHorizontal;
}
public void setExtendsBidirectionalHorizontal(
boolean extendsBidirectionalHorizontal) {
this.extendsBidirectionalHorizontal = extendsBidirectionalHorizontal;
}
public FloatingTextEditor getEditor() {
return editor;
}
public IGraphicalViewer getViewer() {
return viewer;
}
public ZoomManager getZoomManager() {
return zoomManager;
}
public int getMinWidth() {
return minWidth;
}
public void setMinWidth(int minWidth) {
this.minWidth = minWidth;
}
public int getMaxWidth() {
return maxWidth;
}
public void setMaxWidth(int maxWidth) {
this.maxWidth = maxWidth;
}
public void setEditor(FloatingTextEditor editor) {
this.editor = editor;
}
public void setViewer(IGraphicalViewer viewer) {
this.viewer = viewer;
}
public int getPrefWidth() {
return prefWidth;
}
public void setPrefWidth(int prefWidth) {
this.prefWidth = prefWidth;
}
public int getExpansion() {
return expansion;
}
public void setExpansion(int expansion) {
this.expansion = expansion;
}
public void activate() {
if (editor != null) {
editor.addFloatingTextEditorListener(this);
}
if (viewer != null) {
Control control = viewer.getControl();
if (control != null && !control.isDisposed()) {
control.addListener(SWT.Resize, this);
}
this.zoomManager = viewer.getZoomManager();
this.zoomManager.addZoomListener(this);
}
}
public void deactivate() {
if (editor != null) {
editor.removeFloatingTextEditorListener(this);
editor = null;
}
if (zoomManager != null) {
zoomManager.removeZoomListener(this);
zoomManager = null;
}
if (viewer != null) {
Control control = viewer.getControl();
if (control != null && !control.isDisposed()) {
control.removeListener(SWT.Resize, this);
}
viewer = null;
}
}
public void refreshEditor() {
if (editor == null || editor.isClosed())
return;
updateEditorFont();
updateEditorBounds();
ensureEditorVisible();
}
private void updateEditorBounds() {
if (editor == null || editor.isClosed())
return;
Rectangle r = getPreferredBounds();
if (r == null)
return;
translateToControl(r);
StyledText textWidget = editor.getTextViewer().getTextWidget();
org.eclipse.swt.graphics.Rectangle trim = textWidget.computeTrim(0, 0,
0, 0);
int widthHint;
if (getPrefWidth() < 0) {
widthHint = -1;
} else {
widthHint = Math.max(1, (int) (getPrefWidth() * getScale())
- trim.width);
}
org.eclipse.swt.graphics.Point prefSize = textWidget.computeSize(
widthHint, -1);
int prefWidth = prefSize.x - trim.width;
int prefHeight = prefSize.y - trim.height;
Rectangle clientArea = getViewerClientArea();
int maxWidth = -1;
int maxHeight = -1;
if (clientArea != null) {
org.eclipse.swt.graphics.Rectangle trim2 = editor.computeTrim(0, 0,
0, 0);
int gap = (int) (GAP * getScale());
maxWidth = clientArea.width - trim2.width - gap;
maxHeight = clientArea.height - trim2.height - gap;
if (getMaxWidth() > 0 && getMaxWidth() < maxWidth) {
maxWidth = (int) (getMaxWidth() * getScale());
}
if (prefWidth > maxWidth) {
prefWidth = maxWidth;
}
if (prefHeight > maxHeight) {
prefHeight = maxHeight;
}
}
int minWidth;
if (getMinWidth() > 0) {
minWidth = (int) (getMinWidth() * getScale());
} else {
if (getPrefWidth() < 0) {
minWidth = r.width;
} else {
minWidth = (int) (20 * getScale());
}
}
if (prefWidth < minWidth) {
prefWidth = minWidth;
}
prefSize = textWidget.computeSize(prefWidth, -1);
prefSize.x -= trim.width;
prefSize.y -= trim.height;
if (getPrefWidth() < 0) {
int f = (int) (getExpansion() * getScale());
prefWidth = (prefSize.x + f - 1) / f * f + f * 2 / 5;
}
prefHeight = prefSize.y;
if (maxWidth > 0) {
prefWidth = Math.min(maxWidth, prefWidth);
}
if (maxHeight > 0) {
prefHeight = Math.min(maxHeight, prefHeight);
}
if (extendsBidirectionalHorizontal) {
r.x += (r.width - prefWidth) / 2;
}
r.y += (r.height - prefHeight) / 2;
r.width = prefWidth;
r.height = prefHeight;
editor.getControl().setBounds(
editor.computeTrim(r.x, r.y, r.width, r.height));
updateScrollBars(prefWidth < prefSize.x, prefHeight < prefSize.y);
}
protected Rectangle getViewerClientArea() {
if (viewer != null) {
return new Rectangle(viewer.getCanvas().getClientArea());
}
return null;
}
private void updateScrollBars(boolean hVisible, boolean vVisible) {
if (editor == null)
return;
StyledText textWidget = editor.getTextViewer().getTextWidget();
ScrollBar hBar = textWidget.getHorizontalBar();
if (hBar != null) {
hBar.setVisible(hVisible);
}
ScrollBar vBar = textWidget.getVerticalBar();
if (vBar != null) {
vBar.setVisible(vVisible);
}
}
private void translateToControl(Rectangle r) {
r.scale(getScale());
Viewport viewport = getViewport();
if (viewport != null) {
Point viewLocation = viewport.getViewLocation();
r.translate(-viewLocation.x, -viewLocation.y);
}
}
protected Viewport getViewport() {
if (viewer != null) {
return viewer.getCanvas().getViewport();
}
return null;
}
private void updateEditorFont() {
if (editor == null)
return;
ITextViewer textViewer = editor.getTextViewer();
if (textViewer == null)
return;
Font font = getPreferredFont();
if (font != null && !font.isDisposed()) {
textViewer.getTextWidget().setFont(font);
}
}
protected abstract Font getPreferredFont();
protected StyleRange createStyleRange(TextStyle style, IDocument document) {
if (style == null || document == null)
return new StyleRange();
StyleRange range = new StyleRange(0, document.getLength(), null, null);
range.font = style.font;
range.strikeout = style.strikeout;
range.underline = style.underline;
return range;
}
protected void ensureEditorVisible() {
if (editor != null && viewer instanceof GraphicalViewer) {
Rectangle r = new Rectangle(editor.getControl().getBounds());
int gap = (int) (GAP / getScale());
r.expand(gap, gap);
((GraphicalViewer) viewer).ensureControlVisible(r);
}
}
protected double getScale() {
if (zoomManager != null)
return zoomManager.getScale();
return 1;
}
protected abstract Rectangle getPreferredBounds();
public void editingStarted(TextEvent e) {
refreshEditor();
}
public void textChanged(TextEvent e) {
refreshEditor();
}
public void editingAboutToCancel(TextEvent e) {
}
public void editingAboutToFinish(TextEvent e) {
}
public void editingAboutToStart(TextEvent e) {
}
public void editingCanceled(TextEvent e) {
}
public void editingFinished(TextEvent e) {
}
public void textAboutToChange(TextEvent e) {
}
public void scaleChanged(ZoomObject source, double oldValue, double newValue) {
refreshEditor();
}
public void handleEvent(Event event) {
refreshEditor();
}
}