/*******************************************************************************
* Copyright (c) 2007-2011, G. Weirich and Elexis
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* bogdan314 - initial implementation
* Sponsor:
* G. Weirich
******************************************************************************/
package ch.elexis.base.textplugin;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.PaintObjectEvent;
import org.eclipse.swt.custom.PaintObjectListener;
import org.eclipse.swt.custom.StyleRange;
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.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.GlyphMetrics;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.printing.Printer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
public class Page extends EStyledText implements MouseListener, MouseMoveListener, KeyListener,
VerifyListener, PaintObjectListener {
private boolean altDown;
private boolean mouseDown;
private Point mouseScreenLoc;
private Point mouseWidgetLoc;
protected List<TextBox> textBoxes;
private int state;
private boolean highlight;
private final List<Integer> offsets;
private final List<Control> controls;
private final static int STATE_NONE = 0;
private final static int STATE_MOVE = 1;
private final static int STATE_RESIZE_LEFT = 2;
private final static int STATE_RESIZE_RIGHT = 3;
private final static int STATE_RESIZE_UP = 4;
private final static int STATE_RESIZE_DOWN = 5;
private static final int MARGIN = 1;
public Page(final Composite parent, final ElexisEditor editor){
super(parent, editor, SWT.BORDER | SWT.WRAP);
setSize(602, 800);
textBoxes = new ArrayList<TextBox>();
setBackground(getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
addKeyListener(this);
addMouseListener(this);
addMouseMoveListener(this);
mouseScreenLoc = mouseWidgetLoc = new Point(0, 0);
addVerifyListener(this);
addPaintObjectListener(this);
offsets = new ArrayList<Integer>();
controls = new ArrayList<Control>();
}
public TextBox insertBox(){
// remove boxes temporary
Composite comp = new Composite(this, SWT.NONE);
for (Iterator<TextBox> it = textBoxes.iterator(); it.hasNext();) {
TextBox old = it.next();
old.setParent(comp);
}
TextBox box = new TextBox(this, editor);
// add them back
Control[] controlls = comp.getChildren();
for (int i = controlls.length - 1; i >= 0; i--) {
controlls[i].setParent(this);
}
comp.dispose();
box.setHighlight(highlight);
box.forceFocus();
textBoxes.add(box);
box.addKeyListener(this);
box.addMouseListener(this);
box.addMouseMoveListener(this);
return box;
}
public void setHightlight(final boolean b){
this.highlight = b;
for (Iterator<TextBox> it = textBoxes.iterator(); it.hasNext();) {
TextBox box = it.next();
box.setHighlight(b);
}
}
public void mouseDoubleClick(final MouseEvent e){}
public void mouseDown(final MouseEvent e){
mouseDown = true;
mouseWidgetLoc = new Point(e.x, e.y);
mouseScreenLoc = getDisplay().getCursorLocation();
}
public void mouseUp(final MouseEvent e){
mouseDown = false;
}
public void mouseMove(final MouseEvent e){
mouseWidgetLoc = new Point(e.x, e.y);
Object o = e.getSource();
Control control = (Control) o;
if (mouseDown && (control instanceof TextBox)) {
TextBox box = (TextBox) control;
switch (state) {
case STATE_MOVE:
moveBox(box);
break;
case STATE_RESIZE_LEFT:
resizeLeft(box);
break;
case STATE_RESIZE_RIGHT:
resizeRight(box);
break;
case STATE_RESIZE_UP:
resizeUp(box);
break;
case STATE_RESIZE_DOWN:
resizeDown(box);
break;
}
} else {
updateState(control);
}
}
private void resizeDown(final TextBox box){
Point mouseNewLoc = getDisplay().getCursorLocation();
int diff = mouseNewLoc.y - mouseScreenLoc.y;
Rectangle bounds = box.getBounds();
bounds.height += diff;
if ((bounds.height > TextBox.MIN_SIZE) && (bounds.y + bounds.height < getSize().y)) {
box.setBounds(bounds);
mouseScreenLoc = mouseNewLoc;
}
}
private void resizeUp(final TextBox box){
Point mouseNewLoc = getDisplay().getCursorLocation();
int diff = mouseNewLoc.y - mouseScreenLoc.y;
Rectangle bounds = box.getBounds();
bounds.y += diff;
bounds.height -= diff;
if ((bounds.y > 0) && (bounds.height > TextBox.MIN_SIZE)) {
box.setBounds(bounds);
mouseScreenLoc = mouseNewLoc;
}
}
private void resizeRight(final TextBox box){
Point mouseNewLoc = getDisplay().getCursorLocation();
int diff = mouseNewLoc.x - mouseScreenLoc.x;
Rectangle bounds = box.getBounds();
bounds.width += diff;
if ((bounds.width > TextBox.MIN_SIZE) && (bounds.x + bounds.width < getSize().x)) {
box.setBounds(bounds);
mouseScreenLoc = mouseNewLoc;
}
}
private void resizeLeft(final TextBox box){
Point mouseNewLoc = getDisplay().getCursorLocation();
int diff = mouseNewLoc.x - mouseScreenLoc.x;
Rectangle bounds = box.getBounds();
bounds.x += diff;
bounds.width -= diff;
if ((bounds.x > 0) && (bounds.width > TextBox.MIN_SIZE)) {
box.setBounds(bounds);
mouseScreenLoc = mouseNewLoc;
}
}
private void moveBox(final TextBox box){
Point loc = box.getLocation();
Point size = getSize();
Point boxSize = box.getSize();
Point mouseNewLoc = getDisplay().getCursorLocation();
int x = Math.max(0, loc.x + mouseNewLoc.x - mouseScreenLoc.x);
int y = Math.max(0, loc.y + mouseNewLoc.y - mouseScreenLoc.y);
x = Math.min(size.x - boxSize.x, x);
y = Math.min(size.y - boxSize.y, y);
box.forceLocation(x, y);
mouseScreenLoc = mouseNewLoc;
}
private void updateState(final Control control){
if (altDown) {
state = checkResize(control);
if (state == STATE_NONE) {
state = STATE_MOVE;
}
} else {
state = STATE_NONE;
}
int cursor = SWT.CURSOR_ARROW;
switch (state) {
case STATE_MOVE:
cursor = SWT.CURSOR_HAND;
break;
case STATE_RESIZE_LEFT:
case STATE_RESIZE_RIGHT:
cursor = SWT.CURSOR_SIZEW;
break;
case STATE_RESIZE_DOWN:
case STATE_RESIZE_UP:
cursor = SWT.CURSOR_SIZEN;
break;
}
Cursor mouseCursor = getDisplay().getSystemCursor(cursor);
for (Iterator<TextBox> it = textBoxes.iterator(); it.hasNext();) {
TextBox box = it.next();
box.setCursor(mouseCursor);
}
}
private int checkResize(final Control control){
int diff = control.getBorderWidth() + 4;
if (mouseWidgetLoc.x < diff) {
return STATE_RESIZE_LEFT;
} else if (mouseWidgetLoc.x > control.getSize().x - 2 * diff) {
return STATE_RESIZE_RIGHT;
} else if (mouseWidgetLoc.y < diff) {
return STATE_RESIZE_UP;
} else if (mouseWidgetLoc.y > control.getSize().y - 2 * diff) {
return STATE_RESIZE_DOWN;
}
return STATE_NONE;
}
public void keyPressed(final KeyEvent e){
altDown = e.keyCode == SWT.ALT;
updateState((Control) e.getSource());
}
public void keyReleased(final KeyEvent e){
if (e.keyCode == SWT.ALT) {
altDown = false;
}
updateState((Control) e.getSource());
}
public void clear(){
setText("");
for (Iterator<TextBox> it = textBoxes.iterator(); it.hasNext();) {
TextBox box = it.next();
box.dispose();
}
textBoxes.clear();
}
@Override
public void readFrom(final DataInputStream in) throws IOException{
clear();
int boxesCount = in.readInt();
super.readFrom(in);
for (int i = 0; i < boxesCount; i++) {
TextBox box = editor.insertBox();
box.readFrom(in);
}
}
@Override
public void writeTo(final DataOutputStream out) throws IOException{
out.writeInt(textBoxes.size());
super.writeTo(out);
for (Iterator<TextBox> it = textBoxes.iterator(); it.hasNext();) {
TextBox box = it.next();
box.writeTo(out);
}
}
public void addTable(final Table table, final int offset){
offsets.add(offset);
controls.add(table);
StyleRange style = new StyleRange();
style.start = offset;
style.length = 1;
Rectangle rect = table.getBounds();
int ascent = 2 * rect.height / 3;
int descent = rect.height - ascent;
style.metrics =
new GlyphMetrics(ascent + MARGIN, descent + MARGIN, rect.width + 2 * MARGIN);
setStyleRange(style);
table.addListener(SWT.MouseWheel, new Listener() {
public void handleEvent(final Event event){
TableItem item = table.getItem(new Point(event.x, event.y));
int x = event.x;
int index = 0;
TableColumn column = table.getColumn(index);
int w = column.getWidth();
while (x > w) {
index++;
if (index >= table.getColumnCount()) {
return;
}
column = table.getColumn(index);
w += column.getWidth();
}
int inc = (event.count > 0 ? 5 : -5);
if (column.getWidth() + inc < 5) {
return;
}
column.setWidth(column.getWidth() + inc);
int rowcount = table.getItemCount();
int columncount = table.getColumnCount();
Rectangle rect = table.getItem(rowcount - 1).getBounds(columncount - 1);
table.setSize(rect.x + rect.width + 5, rect.y + rect.height + 5);
rect = table.getBounds();
StyleRange style = new StyleRange();
style.start = offset;
style.length = 1;
int ascent = 2 * rect.height / 3;
int descent = rect.height - ascent;
style.metrics =
new GlyphMetrics(ascent + MARGIN, descent + MARGIN, rect.width + 2 * MARGIN);
setStyleRange(style);
}
});
}
public void verifyText(final VerifyEvent e){
int start = e.start;
int replaceCharCount = e.end - e.start;
int newCharCount = e.text.length();
int index = 0;
for (Iterator<Integer> it = offsets.iterator(); it.hasNext(); index++) {
int offset = it.next();
if ((start <= offset) && (offset < start + replaceCharCount)) {
// this widget is being deleted from the text
Control control = controls.get(index);
if ((control != null) && !control.isDisposed()) {
control.dispose();
control = null;
}
offset = -1;
}
if ((offset != -1) && (offset >= start)) {
offset += newCharCount - replaceCharCount;
}
offsets.set(index, offset);
}
}
public void paintObject(final PaintObjectEvent event){
StyleRange style = event.style;
int start = style.start;
int index = 0;
for (Iterator<Integer> it = offsets.iterator(); it.hasNext(); index++) {
int offset = it.next();
if (start == offset) {
Control control = controls.get(index);
Point pt = control.getSize();
int x = event.x + MARGIN;
int y = event.y + event.ascent - 2 * pt.y / 3;
control.setLocation(x, y);
break;
}
}
}
private Rectangle centerRect(final Point containerSize, final Point childSize){
int x = (containerSize.x - childSize.x) / 2;
int y = (containerSize.y - childSize.y) / 2;
Rectangle rect = new Rectangle(x, y, childSize.x, childSize.y);
return rect;
}
public void print(final Printer printer, final GC gc){
Rectangle clientArea = printer.getClientArea();
Rectangle trim = printer.computeTrim(0, 0, 0, 0);
Point dpi = printer.getDPI();
int leftMargin = dpi.x + trim.x; // one inch from left side of paper
int rightMargin = clientArea.width - dpi.x + trim.x + trim.width; // one inch from right
// side of paper
int topMargin = dpi.y + trim.y; // one inch from top edge of paper
int bottomMargin = clientArea.height - dpi.y + trim.y + trim.height; // one inch from
// bottom edge of
int x = leftMargin;
int ex = leftMargin;
int y = topMargin;
StringBuffer line = new StringBuffer();
FontMetrics fm = gc.getFontMetrics();
List<StyleRange> styles = getStyles();
for (Iterator it = styles.iterator(); it.hasNext();) {
StyleRange style = (StyleRange) it.next();
gc.setFont(new Font(printer, style.font.getFontData()[0].getName(), style.font
.getFontData()[0].getHeight(), style.fontStyle));
fm = gc.getFontMetrics();
String text = getText(style.start, style.start + style.length - 1);
List<String> words = split(text);
Iterator<String> wit = words.iterator();
String word = wit.hasNext() ? wit.next() : null;
while (word != null) {
if (word.equals("\r")) {
gc.drawString(line.toString(), leftMargin, y);
line.setLength(0);
x = ex = leftMargin;
y += fm.getHeight();
}
word = (ex == leftMargin ? word.trim() : word);
ex += gc.stringExtent(word).x;
if (ex < rightMargin) {
line.append(word);
word = wit.hasNext() ? wit.next() : null;
} else {
gc.drawString(line.toString(), x, y);
line.setLength(0);
x = ex = leftMargin;
y += fm.getHeight();
}
}
if (line.length() > 0) {
gc.drawString(line.toString(), x, y);
// x = gc.stringExtent(line.toString()).x;
// y += fm.getHeight();
x = ex;
line.setLength(0);
}
}
}
private List<String> split(final String text){
List<String> words = new ArrayList<String>();
String delims = " \n\r";
boolean previosWordIsLineReturn = false;
for (StringTokenizer st = new StringTokenizer(text, delims, true); st.hasMoreTokens();) {
String word = st.nextToken();
if (word.equals("\n") || word.equals("\r")) {
if (!previosWordIsLineReturn) {
words.add(word);
previosWordIsLineReturn = false;
} else {
previosWordIsLineReturn = true;
}
} else {
words.add(word);
previosWordIsLineReturn = false;
}
}
return words;
}
}