/* ******************************************************************************
* 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.tabfolder;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Region;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.xmind.ui.resources.ColorUtils;
/**
* @author Brian Sun
*/
public class PageMoveHelper implements Listener {
private static final int INSERTION_MARK_WIDTH = 5;
private static final int W = 15;
private static final int H = 14;
private static final int[] SHAPE = new int[] { 4, 0, //
W - 4, 0, //
W - 4, H - 8, //
W - 1, H - 8, //
W / 2, H, //
0, H - 8,//
4, H - 8 };
private static final int[] BORDER = new int[] { 4, 0, //
W - 5, 0, //
W - 5, H - 8, //
W - 2, H - 8, //
W / 2, H - 2, //
1, H - 8,//
4, H - 8 };
private CTabFolder tabFolder;
private CTabItem sourceItem = null;
private int insertionMarkIndex = -1;
private List<IPageMoveListener> listeners = null;
private Shell arrow = null;
private Region arrowShape = null;
/**
* @param parent
*/
public PageMoveHelper(CTabFolder tabFolder) {
this.tabFolder = tabFolder;
hookTabFolder();
}
private void hookTabFolder() {
tabFolder.addListener(SWT.Paint, this);
tabFolder.addListener(SWT.MouseDown, this);
tabFolder.addListener(SWT.MouseUp, this);
tabFolder.addListener(SWT.MouseMove, this);
}
private int findInsertionMarkIndex(int x, int y) {
int resultIndex = -1;
CTabItem item = tabFolder.getItem(new Point(x, y));
if (item != null) {
resultIndex = tabFolder.indexOf(item);
int division = calculateDivision(item, y);
if (x >= division)
resultIndex++;
} else if (tabFolder.getItemCount() > 0) {
int division = calculateDivision(0, y);
if (division >= 0 && x < division) {
resultIndex = 0;
} else {
int lastIndex = tabFolder.getItemCount() - 1;
division = calculateDivision(lastIndex, y);
if (division >= 0 && x > division)
resultIndex = lastIndex + 1;
}
}
return resultIndex;
}
private int calculateDivision(int index, int y) {
return calculateDivision(tabFolder.getItem(index), y);
}
private int calculateDivision(CTabItem item, int y) {
Rectangle r = item.getBounds();
if (y >= r.y && y < r.y + r.width)
return r.x + r.width * 2 / 3;
return -1;
}
private void cancel() {
disposeArrow();
tabFolder.setCursor(null);
sourceItem = null;
insertionMarkIndex = -1;
}
public void addListener(IPageMoveListener l) {
if (listeners == null)
listeners = new ArrayList<IPageMoveListener>();
listeners.add(l);
}
public void removeListener(IPageMoveListener l) {
listeners.remove(l);
}
protected void firePageMoved(final int fromIndex, final int toIndex) {
if (listeners == null)
return;
for (final Object l : listeners.toArray()) {
SafeRunner.run(new SafeRunnable() {
public void run() throws Exception {
((IPageMoveListener) l).pageMoved(fromIndex, toIndex);
}
});
}
}
public void handleEvent(Event event) {
switch (event.type) {
case SWT.Paint:
handlePaint(event);
break;
case SWT.MouseDown:
handleMouseDown(event);
break;
case SWT.MouseUp:
handleMouseUp(event);
break;
case SWT.MouseMove:
handleMouseMove(event);
break;
case SWT.Dispose:
handleDispose(event);
break;
}
}
private void handlePaint(Event e) {
if (insertionMarkIndex >= 0) {
paintInsertionMarkAtIndex(insertionMarkIndex, e.gc, e.display);
}
}
private Point calcInsertionLocation(int insertIndex, boolean toDisplay) {
int lastIndex = tabFolder.getItemCount() - 1;
if (insertIndex >= 0 && insertIndex <= lastIndex) {
CTabItem item = tabFolder.getItem(insertIndex);
Rectangle r = item.getBounds();
if (toDisplay)
return tabFolder.toDisplay(r.x, r.y);
return new Point(r.x, r.y);
} else if (insertIndex > lastIndex) {
CTabItem item = tabFolder.getItem(lastIndex);
Rectangle r = item.getBounds();
if (toDisplay)
return tabFolder.toDisplay(r.x + r.width - 1, r.y);
return new Point(r.x + r.width - 1, r.y);
}
return null;
}
private void paintInsertionMarkAtIndex(int insertIndex, GC gc,
Display display) {
Point loc = calcInsertionLocation(insertIndex, false);
if (loc != null) {
paintInsertionMarkAt(loc.x, loc.y, tabFolder.getTabHeight(), gc,
display);
}
}
private void paintInsertionMarkAt(int x, int y, int height, GC gc,
Display display) {
gc.setAlpha(0xd0);
gc.setBackground(ColorUtils.getColor("#0060d0")); //$NON-NLS-1$
gc.fillRectangle(x - INSERTION_MARK_WIDTH / 2, y, INSERTION_MARK_WIDTH,
height);
}
private void handleMouseDown(Event e) {
sourceItem = tabFolder.getItem(new Point(e.x, e.y));
}
private void handleMouseUp(Event e) {
if (sourceItem != null && insertionMarkIndex >= 0) {
int from = tabFolder.indexOf(sourceItem);
int to = insertionMarkIndex;//tabFolder.indexOf( over );
if (insertionMarkIndex > tabFolder.getSelectionIndex())
to--;
if (from != to) {
firePageMoved(from, to);
}
tabFolder.setSelection(to);
tabFolder.redraw();
}
cancel();
}
private void handleMouseMove(Event e) {
if ((e.stateMask & SWT.BUTTON_MASK) != 0 && sourceItem != null) {
tabFolder.setCursor(Display.getCurrent().getSystemCursor(
SWT.CURSOR_HAND));
int oldInsertIndex = insertionMarkIndex;
int newInsertIndex = findInsertionMarkIndex(e.x, e.y);
if (newInsertIndex != oldInsertIndex) {
insertionMarkIndex = newInsertIndex;
updateArrow(insertionMarkIndex);
tabFolder.redraw();
}
} else {
cancel();
}
}
private void handleDispose(Event e) {
disposeArrow();
disposeArrowShape();
}
private void updateArrow(int insertIndex) {
createArrow();
Point loc = calcInsertionLocation(insertIndex, true);
if (loc != null) {
arrow.setVisible(true);
arrow.setLocation(loc.x - W / 2, loc.y - H - 3);
} else {
arrow.setVisible(false);
}
}
private void createArrow() {
if (arrow != null && !arrow.isDisposed())
return;
arrow = new Shell(tabFolder.getShell(), SWT.ON_TOP | SWT.NO_TRIM);
arrow.setRegion(getArrowShape());
arrow.setBackground(ColorUtils.getColor("#4088ff")); //$NON-NLS-1$
arrow.setSize(W + 1, H + 1);
arrow.addListener(SWT.Paint, new Listener() {
public void handleEvent(Event event) {
GC gc = event.gc;
gc.setForeground(ColorUtils.getColor("#000080")); //$NON-NLS-1$
gc.setLineStyle(SWT.LINE_SOLID);
gc.setLineWidth(1);
gc.drawPolygon(BORDER);
}
});
}
private Region getArrowShape() {
if (arrowShape == null || arrowShape.isDisposed()) {
arrowShape = new Region(tabFolder.getDisplay());
arrowShape.add(SHAPE);
}
return arrowShape;
}
private void disposeArrow() {
if (arrow != null) {
arrow.dispose();
arrow = null;
}
}
private void disposeArrowShape() {
if (arrowShape != null) {
arrowShape.dispose();
arrowShape = null;
}
}
}