Table Widget: add table widget sources
authorjihye424.kim <jihye424.kim@samsung.com>
Tue, 11 Aug 2015 09:58:11 +0000 (18:58 +0900)
committerjihye424.kim <jihye424.kim@samsung.com>
Wed, 12 Aug 2015 06:54:59 +0000 (15:54 +0900)
- widget_src: custom scroll bar sources
from sacrilege-custom-swt-scrollbar(www.codeaffine.com)

Change-Id: I099c6027caef1f2060a70093663a895d4f5a370a
Signed-off-by: jihye424.kim <jihye424.kim@samsung.com>
49 files changed:
.classpath
resource/table/btn_checkbox_checked_hover.png [new file with mode: 0644]
resource/table/btn_checkbox_checked_nml.png [new file with mode: 0644]
resource/table/btn_checkbox_unchecked_hover.png [new file with mode: 0644]
resource/table/btn_checkbox_unchecked_nml.png [new file with mode: 0644]
resource/table/down.png [new file with mode: 0644]
resource/table/up.png [new file with mode: 0644]
src/org/tizen/emulator/manager/ui/table/CheckBoxButton.java [new file with mode: 0644]
src/org/tizen/emulator/manager/ui/table/ColorResources.java [new file with mode: 0644]
src/org/tizen/emulator/manager/ui/table/FontResources.java [new file with mode: 0644]
src/org/tizen/emulator/manager/ui/table/Helper.java [new file with mode: 0644]
src/org/tizen/emulator/manager/ui/table/ImageResources.java [new file with mode: 0644]
src/org/tizen/emulator/manager/ui/table/Table.java [new file with mode: 0644]
src/org/tizen/emulator/manager/ui/table/TableColumn.java [new file with mode: 0644]
src/org/tizen/emulator/manager/ui/table/TableColumnDataTransfer.java [new file with mode: 0644]
src/org/tizen/emulator/manager/ui/table/TableColumnDragAndDrop.java [new file with mode: 0644]
src/org/tizen/emulator/manager/ui/table/TableItem.java [new file with mode: 0644]
src/org/tizen/emulator/manager/ui/table/TableScrolledComposite.java [new file with mode: 0644]
src/org/tizen/emulator/manager/ui/table/TableScrolledCompositeLayout.java [new file with mode: 0644]
src/org/tizen/emulator/manager/ui/table/TextSizeUtil.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/util/ActionScheduler.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/util/ButtonClick.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/util/DragDetector.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/util/MouseDownActionTimer.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/util/ReadAndDispatch.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/util/ResourceLoader.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/util/UIThreadSynchronizer.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/util/Unsafe.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ClickControl.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ComponentDistribution.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/Decrementer.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/Direction.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/DragControl.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/DragShifter.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/FastDecrementer.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/FastIncrementer.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/FlatScrollBar.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/FlatScrollBarLayout.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ImageDrawer.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ImageUpdate.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/Incrementer.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/MouseTracker.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/MouseWheelShifter.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/MouseWheelSupport.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ResizeObserver.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/SelectionRaster.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ShiftData.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/UntypedSelectionAdapter.java [new file with mode: 0644]
widget_src/com/codeaffine/eclipse/swt/widget/scrollbar/ViewComponent.java [new file with mode: 0644]

index 75f1945..2df9e54 100644 (file)
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
        <classpathentry kind="src" path="src"/>
+       <classpathentry kind="src" path="widget_src"/>
        <classpathentry kind="src" path="jaxb_src"/>
        <classpathentry kind="src" path="resource"/>
        <classpathentry exported="true" kind="lib" path="lib/swt.jar" sourcepath="lib/src.zip"/>
diff --git a/resource/table/btn_checkbox_checked_hover.png b/resource/table/btn_checkbox_checked_hover.png
new file mode 100644 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..5b36502
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Emulator Manager
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * JiHye Kim <jihye424.kim@samsung.com>
+ * Minkee Lee <minkee.lee@samsung.com>
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * 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 (file)
index 0000000..3b36c6e
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Emulator Manager
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * JiHye Kim <jihye424.kim@samsung.com>
+ * Minkee Lee <minkee.lee@samsung.com>
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * 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 (file)
index 0000000..63660f5
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Emulator Manager
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * JiHye Kim <jihye424.kim@samsung.com>
+ * Minkee Lee <minkee.lee@samsung.com>
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * 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 (file)
index 0000000..38cbd19
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Emulator Manager
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * JiHye Kim <jihye424.kim@samsung.com>
+ * Minkee Lee <minkee.lee@samsung.com>
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * 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 (file)
index 0000000..d69f244
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Emulator Manager
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * JiHye Kim <jihye424.kim@samsung.com>
+ * Minkee Lee <minkee.lee@samsung.com>
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * 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 (file)
index 0000000..611294b
--- /dev/null
@@ -0,0 +1,1417 @@
+/*
+ * Emulator Manager
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * JiHye Kim <jihye424.kim@samsung.com>
+ * Minkee Lee <minkee.lee@samsung.com>
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * 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<TableColumn> columns = new ArrayList<TableColumn>();
+       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<TableColumn> 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 (file)
index 0000000..e1d412c
--- /dev/null
@@ -0,0 +1,617 @@
+/*
+ * Emulator Manager
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * JiHye Kim <jihye424.kim@samsung.com>
+ * Minkee Lee <minkee.lee@samsung.com>
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * 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<TableColumn> columns = (ArrayList<TableColumn>) 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 (file)
index 0000000..a7b3017
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Emulator Manager
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * JiHye Kim <jihye424.kim@samsung.com>
+ * Minkee Lee <minkee.lee@samsung.com>
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * 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 (file)
index 0000000..6df2ceb
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Emulator Manager
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * JiHye Kim <jihye424.kim@samsung.com>
+ * Minkee Lee <minkee.lee@samsung.com>
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * 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 (file)
index 0000000..2e045b3
--- /dev/null
@@ -0,0 +1,942 @@
+/*
+ * Emulator Manager
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * JiHye Kim <jihye424.kim@samsung.com>
+ * Minkee Lee <minkee.lee@samsung.com>
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * 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 (file)
index 0000000..9fb1040
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * Emulator Manager
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * JiHye Kim <jihye424.kim@samsung.com>
+ * Minkee Lee <minkee.lee@samsung.com>
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * 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 (file)
index 0000000..b4a01e0
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Emulator Manager
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * JiHye Kim <jihye424.kim@samsung.com>
+ * Minkee Lee <minkee.lee@samsung.com>
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * 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 (file)
index 0000000..93091ae
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Emulator Manager
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * JiHye Kim <jihye424.kim@samsung.com>
+ * Minkee Lee <minkee.lee@samsung.com>
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * 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 (file)
index 0000000..54ed23c
--- /dev/null
@@ -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 (file)
index 0000000..a8472e7
--- /dev/null
@@ -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 (file)
index 0000000..1525a1e
--- /dev/null
@@ -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 (file)
index 0000000..f6c614e
--- /dev/null
@@ -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 (file)
index 0000000..2991a25
--- /dev/null
@@ -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 (file)
index 0000000..f70ff91
--- /dev/null
@@ -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 (file)
index 0000000..4ab2b83
--- /dev/null
@@ -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 (file)
index 0000000..a9ac7b3
--- /dev/null
@@ -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> T newInstance( Class<T> 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 (file)
index 0000000..baeefdc
--- /dev/null
@@ -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 (file)
index 0000000..54e1cdf
--- /dev/null
@@ -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 (file)
index 0000000..89a7402
--- /dev/null
@@ -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 (file)
index 0000000..18a45ab
--- /dev/null
@@ -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 (file)
index 0000000..8a95eb6
--- /dev/null
@@ -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 (file)
index 0000000..3d992af
--- /dev/null
@@ -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 (file)
index 0000000..290d592
--- /dev/null
@@ -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 (file)
index 0000000..92d2e8c
--- /dev/null
@@ -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 (file)
index 0000000..53ef9cb
--- /dev/null
@@ -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<SelectionListener> 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<SelectionListener>();
+    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 (file)
index 0000000..21534fc
--- /dev/null
@@ -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 (file)
index 0000000..91c56bd
--- /dev/null
@@ -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 (file)
index 0000000..02ff9a5
--- /dev/null
@@ -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 (file)
index 0000000..0ad5f11
--- /dev/null
@@ -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 (file)
index 0000000..eacce80
--- /dev/null
@@ -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 (file)
index 0000000..26a6768
--- /dev/null
@@ -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 (file)
index 0000000..96c23fe
--- /dev/null
@@ -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 (file)
index 0000000..c19789d
--- /dev/null
@@ -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 (file)
index 0000000..7786d7a
--- /dev/null
@@ -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 (file)
index 0000000..b39be40
--- /dev/null
@@ -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 (file)
index 0000000..fc35ee3
--- /dev/null
@@ -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<SelectionListener> 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 (file)
index 0000000..1d54c33
--- /dev/null
@@ -0,0 +1,7 @@
+package com.codeaffine.eclipse.swt.widget.scrollbar;
+
+import org.eclipse.swt.widgets.Control;
+
+public interface ViewComponent {
+  Control getControl();
+}