diff --git a/xyz.veronie.bgg.ui/.classpath b/xyz.veronie.bgg.ui/.classpath index 111b080..148af0e 100644 --- a/xyz.veronie.bgg.ui/.classpath +++ b/xyz.veronie.bgg.ui/.classpath @@ -13,5 +13,6 @@ + diff --git a/xyz.veronie.bgg.ui/Application.e4xmi b/xyz.veronie.bgg.ui/Application.e4xmi index 389a6e9..a3bb17f 100644 --- a/xyz.veronie.bgg.ui/Application.e4xmi +++ b/xyz.veronie.bgg.ui/Application.e4xmi @@ -1,30 +1,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + @@ -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); + +};