-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
@@ -34,6 +14,7 @@
+
diff --git a/xyz.veronie.bgg.ui/META-INF/MANIFEST.MF b/xyz.veronie.bgg.ui/META-INF/MANIFEST.MF
index 3ba545a..9cab8bd 100644
--- a/xyz.veronie.bgg.ui/META-INF/MANIFEST.MF
+++ b/xyz.veronie.bgg.ui/META-INF/MANIFEST.MF
@@ -3,6 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Name: Secondtry
Bundle-SymbolicName: xyz.veronie.bgg.ui;singleton:=true
Bundle-Version: 1.0.0.qualifier
+Bundle-ClassPath: lib/sqlite-jdbc-3.34.0.jar,
+ swing2swt.jar
Require-Bundle: org.eclipse.core.runtime,
org.eclipse.swt,
org.eclipse.e4.core.di,
@@ -15,9 +17,9 @@ Require-Bundle: org.eclipse.core.runtime,
org.eclipse.osgi.services,
javax.inject,
org.eclipse.e4.ui.model.workbench
-Automatic-Module-Name: de.wt.secondtry
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: javax.inject;version="1.0.0",
+Import-Package: javax.annotation;version="1.0.0";resolution:=optional,
+ javax.inject;version="1.0.0",
org.eclipse.e4.ui.model.application.descriptor.basic,
org.eclipse.e4.ui.model.application.ui.basic
-Bundle-ClassPath: lib/sqlite-jdbc-3.34.0.jar
+Automatic-Module-Name: de.wt.secondtry
diff --git a/xyz.veronie.bgg.ui/icons/download-button.png b/xyz.veronie.bgg.ui/icons/download-button.png
deleted file mode 100644
index 33b99d5..0000000
Binary files a/xyz.veronie.bgg.ui/icons/download-button.png and /dev/null differ
diff --git a/xyz.veronie.bgg.ui/icons/export_nandeck_60x60.png b/xyz.veronie.bgg.ui/icons/export_nandeck_60x60.png
new file mode 100644
index 0000000..8bfd4c9
Binary files /dev/null and b/xyz.veronie.bgg.ui/icons/export_nandeck_60x60.png differ
diff --git a/xyz.veronie.bgg.ui/icons/folder-symbol.png b/xyz.veronie.bgg.ui/icons/folder-symbol.png
deleted file mode 100644
index 7863b0a..0000000
Binary files a/xyz.veronie.bgg.ui/icons/folder-symbol.png and /dev/null differ
diff --git a/xyz.veronie.bgg.ui/icons/noun_Download_60x60.png b/xyz.veronie.bgg.ui/icons/noun_Download_60x60.png
new file mode 100644
index 0000000..f817fa1
Binary files /dev/null and b/xyz.veronie.bgg.ui/icons/noun_Download_60x60.png differ
diff --git a/xyz.veronie.bgg.ui/icons/noun_Family_60x60.png b/xyz.veronie.bgg.ui/icons/noun_Family_60x60.png
new file mode 100644
index 0000000..f573736
Binary files /dev/null and b/xyz.veronie.bgg.ui/icons/noun_Family_60x60.png differ
diff --git a/xyz.veronie.bgg.ui/icons/noun_List_60x60.png b/xyz.veronie.bgg.ui/icons/noun_List_60x60.png
new file mode 100644
index 0000000..f38e9bc
Binary files /dev/null and b/xyz.veronie.bgg.ui/icons/noun_List_60x60.png differ
diff --git a/xyz.veronie.bgg.ui/icons/noun_Meeple_60x60.png b/xyz.veronie.bgg.ui/icons/noun_Meeple_60x60.png
new file mode 100644
index 0000000..89d3c2e
Binary files /dev/null and b/xyz.veronie.bgg.ui/icons/noun_Meeple_60x60.png differ
diff --git a/xyz.veronie.bgg.ui/icons/noun_Save_60x60.png b/xyz.veronie.bgg.ui/icons/noun_Save_60x60.png
new file mode 100644
index 0000000..7d2281a
Binary files /dev/null and b/xyz.veronie.bgg.ui/icons/noun_Save_60x60.png differ
diff --git a/xyz.veronie.bgg.ui/icons/noun_Undo_60x60.png b/xyz.veronie.bgg.ui/icons/noun_Undo_60x60.png
new file mode 100644
index 0000000..aaa98c3
Binary files /dev/null and b/xyz.veronie.bgg.ui/icons/noun_Undo_60x60.png differ
diff --git a/xyz.veronie.bgg.ui/icons/noun_open_60x60.png b/xyz.veronie.bgg.ui/icons/noun_open_60x60.png
new file mode 100644
index 0000000..c9cae07
Binary files /dev/null and b/xyz.veronie.bgg.ui/icons/noun_open_60x60.png differ
diff --git a/xyz.veronie.bgg.ui/icons/only_new_60x60.png b/xyz.veronie.bgg.ui/icons/only_new_60x60.png
new file mode 100644
index 0000000..77c5e58
Binary files /dev/null and b/xyz.veronie.bgg.ui/icons/only_new_60x60.png differ
diff --git a/xyz.veronie.bgg.ui/icons/result_add_60x60.png b/xyz.veronie.bgg.ui/icons/result_add_60x60.png
new file mode 100644
index 0000000..724da67
Binary files /dev/null and b/xyz.veronie.bgg.ui/icons/result_add_60x60.png differ
diff --git a/xyz.veronie.bgg.ui/icons/result_intersect_60x60.png b/xyz.veronie.bgg.ui/icons/result_intersect_60x60.png
new file mode 100644
index 0000000..c079004
Binary files /dev/null and b/xyz.veronie.bgg.ui/icons/result_intersect_60x60.png differ
diff --git a/xyz.veronie.bgg.ui/icons/result_replace_60x60.png b/xyz.veronie.bgg.ui/icons/result_replace_60x60.png
new file mode 100644
index 0000000..12a983a
Binary files /dev/null and b/xyz.veronie.bgg.ui/icons/result_replace_60x60.png differ
diff --git a/xyz.veronie.bgg.ui/icons/result_subtract_60x60.png b/xyz.veronie.bgg.ui/icons/result_subtract_60x60.png
new file mode 100644
index 0000000..34fad94
Binary files /dev/null and b/xyz.veronie.bgg.ui/icons/result_subtract_60x60.png differ
diff --git a/xyz.veronie.bgg.ui/icons/save-button.png b/xyz.veronie.bgg.ui/icons/save-button.png
deleted file mode 100644
index 1e21dfa..0000000
Binary files a/xyz.veronie.bgg.ui/icons/save-button.png and /dev/null differ
diff --git a/xyz.veronie.bgg.ui/icons/save-button_20x20.png b/xyz.veronie.bgg.ui/icons/save-button_20x20.png
deleted file mode 100644
index 1a9b96e..0000000
Binary files a/xyz.veronie.bgg.ui/icons/save-button_20x20.png and /dev/null differ
diff --git a/xyz.veronie.bgg.ui/src/org/eclipse/wb/swt/ResourceManager.java b/xyz.veronie.bgg.ui/src/org/eclipse/wb/swt/ResourceManager.java
new file mode 100644
index 0000000..8e96dfe
--- /dev/null
+++ b/xyz.veronie.bgg.ui/src/org/eclipse/wb/swt/ResourceManager.java
@@ -0,0 +1,438 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc. and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Google, Inc. - initial API and implementation
+ * Wim Jongman - 1.8 and higher compliance
+ *******************************************************************************/
+package org.eclipse.wb.swt;
+
+import java.io.File;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.resource.CompositeImageDescriptor;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageDataProvider;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.osgi.framework.Bundle;
+
+/**
+ * Utility class for managing OS resources associated with SWT/JFace controls
+ * such as colors, fonts, images, etc.
+ *
+ * This class is created automatically when you fiddle around with images and
+ * colors in WB. You might want to prevent your application from using this
+ * class and provide your own more effective means of resource caching.
+ *
+ * Even though this class can be used to manage these resources, if they are
+ * here for the duration of the application and not used then you still have an
+ * effective resource leak.
+ *
+ * Application code must explicitly invoke the dispose()
method to
+ * release the operating system resources managed by cached objects when those
+ * objects and OS resources are no longer needed.
+ *
+ * This class may be freely distributed as part of any application or plugin.
+ *
+ *
+ * @author scheglov_ke
+ * @author Dan Rubel
+ * @author Wim Jongman
+ */
+public class ResourceManager extends SWTResourceManager {
+
+ /**
+ * The map where we store our images.
+ */
+ private static Map m_descriptorImageMap = new HashMap();
+
+ /**
+ * Returns an {@link ImageDescriptor} stored in the file at the specified path
+ * relative to the specified class.
+ *
+ * @param clazz the {@link Class} relative to which to find the image
+ * descriptor.
+ * @param path the path to the image file.
+ * @return the {@link ImageDescriptor} stored in the file at the specified path.
+ */
+ public static ImageDescriptor getImageDescriptor(Class> clazz, String path) {
+ return ImageDescriptor.createFromFile(clazz, path);
+ }
+
+ /**
+ * Returns an {@link ImageDescriptor} stored in the file at the specified path.
+ *
+ * @param path the path to the image file.
+ * @return the {@link ImageDescriptor} stored in the file at the specified path.
+ */
+ public static ImageDescriptor getImageDescriptor(String path) {
+ try {
+ return ImageDescriptor.createFromURL(new File(path).toURI().toURL());
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns an {@link Image} based on the specified {@link ImageDescriptor}.
+ *
+ * @param descriptor the {@link ImageDescriptor} for the {@link Image}.
+ * @return the {@link Image} based on the specified {@link ImageDescriptor}.
+ */
+ public static Image getImage(ImageDescriptor descriptor) {
+ if (descriptor == null) {
+ return null;
+ }
+ Image image = m_descriptorImageMap.get(descriptor);
+ if (image == null) {
+ image = descriptor.createImage();
+ m_descriptorImageMap.put(descriptor, image);
+ }
+ return image;
+ }
+
+ /**
+ * Maps images to decorated images.
+ */
+ @SuppressWarnings("unchecked")
+ private static Map>[] m_decoratedImageMap = new Map[LAST_CORNER_KEY];
+
+ /**
+ * Returns an {@link Image} composed of a base image decorated by another image.
+ *
+ * @param baseImage the base {@link Image} that should be decorated.
+ * @param decorator the {@link Image} to decorate the base image.
+ * @return {@link Image} The resulting decorated image.
+ */
+ public static Image decorateImage(Image baseImage, Image decorator) {
+ return decorateImage(baseImage, decorator, BOTTOM_RIGHT);
+ }
+
+ /**
+ * Returns an {@link Image} composed of a base image decorated by another image.
+ *
+ * @param baseImage
+ * the base {@link Image} that should be decorated.
+ * @param decorator
+ * the {@link Image} to decorate the base image.
+ * @param corner
+ * the corner to place decorator image.
+ * @return the resulting decorated {@link Image}.
+ */
+ public static Image decorateImage(final Image baseImage, final Image decorator, final int corner) {
+ if (corner <= 0 || corner >= LAST_CORNER_KEY) {
+ throw new IllegalArgumentException("Wrong decorate corner");
+ }
+ Map> cornerDecoratedImageMap = m_decoratedImageMap[corner];
+ if (cornerDecoratedImageMap == null) {
+ cornerDecoratedImageMap = new HashMap>();
+ m_decoratedImageMap[corner] = cornerDecoratedImageMap;
+ }
+ Map decoratedMap = cornerDecoratedImageMap.get(baseImage);
+ if (decoratedMap == null) {
+ decoratedMap = new HashMap();
+ cornerDecoratedImageMap.put(baseImage, decoratedMap);
+ }
+ //
+ Image result = decoratedMap.get(decorator);
+ if (result == null) {
+ final Rectangle bib = baseImage.getBounds();
+ final Rectangle dib = decorator.getBounds();
+ final Point baseImageSize = new Point(bib.width, bib.height);
+ CompositeImageDescriptor compositImageDesc = new CompositeImageDescriptor() {
+ @Override
+ protected void drawCompositeImage(int width, int height) {
+ drawImage(createCachedImageDataProvider(baseImage), 0, 0);
+ if (corner == TOP_LEFT) {
+ drawImage(getUnzoomedImageDataProvider(decorator.getImageData()) , 0, 0);
+ } else if (corner == TOP_RIGHT) {
+ drawImage(getUnzoomedImageDataProvider(decorator.getImageData()), bib.width - dib.width, 0);
+ } else if (corner == BOTTOM_LEFT) {
+ drawImage(getUnzoomedImageDataProvider(decorator.getImageData()), 0, bib.height - dib.height);
+ } else if (corner == BOTTOM_RIGHT) {
+ drawImage(getUnzoomedImageDataProvider(decorator.getImageData()), bib.width - dib.width, bib.height - dib.height);
+ }
+ }
+ @Override
+ protected Point getSize() {
+ return baseImageSize;
+ }
+ };
+ //
+ result = compositImageDesc.createImage();
+ decoratedMap.put(decorator, result);
+ }
+ return result;
+ }
+
+ private static ImageDataProvider getUnzoomedImageDataProvider(ImageData imageData) {
+ return zoom -> zoom == 100 ? imageData : null;
+ }
+
+
+ /**
+ * Dispose all of the cached images.
+ */
+ public static void disposeImages() {
+ SWTResourceManager.disposeImages();
+ // dispose ImageDescriptor images
+ {
+ for (Iterator I = m_descriptorImageMap.values().iterator(); I.hasNext();) {
+ I.next().dispose();
+ }
+ m_descriptorImageMap.clear();
+ }
+ // dispose decorated images
+ for (int i = 0; i < m_decoratedImageMap.length; i++) {
+ Map> cornerDecoratedImageMap = m_decoratedImageMap[i];
+ if (cornerDecoratedImageMap != null) {
+ for (Map decoratedMap : cornerDecoratedImageMap.values()) {
+ for (Image image : decoratedMap.values()) {
+ image.dispose();
+ }
+ decoratedMap.clear();
+ }
+ cornerDecoratedImageMap.clear();
+ }
+ }
+ // dispose plugin images
+ {
+ for (Iterator I = m_URLImageMap.values().iterator(); I.hasNext();) {
+ I.next().dispose();
+ }
+ m_URLImageMap.clear();
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Plugin images support
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Maps URL to images.
+ */
+ private static Map m_URLImageMap = new HashMap();
+
+ /**
+ * Provider for plugin resources, used by WindowBuilder at design time.
+ */
+ public interface PluginResourceProvider {
+ URL getEntry(String symbolicName, String path);
+ }
+
+ /**
+ * Instance of {@link PluginResourceProvider}, used by WindowBuilder at design
+ * time.
+ */
+ private static PluginResourceProvider m_designTimePluginResourceProvider = null;
+
+ /**
+ * Returns an {@link Image} based on a plugin and file path.
+ *
+ * @param plugin the plugin {@link Object} containing the image
+ * @param name the path to the image within the plugin
+ * @return the {@link Image} stored in the file at the specified path
+ *
+ * @deprecated Use {@link #getPluginImage(String, String)} instead.
+ */
+ @Deprecated
+ public static Image getPluginImage(Object plugin, String name) {
+ try {
+ URL url = getPluginImageURL(plugin, name);
+ if (url != null) {
+ return getPluginImageFromUrl(url);
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ return null;
+ }
+
+ /**
+ * Returns an {@link Image} based on a {@link Bundle} and resource entry path.
+ *
+ * @param symbolicName the symbolic name of the {@link Bundle}.
+ * @param path the path of the resource entry.
+ * @return the {@link Image} stored in the file at the specified path.
+ */
+ public static Image getPluginImage(String symbolicName, String path) {
+ try {
+ URL url = getPluginImageURL(symbolicName, path);
+ if (url != null) {
+ return getPluginImageFromUrl(url);
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ return null;
+ }
+
+ /**
+ * Returns an {@link Image} based on given {@link URL}.
+ */
+ private static Image getPluginImageFromUrl(URL url) {
+ try {
+ try {
+ String key = url.toExternalForm();
+ Image image = m_URLImageMap.get(key);
+ if (image == null) {
+ InputStream stream = url.openStream();
+ try {
+ image = getImage(stream);
+ m_URLImageMap.put(key, image);
+ } finally {
+ stream.close();
+ }
+ }
+ return image;
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ return null;
+ }
+
+ /**
+ * Returns an {@link ImageDescriptor} based on a plugin and file path.
+ *
+ * @param plugin the plugin {@link Object} containing the image.
+ * @param name the path to th eimage within the plugin.
+ * @return the {@link ImageDescriptor} stored in the file at the specified path.
+ *
+ * @deprecated Use {@link #getPluginImageDescriptor(String, String)} instead.
+ */
+ @Deprecated
+ public static ImageDescriptor getPluginImageDescriptor(Object plugin, String name) {
+ try {
+ try {
+ URL url = getPluginImageURL(plugin, name);
+ return ImageDescriptor.createFromURL(url);
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ return null;
+ }
+
+ /**
+ * Returns an {@link ImageDescriptor} based on a {@link Bundle} and resource
+ * entry path.
+ *
+ * @param symbolicName the symbolic name of the {@link Bundle}.
+ * @param path the path of the resource entry.
+ * @return the {@link ImageDescriptor} based on a {@link Bundle} and resource
+ * entry path.
+ */
+ public static ImageDescriptor getPluginImageDescriptor(String symbolicName, String path) {
+ try {
+ URL url = getPluginImageURL(symbolicName, path);
+ if (url != null) {
+ return ImageDescriptor.createFromURL(url);
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ return null;
+ }
+
+ /**
+ * Returns an {@link URL} based on a {@link Bundle} and resource entry path.
+ */
+ private static URL getPluginImageURL(String symbolicName, String path) {
+ // try runtime plugins
+ {
+ Bundle bundle = Platform.getBundle(symbolicName);
+ if (bundle != null) {
+ return bundle.getEntry(path);
+ }
+ }
+ // try design time provider
+ if (m_designTimePluginResourceProvider != null) {
+ return m_designTimePluginResourceProvider.getEntry(symbolicName, path);
+ }
+ // no such resource
+ return null;
+ }
+
+ /**
+ * Returns an {@link URL} based on a plugin and file path.
+ *
+ * @param plugin the plugin {@link Object} containing the file path.
+ * @param name the file path.
+ * @return the {@link URL} representing the file at the specified path.
+ * @throws Exception
+ */
+ private static URL getPluginImageURL(Object plugin, String name) throws Exception {
+ // try to work with 'plugin' as with OSGI BundleContext
+ try {
+ Class> BundleClass = Class.forName("org.osgi.framework.Bundle"); //$NON-NLS-1$
+ Class> BundleContextClass = Class.forName("org.osgi.framework.BundleContext"); //$NON-NLS-1$
+ if (BundleContextClass.isAssignableFrom(plugin.getClass())) {
+ Method getBundleMethod = BundleContextClass.getMethod("getBundle", new Class[0]); //$NON-NLS-1$
+ Object bundle = getBundleMethod.invoke(plugin, new Object[0]);
+ //
+ Class> PathClass = Class.forName("org.eclipse.core.runtime.Path"); //$NON-NLS-1$
+ Constructor> pathConstructor = PathClass.getConstructor(new Class[] { String.class });
+ Object path = pathConstructor.newInstance(new Object[] { name });
+ //
+ Class> IPathClass = Class.forName("org.eclipse.core.runtime.IPath"); //$NON-NLS-1$
+ Class> PlatformClass = Class.forName("org.eclipse.core.runtime.Platform"); //$NON-NLS-1$
+ Method findMethod = PlatformClass.getMethod("find", new Class[] { BundleClass, IPathClass }); //$NON-NLS-1$
+ return (URL) findMethod.invoke(null, new Object[] { bundle, path });
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ // else work with 'plugin' as with usual Eclipse plugin
+ {
+ Class> PluginClass = Class.forName("org.eclipse.core.runtime.Plugin"); //$NON-NLS-1$
+ if (PluginClass.isAssignableFrom(plugin.getClass())) {
+ //
+ Class> PathClass = Class.forName("org.eclipse.core.runtime.Path"); //$NON-NLS-1$
+ Constructor> pathConstructor = PathClass.getConstructor(new Class[] { String.class });
+ Object path = pathConstructor.newInstance(new Object[] { name });
+ //
+ Class> IPathClass = Class.forName("org.eclipse.core.runtime.IPath"); //$NON-NLS-1$
+ Method findMethod = PluginClass.getMethod("find", new Class[] { IPathClass }); //$NON-NLS-1$
+ return (URL) findMethod.invoke(plugin, new Object[] { path });
+ }
+ }
+ return null;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // General
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Dispose of cached objects and their underlying OS resources. This should only
+ * be called when the cached objects are no longer needed (e.g. on application
+ * shutdown).
+ */
+ public static void dispose() {
+ disposeColors();
+ disposeFonts();
+ disposeImages();
+ }
+}
\ No newline at end of file
diff --git a/xyz.veronie.bgg.ui/src/org/eclipse/wb/swt/SWTResourceManager.java b/xyz.veronie.bgg.ui/src/org/eclipse/wb/swt/SWTResourceManager.java
new file mode 100644
index 0000000..d8a2858
--- /dev/null
+++ b/xyz.veronie.bgg.ui/src/org/eclipse/wb/swt/SWTResourceManager.java
@@ -0,0 +1,447 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.swt;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Utility class for managing OS resources associated with SWT controls such as colors, fonts, images, etc.
+ *
+ * !!! IMPORTANT !!! Application code must explicitly invoke the dispose()
method to release the
+ * operating system resources managed by cached objects when those objects and OS resources are no longer
+ * needed (e.g. on application shutdown)
+ *
+ * This class may be freely distributed as part of any application or plugin.
+ *
+ * @author scheglov_ke
+ * @author Dan Rubel
+ */
+public class SWTResourceManager {
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Color
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private static Map m_colorMap = new HashMap();
+ /**
+ * Returns the system {@link Color} matching the specific ID.
+ *
+ * @param systemColorID
+ * the ID value for the color
+ * @return the system {@link Color} matching the specific ID
+ */
+ public static Color getColor(int systemColorID) {
+ Display display = Display.getCurrent();
+ return display.getSystemColor(systemColorID);
+ }
+ /**
+ * Returns a {@link Color} given its red, green and blue component values.
+ *
+ * @param r
+ * the red component of the color
+ * @param g
+ * the green component of the color
+ * @param b
+ * the blue component of the color
+ * @return the {@link Color} matching the given red, green and blue component values
+ */
+ public static Color getColor(int r, int g, int b) {
+ return getColor(new RGB(r, g, b));
+ }
+ /**
+ * Returns a {@link Color} given its RGB value.
+ *
+ * @param rgb
+ * the {@link RGB} value of the color
+ * @return the {@link Color} matching the RGB value
+ */
+ public static Color getColor(RGB rgb) {
+ Color color = m_colorMap.get(rgb);
+ if (color == null) {
+ Display display = Display.getCurrent();
+ color = new Color(display, rgb);
+ m_colorMap.put(rgb, color);
+ }
+ return color;
+ }
+ /**
+ * Dispose of all the cached {@link Color}'s.
+ */
+ public static void disposeColors() {
+ for (Color color : m_colorMap.values()) {
+ color.dispose();
+ }
+ m_colorMap.clear();
+ }
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Image
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Maps image paths to images.
+ */
+ private static Map m_imageMap = new HashMap();
+ /**
+ * Returns an {@link Image} encoded by the specified {@link InputStream}.
+ *
+ * @param stream
+ * the {@link InputStream} encoding the image data
+ * @return the {@link Image} encoded by the specified input stream
+ */
+ protected static Image getImage(InputStream stream) throws IOException {
+ try {
+ Display display = Display.getCurrent();
+ ImageData data = new ImageData(stream);
+ if (data.transparentPixel > 0) {
+ return new Image(display, data, data.getTransparencyMask());
+ }
+ return new Image(display, data);
+ } finally {
+ stream.close();
+ }
+ }
+ /**
+ * Returns an {@link Image} stored in the file at the specified path.
+ *
+ * @param path
+ * the path to the image file
+ * @return the {@link Image} stored in the file at the specified path
+ */
+ public static Image getImage(String path) {
+ Image image = m_imageMap.get(path);
+ if (image == null) {
+ try {
+ image = getImage(new FileInputStream(path));
+ m_imageMap.put(path, image);
+ } catch (Exception e) {
+ image = getMissingImage();
+ m_imageMap.put(path, image);
+ }
+ }
+ return image;
+ }
+ /**
+ * Returns an {@link Image} stored in the file at the specified path relative to the specified class.
+ *
+ * @param clazz
+ * the {@link Class} relative to which to find the image
+ * @param path
+ * the path to the image file, if starts with '/'
+ * @return the {@link Image} stored in the file at the specified path
+ */
+ public static Image getImage(Class> clazz, String path) {
+ String key = clazz.getName() + '|' + path;
+ Image image = m_imageMap.get(key);
+ if (image == null) {
+ try {
+ image = getImage(clazz.getResourceAsStream(path));
+ m_imageMap.put(key, image);
+ } catch (Exception e) {
+ image = getMissingImage();
+ m_imageMap.put(key, image);
+ }
+ }
+ return image;
+ }
+ private static final int MISSING_IMAGE_SIZE = 10;
+ /**
+ * @return the small {@link Image} that can be used as placeholder for missing image.
+ */
+ private static Image getMissingImage() {
+ Image image = new Image(Display.getCurrent(), MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE);
+ //
+ GC gc = new GC(image);
+ gc.setBackground(getColor(SWT.COLOR_RED));
+ gc.fillRectangle(0, 0, MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE);
+ gc.dispose();
+ //
+ return image;
+ }
+ /**
+ * Style constant for placing decorator image in top left corner of base image.
+ */
+ public static final int TOP_LEFT = 1;
+ /**
+ * Style constant for placing decorator image in top right corner of base image.
+ */
+ public static final int TOP_RIGHT = 2;
+ /**
+ * Style constant for placing decorator image in bottom left corner of base image.
+ */
+ public static final int BOTTOM_LEFT = 3;
+ /**
+ * Style constant for placing decorator image in bottom right corner of base image.
+ */
+ public static final int BOTTOM_RIGHT = 4;
+ /**
+ * Internal value.
+ */
+ protected static final int LAST_CORNER_KEY = 5;
+ /**
+ * Maps images to decorated images.
+ */
+ @SuppressWarnings("unchecked")
+ private static Map>[] m_decoratedImageMap = new Map[LAST_CORNER_KEY];
+ /**
+ * Returns an {@link Image} composed of a base image decorated by another image.
+ *
+ * @param baseImage
+ * the base {@link Image} that should be decorated
+ * @param decorator
+ * the {@link Image} to decorate the base image
+ * @return {@link Image} The resulting decorated image
+ */
+ public static Image decorateImage(Image baseImage, Image decorator) {
+ return decorateImage(baseImage, decorator, BOTTOM_RIGHT);
+ }
+ /**
+ * Returns an {@link Image} composed of a base image decorated by another image.
+ *
+ * @param baseImage
+ * the base {@link Image} that should be decorated
+ * @param decorator
+ * the {@link Image} to decorate the base image
+ * @param corner
+ * the corner to place decorator image
+ * @return the resulting decorated {@link Image}
+ */
+ public static Image decorateImage(final Image baseImage, final Image decorator, final int corner) {
+ if (corner <= 0 || corner >= LAST_CORNER_KEY) {
+ throw new IllegalArgumentException("Wrong decorate corner");
+ }
+ Map> cornerDecoratedImageMap = m_decoratedImageMap[corner];
+ if (cornerDecoratedImageMap == null) {
+ cornerDecoratedImageMap = new HashMap>();
+ m_decoratedImageMap[corner] = cornerDecoratedImageMap;
+ }
+ Map decoratedMap = cornerDecoratedImageMap.get(baseImage);
+ if (decoratedMap == null) {
+ decoratedMap = new HashMap();
+ cornerDecoratedImageMap.put(baseImage, decoratedMap);
+ }
+ //
+ Image result = decoratedMap.get(decorator);
+ if (result == null) {
+ Rectangle bib = baseImage.getBounds();
+ Rectangle dib = decorator.getBounds();
+ //
+ result = new Image(Display.getCurrent(), bib.width, bib.height);
+ //
+ GC gc = new GC(result);
+ gc.drawImage(baseImage, 0, 0);
+ if (corner == TOP_LEFT) {
+ gc.drawImage(decorator, 0, 0);
+ } else if (corner == TOP_RIGHT) {
+ gc.drawImage(decorator, bib.width - dib.width, 0);
+ } else if (corner == BOTTOM_LEFT) {
+ gc.drawImage(decorator, 0, bib.height - dib.height);
+ } else if (corner == BOTTOM_RIGHT) {
+ gc.drawImage(decorator, bib.width - dib.width, bib.height - dib.height);
+ }
+ gc.dispose();
+ //
+ decoratedMap.put(decorator, result);
+ }
+ return result;
+ }
+ /**
+ * Dispose all of the cached {@link Image}'s.
+ */
+ public static void disposeImages() {
+ // dispose loaded images
+ {
+ for (Image image : m_imageMap.values()) {
+ image.dispose();
+ }
+ m_imageMap.clear();
+ }
+ // dispose decorated images
+ for (int i = 0; i < m_decoratedImageMap.length; i++) {
+ Map> cornerDecoratedImageMap = m_decoratedImageMap[i];
+ if (cornerDecoratedImageMap != null) {
+ for (Map decoratedMap : cornerDecoratedImageMap.values()) {
+ for (Image image : decoratedMap.values()) {
+ image.dispose();
+ }
+ decoratedMap.clear();
+ }
+ cornerDecoratedImageMap.clear();
+ }
+ }
+ }
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Font
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Maps font names to fonts.
+ */
+ private static Map m_fontMap = new HashMap();
+ /**
+ * Maps fonts to their bold versions.
+ */
+ private static Map m_fontToBoldFontMap = new HashMap();
+ /**
+ * Returns a {@link Font} based on its name, height and style.
+ *
+ * @param name
+ * the name of the font
+ * @param height
+ * the height of the font
+ * @param style
+ * the style of the font
+ * @return {@link Font} The font matching the name, height and style
+ */
+ public static Font getFont(String name, int height, int style) {
+ return getFont(name, height, style, false, false);
+ }
+ /**
+ * Returns a {@link Font} based on its name, height and style. Windows-specific strikeout and underline
+ * flags are also supported.
+ *
+ * @param name
+ * the name of the font
+ * @param size
+ * the size of the font
+ * @param style
+ * the style of the font
+ * @param strikeout
+ * the strikeout flag (warning: Windows only)
+ * @param underline
+ * the underline flag (warning: Windows only)
+ * @return {@link Font} The font matching the name, height, style, strikeout and underline
+ */
+ public static Font getFont(String name, int size, int style, boolean strikeout, boolean underline) {
+ String fontName = name + '|' + size + '|' + style + '|' + strikeout + '|' + underline;
+ Font font = m_fontMap.get(fontName);
+ if (font == null) {
+ FontData fontData = new FontData(name, size, style);
+ if (strikeout || underline) {
+ try {
+ Class> logFontClass = Class.forName("org.eclipse.swt.internal.win32.LOGFONT"); //$NON-NLS-1$
+ Object logFont = FontData.class.getField("data").get(fontData); //$NON-NLS-1$
+ if (logFont != null && logFontClass != null) {
+ if (strikeout) {
+ logFontClass.getField("lfStrikeOut").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$
+ }
+ if (underline) {
+ logFontClass.getField("lfUnderline").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$
+ }
+ }
+ } catch (Throwable e) {
+ System.err.println("Unable to set underline or strikeout" + " (probably on a non-Windows platform). " + e); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ font = new Font(Display.getCurrent(), fontData);
+ m_fontMap.put(fontName, font);
+ }
+ return font;
+ }
+ /**
+ * Returns a bold version of the given {@link Font}.
+ *
+ * @param baseFont
+ * the {@link Font} for which a bold version is desired
+ * @return the bold version of the given {@link Font}
+ */
+ public static Font getBoldFont(Font baseFont) {
+ Font font = m_fontToBoldFontMap.get(baseFont);
+ if (font == null) {
+ FontData fontDatas[] = baseFont.getFontData();
+ FontData data = fontDatas[0];
+ font = new Font(Display.getCurrent(), data.getName(), data.getHeight(), SWT.BOLD);
+ m_fontToBoldFontMap.put(baseFont, font);
+ }
+ return font;
+ }
+ /**
+ * Dispose all of the cached {@link Font}'s.
+ */
+ public static void disposeFonts() {
+ // clear fonts
+ for (Font font : m_fontMap.values()) {
+ font.dispose();
+ }
+ m_fontMap.clear();
+ // clear bold fonts
+ for (Font font : m_fontToBoldFontMap.values()) {
+ font.dispose();
+ }
+ m_fontToBoldFontMap.clear();
+ }
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Cursor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Maps IDs to cursors.
+ */
+ private static Map m_idToCursorMap = new HashMap();
+ /**
+ * Returns the system cursor matching the specific ID.
+ *
+ * @param id
+ * int The ID value for the cursor
+ * @return Cursor The system cursor matching the specific ID
+ */
+ public static Cursor getCursor(int id) {
+ Integer key = Integer.valueOf(id);
+ Cursor cursor = m_idToCursorMap.get(key);
+ if (cursor == null) {
+ cursor = new Cursor(Display.getDefault(), id);
+ m_idToCursorMap.put(key, cursor);
+ }
+ return cursor;
+ }
+ /**
+ * Dispose all of the cached cursors.
+ */
+ public static void disposeCursors() {
+ for (Cursor cursor : m_idToCursorMap.values()) {
+ cursor.dispose();
+ }
+ m_idToCursorMap.clear();
+ }
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // General
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Dispose of cached objects and their underlying OS resources. This should only be called when the cached
+ * objects are no longer needed (e.g. on application shutdown).
+ */
+ public static void dispose() {
+ disposeColors();
+ disposeImages();
+ disposeFonts();
+ disposeCursors();
+ }
+}
\ No newline at end of file
diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/dialogs/SaveGameListDialog.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/dialogs/SaveGameListDialog.java
index d298fa1..af4931e 100644
--- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/dialogs/SaveGameListDialog.java
+++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/dialogs/SaveGameListDialog.java
@@ -1,70 +1,93 @@
package xyz.veronie.bgg.ui.dialogs;
-import static org.eclipse.jface.widgets.WidgetFactory.text;
-
import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.FocusEvent;
-import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
+import org.eclipse.wb.swt.SWTResourceManager;
+
+import xyz.veronie.bgg.ui.helpers.BatColors;
public class SaveGameListDialog extends Dialog {
+ private Text textField;
private String entryString;
+ /**
+ * Create the dialog.
+ * @param parentShell
+ */
public SaveGameListDialog(Shell parentShell) {
- super(parentShell);
- }
-
- @Override
- protected Control createDialogArea(Composite parent) {
- Composite container = (Composite) super.createDialogArea(parent);
-
- GridLayout layout = new GridLayout(1, false);
- layout.marginLeft = 16;
- layout.marginTop = 16;
- layout.marginRight = 16;
- layout.marginBottom = 16;
- container.setLayout(layout);
+ super(parentShell);
+ setShellStyle(SWT.APPLICATION_MODAL);
+ }
- Text text = text(SWT.BORDER).message("Enter game list name")
- .limitTo(255)
- .layoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false))
- .create(container);
+ /**
+ * Create contents of the dialog.
+ * @param parent
+ */
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ parent.setBackground(BatColors.getBackgroundColor());
+ Composite container = (Composite) super.createDialogArea(parent);
+ container.setFont(SWTResourceManager.getFont("Segoe UI", 11, SWT.NORMAL));
+ container.setBackground(BatColors.getBackgroundColor());
+ GridLayout gl_container = new GridLayout(1, false);
+ gl_container.verticalSpacing = 24;
+ gl_container.marginWidth = 24;
+ gl_container.marginTop = 24;
+ gl_container.marginRight = 24;
+ gl_container.marginLeft = 24;
+ gl_container.marginHeight = 24;
+ gl_container.marginBottom = 24;
+ gl_container.horizontalSpacing = 24;
+ container.setLayout(gl_container);
+
+ Label lblEnterAName = new Label(container, SWT.NONE);
+ lblEnterAName.setFont(SWTResourceManager.getFont("Segoe UI", 11, SWT.NORMAL));
+ lblEnterAName.setText("Enter a name for the game list:");
+
+ textField = new Text(container, SWT.BORDER);
+ textField.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ Text t = (Text) e.widget;
+ entryString = t.getText();
+ }
+ });
+ textField.setFont(SWTResourceManager.getFont("Segoe UI", 11, SWT.NORMAL));
+ textField.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ return container;
+ }
- FocusListener focusListener = new FocusListener() {
- public void focusGained(FocusEvent e) {
- }
+ /**
+ * Create contents of the button bar.
+ * @param parent
+ */
+ @Override
+ protected void createButtonsForButtonBar(Composite parent) {
+ parent.setBackground(BatColors.getBackgroundColor());
+ createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
+ createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
+ }
- public void focusLost(FocusEvent e) {
- Text t = (Text) e.widget;
- entryString = t.getText();
- }
- };
- text.addFocusListener(focusListener);
-
- return container;
- }
-
- // overriding this methods allows you to set the
- // title of the custom dialog
- @Override
- protected void configureShell(Shell newShell) {
- super.configureShell(newShell);
- newShell.setText("Enter game list name");
- }
+ /**
+ * Return the initial size of the dialog.
+ */
+ @Override
+ protected Point getInitialSize() {
+ return new Point(450, 300);
+ }
- @Override
- protected Point getInitialSize() {
- return new Point(450, 300);
- }
public String getEntryText() {
return entryString;
diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/handlers/HandleSaveGamelist.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/handlers/HandleSaveGamelist.java
index f5cf57f..0e3814f 100644
--- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/handlers/HandleSaveGamelist.java
+++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/handlers/HandleSaveGamelist.java
@@ -1,8 +1,6 @@
package xyz.veronie.bgg.ui.handlers;
-import javax.inject.Inject;
-
import org.eclipse.e4.core.di.annotations.CanExecute;
import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.jface.dialogs.Dialog;
@@ -15,9 +13,14 @@ import xyz.veronie.bgg.ui.dialogs.SaveGameListDialog;
public class HandleSaveGamelist {
- @Inject
+
+// @Inject
ThingProvider thingProvider;
+ public HandleSaveGamelist(ThingProvider thingProvider) {
+ this.thingProvider = thingProvider;
+ }
+
@Execute
public void execute(Shell shell) {
diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/helpers/BatColors.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/helpers/BatColors.java
new file mode 100644
index 0000000..5fc2c29
--- /dev/null
+++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/helpers/BatColors.java
@@ -0,0 +1,16 @@
+package xyz.veronie.bgg.ui.helpers;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.wb.swt.SWTResourceManager;
+
+public class BatColors {
+
+ public static Color getBackgroundColor() {
+ return SWTResourceManager.getColor(155,142,237);
+ }
+
+
+ public static Color getButtonBgColor() {
+ return SWTResourceManager.getColor(255,81,0);
+ }
+}
diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/helpers/BatLayouts.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/helpers/BatLayouts.java
new file mode 100644
index 0000000..bac0f02
--- /dev/null
+++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/helpers/BatLayouts.java
@@ -0,0 +1,18 @@
+package xyz.veronie.bgg.ui.helpers;
+
+import org.eclipse.swt.layout.GridLayout;
+
+public class BatLayouts {
+
+ public static void applyStandardSpacing(GridLayout gl) {
+ gl.horizontalSpacing = 8;
+ gl.verticalSpacing = 24;
+ gl.marginWidth = 8;
+ gl.marginTop = 8;
+ gl.marginRight = 8;
+ gl.marginLeft = 8;
+ gl.marginHeight = 8;
+ gl.marginBottom = 8;
+ }
+
+}
diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/BatMain.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/BatMain.java
new file mode 100644
index 0000000..83b15d2
--- /dev/null
+++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/BatMain.java
@@ -0,0 +1,274 @@
+
+package xyz.veronie.bgg.ui.parts;
+
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+
+import org.eclipse.e4.core.di.annotations.Optional;
+import org.eclipse.e4.core.services.events.IEventBroker;
+import org.eclipse.e4.ui.di.UIEventTopic;
+import org.eclipse.e4.ui.model.application.MApplication;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.workbench.modeling.EModelService;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.e4.ui.workbench.modeling.EPartService.PartState;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.wb.swt.ResourceManager;
+import org.eclipse.wb.swt.SWTResourceManager;
+
+import xyz.veronie.bgg.result.Thing;
+import xyz.veronie.bgg.result.ThingProvider;
+import xyz.veronie.bgg.types.EventConstants;
+import xyz.veronie.bgg.ui.handlers.HandleSaveGamelist;
+import xyz.veronie.bgg.ui.helpers.BatColors;
+import xyz.veronie.bgg.ui.helpers.BatLayouts;
+
+public class BatMain {
+ private Table tableGameList;
+ private TableViewer tableViewer;
+
+ @Inject
+ private ThingProvider thingProvider;
+
+ @Inject
+ private IEventBroker eventBroker;
+
+ @Inject
+ public BatMain() {
+
+ }
+
+ @PostConstruct
+ public void postConstruct(Composite parent, MApplication application, EPartService partService, EModelService modelService) {
+ Color bgColor = BatColors.getBackgroundColor();
+
+ parent.setBackground(bgColor);
+ GridLayout gl_parent = new GridLayout(1, false);
+ gl_parent.horizontalSpacing = 0;
+ gl_parent.verticalSpacing = 0;
+ gl_parent.marginWidth = 0;
+ gl_parent.marginHeight = 0;
+ parent.setLayout(gl_parent);
+
+ Composite main = new Composite(parent, SWT.NONE);
+ GridData gd_main = new GridData(SWT.LEFT, SWT.TOP, true, true, 1, 1);
+ gd_main.minimumHeight = 192;
+ gd_main.minimumWidth = 348;
+ main.setLayoutData(gd_main);
+ main.setBackground(bgColor);
+ GridLayout gl_main = new GridLayout(1, false);
+ gl_main.verticalSpacing = 24;
+ BatLayouts.applyStandardSpacing(gl_main);
+ main.setLayout(gl_main);
+
+ Composite buttonRow = new Composite(main, SWT.NONE);
+ buttonRow.setBackground(bgColor);
+ GridLayout gl_buttonRow = new GridLayout(5, false);
+ gl_buttonRow.verticalSpacing = 8;
+ gl_buttonRow.marginWidth = 0;
+ gl_buttonRow.marginHeight = 8;
+ gl_buttonRow.horizontalSpacing = 8;
+ BatLayouts.applyStandardSpacing(gl_buttonRow);
+ buttonRow.setLayout(gl_buttonRow);
+ buttonRow.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, true, false, 1, 1));
+
+ Button btnFetch = new Button(buttonRow, SWT.NONE);
+ btnFetch.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseUp(MouseEvent e) {
+ List parts = modelService.findElements(application, "xyz.veronie.bgg.ui.part.fetch", MPart.class);
+ if(parts != null && parts.size() >= 1) {
+ MPart fetchPart = parts.get(0);
+ partService.showPart(fetchPart, PartState.CREATE);
+ partService.showPart(fetchPart, PartState.ACTIVATE);
+ }
+ }
+ });
+
+ btnFetch.setToolTipText("Fetch games from BGG");
+ btnFetch.setForeground(SWTResourceManager.getColor(SWT.COLOR_TRANSPARENT));
+ btnFetch.setBackground(SWTResourceManager.getColor(SWT.COLOR_TRANSPARENT));
+ btnFetch.setImage(ResourceManager.getPluginImage("xyz.veronie.bgg.ui", "icons/noun_Download_60x60.png"));
+
+ Button btnSave = new Button(buttonRow, SWT.NONE);
+ btnSave.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseUp(MouseEvent e) {
+ // TODO: find a loose coupling instead. Where is EHandlerService???
+ HandleSaveGamelist handleSaveGameList = new HandleSaveGamelist(thingProvider);
+ if(handleSaveGameList.canExecute()) {
+ handleSaveGameList.execute(parent.getShell());
+ }
+ }
+ });
+ btnSave.setToolTipText("Save list of games");
+ btnSave.setBackground(SWTResourceManager.getColor(SWT.COLOR_TRANSPARENT));
+ btnSave.setForeground(SWTResourceManager.getColor(SWT.COLOR_TRANSPARENT));
+ btnSave.setImage(ResourceManager.getPluginImage("xyz.veronie.bgg.ui", "icons/noun_Save_60x60.png"));
+
+ Button btnLoad = new Button(buttonRow, SWT.NONE);
+ btnLoad.setBackground(SWTResourceManager.getColor(SWT.COLOR_TRANSPARENT));
+ btnLoad.setImage(ResourceManager.getPluginImage("xyz.veronie.bgg.ui", "icons/noun_open_60x60.png"));
+ btnLoad.setToolTipText("Load list of games");
+
+ Button btnUndo = new Button(buttonRow, SWT.NONE);
+ btnUndo.setToolTipText("Undo game list operation");
+ btnUndo.setImage(ResourceManager.getPluginImage("xyz.veronie.bgg.ui", "icons/noun_Undo_60x60.png"));
+ btnUndo.setBackground(SWTResourceManager.getColor(SWT.COLOR_TRANSPARENT));
+
+ Button btnExport = new Button(buttonRow, SWT.NONE);
+ btnExport.setBackground(SWTResourceManager.getColor(SWT.COLOR_TRANSPARENT));
+ btnExport.setImage(ResourceManager.getPluginImage("xyz.veronie.bgg.ui", "icons/export_nandeck_60x60.png"));
+ btnExport.setToolTipText("Export for nanDeck");
+
+ tableViewer = new TableViewer(main, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI);
+ tableGameList = tableViewer.getTable();
+ tableGameList.setLinesVisible(true);
+ tableGameList.setHeaderVisible(true);
+ GridData gd_tableGameList = new GridData(SWT.LEFT, SWT.TOP, true, true, 1, 1);
+ gd_tableGameList.heightHint = 466;
+ tableGameList.setLayoutData(gd_tableGameList);
+
+ createColumns(tableViewer);
+ tableViewer.setContentProvider(new ArrayContentProvider());
+ // Get the content for the viewer, setInput will call getElements in the
+ // contentProvider
+ tableViewer.setInput(thingProvider.getThings());
+ // make the selection available to other views
+ // TODO: getSite().setSelectionProvider(viewer);
+ // Set the sorter for the table
+ tableViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ IStructuredSelection selection = tableViewer.getStructuredSelection();
+ Thing thing = (Thing)selection.getFirstElement();
+ eventBroker.send(EventConstants.TOPIC_THING_SELECTION, thing);
+ }
+ });
+
+
+ Composite statusRow = new Composite(main, SWT.NONE);
+ statusRow.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, true, false, 1, 1));
+ GridLayout gl_statusRow = new GridLayout(1, false);
+ gl_statusRow.verticalSpacing = 8;
+ gl_statusRow.marginWidth = 8;
+ gl_statusRow.marginHeight = 8;
+ gl_statusRow.horizontalSpacing = 8;
+ BatLayouts.applyStandardSpacing(gl_statusRow);
+ statusRow.setLayout(gl_statusRow);
+
+ Label lblResultStatus = new Label(statusRow, SWT.NONE);
+ lblResultStatus.setText("0 items");
+
+ }
+
+
+
+
+
+
+ public TableViewer getViewer() {
+ return tableViewer;
+ }
+
+ // This will create the columns for the table
+ private void createColumns(final TableViewer viewer) {
+
+ // https://stackoverflow.com/questions/12641354/putting-an-image-in-to-a-jface-table-cell-is-causing-gap-for-image-to-appear-in
+ TableViewerColumn col = createTableViewerColumn(Thing.IdHeader, 250, 0);
+ col.setLabelProvider(new ColumnLabelProvider() {
+ @Override
+ public String getText(Object element) {
+ Thing t = (Thing) element;
+ return t.getField((int)col.getColumn().getData());
+ }
+ });
+
+ TableViewerColumn col2 = createTableViewerColumn(Thing.ThumbHeader, 150, 1);
+ col2.setLabelProvider(new ColumnLabelProvider() {
+ @Override
+ public Image getImage(Object element) {
+ Thing t = (Thing) element;
+ Image img = t.getThumbnail();
+ return img;
+ }
+
+ @Override
+ public String getText(Object element) {
+ return "";
+ }
+
+ });
+
+ TableViewerColumn col3 = createTableViewerColumn(Thing.NameHeader, 400, 2);
+ col3.setLabelProvider(new ColumnLabelProvider() {
+ @Override
+ public String getText(Object element) {
+ Thing t = (Thing) element;
+ return t.getField((int)col3.getColumn().getData());
+ }
+ });
+ }
+
+ private TableViewerColumn createTableViewerColumn(String title, int bound, final int colNumber) {
+ final TableViewerColumn viewerColumn = new TableViewerColumn(tableViewer,
+ SWT.NONE);
+ final TableColumn column = viewerColumn.getColumn();
+ column.setText(title);
+ column.setWidth(bound);
+ column.setAlignment(SWT.LEFT);
+ column.setResizable(true);
+ column.setMoveable(true);
+ column.setData(colNumber);
+
+ return viewerColumn;
+
+ }
+
+
+
+
+ /**
+ * Passing the focus request to the viewer's control.
+ */
+ public void setFocus() {
+ tableViewer.getControl().setFocus();
+ }
+
+
+ @Inject
+ @Optional
+ private void subscribeTopicResultChanged
+ (@UIEventTopic(EventConstants.TOPIC_RESULT_CHANGED)
+ String empty) {
+ System.out.println("TOPIC_RESULT_CHANGED");
+ List things = thingProvider.getThings();
+ tableViewer.setInput(things);
+ tableViewer.refresh(true);
+// if(things != null) {
+// statsLabel.setText(Integer.toString(things.size()) + " items");
+// statsLabel.redraw();
+// }
+ }
+
+}
\ No newline at end of file
diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/BggResultPart.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/BggResultPart.java
index 22717da..236baa0 100644
--- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/BggResultPart.java
+++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/BggResultPart.java
@@ -33,33 +33,6 @@ import xyz.veronie.bgg.result.Thing;
import xyz.veronie.bgg.result.ThingProvider;
import xyz.veronie.bgg.types.EventConstants;
-abstract class CenterImageLabelProvider extends OwnerDrawLabelProvider {
-
- protected void measure(Event event, Object element) {
- }
-
- protected void paint(Event event, Object element) {
-
- Image img = getImage(element);
-
- if (img != null) {
- Rectangle bounds = ((TableItem) event.item).getBounds(event.index);
- Rectangle imgBounds = img.getBounds();
- bounds.width /= 2;
- bounds.width -= imgBounds.width / 2;
- bounds.height /= 2;
- bounds.height -= imgBounds.height / 2;
-
- int x = bounds.width > 0 ? bounds.x + bounds.width : bounds.x;
- int y = bounds.height > 0 ? bounds.y + bounds.height : bounds.y;
-
- event.gc.drawImage(img, x, y);
- }
- }
-
- protected abstract Image getImage(Object element);
-
-};
public class BggResultPart {
private TableViewer viewer;
@@ -72,7 +45,7 @@ public class BggResultPart {
private IEventBroker eventBroker;
@PostConstruct
- public void createControls(Composite parent) {
+ public void createContents(Composite parent) {
Composite main = new Composite(parent, SWT.FILL);
main.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/FetchPart.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/FetchPart.java
new file mode 100644
index 0000000..9b589ab
--- /dev/null
+++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/FetchPart.java
@@ -0,0 +1,298 @@
+
+package xyz.veronie.bgg.ui.parts;
+
+import java.util.ArrayList;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+
+import org.eclipse.e4.core.services.events.IEventBroker;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.wb.swt.ResourceManager;
+
+import xyz.veronie.bgg.result.BggApi;
+import xyz.veronie.bgg.result.ResultConfig;
+import xyz.veronie.bgg.result.ResultConfigManager;
+import xyz.veronie.bgg.result.Thing;
+import xyz.veronie.bgg.result.ThingProvider;
+import xyz.veronie.bgg.types.EventConstants;
+import xyz.veronie.bgg.types.SourceFilter;
+import xyz.veronie.bgg.ui.filters.BggUserSourceFilter;
+import xyz.veronie.bgg.ui.filters.FamilySourceFilter;
+import xyz.veronie.bgg.ui.filters.GeeklistSourceFilter;
+import xyz.veronie.bgg.ui.helpers.BatColors;
+import xyz.veronie.bgg.ui.helpers.BatLayouts;
+import org.eclipse.swt.widgets.Label;
+
+public class FetchPart {
+
+ @Inject
+ private ResultConfigManager configManager;
+
+ // inject all source filter composites
+ @Inject private BggUserSourceFilter bggUserSourceFilter;
+ @Inject private GeeklistSourceFilter geeklistSourceFilter;
+ @Inject private FamilySourceFilter familySourceFilter;
+
+ @Inject
+ private IEventBroker eventBroker;
+
+ @Inject
+ private ThingProvider thingProvider;
+
+ @Inject
+ private BggApi bggApi;
+
+ private Button btnBggUser;
+ private Button btnFamily;
+ private Button btnGeeklist;
+ private SourceFilter source;
+ private Composite main;
+
+ private Group filterGroup;
+
+ @Inject
+ public FetchPart() {
+
+ }
+
+ @PostConstruct
+ public void postConstruct(Composite parent) {
+ parent.setBackground(BatColors.getBackgroundColor());
+ parent.setLayout(new GridLayout(1, false));
+
+ main = new Composite(parent, SWT.NONE);
+ GridLayout gl_main = new GridLayout(1, false);
+ BatLayouts.applyStandardSpacing(gl_main);
+ main.setLayout(gl_main);
+ main.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, true, true, 1, 1));
+
+ Composite buttonRow = new Composite(main, SWT.NONE);
+ buttonRow.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1));
+ GridLayout gl_buttonRow = new GridLayout(3, false);
+ BatLayouts.applyStandardSpacing(gl_main);
+ buttonRow.setLayout(gl_buttonRow);
+
+ btnBggUser = new Button(buttonRow, SWT.NONE);
+ btnBggUser.setImage(ResourceManager.getPluginImage("xyz.veronie.bgg.ui", "icons/noun_Meeple_60x60.png"));
+
+ btnFamily = new Button(buttonRow, SWT.NONE);
+ btnFamily.setImage(ResourceManager.getPluginImage("xyz.veronie.bgg.ui", "icons/noun_Family_60x60.png"));
+
+ btnGeeklist = new Button(buttonRow, SWT.NONE);
+ btnGeeklist.setImage(ResourceManager.getPluginImage("xyz.veronie.bgg.ui", "icons/noun_List_60x60.png"));
+
+
+ btnBggUser.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseUp(MouseEvent e) {
+ selectFilter(SourceFilter.BGG_USER);
+ }
+ });
+
+ btnFamily.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseUp(MouseEvent e) {
+ selectFilter(SourceFilter.FAMILY);
+ }
+ });
+
+ btnGeeklist.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseUp(MouseEvent e) {
+ selectFilter(SourceFilter.GEEKLIST);
+ }
+ });
+
+
+ Composite centerComposite = new Composite(main, SWT.NONE);
+ centerComposite.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, true, true, 1, 1));
+ GridLayout gl_centerComposite = new GridLayout(1, false);
+ BatLayouts.applyStandardSpacing(gl_centerComposite);
+ centerComposite.setLayout(gl_centerComposite);
+
+ filterGroup = new Group(centerComposite, SWT.NONE);
+ filterGroup.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, true, 1, 1));
+ filterGroup.setLayout(new GridLayout(1, false));
+
+ Composite applyComposite = new Composite(main, SWT.NONE);
+ applyComposite.setLayout(new GridLayout(5, false));
+ applyComposite.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, true, false, 1, 1));
+
+ Button btnOnlyNew = new Button(applyComposite, SWT.NONE);
+ btnOnlyNew.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseUp(MouseEvent e) {
+ }
+ });
+ btnOnlyNew.setImage(ResourceManager.getPluginImage("xyz.veronie.bgg.ui", "icons/only_new_60x60.png"));
+ btnOnlyNew.setToolTipText("Keep only new");
+
+ Button btnReplace = new Button(applyComposite, SWT.NONE);
+ btnReplace.setImage(ResourceManager.getPluginImage("xyz.veronie.bgg.ui", "icons/result_replace_60x60.png"));
+ btnReplace.setToolTipText("Replace");
+
+ Button btnAdd = new Button(applyComposite, SWT.NONE);
+ btnAdd.setImage(ResourceManager.getPluginImage("xyz.veronie.bgg.ui", "icons/result_add_60x60.png"));
+ btnAdd.setToolTipText("Add");
+
+ Button btnSubtract = new Button(applyComposite, SWT.NONE);
+ btnSubtract.setImage(ResourceManager.getPluginImage("xyz.veronie.bgg.ui", "icons/result_subtract_60x60.png"));
+ btnSubtract.setToolTipText("Subtract");
+
+ Button btnIntersect = new Button(applyComposite, SWT.NONE);
+ btnIntersect.setImage(ResourceManager.getPluginImage("xyz.veronie.bgg.ui", "icons/result_intersect_60x60.png"));
+ btnIntersect.setToolTipText("Intersect");
+
+
+ // init filter using config manager
+ source = configManager.getResultConfig().source;
+ selectFilter(source);
+ showFilter(filterGroup, source);
+
+
+ }
+
+
+ private void selectFilter(SourceFilter source) {
+ switch(source) {
+ default:
+ case BGG_USER:
+ btnBggUser.setFocus();
+ btnBggUser.setBackground(BatColors.getButtonBgColor());
+ btnFamily.setBackground(null);
+ btnGeeklist.setBackground(null);
+ break;
+ case FAMILY:
+ btnBggUser.setFocus();
+ btnBggUser.setBackground(null);
+ btnFamily.setBackground(BatColors.getButtonBgColor());
+ btnGeeklist.setBackground(null);
+ break;
+ case GEEKLIST:
+ btnGeeklist.setFocus();
+ btnBggUser.setBackground(null);
+ btnFamily.setBackground(null);
+ btnGeeklist.setBackground(BatColors.getButtonBgColor());
+ break;
+ }
+ showFilter(filterGroup, source);
+ eventBroker.send(EventConstants.TOPIC_ACTION_CHANGED, source);
+ }
+
+
+ /// show different filter controls depending on selection in cbSource ComboViewer
+ private void showFilter(Composite parent, SourceFilter source) {
+
+ // clean up
+ for(Control child : parent.getChildren()) {
+ child.dispose();
+ }
+
+ // create a new filter area based on selection:
+ switch(source) {
+ default:
+ case BGG_USER:
+ System.out.println("construct " + source);
+ bggUserSourceFilter.create(parent, SWT.FILL);
+ break;
+ case GEEKLIST:
+ System.out.println("construct " + source);
+ geeklistSourceFilter.create(parent, SWT.FILL);
+ break;
+ case FAMILY:
+ System.out.println("construct " + source);
+ familySourceFilter.create(parent, SWT.FILL);
+ break;
+ }
+
+ main.layout(true, true);
+ }
+
+
+ private void checkEntry() {
+ ResultConfig resultConfig = configManager.getResultConfig();
+
+ if(source == SourceFilter.BGG_USER) {
+ String user = resultConfig.user;
+ if(user == null || user.isEmpty()) {
+ MessageDialog.openError(main.getShell(), "", "Please enter a user name.");
+ return;
+ } else {
+ eventBroker.send(EventConstants.TOPIC_STATUS, "Fetching " + source.toString() + " '" + user + "'...");
+ try {
+ ArrayList things = bggApi.getThingsForUser(user);
+ useThingsOnResult(things);
+ }
+ catch(IllegalArgumentException ex) {
+ MessageDialog.openError(main.getShell(), "", ex.getMessage());
+ }
+ }
+
+ } else if(source == SourceFilter.GEEKLIST) {
+ Integer geeklistId = resultConfig.geeklistId;
+ if(geeklistId == null) {
+ MessageDialog.openError(main.getShell(), "", "Please enter a geeklist id.");
+ return;
+ } else {
+ eventBroker.send(EventConstants.TOPIC_STATUS, "Fetching for geeklist id '" + geeklistId + "'");
+ try {
+ ArrayList things = bggApi.getThingsForGeeklist(geeklistId);
+ useThingsOnResult(things);
+ }
+ catch(IllegalArgumentException ex) {
+ MessageDialog.openError(main.getShell(), "", ex.getMessage());
+ }
+ }
+ } else if(source == SourceFilter.FAMILY) {
+ Integer familyId = resultConfig.familyId;
+ if(familyId == null) {
+ MessageDialog.openError(main.getShell(), "", "Please enter a family id.");
+ return;
+ } else {
+ eventBroker.send(EventConstants.TOPIC_STATUS, "Fetching for family id '" + familyId + "'");
+ try {
+ ArrayList things = bggApi.getThingsForFamily(familyId);
+ useThingsOnResult(things);
+ }
+ catch(IllegalArgumentException ex) {
+ MessageDialog.openError(main.getShell(), "", ex.getMessage());
+ }
+ }
+ }
+ }
+
+ private void useThingsOnResult(ArrayList things) {
+ switch(configManager.getResultConfig().action) {
+ case REP:
+ thingProvider.replaceThings(things);
+ break;
+ case ADD:
+ thingProvider.addThings(things);
+ break;
+ case SUB:
+ thingProvider.subtractThings(things);
+ break;
+ case AND:
+ thingProvider.intersectThings(things);
+ break;
+ case ONLY_NEW:
+ thingProvider.keepOnlyNew(things);
+ break;
+ }
+ eventBroker.send(EventConstants.TOPIC_RESULT_CHANGED, "");
+ eventBroker.send(EventConstants.TOPIC_STATUS, "Fetched " + Integer.toString(things.size()) + " things.");
+ }
+
+}
\ No newline at end of file
diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/internal/CenterImageLabelProvider.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/internal/CenterImageLabelProvider.java
new file mode 100644
index 0000000..762b6ab
--- /dev/null
+++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/internal/CenterImageLabelProvider.java
@@ -0,0 +1,35 @@
+package xyz.veronie.bgg.ui.parts.internal;
+
+import org.eclipse.jface.viewers.OwnerDrawLabelProvider;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.TableItem;
+
+public abstract class CenterImageLabelProvider extends OwnerDrawLabelProvider {
+
+ protected void measure(Event event, Object element) {
+ }
+
+ protected void paint(Event event, Object element) {
+
+ Image img = getImage(element);
+
+ if (img != null) {
+ Rectangle bounds = ((TableItem) event.item).getBounds(event.index);
+ Rectangle imgBounds = img.getBounds();
+ bounds.width /= 2;
+ bounds.width -= imgBounds.width / 2;
+ bounds.height /= 2;
+ bounds.height -= imgBounds.height / 2;
+
+ int x = bounds.width > 0 ? bounds.x + bounds.width : bounds.x;
+ int y = bounds.height > 0 ? bounds.y + bounds.height : bounds.y;
+
+ event.gc.drawImage(img, x, y);
+ }
+ }
+
+ protected abstract Image getImage(Object element);
+
+};