From: jihye424.kim Date: Tue, 11 Aug 2015 09:58:11 +0000 (+0900) Subject: Table Widget: add table widget sources X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=fbe4ad3ace0af89559056b32c6bd997fbb921b7d;p=sdk%2Femulator%2Femulator-manager.git Table Widget: add table widget sources - widget_src: custom scroll bar sources from sacrilege-custom-swt-scrollbar(www.codeaffine.com) Change-Id: I099c6027caef1f2060a70093663a895d4f5a370a Signed-off-by: jihye424.kim --- diff --git a/.classpath b/.classpath index 75f1945..2df9e54 100644 --- a/.classpath +++ b/.classpath @@ -1,6 +1,7 @@ + diff --git a/resource/table/btn_checkbox_checked_hover.png b/resource/table/btn_checkbox_checked_hover.png new file mode 100644 index 0000000..4581d09 Binary files /dev/null and b/resource/table/btn_checkbox_checked_hover.png differ diff --git a/resource/table/btn_checkbox_checked_nml.png b/resource/table/btn_checkbox_checked_nml.png new file mode 100644 index 0000000..37ee0e4 Binary files /dev/null and b/resource/table/btn_checkbox_checked_nml.png differ diff --git a/resource/table/btn_checkbox_unchecked_hover.png b/resource/table/btn_checkbox_unchecked_hover.png new file mode 100644 index 0000000..58a33bc Binary files /dev/null and b/resource/table/btn_checkbox_unchecked_hover.png differ diff --git a/resource/table/btn_checkbox_unchecked_nml.png b/resource/table/btn_checkbox_unchecked_nml.png new file mode 100644 index 0000000..9ba0083 Binary files /dev/null and b/resource/table/btn_checkbox_unchecked_nml.png differ diff --git a/resource/table/down.png b/resource/table/down.png new file mode 100644 index 0000000..6da26c6 Binary files /dev/null and b/resource/table/down.png differ diff --git a/resource/table/up.png b/resource/table/up.png new file mode 100644 index 0000000..ce162c3 Binary files /dev/null and b/resource/table/up.png differ diff --git a/src/org/tizen/emulator/manager/ui/table/CheckBoxButton.java b/src/org/tizen/emulator/manager/ui/table/CheckBoxButton.java new file mode 100644 index 0000000..5b36502 --- /dev/null +++ b/src/org/tizen/emulator/manager/ui/table/CheckBoxButton.java @@ -0,0 +1,189 @@ +/* + * Emulator Manager + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * JiHye Kim + * Minkee Lee + * SeokYeon Hwang + * Sangho Park + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +package org.tizen.emulator.manager.ui.table; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.TypedListener; + +public class CheckBoxButton extends Canvas { + private Image image = null; + private Image imageHover = null; + private Image checkedImage = null; + private Image checkedImageHover = null; + private boolean isSelected = false; + private boolean isHover = false; + + private Color checkedBackground = null; + + public CheckBoxButton(Composite parent, int style) { + super(parent, SWT.DOUBLE_BUFFERED | style); + + initButton(); + addListeners(); + } + + protected void initButton() { + image = ImageResources.CHECKBOX_UNCHECKED_NML; + imageHover = ImageResources.CHECKBOX_UNCHECKED_HOVER; + checkedImage = ImageResources.CHECKBOX_CHECKED_NML; + checkedImageHover = ImageResources.CHECKBOX_CHECKED_HOVER; + } + + private void addListeners() { + addPaintListener(paintListener); + addListener(SWT.MouseDown, mouseListener); + addListener(SWT.MouseUp, mouseListener); + addListener(SWT.MouseEnter, mouseListener); + addListener(SWT.MouseExit, mouseListener); + addDisposeListener(disposeListener); + } + + @Override + public Point computeSize(int wHint, int hHint, boolean changed) { + checkWidget(); + int width = 0, height = 0; + + Rectangle bounds = image.getBounds(); + width = bounds.width; + height = bounds.height; + + if (wHint != SWT.DEFAULT) + width = wHint; + + if (hHint != SWT.DEFAULT) + height = hHint; + + return new Point(width, height); + } + + private DisposeListener disposeListener = new DisposeListener() { + + @Override + public void widgetDisposed(DisposeEvent arg0) { + Helper.tryDispose(image, imageHover, checkedImage, checkedImageHover, checkedBackground); + } + }; + + private PaintListener paintListener = new PaintListener() { + + @Override + public void paintControl(PaintEvent e) { + Rectangle rect = ((Canvas) e.widget).getClientArea(); + + if (isSelected && checkedBackground != null) { + e.gc.setBackground(checkedBackground); + } else { + e.gc.setBackground(getBackground()); + } + e.gc.fillRectangle(rect); + + Image img = image; + if (isSelected) { + img = isHover ? checkedImageHover : checkedImage; + } else { + img = isHover ? imageHover : image; + } + + Rectangle bounds = img.getBounds(); + int x = rect.x + (rect.width - bounds.width) / 2; + int y = rect.y + (rect.height - bounds.height) / 2; + + if (null != img) { + e.gc.drawImage(img, x, y); + } + } + }; + + public void addSelectionListener(SelectionListener listener) { + checkWidget(); + addListener(SWT.Selection, new TypedListener(listener)); + addListener(SWT.DefaultSelection, new TypedListener(listener)); + } + + public void removeSelectionListener(SelectionListener listener) { + checkWidget(); + removeListener(SWT.Selection, listener); + removeListener(SWT.DefaultSelection, listener); + } + + protected Listener mouseListener = new Listener() { + + @Override + public void handleEvent(Event event) { + if (!isEnabled()) { + return; + } + + CheckBoxButton button = (CheckBoxButton) event.widget; + if (event.type == SWT.MouseDown) { + setSelection(!isSelection()); + button.notifyListeners(SWT.Selection, new Event()); + } else if (event.type == SWT.MouseEnter) { + isHover = true; + redraw(); + } else if (event.type == SWT.MouseExit) { + isHover = false; + redraw(); + } + } + }; + + public void setSelection(boolean selected) { + checkWidget(); + isSelected = selected; + } + + public boolean isSelection() { + checkWidget(); + return isSelected; + } + + public void setSelectedBackground(Color color) { + checkWidget(); + if (color == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + this.checkedBackground = color; + } +} diff --git a/src/org/tizen/emulator/manager/ui/table/ColorResources.java b/src/org/tizen/emulator/manager/ui/table/ColorResources.java new file mode 100644 index 0000000..3b36c6e --- /dev/null +++ b/src/org/tizen/emulator/manager/ui/table/ColorResources.java @@ -0,0 +1,66 @@ +/* + * Emulator Manager + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * JiHye Kim + * Minkee Lee + * SeokYeon Hwang + * Sangho Park + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +package org.tizen.emulator.manager.ui.table; + +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Display; + +public final class ColorResources { + // Table Column + public static Color COLUMN_FONT_COLOR + = new Color(Display.getCurrent(), new RGB(41, 41, 41)); + public static Color COLUMN_BG_COLOR + = new Color(Display.getCurrent(), new RGB(230, 231, 233)); + + // Table Item + public static Color ITEM_FONT_COLOR + = new Color(Display.getCurrent(), new RGB(96, 115, 124)); + public static Color ITEM_SELECTED_FONT_COLOR + = new Color(Display.getCurrent(), new RGB(0, 0, 0)); + + public static Color ITEM_BG_COLOR_ODD + = new Color(Display.getCurrent(), new RGB(240, 241, 243)); + public static Color ITEM_BG_COLOR_EVEN + = new Color(Display.getCurrent(), new RGB(255, 255, 255)); + public static Color ITEM_HOVER_BG_COLOR + = new Color(Display.getCurrent(), new RGB(226, 246, 255)); + public static Color ITEM_SELECTED_BG_COLOR + = new Color(Display.getCurrent(), new RGB(204, 239, 255)); + public static Color ITEM_FOCUST_OUT_SELECTED_BG_COLOR + = new Color(Display.getCurrent(), new RGB(200, 200, 200)); + + public static Color COLUMN_CONTENTS_BG_COLOR + = new Color(Display.getCurrent(), new RGB(204, 239, 250)); // blue + + public static Color DEFAULT_STROKE + = new Color(Display.getCurrent(), new RGB(194, 208, 211)); +} diff --git a/src/org/tizen/emulator/manager/ui/table/FontResources.java b/src/org/tizen/emulator/manager/ui/table/FontResources.java new file mode 100644 index 0000000..63660f5 --- /dev/null +++ b/src/org/tizen/emulator/manager/ui/table/FontResources.java @@ -0,0 +1,134 @@ +/* + * Emulator Manager + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * JiHye Kim + * Minkee Lee + * SeokYeon Hwang + * Sangho Park + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +package org.tizen.emulator.manager.ui.table; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.widgets.Display; + +public class FontResources { + public static Font COLUMN_FONT = setSizeDefaultFont(12); + public static Font ITEM_FONT = setSizeDefaultFont(11); + + private static boolean isMac = false; + private static boolean isWin = false; + private static boolean isLinux = false; + static { + + if (System.getProperty("os.name").toLowerCase().indexOf("linux") > -1) { + isLinux = true; + } else if (System.getProperty("os.name").toLowerCase().indexOf("win") > -1) { + isWin = true; + } else if (System.getProperty("os.name").toLowerCase().indexOf("mac") > -1) { + isMac = true; + } + } + + private static Font defaultFont = null; + + static { + defaultFont = getDADefaultFont(); + } + + public static Font getDADefaultFont() { + if (defaultFont == null) { + String fontName = null; + if (isLinux) { + fontName = "Dejavu Sans"; + } else if (isWin) { + fontName = "Verdana"; + } else if (isMac) { + fontName = "Lucida Grande"; + } else { + defaultFont = Display.getCurrent().getSystemFont(); + return defaultFont; + } + defaultFont = isMac + ? new Font(Display.getCurrent(), new FontData[] { new FontData(fontName, 11, SWT.NORMAL) }) + : new Font(Display.getCurrent(), new FontData[] { new FontData(fontName, 9, SWT.NORMAL) }); + } + + return defaultFont; + } + + public static Font setSizeDefaultFont(int size) { + if (defaultFont == null) { + defaultFont = getDADefaultFont(); + } + + if (isMac) { + size = size + 2; + } + + FontData[] fontData = defaultFont.getFontData(); + for (int i = 0; i < fontData.length; i++) { + fontData[i].setHeight(size); + } + return new Font(Display.getCurrent(), fontData); + } + + public static Font setStyleDefaultFont(int style) { + if (defaultFont == null) { + defaultFont = getDADefaultFont(); + } + FontData[] fontData = defaultFont.getFontData(); + for (int i = 0; i < fontData.length; i++) { + fontData[i].setStyle(style); + } + return new Font(Display.getCurrent(), fontData); + } + + public static Font setStyleAndSizeDefaultFont(int style, int size) { + if (defaultFont == null) { + defaultFont = getDADefaultFont(); + } + + if (isMac) { + size = size + 2; + } + + FontData[] fontData = defaultFont.getFontData(); + for (int i = 0; i < fontData.length; i++) { + fontData[i].setStyle(style); + fontData[i].setHeight(size); + } + return new Font(Display.getCurrent(), fontData); + } + + public static Font setStyleFont(int style, Font font) { + FontData[] fontData = font.getFontData(); + for (int i = 0; i < fontData.length; i++) { + fontData[i].setStyle(style); + } + return new Font(Display.getCurrent(), fontData); + } +} diff --git a/src/org/tizen/emulator/manager/ui/table/Helper.java b/src/org/tizen/emulator/manager/ui/table/Helper.java new file mode 100644 index 0000000..38cbd19 --- /dev/null +++ b/src/org/tizen/emulator/manager/ui/table/Helper.java @@ -0,0 +1,80 @@ +/* + * Emulator Manager + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * JiHye Kim + * Minkee Lee + * SeokYeon Hwang + * Sangho Park + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +package org.tizen.emulator.manager.ui.table; + +import org.eclipse.swt.widgets.Widget; + +public class Helper { + + public static int checkBits( int style, int int0, int int1, int int2, int int3, int int4, int int5 ) { + int mask = int0 | int1 | int2 | int3 | int4 | int5; + int result = style; + + if( (result & mask) == 0 ) { + result |= int0; + } + + if((result & int0) != 0) { + result = (result & ~mask) | int0; + } + if((result & int1) != 0) { + result = (result & ~mask) | int1; + } + if((result & int2) != 0) { + result = (result & ~mask) | int2; + } + if((result & int3) != 0) { + result = (result & ~mask) | int3; + } + if((result & int4) != 0) { + result = (result & ~mask) | int4; + } + if( (result & int5) != 0) { + result = (result & ~mask) | int5; + } + return result; + } + + public static void tryDispose(final Object... Disposables) { + if (null == Disposables) { + return; + } + + for (Object obj : Disposables) { + if (null == obj) { + continue; + } + if (obj instanceof Widget) { + ((Widget) obj).dispose(); + } + } + } +} diff --git a/src/org/tizen/emulator/manager/ui/table/ImageResources.java b/src/org/tizen/emulator/manager/ui/table/ImageResources.java new file mode 100644 index 0000000..d69f244 --- /dev/null +++ b/src/org/tizen/emulator/manager/ui/table/ImageResources.java @@ -0,0 +1,64 @@ +/* + * Emulator Manager + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * JiHye Kim + * Minkee Lee + * SeokYeon Hwang + * Sangho Park + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +package org.tizen.emulator.manager.ui.table; + +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; + +public class ImageResources { + public static Image CHECKBOX_UNCHECKED_HOVER = getImage("btn_checkbox_unchecked_hover", "png"); + public static Image CHECKBOX_UNCHECKED_NML = getImage("btn_checkbox_unchecked_nml", "png"); + public static Image CHECKBOX_CHECKED_HOVER = getImage("btn_checkbox_checked_hover", "png"); + public static Image CHECKBOX_CHECKED_NML = getImage("btn_checkbox_checked_nml", "png"); + public static Image SORT_INDICATOR_UP = getImage("up", "png"); + public static Image SORT_INDICATOR_DOWN = getImage("down", "png"); + + public static Image getImage(String name, String extension) { + Image i = null; + ClassLoader loader = ImageResources.class.getClassLoader(); + if (loader != null) { + InputStream is = loader.getResourceAsStream("table/" + name + "." + extension); + if (is != null) { + i = new Image(Display.getCurrent(), is); + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + return i; + } +} diff --git a/src/org/tizen/emulator/manager/ui/table/Table.java b/src/org/tizen/emulator/manager/ui/table/Table.java new file mode 100644 index 0000000..611294b --- /dev/null +++ b/src/org/tizen/emulator/manager/ui/table/Table.java @@ -0,0 +1,1417 @@ +/* + * Emulator Manager + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * JiHye Kim + * Minkee Lee + * SeokYeon Hwang + * Sangho Park + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +package org.tizen.emulator.manager.ui.table; + +import java.util.ArrayList; +import java.util.List; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.FormAttachment; +import org.eclipse.swt.layout.FormData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.TypedListener; + +public class Table extends TableScrolledComposite { + private static final int[] EMPTY_SELECTION = new int[0]; + private static final int DEFAULT_COLUMN_HEIGHT = 30; + private static final int DEFAULT_ITEM_HEIGHT = 30; + private static final int DEFAULT_SELECTED_ITEM_HEIGHT = 90; + private static final int CHECK_BOX_WIDTH = 30; + + private TableItem[] items; + private int[] selection; + TableItem currentItem; + private int itemCount; + private int lastSelectedIndex = -1; + private int focusIndex; + private int topIndex = 0; + + private int selectedItemHeight = DEFAULT_SELECTED_ITEM_HEIGHT; + private int itemHeight = DEFAULT_ITEM_HEIGHT; + + private List columns = new ArrayList(); + private int[] columnOrder; + private TableColumn sortColumn; + private int sortDirection; + private int columnHeight = DEFAULT_COLUMN_HEIGHT; + + private boolean headerVisible = true; + private boolean linesVisible = true; + + Composite table = null; + Composite tableItem = null; + Composite tableColumn = null; + private int width = 0; + private int tableWidth = 0; + private int tableHeight = 0; + + private boolean isCtrlKeyOn; + private boolean isShiftKeyOn; + + private boolean isCheck = false; + private int checkBoxWidth = CHECK_BOX_WIDTH; + private CheckBoxButton checkBox; + + public Table(Composite parent, int style) { + super(parent, checkStyle(style)); + + table = new Composite(this, SWT.None); + this.setContent(table); + + tableColumn = new Composite(table, SWT.None); + + tableItem = new Composite(table, SWT.NONE); + tableItem.setBackground(ColorResources.ITEM_SELECTED_BG_COLOR); + this.setTableItemContent(tableItem); + + focusIndex = -1; + sortDirection = SWT.None; + setTableEmpty(); + + if ((style & SWT.CHECK) != 0) { + isCheck = true; + checkBox = new CheckBoxButton(tableColumn, SWT.None); + checkBox.setBackground(ColorResources.COLUMN_BG_COLOR); + checkBox.setSelectedBackground(checkBox.getBackground()); + + } + + addListener(); + } + + private void addListener() { + tableItem.addKeyListener(controlKeyEvent); + tableItem.addPaintListener(tableItemPaintListener); + tableColumn.addPaintListener(tableColumnPaintListener); + + this.addControlListener(controlListener); + //this.addListener(SWT.FocusIn, foucusEventListener); + //this.addListener(SWT.FocusOut, foucusEventListener); + + if (isCheck && checkBox != null) { + checkBox.addSelectionListener(new SelectionListener(){ + @Override + public void widgetSelected(SelectionEvent e) { + if (checkBox.isSelection()) { + _selectAll(true); + } else { + _selectAll(false); + } + } + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + }); + } + } + + public void addSelectionListener (SelectionListener listener) { + checkWidget(); + if (listener == null) { + SWT.error (SWT.ERROR_NULL_ARGUMENT); + } + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Selection,typedListener); + addListener (SWT.DefaultSelection,typedListener); + } + + public void removeSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) { + SWT.error (SWT.ERROR_NULL_ARGUMENT); + } + removeListener (SWT.Selection, listener); + removeListener (SWT.DefaultSelection,listener); + } + + private void setTableEmpty() { + items = new TableItem[4]; + selection = EMPTY_SELECTION; + } + + private static int checkStyle(int style) { + int result = style; + + if( ( style & SWT.NO_SCROLL ) == 0 ) { + result |= SWT.H_SCROLL | SWT.V_SCROLL; + } + + /* GTK is always FULL_SELECTION */ + style |= SWT.FULL_SELECTION; + return Helper.checkBits(result, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0); + } + + public void clear(int index) { + checkWidget (); + if (!(0 <= index && index < itemCount)) { + SWT.error(SWT.ERROR_INVALID_RANGE); + } + TableItem item = items[index]; + if (item != null) { + item.clear (); + } + } + + public void clear(int start, int end){} + public void clear(int[] indices){} + + public void clearAll () { + checkWidget (); + for (TableItem item : items) { + if (item != null) { + item.clear(); + } + } + } + + private void removeItem(int index) { + TableItem item = items[index]; + if (item != null && !item.isDisposed()) { + item.dispose(); + itemCount--; + } else { + destroyItem(null); + } + } + + public void removeAll() { + checkWidget(); + while (itemCount > 0) { + removeItem(0); + } + } + + public void remove(int index) { + checkWidget(); + if (!(0 <= index && index < itemCount)) { + SWT.error(SWT.ERROR_ITEM_NOT_REMOVED); + } + removeItem(index); + } + + public void remove(int start, int end) {} + public void remove(int[] indices) {} + + private void removeFromSelection(int index) { + if (index < 0 || index >= itemCount) { + return; + } + + for (int i = 0; i < selection.length; i++) { + if (index == selection[i]) { + int length = selection.length; + int[] newSelection = new int[length - 1]; + System.arraycopy(selection, 0, newSelection, 0, i); + if (i < length - 1) { + System.arraycopy(selection, i+1, newSelection, i, length - i - 1); + } + selection = newSelection; + break; + } + } + } + + public void deselect(int index) { + checkWidget(); + boolean isNeedReset = selection.length == 1; + removeFromSelection(index); + if (isNeedReset) { + lastSelectedIndex = -1; + this.rearrange(index); + } + } + + public void deselect(int start, int end) { + checkWidget(); + if (start == 0 && end == itemCount - 1) { + deselectAll(); + } else { + int actualStart = Math.max(0, start); + for (int i = actualStart; i <= end; i++) { + removeFromSelection(i); + } + } + } + + public void deselect(int[] indices) {} + + public void deselectAll() { + checkWidget(); + selection = EMPTY_SELECTION; + } + + public void select(int index) { + checkWidget(); + if (index >= 0 && index < itemCount) { + if ((getStyle() & SWT.SINGLE) != 0) { + selection = new int[] {index}; + } else { + if (!isSelected(index)) { + int length = selection.length; + int[] newSelection = new int[length + 1]; + System.arraycopy(selection, 0, newSelection, 0, length); + newSelection[length] = index; + selection = newSelection; + } + } + } + } + + public void select(int start, int end) { + checkWidget(); + if (end < 0 || start > end) { + return; + } + + if ((getStyle() & SWT.SINGLE) != 0 && start != end) { + return; + } + + if (itemCount == 0 || start >= itemCount) { + return; + } + + int adjustedStart = Math.max(0, start); + int adjustedEnd = Math.min(end, itemCount - 1); + if (adjustedStart == 0 && adjustedEnd == itemCount - 1) { + selectAll(); + } else { + for (int i = adjustedStart; i <= adjustedEnd; i++) { + select(i); + } + } + } + + public void select(int[] indices) { + checkWidget(); + if (indices == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + int length = indices.length; + if (length != 0 && ((getStyle() & SWT.SINGLE) == 0 + || length <= 1)) { +// for (int i = length - 1; i >= 0; --i) { +// select(indices[i]); +// } + for (int i = 0; i < length; i++) { + select(indices[i]); + } + } + } + + public void selectAll() { + checkWidget(); + if ((getStyle() & SWT.SINGLE) == 0) { + for (int i = items.length; i > 0; i--) { + select(i-1); + } + } + } + + public boolean isSelected(int index) { + checkWidget(); + boolean result = false; + if (index >= 0 && index < itemCount) { + if (selection != null) { + for (int i = 0; i < selection.length; i++) { + if (selection[i] == index) { + result = true; + break; + } + } + } + } + return result; + } + + /** + * Returns the zero-relative index of the item which is currently selected, + * or -1 if no item is selected. + * @return the index of the selected item + */ + public int getSelectionIndex() { + checkWidget(); + int result = -1; + int topSelectedIndex = -1; + for (int i = 0; i < selection.length; i++) { + if (focusIndex == selection[i]) { + result = selection[i]; + } + if (topSelectedIndex == -1) { + topSelectedIndex = selection[i]; + } else { + topSelectedIndex = Math.min(topSelectedIndex, selection[i]); + } + } + if (result == -1) { + result = topSelectedIndex; + } + return result; + } + + public int getSelectionCount() { + checkWidget(); + return selection.length; + } + + public TableItem[] getSelection() { + checkWidget(); + TableItem[] result = new TableItem[selection.length]; + for (int i = 0; i < selection.length; i++) { + result[i] = _getItem(selection[i]); + } + return result; + } + + private void rearrange(int index) { + if (lastSelectedIndex == -1) { + updateScrollBars(true); + items[index].updateSizeAndLocation(-1); + for (int i = index + 1; i < items.length; i ++) { + if (items[i] != null) { + items[i].updateLocation(-1); + } + } + } else { + if (lastSelectedIndex > index) { + items[index].updateSizeAndLocation(-1); + items[lastSelectedIndex].updateSizeAndLocation(-1); + for (int i = index + 1; i < lastSelectedIndex; i ++) { + if (items[i] != null) { + items[i].updateLocation(-1); + } + } + } else { + items[lastSelectedIndex].updateSizeAndLocation(-1); + items[index].updateSizeAndLocation(-1); + for (int i = lastSelectedIndex + 1; i < index; i ++) { + if (items[i] != null) { + items[i].updateLocation(-1); + } + } + } + } + lastSelectedIndex = index; + } + + private void rearrange() { + if (lastSelectedIndex > -1) { + updateScrollBars(true); + items[lastSelectedIndex].updateSizeAndLocation(-1); + for (int i = lastSelectedIndex + 1; i < items.length; i++) { + if (items[i] != null) { + items[i].updateLocation(-1); + } + } + } + lastSelectedIndex = -1; + } + + public void setSelection(int index) { + checkWidget(); + deselectAll(); + select(index); + if (index < itemCount) { + setFocusIndex(index); + } + rearrange(index); + showSelection(); + } + + public void setSelection(int start, int end) { + checkWidget(); + deselectAll(); + select(start, end); + if (end >= 0 && start <= end + && ((getStyle() & SWT.SINGLE) == 0 || start == end) + && itemCount != 0 + && start < itemCount) { + setFocusIndex(Math.max(0, start)); + } + if (selection.length == 1) { + rearrange(selection[0]); + } else if (selection.length >= 2 && lastSelectedIndex > -1) { + rearrange(); + } + showSelection(); + } + + public void setSelection(TableItem item) { + checkWidget(); + if (item == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + int index = indexOf(item); + if (index < 0) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + + checkWidget(); + deselectAll(); + select(index); + setFocusIndex(index); + rearrange(selection[index]); + showSelection(); + } + + public void setSelection(int[] indices ) { + checkWidget(); + if (indices == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + deselectAll(); + select(indices); + int length = indices.length; + if (length != 0 && ((getStyle() & SWT.SINGLE) == 0 + || length <= 1)) { + setFocusIndex(indices[0]); + } + if (selection.length == 1) { + rearrange(selection[0]); + } else if (selection.length >= 2 && lastSelectedIndex > -1) { + rearrange(); + } + showSelection(); + } + + public void setSelection(TableItem[] items) {} + + private void setFocusIndex(int focusIndex) { + if (focusIndex >= 0) { + this.focusIndex = focusIndex; + } + } + + void _selectAll(boolean select) { + if (select) { + deselectAll(); + selectAll(); + rearrange(); + } else { + deselectAll(); + } + + showSelection(); + checkBox.redraw(); + if (items.length > 0) { + Event e = new Event(); + e.item = items[0]; + e.widget = this; + notifyListeners(SWT.Selection, e); + } + } + + // for table item + void _select(int index) { + if (selection.length == 1) { + lastSelectedIndex = selection[0]; + } + + if (isCheck && checkBox.isSelection()) { + checkBox.setSelection(false); + checkBox.redraw(); + } + + if ((getStyle() & SWT.SINGLE) != 0 + || (!isCtrlKeyOn && !isShiftKeyOn)) { + setSelection(index); + } else if (isCtrlKeyOn) { + if (this.isSelected(index)) { + if (selection.length == 1) { + this.deselect(index); + int[] newSelection = new int[selection.length]; + System.arraycopy(selection, 0, newSelection, 0, selection.length); + setSelection(newSelection); + } else { + this.deselect(index); + int[] newSelection = new int[selection.length]; + System.arraycopy(selection, 0, newSelection, 0, selection.length); + setSelection(newSelection); + } + } else { + int length = selection != null ? selection.length : 0; + if (length == 0) { + setSelection(index); + } else { + int[] newSelection = new int[length + 1]; + System.arraycopy(selection, 0, newSelection, 0, length); + newSelection[length] = index; + setSelection(newSelection); + } + } + } else if (isShiftKeyOn) { + if (selection.length > 0) { + if (index > (selection[selection.length - 1])) { + setSelection(selection[selection.length - 1], index); + } else { + setSelection(index, selection[selection.length - 1]); + } + } else { + setSelection(index); + } + } + Event e = new Event(); + e.item = items[index]; + e.widget = this; + notifyListeners(SWT.Selection, e); + } + + public int getItemCount() { + checkWidget(); + return itemCount; + } + + public TableItem[] getItems() { + checkWidget(); + TableItem[] result = new TableItem[itemCount]; + if ((getStyle() & SWT.VIRTUAL) != 0) { + for (int i = 0; i < itemCount; i++) { + result[i] = _getItem(i); + } + } else { + System.arraycopy(items, 0, result, 0, itemCount); + } + return result; + } + + public TableItem getItem(int index) { + checkWidget(); + if (index < 0 || index >= itemCount) { + SWT.error(SWT.ERROR_INVALID_RANGE); + } + return items[index]; + } + + public int indexOf(TableItem item) { + checkWidget(); + if (item == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + for (int i = 0; i < itemCount; i++) { + if (items[i] == item) { + return i; + } + } + return -1; + } + + private TableItem _getItem(int index) { + if ((getStyle() & SWT.VIRTUAL) != 0 && items[index] == null) { + items[index] = new TableItem(this, SWT.NONE, index); + } + return items[index]; + } + + public int getColumnCount() { + checkWidget(); + return columns.size(); + } + + public TableColumn[] getColumns() { + checkWidget(); + return (TableColumn[])columns.toArray(); + } + + public List getColumnList() { + checkWidget(); + return columns; + } + + public TableColumn getColumn(int index) { + checkWidget(); + return columns.get(index); + } + + public int indexOf(TableColumn tableColumn) { + checkWidget(); + if (tableColumn == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + return columns.indexOf(tableColumn); + } + + public int[] getColumnOrder() { + checkWidget(); + int[] result; + if (columns.size() == 0) { + result = new int[0]; + } else { + result = new int[columns.size()]; + System.arraycopy(columnOrder, 0, result, 0, columns.size()); + } + return result; + } + + public void setColumnOrder(int[] order) { + checkWidget(); + if (order == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + int columnCount = columns.size(); + if (order.length != columnCount) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + if( columnCount > 0 ) { + int[] oldOrder = new int[ columnCount ]; + System.arraycopy( columnOrder, 0, oldOrder, 0, columnOrder.length ); + boolean reorder = false; + boolean[] seen = new boolean[ columnCount ]; + for( int i = 0; i < order.length; i++ ) { + int index = order[ i ]; + if( index < 0 || index >= columnCount ) { + SWT.error( SWT.ERROR_INVALID_RANGE ); + } + if( seen[ index ] ) { + SWT.error( SWT.ERROR_INVALID_ARGUMENT ); + } + seen[ index ] = true; + if( index != oldOrder[ i ] ) { + reorder = true; + } + } + if( reorder ) { + System.arraycopy( order, 0, columnOrder, 0, columnOrder.length ); + FormData data = null; + TableColumn c = null; + for( int i = 0; i < seen.length; i++ ) { + c = columns.get(columnOrder[ i ]); + data = new FormData(); + if (i == 0) { + data.top = new FormAttachment(0,0); + data.left = new FormAttachment(0, 0); + } else { + data.top = new FormAttachment(0,0); + data.left = new FormAttachment(columns.get(columnOrder[ i-1 ])); + } + data.width = c.getWidth(); + data.height = 30; + c.setLayoutData(data); + c.redraw(); + } + + this.layout(true, true); + } + } + } + + void createColumn(TableColumn column, final int index) { + if (index < 0) { + SWT.error(SWT.ERROR_INVALID_RANGE); + } + + columns.add(index, column); + + if (columnOrder == null) { + columnOrder = new int[] {index}; + } else { + int length = columnOrder.length; + for (int i = index; i < length; i++) { + columnOrder[i]++; + } + int[] newColumnOrder = new int[ length + 1 ]; + System.arraycopy( columnOrder, 0, newColumnOrder, 0, index ); + System.arraycopy( columnOrder, index, newColumnOrder, index + 1, length - index ); + columnOrder = newColumnOrder; + columnOrder[ index ] = index; + } + + for (int i = 0; i < itemCount; i++) { + if (items[i] != null) { + items[i].shiftData(index); + } + } + + updateScrollBars(true); + } + + public void destroyColumn(TableColumn column) { + int index = columns.indexOf(column); + for (TableItem item : items) { + if (item != null) { + item.removeData(index); + } + } + + // TODO + if (column == sortColumn) { + sortColumn = null; + } + + columns.remove(column); + + // remove from column order + int length = columnOrder.length; + int[] newColumnOrder = new int[length - 1]; + int count = 0; + for (int i = 0; i < length; i++) { + if (columnOrder[i] == index) { + continue; + } + + int newIndex = columnOrder[i]; + if (index < newIndex) { + newIndex--; + } + newColumnOrder[count++] = newIndex; + } + + updateScrollBars(true); + } + + void createItem(TableItem item, int index) { + if (index < 0 || index > itemCount) { + SWT.error(SWT.ERROR_INVALID_RANGE); + } + + if (itemCount == items.length) { + int length = items.length + 4; + TableItem[] newItems = new TableItem[length]; + System.arraycopy(items, 0, newItems, 0, items.length); + items = newItems; + } + + // Insert + System.arraycopy(items, index, items, index + 1, itemCount - index); + items[index] = item; + itemCount++; + + adjustItemIndices( index ); + // adjust the selection indices + /* + for( int i = 0; i < selection.length; i++ ) { + if( selection[ i ] >= index ) { + selection[ i ] = selection[ i ] + 1; + } + } + */ + // advance focusIndex when an item is inserted before the focused item + if( index <= focusIndex ) { + focusIndex++; + } + updateScrollBars(true); + } + + final void destroyItem(TableItem item) { + // TODO + } + + private void adjustItemIndices(int start) { + for (int i = start; i < itemCount; i++) { + if (items[i] != null) { + items[i].setIndex(i); + } + } + } + + static final int DEFAULT_WIDTH = 64; + static final int DEFAULT_HEIGHT = 64; + @Override + public Point computeSize( int wHint, int hHint, boolean changed ) { + checkWidget(); + if (wHint != SWT.DEFAULT && wHint < 0) { + wHint = 0; + } + if (hHint != SWT.DEFAULT && hHint < 0) { + hHint = 0; + } + + int width = 0; + int height = 0; + if( columns.size() > 0 ) { + for (TableColumn colum : columns) { + width += colum.getWidth(); + } + } else { + //width = getItemsPreferredWidth( 0 ); + } + height += getHeaderHeight(); + //TODO: item height is fixed + height += getItemCount() * getItemHeight(); + if( width == 0) { + width = DEFAULT_WIDTH; + } + if(height == 0) { + height = DEFAULT_HEIGHT; + } + if(wHint > 0) { + width = wHint; + } + if(hHint > 0) { + height = hHint; + } + int border = getBorderWidth(); + width += border * 2; + height += border * 2; + /* + if( ( style & SWT.V_SCROLL ) != 0 ) { + width += getVerticalBar().getSize().x; + } + if( ( style & SWT.H_SCROLL ) != 0 ) { + height += getHorizontalBar().getSize().y; + } + */ + return new Point( width, height ); + } + + private int getHeaderHeight() { + checkWidget(); + int result = 0; + if(headerVisible) { + Font headerFont = FontResources.COLUMN_FONT; // TODO + int textHeight = (int)TextSizeUtil.getCharHeight(headerFont); + int imageHeight = 0; + for(int i = 0; i < columns.size(); i++) { + TableColumn column = columns.get(i); + /* + if(column.getText().contains("\n")) { + int columnTextHeight = TextSizeUtil.textExtent( headerFont, column.getText(), 0 ).y; + textHeight = Math.max( textHeight, columnTextHeight ); + } + */ + Image image = column.getImage(); + int height = image == null ? 0 : image.getBounds().height; + if(height > imageHeight) { + imageHeight = height; + } + } + result = Math.max(textHeight, imageHeight); + result = Math.max(result, columnHeight); + } + return result; + } + + public boolean getHeaderVisible() { + checkWidget(); + return headerVisible; + } + + public void setHeaderVisible(boolean headerVisible) { + checkWidget(); + boolean changed = headerVisible != this.headerVisible; + this.headerVisible = headerVisible; + if (changed) { + updateScrollBars(true); + } + } + + public boolean getLinesVisible() { + checkWidget(); + return linesVisible; + } + + public void setLinesVisible(boolean linesVisible) { + checkWidget(); + this.linesVisible = linesVisible; + } + + public TableColumn getSortColumn() { + checkWidget(); + return sortColumn; + } + + public void setSortColumn(TableColumn column) { + checkWidget(); + if (column != null && column.isDisposed()) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + sortColumn = column; + } + + public int getSortDirection() { + checkWidget(); + return sortDirection; + } + + public void setSortDirection(int direction) { + checkWidget(); + if ((direction & (SWT.UP | SWT.DOWN)) != 0 + || direction == SWT.NONE) { + sortDirection = direction; + } + } + + boolean vVisible = false; + boolean hVisible = false; + public void updateScrollBars(boolean isComputeSize) { + if (isComputeSize) { + computeTableSize(); + } + + Rectangle tableRect = new Rectangle(0, 0, tableWidth, tableHeight); + Rectangle tableItemRect = new Rectangle(0, 0, tableWidth, tableHeight - columnHeight); + hVisible = this.needHScroll(tableRect, false); + vVisible = this.needVScroll(tableItemRect, hVisible); + if (vVisible) { + hVisible = this.needHScroll(tableRect, vVisible); + } + + setTableSize(); + } + + void computeTableSize() { + int width = 0; + for (TableColumn column : columns) { + width += column.getWidth(); + } + + tableWidth = width; + if (isCheck) { + tableWidth += CHECK_BOX_WIDTH; + } + + if (getSelectionCount() == 1) { + tableHeight = columnHeight + (itemCount-1) * this.getItemHeight() + + this.getSelectedItemHeight(); + } else { + tableHeight = columnHeight + itemCount * this.getItemHeight(); + } + } + + void setTableSize() { + Rectangle rect = this.getBounds(); + if (vVisible) { + tableWidth += this.getScrollBarWidth(); + } + if (hVisible) { + tableHeight += this.getScrollBarWidth(); + } + int width = Math.max(tableWidth, rect.width); + int height = Math.max(tableHeight, rect.height); + + + + table.setSize(width, height); + tableColumn.setBounds(0, 0, width, 30); + tableItem.setBounds(0, columnHeight, width, height - columnHeight); + } + + public int getColumnHeightSize() { + return columnHeight; + } + + public void setColumnHeightSize(int columnHeightSize) { + this.columnHeight = columnHeightSize; + } + + public boolean checkData(TableItem tableItem, Object indexOf) { + // TODO Auto-generated method stub + return true; + } + + private int leftOffset = 5; + int getLeftOffset() { + return this.leftOffset; + } + + int getColumnLeftOffset(int index) { + int result = leftOffset; + if (index >= 0) { + result = isFixedColumn(index) ? 0 : leftOffset; + } + return result; + } + + // TODO + private boolean isFixedColumn(int index) { + return false; + } + + public void setTopIndex(int index) { + checkWidget(); + if (this.topIndex != index + && index >= 0 && index < itemCount) { + adjustTopIndex(); + if ((getStyle() & SWT.VIRTUAL) != 0) { + redraw(); + } + } + } + public int getTopIndex() { + checkWidget(); + return topIndex; + } + + private void adjustTopIndex() { +// int visibleItemCount = getVisibleItemCount(false); + int visibleItemCount = 0; + int correction = visibleItemCount == 0 ? 1 : 0; + if (topIndex > itemCount - visibleItemCount - correction) { + topIndex = Math.max(0, itemCount - visibleItemCount - correction); + } + } + + public Rectangle getCellPadding() { + // TODO Auto-generated method stub + return new Rectangle(6, 3, 6, 3); + } + + // SWT.CHECK + boolean isChecked() { + return isCheck; + } + + int getCheckBoxWidth() { + return checkBoxWidth; + } +/* + public Point getCheckSize() { + return new Point(0, 0); + } + + public Point getCheckSize(int index) { + if (index == 0 && getColumnCount() == 0) { + return getCheckSize(); + } else { + int[] columnOrder = getColumnOrder(); + if(columnOrder[0] == index) { + return getCheckSize(); + } + } + return new Point(0, 0); + } +*/ + public boolean hasColumnImages(int index) { + return columns.get(index).getImage() == null ? false : true; + } + + public int getCellSpacing() { + // TODO Auto-generated method stub + return 5; + } + + public int getItemHeight() { + checkWidget(); + /* + int result = -1; //customItemHeight; + if (result == -1) { + int textHeight = (int)TextSizeUtil.getCharHeight(getFont()); + int imageHeight = getItemImageSize().y; + result = Math.max(imageHeight, textHeight) + getCellPadding().height; + result = Math.max(itemHeight, result); + // TODO check style + } + return result; + */ + return itemHeight; + } + + public int getSelectedItemHeight() { + checkWidget(); + return selectedItemHeight; + } + + public void setItemHeight(int height) { + checkWidget(); + if (height != itemHeight && height > 0) { + itemHeight = height; + } + } + + public void setSelectedItemHeight(int height) { + checkWidget(); + if (height != selectedItemHeight && height > 0) { + selectedItemHeight = height; + } + } + + int getItemsPreferredWidth(int i) { + int width = 0; + for (TableItem item : items) { + if (item != null) { + int packWidth = item.getPackWidth(i); + if (packWidth > width) { + width = packWidth; + } + } + } + return width; + } + + int getWidth() { + return width; + } + + public void showColumn(TableColumn column) {} + public void showItem(TableItem item) {} + public void showSelection() { + for (TableItem item : items) { +// if (item != null) { +// Event event = new Event(); +// event.index = -1; +// item.notifyListeners(SWT.Resize, event); +// } + if (item != null) { + item.redraw(); + } + } + } + + protected ControlListener controlListener = new ControlListener() { + @Override + public void controlMoved(ControlEvent e) { + } + + @Override + public void controlResized(ControlEvent e) { + updateScrollBars(false); + + for (TableItem item : items) { + if (item != null) { + item.updateSizeAndLocation(-1); + } + } + } + }; + + private PaintListener tableColumnPaintListener = new PaintListener() { + + @Override + public void paintControl(PaintEvent e) { + Rectangle rect= ((Composite)e.widget).getClientArea(); + width = rect.width; + + GC gc = e.gc; + if (headerVisible) { + gc.setBackground(ColorResources.COLUMN_BG_COLOR); + Rectangle columnRect = new Rectangle(0, 0, rect.width, columnHeight); + gc.setClipping(columnRect); + gc.fillRectangle(columnRect); + if (isCheck) { + checkBox.setBounds(0, 0, checkBoxWidth - 1, columnHeight); + gc.setForeground(ColorResources.DEFAULT_STROKE); + gc.drawLine(checkBoxWidth - 1, 0, checkBoxWidth -1, rect.height); + } + } + } + }; + + private PaintListener tableItemPaintListener = new PaintListener() { + + @Override + public void paintControl(PaintEvent e) { + Rectangle rect= ((Composite)e.widget).getClientArea(); + width = rect.width; + + GC gc = e.gc; + int top = 0; + int height = getItemHeight(); + for (int i = 0; top < rect.height; i++, top += height) { + Rectangle drawingRect = new Rectangle(0, top, rect.width, height); + gc.setClipping(drawingRect); + if ((i%2) == 0) { + gc.setBackground(ColorResources.ITEM_BG_COLOR_EVEN); + } else { + gc.setBackground(ColorResources.ITEM_BG_COLOR_ODD); + } + gc.fillRectangle(drawingRect); + } + + gc.setForeground(ColorResources.DEFAULT_STROKE); + gc.setLineWidth(1); + int leftOffset = isCheck ? checkBoxWidth : 0; + gc.setClipping(rect); + if (isCheck) { + gc.drawLine(checkBoxWidth -1, 0, checkBoxWidth - 1, rect.height); + } + for (TableColumn column : columns) { + int width = column.getWidth(); + + gc.drawLine(leftOffset + width-1, 0, leftOffset + width -1, rect.height); + leftOffset += width; + } + } + + }; + + private KeyAdapter controlKeyEvent = new KeyAdapter() { + @ Override + public void keyPressed(KeyEvent e) { + switch (e.keyCode) { + case SWT.CTRL: + isCtrlKeyOn = true; + break; + case SWT.SHIFT: + if (!isCtrlKeyOn) { + isShiftKeyOn = true; + } + break; + default: + break; + } + } + + @ Override + public void keyReleased(KeyEvent e) { + switch (e.keyCode) { + case SWT.CTRL: + isCtrlKeyOn = false; + break; + case SWT.SHIFT: + isShiftKeyOn = false; + break; + case SWT.ARROW_UP: + if (lastSelectedIndex > 0) { + _select(lastSelectedIndex - 1); + } + break; + case SWT.ARROW_DOWN: + if (lastSelectedIndex > -1 && lastSelectedIndex < (itemCount-1)) { + _select(lastSelectedIndex + 1); + } + break; + default: + break; + } + } + }; + + protected Listener foucusEventListener = new Listener() { + @Override + public void handleEvent(Event event) { + switch (event.type) { + case SWT.FocusIn: + showSelection(); + break; + case SWT.FocusOut: + showSelection(); + break; + } + } + }; + + private Listener sortListener = new Listener() { + + @Override + public void handleEvent(Event event) { + TableColumn column = (TableColumn)event.widget; + int index = columns.indexOf(column); + if (index == -1) { + return; + } + + int dir = getSortDirection(); + + if (getSortColumn() != column) { + setSortColumn(column); + dir = SWT.UP; + } + + TableItem[] newItems = new TableItem[itemCount]; + int[] newSelection = new int[selection.length]; + newItems[0] = items[0]; + for (int i = 1; i < items.length; i++) { + if (items[i] == null) { + continue; + } + String value1 = items[i].getText(index); + if (value1 == null) { + continue; + } + int j = i - 1; + + while (j >= 0) { + String value2 = newItems[j].getText(index); + int compare = value1.compareTo(value2); + if (compare < 0 && dir == SWT.UP) { + newItems[j + 1] = newItems[j]; + j--; + } else if (compare > 0 && dir == SWT.DOWN){ + newItems[j + 1] = newItems[j]; + j--; + } else { + break; + } + } + newItems[j + 1] = items[i]; +// for (int j = 0; j < i; j++) { +// if (items[j] == null) { +// continue; +// } +// String value2 = items[j].getText(index); +// boolean needSort = false; +// int compare = collator.compare(value1, value2); +// if (compare > 0 && dir == SWT.UP) { +// needSort = true; +// } else if (compare < 0 && dir == SWT.DOWN){ +// needSort = true; +// } + +// if (needSort) { +// TableItem item = items[i]; +// TableItem[] newItems = new TableItem[itemCount]; +// if (j > 0) { +// System.arraycopy(items, 0, newItems, 0, j); +// } +// System.arraycopy(items, j, newItems,j+1, i - j); +// System.arraycopy(items, i + 1, newItems, i + 1, itemCount - i - 1); +// newItems[j] = item; +// items = newItems; +// adjustItemIndices(0); +// } + } + adjustItemIndices(0); + + for (int in = 0; in < selection.length; in++) { + TableItem item = items[selection[in]]; + for (int i = 0; i < newItems.length; i++) { + if (newItems[i] == item) { + newSelection[in] = i; + break; + } + } + } + items = newItems; + dir = dir == SWT.UP ? SWT.DOWN : SWT.UP; + setSortDirection(dir); + setSelection(newSelection); + showSelection(); + } + }; + + public Listener getDifaultSortListener() { + checkWidget(); + return sortListener; + } +} diff --git a/src/org/tizen/emulator/manager/ui/table/TableColumn.java b/src/org/tizen/emulator/manager/ui/table/TableColumn.java new file mode 100644 index 0000000..e1d412c --- /dev/null +++ b/src/org/tizen/emulator/manager/ui/table/TableColumn.java @@ -0,0 +1,617 @@ +/* + * Emulator Manager + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * JiHye Kim + * Minkee Lee + * SeokYeon Hwang + * Sangho Park + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +package org.tizen.emulator.manager.ui.table; + +import java.util.ArrayList; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Cursor; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.TypedListener; + +public class TableColumn extends Canvas { + private static final int SORT_INDICATOR_WIDTH = 10; + private static final int SPACING = 5; + // minimum size + private static final int RESIZABLE_WIDTH = 10; + private static final int MIN_WIDTH = 20; + private static final int HEADERPADDING_WIDTH = 30; + + private final Table parent; + private int style = 0; + + private int width; + private String text; + private Image image; + + private boolean resizable = false; + private boolean moveable = false; + + // mouse hover and push + //private boolean isSelected = false; + private boolean isHoverState; + private Font font; + private Font selectedFont; + private Color background; + private Color selectedBackground; + private Color foreground; + private Color selectedForeground; + + // resize + private boolean isResizedState; + private boolean isResizeableState; + private boolean setCursor; + private Cursor backupCursor; + + // drag and drop + private TableColumnDragAndDrop dnd; + private boolean backupMoveable; + private Image dndImage; + + // draw text + private String drawingText; + private boolean needRedrawingText = true; + + //private SortButton sortButton;; + private Image sortIndicator; + + public TableColumn(Table parent, int style) { + this(parent, style, checkNull(parent).getColumnCount()); + } + + public TableColumn(Table parent, int style, int index) { + super(parent.tableColumn, checkStyle(style)); + this.parent = parent; + this.style = checkStyle(style); + + this.resizable = true; + + createColumn(index); + } + + private static int checkStyle (int style) { + style = SWT.DOUBLE_BUFFERED | style; + return Helper.checkBits (style, SWT.LEFT, SWT.RIGHT, SWT.CENTER, 0, 0, 0); + } + + private static Table checkNull(Table table) { + if (table == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + return table; + } + + private void createColumn (final int index) { + parent.createColumn (this, index); + + text = ""; + + //sortButton = new SortButton(this, SWT.NONE); +// sortButton.addSelectionListener(new SelectionListener() { +// @Override +// public void widgetSelected(SelectionEvent se) { +// Event e = new Event(); +// e.widget = ((SortButton)se.widget).getParent(); +// notifyListeners(SWT.Selection, e); +// } +// +// @Override +// public void widgetDefaultSelected(SelectionEvent e) { +// // TODO Auto-generated method stub +// } +// +// }); + + this.setFont(FontResources.COLUMN_FONT); + this.setForeground(ColorResources.COLUMN_FONT_COLOR); + this.setBackground(ColorResources.COLUMN_BG_COLOR); + + addListeners(); + } + + private void addListeners() { + this.addPaintListener(paintListener); + addListener(SWT.MouseEnter, mouseMoveEventListener); + addListener(SWT.MouseExit, mouseMoveEventListener); + addListener(SWT.MouseMove, mouseMoveEventListener); + addControlListener(controlListener); + addMouseListener(mouseListener); + } + + public void addSelectionListener (SelectionListener listener) { + checkWidget(); + if (listener == null) { + SWT.error (SWT.ERROR_NULL_ARGUMENT); + } + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Selection,typedListener); + addListener (SWT.DefaultSelection,typedListener); + } + + public void removeSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) { + SWT.error (SWT.ERROR_NULL_ARGUMENT); + } + removeListener (SWT.Selection, listener); + removeListener (SWT.DefaultSelection,listener); + } + + public void pack () { + checkWidget(); + int w = getPreferredWidth(); + if (w != width) { + setWidth(w); + } + } + + public Table getParent() { + checkWidget(); + return parent; + } + + public int getWidth() { + checkWidget(); + return width; + } + + public void setWidth(int width) { + checkWidget(); + if (width < 0) { + return; + } + + if (width == this.width) { + return; + } + + this.width = width; + setBounds(getLeft(), getBounds().y, width, parent.getColumnHeightSize()); + parent.updateScrollBars(true); + processNextColumnsMoveEvent(); + } + + public int getAlignment() { + checkWidget(); + if ((style & SWT.LEFT) != 0) { + return SWT.LEFT; + } + if((style & SWT.CENTER) != 0) { + return SWT.CENTER; + } + + if((style & SWT.RIGHT) != 0) { + return SWT.RIGHT; + } + + return SWT.LEFT; + } + + public void setAlignment(int alignment) { + checkWidget(); + if( (alignment & ( SWT.LEFT | SWT.RIGHT | SWT.CENTER )) != 0 ) { + style &= ~(SWT.LEFT | SWT.RIGHT | SWT.CENTER); + style |= alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER); + } + } + + public boolean getMoveable() { + checkWidget(); + return moveable; + } + + public void setMoveable(boolean moveable) { + checkWidget(); + this.moveable = moveable; + if (moveable && dnd == null) { + dnd = new TableColumnDragAndDrop(this); + } + } + + public boolean getResizable() { + checkWidget(); + return resizable; + } + + public void setResizable(boolean resizable) { + checkWidget(); + this.resizable = resizable; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + this.redraw(); + } + + public Image getImage() { + checkWidget(); + return image; + } + + public void setImage(Image image) { + this.image = image; + } + + // TODO: for drag and drop + public Image getDndImage() { + return dndImage; + } + + @Override + public void setBackground(Color color) { + super.setBackground(color); + background = color; + if (selectedBackground != null) { + selectedBackground.dispose(); + } + selectedBackground = new Color(Display.getCurrent(), + Math.min(color.getRed() + 15, 255), + Math.min(color.getGreen() + 15, 255), + Math.min(color.getBlue() + 15, 255)); + +// if (sortButton != null) { +// sortButton.setBackground(color); +// sortButton.setHoverBackground(selectedBackground); +// } + } + + @Override + public void setForeground(Color color) { + super.setForeground(color); + foreground = color; + if (selectedForeground != null) { + selectedForeground.dispose(); + } + selectedForeground = new Color(Display.getCurrent(), + Math.min(color.getRed() - 15, 255), + Math.min(color.getGreen() - 15, 255), + Math.min(color.getBlue() - 15, 255)); + } + + @Override + public void setFont(Font font) { + super.setFont(font); + this.font = font; + this.selectedFont = font; + } + + void relesaeParent() { + parent.destroyColumn(this); + } + + private void drawColumn(PaintEvent e) { + Rectangle rect = ((Canvas)e.widget).getClientArea(); + dndImage = new Image(Display.getCurrent(), rect.width, rect.height); + GC gc = new GC(dndImage); + + // drawing back ground + gc.setBackground(isHoverState ? selectedBackground : background); + gc.fillRectangle(rect); + + gc.setForeground(ColorResources.DEFAULT_STROKE); + gc.setLineWidth(1); + gc.drawLine(rect.width-1, 0, rect.width-1, rect.height); + + gc.setForeground(isHoverState ? selectedForeground : foreground); + + if (parent.getSortColumn() == this) { + // sort column + int x = rect.x + (rect.width - SORT_INDICATOR_WIDTH - SPACING); + if (parent.getSortDirection() == SWT.UP) { + sortIndicator = ImageResources.SORT_INDICATOR_UP; + } else if (parent.getSortDirection() == SWT.DOWN){ + sortIndicator = ImageResources.SORT_INDICATOR_DOWN; + } else { + sortIndicator = null; + } + if (sortIndicator != null) { + Rectangle bounds = sortIndicator.getBounds(); + //sortButton.setBounds(x, 0, SORT_INDICATOR_WIDTH, rect.height); + gc.drawImage(sortIndicator, x, rect.y + (rect.height- bounds.height) / 2); + } + } + if (image != null || !text.isEmpty()) { + // + int imageX = rect.x; + int textX = rect.x; + int imageY = 0; + int textY = 0; + + Point imageEX = new Point(0, 0); + if (image != null) { + imageEX = new Point(image.getImageData().width, + image.getImageData().height); + } + + if (needRedrawingText) { + int textWidth = TextSizeUtil.textExtent(getFont(), text); + drawingText = text; + if (textWidth > (rect.width - imageEX.x - 10)) { + while (textWidth > (rect.width - 10) - TextSizeUtil.OMITLEN && drawingText.length() > 1) { + drawingText = drawingText.substring(0, drawingText.length() - 1); + textWidth = TextSizeUtil.textExtent(getFont(), drawingText); + } + drawingText = drawingText + TextSizeUtil.OMIT; + } + needRedrawingText = false; + } + + int textWidth = TextSizeUtil.textExtent(getFont(), drawingText); + switch (getAlignment()) { + case SWT.LEFT: + imageX += (image != null ? SPACING : 0); + textX += imageX + imageEX.x + SPACING; + break; + case SWT.RIGHT: + imageX += (image != null + ? rect.width - imageEX.x - SPACING - textWidth - 10 + : rect.width - textWidth - 10); + textX += imageX + imageEX.x + SPACING; + break; + case SWT.CENTER: + imageX += (image != null + ? rect.x + ((rect.width - image.getImageData().width - textWidth) / 2) + : rect.x + ((rect.width - textWidth) / 2)); + textX += imageX + imageEX.x; + break; + } + + if (image != null) { + imageY = rect.y + ((rect.height - image.getImageData().height) / 2); + gc.drawImage(image, imageX, imageY); + } + + int fontHeight = gc.getFontMetrics().getHeight(); + textY = rect.y + (rect.height - fontHeight) / 2 - 1; + + if (!text.isEmpty()) { + gc.setFont(isHoverState? selectedFont : font); + gc.drawText(drawingText, textX, textY, true); + } + } + + e.gc.drawImage(dndImage, 0, 0); + gc.dispose(); + } + + protected PaintListener paintListener = new PaintListener() { + + @Override + public void paintControl(PaintEvent e) { + drawColumn(e); + } + }; + + protected Listener mouseMoveEventListener = new Listener() { + + @Override + public void handleEvent(Event event) { + if (!isEnabled()) { + return; + } + + TableColumn column = (TableColumn) event.widget; + if (event.type == SWT.MouseEnter) { + isHoverState = true; + redraw(); + } else if (event.type == SWT.MouseExit) { + isHoverState = false; + if (resizable) { + isResizeableState = false; + if (setCursor) { + setCursor = false; + column.setCursor(backupCursor); + } + } + // TODO + column.redraw(); + column.update(); + } else if (event.type == SWT.MouseMove) { + if (!resizable) { + return; + } + + if (event.x >= (width - RESIZABLE_WIDTH)) { + isResizeableState = true; + if (!setCursor) { + setCursor = true; + backupCursor = column.getCursor(); + column.setCursor(new Cursor(Display.getCurrent(), SWT.CURSOR_SIZEWE)); + } + } else { + isResizeableState = false; + if (setCursor) { + setCursor = false; + column.setCursor(backupCursor); + } + } + + if (isResizedState) { + if (event.x > MIN_WIDTH) { + setWidth(event.x); + redraw(); + } + } + } + } + }; + + protected ControlListener controlListener = new ControlListener() { + + @Override + public void controlMoved(ControlEvent e) { + int left = getLeft(); + Rectangle rect = new Rectangle(left, 0, width, parent.getColumnHeightSize()); + setBounds(rect); + } + + @Override + public void controlResized(ControlEvent e) { + needRedrawingText = true; + parent.redraw(); + for (TableItem item : parent.getItems()) { + if (item != null) { + item.updateSizeAndLocation(getOrderIndex()); + } + } + redraw(); + } + }; + + protected MouseListener mouseListener = new MouseListener() { + @Override + public void mouseDoubleClick(MouseEvent e) { + if (isResizeableState) { + setWidth(getPreferredWidth()); + redraw(); + } + } + + @Override + public void mouseDown(MouseEvent e) { + if (e.x > (width - RESIZABLE_WIDTH) || width < MIN_WIDTH) { + if (!isResizedState) { + isResizedState = true; + backupMoveable = moveable; + moveable = false; + } + } else { + Event ev = new Event(); + ev.widget = e.widget; + notifyListeners(SWT.Selection, ev); + } + } + + @Override + public void mouseUp(MouseEvent e) { + if (isResizedState) { + isResizedState = false; + moveable = backupMoveable; + } + } + }; + + // left offset + final int getLeft() { + int result = 0; + ArrayList columns = (ArrayList) parent.getColumnList(); + int[] columnOrder = parent.getColumnOrder(); + int orderedIndex = -1; + for (int i = 0; orderedIndex == -1 && i < columnOrder.length; i++) { + if (columnOrder[i] == parent.indexOf(this)) { + orderedIndex = i; + } + } + for (int i = 0; i < orderedIndex; i++) { + result += columns.get(columnOrder[i]).getWidth(); + } + + if (parent.isChecked()) { + result += parent.getCheckBoxWidth(); + } + return result; + } + + final int getOrderIndex() { + int[] columnOrder = parent.getColumnOrder(); + int orderedIndex = -1; + for (int i = 0; orderedIndex == -1 && i < columnOrder.length; i++) { + if (columnOrder[i] == parent.indexOf(this)) { + orderedIndex = i; + break; + } + } + return orderedIndex; + } + + final int getPreferredWidth() { + int result = 0; + Font font = getFont(); + if (text != null && text.length() >= 0) { + if (text.indexOf('\n') != -1) { + result = TextSizeUtil.stringExtend(font, text); + } else { + result = TextSizeUtil.textExtent(font, text); + } + + Image image = getImage(); + if (image != null) { + result += image.getBounds().width + SPACING; + } + + if (parent.getSortColumn() == this && parent.getSortDirection() != SWT.NONE) { + result += SORT_INDICATOR_WIDTH + SPACING; + } + } + + // Extend computed width if there are wider items + int columnIndex = parent.indexOf(this); + int itemsPreferredWidth = parent.getItemsPreferredWidth(columnIndex); + result = Math.max(result, itemsPreferredWidth); + + result += HEADERPADDING_WIDTH; + + return result; + } + + private void processNextColumnsMoveEvent() { + int[] columnsOrder = parent.getColumnOrder(); + boolean found = false; + for (int i = 0; i < columnsOrder.length; i++) { + TableColumn column = parent.getColumn(columnsOrder[i]); + if (column == this) { + found = true; + } else if (found) { + column.notifyListeners(SWT.Move, new Event()); + } + } + } +} diff --git a/src/org/tizen/emulator/manager/ui/table/TableColumnDataTransfer.java b/src/org/tizen/emulator/manager/ui/table/TableColumnDataTransfer.java new file mode 100644 index 0000000..a7b3017 --- /dev/null +++ b/src/org/tizen/emulator/manager/ui/table/TableColumnDataTransfer.java @@ -0,0 +1,142 @@ +/* + * Emulator Manager + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * JiHye Kim + * Minkee Lee + * SeokYeon Hwang + * Sangho Park + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +package org.tizen.emulator.manager.ui.table; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.eclipse.swt.dnd.ByteArrayTransfer; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.TransferData; + +public class TableColumnDataTransfer extends ByteArrayTransfer { + static class TableColumnData { + int orderIndex; + } + + private static final String TYPE_NAME = "table_column_data_type"; + private static final int TYPE_ID = registerType(TYPE_NAME); + private static TableColumnDataTransfer instance = new TableColumnDataTransfer(); + + public static TableColumnDataTransfer getInstance() { + return instance; + } + + private TableColumnDataTransfer() { + } + + public void javaToNative(Object object, TransferData transferData) { + if (!checkType(object) || !isSupportedType(transferData)) { + DND.error(DND.ERROR_INVALID_DATA); + } + + TableColumnData columnData = (TableColumnData) object; + ByteArrayOutputStream out = null; + DataOutputStream writeOut = null; + + try { + out = new ByteArrayOutputStream(); + writeOut = new DataOutputStream(out); + writeOut.writeInt(columnData.orderIndex); + byte[] buffer = out.toByteArray(); + super.javaToNative(buffer, transferData); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (writeOut != null) { + try { + writeOut.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public Object nativeToJava(TransferData transferData) { + if (!isSupportedType(transferData)) { + return null; + } + + byte[] buffer = (byte[]) super.nativeToJava(transferData); + if (buffer == null) { + return null; + } + + TableColumnData columnData = null; + ByteArrayInputStream in = null; + DataInputStream readIn = null; + + try { + in = new ByteArrayInputStream(buffer); + readIn = new DataInputStream(in); + columnData = new TableColumnData(); + columnData.orderIndex = readIn.readInt(); + readIn.close(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } finally { + if (readIn != null) { + try { + readIn.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + return columnData; + } + + protected String[] getTypeNames() { + return new String[] { TYPE_NAME }; + } + + protected int[] getTypeIds() { + return new int[] { TYPE_ID }; + } + + boolean checkType(Object object) { + if (object == null || !(object instanceof TableColumnData)) { + return false; + } + + return true; + } + + protected boolean validate(Object object) { + return checkType(object); + } +} diff --git a/src/org/tizen/emulator/manager/ui/table/TableColumnDragAndDrop.java b/src/org/tizen/emulator/manager/ui/table/TableColumnDragAndDrop.java new file mode 100644 index 0000000..6df2ceb --- /dev/null +++ b/src/org/tizen/emulator/manager/ui/table/TableColumnDragAndDrop.java @@ -0,0 +1,137 @@ +/* + * Emulator Manager + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * JiHye Kim + * Minkee Lee + * SeokYeon Hwang + * Sangho Park + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +package org.tizen.emulator.manager.ui.table; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DragSource; +import org.eclipse.swt.dnd.DragSourceAdapter; +import org.eclipse.swt.dnd.DragSourceEvent; +import org.eclipse.swt.dnd.DropTarget; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.dnd.DropTargetAdapter; +import org.eclipse.swt.dnd.DropTargetEvent; +import org.eclipse.swt.widgets.Event; +import org.tizen.emulator.manager.ui.table.TableColumnDataTransfer.TableColumnData; + +public class TableColumnDragAndDrop { + private TableColumn column; + + public TableColumnDragAndDrop(final TableColumn column) { + this.column = column; + + DragSource ds = new DragSource(column, DND.DROP_MOVE); + ds.setTransfer(new Transfer[] {TableColumnDataTransfer.getInstance()}); + ds.addDragListener(sourceAdapter); + + DropTarget dt = new DropTarget(column, DND.DROP_MOVE); + dt.setTransfer(new Transfer[] {TableColumnDataTransfer.getInstance()}); + dt.addDropListener(targetAdapter); + } + + private DragSourceAdapter sourceAdapter = new DragSourceAdapter() { + public void dragStart(DragSourceEvent event) { + // TODO + event.image = column.getDndImage(); + + if (!column.getMoveable()) { + event.doit = false; + } + } + + public void dragSetData(DragSourceEvent event) { + // send order index to target + int index = column.getParent().indexOf(column); + int[] columnOrder = column.getParent().getColumnOrder(); + int orderIndex = -1; + for (int i = 0; i< columnOrder.length; i++) { + if (columnOrder[i] == index) { + orderIndex = i; + break; + } + } + + TableColumnData data = new TableColumnData(); + data.orderIndex = orderIndex; + TableColumnDataTransfer.getInstance().javaToNative(data, event.dataType); + event.data = data; + } + }; + + private DropTargetAdapter targetAdapter = new DropTargetAdapter() { + public void dragEnter(DropTargetEvent event) { + // TODO + // draw guide icon + } + public void dragLeave(DropTargetEvent event) { + // TODO + } + + public void dragOver(DropTargetEvent event) { + event.feedback = DND.FEEDBACK_SELECT | DND.FEEDBACK_INSERT_BEFORE; + } + + public void drop(DropTargetEvent event) { + TableColumnData data = (TableColumnData)TableColumnDataTransfer.getInstance() + .nativeToJava(event.currentDataType); + + int sourceOrderIndex = data.orderIndex; + int index = column.getParent().indexOf(column); + int[] columnOrder = column.getParent().getColumnOrder(); + int sourceIndex = columnOrder[sourceOrderIndex]; + int targetOrderIndex = -1; + for (int i = 0; i< columnOrder.length; i++) { + if (columnOrder[i] == index) { + targetOrderIndex = i; + break; + } + } + + if (sourceOrderIndex > targetOrderIndex) { + for (int i = sourceOrderIndex; i > targetOrderIndex; i--) { + columnOrder[i] = columnOrder[i-1]; + } + } else { + for (int i = sourceOrderIndex; i < targetOrderIndex; i++) { + columnOrder[i] = columnOrder[i+1]; + } + } + + columnOrder[targetOrderIndex] = sourceIndex; + column.getParent().setColumnOrder(columnOrder); + + for (int i = 0; i < columnOrder.length; i++) { + TableColumn c = column.getParent().getColumn(columnOrder[i]); + c.notifyListeners(SWT.Move, new Event()); + } + } + }; +} diff --git a/src/org/tizen/emulator/manager/ui/table/TableItem.java b/src/org/tizen/emulator/manager/ui/table/TableItem.java new file mode 100644 index 0000000..2e045b3 --- /dev/null +++ b/src/org/tizen/emulator/manager/ui/table/TableItem.java @@ -0,0 +1,942 @@ +/* + * Emulator Manager + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * JiHye Kim + * Minkee Lee + * SeokYeon Hwang + * Sangho Park + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +package org.tizen.emulator.manager.ui.table; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; + +public class TableItem extends Canvas { + private static final class Data { + String text = ""; + Image image; + private Image selectedImage; + Font font; + Color backgroundColor; + Color foregroundColor; + + int textWidth = -1; + boolean needRedrawingText = true; + String drawingText = ""; + } + + private Table parent; + private Data[] dataList; + private Font font; + private Color background; + private Color selectedBackground; + //private Color focusOutSelectedBackground; + private Color foreground; + private Color selectedForeground; + + private boolean checked; + private boolean grayed; + private int index; + + //private boolean isHoverState = false; + private boolean isSelected = false; + + private CheckBoxButton checkBox; + + public TableItem(Table parent, int style) { + this(parent, style, checkNull (parent).getItemCount()); + } + + public TableItem(final Table parent, int style, int index) { + super(parent.tableItem, style); + this.parent = parent; + this.index = index; + + parent.createItem(this, index); + + setFont(FontResources.ITEM_FONT); + setForeground(ColorResources.ITEM_FONT_COLOR); + if ((index % 2) == 0) { + setBackground(ColorResources.ITEM_BG_COLOR_EVEN); + } else { + setBackground(ColorResources.ITEM_BG_COLOR_ODD); + } + + selectedForeground = ColorResources.ITEM_SELECTED_FONT_COLOR; + selectedBackground = ColorResources.ITEM_SELECTED_BG_COLOR; + //focusOutSelectedBackground = ColorResources.ITEM_FOCUST_OUT_SELECTED_BG_COLOR; + + if (parent.isChecked()) { + checkBox = new CheckBoxButton(this, SWT.None); + checkBox.setBackground(background); + checkBox.setSelectedBackground(selectedBackground); + } + + addListeners(); + } + + private void addListeners() { + this.addListener(SWT.MouseDown, mouseEventListener); + this.addControlListener(controlListener); + this.addPaintListener(paintListener); + if (parent.isChecked()) { + checkBox.addSelectionListener(new SelectionListener() { + @Override + public void widgetSelected(SelectionEvent e) { + parent._select(index); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + }); + } + } + + public Table getParent() { + checkWidget(); + return parent; + } + + public void setBackground(Color color) { + checkWidget(); + if (color != null && color.isDisposed()) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + + if (!equals(background, color)) { + background = color; + parent.redraw(); + } + } + + public void setBackground(int index, Color color) { + checkWidget(); + if (color != null && color.isDisposed()) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + + int count = Math.max(1, parent.getColumnCount()); + if (index >= 0 && index < count) { + ensureData(index, count); + if (!equals(dataList[index].backgroundColor, color)) { + dataList[index].backgroundColor = color; + parent.redraw(); + } + } + } + + public Color getBackground() { + checkWidget(); + if (!parent.checkData(this, parent.indexOf(this))) { + SWT.error(SWT.ERROR_WIDGET_DISPOSED); + } + if (background == null) { + return parent.getBackground(); + } else { + return background; + } + } + + public Color getBackground(int index) { + checkWidget(); + if (!parent.checkData(this, parent.indexOf(this))) { + SWT.error(SWT.ERROR_WIDGET_DISPOSED); + } + + if(hasData(index) && dataList[index].backgroundColor != null) { + return dataList[index].backgroundColor; + } + + return getBackground(); + } + + public void setFont(Font font) { + checkWidget(); + if (font != null && font.isDisposed()) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + + if (!equals(this.font, font)) { + this.font = font; + clearTextWidth(); + if (parent.getColumnCount() == 0) { + parent.updateScrollBars(true); + } + parent.redraw(); + } + } + + public void setFont(int index, Font font) { + checkWidget(); + if (font != null && font.isDisposed()) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + + int count = Math.max(1, parent.getColumnCount()); + if (index >= 0 && index < count) { + ensureData(index, count); + Data data = dataList[index]; + if (!equals(font, data.font)) { + data.font = font; + data.textWidth = -1; + parent.redraw(); + } + } + } + + public Font getFont() { + checkWidget(); + if (!parent.checkData(this, parent.indexOf(this))) { + SWT.error(SWT.ERROR_WIDGET_DISPOSED); + } + if (font == null) { + return parent.getFont(); + } + return font; + } + + public Font getFont(int index) { + checkWidget(); + if (!parent.checkData(this, parent.indexOf(this))) { + SWT.error(SWT.ERROR_WIDGET_DISPOSED); + } + + if (hasData(index) && dataList[index].font != null) { + return dataList[index].font; + } + return getFont(); + } + + public void setForeground(Color color) { + checkWidget(); + if (color != null && color.isDisposed()) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + + if (!equals(foreground, color)) { + foreground = color; + parent.redraw(); + } + } + + public void setForeground(int index, Color color) { + checkWidget(); + if (color != null && color.isDisposed()) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + + int count = Math.max(1, parent.getColumnCount()); + if (index >= 0 && index < count) { + ensureData(index, count); + if (!equals(dataList[index].foregroundColor, color)) { + dataList[index].foregroundColor = color; + parent.redraw(); + } + } + } + + public Color getForeground() { + checkWidget(); + if (!parent.checkData(this, parent.indexOf(this))) { + SWT.error(SWT.ERROR_WIDGET_DISPOSED); + } + if (foreground == null) { + return parent.getForeground(); + } else { + return foreground; + } + } + + public Color getForeground(int index) { + checkWidget(); + if (!parent.checkData(this, parent.indexOf(this))) { + SWT.error(SWT.ERROR_WIDGET_DISPOSED); + } + + if(hasData(index) && dataList[index].foregroundColor != null) { + return dataList[index].foregroundColor; + } + + return getForeground(); + } + + public void setChecked(boolean checked) { + checkWidget(); + if ((parent.getStyle() & SWT.CHECK) != 0) { + this.checked = checked; + checkBox.setSelection(checked); + } + } + + public boolean getChecked() { + checkWidget(); + if (!parent.checkData(this, parent.indexOf(this))) { + SWT.error(SWT.ERROR_WIDGET_DISPOSED); + } + + if ((parent.getStyle() & SWT.CHECK) != 0) { + return checked; + } + return false; + } + + public void setGrayed(boolean grayed) { + checkWidget(); + if ((parent.getStyle() & SWT.CHECK) != 0) { + this.grayed = grayed; + } + } + + /** + * + * @return the grayed state of the check box + */ + public boolean getGrayed() { + checkWidget(); + if (!parent.checkData(this, parent.indexOf(this))) { + SWT.error(SWT.ERROR_WIDGET_DISPOSED); + } + + if ((parent.getStyle() & SWT.CHECK) != 0) { + return grayed; + } + return false; + } + + public void setImage(Image image) { + checkWidget(); + setImage(0, image); + } + + public void setImage (int index, Image image) { + checkWidget(); + if (image != null && image.isDisposed()) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + + int count = Math.max(1, parent.getColumnCount()); + if (index >= 0 && index < count) { + ensureData(index, count); + if (!equals(dataList[index].image, image)) { + //parent.updateColumnImageCount(index, dataList[index].image, image); + dataList[index].image = image; + //parent.updateImageImageSize(image); + if (parent.getColumnCount() == 0) { + parent.updateScrollBars(true); + } + parent.redraw(); + } + } + } + + public void setImage(Image[] images) { + checkWidget(); + if (images == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + + for (int i = 0; i < images.length; i++) { + if (images[i] != null && images[i].isDisposed()) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + } + + for (int i = 0; i < images.length; i++) { + setImage(i, images[i]); + } + } + + public Image getImage() { + checkWidget(); + return getImage(0); + } + + public Image getImage(int index) { + checkWidget(); + if (!parent.checkData(this, parent.indexOf(this))) { + SWT.error(SWT.ERROR_WIDGET_DISPOSED); + } + if (hasData(index)) { + return dataList[index].image; + } + return null; + } + + public void setSelectedImage(int index, Image image) { + checkWidget(); + if (image != null && image.isDisposed()) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + + int count = Math.max(1, parent.getColumnCount()); + if (index >= 0 && index < count) { + ensureData(index, count); + if (!equals(dataList[index].selectedImage, image)) { + dataList[index].selectedImage = image; + if (parent.getColumnCount() == 0) { + parent.updateScrollBars(true); + } + parent.redraw(); + } + } + } + + public Image getSelectedImage(int index) { + checkWidget(); + if (!parent.checkData(this, parent.indexOf(this))) { + SWT.error(SWT.ERROR_WIDGET_DISPOSED); + } + if (hasData(index)) { + return dataList[index].selectedImage; + } + return null; + } + + public void setText (String text) { + checkWidget(); + setText(0, text); + } + + public void setText (int index, String text) { + checkWidget(); + if (text == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + + int count = Math.max(1, parent.getColumnCount()); + if (index >= 0 && index < count) { + ensureData(index, count); + if (!text.equals(dataList[index].text)) { + dataList[index].text = text; + dataList[index].textWidth = TextSizeUtil.textExtent(font, text); + if (parent.getColumnCount() == 0) { + parent.updateScrollBars(true); + } + parent.redraw(); + } + } + } + + public void setText (String [] textList) { + checkWidget(); + if (textList == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + + for (int i = 0; i < textList.length; i++) { + if (textList[i] != null) { + setText(i, textList[i]); + } + } + } + + public String getText() { + checkWidget(); + return getText(0); + } + + public String getText(int index) { + checkWidget(); + if (!parent.checkData(this, parent.indexOf(this))) { + SWT.error(SWT.ERROR_WIDGET_DISPOSED); + } + if (hasData(index)) { + return dataList[index].text; + } + return ""; + } + + public Rectangle getBounds() { + return getBounds(0); + } + + public Rectangle getBounds(int index) { + checkWidget(); + if (!parent.checkData(this, parent.indexOf(this))) { + SWT.error(SWT.ERROR_WIDGET_DISPOSED); + } + + Rectangle result = null; + int columnCount = parent.getColumnCount(); + if (columnCount > 0 && + (index < 0 || index >= columnCount)) { + result = new Rectangle(0, 0, 0, 0); + } else { + int left = getLeft(index); + int top = getTop(parent.indexOf(this)); + int width = 0; + if (index == 0 && columnCount == 0) { + int spacing = getSpacing(index); + int paddingWidth = parent.getCellPadding().width; + width = getImageWidth(index) + spacing + + getTextWidth(index, font) + paddingWidth; + } else if (index >= 0 && index < columnCount) { + width = parent.getColumn(index).getWidth(); + } + + int height = getHeight(); + result = new Rectangle(left, top, width, height); + } + return result; + } + + public Rectangle getImageBounds(int index) { + checkWidget(); + if (!parent.checkData(this, parent.indexOf(this))) { + SWT.error(SWT.ERROR_WIDGET_DISPOSED); + } + + int itemIndex = parent.indexOf(this); + Rectangle cellPadding = parent.getCellPadding(); + int left = getLeft(index) + cellPadding.x; + int top = getTop(itemIndex); + int width = getImageWidth(index); + int height = getHeight(); + return new Rectangle(left, top, width, height); + } + + // TODO + public int getImageIndent() { + checkWidget(); + return 0; + } + + public Rectangle getTextBounds(int index) { + checkWidget(); + if (index < -1 || index > parent.getColumnCount()) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + + int itemIndex = parent.indexOf(this); + if (!parent.checkData(this, itemIndex)) { + SWT.error(SWT.ERROR_WIDGET_DISPOSED); + } + + if (index == 0 && parent.getColumnCount() == 0) { + index = 0; + } + + + Rectangle cellPadding = parent.getCellPadding(); + int left = 0; + int top = 0; + int width = 0; + int imageWidth = 0; + int height = getHeight(); + + if (parent.hasColumnImages(index)) { + imageWidth = parent.getColumn(index).getImage().getBounds().width; + } + + int spacing = getSpacing(index); + left = getLeft(index) + cellPadding.x + imageWidth + spacing; + top = getTop(itemIndex); + if (index == 0) { + width = getTextWidth(0, getFont()); + } else { + width = getColumnWidth(index) - cellPadding.width - imageWidth - spacing; + } + if (width < 0) { + width = 0; + } + + return new Rectangle(left, top, width, height); + } + + public void clear() { + dataList = null; + checked = false; + grayed = false; + parent.updateScrollBars(true); + if ((parent.getStyle() & SWT.VERTICAL) != 0) { + parent.redraw(); + } + } + + public void setIndex(int i) { + this.index = i; + if ((index % 2) == 0) { + setBackground(ColorResources.ITEM_BG_COLOR_EVEN); + } else { + setBackground(ColorResources.ITEM_BG_COLOR_ODD); + } + if (parent.isChecked() && checkBox != null) { + checkBox.setBackground(getBackground()); + } + } + + final void shiftData(int index) { + if (dataList != null && dataList.length > index + && parent.getColumnCount() > 1) { + Data[] newDataList = new Data[dataList.length + 1]; + System.arraycopy(dataList, 0, newDataList, 0, index); + int offset = dataList.length - index; + System.arraycopy(dataList, index, newDataList, index + 1, offset); + dataList = newDataList; + } + } + + final void removeData(int index) { + if (dataList != null && dataList.length > index + && parent.getColumnCount() > 1) { + Data[] newDataList = new Data[dataList.length - 1]; + System.arraycopy(dataList, 0, newDataList, 0, index); + int offset = dataList.length - index - 1; + System.arraycopy(dataList, index + 1, newDataList, index, offset); + dataList = newDataList; + } + } + + private int getColumnWidth(int index) { + TableColumn column = parent.getColumn(index); + return column.getWidth(); + } + + private int getLeft(int index) { + int result = 0; + int columnCount = parent.getColumnCount(); + if (index == 0 && columnCount == 0) { + result = parent.getLeftOffset(); + } else if (index >= 0 && index < columnCount){ + int columnLeft = parent.getColumn(index).getLeft(); + result = columnLeft - parent.getColumnLeftOffset(index); + } + return result; + } + + private int getTop(int index) { + int upperItems = index - parent.getTopIndex(); + int itemHeight = parent.getItemHeight(); + int result = 0; + if (parent.getSelectionCount() == 1) { + int select = parent.getSelectionIndex(); + if (index > select) { + result = (upperItems-1) * itemHeight + parent.getSelectedItemHeight(); + } else { + result = upperItems * itemHeight; + } + } else { + result = upperItems * itemHeight; + } + return result; + } + + private int getHeight() { + if (parent.getSelectionCount() == 1 + && parent.getSelectionIndex() == index) { + return parent.getSelectedItemHeight(); + } else { + return parent.getItemHeight(); + } + } + + final int getPackWidth(int index) { + return getImageWidth(index) + + getSpacing(index) + + getTextWidth(index, parent.getFont()) + + parent.getCellPadding().width; + } + + private int getImageWidth(int index) { + Image image = getImage(index); + if (image != null) { + return image.getImageData().width; + } + return 0; + } + + private int getTextWidth(int index, Font font) { + if (hasData(index)) { + Data data = dataList[index]; + if (data.textWidth == -1) { + data.textWidth + = TextSizeUtil.stringExtend(font, data.text); + } + return data.textWidth; + } + return 0; + } + + private void clearTextWidth() { + if (dataList != null) { + for (Data data : dataList) { + if (data != null) { + data.textWidth = -1; + } + } + } + } + + private int getSpacing(int index) { + if (parent.hasColumnImages(index)) { + return parent.getCellSpacing(); + } + return 0; + } + + //// + //// help function + /// + + private void ensureData(int index, int columnCount) { + if (dataList == null) { + dataList = new Data[columnCount]; + } else if (dataList.length < columnCount) { + Data[] newDataList = new Data[columnCount]; + System.arraycopy(dataList, 0, newDataList, 0, dataList.length); + dataList = newDataList; + } + + if (dataList[index] == null) { + dataList[index] = new Data(); + } + } + + private boolean hasData(int index) { + return dataList != null && index >= 0 + && index < dataList.length && dataList[index] != null; + } + + private static Table checkNull (Table control) { + if (control == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + return control; + } + + private static boolean equals(Object obj1, Object obj2) { + if (obj1 == obj2) { + return true; + } else if (obj1 == null) { + return false; + } else { + return obj1.equals(obj2); + } + } + + void updateLocation(int columnIndex) { + Rectangle rect = this.getClientArea(); + setBounds(new Rectangle(rect.x, getTop(index), rect.width, rect.height)); + } + + void updateSizeAndLocation(int columnIndex) { + if (columnIndex > -1 && dataList[columnIndex] != null) { + dataList[columnIndex].needRedrawingText = true; + } + + int width = 0; + for (int i = 0; i < parent.getColumnCount(); i++) { + width += getBounds(i).width; + } + + width = Math.max(width, parent.getWidth()); + int top = getTop(index); + setBounds(new Rectangle(0, top, width, getHeight())); + } + + protected ControlListener controlListener = new ControlListener() { + @Override + public void controlMoved(ControlEvent e) { + redraw(); + } + @Override + public void controlResized(ControlEvent e) { + redraw(); + } + }; + + protected Listener mouseEventListener = new Listener() { + + @Override + public void handleEvent(Event event) { + switch(event.type) { + /* + case SWT.MouseEnter: + isHoverState = true; + redraw(); + break; + case SWT.MouseExit: + isHoverState = false; + redraw(); + break; + */ + case SWT.MouseDown: + parent._select(index); + break; + default: + break; + } + } + + }; + + private Rectangle clientArea; + protected PaintListener paintListener = new PaintListener() { + + @Override + public void paintControl(PaintEvent e) { + clientArea = ((Canvas)e.widget).getClientArea(); + GC gc = e.gc; + isSelected = parent.isSelected(index); + if (isSelected) { + //if (parent.isFocusControl()) { + gc.setBackground(selectedBackground); + //} else { + // gc.setBackground(focusOutSelectedBackground); + //} + } else { + gc.setBackground(background); + } + gc.fillRectangle(clientArea); + + if (parent.isChecked()) { + drawCheckBox(e); + } + + int leftOffset = parent.isChecked() ? parent.getCheckBoxWidth() : 0; + drawItem(e, 0, leftOffset); + for (int i = 1; i < parent.getColumnCount(); i++) { + leftOffset += getBounds(i-1).width; + drawItem(e, i, leftOffset); + } + } + }; + + private void drawCheckBox(PaintEvent e) { + if (clientArea == null) { + return; + } + GC gc = e.gc; + int width = parent.getCheckBoxWidth(); + Rectangle drawingRect = new Rectangle(clientArea.x, clientArea.y, width, clientArea.height); + gc.setClipping(drawingRect); + + checkBox.setBounds(drawingRect.x, drawingRect.y, width - 1, drawingRect.height); + if (isSelected) { + checkBox.setSelection(true); + } else { + checkBox.setSelection(false); + } + gc.setForeground(ColorResources.DEFAULT_STROKE); + gc.setLineWidth(1); + gc.drawLine(width - 1, 0, width - 1, clientArea.height); + } + + private void drawItem(PaintEvent e, int index, int leftOffset) { + if (clientArea == null) { + return; + } + GC gc = e.gc; + int width = getBounds(index).width; + Rectangle drawingRect = new Rectangle(clientArea.x + leftOffset, clientArea.y, width, clientArea.height); + gc.setClipping(drawingRect); + + if (dataList[index] != null) { + int imageX = clientArea.x; + int textX = clientArea.x; + int imageY = 0; + int textY = 0; + + Point imageEX = new Point(0, 0); + Image image = null; + if (isSelected && parent.getSelectionCount() == 1) { + image = getSelectedImage(index); + } else { + image = getImage(index); + } + if (image != null) { + imageEX = new Point(image.getImageData().width, + image.getImageData().height); + } + + String drawingText = dataList[index].drawingText; + if (dataList[index].needRedrawingText) { + drawingText = getText(index); + int textWidth = TextSizeUtil.textExtent(getFont(), drawingText); + if (textWidth > (drawingRect.width - imageEX.x - 10)) { + while((textWidth > (drawingRect.width - 10) - TextSizeUtil.OMITLEN) + && drawingText.length() > 1) { + drawingText = drawingText.substring(0, drawingText.length() - 1); + textWidth = TextSizeUtil.textExtent(getFont(), drawingText); + } + drawingText = drawingText + TextSizeUtil.OMIT; + } + dataList[index].needRedrawingText = false; + dataList[index].drawingText = drawingText; + } + + int textWidth = TextSizeUtil.textExtent(getFont(), drawingText); + switch(parent.getColumn(index).getAlignment()) { + case SWT.LEFT: + imageX += (image != null + ? leftOffset + 5 + : leftOffset + 5); + textX += imageX + imageEX.x + 5; + break; + case SWT.RIGHT: + imageX += (image != null + ? leftOffset + drawingRect.width - imageEX.x -2 - textWidth - 10 + : leftOffset + drawingRect.width - textWidth - 10); + textX += imageX + imageEX.x + 5; + break; + case SWT.CENTER: + imageX += (image != null + ? leftOffset + (drawingRect.width - image.getImageData().width - textWidth) / 2 + : leftOffset + (drawingRect.width - textWidth) / 2); + textX += imageX + imageEX.x; + break; + default: + } + + if (image != null) { + imageY = drawingRect.y + ((drawingRect.height - image.getImageData().height)/2); + gc.drawImage(image, imageX, imageY); + } + + if (getText(index) != null && !getText(index).isEmpty()) { + gc.setFont(font); + textY = drawingRect.y + (drawingRect.height - gc.getFontMetrics().getHeight())/2 - 1; + gc.setForeground(isSelected ? selectedForeground : foreground); + gc.drawText(drawingText, textX, textY); + } + } + gc.setForeground(ColorResources.DEFAULT_STROKE); + gc.setLineWidth(1); + gc.drawLine(leftOffset + width - 1, 0, leftOffset + width -1, drawingRect.height); + } +} diff --git a/src/org/tizen/emulator/manager/ui/table/TableScrolledComposite.java b/src/org/tizen/emulator/manager/ui/table/TableScrolledComposite.java new file mode 100644 index 0000000..9fb1040 --- /dev/null +++ b/src/org/tizen/emulator/manager/ui/table/TableScrolledComposite.java @@ -0,0 +1,322 @@ +/* + * Emulator Manager + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * JiHye Kim + * Minkee Lee + * SeokYeon Hwang + * Sangho Park + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +package org.tizen.emulator.manager.ui.table; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +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.Shell; + +import com.codeaffine.eclipse.swt.widget.scrollbar.FlatScrollBar; + +public class TableScrolledComposite extends Composite { + Control tableContent; + Control tableItemContent; + Listener contentListener; + Listener filter; + + int minHeight = 0; + int minWidth = 0; + + FlatScrollBar vBar; + FlatScrollBar hBar; + + private int scrollBarWidth = 6; + + public TableScrolledComposite(Composite parent, int style) { + super(parent, checkStyle(style)); + super.setLayout(new TableScrolledCompositeLayout()); + + if ((style & SWT.VERTICAL) != 0) { + vBar = new FlatScrollBar(this, SWT.VERTICAL); + if (vBar != null) { + vBar.setVisible(false); + vBar.addListener(SWT.Selection, new Listener() { + @Override + public void handleEvent(Event event) { + vScroll(); + } + }); + } + } + + if ((style & SWT.HORIZONTAL) != 0) { + hBar = new FlatScrollBar(this, SWT.HORIZONTAL); + if (hBar != null) { + hBar.setVisible(false); + hBar.addListener(SWT.Selection, new Listener() { + @Override + public void handleEvent(Event event) { + hScroll(); + } + }); + } + } + + contentListener = new Listener() { + @Override + public void handleEvent(Event event) { + if (event.type == SWT.Resize) { + layout(false); + } + } + }; + } + + private static int checkStyle (int style) { + int mask = SWT.BORDER | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT; + return style & mask; + } + + boolean contains(Control control) { + if (control == null || control.isDisposed()) { + return false; + } + + Composite parent = control.getParent(); + while (parent != null && !(parent instanceof Shell)) { + if (this == parent) return true; + parent = parent.getParent(); + } + + return false; + } + + public Control getContent() { + return tableContent; + } + + public Point getOrigin() { + checkWidget(); + if (tableContent == null) { + return new Point(0, 0); + } + Point location = tableContent.getLocation(); + return new Point(-location.x, -location.y); + } + + public void setOrigin(Point origin) { + setOrigin(origin.x, origin.y); + } + + public void setOrigin(int x, int y) { + checkWidget(); + if (tableContent == null) return; + if (hBar != null) { + hBar.setSelection(x); + hBar.notifyListeners(SWT.None); + x = -hBar.getSelection(); + } else { + x = 0; + } + + if (vBar != null) { + vBar.setSelection(y); + hBar.notifyListeners(SWT.None); + y = -vBar.getSelection(); + } else { + y = 0; + } + tableContent.setLocation(x, 0); + } + + public void setTableItemContent(Control content) { + this.tableItemContent = content; + } + + public void setContent(Control content) { + checkWidget(); + if (this.tableContent != null && !this.tableContent.isDisposed()) { + this.tableContent.removeListener(SWT.Resize, contentListener); + this.tableContent.setBounds(new Rectangle(-200, -200, 0, 0)); + } + + this.tableContent = content; + + if (this.tableContent != null) { + + if (vBar != null) { + vBar.setMaximum (0); + vBar.setThumb (0); + vBar.setSelection(0); + vBar.notifyListeners(SWT.None); + } + if (hBar != null) { + hBar.setMaximum (0); + hBar.setThumb (0); + hBar.setSelection(0); + hBar.notifyListeners(SWT.None); + } + + layout(false); + this.tableContent.addListener(SWT.Resize, contentListener); + + } else { + if (hBar != null) { + hBar.setVisible(false); + } + if (vBar != null) { + vBar.setVisible(false); + } + } + } + + boolean needVScroll(Rectangle contentRect, boolean hVisible) { + if (vBar == null) { + return false; + } + boolean result = false; + Rectangle hostRect = getBounds(); + //int border = getBorderWidth(); + //hostRect.height -= 2 * border; + + if(hostRect.height != contentRect.height) { + if (hVisible && hBar != null) { + hostRect.height -= hBar.getSize().y; + } + } + + if (contentRect.height > hostRect.height) { + result = true; + } + + return result; + } + + void vScroll() { + if (tableItemContent == null) return; + Point location = tableItemContent.getLocation (); + int vSelection = vBar.getSelection(); + tableItemContent.setLocation (location.x, -vSelection); + } + + boolean needHScroll(Rectangle contentRect, boolean vVisible) { + if (hBar == null) { + return false; + } + + boolean result = false; + Rectangle hostRect = getBounds(); + //int border = getBorderWidth(); + //hostRect.width -= 2 * border; + + if (hostRect.width != contentRect.width) { + if (vVisible && vBar != null) { + hostRect.width -= vBar.getSize().x; + } + } + + if (contentRect.width > hostRect.width) { + result = true; + } + + return result; + } + + void hScroll() { + if (tableContent == null) return; + Point location = tableContent.getLocation (); + int hSelection = hBar.getSelection(); + tableContent.setLocation (-hSelection, location.y); + } + + public void setMinSize(Point size) { + if (size == null) { + setMinSize(0, 0); + } else { + setMinSize(size.x, size.y); + } + } + + public void setMinSize(int width, int height) { + checkWidget(); + if (width == minWidth && height == minHeight) { + return; + } + + minWidth = Math.max(0, width); + minHeight = Math.max(0, height); + layout(false); + } + + public void showControl(Control control) { + checkWidget(); + if (control == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + if (control.isDisposed()) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + if (!contains(control)) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + + Rectangle itemRect = getDisplay().map(control.getParent(), this, control.getBounds()); + Rectangle area = getClientArea(); + Point origin = getOrigin(); + + if (itemRect.x < 0) { + origin.x = Math.max(0, origin.x + itemRect.x); + } else { + if (area.width < itemRect.x + itemRect.width) { + origin.x = Math.max(0, + origin.x + itemRect.x + + Math.min(itemRect.width, area.width) + - area.width); + } + } + + if (itemRect.y < 0) { + origin.y = Math.max(0, origin.y + itemRect.y); + } else { + if (area.height < itemRect.y + itemRect.height) { + origin.y = Math.max(0, + origin.y + itemRect.y + + Math.min(itemRect.height, area.height) + - area.height); + } + } + + setOrigin(origin); + } + + public int getScrollBarWidth() { + return scrollBarWidth; + } + + public void setScrollBarWidth(int scrollBarWidth) { + this.scrollBarWidth = scrollBarWidth; + } +} diff --git a/src/org/tizen/emulator/manager/ui/table/TableScrolledCompositeLayout.java b/src/org/tizen/emulator/manager/ui/table/TableScrolledCompositeLayout.java new file mode 100644 index 0000000..b4a01e0 --- /dev/null +++ b/src/org/tizen/emulator/manager/ui/table/TableScrolledCompositeLayout.java @@ -0,0 +1,167 @@ +/* + * Emulator Manager + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * JiHye Kim + * Minkee Lee + * SeokYeon Hwang + * Sangho Park + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +package org.tizen.emulator.manager.ui.table; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Layout; + +import com.codeaffine.eclipse.swt.widget.scrollbar.FlatScrollBar; + +public class TableScrolledCompositeLayout extends Layout { + boolean inLayout = false; + static final int DEFAULT_WIDTH = 64; + static final int DEFAULT_HEIGHT = 64; + + private FlatScrollBar hBar; + private FlatScrollBar vBar; + + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, + boolean flushCache) { + TableScrolledComposite sc = (TableScrolledComposite)composite; + Point size = new Point(DEFAULT_WIDTH, DEFAULT_HEIGHT); + if (sc.tableContent != null) { + Point currentSize = sc.tableContent.getSize(); + size.x = currentSize.x; + size.y = currentSize.y; + } + size.x = Math.max(size.x, sc.minWidth); + size.y = Math.max(size.y, sc.minHeight); + if (wHint != SWT.DEFAULT) size.x = wHint; + if (hHint != SWT.DEFAULT) size.y = hHint; + return size; + } + + @Override + protected void layout(Composite composite, boolean flushCache) { + if (inLayout) { + return; + } + TableScrolledComposite sc = (TableScrolledComposite)composite; + if (sc.tableContent == null) { + return; + } + + Rectangle hostRect = sc.getClientArea(); + hBar = sc.hBar; + vBar = sc.vBar; + if (hBar != null) { + if (hBar.getSize().y >= hostRect.height) { + return; + } + } + if (vBar != null) { + if (vBar.getSize().x >= hostRect.width) { + return; + } + } + + inLayout = true; + Rectangle tableRect = sc.tableContent.getBounds(); + Rectangle tableItemRect = sc.tableItemContent.getBounds(); + + boolean hVisible = sc.needHScroll(tableRect, false); + boolean vVisible = sc.needVScroll(tableItemRect, hVisible); + int scrollBarWidth = sc.getScrollBarWidth(); + + if (!hVisible && vVisible) { + hVisible = sc.needHScroll(tableRect, vVisible); + } + if (hBar != null) { + hBar.setVisible(hVisible); + if (hVisible) { + hBar.setBounds(0, hostRect.height-scrollBarWidth, + hostRect.width, scrollBarWidth); + } else { + hBar.setBounds(0, 0, 0, 0); + } + } + if (vBar != null) { + vBar.setVisible(vVisible); + if (vVisible) { + // 30 is table column height + vBar.setBounds(hostRect.width - scrollBarWidth, 30, scrollBarWidth, + (hVisible ? hostRect.height - 30 - scrollBarWidth + : hostRect.height - 30)); + } else { + vBar.setBounds(0, 0, 0, 0); + } + } + +// boolean isChangeTableRect = false; +// boolean isChangeTableItemRect = false; + GC gc = new GC (sc); + if (hBar != null && hBar.isVisible()) { + int hPage = tableRect.width - hostRect.width; + hBar.setMaximum(hPage); + hBar.setIncrementButtonLength(6); + hBar.setIncrement(gc.getFontMetrics().getAverageCharWidth()); + hBar.setPageIncrement(hostRect.width / 2); + int hSelection = hBar.getSelection (); + if (hSelection >= hPage) { + if (hPage <= 0) { + hSelection = 0; + hBar.setSelection(0); + } + tableRect.x = -hSelection; + } + } + + // using tableItemRect + if (vBar != null && vBar.isVisible()) { + int vPage = tableItemRect.height - hostRect.height; + vBar.setMaximum(vPage); + //vBar.setIncrement(30); + vBar.setIncrementButtonLength(6); + vBar.setPageIncrement(hostRect.height / 2); + int vSelection = vBar.getSelection (); + if (vSelection >= vPage) { + if (vPage <= 0) { + vSelection = 0; + vBar.setSelection(0); + } + //tableRect.y = -vSelection; + tableItemRect.y = -vSelection; + } + } + + gc.dispose (); + + sc.tableContent.setBounds(tableRect); + sc.tableItemContent.setBounds(tableItemRect); + inLayout = false; + } + +} diff --git a/src/org/tizen/emulator/manager/ui/table/TextSizeUtil.java b/src/org/tizen/emulator/manager/ui/table/TextSizeUtil.java new file mode 100644 index 0000000..93091ae --- /dev/null +++ b/src/org/tizen/emulator/manager/ui/table/TextSizeUtil.java @@ -0,0 +1,77 @@ +/* + * Emulator Manager + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * JiHye Kim + * Minkee Lee + * SeokYeon Hwang + * Sangho Park + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +package org.tizen.emulator.manager.ui.table; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Display; + +public class TextSizeUtil { + static final int STRING_EXTENT = 0; + static final int TEXT_EXTENT = 1; + static final int MARKUP_EXTENT = 2; + + public static final int OMITLEN = 12; + public static final String OMIT = "..."; + + public static float getCharHeight(Font font) { + float result; + FontData fontData = font.getFontData()[0]; + result = fontData.getHeight() * 0.48f; + + if ((fontData.getStyle() & SWT.BOLD)!= 0) { + result *= 1.45; + } + return result; + } + + public static int textExtent(Font font, String text) { + + GC gc = new GC(Display.getCurrent()); + gc.setFont(font); + FontMetrics metrics = gc.getFontMetrics(); + int width = metrics.getAverageCharWidth(); + gc.dispose(); + return width * text.length(); + } + + public static int stringExtend(Font font, String text) { + GC gc = new GC(Display.getCurrent()); + gc.setFont(font); + Point p = gc.stringExtent(text); + gc.dispose(); + return p.x; + } +} diff --git a/widget_src/com/codeaffine/eclipse/swt/util/ActionScheduler.java b/widget_src/com/codeaffine/eclipse/swt/util/ActionScheduler.java new file mode 100644 index 0000000..54ed23c --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/util/ActionScheduler.java @@ -0,0 +1,18 @@ +package com.codeaffine.eclipse.swt.util; + +import org.eclipse.swt.widgets.Display; + +public class ActionScheduler { + + private final Display display; + private final Runnable action; + + public ActionScheduler( Display display, Runnable action ) { + this.display = display; + this.action = action; + } + + public void schedule( int delay ) { + display.timerExec( delay, action ); + } +} diff --git a/widget_src/com/codeaffine/eclipse/swt/util/ButtonClick.java b/widget_src/com/codeaffine/eclipse/swt/util/ButtonClick.java new file mode 100644 index 0000000..a8472e7 --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/util/ButtonClick.java @@ -0,0 +1,45 @@ +package com.codeaffine.eclipse.swt.util; + +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Control; + +public class ButtonClick { + + public static final int LEFT_BUTTON = 1; + + private boolean armed; + + public boolean isArmed() { + return armed; + } + + public void arm( MouseEvent event ) { + if( event.button == LEFT_BUTTON ) { + armed = true; + } + } + + public void disarm() { + armed = false; + } + + public void trigger( MouseEvent event, Runnable action ) { + try { + doTrigger( event, action ); + } finally { + disarm(); + } + } + + private void doTrigger( MouseEvent event, Runnable action ) { + if( armed && inRange( event ) ) { + action.run(); + } + } + + private static boolean inRange( MouseEvent event ) { + Point size = ( ( Control )event.widget ).getSize(); + return event.x >= 0 && event.x <= size.x && event.y >= 0 && event.y <= size.y; + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/util/DragDetector.java b/widget_src/com/codeaffine/eclipse/swt/util/DragDetector.java new file mode 100644 index 0000000..1525a1e --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/util/DragDetector.java @@ -0,0 +1,60 @@ +package com.codeaffine.eclipse.swt.util; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; + +// TODO [fappel]: This is a workaround for a problem described here: +// http://stackoverflow.com/questions/3908290/mousedown-events-are-not-delivered-until-mouseup-when-a-drag-source-is-present +// This seems to be related to https://bugs.eclipse.org/bugs/show_bug.cgi?id=328396 +// which is resolved. As it did not work on my setup I adapted the workaround of the last +// stackoverflow answer. + +public class DragDetector { + + int lastMouseX; + int lastMouseY; + boolean dragEventGenerated; + + private final Control control; + private final int sensibility; + + public DragDetector( Control control, int sensibility ) { + this.control = control; + this.sensibility = sensibility; + this.control.setDragDetect( false ); + } + + public void mouseMove( MouseEvent e ) { + if( ( e.stateMask & SWT.BUTTON1 ) > 0 ) { + int deltaX = lastMouseX - e.x; + int deltaY = lastMouseY - e.y; + int dragDistance = deltaX * deltaX + deltaY * deltaY; + if( !dragEventGenerated && dragDistance > sensibility ) { + dragEventGenerated = true; + Event event = createDragEvent( e ); + control.notifyListeners( SWT.DragDetect, event ); + } + lastMouseX = e.x; + lastMouseY = e.y; + } + } + + public void dragHandled() { + dragEventGenerated = false; + } + + private Event createDragEvent( MouseEvent e ) { + Event event = new Event(); + event.type = SWT.DragDetect; + event.display = control.getDisplay(); + event.widget = control; + event.button = e.button; + event.stateMask = e.stateMask; + event.time = e.time; + event.x = e.x; + event.y = e.y; + return event; + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/util/MouseDownActionTimer.java b/widget_src/com/codeaffine/eclipse/swt/util/MouseDownActionTimer.java new file mode 100644 index 0000000..f6c614e --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/util/MouseDownActionTimer.java @@ -0,0 +1,37 @@ +package com.codeaffine.eclipse.swt.util; + +import org.eclipse.swt.widgets.Display; + +public class MouseDownActionTimer implements Runnable { + + public static final int INITIAL_DELAY = 300; + public static final int FAST_DELAY = 50; + + private final ActionScheduler scheduler; + private final TimerAction timerAction; + private final ButtonClick mouseClick; + + public interface TimerAction extends Runnable { + boolean isEnabled(); + } + + public MouseDownActionTimer( TimerAction timerAction, ButtonClick mouseClick, Display display ) { + this.scheduler = new ActionScheduler( display, this ); + this.timerAction = timerAction; + this.mouseClick = mouseClick; + } + + public void activate() { + if( timerAction.isEnabled() ) { + scheduler.schedule( INITIAL_DELAY ); + } + } + + @Override + public void run() { + if( mouseClick.isArmed() && timerAction.isEnabled() ) { + timerAction.run(); + scheduler.schedule( FAST_DELAY ); + } + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/util/ReadAndDispatch.java b/widget_src/com/codeaffine/eclipse/swt/util/ReadAndDispatch.java new file mode 100644 index 0000000..2991a25 --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/util/ReadAndDispatch.java @@ -0,0 +1,21 @@ +package com.codeaffine.eclipse.swt.util; + +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Widget; + +public class ReadAndDispatch { + + public void spinLoop( Widget widget ) { + spinLoop( widget, Long.MAX_VALUE - System.currentTimeMillis() ); + } + + public void spinLoop( Widget widget, long duration ) { + long end = System.currentTimeMillis() + duration; + while( !widget.isDisposed() && ( end - System.currentTimeMillis() ) > 0 ) { + Display display = widget.getDisplay(); + if( !display.readAndDispatch() ) { + display.sleep(); + } + } + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/util/ResourceLoader.java b/widget_src/com/codeaffine/eclipse/swt/util/ResourceLoader.java new file mode 100644 index 0000000..f70ff91 --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/util/ResourceLoader.java @@ -0,0 +1,53 @@ +package com.codeaffine.eclipse.swt.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ResourceLoader { + + private static final int BUFFER_SIZE = 8192; + + public byte[] load( String resourcePath ) { + return load( resourcePath, getClass().getClassLoader() ); + } + + public byte[] load( String resourcePath, ClassLoader classLoader ) { + return load( verifyExistance( classLoader.getResourceAsStream( resourcePath ), resourcePath ) ); + } + + private static byte[] load( InputStream input ) { + try { + return doLoad( input ); + } catch( IOException e ) { + throw new IllegalStateException( e ); + } finally { + closeSilently( input ); + } + } + + private static byte[] doLoad( InputStream input ) throws IOException { + ByteArrayOutputStream result = new ByteArrayOutputStream(); + byte[] buffer = new byte[ BUFFER_SIZE ]; + int bytesRead; + while( ( bytesRead = input.read( buffer ) ) != -1 ) { + result.write( buffer, 0, bytesRead ); + } + return result.toByteArray(); + } + + private static InputStream verifyExistance( InputStream inputStream , String resourcePath ) { + if( inputStream == null ) { + throw new IllegalArgumentException( "'" + resourcePath + "' does not exist." ); + } + return inputStream; + } + + private static void closeSilently( InputStream input ) { + try { + input.close(); + } catch( IOException e ) { + throw new IllegalStateException( e ); + } + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/util/UIThreadSynchronizer.java b/widget_src/com/codeaffine/eclipse/swt/util/UIThreadSynchronizer.java new file mode 100644 index 0000000..4ab2b83 --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/util/UIThreadSynchronizer.java @@ -0,0 +1,82 @@ +package com.codeaffine.eclipse.swt.util; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.SWTException; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Widget; + + +public class UIThreadSynchronizer { + + public void asyncExec( Widget contextWidget, Runnable runnable ) { + new UIThreadRunner( contextWidget ).asyncExec( runnable ); + } + + public void syncExec( Widget contextWidget, Runnable runnable ) { + new UIThreadRunner( contextWidget ).syncExec( runnable ); + } + + private static class UIThreadRunner { + private final Widget contextWidget; + + UIThreadRunner( Widget contextWidget ) { + this.contextWidget = contextWidget; + } + + void asyncExec( Runnable runnable ) { + Display display = getDisplay(); + if( display != null ) { + display.asyncExec( new GuardedRunnable( contextWidget, runnable ) ); + } + } + + void syncExec( Runnable runnable ) { + Display display = getDisplay(); + if( display != null ) { + display.syncExec( new GuardedRunnable( contextWidget, runnable ) ); + } + } + + private Display getDisplay() { + Display result = null; + if( !contextWidget.isDisposed() ) { + result = safeGetDisplay(); + } + return result; + } + + private Display safeGetDisplay() { + Display result = null; + try { + result = contextWidget.getDisplay(); + } catch( SWTException exception ) { + handleSWTException( exception ); + } + return result; + } + + private static void handleSWTException( SWTException exception ) { + if( exception.code != SWT.ERROR_WIDGET_DISPOSED ) { + throw exception; + } + } + } + + private static class GuardedRunnable implements Runnable { + private final Widget contextWidget; + private final Runnable runnable; + + GuardedRunnable( Widget contextWidget, Runnable runnable ) { + this.contextWidget = contextWidget; + this.runnable = runnable; + } + + @Override + public void run() { + if( !contextWidget.isDisposed() ) { + runnable.run(); + } + } + } + +} diff --git a/widget_src/com/codeaffine/eclipse/swt/util/Unsafe.java b/widget_src/com/codeaffine/eclipse/swt/util/Unsafe.java new file mode 100644 index 0000000..a9ac7b3 --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/util/Unsafe.java @@ -0,0 +1,94 @@ +package com.codeaffine.eclipse.swt.util; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.ProtectionDomain; + +public class Unsafe { + + private Object unsafeInstance; + private Method allocateInstance; + private Method defineClass; + + public Unsafe() { + try { + unsafeInstance = getUnsafeInstance(); + allocateInstance = getAllocateInstanceMethod(); + defineClass = getDefineClassMethod(); + } catch( Exception unresolvable ) { + throw new IllegalStateException( unresolvable ); + } + } + + public T newInstance( Class type ) { + try { + return type.cast( allocateInstance.invoke( unsafeInstance, type ) ); + } catch( IllegalAccessException iae ) { + throw new RuntimeException( iae ); + } catch( InvocationTargetException ite ) { + throw toRuntimeException( ite ); + } + } + + public Class defineClass( + String name, byte[] bytes, int offset, int length, ClassLoader loader, ProtectionDomain domain ) + { + Class result = loadFromClassLoader( name, loader ); + if( result == null ) { + result = defineClassUnsafe( name, bytes, offset, length, loader, domain ); + } + return result; + } + + private static Object getUnsafeInstance() throws Exception { + Field unsafeField = getUnsafeClass().getDeclaredField( "theUnsafe" ); + unsafeField.setAccessible( true ); + return unsafeField.get( null ); + } + + private static Method getAllocateInstanceMethod() throws Exception { + return getUnsafeClass().getMethod( "allocateInstance", Class.class ); + } + + private static Method getDefineClassMethod() throws Exception { + return getUnsafeClass().getMethod( + "defineClass", String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class ); + } + + private static Class getUnsafeClass() throws ClassNotFoundException { + return Unsafe.class.getClassLoader().loadClass( "sun.misc.Unsafe" ); + } + + private static Class loadFromClassLoader( String name, ClassLoader loader ) { + try { + return loader.loadClass( name ); + } catch( SecurityException ignored ) { + return null; + } catch( ClassNotFoundException ignored ) { + return null; + } + } + + private Class defineClassUnsafe( + String name, byte[] bytes, int offset, int length, ClassLoader loader, ProtectionDomain domain ) + { + try { + return ( Class )defineClass.invoke( + unsafeInstance, name, bytes, Integer.valueOf( offset), Integer.valueOf( length ), loader, domain ); + } catch( RuntimeException rte ) { + throw rte; + } catch( InvocationTargetException ite ) { + throw toRuntimeException( ite ); + } catch( IllegalAccessException iae ) { + throw new RuntimeException( iae ); + } + } + + private static RuntimeException toRuntimeException( InvocationTargetException e ) { + if( e.getCause() instanceof RuntimeException ) { + return ( RuntimeException )e.getCause(); + } + return new RuntimeException( e.getCause() ); + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ClickControl.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ClickControl.java new file mode 100644 index 0000000..baeefdc --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ClickControl.java @@ -0,0 +1,101 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.MouseTrackListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +import com.codeaffine.eclipse.swt.util.ButtonClick; +import com.codeaffine.eclipse.swt.util.MouseDownActionTimer; +import com.codeaffine.eclipse.swt.util.MouseDownActionTimer.TimerAction; + +class ClickControl extends ControlAdapter implements ViewComponent, TimerAction, MouseListener, MouseTrackListener { + + private final MouseDownActionTimer mouseDownActionTimer; + private final ClickAction clickAction; + private final ButtonClick buttonClick; + private final Label control; + private final ImageUpdate imageUpdate; + + public interface ClickAction extends Runnable { + void setCoordinates( int x, int y ); + } + + ClickControl( Composite parent, ClickAction clickAction, int maxExtension ) { + this.control = new Label( parent, SWT.NONE ); + this.imageUpdate = new ImageUpdate( control, maxExtension ); + this.buttonClick = new ButtonClick(); + this.mouseDownActionTimer = new MouseDownActionTimer( this, buttonClick, control.getDisplay() ); + this.clickAction = clickAction; + this.control.addMouseTrackListener( this ); + this.control.addMouseListener( this ); + this.control.addControlListener( this ); + } + + @Override + public void controlResized( ControlEvent event ) { + imageUpdate.update(); + } + + @Override + public Label getControl() { + return control; + } + + @Override + public void mouseDown( MouseEvent event ) { + buttonClick.arm( event ); + clickAction.setCoordinates( event.x, event.y ); + mouseDownActionTimer.activate(); + } + + @Override + public void mouseUp( MouseEvent event ) { + buttonClick.trigger( event, clickAction ); + } + + @Override + public void run() { + clickAction.run(); + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public void mouseExit( MouseEvent event ) { + buttonClick.disarm(); + } + + void setForeground( Color color ) { + imageUpdate.setForeground( color ); + } + + Color getForeground() { + return imageUpdate.getForeground(); + } + + void setBackground( Color color ) { + imageUpdate.setBackground( color ); + } + + Color getBackground() { + return imageUpdate.getBackground(); + } + + @Override + public void mouseEnter( MouseEvent event ) {} + + @Override + public void mouseHover( MouseEvent event ) {} + + @Override + public void mouseDoubleClick( MouseEvent event ) {} +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ComponentDistribution.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ComponentDistribution.java new file mode 100644 index 0000000..54e1cdf --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ComponentDistribution.java @@ -0,0 +1,107 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import static java.lang.Math.max; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +class ComponentDistribution { + + private static final int MIN_DRAG_LENGTH = 17; + + final int upFastLength; + final int dragStart; + final int dragLength; + final int downFastStart; + final int downFastLength; + final int downStart; + final int buttonLen; + + ComponentDistribution( int buttonLen, int len, int range, int pos, int thumb ) { + int slideLen = slideLen( buttonLen, len ); + int relDragLen = relDragLen( slideLen, range, thumb ); + int minDragLength = max( MIN_DRAG_LENGTH, buttonLen ); + int interval = interval( range, relDragLen, minDragLength ); + this.dragLength = dragLen( minDragLength, relDragLen ); + this.upFastLength = upFastLen( minDragLength, interval, pos, slideLen, relDragLen, dragLength ); + this.downStart = downStart( buttonLen, len ); + this.downFastStart = downFastStart( buttonLen, upFastLength, dragLength ); + this.dragStart = dragStart( buttonLen, upFastLength ); + this.downFastLength = downFastLen( minDragLength, interval, pos, slideLen, relDragLen, dragLength, upFastLength ); + this.buttonLen = buttonLen; + } + + + + private static int slideLen( int buttonLen, int len ) { + return len - buttonLen * 2; + } + + private static int relDragLen( int slideLen, int range, int thumb ) { + return divide( slideLen * thumb, range ); + } + + private static int interval( int range, int relDragLen, int minDragLength ) { + int result = range; + if( useMinDragLen( minDragLength, relDragLen ) ) { + result += minDragLength - relDragLen / 2; + } + return result; + } + + private static int dragLen( int buttonLen, int relDragLen ) { + return max( relDragLen, buttonLen ); + } + + private static int upFastLen( int buttonLen, int range, int pos, int slideLen, int relDragLen, int dragLen ) { + int result = slideLen * pos / range; + if( useMinDragLen( buttonLen, relDragLen ) ) { + result -= divide( ( dragLen - relDragLen ) * pos, range ); + } + return result; + } + + private static int downStart( int buttonLen, int len ) { + return len - buttonLen; + } + + private static int downFastStart( int buttonLen, int upFastLength, int dragLength ) { + return buttonLen + upFastLength + dragLength; + } + + private static int dragStart( int buttonLen, int upFastLen ) { + return buttonLen + upFastLen; + } + + private static int downFastLen( + int buttonLen, int range, int pos, int slideLen, int relDragLen, int dragLen, int upFastLen ) + { + int result = divide( slideLen * ( range - pos ), range ) - dragLen; + if( useMinDragLen( buttonLen, relDragLen ) ) { + result += divide( ( dragLen - relDragLen ) * pos, range ); + } + return adjustDownFastLen( result, slideLen, dragLen, upFastLen ); + } + + private static boolean useMinDragLen( int buttonLen, int relDragLen ) { + return relDragLen < buttonLen; + } + + static int divide( int dividend, int divisor ) { + BigDecimal bigDividend = new BigDecimal( dividend ); + BigDecimal bigDivisor = new BigDecimal( divisor ); + return bigDividend .divide( bigDivisor, 0, RoundingMode.HALF_EVEN ) .intValue(); + } + + private static int adjustDownFastLen( int tentative, int slideLen, int dragLen, int upFastLen ) { + // TODO [fappel]: Without this there is a flickering of the downFast label of one pixel. + // Check whether this can be resolved by better rounding or whatsoever. + int result = tentative; + if( slideLen < upFastLen + dragLen + result ) { + result--; + } else if( slideLen > upFastLen + dragLen + result ) { + result++; + } + return result; + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/Decrementer.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/Decrementer.java new file mode 100644 index 0000000..89a7402 --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/Decrementer.java @@ -0,0 +1,24 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import org.eclipse.swt.SWT; + +import com.codeaffine.eclipse.swt.widget.scrollbar.ClickControl.ClickAction; + +class Decrementer implements ClickAction { + + private final FlatScrollBar scrollBar; + + Decrementer( FlatScrollBar scrollBar ) { + this.scrollBar = scrollBar; + } + + @Override + public void run() { + int selection = scrollBar.getSelection() - scrollBar.getIncrement(); + scrollBar.setSelectionInternal( selection, SWT.ARROW_UP ); + } + + @Override + public void setCoordinates( int x, int y ) { + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/Direction.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/Direction.java new file mode 100644 index 0000000..18a45ab --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/Direction.java @@ -0,0 +1,203 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import static java.lang.Math.max; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +enum Direction { + + HORIZONTAL( SWT.HORIZONTAL ) { + + @Override + protected void layout( FlatScrollBar scrollBar, int buttonLength ) { + ComponentDistribution distribution = calculateComponentDistribution( scrollBar, buttonLength ); + Rectangle[] componentBounds = calculateComponentBounds( distribution, scrollBar ); + applyComponentBounds( scrollBar, componentBounds ); + } + + private ComponentDistribution calculateComponentDistribution( FlatScrollBar scrollBar, int buttonLength ) { + return calculateComponentDistribution( scrollBar, buttonLength, getControlBounds( scrollBar ).width ); + } + + private Rectangle[] calculateComponentBounds( ComponentDistribution distribution, FlatScrollBar scrollBar ) { + int width = getControlBounds( scrollBar ).width; + int height = getControlBounds( scrollBar ).height - BAR_BREADTH + 1; + int balance = getRoundingBalance( distribution, scrollBar ); + return new Rectangle[] { + calcButtons( distribution, width, $( 0, CLEARANCE, distribution.buttonLen, height ) ), + $( distribution.buttonLen, CLEARANCE, distribution.upFastLength, height ), + calcDrag( distribution, $( distribution.dragStart, CLEARANCE, distribution.dragLength + balance, height ) ), + $( distribution.downFastStart, CLEARANCE, distribution.downFastLength - balance, height ), + calcButtons( distribution, width, $( distribution.downStart, CLEARANCE, distribution.buttonLen, height ) ) + }; + } + + private Rectangle calcButtons( ComponentDistribution distribution, int length, Rectangle bounds ) { + Rectangle result = bounds; + if( length <= distribution.buttonLen* 2 ) { + int downStart = calcDownStartForSmallLength( bounds.x, length ); + result = $( downStart, CLEARANCE, length / 2, bounds.height ); + } + return result; + } + + @Override + protected void setDefaultSize( Control control ) { + Point size = control.getSize(); + control.setSize( size.x, BAR_BREADTH ); + } + + @Override + protected Point computeSize( Composite composite, int wHint, int hHint, boolean changed ) { + int x = wHint == SWT.DEFAULT ? composite.getParent().getClientArea().width : wHint; + return new Point( x, BAR_BREADTH ); + } + + @Override + protected void expand( Control control, int maxEexpansion ) { + Rectangle bounds = control.getBounds(); + int expand = expand( bounds.height, maxEexpansion ); + control.setBounds( bounds.x, bounds.y - expand, bounds.width, bounds.height + expand ); + } + }, + + VERTICAL( SWT.VERTICAL ) { + + @Override + protected void layout( FlatScrollBar scrollBar, int buttonLength ) { + ComponentDistribution calculation = calculateComponentDistribution( scrollBar, buttonLength ); + applyComponentBounds( scrollBar, calculateComponentBounds( calculation, scrollBar ) ); + } + + private ComponentDistribution calculateComponentDistribution( FlatScrollBar scrollBar, int buttonLength ) { + return calculateComponentDistribution( scrollBar, buttonLength, getControlBounds( scrollBar ).height ); + } + + private Rectangle[] calculateComponentBounds( ComponentDistribution distribution, FlatScrollBar scrollBar ) { + int width = getControlBounds( scrollBar ).width - BAR_BREADTH + 1; + int height = getControlBounds( scrollBar ).height; + int balance = getRoundingBalance( distribution, scrollBar ); + return new Rectangle[] { + calculateButtons( distribution, height, $( CLEARANCE, 0, width, distribution.buttonLen ) ), + $( CLEARANCE, distribution.buttonLen, width, distribution.upFastLength ), + calcDrag( distribution, $( CLEARANCE, distribution.dragStart, width, distribution.dragLength + balance ) ), + $( CLEARANCE, distribution.downFastStart, width, distribution.downFastLength - balance ), + calculateButtons( distribution, height, $( CLEARANCE, distribution.downStart, width, distribution.buttonLen ) ) + }; + } + + private Rectangle calculateButtons( ComponentDistribution distribution, int length, Rectangle bounds ) { + Rectangle result = bounds; + if( length <= distribution.buttonLen * 2 ) { + int downStart = calcDownStartForSmallLength( bounds.y, length ); + result = $( CLEARANCE, downStart, bounds.width, length / 2 ); + } + return result; + } + + @Override + protected void setDefaultSize( Control control ) { + Point size = control.getSize(); + control.setSize( BAR_BREADTH, size.y ); + } + + @Override + protected Point computeSize( Composite composite, int wHint, int hHint, boolean changed ) { + int y = hHint == SWT.DEFAULT ? composite.getParent().getClientArea().height : hHint; + return new Point( BAR_BREADTH, y ); + } + + @Override + protected void expand( Control control, int maxExpansion ) { + Rectangle bounds = control.getBounds(); + int expand = expand( bounds.width, maxExpansion ); + control.setBounds( bounds.x - expand, bounds.y, bounds.width + expand, bounds.height ); + } + }; + + static final Rectangle EMPTY_RECTANGLE = $( 0, 0, 0, 0 ); + static final int BAR_BREADTH = 6; + static final int CLEARANCE = BAR_BREADTH - 2; + + private final int value; + + protected abstract void layout( FlatScrollBar scrollBar, int buttonLength ); + protected abstract void setDefaultSize( Control control ); + protected abstract Point computeSize( Composite comp, int wHint, int hHint, boolean changed ); + protected abstract void expand( Control control, int maxExpansion ); + + Direction( int value ) { + this.value = value; + } + + public int value() { + return value; + } + + private static ComponentDistribution calculateComponentDistribution( + FlatScrollBar scrollBar , int buttonLength , int length ) + { + int range = scrollBar.getMaximum() - scrollBar.getMinimum(); + int position = scrollBar.getSelection() - scrollBar.getMinimum(); + int thumb = scrollBar.getThumb(); + return new ComponentDistribution( buttonLength, length, range, position, thumb ); + } + + private static Rectangle getControlBounds( FlatScrollBar scrollBar ) { + return scrollBar.getClientArea(); + } + + private static void applyComponentBounds( FlatScrollBar scrollBar, Rectangle[] bounds ) { + scrollBar.up.getControl().setBounds( bounds[ 0 ] ); + scrollBar.upFast.getControl().setBounds( bounds[ 1 ] ); + scrollBar.drag.getControl().setBounds( bounds[ 2 ] ); + scrollBar.downFast.getControl().setBounds( bounds[ 3 ] ); + scrollBar.down.getControl().setBounds( bounds[ 4 ] ); + } + + // TODO [fappel]: There is a 1 pixel rounding problem at the seam of drag/downFast with down. + // Seems to work but I would prefer a better solution if possible + private static int getRoundingBalance( ComponentDistribution calculation, FlatScrollBar scrollBar ) { + int result = 0; + int maximumSelection = scrollBar.getMaximum() - scrollBar.getThumb(); + if( scrollBar.getSelection() == maximumSelection && 0 != calculation.downFastLength ) { + result = 1; + } + return result; + } + + private static int expand( int toExpand, int maxExpansion ) { + return max( 0, BAR_BREADTH + maxExpansion - max( BAR_BREADTH, toExpand ) ); + } + + private static Rectangle calcDrag( ComponentDistribution distribution, Rectangle bounds ) { + Rectangle result = bounds; + if( isUndercutOfDragVisibility( distribution ) ) { + result = EMPTY_RECTANGLE; + } + return result; + } + + private static boolean isUndercutOfDragVisibility( ComponentDistribution distribution ) { + return distribution.dragLength + distribution.buttonLen >= distribution.downStart; + } + + private static int calcDownStartForSmallLength( int position, int length ) { + int result = position; + if( isDownStartPosition( position ) ) { + result = length / 2; + } + return result; + } + private static boolean isDownStartPosition( int position ) { + return position > 0 || position < 0; + } + + private static Rectangle $( int x, int y, int width, int height ) { + return new Rectangle( x, y , width , height ); + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/DragControl.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/DragControl.java new file mode 100644 index 0000000..8a95eb6 --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/DragControl.java @@ -0,0 +1,106 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.DragDetectEvent; +import org.eclipse.swt.events.DragDetectListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.MouseMoveListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +import com.codeaffine.eclipse.swt.util.DragDetector; + +class DragControl + extends ControlAdapter + implements ViewComponent, DragDetectListener, MouseListener, MouseMoveListener +{ + + private final DragDetector dragDetector; + private final ImageUpdate imageUpdate; + private final DragAction dragAction; + private final Label control; + + private Point startingPosition; + + public interface DragAction { + void start(); + void run( int startX, int startY, int currentX, int currentY ); + void end(); + } + + DragControl( Composite parent, DragAction dragAction, int maxExpansion ) { + this.control = new Label( parent, SWT.NONE ); + this.imageUpdate = new ImageUpdate( control, maxExpansion ); + this.dragDetector = new DragDetector( control, 0 ); + this.dragAction = dragAction; + initializeControl(); + } + + @Override + public Label getControl() { + return control; + } + + @Override + public void dragDetected( DragDetectEvent event ) { + if( startingPosition != null ) { + dragAction.run( startingPosition.x, startingPosition.y, event.x, event.y ); + } + dragDetector.dragHandled(); + } + + @Override + public void mouseDown( MouseEvent event ) { + startingPosition = new Point( event.x, event.y ); + dragAction.start(); + } + + @Override + public void mouseUp( MouseEvent event ) { + if( startingPosition != null ) { + dragAction.end(); + } + startingPosition = null; + } + + @Override + public void mouseMove( MouseEvent event ) { + dragDetector.mouseMove( event ); + } + + @Override + public void controlResized( ControlEvent event ) { + imageUpdate.update(); + } + + void setForeground( Color color ) { + imageUpdate.setForeground( color ); + } + + Color getForeground() { + return imageUpdate.getForeground(); + } + + Color getBackground() { + return imageUpdate.getBackground(); + } + + void setBackground( Color color ) { + imageUpdate.setBackground( color ); + } + + private void initializeControl( ) { + control.addMouseListener( this ); + control.addMouseMoveListener( this ); + control.addControlListener( this ); + control.addDragDetectListener( this ); + } + + @Override + public void mouseDoubleClick( MouseEvent event ) {} +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/DragShifter.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/DragShifter.java new file mode 100644 index 0000000..3d992af --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/DragShifter.java @@ -0,0 +1,59 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import static com.codeaffine.eclipse.swt.widget.scrollbar.Direction.HORIZONTAL; +import static com.codeaffine.eclipse.swt.widget.scrollbar.ShiftData.calculateSelectionRange; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; + +import com.codeaffine.eclipse.swt.widget.scrollbar.DragControl.DragAction; + +final class DragShifter implements DragAction { + + private final FlatScrollBar scrollBar; + private final int buttonLength; + + public DragShifter( FlatScrollBar scrollBar, int buttonLength ) { + this.scrollBar = scrollBar; + this.buttonLength = buttonLength; + } + + @Override + public void start() { + scrollBar.notifyListeners( SWT.DRAG ); + } + + @Override + public void run( int startX, int startY, int currentX, int currentY ) { + ShiftData shiftData = newShiftData( startX, startY, currentX, currentY ); + if( shiftData.canShift() ) { + int selectionRange = calculateSelectionRange( scrollBar ); + int selectionDelta = shiftData.calculateSelectionDelta( selectionRange ); + int selection = scrollBar.getSelection() + selectionDelta; + scrollBar.setSelectionInternal( selection, SWT.DRAG ); + } + } + + @Override + public void end() { + scrollBar.notifyListeners( SWT.NONE ); + } + + private ShiftData newShiftData( int startX, int startY, int currentX, int currentY ) { + ShiftData result; + if( scrollBar.direction == HORIZONTAL ) { + result = new ShiftData( buttonLength, getScrollBarSize().x, getDragSize().x, currentX - startX ); + } else { + result = new ShiftData( buttonLength, getScrollBarSize().y, getDragSize().y, currentY - startY ); + } + return result; + } + + private Point getScrollBarSize() { + return scrollBar.getSize(); + } + + private Point getDragSize() { + return scrollBar.drag.getControl().getSize(); + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/FastDecrementer.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/FastDecrementer.java new file mode 100644 index 0000000..290d592 --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/FastDecrementer.java @@ -0,0 +1,49 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; + +import com.codeaffine.eclipse.swt.widget.scrollbar.ClickControl.ClickAction; + +class FastDecrementer implements ClickAction { + + private final FlatScrollBar scrollBar; + + private int x; + private int y; + + FastDecrementer( FlatScrollBar scrollBar ) { + this.scrollBar = scrollBar; + } + + @Override + public void run() { + Rectangle drag = getDragBounds(); + Point mouse = getMouseLocation(); + if( mouse.x <= drag.x || mouse.y <= drag.y ) { + int selection = scrollBar.getSelection() - scrollBar.getPageIncrement(); + scrollBar.setSelectionInternal( selection, SWT.PAGE_UP ); + } + } + + @Override + public void setCoordinates( int x, int y ) { + this.x = x; + this.y = y; + } + + private Point getMouseLocation() { + return getDisplay().map( scrollBar.upFast.getControl(), null, x, y ); + } + + private Rectangle getDragBounds() { + Rectangle dragBounds = scrollBar.drag.getControl().getBounds(); + return getDisplay().map( scrollBar, null, dragBounds ); + } + + private Display getDisplay() { + return scrollBar.getDisplay(); + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/FastIncrementer.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/FastIncrementer.java new file mode 100644 index 0000000..92d2e8c --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/FastIncrementer.java @@ -0,0 +1,42 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; + +import com.codeaffine.eclipse.swt.widget.scrollbar.ClickControl.ClickAction; + +class FastIncrementer implements ClickAction { + + private final FlatScrollBar scrollBar; + + private Point mouse; + + FastIncrementer( FlatScrollBar scrollBar ) { + this.scrollBar = scrollBar; + } + + @Override + public void run() { + Rectangle drag = getDragBounds(); + if( mouse.x > drag.x + drag.width || mouse.y > drag.y + drag.height ) { + int selection = scrollBar.getSelection() + scrollBar.getPageIncrement(); + scrollBar.setSelectionInternal( selection, SWT.PAGE_DOWN ); + } + } + + @Override + public void setCoordinates( int x, int y ) { + mouse = getMouseLocation( x, y ); + } + + private Point getMouseLocation(int x, int y) { + return Display.getCurrent().map( scrollBar.downFast.getControl(), null, x, y ); + } + + private Rectangle getDragBounds() { + Rectangle dragBounds = scrollBar.drag.getControl().getBounds(); + return Display.getCurrent().map( scrollBar, null, dragBounds ); + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/FlatScrollBar.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/FlatScrollBar.java new file mode 100644 index 0000000..53ef9cb --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/FlatScrollBar.java @@ -0,0 +1,293 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import static com.codeaffine.eclipse.swt.widget.scrollbar.UntypedSelectionAdapter.lookup; + +import java.util.Collection; +import java.util.HashSet; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Layout; +import org.eclipse.swt.widgets.Listener; + +public class FlatScrollBar extends Composite { + + static final int DEFAULT_MINIMUM = 0; + static final int DEFAULT_MAXIMUM = 100; + static final int DEFAULT_INCREMENT = 1; + static final int DEFAULT_THUMB = 10; + static final int DEFAULT_PAGE_INCREMENT = DEFAULT_THUMB; + static final int DEFAULT_SELECTION = 0; + static final int DEFAULT_BUTTON_LENGTH = 0; + static final int DEFAULT_MAX_EXPANSION = Direction.CLEARANCE + 2; + + final ClickControl up; + final ClickControl upFast; + final DragControl drag; + final ClickControl downFast; + final ClickControl down; + final Direction direction; + final MouseWheelShifter mouseWheelHandler; + final Collection listeners; + + private int minimum; + private int maximum; + private int increment; + private int pageIncrement; + private int thumb; + private int selection; + private boolean onDrag; + private int buttonLength; + + public FlatScrollBar( final Composite parent, int style ) { + this( parent, style, DEFAULT_BUTTON_LENGTH, DEFAULT_MAX_EXPANSION ); + } + + FlatScrollBar( Composite parent, int style, int buttonLength, int maxExpansion ) { + super( parent, SWT.NONE ); + super.setLayout( new FlatScrollBarLayout( getDirection( style ) ) ); + this.minimum = DEFAULT_MINIMUM; + this.maximum = DEFAULT_MAXIMUM; + this.increment = DEFAULT_INCREMENT; + this.pageIncrement = DEFAULT_PAGE_INCREMENT; + this.thumb = DEFAULT_THUMB; + this.selection = DEFAULT_SELECTION; + this.buttonLength = buttonLength; + this.direction = getDirection( style ); + this.direction.setDefaultSize( this ); + this.up = new ClickControl( this, new Decrementer( this ), maxExpansion ); + this.upFast = new ClickControl( this, new FastDecrementer( this ), maxExpansion ); + this.drag = new DragControl( this, new DragShifter( this, buttonLength ), maxExpansion ); + this.downFast = new ClickControl( this, new FastIncrementer( this ), maxExpansion ); + this.down = new ClickControl( this, new Incrementer( this ), maxExpansion ); + this.mouseWheelHandler = new MouseWheelShifter( this, parent, buttonLength ); + this.listeners = new HashSet(); + addMouseTrackListener( new MouseTracker( this, maxExpansion ) ); + addControlListener( new ResizeObserver( this ) ); + setDefaultColorScheme(); + } + + @Override + public void setLayout( Layout layout ) { + throw new UnsupportedOperationException( FlatScrollBar.class.getName() + " does not allow to change layout." ); + }; + + @Override + public int getStyle() { + return direction != null ? super.getStyle() | direction.value() : super.getStyle(); + }; + + Direction getDirection() { + return direction; + } + + public void setMinimum( int minimum ) { + if( this.minimum != minimum && minimum >= 0 && minimum < maximum ) { + this.minimum = minimum; + adjustThumb(); + adjustSelection(); + layout(); + } + } + + public int getMinimum() { + return minimum; + } + + public void setMaximum( int maximum ) { + if( this.maximum != maximum && maximum >= 0 && maximum > minimum ) { + this.maximum = maximum; + adjustThumb(); + adjustSelection(); + layout(); + } + } + + public int getMaximum() { + return maximum; + } + + public void setThumb( int thumb ) { + if( this.thumb != thumb && thumb >= 1 ) { + this.thumb = thumb; + adjustThumb(); + adjustSelection(); + layout(); + } + } + + public int getThumb() { + return thumb; + } + + public void setIncrement( int increment ) { + if( this.increment != increment ) { + this.increment = increment; + layout(); + } + } + + public int getIncrement() { + return increment; + } + + public void setPageIncrement( int pageIncrement ) { + this.pageIncrement = pageIncrement; + } + + public int getPageIncrement() { + return pageIncrement; + } + + public void setSelection( int selection ) { + if( !onDrag ) { + updateSelection( selection ); + } + } + + public int getSelection() { + return selection; + } + + public void addSelectionListener( SelectionListener selectionListener ) { + listeners.add( selectionListener ); + } + + public void removeSelectionListener( SelectionListener selectionListener ) { + listeners.remove( selectionListener ); + } + + @Override + public void addListener( int eventType, final Listener listener ) { + if( eventType == SWT.Selection ) { + addSelectionListener( new UntypedSelectionAdapter( listener ) ); + } else { + super.addListener( eventType, listener ); + } + } + + @Override + public void removeListener( int eventType, Listener listener ) { + if( eventType == SWT.Selection ) { + removeSelectionListener( lookup( listeners, listener ) ); + } else { + super.removeListener( eventType, listener ); + } + } + + @Override + public void layout() { + direction.layout( this, buttonLength ); + update(); + } + + public void setIncrementButtonLength( int length ) { + this.buttonLength = length; + layout(); + } + + public int getIncrementButtonLength() { + return buttonLength; + } + + public void setIncrementColor( Color color ) { + up.setForeground( color ); + down.setForeground( color ); + } + + public Color getIncrementColor() { + return up.getForeground(); + } + + public void setPageIncrementColor( Color color ) { + upFast.setForeground( color ); + downFast.setForeground( color ); + } + + public Color getPageIncrementColor() { + return upFast.getForeground(); + } + + public void setThumbColor( Color color ) { + drag.setForeground( color ); + } + + public Color getThumbColor() { + return drag.getForeground(); + } + + @Override + public void setBackground( Color color ) { + up.setBackground( color ); + upFast.setBackground( color ); + drag.setBackground( color ); + downFast.setBackground( color ); + down.setBackground( color ); + super.setBackground( color ); + } + + protected void setSelectionInternal( int selection, int detail ) { + int oldSelection = this.selection; + updateSelection( selection ); + if( oldSelection != this.selection ) { + notifyListeners( detail ); + } + } + + private void updateSelection( int selection ) { + if( this.selection != selection ) { + this.selection = selection; + adjustSelection(); + layout(); + } + } + + public void notifyListeners( int detail ) { + updateOnDrag( detail ); + SelectionEvent selectionEvent = createEvent( detail ); + for( SelectionListener listener : listeners ) { + listener.widgetSelected( selectionEvent ); + } + } + + private void updateOnDrag( int detail ) { + onDrag = ( onDrag || ( SWT.DRAG & detail ) > 0 ) && ( SWT.NONE != detail ); + } + + private SelectionEvent createEvent( int detail ) { + Event event = new Event(); + event.widget = this; + event.detail = detail; + return new SelectionEvent( event ); + } + + private void adjustThumb() { + if( thumb > maximum - minimum ) { + thumb = Math.min( maximum - minimum, thumb ); + thumb = Math.max( 1, thumb ); + } + } + + private void adjustSelection() { + selection = Math.min( selection, maximum - thumb ); + selection = Math.max( selection, minimum ); + } + + private static Direction getDirection( int style ) { + return ( style & SWT.HORIZONTAL ) > 0 ? Direction.HORIZONTAL : Direction.VERTICAL; + } + + private void setDefaultColorScheme() { + up.setForeground( Display.getCurrent().getSystemColor( SWT.COLOR_WIDGET_NORMAL_SHADOW ) ); + upFast.setForeground( Display.getCurrent().getSystemColor( SWT.COLOR_WIDGET_BACKGROUND ) ); + drag.setForeground( Display.getCurrent().getSystemColor( SWT.COLOR_WIDGET_FOREGROUND ) ); + downFast.setForeground( Display.getCurrent().getSystemColor( SWT.COLOR_WIDGET_BACKGROUND ) ); + down.setForeground( Display.getCurrent().getSystemColor( SWT.COLOR_WIDGET_NORMAL_SHADOW ) ); + setBackground( getBackground() ); + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/FlatScrollBarLayout.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/FlatScrollBarLayout.java new file mode 100644 index 0000000..21534fc --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/FlatScrollBarLayout.java @@ -0,0 +1,23 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Layout; + +class FlatScrollBarLayout extends Layout { + + private final Direction direction; + + public FlatScrollBarLayout( Direction orientation ) { + this.direction = orientation; + } + + @Override + protected void layout( Composite composite, boolean flushCache ) { + } + + @Override + protected Point computeSize( Composite composite, int wHint, int hHint, boolean flushCache ) { + return direction.computeSize( composite, wHint, hHint, flushCache ); + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ImageDrawer.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ImageDrawer.java new file mode 100644 index 0000000..91c56bd --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ImageDrawer.java @@ -0,0 +1,68 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import static java.lang.Math.min; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; + +class ImageDrawer { + + private final int maxExpansion; + + private Color background; + private Color foreground; + + ImageDrawer( int expansion ) { + this.maxExpansion = expansion; + this.foreground = Display.getCurrent().getSystemColor( SWT.COLOR_WIDGET_DARK_SHADOW ); + this.background = Display.getCurrent().getSystemColor( SWT.COLOR_LIST_BACKGROUND ); + } + + void setForeground( Color foreground ) { + if( foreground != null ) { + this.foreground = foreground; + } + } + + Color getForeground() { + return foreground; + } + + void setBackground( Color background ) { + if( background != null ) { + this.background = background; + } + } + + Color getBackground() { + return background; + } + + Image draw( int width, int height ) { + Image result = new Image( getDisplay(), width, height ); + GC gc = new GC( result ); + try { + draw( gc, width, height ); + } finally { + gc.dispose(); + } + return result; + } + + private void draw( GC gc, int width, int height ) { + gc.setBackground( background ); + gc.fillRectangle( 0, 0, width, height ); + gc.setBackground( foreground ); + gc.setAdvanced( true ); + gc.setAntialias( SWT.ON ); + int arc = min( width, height ) == 1 ? 1 : maxExpansion + 2; + gc.fillRoundRectangle( 0, 0, width, height, arc, arc ); + } + + private static Display getDisplay() { + return Display.getCurrent(); + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ImageUpdate.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ImageUpdate.java new file mode 100644 index 0000000..02ff9a5 --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ImageUpdate.java @@ -0,0 +1,42 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Label; + +class ImageUpdate { + + private final ImageDrawer imageDrawer; + private final Label control; + + ImageUpdate( Label control, int maxExpansion ) { + this.imageDrawer = new ImageDrawer( maxExpansion ); + this.control = control; + } + + void setForeground( Color color ) { + imageDrawer.setForeground( color ); + } + + Color getForeground() { + return imageDrawer.getForeground(); + } + + void setBackground( Color color ) { + imageDrawer.setBackground( color ); + } + + Color getBackground() { + return imageDrawer.getBackground(); + } + + void update() { + if( control.getImage() != null ) { + control.getImage().dispose(); + } + Point size = control.getSize(); + if( size.x > 0 && size.y > 0 ) { + control.setImage( imageDrawer.draw( size.x, size.y ) ); + } + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/Incrementer.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/Incrementer.java new file mode 100644 index 0000000..0ad5f11 --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/Incrementer.java @@ -0,0 +1,24 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import org.eclipse.swt.SWT; + +import com.codeaffine.eclipse.swt.widget.scrollbar.ClickControl.ClickAction; + +class Incrementer implements ClickAction { + + private final FlatScrollBar scrollBar; + + Incrementer( FlatScrollBar scrollBar ) { + this.scrollBar = scrollBar; + } + + @Override + public void run() { + int selection = scrollBar.getSelection() + scrollBar.getIncrement(); + scrollBar.setSelectionInternal( selection, SWT.ARROW_DOWN ); + } + + @Override + public void setCoordinates( int x, int y ) { + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/MouseTracker.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/MouseTracker.java new file mode 100644 index 0000000..eacce80 --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/MouseTracker.java @@ -0,0 +1,66 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseTrackAdapter; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; + +class MouseTracker extends MouseTrackAdapter implements Runnable, DisposeListener { + + static final int DELAY = 500; + + private final FlatScrollBar scrollBar; + private final int maxExpansion; + + private Rectangle expandedBounds; + private Rectangle originBounds; + private boolean mouseOver; + private boolean disposed; + + MouseTracker( FlatScrollBar scrollBar, int maxExpansion ) { + this.scrollBar = scrollBar; + this.maxExpansion = maxExpansion; + this.scrollBar.addDisposeListener( this ); + this.scrollBar.up.getControl().addMouseTrackListener( this ); + this.scrollBar.upFast.getControl().addMouseTrackListener( this ); + this.scrollBar.drag.getControl().addMouseTrackListener( this ); + this.scrollBar.downFast.getControl().addMouseTrackListener( this ); + this.scrollBar.down.getControl().addMouseTrackListener( this ); + } + + @Override + public void mouseEnter( MouseEvent event ) { + mouseOver = true; + if( !disposed && originBounds == null ) { + originBounds = scrollBar.getBounds(); + scrollBar.getDirection().expand( scrollBar, maxExpansion ); + expandedBounds = scrollBar.getBounds(); + } + } + + @Override + public void mouseExit( MouseEvent event ) { + mouseOver = false; + if( !disposed ) { + Display.getCurrent().timerExec( DELAY, this ); + } + } + + @Override + public void run() { + if( !disposed && !mouseOver ) { + if( scrollBar.getBounds().equals( expandedBounds ) ) { + scrollBar.setBounds( originBounds ); + } + originBounds = null; + expandedBounds = null; + } + } + + @Override + public void widgetDisposed( DisposeEvent e ) { + disposed = true; + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/MouseWheelShifter.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/MouseWheelShifter.java new file mode 100644 index 0000000..26a6768 --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/MouseWheelShifter.java @@ -0,0 +1,69 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import static com.codeaffine.eclipse.swt.widget.scrollbar.Direction.HORIZONTAL; +import static com.codeaffine.eclipse.swt.widget.scrollbar.ShiftData.calculateSelectionRange; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; + +public class MouseWheelShifter implements Listener, DisposeListener { + + private final FlatScrollBar scrollBar; + private final Composite parent; + private final int buttonLength; + + MouseWheelShifter( FlatScrollBar scrollBar, Composite parent, int buttonLength ) { + this.scrollBar = scrollBar; + this.parent = parent; + this.buttonLength = buttonLength; + parent.addListener( getListenerType(), this ); + scrollBar.addDisposeListener( this ); + } + + @Override + public void handleEvent( Event event ) { + handleMouseWheelScroll( event ); + } + + @Override + public void widgetDisposed( DisposeEvent e ) { + parent.removeListener( getListenerType(), this ); + } + + private void handleMouseWheelScroll( Event event ) { + ShiftData shiftData = newShiftData( event.count ); + if( shiftData.canShift() ) { + int selectionRange = calculateSelectionRange( scrollBar ); + int selectionDelta = shiftData.calculateSelectionDelta( selectionRange ); + int selection = scrollBar.getSelection() - selectionDelta; + scrollBar.setSelectionInternal( selection, scrollBar.direction.value() ); + } + } + + private ShiftData newShiftData( int delta ) { + ShiftData result; + if( scrollBar.direction == Direction.HORIZONTAL ) { + result = new ShiftData( buttonLength, getScrollBarSize().x, getDragSize().x, delta ); + } else { + result = new ShiftData( buttonLength, getScrollBarSize().y, getDragSize().y, delta ); + } + return result; + } + + private Point getScrollBarSize() { + return scrollBar.getSize(); + } + + private Point getDragSize() { + return scrollBar.drag.getControl().getSize(); + } + + private int getListenerType() { + return scrollBar.direction == HORIZONTAL ? SWT.MouseHorizontalWheel: SWT.MouseVerticalWheel; + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/MouseWheelSupport.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/MouseWheelSupport.java new file mode 100644 index 0000000..96c23fe --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/MouseWheelSupport.java @@ -0,0 +1,110 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Slider; + +// Workaround for https://github.com/rherrmann/gitplus/issues/594 +public class MouseWheelSupport { + + private final FlatScrollBar scrollBar; + + private Slider slider; + private ScrollBarAdapter scrollBarAdapter; + + static class SliderAdapter extends SelectionAdapter { + + private final MouseWheelSupport mouseWheelSupport; + + SliderAdapter( MouseWheelSupport mouseWheelSupport ) { + this.mouseWheelSupport = mouseWheelSupport; + } + + @Override + public void widgetSelected( SelectionEvent event ) { + mouseWheelSupport.updateScrollBarSelection( event.detail ); + mouseWheelSupport.copySettings(); + } + } + + static class ScrollBarAdapter extends SelectionAdapter implements ControlListener, DisposeListener { + + private final MouseWheelSupport mouseWheelSupport; + + ScrollBarAdapter( MouseWheelSupport mouseWheelSupport ) { + this.mouseWheelSupport = mouseWheelSupport; + } + + @Override + public void controlResized( ControlEvent event ) { + mouseWheelSupport.copySettings(); + } + + @Override + public void controlMoved( ControlEvent event ) { + mouseWheelSupport.copySettings(); + } + + @Override + public void widgetSelected( SelectionEvent event ) { + mouseWheelSupport.copySettings(); + } + @Override + public void widgetDisposed( DisposeEvent event ) { + mouseWheelSupport.dispose(); + } + } + + public MouseWheelSupport( FlatScrollBar scrollBar ) { + this.scrollBar = scrollBar; + } + + public Control getControl() { + return slider; + } + + public void dispose() { + if( !scrollBar.isDisposed() ) { + scrollBar.removeControlListener( scrollBarAdapter ); + } + scrollBar.removeSelectionListener( scrollBarAdapter ); + slider.dispose(); + } + + public void create() { + if( scrollBar.getDirection() == Direction.HORIZONTAL ) { + slider = new Slider( scrollBar.getParent(), SWT.HORIZONTAL ); + } else { + slider = new Slider( scrollBar.getParent(), SWT.VERTICAL ); + } + scrollBarAdapter = new ScrollBarAdapter( this ); + scrollBar.addControlListener( scrollBarAdapter ); + scrollBar.addDisposeListener( scrollBarAdapter ); + scrollBar.addSelectionListener( scrollBarAdapter ); + slider.addSelectionListener( new SliderAdapter( this ) ); + copySettings(); + } + + protected void copySettings() { + if( slider.getLayoutData() == null ) { + slider.setBounds( scrollBar.getBounds() ); + } + slider.moveBelow( null ); + slider.setMinimum( scrollBar.getMinimum() ); + slider.setMaximum( scrollBar.getMaximum() ); + slider.setThumb( scrollBar.getThumb() ); + slider.setIncrement( scrollBar.getIncrement() ); + slider.setPageIncrement( scrollBar.getPageIncrement() ); + slider.setSelection( scrollBar.getSelection() ); + } + + protected void updateScrollBarSelection( int detail ) { + scrollBar.setSelectionInternal( slider.getSelection(), detail ); + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ResizeObserver.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ResizeObserver.java new file mode 100644 index 0000000..c19789d --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ResizeObserver.java @@ -0,0 +1,19 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; + +class ResizeObserver extends ControlAdapter { + + private final FlatScrollBar flatScrollBar; + + public ResizeObserver( FlatScrollBar flatScrollBar ) { + this.flatScrollBar = flatScrollBar; + } + + @Override + public void controlResized( ControlEvent event ) { + flatScrollBar.layout(); + flatScrollBar.moveAbove( null ); + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/SelectionRaster.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/SelectionRaster.java new file mode 100644 index 0000000..7786d7a --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/SelectionRaster.java @@ -0,0 +1,30 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import static java.lang.Math.abs; + +public class SelectionRaster { + + private final FlatScrollBar scrollBar; + private final int rasterSize; + + public SelectionRaster( FlatScrollBar scrollBar, int rasterSize ) { + this.scrollBar = scrollBar; + this.rasterSize = rasterSize; + } + + public void updateSelection( int selection ) { + int rasterValue = calculateRasterValue( selection ); + int scrollBarSelection = scrollBar.getSelection(); + if( isDifferentRasterSection( rasterValue, scrollBarSelection ) ) { + scrollBar.setSelection( rasterValue ); + } + } + + public int calculateRasterValue( int selection ) { + return ( selection / rasterSize ) * rasterSize; + } + + private boolean isDifferentRasterSection( int rasterValue, int scrollBarSelection ) { + return abs( rasterValue - scrollBarSelection ) >= rasterSize; + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ShiftData.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ShiftData.java new file mode 100644 index 0000000..b39be40 --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ShiftData.java @@ -0,0 +1,32 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import static com.codeaffine.eclipse.swt.widget.scrollbar.ComponentDistribution.divide; + +class ShiftData { + + private final int slidePixels; + private final int movedPixels; + private final int buttonLength; + + ShiftData( int buttonLength, int scrollBarPixels, int dragPixels, int movedPixels ) { + this.buttonLength = buttonLength; + this.slidePixels = calculateSlidePixels( scrollBarPixels, dragPixels ); + this.movedPixels = movedPixels; + } + + boolean canShift( ) { + return slidePixels > 0; + } + + int calculateSelectionDelta( int selectionRange ) { + return divide( movedPixels * selectionRange, slidePixels ); + } + + static int calculateSelectionRange( FlatScrollBar scrollBar ) { + return scrollBar.getMaximum() - scrollBar.getMinimum() - scrollBar.getThumb(); + } + + private int calculateSlidePixels( int scrollBarPixels, int dragPixels ) { + return scrollBarPixels - 2 * buttonLength - dragPixels; + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/UntypedSelectionAdapter.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/UntypedSelectionAdapter.java new file mode 100644 index 0000000..fc35ee3 --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/UntypedSelectionAdapter.java @@ -0,0 +1,43 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import java.util.Collection; + +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; + +class UntypedSelectionAdapter extends SelectionAdapter { + + final Listener listener; + + UntypedSelectionAdapter( Listener listener ) { + this.listener = listener; + } + + @Override + public void widgetSelected( SelectionEvent selectionEvent ) { + Event event = new Event(); + event.widget = selectionEvent.widget; + event.detail = selectionEvent.detail; + listener.handleEvent( event ); + } + + static SelectionListener lookup( Collection listeners, Listener untypedListener ) { + for( SelectionListener listener : listeners ) { + if( isAdapterType( listener ) && matches( untypedListener, listener ) ) { + return listener; + } + } + return null; + } + + private static boolean isAdapterType( SelectionListener listener ) { + return listener instanceof UntypedSelectionAdapter; + } + + private static boolean matches( Listener untypedListener, SelectionListener listener ) { + return ( ( UntypedSelectionAdapter )listener ).listener == untypedListener; + } +} \ No newline at end of file diff --git a/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ViewComponent.java b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ViewComponent.java new file mode 100644 index 0000000..1d54c33 --- /dev/null +++ b/widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ViewComponent.java @@ -0,0 +1,7 @@ +package com.codeaffine.eclipse.swt.widget.scrollbar; + +import org.eclipse.swt.widgets.Control; + +public interface ViewComponent { + Control getControl(); +}