summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephan Schwiebert2011-08-23 08:37:27 (EDT)
committer Fabian Steeg2011-08-23 08:50:16 (EDT)
commite3dcdbe6492220de5280053a816e2fbc959c4397 (patch)
treefd285150becd6a020a556aac8474b657e7de208a
parente2e52cb7997cfe469d0f3cbcc8292ccadaef597c (diff)
downloadorg.eclipse.gef4-e3dcdbe6492220de5280053a816e2fbc959c4397.zip
org.eclipse.gef4-e3dcdbe6492220de5280053a816e2fbc959c4397.tar.gz
org.eclipse.gef4-e3dcdbe6492220de5280053a816e2fbc959c4397.tar.bz2
[349969] Integrate tag cloud visualization contributed by Stephan Schwiebert, see CQ 5299
-rw-r--r--org.eclipse.zest.cloudio/.classpath7
-rw-r--r--org.eclipse.zest.cloudio/.project28
-rw-r--r--org.eclipse.zest.cloudio/.settings/org.eclipse.jdt.core.prefs8
-rw-r--r--org.eclipse.zest.cloudio/META-INF/MANIFEST.MF13
-rw-r--r--org.eclipse.zest.cloudio/build.properties5
-rw-r--r--org.eclipse.zest.cloudio/cloudio.target11
-rw-r--r--org.eclipse.zest.cloudio/img/add.gifbin0 -> 318 bytes
-rw-r--r--org.eclipse.zest.cloudio/img/remove.gifbin0 -> 351 bytes
-rw-r--r--org.eclipse.zest.cloudio/img/toggle_colors.gifbin0 -> 216 bytes
-rw-r--r--org.eclipse.zest.cloudio/pom.xml46
-rw-r--r--org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/Activator.java87
-rw-r--r--org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/CloudOptionsComposite.java516
-rw-r--r--org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/ICloudLabelProvider.java72
-rw-r--r--org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/IEditableCloudLabelProvider.java31
-rw-r--r--org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/TagCloud.java1340
-rw-r--r--org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/TagCloudViewer.java375
-rw-r--r--org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/Word.java81
-rw-r--r--org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/layout/DefaultLayouter.java147
-rw-r--r--org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/layout/ILayouter.java50
-rw-r--r--org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/util/RectTree.java225
-rw-r--r--org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/util/SmallRect.java47
-rw-r--r--org.eclipse.zest.examples.cloudio.build/.project11
-rw-r--r--org.eclipse.zest.examples.cloudio.build/pom.xml117
-rw-r--r--org.eclipse.zest.examples.cloudio.feature/.project17
-rw-r--r--org.eclipse.zest.examples.cloudio.feature/build.properties1
-rw-r--r--org.eclipse.zest.examples.cloudio.feature/feature.xml225
-rw-r--r--org.eclipse.zest.examples.cloudio.feature/pom.xml15
-rw-r--r--org.eclipse.zest.examples.cloudio.rcp/.project11
-rw-r--r--org.eclipse.zest.examples.cloudio.rcp/org.eclipse.zest.examples.cloudio.rcp.product56
-rw-r--r--org.eclipse.zest.examples.cloudio.rcp/plugin_customization.ini1
-rw-r--r--org.eclipse.zest.examples.cloudio.rcp/pom.xml56
-rw-r--r--org.eclipse.zest.examples.cloudio/.classpath7
-rw-r--r--org.eclipse.zest.examples.cloudio/.project28
-rw-r--r--org.eclipse.zest.examples.cloudio/.settings/org.eclipse.jdt.core.prefs8
-rw-r--r--org.eclipse.zest.examples.cloudio/META-INF/MANIFEST.MF12
-rw-r--r--org.eclipse.zest.examples.cloudio/build.properties5
-rw-r--r--org.eclipse.zest.examples.cloudio/plugin.xml151
-rw-r--r--org.eclipse.zest.examples.cloudio/pom.xml16
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/Application.java56
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ApplicationActionBarAdvisor.java72
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ApplicationWorkbenchAdvisor.java51
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ApplicationWorkbenchWindowAdvisor.java41
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/PerspectiveFactory.java28
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/about/AboutDialog.java135
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/AboutAction.java39
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/AbstractTagCloudAction.java52
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/DeselectAllAction.java27
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ExportImageAction.java48
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/LoadFileAction.java53
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/LoadStopWordsAction.java32
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/SelectAllAction.java29
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ZoomFitAction.java25
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ZoomInAction.java25
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ZoomOutAction.java25
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ZoomResetAction.java25
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/data/Type.java34
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/data/TypeCollector.java111
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/CloudioApplicationPlugin.java61
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/TagCloudViewPart.java230
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/TypeLabelProvider.java143
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/customization/CharacterLabelProvider.java38
-rw-r--r--org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/customization/CharacterLayouter.java47
-rw-r--r--org.eclipse.zest.feature/feature.xml7
-rw-r--r--org.eclipse.zest.source-feature/feature.xml7
-rw-r--r--org.eclipse.zest.tests/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.zest.tests/pom.xml2
-rw-r--r--org.eclipse.zest.tests/src/org/eclipse/zest/tests/AllHeadlessTests.java5
-rw-r--r--org.eclipse.zest.tests/src/org/eclipse/zest/tests/cloudio/TagCloudTests.java629
-rw-r--r--org.eclipse.zest.tests/src/org/eclipse/zest/tests/cloudio/TagCloudViewerTests.java195
-rw-r--r--org.eclipse.zest.tests/src/org/eclipse/zest/tests/cloudio/TestLabelProvider.java52
-rw-r--r--pom.xml1
71 files changed, 6151 insertions, 3 deletions
diff --git a/org.eclipse.zest.cloudio/.classpath b/org.eclipse.zest.cloudio/.classpath
new file mode 100644
index 0000000..fbb14bd
--- /dev/null
+++ b/org.eclipse.zest.cloudio/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src/main/java"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.zest.cloudio/.project b/org.eclipse.zest.cloudio/.project
new file mode 100644
index 0000000..13e1680
--- /dev/null
+++ b/org.eclipse.zest.cloudio/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.zest.cloudio</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.zest.cloudio/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.zest.cloudio/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..959127e
--- /dev/null
+++ b/org.eclipse.zest.cloudio/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Thu Jun 02 21:00:19 CEST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/org.eclipse.zest.cloudio/META-INF/MANIFEST.MF b/org.eclipse.zest.cloudio/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..d633766
--- /dev/null
+++ b/org.eclipse.zest.cloudio/META-INF/MANIFEST.MF
@@ -0,0 +1,13 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Cloudio UI
+Bundle-SymbolicName: org.eclipse.zest.cloudio;singleton:=true
+Bundle-Version: 2.0.0.qualifier
+Bundle-Activator: org.eclipse.zest.cloudio.Activator
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Export-Package: org.eclipse.zest.cloudio,
+ org.eclipse.zest.cloudio.layout,
+ org.eclipse.zest.cloudio.util
diff --git a/org.eclipse.zest.cloudio/build.properties b/org.eclipse.zest.cloudio/build.properties
new file mode 100644
index 0000000..23920ae
--- /dev/null
+++ b/org.eclipse.zest.cloudio/build.properties
@@ -0,0 +1,5 @@
+source.. = src/main/java
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ img/
diff --git a/org.eclipse.zest.cloudio/cloudio.target b/org.eclipse.zest.cloudio/cloudio.target
new file mode 100644
index 0000000..2c9f2d4
--- /dev/null
+++ b/org.eclipse.zest.cloudio/cloudio.target
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?pde version="3.6"?>
+
+<target includeMode="feature" name="Eclipse36">
+<locations>
+<location includeAllPlatforms="true" includeMode="slicer" type="InstallableUnit">
+<unit id="org.eclipse.rcp.feature.group" version="3.6.2.r362_v20101104-9SAxFMKFkSAqi8axkv1ZjegmiBLY"/>
+<repository location="http://download.eclipse.org/eclipse/updates/3.6"/>
+</location>
+</locations>
+</target>
diff --git a/org.eclipse.zest.cloudio/img/add.gif b/org.eclipse.zest.cloudio/img/add.gif
new file mode 100644
index 0000000..252d7eb
--- /dev/null
+++ b/org.eclipse.zest.cloudio/img/add.gif
Binary files differ
diff --git a/org.eclipse.zest.cloudio/img/remove.gif b/org.eclipse.zest.cloudio/img/remove.gif
new file mode 100644
index 0000000..b6922ac
--- /dev/null
+++ b/org.eclipse.zest.cloudio/img/remove.gif
Binary files differ
diff --git a/org.eclipse.zest.cloudio/img/toggle_colors.gif b/org.eclipse.zest.cloudio/img/toggle_colors.gif
new file mode 100644
index 0000000..e3a7cc7
--- /dev/null
+++ b/org.eclipse.zest.cloudio/img/toggle_colors.gif
Binary files differ
diff --git a/org.eclipse.zest.cloudio/pom.xml b/org.eclipse.zest.cloudio/pom.xml
new file mode 100644
index 0000000..fa0e6ab
--- /dev/null
+++ b/org.eclipse.zest.cloudio/pom.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>org.eclipse.zest</artifactId>
+ <groupId>org.eclipse</groupId>
+ <version>2.0.0-SNAPSHOT</version>
+ </parent>
+ <groupId>org.eclipse</groupId>
+ <artifactId>org.eclipse.zest.cloudio</artifactId>
+ <version>2.0.0-SNAPSHOT</version>
+ <packaging>eclipse-plugin</packaging>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.3</version>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <phase>package</phase>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>cloudio.target</file>
+ <type>target</type>
+ <classifier>cloudio</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>${tycho.groupId}</groupId>
+ <artifactId>tycho-source-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/Activator.java b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/Activator.java
new file mode 100644
index 0000000..7fd45b0
--- /dev/null
+++ b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/Activator.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.cloudio;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class Activator extends AbstractUIPlugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.schwiebert.eclipsetagcloud"; //$NON-NLS-1$
+
+ // The shared instance
+ private static Activator plugin;
+
+
+ public static final String ADD = "add.gif";
+
+ public static final String REMOVE = "remove.gif";
+
+ public static final String TOGGLE_COLORS = "toggle_colors.gif";
+
+
+ /**
+ * The constructor
+ */
+ public Activator() {
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ ImageLoader il = new ImageLoader();
+ loadImage(il, ADD);
+ loadImage(il, REMOVE);
+ loadImage(il, TOGGLE_COLORS);
+ }
+
+ private void loadImage(ImageLoader il, String fileName) throws IOException {
+ InputStream stream = getBundle().getResource("img/"+fileName).openStream();
+ ImageData[] data = il.load(stream);
+ Image image = new Image(Display.getDefault(), data[0]);
+ getImageRegistry().put(fileName, image);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault() {
+ return plugin;
+ }
+
+}
diff --git a/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/CloudOptionsComposite.java b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/CloudOptionsComposite.java
new file mode 100644
index 0000000..9aeddb6
--- /dev/null
+++ b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/CloudOptionsComposite.java
@@ -0,0 +1,516 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.cloudio;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.ColorDialog;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.FontDialog;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+
+/**
+ * Provides options to modify the rendering of a {@link TagCloudViewer} using an
+ * {@link IEditableCloudLabelProvider}.
+ *
+ * @author sschwieb
+ *
+ */
+public class CloudOptionsComposite extends Composite {
+
+ protected TagCloudViewer viewer;
+
+ protected List<RGB> colors = new ArrayList<RGB>();
+ protected List<FontData> fonts = new ArrayList<FontData>();
+
+ protected List<List<RGB>> colorSchemes = new ArrayList<List<RGB>>();
+
+ protected int currentScheme;
+
+ private static class ListContentProvider implements ITreeContentProvider {
+
+ @Override
+ public void dispose() {}
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {}
+
+ @Override
+ public Object[] getElements(Object inputElement) {
+ return ((List<?>)inputElement).toArray();
+ }
+
+ @Override
+ public Object[] getChildren(Object parentElement) {
+ return null;
+ }
+
+ @Override
+ public Object getParent(Object element) {
+ return null;
+ }
+
+ @Override
+ public boolean hasChildren(Object element) {
+ return false;
+ }
+
+ }
+
+ public CloudOptionsComposite(Composite parent, int style, TagCloudViewer viewer) {
+ super(parent, style);
+ Assert.isLegal(viewer.getLabelProvider() instanceof IEditableCloudLabelProvider, "Cloud label provider must be of type " + IEditableCloudLabelProvider.class);
+ this.viewer = viewer;
+ setLayout(new GridLayout());
+ addGroups();
+ }
+
+ protected void addGroups() {
+ addLayoutButtons(this);
+ addColorButtons(this);
+ addFontButtons(this);
+ }
+
+ protected void addScheme(RGB...rgbs) {
+ List<RGB> colors = new ArrayList<RGB>();
+ for (RGB rgb : rgbs) {
+ colors.add(rgb);
+ }
+ colorSchemes.add(colors);
+ }
+
+ protected void updateColors() {
+ IEditableCloudLabelProvider lp = (IEditableCloudLabelProvider) viewer.getLabelProvider();
+ lp.setColors(colors);
+ List<Word> words = viewer.getCloud().getWords();
+ for (Word word : words) {
+ word.setColor(lp.getColor(word.data));
+ }
+ viewer.getCloud().redrawTextLayerImage();
+ }
+
+ protected void updateFonts() {
+ IEditableCloudLabelProvider lp = (IEditableCloudLabelProvider) viewer.getLabelProvider();
+ lp.setFonts(fonts);
+ }
+
+ protected void initColors() {
+ addScheme(new RGB(222, 177, 17), new RGB(97, 28, 24), new RGB(102,109,17), new RGB(189, 112, 20), new RGB(111, 92, 16), new RGB(111, 32, 27));
+ addScheme(new RGB(1,175,255), new RGB(57,99,213), new RGB(21,49,213), new RGB(30,125,42));
+ addScheme(new RGB(255,92,93), new RGB(255,0,0), new RGB(255,41,43), new RGB(182,31,32), new RGB(153,0,0));
+ addScheme(new RGB(255,157,0), new RGB(255,206,0), new RGB(40,0,159), new RGB(0,41,156));
+ addScheme(new RGB(255,46,0), new RGB(255,255,14), new RGB(183, 183, 183), new RGB(122, 122, 122), new RGB(81, 81, 81), new RGB(61, 61, 61), new RGB(165, 165, 165));
+ addScheme(new RGB(255,0,206), new RGB(255,220,0), new RGB(0, 255, 42));
+ addScheme(new RGB(89, 79, 69), new RGB(168, 165, 126), new RGB(68, 49, 14), new RGB(86, 68, 34), new RGB(148, 141, 129), new RGB(92, 90, 41));
+ addScheme(new RGB(66,71,37), new RGB(85,122,18), new RGB(117,131,49), new RGB(49,45,17));
+ addScheme(new RGB(254,213,44), new RGB(255,177,10), new RGB(233,121,0), new RGB(229,109,3), new RGB(202,80,8), new RGB(129,52,7), new RGB(89,47,14));
+ addScheme(new RGB(139,124,115), new RGB(91,95,129), new RGB(50,23,18), new RGB(255,251,237));
+ nextColors();
+ }
+
+ protected void nextColors() {
+ currentScheme = (currentScheme+1)%colorSchemes.size();
+ colors = colorSchemes.get(currentScheme);
+ }
+
+ protected Group addFontButtons(final Composite parent) {
+ Group buttons = new Group(parent, SWT.SHADOW_IN);
+ buttons.setLayout(new GridLayout(2, false));
+ buttons.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ Label l = new Label(buttons, SWT.NONE);
+ l.setText("Fonts");
+ GridData gd = new GridData();
+ gd.horizontalSpan=2;
+ l.setLayoutData(gd);
+ final TreeViewer tv = new TreeViewer(buttons);
+ Composite comp = new Composite(buttons, SWT.NONE);
+ comp.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, true));
+ comp.setLayout(new RowLayout(SWT.VERTICAL));
+ tv.getTree().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ ListContentProvider cp = new ListContentProvider();
+ tv.setContentProvider(cp);
+ tv.setLabelProvider(new ColumnLabelProvider() {
+
+ @Override
+ public String getText(Object element) {
+ FontData fd = (FontData) element;
+ return fd.getName();
+ }
+
+ });
+ fonts.add(getFont().getFontData()[0]);
+ tv.setInput(fonts);
+ Button add = new Button(comp, SWT.FLAT);
+ add.setImage(Activator.getDefault().getImageRegistry().get(Activator.ADD));
+ add.setToolTipText("Add font...");
+ add.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ FontDialog fd = new FontDialog(parent.getShell());
+ FontData fontData = fd.open();
+ if(fontData != null) {
+ fonts.add(fontData);
+ tv.setInput(fonts);
+ updateFonts();
+ }
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+ Button remove = new Button(comp, SWT.FLAT);
+ remove.setToolTipText("Remove selected fonts");
+ remove.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ IStructuredSelection selection = (IStructuredSelection) tv.getSelection();
+ fonts.removeAll(selection.toList());
+ tv.setInput(fonts);
+ updateFonts();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+ remove.setImage(Activator.getDefault().getImageRegistry().get(Activator.REMOVE));
+ return buttons;
+ }
+
+ protected Image createImageFromColor(RGB rgb, int size) {
+ Image image;
+ Color color = new Color(Display.getDefault(), rgb);
+ image = new Image(Display.getDefault(), size, size);
+ GC gc = new GC(image);
+ gc.setBackground(color);
+ gc.fillRoundRectangle(0, 0, size, size, 3, 3);
+ color.dispose();
+ gc.dispose();
+ return image;
+ }
+
+ protected Group addColorButtons(final Composite parent) {
+ Group buttons = new Group(parent, SWT.SHADOW_IN);
+ buttons.setLayout(new GridLayout(2, false));
+ buttons.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ Label l = new Label(buttons, SWT.NONE);
+ l.setText("Colors");
+ GridData gd = new GridData();
+ gd.horizontalSpan=2;
+ l.setLayoutData(gd);
+ final TreeViewer tv = new TreeViewer(buttons);
+ Composite comp = new Composite(buttons, SWT.NONE);
+ comp.setLayout(new RowLayout(SWT.VERTICAL));
+ comp.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, true));
+ tv.getTree().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ ListContentProvider cp = new ListContentProvider();
+ tv.setContentProvider(cp);
+ tv.setLabelProvider(new ColumnLabelProvider() {
+
+ private Map<Object, Image> images = new HashMap<Object, Image>();
+
+ @Override
+ public Image getImage(Object element) {
+ Image image = images.get(element);
+ if(image == null) {
+ RGB rgb = (RGB) element;
+ image = createImageFromColor(rgb, 24);
+ images.put(element, image);
+ }
+ return image;
+ }
+
+ @Override
+ public void dispose() {
+ Collection<Image> images = this.images.values();
+ for (Image image : images) {
+ image.dispose();
+ }
+ this.images.clear();
+ }
+
+ });
+ initColors();
+ tv.setInput(colors);
+ Button add = new Button(comp, SWT.FLAT);
+ add.setImage(Activator.getDefault().getImageRegistry().get(Activator.ADD));
+ add.setToolTipText("Add color...");
+ add.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ ColorDialog cd = new ColorDialog(parent.getShell());
+ RGB color = cd.open();
+ if(color != null) {
+ colors.add(color);
+ tv.setInput(colors);
+ updateColors();
+ }
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+ Button remove = new Button(comp, SWT.FLAT);
+ remove.setToolTipText("Remove selected colors");
+ remove.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ IStructuredSelection selection = (IStructuredSelection) tv.getSelection();
+ colors.removeAll(selection.toList());
+
+ tv.setInput(colors);
+ updateColors();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+ remove.setImage(Activator.getDefault().getImageRegistry().get(Activator.REMOVE));
+ Button toggle = new Button(comp, SWT.FLAT);
+ toggle.setToolTipText("Toggle Colors");
+ toggle.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ nextColors();
+ tv.setInput(colors);
+ updateColors();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+ toggle.setImage(Activator.getDefault().getImageRegistry().get(Activator.TOGGLE_COLORS));
+
+ comp = new Composite(buttons, SWT.NONE);
+ gd = new GridData(SWT.FILL, SWT.FILL, true, false);
+ gd.horizontalSpan=2;
+ comp.setLayout(new GridLayout(2, true));
+ comp.setLayoutData(gd);
+ final Button bg = new Button(comp, SWT.FLAT);
+ bg.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ bg.setText("Background");
+ bg.setImage(createImageFromColor(viewer.getCloud().getBackground().getRGB(), 16));
+ bg.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ ColorDialog cd = new ColorDialog(parent.getShell());
+ RGB color = cd.open();
+ if(color == null) return;
+ Color old = viewer.getCloud().getBackground();
+ Color c = new Color(Display.getDefault(), color);
+ viewer.getCloud().setBackground(c);
+ old.dispose();
+ viewer.getCloud().redrawTextLayerImage();
+ Image oldImage = bg.getImage();
+ Image newImage = createImageFromColor(color, 16);
+ bg.setImage(newImage);
+ oldImage.dispose();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+ final Button sel = new Button(comp, SWT.FLAT);
+ sel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ sel.setText("Selection");
+ sel.setImage(createImageFromColor(viewer.getCloud().getSelectionColor().getRGB(), 16));
+ sel.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ ColorDialog cd = new ColorDialog(parent.getShell());
+ RGB color = cd.open();
+ if(color == null) return;
+ Color old = viewer.getCloud().getSelectionColor();
+ Color c = new Color(Display.getDefault(), color);
+ viewer.getCloud().setSelectionColor(c);
+ old.dispose();
+ viewer.getCloud().redrawTextLayerImage();
+ Image oldImage = sel.getImage();
+ Image newImage = createImageFromColor(color, 16);
+ sel.setImage(newImage);
+ oldImage.dispose();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+ return buttons;
+ }
+
+ protected Group addLayoutButtons(Composite parent) {
+ Group buttons = new Group(parent, SWT.SHADOW_IN);
+ buttons.setLayout(new GridLayout(2, true));
+ buttons.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ Label l = new Label(buttons, SWT.NONE);
+ l.setText("Number of Words");
+ final Combo words = new Combo(buttons, SWT.DROP_DOWN | SWT.READ_ONLY);
+ words.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ words.setItems(new String[] {"100","200","300","400","500","600","700","800","900","1000", "1100", "1200", "1300", "1400", "1500","1600", "1700", "1800", "1900", "2000"});
+ words.select(2);
+ words.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ String item = words.getItem(words.getSelectionIndex());
+ viewer.setMaxWords(Integer.parseInt(item));
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+
+ l = new Label(buttons, SWT.NONE);
+ l.setText("Max Font Size");
+ final Combo font = new Combo(buttons, SWT.DROP_DOWN | SWT.READ_ONLY);
+ font.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ font.setItems(new String[] {"50", "100", "150", "200", "250", "300", "350", "400", "450", "500"});
+ font.select(1);
+ font.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ String item = font.getItem(font.getSelectionIndex());
+ viewer.getCloud().setMaxFontSize(Integer.parseInt(item));
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+
+ l = new Label(buttons, SWT.NONE);
+ l.setText("Min Font Size");
+ final Combo minFont = new Combo(buttons, SWT.DROP_DOWN | SWT.READ_ONLY);
+ minFont.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ minFont.setItems(new String[] {"10", "15", "20", "25", "30", "35", "40", "45", "50"});
+ minFont.select(1);
+ minFont.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ String item = minFont.getItem(minFont.getSelectionIndex());
+ viewer.getCloud().setMinFontSize(Integer.parseInt(item));
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+
+ l = new Label(buttons, SWT.NONE);
+ l.setText("Boost");
+ final Combo boost = new Combo(buttons, SWT.DROP_DOWN | SWT.READ_ONLY);
+ boost.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ boost.setItems(new String[] {"0", "1", "2","3","4","5","6","7","8","9","10"});
+ boost.select(0);
+ boost.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ String item = boost.getItem(boost.getSelectionIndex());
+ viewer.setBoost(Integer.parseInt(item));
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+
+
+ l = new Label(buttons, SWT.NONE);
+ l.setText("Boost Factor");
+ final Combo boostFactor = new Combo(buttons, SWT.DROP_DOWN | SWT.READ_ONLY);
+ boostFactor.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ boostFactor.setItems(new String[] {"1","1.5","2","2.5","3","3.5", "4", "4.5", "5"});
+ boostFactor.select(0);
+ boostFactor.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ String item = boostFactor.getItem(boostFactor.getSelectionIndex());
+ viewer.setBoostFactor(Float.parseFloat(item));
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+
+ if(viewer.getLabelProvider() instanceof IEditableCloudLabelProvider) {
+
+ }
+
+ l = new Label(buttons, SWT.NONE);
+ l.setText("Angles");
+ final Combo angles = new Combo(buttons, SWT.DROP_DOWN | SWT.READ_ONLY);
+ angles.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ angles.setItems(new String[] {"Horizontal only", "Vertical only", "Horizontal & Vertical", "45 Degrees, mostly horizontal", "45 Degrees", "Random"});
+ final List<List<Float>> anglesLists = new ArrayList<List<Float>>();
+ anglesLists.add(Arrays.asList(0F));
+ anglesLists.add(Arrays.asList(-90F,90F));
+ anglesLists.add(Arrays.asList(0F,-90F,0F,90F));
+ anglesLists.add(Arrays.asList(0F,-90F,-45F, 0F,45F, 90F,0F,0F,0F,0F));
+ anglesLists.add(Arrays.asList(-90F,-45F, 0F,45F, 90F));
+ List<Float> tmp = new ArrayList<Float>();
+ for(int i = -90; i <= 90; i++) {
+ tmp.add((float) i);
+ }
+ anglesLists.add(tmp);
+ angles.select(0);
+ angles.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ int index = angles.getSelectionIndex();
+ IEditableCloudLabelProvider lp = (IEditableCloudLabelProvider) viewer.getLabelProvider();
+ lp.setAngles(anglesLists.get(index));
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+ return buttons;
+ }
+
+ public List<RGB> getColors() {
+ return colors;
+ }
+
+ public List<FontData> getFonts() {
+ return fonts;
+ }
+}
diff --git a/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/ICloudLabelProvider.java b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/ICloudLabelProvider.java
new file mode 100644
index 0000000..d3fb132
--- /dev/null
+++ b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/ICloudLabelProvider.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.cloudio;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.FontData;
+
+/**
+ * Defines the label of an element within the cloud. Besides of the
+ * string-label, each element can be assigned a unique weight (used
+ * to calculate the font size of the rendered element), color,
+ * font and angle.
+ * @author sschwieb
+ *
+ */
+public interface ICloudLabelProvider extends IBaseLabelProvider {
+
+ /**
+ * The label of the given element, which must not
+ * be <code>null</code>.
+ * @param element
+ * @return
+ */
+ public String getLabel(Object element);
+
+ /**
+ * The weight of the given element, which must be between
+ * 0 and 1 (inclusive).
+ * @param element
+ * @return
+ */
+ public double getWeight(Object element);
+
+ /**
+ * The {@link Color} of the given element, which must not
+ * be <code>null</code>.
+ * @param element
+ * @return
+ */
+ public Color getColor(Object element);
+
+ /**
+ * The {@link FontData}-array which defines the font
+ * of the given element. Each element must be provided
+ * with a unique array. Must not return <code>null</code>.
+ * @param element
+ * @return
+ */
+ public FontData[] getFontData(Object element);
+
+ /**
+ * The angle of the element, which must be between -90 and 90, inclusive.
+ * @param element
+ * @return
+ */
+ public float getAngle(Object element);
+
+ /**
+ * Return the tool tip of the element, or <code>null</code>, if none.
+ * @param data
+ * @return
+ */
+ public String getToolTip(Object element);
+
+}
diff --git a/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/IEditableCloudLabelProvider.java b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/IEditableCloudLabelProvider.java
new file mode 100644
index 0000000..ced77a7
--- /dev/null
+++ b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/IEditableCloudLabelProvider.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.cloudio;
+
+import java.util.List;
+
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.RGB;
+
+/**
+ * An {@link IEditableCloudLabelProvider} is supported by the {@link CloudOptionsComposite},
+ * such that it can be used to modify colors, fonts, and angles.
+ *
+ * @author sschwieb
+ *
+ */
+public interface IEditableCloudLabelProvider extends ICloudLabelProvider {
+
+ public void setColors(List<RGB> colors);
+
+ public void setFonts(List<FontData> fonts);
+
+ public void setAngles(List<Float> list);
+
+}
diff --git a/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/TagCloud.java b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/TagCloud.java
new file mode 100644
index 0000000..ca9d246
--- /dev/null
+++ b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/TagCloud.java
@@ -0,0 +1,1340 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.cloudio;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EventListener;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.events.MouseWheelListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Path;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.Region;
+import org.eclipse.swt.graphics.Transform;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.zest.cloudio.layout.DefaultLayouter;
+import org.eclipse.zest.cloudio.layout.ILayouter;
+import org.eclipse.zest.cloudio.util.RectTree;
+import org.eclipse.zest.cloudio.util.SmallRect;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class TagCloud extends Canvas {
+
+ /**
+ * Minimum 'resolution' of the {@link RectTree} used for
+ * collision handling.
+ */
+ private static final int RESOLUTION = 5;
+
+ /**
+ * Maximum size of the {@link RectTree} used for collision
+ * handling.
+ */
+ private static final int TREE_SIZE = 5120/* * 2 */;
+
+ /**
+ * Draw area.
+ */
+ private final Rectangle cloudArea = new Rectangle(0, 0, TREE_SIZE, TREE_SIZE);
+
+ /**
+ * Maximum Font Size.
+ */
+ private int maxFontSize = 100;
+
+ private final GC gc;
+
+ /**
+ * Highlight color.
+ */
+ private Color highlightColor;
+
+ /**
+ * Used to detect mousehover, -enter and -exit.
+ */
+ private Word currentWord;
+
+ /**
+ * Opacity of the rendered strings.
+ */
+ private int opacity = 255;
+ /**
+ * Required for scroll bars
+ */
+ private final Point origin = new Point (0, 0);
+
+ /**
+ * Main image, on which all strings are rendered.
+ */
+ private Image textLayerImage;
+
+ /**
+ * Second level image: All Elements plus selected elements
+ * in highlight color.
+ */
+
+ private Image selectionLayerImage;
+
+ /**
+ * Last level image: All + selected elements, zoomed. This
+ * is the image which will be displayed.
+ */
+ private Image zoomLayerImage;
+
+ /**
+ * The list of words to render.
+ */
+ private List<Word> wordsToUse;
+
+ private boolean initialized = false;
+
+ /**
+ * Current zoom factor.
+ */
+ private double currentZoom = 1;
+
+ /**
+ * The region used by the rendered words.
+ */
+ private Region region;
+
+ /**
+ * Minimum font size.
+ */
+ private int minFontSize = 12;
+
+ /**
+ * Set of selected words
+ */
+ private Set<Word> selection = new HashSet<Word>();
+
+ private short[][] cloudMatrix;
+
+ /**
+ * Executor service to process the creation of {@link RectTree} objects
+ * in parallel.
+ */
+ private ExecutorService executors;
+
+ private ILayouter layouter;
+
+ /**
+ * The <code>boost</code> words with highest weight will be further
+ * increased in size. Eye-Candy only.
+ */
+ private int boost;
+
+ /**
+ * Offset of the region which surrounds the placed words, required to
+ * translate between mouse position and underlying words.
+ */
+ private Point regionOffset;
+
+ private int antialias = SWT.ON;
+
+ private float boostFactor;
+
+ private Listener hBarListener;
+
+ private Listener resizeListener;
+
+ private Listener paintListener;
+
+ private Listener mouseTrackListener;
+
+ private Listener mouseMoveListener;
+
+ private Listener mouseUpListener;
+
+ private Listener mouseDCListener;
+
+ private Listener mouseDownListener;
+
+ private Listener mouseWheelListener;
+
+ private Listener vBarListener;
+
+ private Set<EventListener> mouseWheelListeners = new HashSet<EventListener>();
+
+ private Set<EventListener> mouseTrackListeners = new HashSet<EventListener>();
+
+ private Set<EventListener> mouseMoveListeners = new HashSet<EventListener>();
+
+ private Set<EventListener> mouseListeners = new HashSet<EventListener>();
+
+ private Set<SelectionListener> selectionListeners = new HashSet<SelectionListener>();
+
+ /**
+ * Creates a new Tag cloud on the given parent. To add scroll bars
+ * to the cloud, use {@link SWT#HORIZONTAL} and {@link SWT#VERTICAL}.
+ * @param parent
+ * @param style
+ */
+ public TagCloud(Composite parent, int style) {
+ super(parent, style);
+ highlightColor = new Color(getDisplay(), Display.getDefault().getSystemColor(SWT.COLOR_RED).getRGB());
+ gc = new GC(this);
+ layouter = new DefaultLayouter(5, 5);
+ setBackground(new Color(getDisplay(), Display.getDefault().getSystemColor(SWT.COLOR_BLACK).getRGB()));
+ initListeners();
+ textLayerImage = new Image(getDisplay(), 100,100);
+ zoomFit();
+ addDisposeListener(new DisposeListener() {
+
+ @Override
+ public void widgetDisposed(DisposeEvent e) {
+ internalDispose();
+ }
+ });
+ }
+
+ /**
+ * Disposes all system resources created in this class. Resources which were
+ * provided through a {@link ICloudLabelProvider} etc are not disposed.
+ */
+ private void internalDispose() {
+ removeListeners();
+ textLayerImage.dispose();
+ if(selectionLayerImage != null) {
+ selectionLayerImage.dispose();
+ }
+ if(zoomLayerImage != null) {
+ zoomLayerImage.dispose();
+ }
+ if(!this.isDisposed()) {
+ gc.dispose();
+ }
+ super.dispose();
+ }
+
+ private void removeListeners() {
+ if(isDisposed()) return;
+ removeListener(SWT.Paint, paintListener);
+ if(hBarListener != null) {
+ removeListener(SWT.H_SCROLL, hBarListener);
+ }
+ if(vBarListener != null) {
+ removeListener(SWT.V_SCROLL, vBarListener);
+ }
+ removeListener(SWT.MouseDoubleClick, mouseDCListener);
+ removeListener(SWT.MouseDown, mouseDownListener);
+ removeListener(SWT.MouseMove, mouseTrackListener);
+ removeListener(SWT.MouseUp, mouseUpListener);
+ removeListener(SWT.Resize, resizeListener);
+ removeListener(SWT.MouseMove, mouseMoveListener);
+ removeListener(SWT.MouseWheel, mouseWheelListener);
+ }
+
+
+ /**
+ * Resets the zoom to 100 % (original size)
+ */
+ public void zoomReset() {
+ checkWidget();
+ if(selectionLayerImage == null) return;
+ zoomLayerImage = new Image(getDisplay(), selectionLayerImage.getBounds().width, selectionLayerImage.getBounds().height);
+ GC gc = new GC(zoomLayerImage);
+ gc.drawImage(selectionLayerImage, 0, 0);
+ gc.dispose();
+ currentZoom = 1;
+ updateScrollbars();
+ redraw();
+ }
+
+ public double getZoom() {
+ checkWidget();
+ return currentZoom;
+ }
+
+ /**
+ * Resets the zoom such that the generated cloud will
+ * fit extactly into the available space (unless the zoom
+ * factor is too small or too large).
+ */
+ public void zoomFit() {
+ checkWidget();
+ if(selectionLayerImage == null) return;
+ Rectangle imageBound = selectionLayerImage.getBounds();
+ Rectangle destRect = getClientArea();
+ double sx = (double) destRect.width / (double) imageBound.width;
+ double sy = (double) destRect.height / (double) imageBound.height;
+ currentZoom = Math.min(sx, sy);
+ zoom(currentZoom);
+ }
+
+ private void zoom(double s) {
+ checkWidget();
+ if(selectionLayerImage == null) return;
+ if(s < 0.1) s = 0.1;
+ if(s > 3) s = 3;
+ int width = (int) (selectionLayerImage.getBounds().width*s);
+ int height = (int) (selectionLayerImage.getBounds().height*s);
+ if(width == 0 || height == 0) return;
+ zoomLayerImage = new Image(getDisplay(), width, height);
+ Transform tf = new Transform(getDisplay());
+ tf.scale((float)s, (float)s);
+ GC gc = new GC(zoomLayerImage);
+ gc.setTransform(tf);
+ gc.drawImage(selectionLayerImage, 0, 0);
+ gc.dispose();
+ tf.dispose();
+ currentZoom = s;
+ updateScrollbars();
+ redraw();
+ }
+
+ /**
+ * Zooms in, by the factor of 10 percent.
+ */
+ public void zoomIn() {
+ checkWidget();
+ zoom(currentZoom * 1.1);
+ redraw();
+ }
+
+ /**
+ * Zooms out, by the factor of 10 percent.
+ */
+ public void zoomOut() {
+ checkWidget();
+ zoom(currentZoom * 0.9);
+ redraw();
+ }
+
+
+ /**
+ * Returns the maximum cloud area.
+ * @return
+ */
+ protected Rectangle getCloudArea() {
+ return cloudArea ;
+ }
+
+ /**
+ * Returns the font size of the given word.
+ * By default, this is calculated as <code>8 + (word.weight * maxFontSize)</code>.
+ * @param word
+ * @return
+ */
+ private float getFontSize(Word word) {
+ float size = (float) (word.weight * maxFontSize);
+ size += minFontSize;
+ return size;
+ }
+
+ /**
+ * Draws a word with the given color.
+ * @param gc
+ * @param word
+ * @param color
+ */
+ private void drawWord(final GC gc, final Word word, final Color color) {
+ gc.setForeground(color);
+ Font font = new Font(gc.getDevice(), word.getFontData());
+ gc.setFont(font);
+ gc.setAntialias(antialias);
+ gc.setAlpha(opacity);
+ Point stringExtent = word.stringExtent;
+ gc.setForeground(color);
+ int xOffset = word.x - regionOffset.x;
+ int yOffset = word.y - regionOffset.y;
+ double radian = Math.toRadians(word.angle);
+ final double sin = Math.abs(Math.sin(radian));
+ final double cos = Math.abs(Math.cos(radian));
+
+ int y = (int) ((cos*stringExtent.y) + (sin*stringExtent.x));
+ Transform t = new Transform(gc.getDevice());
+ if(word.angle < 0) {
+ t.translate(xOffset, yOffset + y - (int) (cos*stringExtent.y));
+ } else {
+ t.translate(xOffset + (int) (sin*stringExtent.y), yOffset);
+ }
+ t.rotate(word.angle);
+ gc.setTransform(t);
+ gc.drawString(word.string, 0, 0, true);
+ gc.setTransform(null);
+ t.dispose();
+ font.dispose();
+ }
+
+
+ /**
+ * Calculates the bounds of each word, by determining
+ * the {@link Rectangle} a {@link Path} would require
+ * to render an element.
+ * @param monitor
+ */
+ protected void calcExtents(IProgressMonitor monitor) {
+ checkWidget();
+ if(monitor != null) {
+ monitor.subTask("Calculating word boundaries...");
+ }
+ if(wordsToUse == null) return;
+ double step = 80D/wordsToUse.size();
+ double current = 0;
+ int next = 10;
+ executors = Executors.newFixedThreadPool(getNumberOfThreads());
+ for (final Word word : wordsToUse) {
+ FontData[] fontData = word.getFontData();
+ int fontSize = (int) getFontSize(word);
+ for (FontData data : fontData) {
+ data.setHeight((int)fontSize);
+ }
+ final Font font = new Font(gc.getDevice(), fontData);
+ gc.setFont(font);
+ final Color color = gc.getDevice().getSystemColor(SWT.COLOR_BLACK);
+ final Point stringExtent = gc.stringExtent(word.string);
+ FontMetrics fm = gc.getFontMetrics();
+ stringExtent.y = fm.getHeight();
+ executors.execute(new Runnable() {
+ @Override
+ public void run() {
+ double radian = Math.toRadians(word.angle);
+ final double sin = Math.abs(Math.sin(radian));
+ final double cos = Math.abs(Math.cos(radian));
+ final int x = (int) ((cos*stringExtent.x) + (sin*stringExtent.y));
+ final int y = (int) ((cos*stringExtent.y) + (sin*stringExtent.x));
+ ImageData id = createImageData(word, font, stringExtent, sin,
+ cos, x, y, color);
+ calcWordExtents(word, id);
+ font.dispose();
+ }
+ });
+ if(monitor != null) {
+ current += step;
+ if(current > next) {
+ monitor.worked(5);
+ next += 5;
+ }
+ }
+ }
+ executors.shutdown();
+ try {
+ executors.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ Collections.sort(wordsToUse, new Comparator<Word>() {
+
+ @Override
+ public int compare(Word o1, Word o2) {
+ return (o2.width * o2.height) - (o1.width * o1.height);
+ }
+ });
+ short i = 1;
+ for (Word word : wordsToUse) {
+ word.id = i++;
+ }
+ }
+
+ private ImageData createImageData(final Word word, Font font,
+ Point stringExtent, final double sin, final double cos, int x, int y, Color color) {
+ Image img = new Image(null, x, y);
+ word.width = x;
+ word.height = y;
+ word.stringExtent = stringExtent;
+ GC g = new GC(img);
+ g.setAntialias(antialias);
+ g.setForeground(color);
+ Transform t = new Transform(img.getDevice());
+ if(word.angle < 0) {
+ t.translate(0,img.getBounds().height - (int) (cos*stringExtent.y));
+ } else {
+ t.translate((int) (sin*stringExtent.y), 0);
+ }
+ t.rotate(word.angle);
+ g.setTransform(t);
+ g.setFont(font);
+ // Why is drawString sooo slow? between 30 and 90 percent of the whole
+ // draw time...
+ g.drawString(word.string, 0, 0, false);
+ int max = Math.max(x,y);
+ int tmp = TREE_SIZE;
+ while(max < tmp) {
+ tmp = tmp/2;
+ }
+ tmp = tmp*2;
+ SmallRect root = new SmallRect(0, 0, tmp, tmp);
+ word.tree = new RectTree(root, RESOLUTION);
+ final ImageData id = img.getImageData();
+ g.dispose();
+ img.dispose();
+ return id;
+ }
+
+
+ /**
+ * Calculates the extents of a word, based on its rendered image.
+ */
+ private void calcWordExtents(final Word word, final ImageData id) {
+ final int[] pixels = new int[id.width];
+ final boolean[][] matrix = new boolean[id.height][id.width];
+ for (int y = 0; y < id.height; y++) {
+ id.getPixels(0, y, id.width, pixels, 0);
+ for (int i = 0; i < pixels.length; i++) {
+ if (pixels[i] == 0) {
+ matrix[y][i] = true;
+ }
+ }
+ }
+ for(int i = 0; i < id.width; i+= RESOLUTION) {
+ for(int j = 0; j < id.height; j += RESOLUTION) {
+ final int x = Math.max(0, i-1);
+ final int y = Math.max(0, j-1);
+ final int xMax = Math.min(i+RESOLUTION+2, id.width);
+ final int yMax = Math.min(j+RESOLUTION+2, id.height);
+found: for(int a = x; a < xMax; a++) {
+ for(int b = y; b < yMax; b++) {
+ if(matrix[b][a]) {
+ SmallRect r = new SmallRect(i, j, RESOLUTION, RESOLUTION);
+ word.tree.insert(r, word.id);
+ break found;
+ }
+ }
+ }
+ }
+ }
+ word.tree.releaseRects();
+ }
+
+ /**
+ * Generates the layout of the given words.
+ * @param wordsToUse
+ * @param monitor may be <code>null</code>.
+ * @return the number of words which could be placed
+ */
+ protected int layoutWords(Collection<Word> wordsToUse, IProgressMonitor monitor) {
+ checkWidget();
+ if(monitor != null) {
+ monitor.subTask("Placing words...");
+ }
+ region = new Region();
+ final Rectangle cloudArea = getCloudArea();
+ int w = cloudArea.width;
+ int h = cloudArea.height;
+ double current = 0;
+ int next = 10;
+ final Image tmpImage = new Image(getDisplay(), w, h);
+ GC gc = new GC(tmpImage);
+ gc.setBackground(getBackground());
+ gc.setTextAntialias(SWT.ON);
+ gc.setBackground(getBackground());
+ gc.fillRectangle(tmpImage.getBounds());
+ executors = Executors.newFixedThreadPool(1);
+ int success = 0;
+ if(wordsToUse != null) {
+ double step = 100D / wordsToUse.size();
+ long lTime = 0;
+ final GC g = gc;
+ for (Word word : wordsToUse) {
+ long s = System.currentTimeMillis();
+ Point point = layouter.getInitialOffset(word, cloudArea);
+ boolean result = layouter.layout(point, word, cloudArea, cloudMatrix);
+ lTime += System.currentTimeMillis() - s;
+ if(!result) {
+ System.err.println("Failed to place " + word.string);
+ continue;
+ }
+ success++;
+ region.add(word.x, word.y, word.width, word.height);
+ final Word wrd = word;
+ executors.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ drawWord(g, wrd, wrd.getColor());
+ }
+ });
+ current += step;
+ if(current > next) {
+ next += 5;
+ if(monitor != null) {
+ monitor.worked(5);
+ }
+// executors.submit(new Runnable() {
+//
+// @Override
+// public void run() {
+// Rectangle r = region.getBounds();
+// if(textLayerImage != null) {
+// textLayerImage.dispose();
+// }
+// textLayerImage = new Image(getDisplay(), r.width, r.height);
+// GC g2 = new GC(textLayerImage);
+// g2.drawImage(tmpImage, r.x, r.y, r.width, r.height, 0, 0, r.width, r.height);
+// g2.dispose();
+// if(selectionLayerImage != null) {
+// selectionLayerImage.dispose();
+// }
+// selectionLayerImage = new Image(getDisplay(), r.width, r.height);
+// g2 = new GC(selectionLayerImage);
+// g2.drawImage(textLayerImage, 0, 0);
+// g2.dispose();
+// zoomReset();
+// }
+// });
+
+ }
+
+ }
+ executors.shutdown();
+ try {
+ executors.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ Rectangle r = region.getBounds();
+ gc.dispose();
+ if(textLayerImage != null) {
+ textLayerImage.dispose();
+ }
+ r.width += 5;
+ r.height += 5;
+ textLayerImage = new Image(getDisplay(), r.width, r.height);
+ gc = new GC(textLayerImage);
+ if(r.width > tmpImage.getBounds().width - r.x || r.height >= tmpImage.getBounds().width - r.y) {
+ MessageDialog.openError(getShell(), "Failed to place image", "The drawn image does not fit into the available region.");
+ tmpImage.dispose();
+ region.dispose();
+ gc.dispose();
+ return 0;
+ }
+ gc.drawImage(tmpImage, r.x, r.y, r.width, r.height, 0, 0, r.width, r.height);
+ this.regionOffset = new Point(r.x, r.y);
+ tmpImage.dispose();
+ gc.dispose();
+ selectionLayerImage = new Image(getDisplay(), r.width, r.height);
+ gc = new GC(selectionLayerImage);
+ gc.drawImage(textLayerImage, 0, 0);
+ gc.dispose();
+ region.dispose();
+ zoomReset();
+ if(monitor != null) {
+ monitor.worked(10);
+ }
+ return success;
+ }
+
+ /**
+ * Sets the given list as input of the tag cloud,
+ * replacing any previous content. By default,
+ * available word positions will be determined in-order,
+ * starting with the element at position 0.
+ *
+ * @param values
+ * @param maxWords
+ */
+ public int setWords(List<Word> values, IProgressMonitor monitor) {
+ checkWidget();
+ Assert.isLegal(values != null, "List must not be null!");
+ for (Word word : values) {
+ Assert.isLegal(word != null, "Word must not be null!");
+ Assert.isLegal(word.string != null, "Word must define a string!");
+ Assert.isLegal(word.getColor() != null, "A word must define a color");
+ Assert.isLegal(word.getFontData() != null, "A word must define a fontdata array");
+ Assert.isLegal(word.weight >= 0, "Word weight must be between 0 and 1 (inclusive), but value was " + word.weight);
+ Assert.isLegal(word.weight <= 1, "Word weight must be between 0 and 1 (inclusive), but value was " + word.weight);
+ Assert.isLegal(word.angle >= -90, "Angle must be between -90 and +90 (inclusive), but was " + word.angle);
+ Assert.isLegal(word.angle <= 90, "Angle must be between -90 and +90 (inclusive), but was " + word.angle);
+ }
+ this.wordsToUse = new ArrayList<Word>(values);
+ if(boost > 0) {
+ double factor = boostFactor;
+ int i = boost;
+ for (Word word : values) {
+ if(factor <= 1) {
+ break;
+ }
+ word.weight *= factor;
+ factor-= 0.2;
+ i--;
+ if(i == 0) break;
+ }
+ }
+ return layoutCloud(monitor, true);
+ }
+
+ /**
+ * Reset the initial matrix
+ */
+ private void resetLayout() {
+ if(cloudMatrix == null) cloudMatrix = new short[TREE_SIZE][TREE_SIZE];
+ for(int i = 0; i < cloudMatrix.length; i++) {
+ for(int j = 0; j < cloudMatrix[i].length; j++) {
+ cloudMatrix[i][j] = -1;
+ }
+ }
+ }
+
+ private int getNumberOfThreads() {
+ return Runtime.getRuntime().availableProcessors();
+ }
+
+ /**
+ * Initialize internal listeners (scrollbar, mouse, paint...).
+ */
+ private void initListeners() {
+ if(initialized) return;
+ initialized = true;
+ final ScrollBar hBar = this.getHorizontalBar();
+ if(hBar != null) {
+ hBarListener = new Listener() {
+ @Override
+ public void handleEvent (Event e) {
+ int hSelection = hBar.getSelection();
+ int destX = -hSelection - origin.x;
+ Rectangle rect = zoomLayerImage.getBounds();
+ TagCloud.this.scroll (destX, 0, 0, 0, rect.width, rect.height, false);
+ origin.x = -hSelection;
+ }
+ };
+ hBar.addListener (SWT.Selection, hBarListener);
+ }
+ final ScrollBar vBar = this.getVerticalBar();
+ if(vBar != null) {
+ vBarListener = new Listener() {
+ @Override
+ public void handleEvent (Event e) {
+ int vSelection = vBar.getSelection ();
+ int destY = -vSelection - origin.y;
+ Rectangle rect = zoomLayerImage.getBounds();
+ TagCloud.this.scroll (0, destY, 0, 0, rect.width, rect.height, false);
+ origin.y = -vSelection;
+ }
+ };
+ vBar.addListener (SWT.Selection, vBarListener);
+ }
+ resizeListener = new Listener() {
+ @Override
+ public void handleEvent (Event e) {
+ updateScrollbars();
+ TagCloud.this.redraw();
+ }
+ };
+ this.addListener (SWT.Resize, resizeListener);
+ paintListener = new Listener() {
+ @Override
+ public void handleEvent (Event e) {
+ GC gc = e.gc;
+ if(zoomLayerImage == null) return;
+ Rectangle rect = zoomLayerImage.getBounds();
+ Rectangle client = TagCloud.this.getClientArea();
+ int marginWidth = client.width - rect.width;
+ gc.setBackground(getBackground());
+ if (marginWidth > 0) {
+ gc.fillRectangle(rect.width, 0, marginWidth, client.height);
+ }
+ int marginHeight = client.height - rect.height;
+ if (marginHeight > 0) {
+ gc.fillRectangle(0, rect.height, client.width, marginHeight);
+ }
+ gc.drawImage(zoomLayerImage, origin.x, origin.y);
+ }
+ };
+ this.addListener (SWT.Paint, paintListener);
+ mouseTrackListener = new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ Word word = getWordAt(new Point(event.x, event.y));
+ MouseEvent me = createMouseEvent(event, word);
+ if(currentWord != null) {
+ if(word == currentWord) {
+ fireMouseEvent(me, SWT.MouseHover, mouseTrackListeners);
+ } else {
+ currentWord = null;
+ fireMouseEvent(me, SWT.MouseExit, mouseTrackListeners);
+ }
+ }
+ if(currentWord == null && word != null) {
+ currentWord = word;
+ fireMouseEvent(me, SWT.MouseEnter, mouseTrackListeners);
+ }
+ }
+ };
+ this.addListener(SWT.MouseMove, mouseTrackListener);
+ mouseMoveListener = new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ Word word = getWordAt(new Point(event.x, event.y));
+ MouseEvent me = createMouseEvent(event, word);
+ fireMouseEvent(me, SWT.MouseMove, mouseMoveListeners);
+ }
+ };
+ this.addListener(SWT.MouseMove, mouseMoveListener);
+ mouseUpListener = new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ Word word = getWordAt(new Point(event.x, event.y));
+ MouseEvent me = createMouseEvent(event, word);
+ fireMouseEvent(me, SWT.MouseUp, mouseListeners);
+ }
+ };
+ this.addListener(SWT.MouseUp, mouseUpListener);
+ mouseDCListener = new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ Word word = getWordAt(new Point(event.x, event.y));
+ MouseEvent me = createMouseEvent(event, word);
+ fireMouseEvent(me, SWT.MouseDoubleClick, mouseListeners);
+ }
+ };
+ this.addListener(SWT.MouseDoubleClick, mouseDCListener);
+ mouseDownListener = new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ Word word = getWordAt(new Point(event.x, event.y));
+ MouseEvent me = createMouseEvent(event, word);
+ fireMouseEvent(me, SWT.MouseDown, mouseListeners);
+ }
+ };
+ this.addListener(SWT.MouseDown, mouseDownListener);
+ mouseWheelListener = new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ Word word = getWordAt(new Point(event.x, event.y));
+ MouseEvent me = createMouseEvent(event, word);
+ fireMouseEvent(me, SWT.MouseWheel, mouseWheelListeners);
+ }
+ };
+ this.addListener(SWT.MouseWheel, mouseWheelListener);
+ }
+
+ /**
+ * Translates the given point in screen coordinates to the corresponding
+ * point in the (zoomed and scrolled) image and returns the {@link Word}
+ * at this position, or <code>null</code>, if no word exists at this
+ * position.
+ * @param point
+ * @return
+ */
+ private Word getWordAt(Point point) {
+ if(cloudMatrix == null || regionOffset == null) return null;
+ Point translatedMousePos = translateMousePos(point.x, point.y);
+ translatedMousePos.x += regionOffset.x;
+ translatedMousePos.y += regionOffset.y;
+ int x = translatedMousePos.x/RESOLUTION;
+ int y = translatedMousePos.y/RESOLUTION;
+ if(x >= cloudMatrix.length || y >= cloudMatrix[x].length) {
+ return null;
+ }
+ short wordId = cloudMatrix[x][y];
+ if(wordId > 0) {
+ Word clicked = wordsToUse.get(wordId-1);
+ return clicked;
+ }
+ return null;
+ }
+
+ /**
+ * Translates the current mouse position, such that it
+ * corresponds to scroll bars and zoom.
+ * @param x
+ * @param y
+ * @return
+ */
+ private Point translateMousePos(final int x, final int y) {
+ final Point point = new Point(x-origin.x, y-origin.y);
+ point.x /= currentZoom;
+ point.y /= currentZoom;
+ return point;
+ }
+
+ @Override
+ public void addMouseListener(MouseListener listener) {
+ checkWidget();
+ Assert.isLegal(listener != null);
+ mouseListeners.add(listener);
+ }
+
+ @Override
+ public void addMouseMoveListener(MouseMoveListener listener) {
+ checkWidget();
+ Assert.isLegal(listener != null);
+ mouseMoveListeners.add(listener);
+ }
+
+ @Override
+ public void addMouseTrackListener(MouseTrackListener listener) {
+ checkWidget();
+ Assert.isLegal(listener != null);
+ mouseTrackListeners.add(listener);
+ }
+
+ @Override
+ public void addMouseWheelListener(MouseWheelListener listener) {
+ checkWidget();
+ Assert.isLegal(listener != null);
+ mouseWheelListeners.add(listener);
+ }
+
+ public void addSelectionListener(SelectionListener listener) {
+ checkWidget();
+ Assert.isLegal(listener != null);
+ selectionListeners .add(listener);
+ }
+
+ @Override
+ public void removeMouseListener(MouseListener listener) {
+ checkWidget();
+ mouseListeners.remove(listener);
+ }
+
+ @Override
+ public void removeMouseMoveListener(MouseMoveListener listener) {
+ checkWidget();
+ mouseMoveListeners.remove(listener);
+ }
+
+ @Override
+ public void removeMouseTrackListener(MouseTrackListener listener) {
+ checkWidget();
+ mouseTrackListeners.remove(listener);
+ }
+
+ @Override
+ public void removeMouseWheelListener(MouseWheelListener listener) {
+ checkWidget();
+ mouseWheelListeners.remove(listener);
+ }
+
+ public void removeSelectionListener(SelectionListener listener) {
+ checkWidget();
+ selectionListeners.remove(listener);
+ }
+
+ private MouseEvent createMouseEvent(Event event, Word word) {
+ MouseEvent me = new MouseEvent(event);
+ me.x = event.x - origin.x;
+ me.y = event.y - origin.y;
+ me.data = word;
+ me.widget = TagCloud.this;
+ me.display = Display.getCurrent();
+ return me;
+ }
+
+ private void fireMouseEvent(MouseEvent me, int type, Set<EventListener> listeners) {
+ for (EventListener listener : listeners) {
+ if(listener instanceof MouseListener) {
+ MouseListener ml = (MouseListener) listener;
+ switch(type) {
+ case SWT.MouseUp: ml.mouseUp(me); break;
+ case SWT.MouseDoubleClick: ml.mouseDoubleClick(me); break;
+ case SWT.MouseDown: ml.mouseDown(me); break;
+ }
+ }
+ if(listener instanceof MouseTrackListener) {
+ MouseTrackListener ml = (MouseTrackListener) listener;
+ switch(type) {
+ case SWT.MouseEnter: ml.mouseEnter(me); break;
+ case SWT.MouseExit: ml.mouseExit(me); break;
+ case SWT.MouseHover: ml.mouseHover(me); break;
+ }
+ }
+ if(listener instanceof MouseMoveListener) {
+ MouseMoveListener ml = (MouseMoveListener) listener;
+ switch(type) {
+ case SWT.MouseMove: ml.mouseMove(me); break;
+ }
+ }
+ if(listener instanceof MouseWheelListener) {
+ MouseWheelListener ml = (MouseWheelListener) listener;
+ switch(type) {
+ case SWT.MouseWheel: ml.mouseScrolled(me); break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Marks the set of elements as selected.
+ * @param words must not be <code>null</code>.
+ */
+ public void setSelection(Set<Word> words) {
+ checkWidget();
+ Assert.isNotNull(words, "Selection must not be null!");
+ if(wordsToUse == null) return;
+ Set<Word> selection = new HashSet<Word>(words);
+ selection.retainAll(wordsToUse);
+ int w = textLayerImage.getBounds().width;
+ int h = textLayerImage.getBounds().height;
+ if(selectionLayerImage != null) {
+ selectionLayerImage.dispose();
+ }
+ selectionLayerImage = new Image(getDisplay(), w, h);
+ GC gc = new GC(selectionLayerImage);
+ gc.drawImage(textLayerImage, 0, 0);
+ for (Word word : selection) {
+ drawWord(gc, word, highlightColor);
+ }
+ if(!selection.equals(this.selection)) {
+ this.selection = selection;
+ fireSelectionChanged();
+ }
+ gc.dispose();
+ zoom(currentZoom);
+ redraw();
+ }
+
+ private void fireSelectionChanged() {
+ Event e = new Event();
+ e.widget = this;
+ final SelectionEvent event = new SelectionEvent(e);
+ event.data = getSelection();
+ event.widget = this;
+ event.display = Display.getCurrent();
+ for (SelectionListener listener : selectionListeners) {
+ listener.widgetSelected(event);
+ }
+ }
+
+ public void redrawTextLayerImage() {
+ if(wordsToUse == null) return;
+ GC gc = new GC(textLayerImage);
+ gc.setBackground(getBackground());
+ gc.fillRectangle(0, 0, textLayerImage.getBounds().width, textLayerImage.getBounds().height);
+ for (Word word : wordsToUse) {
+ drawWord(gc, word, word.getColor());
+ }
+ gc.dispose();
+ setSelection(getSelection());
+ }
+
+ /**
+ * Returns the set of selected elements.
+ * Never returns <code>null</code>.
+ * @return
+ */
+ public Set<Word> getSelection() {
+ checkWidget();
+ Set<Word> copy = new HashSet<Word>(selection);
+ return copy;
+ }
+
+ /**
+ * Sets the highlight color of the cloud. Default color
+ * is red.
+ * @param color
+ */
+ public void setSelectionColor(Color color) {
+ checkWidget();
+ Assert.isLegal(color != null, "Color must not be null!");
+ this.highlightColor = color;
+ }
+
+ @Override
+ public void setBackground(Color color) {
+ checkWidget();
+ Assert.isLegal(color != null, "Color must not be null!");
+ super.setBackground(color);
+ }
+
+ /**
+ * Does a full relayout of all displayed elements.
+ * @param monitor
+ * @return
+ */
+ public int layoutCloud(IProgressMonitor monitor, boolean recalc) {
+ checkWidget();
+ resetLayout();
+ if(selectionLayerImage != null) {
+ selectionLayerImage.dispose();
+ selectionLayerImage = null;
+ }
+ regionOffset = new Point(0, 0);
+ if(textLayerImage != null) textLayerImage.dispose();
+ int placedWords = 0;
+ try {
+ if(recalc) {
+ calcExtents(monitor);
+ }
+ placedWords = layoutWords(wordsToUse, monitor);
+ } catch (Exception e) {
+ MessageDialog.openError(getShell(), "Exception while layouting data", "An exception occurred while layouting: " + e.getMessage());
+ e.printStackTrace();
+ }
+ zoomFit();
+ redraw();
+ updateScrollbars();
+ return placedWords;
+ }
+
+ private void updateScrollbars() {
+ if(zoomLayerImage == null) {
+ return;
+ }
+ Rectangle rect = zoomLayerImage.getBounds();
+ Rectangle client = getClientArea();
+ ScrollBar hBar = getHorizontalBar();
+ ScrollBar vBar = getVerticalBar();
+ if(hBar != null) {
+ hBar.setMaximum (rect.width);
+ hBar.setThumb (Math.min (rect.width, client.width));
+ int hPage = rect.width - client.width;
+ int hSelection = hBar.getSelection ();
+ if (hSelection >= hPage) {
+ if (hPage <= 0) hSelection = 0;
+ origin.x = -hSelection;
+ }
+ }
+ if(vBar != null) {
+ vBar.setMaximum (rect.height);
+ vBar.setThumb (Math.min (rect.height, client.height));
+ int vPage = rect.height - client.height;
+ int vSelection = vBar.getSelection ();
+ if (vSelection >= vPage) {
+ if (vPage <= 0) vSelection = 0;
+ origin.y = -vSelection;
+ }
+ }
+ }
+
+ /**
+ * Sets the maximum font size (which must be a value greater 0). Note that strings
+ * which are too large to fit into the cloud region will be skipped. By default, this
+ * value is 500.
+ * @param maxSize
+ */
+ public void setMaxFontSize(int maxSize) {
+ checkWidget();
+ Assert.isLegal(maxSize > 0, "Font Size must be greater than zero, but was " + maxSize + "!");
+ maxFontSize = maxSize;
+ }
+
+ /**
+ * Sets the opacity of the words, which must be a value between 0 and 255 (inclusive).
+ * Currently not very useful...
+ * @param opacity
+ */
+ public void setOpacity(int opacity) {
+ checkWidget();
+ Assert.isLegal(opacity > 0, "Opacity must be greater than zero: " + opacity);
+ Assert.isLegal(opacity < 256, "Opacity must be less than 256: " + opacity);
+ this.opacity = opacity;
+ }
+
+ /**
+ * Sets the minimum font size. Should be a reasonable value > 0 (twice of {@link TagCloud#RESOLUTION}
+ * is recommended). By default, this value is 12.
+ * @param size
+ */
+ public void setMinFontSize(int size) {
+ checkWidget();
+ Assert.isLegal(size > 0, "Font Size must be greater zero: " + size);
+ this.minFontSize = size;
+ }
+
+ /**
+ * Returns the {@link ImageData} of the text layer image (all rendered elements, unscaled,
+ * without highlighted selection). Can be used to print or export the cloud.
+ * @return
+ */
+ public ImageData getImageData() {
+ checkWidget();
+ if(textLayerImage == null) return null;
+ return textLayerImage.getImageData();
+ }
+
+ /**
+ * Enable boosting for the first <code>boost</code>
+ * elements. By default, no elements are boosted.
+ * @param boost
+ */
+ public void setBoost(int boost) {
+ checkWidget();
+ Assert.isLegal(boost >= 0, "Boost cannot be negative");
+ this.boost = boost;
+ }
+
+ /**
+ * Enable or disable antialiasing. Enabled by default.
+ * @param enabled
+ */
+ public void setAntiAlias(boolean enabled) {
+ checkWidget();
+ if(enabled) {
+ antialias = SWT.ON;
+ } else {
+ antialias = SWT.OFF;
+ }
+ }
+
+// /**
+// * Work in progress - still broken positioning
+// * @param w
+// * @throws IOException
+// */
+// public void toSVG(Writer w) throws IOException {
+// int counter = 1;
+// w.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" +
+// "<!-- Created with Eclipse Tag Cloud -->\n" +
+// "<svg\n" +
+// "xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n" +
+// "xmlns:cc=\"http://creativecommons.org/ns#\"\n" +
+// "xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n" +
+// "xmlns:svg=\"http://www.w3.org/2000/svg\"\n" +
+// "xmlns=\"http://www.w3.org/2000/svg\"\n" +
+// "version=\"1.1\"\n" +
+// "width=\"" + textLayerImage.getBounds().width + "\"\n" +
+// "height=\"" + textLayerImage.getBounds().height + "\"\n" +
+// "id=\"svg2\">\n" +
+// "<defs\n" +
+// "id=\"defs4\" />\n" +
+// "<metadata\n" +
+// "id=\"metadata7\">\n" +
+// "<rdf:RDF>\n" +
+// "<cc:Work\n" +
+// "rdf:about=\"\">\n" +
+// "<dc:format>image/svg+xml</dc:format>\n" +
+// "<dc:type\n" +
+// "rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" />\n" +
+// "<dc:title></dc:title>\n" +
+// "</cc:Work>\n" +
+// "</rdf:RDF>\n" +
+// "</metadata>\n" +
+// "<g\n" +
+// "id=\"layer1\">\n");
+// GC tmp = new GC(Display.getDefault());
+// String bg = Integer.toHexString(getBackground().getRed()) + Integer.toHexString(getBackground().getGreen()) + Integer.toHexString(getBackground().getBlue());
+// w.append("<rect x=\"" + 0 + "\" y=\"" + 0 + "\" width=\"" + textLayerImage.getBounds().width + "\" height=\"" + textLayerImage.getBounds().height + "\" style=\"fill:" + bg + ";stroke:#006600;\"/>");
+// for (Word word : wordsToUse) {
+// String id = "text" + counter++;
+// FontData fd = word.fontData[0];
+// String style = "font-size:" + fd.getHeight()+"px;" +
+// "font-family:" + fd.getName() + ";";
+// String text = word.string;
+// int x = 0;
+// int y = 0;
+// double radian = Math.toRadians(word.angle);
+// final double sin = Math.abs(Math.sin(radian));
+// final double cos = Math.abs(Math.cos(radian));
+// float fontSize = getFontSize(word);
+// Font font = new Font(tmp.getDevice(), word.fontData);
+// Path p = new Path(tmp.getDevice());
+// p.addString(word.string, 0, 0, font);
+// float[] bounds = new float[4];
+// p.getBounds(bounds);
+// p.dispose();
+// gc.setFont(font);
+// //Point stringExtent = gc.stringExtent(word.string);
+// font.dispose();
+// if(word.angle < 0) {
+// y = word.height - (int) ( cos * fontSize);
+// } else {
+// x = (int) (sin * fontSize);
+// }
+// x += word.x - regionOffset.x;
+// y += word.y - regionOffset.y;
+//
+//// w.append("<rect x=\"" + 0 + "\" y=\"" + 0 + "\" width=\"" + stringExtent.x + "\" height=\"" + stringExtent.y + "\" style=\"fill:none;stroke:#006600;\"" +
+//// " transform=\"translate(" + x + "," + y + ") rotate(" + word.angle + ")\"/>");
+//
+// int xOff = (int) (-bounds[0] + bounds[2]/2);
+// int yOff = (int)(bounds[3] - bounds[1]);
+// String color = Integer.toHexString(word.color.getRed()) + Integer.toHexString(word.color.getGreen()) + Integer.toHexString(word.color.getBlue());
+// String fullString = "\n<text "
+// + "x=\"" + xOff + "\"\n"
+// + "y=\"" + yOff + "\"\n"
+// + "text-anchor=\"middle\"\n"
+// + "transform = \"translate(" + x + "," + y + ") rotate(" + word.angle+")\"\n"
+// + "id=\"" + id + "\"\n"
+// + "xml:space=\"preserve\"\n"
+// + "style=\"font-size:40px;fill:#" + color + ";fill-opacity:1;stroke:none;font-family:Sans\">\n"
+// + "<tspan "
+// + "style=\""+style+"\">"
+// + text + "</tspan>\n"
+// +"</text>\n";
+//
+// w.append(fullString);
+// }
+// tmp.dispose();
+// w.append("</g>\n</svg>\n");
+// }
+
+ public void setBoostFactor(float boostFactor) {
+ Assert.isLegal(boostFactor != 0);
+ this.boostFactor = boostFactor;
+ }
+
+ public Color getSelectionColor() {
+ return highlightColor;
+ }
+
+ public void setLayouter(ILayouter layouter) {
+ checkWidget();
+ Assert.isLegal(layouter != null, "Layouter must not be null!");
+ this.layouter = layouter;
+ }
+
+ public int getMaxFontSize() {
+ checkWidget();
+ return maxFontSize;
+ }
+
+ public int getMinFontSize() {
+ checkWidget();
+ return minFontSize;
+ }
+
+ public int getBoost() {
+ checkWidget();
+ return boost;
+ }
+
+ public float getBoostFactor() {
+ checkWidget();
+ return boostFactor;
+ }
+
+ public List<Word> getWords() {
+ return wordsToUse;
+ }
+
+ public ILayouter getLayouter() {
+ return layouter;
+ }
+
+
+}
diff --git a/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/TagCloudViewer.java b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/TagCloudViewer.java
new file mode 100644
index 0000000..009dbbb
--- /dev/null
+++ b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/TagCloudViewer.java
@@ -0,0 +1,375 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.cloudio;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.viewers.ContentViewer;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.events.MouseWheelListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.zest.cloudio.layout.ILayouter;
+
+
+/**
+ * A model-based adapter for a {@link TagCloud}.
+ * @author sschwieb
+ */
+public class TagCloudViewer extends ContentViewer {
+
+ private TagCloud cloud;
+
+ private Set<Word> selection = new HashSet<Word>();
+
+ private Map<Object, Word> objectMap = new HashMap<Object, Word>();
+
+ private int maxWords = 300;
+
+ private IProgressMonitor monitor;
+
+ /**
+ * Create a new TagCloudViewer for the given {@link TagCloud},
+ * which must not be <code>null</code>.
+ * @param cloud
+ */
+ public TagCloudViewer(TagCloud cloud) {
+ Assert.isLegal(cloud != null, "TagCloud must not be null!");
+ Assert.isLegal(!cloud.isDisposed(), "TagCloud must not be disposed!");
+ this.cloud = cloud;
+ initListeners();
+ }
+ /**
+ * Initialize the default tag cloud listeners.
+ * Can be overridden to modify the behaviour of the viewer.
+ */
+ protected void initListeners() {
+ initSelectionListener();
+ initMouseWheelListener();
+ initToolTipSupport();
+ }
+
+ /**
+ * Initialize tool tip support when the cursor
+ * hovers a word.
+ */
+ protected void initToolTipSupport() {
+ cloud.addMouseTrackListener(new MouseTrackListener() {
+
+ @Override
+ public void mouseHover(MouseEvent e) {}
+
+ @Override
+ public void mouseExit(MouseEvent e) {
+ cloud.setToolTipText(null);
+ }
+
+ @Override
+ public void mouseEnter(MouseEvent e) {
+ Word word = (Word) e.data;
+ ICloudLabelProvider labelProvider = (ICloudLabelProvider) getLabelProvider();
+ cloud.setToolTipText(labelProvider.getToolTip(word.data));
+ }
+ });
+ }
+
+ /**
+ * Initialize the mouse wheel listener to support
+ * zooming in and out.
+ */
+ protected void initMouseWheelListener() {
+ cloud.addMouseWheelListener(new MouseWheelListener() {
+
+ @Override
+ public void mouseScrolled(MouseEvent e) {
+ if(e.count > 0) {
+ cloud.zoomIn();
+ } else {
+ cloud.zoomOut();
+ }
+
+ }
+ });
+ }
+
+ /**
+ * Initialize default selection behaviour: Words can
+ * be selected by mouse click, and selection listeners
+ * are notified when the selection changed.
+ */
+ protected void initSelectionListener() {
+ cloud.addMouseListener(new MouseListener() {
+
+ @Override
+ public void mouseUp(MouseEvent e) {
+ Word word = (Word) e.data;
+ if(word == null) return;
+ boolean remove = selection.remove(word);
+ if(!remove) selection.add(word);
+ cloud.setSelection(selection);
+ }
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+
+ }
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ }
+ });
+ cloud.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ List<Object> data = new ArrayList<Object>();
+ Set<Word> selected = (Set<Word>) e.data;
+ for (Word word : selected) {
+ if(word.data != null) {
+ data.add(word.data);
+ }
+ }
+ StructuredSelection selection = new StructuredSelection(data);
+ fireSelectionChanged(new SelectionChangedEvent(TagCloudViewer.this, selection));
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.Viewer#getControl()
+ */
+ @Override
+ public Control getControl() {
+ return getCloud();
+ }
+
+ /**
+ * Returns the currently selected elements, as an
+ * {@link IStructuredSelection}. Returns an empty
+ * selection if no elements are selected.
+ */
+ @Override
+ public ISelection getSelection() {
+ List<Object> elements = new ArrayList<Object>();
+ for (Word word : selection) {
+ elements.add(word.data);
+ }
+ return new StructuredSelection(elements);
+ }
+
+ @Override
+ public void refresh() {
+ // TODO Auto-generated method stub
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection, boolean)
+ */
+ @Override
+ public void setSelection(ISelection selection, boolean reveal) {
+ this.selection.clear();
+ IStructuredSelection sel = (IStructuredSelection) selection;
+ Iterator<?> iterator = sel.iterator();
+ while(iterator.hasNext()) {
+ Object next = iterator.next();
+ Word word = objectMap.get(next);
+ if(word != null) {
+ this.selection.add(word);
+ }
+ }
+ cloud.setSelection(this.selection);
+ }
+
+ /**
+ * Resets the {@link TagCloud}. If <code>recalc</code> is
+ * <code>true</code>, the displayed elements will be updated
+ * with the values provided by used {@link ICloudLabelProvider}.
+ * Otherwise, the cloud will only be re-layouted, keeping fonts,
+ * colors and angles untouched.
+ * @param monitor
+ * @param recalc
+ */
+ public void reset(IProgressMonitor monitor, boolean recalc) {
+ cloud.layoutCloud(monitor, recalc);
+ }
+
+ /**
+ * Returns the {@link TagCloud} managed by this viewer.
+ * @return
+ */
+ public TagCloud getCloud() {
+ return cloud;
+ }
+
+ /**
+ * Sets the label provider of this viewer, which must be an
+ * {@link ICloudLabelProvider}.
+ */
+ @Override
+ public void setLabelProvider(IBaseLabelProvider labelProvider) {
+ super.setLabelProvider(labelProvider);
+ Assert.isLegal(labelProvider instanceof ICloudLabelProvider);
+ }
+
+ /**
+ * Sets the content provider of this viewer, which must be
+ * an {@link IStructuredContentProvider}.
+ */
+ @Override
+ public void setContentProvider(IContentProvider contentProvider) {
+ Assert.isLegal(contentProvider instanceof IStructuredContentProvider);
+ super.setContentProvider(contentProvider);
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object, java.lang.Object)
+ */
+ @Override
+ protected void inputChanged(Object input, Object oldInput) {
+ selection.clear();
+ objectMap.clear();
+ IStructuredContentProvider contentProvider = (IStructuredContentProvider) getContentProvider();
+ Object[] elements = contentProvider.getElements(input);
+ List<Word> words = new ArrayList<Word>();
+ ICloudLabelProvider labelProvider = (ICloudLabelProvider) getLabelProvider();
+ short i = 0;
+ for (Object element : elements) {
+ Word word = new Word(labelProvider.getLabel(element));
+ word.setColor(labelProvider.getColor(element));
+ word.weight = labelProvider.getWeight(element);
+ word.setFontData(labelProvider.getFontData(element));
+ word.angle = labelProvider.getAngle(element);
+ word.data = element;
+ Assert.isLegal(word.string != null, "Labelprovider must return a String for each element");
+ Assert.isLegal(word.getColor() != null, "Labelprovider must return a Color for each element");
+ Assert.isLegal(word.getFontData() != null, "Labelprovider must return a FontData for each element");
+ Assert.isLegal(word.weight >= 0, "Labelprovider must return a weight between 0 and 1 (inclusive), but value was " + word.weight);
+ Assert.isLegal(word.weight <= 1, "Labelprovider must return a weight between 0 and 1 (inclusive), but value was " + word.weight);
+ Assert.isLegal(word.angle >= -90, "Angle of an element must be between -90 and +90 (inclusive), but was " + word.angle);
+ Assert.isLegal(word.angle <= 90, "Angle of an element must be between -90 and +90 (inclusive), but was " + word.angle);
+ words.add(word);
+ i++;
+ word.id = i;
+ objectMap.put(element, word);
+ if(i == maxWords) break;
+ }
+ selection.clear();
+ if(monitor != null) {
+ monitor.subTask("Layouting...");
+ }
+ cloud.setWords(words, monitor);
+ }
+
+ /**
+ * Sets the maximum number of elements which will be
+ * displayed by the cloud. Note that there is no guarantee
+ * that this amount of elements will actually be displayed,
+ * as this depends on additional factors.
+ * @return
+ */
+ public void setMaxWords(int words) {
+ this.maxWords = words;
+ }
+
+ /**
+ * Calls {@link TagCloud#zoomFit()} to scale the cloud such
+ * that it fits the current visible area.
+ */
+ public void zoomFit() {
+ cloud.zoomFit();
+ }
+
+ /**
+ * Zooms in
+ */
+ public void zoomIn() {
+ cloud.zoomIn();
+ }
+
+ /**
+ * Zooms out
+ */
+ public void zoomOut() {
+ cloud.zoomOut();
+ }
+
+ /**
+ * Resets the zoom to 100%
+ */
+ public void zoomReset() {
+ cloud.zoomReset();
+ }
+
+ public void setBoost(int boost) {
+ cloud.setBoost(boost);
+ }
+
+ /**
+ * Returns the maximum number of elements which will be
+ * displayed by the cloud. Note that there is no guarantee
+ * that this amount of elements will actually be displayed,
+ * as this depends on additional factors.
+ * @return
+ */
+ public int getMaxWords() {
+ return maxWords;
+ }
+
+ /**
+ * Same as {@link TagCloudViewer#setInput(Object)}, but with
+ * an {@link IProgressMonitor} to provide feedback during the
+ * layout phase.
+ * @param input
+ * @param progressMonitor
+ */
+ public void setInput(Object input, IProgressMonitor progressMonitor) {
+ this.monitor = progressMonitor;
+ super.setInput(input);
+ this.monitor = null;
+ }
+
+ public void setBoostFactor(float boostFactor) {
+ cloud.setBoostFactor(boostFactor);
+ }
+
+ public void setLayouter(ILayouter layouter) {
+ cloud.setLayouter(layouter);
+ }
+
+ public ILayouter getLayouter() {
+ return cloud.getLayouter();
+ }
+
+}
diff --git a/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/Word.java b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/Word.java
new file mode 100644
index 0000000..efc837b
--- /dev/null
+++ b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/Word.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.cloudio;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.zest.cloudio.util.RectTree;
+
+/**
+ * Helper class which stores all data
+ * required to render an element.
+ * @author sschwieb
+ *
+ */
+public class Word {
+
+ public Word(String string) {
+ this.string = string;
+ }
+
+ public final String string;
+
+ public double weight;
+
+ public int x;
+
+ public int y;
+
+ private Color color;
+
+ public RectTree tree;
+
+ public float angle;
+
+ private FontData[] fontData;
+
+ public FontData[] getFontData() {
+ return fontData;
+ }
+
+ public void setFontData(FontData[] fontData) {
+ Assert.isLegal(fontData != null, "FontData-Array must not be null!");
+ this.fontData = fontData.clone();
+ }
+
+ public short id;
+
+ public int height;
+
+ public int width;
+
+ public Object data;
+
+ public Point stringExtent;
+
+ @Override
+ public String toString() {
+ return string;
+ }
+
+ public void setColor(Color color) {
+ Assert.isLegal(color != null, "Color must not be null!");
+ Assert.isLegal(!color.isDisposed(), "Color is disposed!");
+ this.color = color;
+ }
+
+ public Color getColor() {
+ Assert.isLegal(color != null, "Color must not be null!");
+ Assert.isLegal(!color.isDisposed(), "Color is disposed!");
+ return color;
+ }
+
+}
diff --git a/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/layout/DefaultLayouter.java b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/layout/DefaultLayouter.java
new file mode 100644
index 0000000..17cddf3
--- /dev/null
+++ b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/layout/DefaultLayouter.java
@@ -0,0 +1,147 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.cloudio.layout;
+
+import java.util.Random;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.zest.cloudio.Word;
+import org.eclipse.zest.cloudio.util.RectTree;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class DefaultLayouter implements ILayouter {
+
+ public static final String X_AXIS_VARIATION = "xaxis";
+
+
+ public static final String Y_AXIS_VARIATION = "yaxis";
+
+
+ private Random random = new Random();
+
+
+ /**
+ * Percentage of x axis variation. By default, searching for free space
+ * is started in the center of the available area. By increasing this
+ * value, the start point is moved on the x axis.
+ *
+ */
+ private int xAxisVariation;
+
+ /**
+ * Percentage of x axis variation. By default, searching for free space
+ * is started in the center of the available area. By increasing this
+ * value, the start point is moved on the y axis.
+ *
+ */
+ private int yAxisVariation;
+
+
+ public DefaultLayouter(int i, int j) {
+ this.xAxisVariation = i;
+ this.yAxisVariation = j;
+ }
+
+ public Point getInitialOffset(Word word, Rectangle cloudArea) {
+ int xOff = 0;
+ if(xAxisVariation > 0) {
+ int range = (cloudArea.width-word.width)/200*xAxisVariation;
+ if(range > 0) {
+ xOff = random.nextInt(range);
+ if(random.nextBoolean()) {
+ xOff = -xOff;
+ }
+ }
+ }
+ int yOff = 0;
+ if(yAxisVariation > 0) {
+ int range = cloudArea.height/200*yAxisVariation;
+ if(range > 0) {
+ yOff = random.nextInt(range);
+ if(random.nextBoolean()) yOff = -yOff;
+ }
+ }
+ return new Point(xOff, yOff);
+ }
+
+ /**
+ * Tries to position the given word in the given area. First a start point is chosen,
+ * then the {@link RectTree} of the word and the main area is used to detect whether
+ * the word can be placed at the given position, or not. If not, the current point
+ * is moved slightly in a spiral manner, similar to the approach of Wordle.
+ * @param word
+ * @param cloudArea
+ * @return
+ */
+ public boolean layout(Point offset, final Word word, final Rectangle cloudArea, short[][] mainTree) {
+ Assert.isLegal(word != null, "Word cannot be null!");
+ Point next = new Point(-word.width/2, -word.height/2);
+ next.x += random.nextInt(25);
+ next.y += random.nextInt(25);
+ double growFactor = 1.6;
+ offset.x += cloudArea.width/2;
+ offset.y += cloudArea.height/2;
+ for(int i = 0; i < 5000; i++) {
+ final double radius = Math.sqrt((double) (next.x * next.x + next.y * next.y)) + growFactor;
+ double atan = Math.atan2(next.y, next.x);
+ if(growFactor > 1.1) {
+ growFactor -= 0.0007;
+ }
+ if(radius < 80) {
+ atan += 0.7;
+ } else {
+ atan += 20 / radius;
+ }
+ if(growFactor < 0.0005) {
+ growFactor = 0.0005;
+ }
+ next.x = (int) (radius * Math.cos(atan));
+ next.y = (int) (radius * Math.sin(atan));
+ word.x = next.x + offset.x;
+ word.y = next.y + offset.y;
+ RectTree rt = word.tree;
+ if(rt == null) break;
+ rt.move(word.x, word.y);
+ if(cloudArea.x <= word.x && cloudArea.y <= word.y && cloudArea.x+cloudArea.width >= word.x + word.width && cloudArea.y + cloudArea.height >= word.y + word.height) {
+ if(rt.fits(mainTree)) {
+ rt.place(mainTree, word.id);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+
+ public void setOption(String optionName, Object object) {
+ if(X_AXIS_VARIATION.equals(optionName)) {
+ Integer value = (Integer) object;
+ Assert.isLegal(value >= 0, "Parameter must be between 0 and 100 (inclusive): " + value);
+ Assert.isLegal(value <= 100, "Parameter must be between 0 and 100 (inclusive): " + value);
+ this.xAxisVariation = value;
+ return;
+ }
+ if(Y_AXIS_VARIATION.equals(optionName)) {
+ Integer value = (Integer) object;
+ Assert.isLegal(value >= 0, "Parameter must be between 0 and 100 (inclusive): " + value);
+ Assert.isLegal(value <= 100, "Parameter must be between 0 and 100 (inclusive): " + value);
+ this.yAxisVariation = value;
+ return;
+ }
+ System.err.println("Unrecognized option: " + optionName);
+ }
+
+
+}
diff --git a/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/layout/ILayouter.java b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/layout/ILayouter.java
new file mode 100644
index 0000000..79a147b
--- /dev/null
+++ b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/layout/ILayouter.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.cloudio.layout;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.zest.cloudio.Word;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public interface ILayouter {
+
+ /**
+ * Places the given word within the defined rectangle, starting
+ * at the initial position.
+ * @param offset
+ * @param word
+ * @param cloudArea
+ * @param mainTree
+ * @return
+ */
+ public boolean layout(Point initial, final Word word, final Rectangle cloudArea, short[][] mainTree);
+
+ /**
+ * Calculates the initial offset of the given word, within the bounds
+ * of the specified rectangle. The layout algorithm will try to find
+ * a matching position around the initial offset.
+ * @param word
+ * @param cloudArea
+ * @return
+ */
+ public Point getInitialOffset(Word word, Rectangle cloudArea);
+
+ /**
+ * Set Layouter-specific options. See {@link DefaultLayouter}
+ * as an example.
+ * @param optionName
+ * @param object
+ */
+ public void setOption(String optionName, Object object);
+}
diff --git a/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/util/RectTree.java b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/util/RectTree.java
new file mode 100644
index 0000000..fe19e67
--- /dev/null
+++ b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/util/RectTree.java
@@ -0,0 +1,225 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.cloudio.util;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Stack;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+
+/**
+ * A two-dimensional tree structure to store non-overlapping
+ * rectangles.
+ * @author sschwieb
+ *
+ */
+public class RectTree {
+
+ private final int minResolution;
+
+ private short xOffset, yOffset;
+
+ private final RectNode root;
+
+ private LinkedList<RectNode> leaves;
+
+ private static short EMPTY = -1, MISC = 0;
+
+ public Rectangle minBounds = new Rectangle(Short.MAX_VALUE, Short.MAX_VALUE, Short.MIN_VALUE, Short.MIN_VALUE);
+
+ private Stack<RectNode> lastPath = new Stack<RectNode>();
+
+ class RectNode {
+
+ final SmallRect rect;
+
+ private RectNode[] children;
+
+ private final SmallRect[] childAreas;
+
+ short filled = EMPTY;
+
+ public RectNode(SmallRect rect) {
+ this.rect = rect;
+ final int width = rect.width/2;
+ final int height = rect.height/2;
+ if(rect.width > minResolution) {
+ this.childAreas = new SmallRect[4];
+ // top left
+ childAreas[0] = new SmallRect(rect.x, rect.y, width, height);
+ // top right
+ childAreas[1] = new SmallRect(rect.x+width, rect.y, width, height);
+ // bottom left
+ childAreas[2] = new SmallRect(rect.x, rect.y+height, width, height);
+ // bottom right
+ childAreas[3] = new SmallRect(rect.x+width, rect.y+height, width, height);
+ } else {
+ this.childAreas = null;
+ }
+ }
+
+ private int getChildIndex(SmallRect r) {
+ int index = 0;
+ if(r.y>= childAreas[3].y) {
+ if(r.x >= childAreas[3].x) {
+ index = 3;
+ } else {
+ index = 2;
+ }
+ } else {
+ if(r.x >= childAreas[1].x) {
+ index = 1;
+ }
+ }
+ return index;
+ }
+
+ public void insert(SmallRect r, short id) {
+ if(rect.width == minResolution) {
+ filled = id;
+ return;
+ }
+ int i = getChildIndex(r);
+ lastPath.push(this);
+ if(children == null) {
+ children = new RectNode[4];
+ }
+ if(children[i] == null) {
+ children[i] = new RectNode(childAreas[i]);
+ }
+ if(children[i].rect.width >= minResolution && children[i].rect.height >= minResolution) {
+ children[i].insert(r, id);
+ if(children[i].rect.width == minResolution) {
+ children[i].filled = id;
+ SmallRect c = children[i].rect;
+ if(c.x < minBounds.x) minBounds.x = c.x;
+ if(c.y < minBounds.y) minBounds.y = c.y;
+ if(c.x > minBounds.width) minBounds.width = c.x;
+ if(c.y > minBounds.height) minBounds.height = c.y;
+ }
+ }
+ boolean filled = true;
+ if(children != null) {
+ for(int ix = 0; ix < 4; ix++) {
+ if(children[ix] == null) {
+ filled = false;
+ break;
+ }
+ if(children[ix].filled == EMPTY) {
+ filled = false;
+ break;
+ }
+ }
+ }
+ if(filled) {
+ this.filled = MISC;
+ }
+ }
+
+ public boolean isAvailable(final SmallRect oRect) {
+ if(filled >= MISC) return false;
+ if(children == null) {
+ return filled == EMPTY;
+ }
+ final int i = getChildIndex(oRect);
+ if(children[i] == null) return true;
+ return children[i].isAvailable(oRect);
+ }
+
+
+ public short getWordId(Point position) {
+ if(filled > 0) return filled;
+ if(children == null) {
+ return filled;
+ }
+ for(int i = 0; i < childAreas.length; i++) {
+ if(childAreas[i].intersects(position.x-2, position.y-2, 4, 4) && children[i] != null) {
+ return children[i].getWordId(position);
+ }
+ }
+ return 0;
+ }
+
+ }
+
+ public RectTree(SmallRect root, int minResolution) {
+ this.minResolution = minResolution;
+ this.root = new RectNode(root);
+ }
+
+ public void insert(SmallRect r, short id) {
+ while(!lastPath.isEmpty()) {
+ RectNode node = lastPath.pop();
+ if(node.rect.intersects(r)) {
+ node.insert(r, id);
+ return;
+ }
+ }
+ root.insert(r, id);
+ }
+
+ public void move(int x, int y) {
+ this.xOffset = (short) x;
+ this.yOffset = (short) y;
+ }
+
+ public boolean fits(final short[][] mainTree) {
+ LinkedList<RectNode> leaves = getLeaves();
+ Iterator<RectNode> nodes = leaves.iterator();
+ while(nodes.hasNext()) {
+ RectNode node = nodes.next();
+ if(mainTree[(node.rect.x+xOffset)/minResolution][(node.rect.y+yOffset)/minResolution] != EMPTY) {
+ nodes.remove();
+ leaves.addFirst(node);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ LinkedList<RectNode> getLeaves() {
+ if(leaves == null) {
+ leaves = new LinkedList<RectNode>();
+ addLeaves(leaves, root);
+ }
+ return leaves;
+ }
+
+ private void addLeaves(List<RectNode> leaves, RectNode current) {
+ if(current.children == null) {
+ if(current.filled != EMPTY) {
+ leaves.add(current);
+ }
+ } else {
+ for(int i = 0; i < 4; i++) {
+ if(current.children[i] == null) continue;
+ addLeaves(leaves, current.children[i]);
+ }
+ }
+ }
+
+ public void place(final short[][] mainTree, short id) {
+ Collection<RectNode> leaves = getLeaves();
+ for (RectNode node : leaves) {
+ mainTree[(node.rect.x+xOffset)/minResolution][(node.rect.y+yOffset)/minResolution] = id;
+ }
+ }
+
+ public void releaseRects() {
+ getLeaves();
+ root.children = null;
+ }
+
+
+
+}
diff --git a/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/util/SmallRect.java b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/util/SmallRect.java
new file mode 100644
index 0000000..d11aa38
--- /dev/null
+++ b/org.eclipse.zest.cloudio/src/main/java/org/eclipse/zest/cloudio/util/SmallRect.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.cloudio.util;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Rectangle;
+
+/**
+ * A custom variation of a {@link Rectangle}, which stores
+ * the required values as short instead of int, thus saving
+ * some space.
+ *
+ * @author sschwieb
+ *
+ */
+public class SmallRect {
+
+ final short x, y, width, height;
+
+ public SmallRect(int x, int y, int width, int height) {
+ this.x = (short) x;
+ this.y = (short) y;
+ this.width = (short) width;
+ this.height = (short) height;
+ }
+
+ public boolean intersects(SmallRect rect) {
+ if (rect == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ return rect == this || intersects (rect.x, rect.y, rect.width, rect.height);
+ }
+
+ public boolean intersects (final int x, final int y, final int width, final int height) {
+ return (x < this.x + this.width) && (y < this.y + this.height) &&
+ (x + width > this.x) && (y + height > this.y);
+ }
+
+ public String toString () {
+ return "Rectangle {" + x + ", " + y + ", " + width + ", " + height + "}";
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio.build/.project b/org.eclipse.zest.examples.cloudio.build/.project
new file mode 100644
index 0000000..6fcd23a
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio.build/.project
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.zest.examples.cloudio.build</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ </buildSpec>
+ <natures>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.zest.examples.cloudio.build/pom.xml b/org.eclipse.zest.examples.cloudio.build/pom.xml
new file mode 100644
index 0000000..dcbe589
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio.build/pom.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.eclipse.zest.cloudio</groupId>
+ <version>2.0.0-SNAPSHOT</version>
+ <artifactId>parent</artifactId>
+ <packaging>pom</packaging>
+
+ <modules>
+ <module>../org.eclipse.zest.cloudio</module>
+ <module>../org.eclipse.zest.examples.cloudio</module>
+ <module>../org.eclipse.zest.examples.cloudio.feature</module>
+ <module>../org.eclipse.zest.examples.cloudio.rcp</module>
+
+ </modules>
+
+ <properties>
+ <tycho-version>0.12.0</tycho-version>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>helios</id>
+ <layout>p2</layout>
+ <url>http://download.eclipse.org/releases/helios</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
+
+ <pluginRepositories>
+ <pluginRepository>
+ <id>sonatype-repository</id>
+ <url>https://repository.sonatype.org/content/repositories/tycho-014/</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </pluginRepository>
+ </pluginRepositories>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.3.2</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-compiler-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-maven-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <extensions>true</extensions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>target-platform-configuration</artifactId>
+ <version>${tycho-version}</version>
+ <configuration>
+ <resolver>p2</resolver>
+ <target>
+ <artifact>
+ <groupId>org.eclipse</groupId>
+ <artifactId>org.eclipse.zest.cloudio</artifactId>
+ <version>2.0.0-SNAPSHOT</version>
+ <classifier>cloudio</classifier>
+ </artifact>
+ </target>
+ <ignoreTychoRepositories>false</ignoreTychoRepositories>
+ <environments>
+ <environment> <os>macosx</os> <ws>cocoa</ws> <arch>x86_64</arch> </environment>
+ <environment> <os>macosx</os> <ws>cocoa</ws> <arch>x86</arch> </environment>
+ <environment> <os>linux</os> <ws>gtk</ws> <arch>x86</arch> </environment>
+ <environment> <os>linux</os> <ws>gtk</ws> <arch>x86_64</arch> </environment>
+ <environment> <os>win32</os> <ws>win32</ws> <arch>x86</arch> </environment>
+ <environment> <os>win32</os> <ws>win32</ws> <arch>x86_64</arch> </environment>
+ </environments>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <!--
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.8</version>
+ <configuration>
+
+ </configuration>
+ </plugin>
+ </plugins>
+
+ </reporting>-->
+
+</project>
diff --git a/org.eclipse.zest.examples.cloudio.feature/.project b/org.eclipse.zest.examples.cloudio.feature/.project
new file mode 100644
index 0000000..88f0179
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.zest.examples.cloudio.feature</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.FeatureBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.FeatureNature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.zest.examples.cloudio.feature/build.properties b/org.eclipse.zest.examples.cloudio.feature/build.properties
new file mode 100644
index 0000000..64f93a9
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio.feature/build.properties
@@ -0,0 +1 @@
+bin.includes = feature.xml
diff --git a/org.eclipse.zest.examples.cloudio.feature/feature.xml b/org.eclipse.zest.examples.cloudio.feature/feature.xml
new file mode 100644
index 0000000..dc98d14
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio.feature/feature.xml
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+ id="org.eclipse.zest.examples.cloudio.feature"
+ label="Cloudio Application Feature"
+ version="2.0.0.qualifier">
+
+ <description>
+ Do not install this feature - it is required for the standalone
+RCP Application only.
+ </description>
+
+ <license url="http://www.example.com/license">
+ Eclipse Public License -v 1.0
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS
+ECLIPSE PUBLIC LICENSE (&quot;AGREEMENT&quot;). ANY USE, REPRODUCTION OR
+DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT&apos;S ACCEPTANCE
+OF THIS AGREEMENT.
+1. DEFINITIONS
+&quot;Contribution&quot; means:
+a) in the case of the initial Contributor, the initial code and
+documentation distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+i) changes to the Program, and
+ii) additions to the Program;
+where such changes and/or additions to the Program originate
+from and are distributed by that particular Contributor. A Contribution
+&apos;originates&apos; from a Contributor if it was added to the Program
+by such Contributor itself or anyone acting on such Contributor&apos;s
+behalf. Contributions do not include additions to the Program
+which: (i) are separate modules of software distributed in conjunction
+with the Program under their own license agreement, and (ii)
+are not derivative works of the Program.
+&quot;Contributor&quot; means any person or entity that distributes the
+Program.
+&quot;Licensed Patents &quot; mean patent claims licensable by a Contributor
+which are necessarily infringed by the use or sale of its Contribution
+alone or when combined with the Program.
+&quot;Program&quot; means the Contributions distributed in accordance with
+this Agreement.
+&quot;Recipient&quot; means anyone who receives the Program under this
+Agreement, including all Contributors.
+2. GRANT OF RIGHTS
+a) Subject to the terms of this Agreement, each Contributor hereby
+grants Recipient a non-exclusive, worldwide, royalty-free copyright
+license to reproduce, prepare derivative works of, publicly display,
+publicly perform, distribute and sublicense the Contribution
+of such Contributor, if any, and such derivative works, in source
+code and object code form.
+b) Subject to the terms of this Agreement, each Contributor hereby
+grants Recipient a non-exclusive, worldwide, royalty-free patent
+license under Licensed Patents to make, use, sell, offer to sell,
+import and otherwise transfer the Contribution of such Contributor,
+if any, in source code and object code form. This patent license
+shall apply to the combination of the Contribution and the Program
+if, at the time the Contribution is added by the Contributor,
+such addition of the Contribution causes such combination to
+be covered by the Licensed Patents. The patent license shall
+not apply to any other combinations which include the Contribution.
+No hardware per se is licensed hereunder.
+c) Recipient understands that although each Contributor grants
+the licenses to its Contributions set forth herein, no assurances
+are provided by any Contributor that the Program does not infringe
+the patent or other intellectual property rights of any other
+entity. Each Contributor disclaims any liability to Recipient
+for claims brought by any other entity based on infringement
+of intellectual property rights or otherwise. As a condition
+to exercising the rights and licenses granted hereunder, each
+Recipient hereby assumes sole responsibility to secure any other
+intellectual property rights needed, if any. For example, if
+a third party patent license is required to allow Recipient to
+distribute the Program, it is Recipient&apos;s responsibility to acquire
+that license before distributing the Program.
+d) Each Contributor represents that to its knowledge it has sufficient
+copyright rights in its Contribution, if any, to grant the copyright
+license set forth in this Agreement.
+3. REQUIREMENTS
+A Contributor may choose to distribute the Program in object
+code form under its own license agreement, provided that:
+a) it complies with the terms and conditions of this Agreement;
+and
+b) its license agreement:
+i) effectively disclaims on behalf of all Contributors all warranties
+and conditions, express and implied, including warranties or
+conditions of title and non-infringement, and implied warranties
+or conditions of merchantability and fitness for a particular
+purpose;
+ii) effectively excludes on behalf of all Contributors all liability
+for damages, including direct, indirect, special, incidental
+and consequential damages, such as lost profits;
+iii) states that any provisions which differ from this Agreement
+are offered by that Contributor alone and not by any other party;
+and
+iv) states that source code for the Program is available from
+such Contributor, and informs licensees how to obtain it in a
+reasonable manner on or through a medium customarily used for
+software exchange.
+When the Program is made available in source code form:
+a) it must be made available under this Agreement; and
+b) a copy of this Agreement must be included with each copy of
+the Program.
+Contributors may not remove or alter any copyright notices contained
+within the Program.
+Each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent
+Recipients to identify the originator of the Contribution.
+4. COMMERCIAL DISTRIBUTION
+Commercial distributors of software may accept certain responsibilities
+with respect to end users, business partners and the like. While
+this license is intended to facilitate the commercial use of
+the Program, the Contributor who includes the Program in a commercial
+product offering should do so in a manner which does not create
+potential liability for other Contributors. Therefore, if a Contributor
+includes the Program in a commercial product offering, such Contributor
+(&quot;Commercial Contributor&quot;) hereby agrees to defend and indemnify
+every other Contributor (&quot;Indemnified Contributor&quot;) against any
+losses, damages and costs (collectively &quot;Losses&quot;) arising from
+claims, lawsuits and other legal actions brought by a third party
+against the Indemnified Contributor to the extent caused by the
+acts or omissions of such Commercial Contributor in connection
+with its distribution of the Program in a commercial product
+offering. The obligations in this section do not apply to any
+claims or Losses relating to any actual or alleged intellectual
+property infringement. In order to qualify, an Indemnified Contributor
+must: a) promptly notify the Commercial Contributor in writing
+of such claim, and b) allow the Commercial Contributor to control,
+and cooperate with the Commercial Contributor in, the defense
+and any related settlement negotiations. The Indemnified Contributor
+may participate in any such claim at its own expense.
+For example, a Contributor might include the Program in a commercial
+product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Contributor&apos;s responsibility
+alone. Under this section, the Commercial Contributor would have
+to defend claims against the other Contributors related to those
+performance claims and warranties, and if a court requires any
+other Contributor to pay any damages as a result, the Commercial
+Contributor must pay those damages.
+5. NO WARRANTY
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM
+IS PROVIDED ON AN &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS
+OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION,
+ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and
+distributing the Program and assumes all risks associated with
+its exercise of rights under this Agreement , including but not
+limited to the risks and costs of program errors, compliance
+with applicable laws, damage to or loss of data, programs or
+equipment, and unavailability or interruption of operations.
+6. DISCLAIMER OF LIABILITY
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
+NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE
+OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGES.
+7. GENERAL
+If any provision of this Agreement is invalid or unenforceable
+under applicable law, it shall not affect the validity or enforceability
+of the remainder of the terms of this Agreement, and without
+further action by the parties hereto, such provision shall be
+reformed to the minimum extent necessary to make such provision
+valid and enforceable.
+If Recipient institutes patent litigation against any entity
+(including a cross-claim or counterclaim in a lawsuit) alleging
+that the Program itself (excluding combinations of the Program
+with other software or hardware) infringes such Recipient&apos;s patent(s),
+then such Recipient&apos;s rights granted under Section 2(b) shall
+terminate as of the date such litigation is filed.
+All Recipient&apos;s rights under this Agreement shall terminate if
+it fails to comply with any of the material terms or conditions
+of this Agreement and does not cure such failure in a reasonable
+period of time after becoming aware of such noncompliance. If
+all Recipient&apos;s rights under this Agreement terminate, Recipient
+agrees to cease use and distribution of the Program as soon as
+reasonably practicable. However, Recipient&apos;s obligations under
+this Agreement and any licenses granted by Recipient relating
+to the Program shall continue and survive.
+Everyone is permitted to copy and distribute copies of this Agreement,
+but in order to avoid inconsistency the Agreement is copyrighted
+and may only be modified in the following manner. The Agreement
+Steward reserves the right to publish new versions (including
+revisions) of this Agreement from time to time. No one other
+than the Agreement Steward has the right to modify this Agreement.
+The Eclipse Foundation is the initial Agreement Steward. The
+Eclipse Foundation may assign the responsibility to serve as
+the Agreement Steward to a suitable separate entity. Each new
+version of the Agreement will be given a distinguishing version
+number. The Program (including Contributions) may always be distributed
+subject to the version of the Agreement under which it was received.
+In addition, after a new version of the Agreement is published,
+Contributor may elect to distribute the Program (including its
+Contributions) under the new version. Except as expressly stated
+in Sections 2(a) and 2(b) above, Recipient receives no rights
+or licenses to the intellectual property of any Contributor under
+this Agreement, whether expressly, by implication, estoppel or
+otherwise. All rights in the Program not expressly granted under
+this Agreement are reserved.
+This Agreement is governed by the laws of the State of New York
+and the intellectual property laws of the United States of America.
+No party to this Agreement will bring a legal action under this
+Agreement more than one year after the cause of action arose.
+Each party waives its rights to a jury trial in any resulting
+litigation.
+ </license>
+
+ <plugin
+ id="org.eclipse.zest.examples.cloudio"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.zest.cloudio"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+</feature>
diff --git a/org.eclipse.zest.examples.cloudio.feature/pom.xml b/org.eclipse.zest.examples.cloudio.feature/pom.xml
new file mode 100644
index 0000000..98d0f1f
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio.feature/pom.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.eclipse.zest.cloudio</groupId>
+ <version>2.0.0-SNAPSHOT</version>
+ <artifactId>parent</artifactId>
+ <relativePath>../org.eclipse.zest.examples.cloudio.build</relativePath>
+ </parent>
+ <groupId>org.eclipse.zest.cloudio</groupId>
+ <artifactId>org.eclipse.zest.examples.cloudio.feature</artifactId>
+ <version>2.0.0-SNAPSHOT</version>
+ <packaging>eclipse-feature</packaging>
+</project> \ No newline at end of file
diff --git a/org.eclipse.zest.examples.cloudio.rcp/.project b/org.eclipse.zest.examples.cloudio.rcp/.project
new file mode 100644
index 0000000..dfe707b
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio.rcp/.project
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.zest.examples.cloudio.rcp</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ </buildSpec>
+ <natures>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.zest.examples.cloudio.rcp/org.eclipse.zest.examples.cloudio.rcp.product b/org.eclipse.zest.examples.cloudio.rcp/org.eclipse.zest.examples.cloudio.rcp.product
new file mode 100644
index 0000000..0201ba0
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio.rcp/org.eclipse.zest.examples.cloudio.rcp.product
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?pde version="3.5"?>
+
+<product name="Cloudio" uid="org.eclipse.zest.examples.cloudio.rcp.product" id="org.eclipse.zest.examples.cloudio.product" application="org.eclipse.zest.examples.cloudio.application" version="2.0.0.qualifier" useFeatures="true" includeLaunchers="true">
+
+ <aboutInfo>
+ <image path="eclipse_lg.gif"/>
+ <text>
+ %productBlurb
+ </text>
+ </aboutInfo>
+
+ <configIni use="default">
+ </configIni>
+
+ <launcherArgs>
+ <programArgs>-consoleLog</programArgs>
+ <vmArgs>-Xms40m -Xmx256m</vmArgs>
+ <vmArgsMac>-XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts -Xdock:icon=../Resources/Eclipse.icns -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts</vmArgsMac>
+ </launcherArgs>
+
+ <windowImages i16="eclipse.gif" i32="eclipse32.gif" i48="eclipse48.gif" i64="eclipse.png" i128="eclipse32.png"/>
+
+ <splash
+ startupProgressRect="0,280,455,15"
+ startupMessageRect="7,220,441,20"
+ startupForegroundColor="C8D5EA" />
+ <launcher>
+ <solaris/>
+ <win useIco="false">
+ <bmp/>
+ </win>
+ </launcher>
+
+ <vm>
+ <linux include="false">org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6</linux>
+ <macos include="false">org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6</macos>
+ <windows include="false">org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6</windows>
+ </vm>
+
+ <plugins>
+ </plugins>
+
+ <features>
+ <feature id="org.eclipse.zest.examples.cloudio.feature" version="2.0.0.qualifier"/>
+ <feature id="org.eclipse.rcp"/>
+ </features>
+
+ <configurations>
+ <plugin id="org.eclipse.core.runtime" autoStart="true" startLevel="0" />
+ <plugin id="org.eclipse.equinox.common" autoStart="true" startLevel="2" />
+ <plugin id="org.eclipse.equinox.ds" autoStart="true" startLevel="1" />
+ <plugin id="org.eclipse.osgi" autoStart="true" startLevel="-1" />
+ </configurations>
+
+</product>
diff --git a/org.eclipse.zest.examples.cloudio.rcp/plugin_customization.ini b/org.eclipse.zest.examples.cloudio.rcp/plugin_customization.ini
new file mode 100644
index 0000000..333fab9
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio.rcp/plugin_customization.ini
@@ -0,0 +1 @@
+org.eclipse.ui/SHOW_PROGRESS_ON_STARTUP = true
diff --git a/org.eclipse.zest.examples.cloudio.rcp/pom.xml b/org.eclipse.zest.examples.cloudio.rcp/pom.xml
new file mode 100644
index 0000000..08398f0
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio.rcp/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>parent</artifactId>
+ <groupId>org.eclipse.zest.cloudio</groupId>
+ <version>2.0.0-SNAPSHOT</version>
+ <relativePath>../org.eclipse.zest.examples.cloudio.build</relativePath>
+ </parent>
+ <groupId>org.eclipse.zest.cloudio</groupId>
+ <version>2.0.0-SNAPSHOT</version>
+ <artifactId>org.eclipse.zest.examples.cloudio.rcp</artifactId>
+ <packaging>eclipse-repository</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-p2-publisher-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <configuration>
+ <publishArtifacts>true</publishArtifacts>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-p2-director-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <executions>
+ <execution>
+ <id>materialize-products</id>
+ <goals>
+ <goal>materialize-products</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>archive-products</id>
+ <goals>
+ <goal>archive-products</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <products>
+ <product>
+ <id>org.eclipse.zest.examples.cloudio.rcp.product</id>
+ </product>
+ </products>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/org.eclipse.zest.examples.cloudio/.classpath b/org.eclipse.zest.examples.cloudio/.classpath
new file mode 100644
index 0000000..fbb14bd
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src/main/java"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.zest.examples.cloudio/.project b/org.eclipse.zest.examples.cloudio/.project
new file mode 100644
index 0000000..c3e2d8d
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.zest.examples.cloudio</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.zest.examples.cloudio/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.zest.examples.cloudio/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..b83af5d
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Fri Jun 03 11:03:42 CEST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/org.eclipse.zest.examples.cloudio/META-INF/MANIFEST.MF b/org.eclipse.zest.examples.cloudio/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..c28279c
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/META-INF/MANIFEST.MF
@@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Cloudio RCP Application
+Bundle-SymbolicName: org.eclipse.zest.examples.cloudio;singleton:=true
+Bundle-Version: 2.0.0.qualifier
+Bundle-Activator: org.eclipse.zest.examples.cloudio.application.ui.CloudioApplicationPlugin
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.ui.views,
+ org.eclipse.core.runtime;bundle-version="3.6.0",
+ org.eclipse.zest.cloudio;bundle-version="2.0.0"
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
diff --git a/org.eclipse.zest.examples.cloudio/build.properties b/org.eclipse.zest.examples.cloudio/build.properties
new file mode 100644
index 0000000..b774e9c
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/build.properties
@@ -0,0 +1,5 @@
+source.. = src/main/java
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml
diff --git a/org.eclipse.zest.examples.cloudio/plugin.xml b/org.eclipse.zest.examples.cloudio/plugin.xml
new file mode 100644
index 0000000..2bac2b8
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/plugin.xml
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension
+ point="org.eclipse.ui.views">
+ <view
+ class="org.eclipse.zest.examples.cloudio.application.ui.TagCloudViewPart"
+ id="org.eclipse.zest.cloudio.sample.tagcloud"
+ name="Tag Cloud View"
+ restorable="true">
+ </view>
+ </extension>
+ <extension
+ point="org.eclipse.ui.actionSets">
+ <actionSet
+ label="File Actions"
+ visible="true"
+ id="file.actions">
+ <action
+ label="Export Image"
+ class="org.eclipse.zest.examples.cloudio.application.actions.ExportImageAction"
+ tooltip="Export image"
+ menubarPath="file/additions"
+ id="org.eclipse.zest.cloudio.export">
+ </action>
+ <action
+ label="Load Stopwords"
+ class="org.eclipse.zest.examples.cloudio.application.actions.LoadStopWordsAction"
+ tooltip="Load stop word list"
+ menubarPath="file/additions"
+ id="org.eclipse.zest.cloudio.loadwords">
+ </action>
+ <action
+ label="Load File"
+ class="org.eclipse.zest.examples.cloudio.application.actions.LoadFileAction"
+ tooltip="Load a new File"
+ menubarPath="file/additions"
+ id="org.eclipse.zest.cloudio.loadfile">
+ </action>
+ </actionSet>
+ <actionSet
+ id="edit.actions"
+ label="Edit Actions"
+ visible="true">
+ <action
+ class="org.eclipse.zest.examples.cloudio.application.actions.ZoomInAction"
+ id="org.eclipse.zest.cloudio.zoomin"
+ label="Zoom In"
+ menubarPath="edit/zoom"
+ style="push">
+ </action>
+ <action
+ class="org.eclipse.zest.examples.cloudio.application.actions.ZoomOutAction"
+ id="org.eclipse.zest.cloudio.zoomout"
+ label="Zoom Out"
+ menubarPath="edit/zoom"
+ style="push">
+ </action>
+ <action
+ class="org.eclipse.zest.examples.cloudio.application.actions.ZoomResetAction"
+ id="org.eclipse.zest.cloudio.zoomreset"
+ label="100 %"
+ menubarPath="edit/zoom"
+ style="push">
+ </action>
+ <action
+ class="org.eclipse.zest.examples.cloudio.application.actions.ZoomFitAction"
+ id="org.eclipse.zest.cloudio.zoomfit"
+ label="Fit Window"
+ menubarPath="edit/zoom"
+ style="push">
+ </action>
+ <action
+ class="org.eclipse.zest.examples.cloudio.application.actions.DeselectAllAction"
+ id="org.eclipse.zest.cloudio.deselectall"
+ label="Clear Selection"
+ menubarPath="edit/select"
+ style="push">
+ </action>
+ <action
+ class="org.eclipse.zest.examples.cloudio.application.actions.SelectAllAction"
+ id="org.eclipse.zest.cloudio.selectall"
+ label="Select All"
+ menubarPath="edit/select"
+ style="push">
+ </action>
+ </actionSet>
+ </extension>
+
+<extension
+ id="application"
+ name="Tag Cloud"
+ point="org.eclipse.core.runtime.applications">
+ <application
+ cardinality="singleton-global"
+ thread="main"
+ visible="true">
+ <run
+ class="org.eclipse.zest.examples.cloudio.application.Application">
+ </run>
+ </application>
+ </extension>
+ <extension
+ point="org.eclipse.ui.perspectives">
+ <perspective
+ class="org.eclipse.zest.examples.cloudio.application.PerspectiveFactory"
+ id="org.eclipse.zest.cloudio.rcp.perspective"
+ name="cloudPerspective">
+ </perspective>
+ </extension>
+ <extension
+ id="product"
+ point="org.eclipse.core.runtime.products">
+ <product
+ application="org.eclipse.zest.examples.cloudio.application"
+ name="Cloudio">
+ <property
+ name="windowImages"
+ value="eclipse.gif,eclipse32.gif,eclipse48.gif,eclipse.png,eclipse32.png">
+ </property>
+ <property
+ name="aboutText"
+ value="%productBlurb">
+ </property>
+ <property
+ name="aboutImage"
+ value="eclipse_lg.gif">
+ </property>
+ <property
+ name="startupForegroundColor"
+ value="C8D5EA">
+ </property>
+ <property
+ name="startupProgressRect"
+ value="0,280,455,15">
+ </property>
+ <property
+ name="startupMessageRect"
+ value="7,220,441,20">
+ </property>
+ <property
+ name="preferenceCustomization"
+ value="plugin_customization.ini">
+ </property>
+ <property
+ name="appName"
+ value="Cloudio">
+ </property>
+ </product>
+ </extension>
+</plugin>
diff --git a/org.eclipse.zest.examples.cloudio/pom.xml b/org.eclipse.zest.examples.cloudio/pom.xml
new file mode 100644
index 0000000..27e8745
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/pom.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>parent</artifactId>
+ <groupId>org.eclipse.zest.cloudio</groupId>
+ <version>2.0.0-SNAPSHOT</version>
+ <relativePath>../org.eclipse.zest.examples.cloudio.build</relativePath>
+ </parent>
+ <groupId>org.eclipse.zest.cloudio</groupId>
+ <artifactId>org.eclipse.zest.examples.cloudio</artifactId>
+ <version>2.0.0-SNAPSHOT</version>
+ <packaging>eclipse-plugin</packaging>
+</project>
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/Application.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/Application.java
new file mode 100644
index 0000000..be1bd04
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/Application.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application;
+
+import org.eclipse.equinox.app.IApplication;
+import org.eclipse.equinox.app.IApplicationContext;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class Application implements IApplication {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app.IApplicationContext)
+ */
+ public Object start(IApplicationContext context) {
+ Display display = PlatformUI.createDisplay();
+ try {
+ int returnCode = PlatformUI.createAndRunWorkbench(display, new ApplicationWorkbenchAdvisor());
+ if (returnCode == PlatformUI.RETURN_RESTART) {
+ return IApplication.EXIT_RESTART;
+ }
+ return IApplication.EXIT_OK;
+ } finally {
+ display.dispose();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.equinox.app.IApplication#stop()
+ */
+ public void stop() {
+ final IWorkbench workbench = PlatformUI.getWorkbench();
+ if (workbench == null)
+ return;
+ final Display display = workbench.getDisplay();
+ display.syncExec(new Runnable() {
+ public void run() {
+ if (!display.isDisposed())
+ workbench.close();
+ }
+ });
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ApplicationActionBarAdvisor.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ApplicationActionBarAdvisor.java
new file mode 100644
index 0000000..7786280
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ApplicationActionBarAdvisor.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application;
+
+import org.eclipse.jface.action.ActionContributionItem;
+import org.eclipse.jface.action.GroupMarker;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
+import org.eclipse.ui.application.ActionBarAdvisor;
+import org.eclipse.ui.application.IActionBarConfigurer;
+import org.eclipse.zest.examples.cloudio.application.actions.AboutAction;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class ApplicationActionBarAdvisor extends ActionBarAdvisor {
+
+ private IWorkbenchAction exitAction;
+ private IWorkbenchAction aboutAction;
+
+
+ public ApplicationActionBarAdvisor(IActionBarConfigurer configurer) {
+ super(configurer);
+ }
+
+ protected void makeActions(final IWorkbenchWindow window) {
+ exitAction = ActionFactory.QUIT.create(window);
+ AboutAction about = new AboutAction();
+ register(about);
+ register(exitAction);
+ this.aboutAction = about;
+
+ }
+
+
+ @Override
+ protected void fillMenuBar(IMenuManager menuBar) {
+ MenuManager fileMenu = new MenuManager("&File", IWorkbenchActionConstants.M_FILE);
+ menuBar.add(fileMenu);
+ ActionContributionItem aboutActionItem = new ActionContributionItem(aboutAction);
+ fileMenu.add(aboutActionItem);
+ fileMenu.add(new Separator());
+ fileMenu.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
+ fileMenu.add(new Separator());
+ MenuManager editMenu = new MenuManager("&Edit", IWorkbenchActionConstants.M_EDIT);
+ editMenu.add(new GroupMarker("select"));
+ editMenu.add(new Separator());
+ editMenu.add(new GroupMarker("zoom"));
+ menuBar.add(editMenu);
+ super.fillMenuBar(menuBar);
+ ActionContributionItem exitActionItem = new ActionContributionItem(exitAction);
+ fileMenu.add(exitActionItem);
+ if(System.getProperty("os.name").contains("Mac")) {
+ aboutActionItem.setVisible(false);
+ exitActionItem.setVisible(false);
+ }
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ApplicationWorkbenchAdvisor.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ApplicationWorkbenchAdvisor.java
new file mode 100644
index 0000000..16fdcd5
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ApplicationWorkbenchAdvisor.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application;
+
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.application.IActionBarConfigurer;
+import org.eclipse.ui.application.IWorkbenchConfigurer;
+import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
+import org.eclipse.ui.application.WorkbenchAdvisor;
+import org.eclipse.ui.application.WorkbenchWindowAdvisor;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor {
+
+ @Override
+ public String getInitialWindowPerspectiveId() {
+ return "org.eclipse.zest.cloudio.rcp.perspective";
+ }
+
+ @Override
+ public void initialize(IWorkbenchConfigurer configurer) {
+ super.initialize(configurer);
+ }
+
+
+ @Override
+ public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(
+ IWorkbenchWindowConfigurer configurer) {
+ return new ApplicationWorkbenchWindowAdvisor(configurer);
+ }
+
+ @Override
+ public void fillActionBars(IWorkbenchWindow window,
+ IActionBarConfigurer configurer, int flags) {
+
+ }
+
+
+
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ApplicationWorkbenchWindowAdvisor.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ApplicationWorkbenchWindowAdvisor.java
new file mode 100644
index 0000000..381479c
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ApplicationWorkbenchWindowAdvisor.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.application.ActionBarAdvisor;
+import org.eclipse.ui.application.IActionBarConfigurer;
+import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
+import org.eclipse.ui.application.WorkbenchWindowAdvisor;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class ApplicationWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor {
+
+ public ApplicationWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) {
+ super(configurer);
+ }
+
+ public ActionBarAdvisor createActionBarAdvisor(IActionBarConfigurer configurer) {
+ return new ApplicationActionBarAdvisor(configurer);
+ }
+
+ public void preWindowOpen() {
+ IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
+ configurer.setInitialSize(new Point(900, 700));
+ configurer.setShowCoolBar(false);
+ configurer.setShowProgressIndicator(false);
+ configurer.setTitle("Cloudio");
+ configurer.setShowStatusLine(false);
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/PerspectiveFactory.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/PerspectiveFactory.java
new file mode 100644
index 0000000..da48ce8
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/PerspectiveFactory.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application;
+
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPerspectiveFactory;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class PerspectiveFactory implements IPerspectiveFactory {
+
+ @Override
+ public void createInitialLayout(IPageLayout layout) {
+ layout.addStandaloneView("org.eclipse.zest.cloudio.sample.tagcloud", false, IPageLayout.TOP, 0.95f, layout.getEditorArea());
+ layout.setFixed(true);
+ layout.setEditorAreaVisible(false);
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/about/AboutDialog.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/about/AboutDialog.java
new file mode 100644
index 0000000..e415af3
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/about/AboutDialog.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.about;
+
+import java.awt.GraphicsEnvironment;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Rectangle;
+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.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.zest.cloudio.TagCloud;
+import org.eclipse.zest.cloudio.Word;
+import org.eclipse.zest.cloudio.layout.DefaultLayouter;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class AboutDialog extends Dialog {
+
+ private static final int RE_LAYOUT = 2;
+ private TagCloud tc;
+
+ public AboutDialog(Shell parentShell) {
+ super(parentShell);
+ setBlockOnOpen(false);
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ parent.setLayout(new GridLayout());
+ tc = new TagCloud(parent, SWT.NONE) {
+
+ public Rectangle getClientArea() {
+ return new Rectangle(0, 0, 400, 330);
+ };
+
+ };
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ data.heightHint=330;
+ data.widthHint=400;
+ tc.setLayoutData(data);
+ tc.setMaxFontSize(50);
+ tc.setMinFontSize(15);
+ tc.setLayouter(new DefaultLayouter(5, 0));
+ List<Word> values = new ArrayList<Word>();
+ String[] fontNames = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
+ Color[] colors = new Color[5];
+ colors[0] = Display.getDefault().getSystemColor(SWT.COLOR_DARK_CYAN);
+ colors[1] = Display.getDefault().getSystemColor(SWT.COLOR_DARK_GREEN);
+ colors[2] = Display.getDefault().getSystemColor(SWT.COLOR_DARK_MAGENTA);
+ colors[3] = Display.getDefault().getSystemColor(SWT.COLOR_DARK_YELLOW);
+ colors[4] = Display.getDefault().getSystemColor(SWT.COLOR_GRAY);
+ Word w = getWord("Cloudio", fontNames, colors);
+ w.weight=0.95;
+ w.angle=-35;
+ w.setColor(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
+ values.add(w);
+ w = getWord("Inspired by Wordle", fontNames, colors);
+ w.setColor(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
+ w.angle = (float) (Math.random() * 90);
+ if(Math.random() < 0.5) w.angle = -w.angle;
+ w.weight=0.2;
+ if(Math.random() < 0.5) w.angle = -w.angle;
+ values.add(w);
+ w = getWord("Used by " + System.getProperty("user.name"), fontNames, colors);
+ w.setColor(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
+ w.weight = 0.2;
+ values.add(w);
+ for(int i = 0; i < 20; i++) {
+ w = getWord("Cloudio", fontNames, colors);
+ values.add(w);
+ w = getWord("Tag Cloud", fontNames, colors);
+ values.add(w);
+ }
+ tc.setWords(values, null);
+ Label l = new Label(parent, SWT.NONE);
+ l.setText("Written by Stephan Schwiebert, 2011");
+ return tc;
+ }
+
+ protected void createButtonsForButtonBar(Composite parent) {
+ createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
+ true);
+ createButton(parent, RE_LAYOUT, "Do the inevitable...", false);
+ }
+
+ @Override
+ protected void buttonPressed(int buttonId) {
+ if(buttonId == RE_LAYOUT) {
+ tc.layoutCloud(null, false);
+ } else {
+ super.buttonPressed(buttonId);
+ }
+ }
+
+ private Word getWord(String string, String[] fontNames, Color[] colors) {
+ Word w = new Word(string);
+ w.setColor(colors[(int) (Math.random()*colors.length-1)]);
+ w.weight = Math.random()/2;
+ w.setFontData(getShell().getFont().getFontData());
+ w.angle = (float) (Math.random() * 20);
+ if(Math.random() < 0.5) w.angle = -w.angle;
+ String name = fontNames[(int) (Math.random()*(fontNames.length-1))];
+ for (FontData fd : w.getFontData()) {
+ fd.setName(name);
+ }
+ return w;
+ }
+
+ @Override
+ public boolean close() {
+ tc.dispose();
+ return super.close();
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/AboutAction.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/AboutAction.java
new file mode 100644
index 0000000..b6ec6e6
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/AboutAction.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.actions;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
+import org.eclipse.zest.examples.cloudio.application.about.AboutDialog;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class AboutAction extends Action implements IWorkbenchAction {
+
+ public AboutAction() {
+ super.setId("about");
+ setText("About");
+ }
+
+ @Override
+ public void run() {
+ AboutDialog dialog = new AboutDialog(Display.getCurrent().getActiveShell());
+ dialog.open();
+ }
+
+ @Override
+ public void dispose() {
+
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/AbstractTagCloudAction.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/AbstractTagCloudAction.java
new file mode 100644
index 0000000..e1c2848
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/AbstractTagCloudAction.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.actions;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+import org.eclipse.zest.cloudio.TagCloudViewer;
+import org.eclipse.zest.examples.cloudio.application.ui.TagCloudViewPart;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public abstract class AbstractTagCloudAction implements IWorkbenchWindowActionDelegate {
+
+ private Shell shell;
+ private TagCloudViewPart tcViewPart;
+
+
+ @Override
+ public void selectionChanged(IAction action, ISelection selection) {}
+
+ @Override
+ public void dispose() {}
+
+ @Override
+ public void init(IWorkbenchWindow window) {
+ this.shell = window.getShell();
+ tcViewPart = (TagCloudViewPart) window.getActivePage().getActivePart();
+ }
+
+ public Shell getShell() {
+ return shell;
+ }
+
+
+ protected TagCloudViewer getViewer() {
+ return tcViewPart.getViewer();
+ }
+
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/DeselectAllAction.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/DeselectAllAction.java
new file mode 100644
index 0000000..ad49298
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/DeselectAllAction.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.actions;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.StructuredSelection;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class DeselectAllAction extends AbstractTagCloudAction {
+
+ @Override
+ public void run(IAction action) {
+ StructuredSelection selection = new StructuredSelection();
+ getViewer().setSelection(selection);
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ExportImageAction.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ExportImageAction.java
new file mode 100644
index 0000000..2c4dead
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ExportImageAction.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.actions;
+
+import java.io.File;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
+import org.eclipse.swt.widgets.FileDialog;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class ExportImageAction extends AbstractTagCloudAction {
+
+ @Override
+ public void run(IAction action) {
+ FileDialog dialog = new FileDialog(getShell(), SWT.SAVE);
+ dialog.setFileName("Cloud.png");
+ dialog.setText("Export PNG image to...");
+ String destFile = dialog.open();
+ if(destFile == null) return;
+ File f = new File(destFile);
+ if(f.exists()) {
+ boolean confirmed = MessageDialog.openConfirm(getShell(), "File already exists", "The file '" + f.getName() + "' does already exist. Do you want to override it?");
+ if(!confirmed) return;
+ }
+ ImageLoader il = new ImageLoader();
+ try {
+ il.data = new ImageData[] {getViewer().getCloud().getImageData()};
+ il.save(destFile, SWT.IMAGE_PNG);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/LoadFileAction.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/LoadFileAction.java
new file mode 100644
index 0000000..820a2d2
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/LoadFileAction.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.actions;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.zest.cloudio.TagCloudViewer;
+import org.eclipse.zest.examples.cloudio.application.data.Type;
+import org.eclipse.zest.examples.cloudio.application.data.TypeCollector;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class LoadFileAction extends AbstractTagCloudAction {
+
+ @Override
+ public void run(IAction action) {
+ FileDialog dialog = new FileDialog(getShell(), SWT.OPEN);
+
+ dialog.setText("Select text file...");
+ String sourceFile = dialog.open();
+ if(sourceFile == null) return;
+ ProgressMonitorDialog pd = new ProgressMonitorDialog(getShell());
+ try {
+ List<Type> types = TypeCollector.getData(new File(sourceFile), "UTF-8");
+ pd.setBlockOnOpen(false);
+ pd.open();
+ pd.getProgressMonitor().beginTask("Generating cloud...", 200);
+ TagCloudViewer viewer = getViewer();
+ viewer.setInput(types, pd.getProgressMonitor());
+ viewer.getCloud().layoutCloud(pd.getProgressMonitor(), false);
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ pd.close();
+ }
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/LoadStopWordsAction.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/LoadStopWordsAction.java
new file mode 100644
index 0000000..8f19522
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/LoadStopWordsAction.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.actions;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.zest.examples.cloudio.application.data.TypeCollector;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class LoadStopWordsAction extends AbstractTagCloudAction {
+
+ @Override
+ public void run(IAction action) {
+ FileDialog dialog = new FileDialog(getShell(), SWT.OPEN);
+ dialog.setText("Select a stopwor file, containing one word per line...");
+ String sourceFile = dialog.open();
+ if(sourceFile == null) return;
+ TypeCollector.setStopwords(sourceFile);
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/SelectAllAction.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/SelectAllAction.java
new file mode 100644
index 0000000..4b3f95c
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/SelectAllAction.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.actions;
+
+import java.util.List;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.StructuredSelection;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class SelectAllAction extends AbstractTagCloudAction {
+
+ @Override
+ public void run(IAction action) {
+ StructuredSelection selection = new StructuredSelection((List<?>)getViewer().getInput());
+ getViewer().setSelection(selection);
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ZoomFitAction.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ZoomFitAction.java
new file mode 100644
index 0000000..17c3b45
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ZoomFitAction.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.actions;
+
+import org.eclipse.jface.action.IAction;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class ZoomFitAction extends AbstractTagCloudAction {
+
+ @Override
+ public void run(IAction action) {
+ getViewer().zoomFit();
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ZoomInAction.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ZoomInAction.java
new file mode 100644
index 0000000..554f7ca
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ZoomInAction.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.actions;
+
+import org.eclipse.jface.action.IAction;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class ZoomInAction extends AbstractTagCloudAction {
+
+ @Override
+ public void run(IAction action) {
+ getViewer().zoomIn();
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ZoomOutAction.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ZoomOutAction.java
new file mode 100644
index 0000000..1ec23c0
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ZoomOutAction.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.actions;
+
+import org.eclipse.jface.action.IAction;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class ZoomOutAction extends AbstractTagCloudAction {
+
+ @Override
+ public void run(IAction action) {
+ getViewer().zoomOut();
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ZoomResetAction.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ZoomResetAction.java
new file mode 100644
index 0000000..a2d9b73
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/actions/ZoomResetAction.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.actions;
+
+import org.eclipse.jface.action.IAction;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class ZoomResetAction extends AbstractTagCloudAction {
+
+ @Override
+ public void run(IAction action) {
+ getViewer().zoomReset();
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/data/Type.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/data/Type.java
new file mode 100644
index 0000000..b571465
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/data/Type.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.data;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class Type {
+
+ private String string;
+ private int occurrences;
+
+ public Type(String string, int occurrences) {
+ this.string = string;
+ this.occurrences = occurrences;
+ }
+
+ public String getString() {
+ return string;
+ }
+
+ public int getOccurrences() {
+ return occurrences;
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/data/TypeCollector.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/data/TypeCollector.java
new file mode 100644
index 0000000..1a82350
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/data/TypeCollector.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.data;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.text.BreakIterator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class TypeCollector {
+
+ private static String stopWords;
+
+ public static List<Type> getData(File file, String encoding) throws IOException {
+ BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
+ BufferedReader br = new BufferedReader(new InputStreamReader(bis, encoding));
+ StringBuffer text = new StringBuffer();
+ String s;
+ while((s = br.readLine()) != null) {
+ text.append(s + "\n");
+ }
+ br.close();
+ Set<String> stops = new HashSet<String>();
+ if(stopWords != null) {
+ bis = new BufferedInputStream(new FileInputStream(stopWords));
+ br = new BufferedReader(new InputStreamReader(bis, encoding));
+ while((s = br.readLine()) != null) {
+ stops.add(s.toLowerCase().trim());
+ }
+ br.close();
+ }
+ BreakIterator iterator = BreakIterator.getWordInstance(Locale.getDefault());
+ String txt = text.toString();
+ iterator.setText(txt);
+ final Map<String, Integer> strings = new HashMap<String, Integer>();
+ int boundary = iterator.first();
+ int lastBoundary = iterator.first();
+ while (boundary != BreakIterator.DONE) {
+ boundary = iterator.next();
+ if (boundary != -1) {
+ String string = txt.substring(lastBoundary, boundary).trim();
+ if (string.length() != 0) {
+ if (!Character.isLetter(string.charAt(string.length() - 1))) {
+ string = string.substring(0, string.length() - 1);
+ }
+ if (stops.contains(string.toLowerCase()) || string.trim().length() <= 1) {
+ lastBoundary = boundary;
+ continue;
+ }
+ Integer count = strings.get(string);
+ if (count == null) {
+ strings.put(string, 1);
+ } else {
+ count = count + 1;
+ strings.put(string, count);
+ }
+ }
+ }
+ lastBoundary = boundary;
+ }
+ return getMostImportantTypes(strings);
+ }
+
+ private static List<Type> getMostImportantTypes(final Map<String, Integer> strings) {
+ List<Type> types = new ArrayList<Type>();
+ Iterator<Entry<String, Integer>> iterator = strings.entrySet().iterator();
+ while(iterator.hasNext()) {
+ Entry<String, Integer> entry = iterator.next();
+ Type type = new Type(entry.getKey(), entry.getValue());
+ types.add(type);
+ }
+ List<Type> sorted = new ArrayList<Type>(types);
+ Collections.sort(sorted, new Comparator<Type>() {
+
+ @Override
+ public int compare(Type o1, Type o2) {
+ return o2.getOccurrences() - o1.getOccurrences();
+ }
+ });
+ return sorted;
+ }
+
+ public static void setStopwords(String sourceFile) {
+ stopWords = sourceFile;
+ }
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/CloudioApplicationPlugin.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/CloudioApplicationPlugin.java
new file mode 100644
index 0000000..2f52316
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/CloudioApplicationPlugin.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.ui;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class CloudioApplicationPlugin extends AbstractUIPlugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.eclipse.examples.zest.cloudio"; //$NON-NLS-1$
+
+ // The shared instance
+ private static CloudioApplicationPlugin plugin;
+
+ /**
+ * The constructor
+ */
+ public CloudioApplicationPlugin() {
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static CloudioApplicationPlugin getDefault() {
+ return plugin;
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/TagCloudViewPart.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/TagCloudViewPart.java
new file mode 100644
index 0000000..5bb8193
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/TagCloudViewPart.java
@@ -0,0 +1,230 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.part.ViewPart;
+import org.eclipse.zest.cloudio.CloudOptionsComposite;
+import org.eclipse.zest.cloudio.TagCloud;
+import org.eclipse.zest.cloudio.TagCloudViewer;
+import org.eclipse.zest.cloudio.layout.DefaultLayouter;
+import org.eclipse.zest.cloudio.layout.ILayouter;
+import org.eclipse.zest.examples.cloudio.application.data.Type;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class TagCloudViewPart extends ViewPart {
+
+ private TagCloudViewer viewer;
+ private TypeLabelProvider labelProvider;
+ private CloudOptionsComposite options;
+ private ILayouter layouter;
+
+ public TagCloudViewPart() {
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ SashForm sash = new SashForm(parent, SWT.HORIZONTAL);
+ sash.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ Composite cloudComp = new Composite(sash, SWT.NONE);
+ cloudComp.setLayout(new GridLayout());
+ cloudComp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ TagCloud cloud = new TagCloud(cloudComp, SWT.HORIZONTAL | SWT.VERTICAL);
+ cloud.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ viewer = new TagCloudViewer(cloud);
+
+ layouter = new DefaultLayouter(20, 10);
+ //layouter = new CharacterLayouter(20,10);
+ viewer.setLayouter(layouter);
+ labelProvider = new TypeLabelProvider();
+ //labelProvider = new CharacterLabelProvider();
+ viewer.setLabelProvider(labelProvider);
+ viewer.setContentProvider(new IStructuredContentProvider() {
+
+ @Override
+ public void inputChanged(Viewer v, Object oldInput, Object newInput) {
+ List<?> list = (List<?>) newInput;
+ if(list == null || list.size() == 0) return;
+ labelProvider.setMaxOccurrences(((Type)list.get(0)).getOccurrences());
+ int minIndex =Math.min(list.size()-1, viewer.getMaxWords());
+ labelProvider.setMinOccurrences(((Type)list.get(minIndex)).getOccurrences());
+ }
+
+ @Override
+ public void dispose() {
+
+ }
+
+ @Override
+ public Object[] getElements(Object inputElement) {
+ return ((List<?>)inputElement).toArray();
+ }
+ });
+ createSideTab(sash);
+
+ cloud.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ viewer.getCloud().addControlListener(new ControlListener() {
+
+ @Override
+ public void controlResized(ControlEvent e) {
+ viewer.getCloud().zoomFit();
+ }
+
+ @Override
+ public void controlMoved(ControlEvent e) {}
+ });
+ ArrayList<Type> types = new ArrayList<Type>();
+ types.add(new Type("Cloudio", 220));
+ types.add(new Type("Cloudio", 150));
+ types.add(new Type("Cloudio", 100));
+ types.add(new Type("No data available", 150));
+ int size = 55;
+ for(int i = 0; i < 50; i++) {
+ types.add(new Type("Tag Cloud", size));
+ size--;
+ }
+ viewer.getCloud().setMaxFontSize(100);
+ viewer.getCloud().setMinFontSize(15);
+ labelProvider.setColors(options.getColors());
+ labelProvider.setFonts(options.getFonts());
+ sash.setWeights(new int[] {72,28});
+ viewer.setInput(types);
+ }
+
+ private void createSideTab(SashForm form) {
+ Composite parent = new Composite(form, SWT.NONE);
+ parent.setLayout(new GridLayout());
+ parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ options = new CloudOptionsComposite(parent, SWT.NONE, viewer) {
+
+ protected Group addLayoutButtons(Composite parent) {
+ Group buttons = super.addLayoutButtons(parent);
+
+
+ Label l = new Label(buttons, SWT.NONE);
+ l.setText("X Axis Variation");
+ final Combo xAxis = new Combo(buttons, SWT.DROP_DOWN | SWT.READ_ONLY);
+ xAxis.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ xAxis.setItems(new String[] {"0", "10","20","30","40","50", "60", "70", "80", "90", "100"});
+ xAxis.select(2);
+ xAxis.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ String item = xAxis.getItem(xAxis.getSelectionIndex());
+ layouter.setOption(DefaultLayouter.X_AXIS_VARIATION, Integer.parseInt(item));
+
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+
+ l = new Label(buttons, SWT.NONE);
+ l.setText("Y Axis Variation");
+ final Combo yAxis = new Combo(buttons, SWT.DROP_DOWN | SWT.READ_ONLY);
+ yAxis.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ yAxis.setItems(new String[] {"0", "10","20","30","40","50", "60", "70", "80", "90", "100"});
+ yAxis.select(1);
+ yAxis.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ String item = yAxis.getItem(yAxis.getSelectionIndex());
+ layouter.setOption(DefaultLayouter.Y_AXIS_VARIATION, Integer.parseInt(item));
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+
+ Button run = new Button(buttons, SWT.NONE);
+ run.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ run.setText("Re-Position");
+ run.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ final ProgressMonitorDialog dialog = new ProgressMonitorDialog(viewer.getControl().getShell());
+ dialog.setBlockOnOpen(false);
+ dialog.open();
+ dialog.getProgressMonitor().beginTask("Layouting tag cloud...", 100);
+ viewer.reset(dialog.getProgressMonitor(),false);
+ dialog.close();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+ Button layout = new Button(buttons, SWT.NONE);
+ layout.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ layout.setText("Re-Layout");
+ layout.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ ProgressMonitorDialog dialog = new ProgressMonitorDialog(viewer.getControl().getShell());
+ dialog.setBlockOnOpen(false);
+ dialog.open();
+ dialog.getProgressMonitor().beginTask("Layouting tag cloud...", 200);
+ viewer.setInput(viewer.getInput(), dialog.getProgressMonitor());
+ viewer.reset(dialog.getProgressMonitor(),false);
+ dialog.close();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ });
+ return buttons;
+ };
+
+ };
+ GridData gd = new GridData(SWT.FILL, SWT.CENTER, true, false);
+ options.setLayoutData(gd);
+ }
+
+
+ @Override
+ public void setFocus() {
+ viewer.getCloud().setFocus();
+ }
+
+ @Override
+ public void dispose() {
+ viewer.getCloud().dispose();
+ labelProvider.dispose();
+ }
+
+ public TagCloudViewer getViewer() {
+ return viewer;
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/TypeLabelProvider.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/TypeLabelProvider.java
new file mode 100644
index 0000000..5d89331
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/TypeLabelProvider.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.ui;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import org.eclipse.jface.viewers.BaseLabelProvider;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.zest.cloudio.IEditableCloudLabelProvider;
+import org.eclipse.zest.examples.cloudio.application.data.Type;
+
+/**
+ *
+ * @author sschwieb
+ *
+ */
+public class TypeLabelProvider extends BaseLabelProvider implements
+ IEditableCloudLabelProvider {
+
+ private double maxOccurrences;
+ private double minOccurrences;
+
+ private Map<Object, Color> colors = new HashMap<Object, Color>();
+ private Map<Object, FontData[]> fonts = new HashMap<Object, FontData[]>();
+ private Random random = new Random();
+ protected List<Color> colorList;
+ protected List<Font> fontList;
+ protected List<Float> angles;
+
+ public TypeLabelProvider() {
+ colorList = new ArrayList<Color>();
+ fontList = new ArrayList<Font>();
+ angles = new ArrayList<Float>();
+ angles.add(0F);
+ }
+
+ @Override
+ public String getLabel(Object element) {
+ return ((Type)element).getString();
+ }
+
+ @Override
+ public double getWeight(Object element) {
+ double count = Math.log(((Type)element).getOccurrences() - minOccurrences+1);
+ count /=(Math.log(maxOccurrences));
+ return count;
+ }
+
+ @Override
+ public Color getColor(Object element) {
+ Color color = colors.get(element);
+ if(color == null) {
+ color = colorList.get(random.nextInt(colorList.size()));
+ colors.put(element, color);
+ }
+ return color;
+ }
+
+ public FontData[] getFontData(Object element) {
+ FontData[] data = fonts.get(element);
+ if(data == null) {
+ data = fontList.get(random.nextInt(fontList.size())).getFontData();
+ fonts.put(element, data);
+ }
+ return data;
+ }
+
+ public void setMaxOccurrences(int occurrences) {
+ this.maxOccurrences = occurrences;
+ }
+
+ public void setMinOccurrences(int occurrences) {
+ this.minOccurrences = occurrences;
+ }
+
+ @Override
+ public void dispose() {
+ for (Color color : colorList) {
+ color.dispose();
+ }
+ for (Font font : fontList) {
+ font.dispose();
+ }
+ }
+
+ public void setAngles(List<Float> angles) {
+ this.angles = angles;
+ }
+
+ @Override
+ public float getAngle(Object element) {
+ float angle = angles.get(random.nextInt(angles.size()));
+ return angle;
+ }
+
+ public void setColors(List<RGB> newColors) {
+ if(newColors.isEmpty()) return;
+ for (Color color : colorList) {
+ color.dispose();
+ }
+ colorList.clear();
+ colors.clear();
+ for (RGB color : newColors) {
+ Color c = new Color(Display.getDefault(), color);
+ colorList.add(c);
+ }
+ }
+
+ public void setFonts(List<FontData> newFonts) {
+ if(newFonts.isEmpty()) return;
+ for (Font font : fontList) {
+ font.dispose();
+ }
+ fontList.clear();
+ fonts.clear();
+ for (FontData data : newFonts) {
+ Font f = new Font(Display.getDefault(), data);
+ fontList.add(f);
+ }
+ }
+
+ @Override
+ public String getToolTip(Object element) {
+ return getLabel(element);
+ }
+
+
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/customization/CharacterLabelProvider.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/customization/CharacterLabelProvider.java
new file mode 100644
index 0000000..039fee6
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/customization/CharacterLabelProvider.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.ui.customization;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.zest.examples.cloudio.application.data.Type;
+import org.eclipse.zest.examples.cloudio.application.ui.TypeLabelProvider;
+
+/**
+ * An example to demonstrate how to modify a label provider
+ * @author sschwieb
+ *
+ */
+public class CharacterLabelProvider extends TypeLabelProvider {
+
+ @Override
+ public Color getColor(Object element) {
+ Type t = (Type) element;
+ char firstChar = Character.toLowerCase(t.getString().charAt(0));
+ if(firstChar < 'g') {
+ return colorList.get(2);
+ }
+ if(firstChar < 'm') {
+ return colorList.get(1);
+ }
+ if(firstChar < 's') {
+ return colorList.get(0);
+ }
+ return colorList.get(3);
+ }
+
+}
diff --git a/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/customization/CharacterLayouter.java b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/customization/CharacterLayouter.java
new file mode 100644
index 0000000..1225d60
--- /dev/null
+++ b/org.eclipse.zest.examples.cloudio/src/main/java/org/eclipse/zest/examples/cloudio/application/ui/customization/CharacterLayouter.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+* Copyright (c) 2011 Stephan Schwiebert. 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
+* <p/>
+* Contributors: Stephan Schwiebert - initial API and implementation
+*******************************************************************************/
+package org.eclipse.zest.examples.cloudio.application.ui.customization;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.zest.cloudio.Word;
+import org.eclipse.zest.cloudio.layout.DefaultLayouter;
+
+/**
+ * An example to show how to modify a layouter
+ * @author sschwieb
+ *
+ */
+public class CharacterLayouter extends DefaultLayouter {
+
+ public CharacterLayouter(int x, int y) {
+ super(x,y);
+ }
+
+ public Point getInitialOffset(Word word, Rectangle cloudArea) {
+ Point parentOffsets = super.getInitialOffset(word, new Rectangle(cloudArea.x, cloudArea.y, cloudArea.width/4, cloudArea.height/4));
+ char firstChar = Character.toLowerCase(word.string.charAt(0));
+ int x=cloudArea.width/4;
+ int y = cloudArea.height/4;
+ if(firstChar < 's') {
+ x = 0;
+ y = 0;
+ }
+ if(firstChar < 'm') {
+ x = cloudArea.width/4;
+ y = 0;
+ }
+ if(firstChar < 'g') {
+ x = 0;
+ y = cloudArea.height/4;
+ }
+ return new Point(x+parentOffsets.x, y+parentOffsets.y);
+ }
+
+}
diff --git a/org.eclipse.zest.feature/feature.xml b/org.eclipse.zest.feature/feature.xml
index 9a7e301..eabb014 100644
--- a/org.eclipse.zest.feature/feature.xml
+++ b/org.eclipse.zest.feature/feature.xml
@@ -63,4 +63,11 @@
version="0.0.0"
unpack="false"/>
+ <plugin
+ id="org.eclipse.zest.cloudio"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
</feature>
diff --git a/org.eclipse.zest.source-feature/feature.xml b/org.eclipse.zest.source-feature/feature.xml
index 2c985a5..ef4d45c 100644
--- a/org.eclipse.zest.source-feature/feature.xml
+++ b/org.eclipse.zest.source-feature/feature.xml
@@ -16,6 +16,13 @@
<license url="%licenseURL">
%license
</license>
+
+ <plugin
+ id="org.eclipse.zest.cloudio.source"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
<plugin
id="org.eclipse.zest.core.source"
diff --git a/org.eclipse.zest.tests/META-INF/MANIFEST.MF b/org.eclipse.zest.tests/META-INF/MANIFEST.MF
index 570bc6b..73c4c11 100644
--- a/org.eclipse.zest.tests/META-INF/MANIFEST.MF
+++ b/org.eclipse.zest.tests/META-INF/MANIFEST.MF
@@ -8,7 +8,8 @@ Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
org.eclipse.zest.dot.ui;bundle-version="0.9.1",
org.junit;bundle-version="4.8.1",
- org.eclipse.zest.jface;bundle-version="2.0.0"
+ org.eclipse.zest.jface;bundle-version="2.0.0",
+ org.eclipse.zest.cloudio;bundle-version="2.0.0"
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Export-Package: org.eclipse.zest.tests.dot
diff --git a/org.eclipse.zest.tests/pom.xml b/org.eclipse.zest.tests/pom.xml
index 0762cee..63991dd 100644
--- a/org.eclipse.zest.tests/pom.xml
+++ b/org.eclipse.zest.tests/pom.xml
@@ -35,7 +35,7 @@
</os>
</activation>
<properties>
- <vmargs>-Xmx512m -XX:MaxPermSize=256m -XstartOnFirstThread</vmargs>
+ <vmargs>-Xmx768m -XX:MaxPermSize=512m -XstartOnFirstThread</vmargs>
</properties>
</profile>
</profiles>
diff --git a/org.eclipse.zest.tests/src/org/eclipse/zest/tests/AllHeadlessTests.java b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/AllHeadlessTests.java
index 8a02cb6..2d9e656 100644
--- a/org.eclipse.zest.tests/src/org/eclipse/zest/tests/AllHeadlessTests.java
+++ b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/AllHeadlessTests.java
@@ -8,6 +8,8 @@
*******************************************************************************/
package org.eclipse.zest.tests;
+import org.eclipse.zest.tests.cloudio.TagCloudTests;
+import org.eclipse.zest.tests.cloudio.TagCloudViewerTests;
import org.eclipse.zest.tests.dot.DotExportSuite;
import org.eclipse.zest.tests.dot.DotImportSuite;
import org.junit.runner.RunWith;
@@ -21,6 +23,7 @@ import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({ GraphSelectionTests.class, GraphViewerTests.class,
IFigureProviderTests.class, LayoutAlgorithmTests.class,
- DotExportSuite.class, DotImportSuite.class })
+ DotExportSuite.class, DotImportSuite.class, TagCloudTests.class,
+ TagCloudViewerTests.class })
public final class AllHeadlessTests {
}
diff --git a/org.eclipse.zest.tests/src/org/eclipse/zest/tests/cloudio/TagCloudTests.java b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/cloudio/TagCloudTests.java
new file mode 100644
index 0000000..28543a4
--- /dev/null
+++ b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/cloudio/TagCloudTests.java
@@ -0,0 +1,629 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stephan Schwiebert. 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
+ * <p/>
+ * Contributors: Stephan Schwiebert - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.zest.tests.cloudio;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import junit.framework.Assert;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.events.MouseWheelListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.zest.cloudio.TagCloud;
+import org.eclipse.zest.cloudio.Word;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TagCloudTests {
+
+ private Display display;
+ private boolean createdDisplay = false;
+ private Composite composite;
+
+ @Before
+ public void setUp() throws Exception {
+ display = Display.getCurrent();
+ if (display == null) {
+ display = new Display();
+ createdDisplay = true;
+ }
+ composite = new Shell(display);
+ composite.setLayout(new FillLayout());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ composite.dispose();
+ if (createdDisplay) {
+ display.dispose();
+ }
+ }
+
+ // Lifecycle:
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConstructor_NullParent() {
+ new TagCloud(null, SWT.NONE);
+ }
+
+ @Test
+ public void testConstructor_ValidParent() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ Assert.assertNotNull(cloud);
+ }
+
+ @Test
+ public void testDispose() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ cloud.dispose();
+ Assert.assertTrue(cloud.isDisposed());
+ }
+
+ // Background Color:
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetInvalidBackgroundColor() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ cloud.setBackground(null);
+ }
+
+ @Test
+ public void testSetValidBackgroundColor() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ Color color = Display.getCurrent().getSystemColor(SWT.COLOR_RED);
+ cloud.setBackground(color);
+ Assert.assertEquals(color, cloud.getBackground());
+ }
+
+ @Test
+ public void testDefaultBackgroundColor() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ Assert.assertNotNull(cloud.getBackground());
+ }
+
+ // Selection Color:
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetInvalidSelectionColor() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ cloud.setSelectionColor(null);
+ }
+
+ @Test
+ public void testSetValidSelectionColor() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ Color color = Display.getCurrent().getSystemColor(SWT.COLOR_RED);
+ cloud.setSelectionColor(color);
+ Assert.assertEquals(color, cloud.getSelectionColor());
+ }
+
+ @Test
+ public void testDefaultSelectionColor() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ Assert.assertNotNull(cloud.getSelectionColor());
+ }
+
+ // Font Size:
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetInvalidMaxFontSize() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ cloud.setMaxFontSize(0);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetInvalidMinFontSize() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ cloud.setMinFontSize(0);
+ }
+
+ @Test
+ public void testSetValidMaxFontSize() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ int size = cloud.getMaxFontSize() + 1;
+ cloud.setMaxFontSize(size * 2);
+ Assert.assertEquals(size * 2, cloud.getMaxFontSize());
+ }
+
+ @Test
+ public void testSetValidMinFontSize() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ int size = cloud.getMinFontSize() + 1;
+ cloud.setMinFontSize(size * 2);
+ Assert.assertEquals(size * 2, cloud.getMinFontSize());
+ }
+
+ // Set Words:
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetIllegalWords1() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ cloud.setWords(null, null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetIllegalWords2() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ List<Word> words = new ArrayList<Word>();
+ words.add(null);
+ cloud.setWords(words, null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetIllegalWords3() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ List<Word> words = new ArrayList<Word>();
+ Word w = new Word("Word");
+ w.setFontData(composite.getFont().getFontData());
+ w.weight = Math.random();
+ words.add(w);
+ cloud.setWords(words, null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetIllegalWords4() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ List<Word> words = new ArrayList<Word>();
+ Word w = new Word("Word");
+ w.setColor(Display.getDefault().getSystemColor(SWT.COLOR_RED));
+ w.weight = Math.random();
+ words.add(w);
+ cloud.setWords(words, null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetIllegalWords5() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ List<Word> words = new ArrayList<Word>();
+ Word word = getWord();
+ word.angle = -180;
+ words.add(word);
+ cloud.setWords(words, null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetIllegalWords6() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ List<Word> words = new ArrayList<Word>();
+ Word word = getWord();
+ word.angle = 180;
+ words.add(word);
+ cloud.setWords(words, null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetIllegalWords7() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ List<Word> words = new ArrayList<Word>();
+ Word word = getWord();
+ word.weight = -1;
+ words.add(word);
+ cloud.setWords(words, null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetIllegalWords8() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ List<Word> words = new ArrayList<Word>();
+ Word word = getWord();
+ word.weight = 2;
+ words.add(word);
+ cloud.setWords(words, null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetIllegalWords9() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ List<Word> words = new ArrayList<Word>();
+ Word word = new Word(null);
+ words.add(word);
+ cloud.setWords(words, null);
+ }
+
+ private Word getWord() {
+ Word w = new Word("Word");
+ w.setColor(Display.getDefault().getSystemColor(SWT.COLOR_GRAY));
+ w.setFontData(composite.getFont().getFontData());
+ w.weight = 1;
+ return w;
+ }
+
+ @Test
+ public void testSetEmptyWordList() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ List<Word> words = new ArrayList<Word>();
+ int placed = cloud.setWords(words, null);
+ Assert.assertEquals(0, placed);
+ }
+
+ @Test
+ public void testSetWordList() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ List<Word> words = new ArrayList<Word>();
+ for (int i = 0; i < 10; i++) {
+ words.add(getWord());
+ }
+ int placed = cloud.setWords(words, null);
+ Assert.assertEquals(10, placed);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetInvalidOpacity1() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ cloud.setOpacity(-1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetInvalidOpacity2() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ cloud.setOpacity(256);
+ }
+
+ // Layouter
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetInvalidLayouter() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ cloud.setLayouter(null);
+ }
+
+ // Zoom
+
+ @Test
+ public void testZoomIn() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ cloud.setWords(Arrays.asList(getWord()), null);
+ double zoom = cloud.getZoom();
+ cloud.zoomIn();
+ Assert.assertTrue(cloud.getZoom() > zoom);
+ }
+
+ @Test
+ public void testZoomReset() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ cloud.setWords(Arrays.asList(getWord()), null);
+ double zoom = cloud.getZoom();
+ cloud.zoomReset();
+ Assert.assertTrue(cloud.getZoom() > zoom);
+ Assert.assertEquals(cloud.getZoom(), 1.0);
+ }
+
+ @Test
+ public void testZoomOut() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ cloud.setWords(Arrays.asList(getWord()), null);
+ cloud.zoomReset();
+ double zoom = cloud.getZoom();
+ cloud.zoomOut();
+ Assert.assertTrue(cloud.getZoom() < zoom);
+ }
+
+ @Test
+ public void testZoomFit() {
+ TagCloud cloud = new TagCloud(composite, SWT.V_SCROLL | SWT.H_SCROLL);
+ cloud.setWords(Arrays.asList(getWord()), null);
+ cloud.zoomReset();
+ double zoom = cloud.getZoom();
+ cloud.zoomFit();
+ Assert.assertTrue(cloud.getZoom() < zoom);
+ // TODO: Test if the cloud really fits the area!
+ }
+
+ // Image:
+
+ @Test
+ public void testGetImageData() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ Assert.assertNotNull(cloud.getImageData());
+ }
+
+ // Test Selection
+
+ @Test
+ public void testInitialSelection() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ Set<Word> selection = cloud.getSelection();
+ Assert.assertNotNull(selection);
+ Assert.assertTrue(selection.isEmpty());
+ }
+
+ @Test
+ public void testSetSelection() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ List<Word> words = new ArrayList<Word>();
+ words.add(getWord());
+ words.add(getWord());
+ cloud.setWords(words, null);
+ Set<Word> sel = new HashSet<Word>();
+ sel.add(words.get(0));
+ cloud.setSelection(sel);
+ Set<Word> selection = cloud.getSelection();
+ Assert.assertEquals(sel, selection);
+ cloud.setSelection(new HashSet<Word>());
+ selection = cloud.getSelection();
+ Assert.assertTrue(selection.isEmpty());
+ }
+
+ @Test
+ public void testSetNotExistingSelection1() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ List<Word> words = new ArrayList<Word>();
+ words.add(getWord());
+ words.add(getWord());
+ cloud.setWords(words, null);
+ Set<Word> sel = new HashSet<Word>();
+ sel.add(getWord());
+ cloud.setSelection(sel);
+ Set<Word> selection = cloud.getSelection();
+ Assert.assertTrue(selection.isEmpty());
+ }
+
+ @Test
+ public void testSetNotExistingSelection2() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ Set<Word> sel = new HashSet<Word>();
+ sel.add(getWord());
+ cloud.setSelection(sel);
+ Set<Word> selection = cloud.getSelection();
+ Assert.assertTrue(selection.isEmpty());
+ }
+
+ // Boost
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetInvalidBoost() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ cloud.setBoost(-1);
+ }
+
+ @Test
+ public void testSetValidBoost() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ Assert.assertEquals(0, cloud.getBoost());
+ cloud.setBoost(3);
+ Assert.assertEquals(3, cloud.getBoost());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetInvalidBoostFactor() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ cloud.setBoostFactor(0);
+ }
+
+ @Test
+ public void testSetValidBoostFactor() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ cloud.setBoostFactor(3.3F);
+ Assert.assertEquals(3.3F, cloud.getBoostFactor());
+ cloud.setBoostFactor(-2.2F);
+ Assert.assertEquals(-2.2F, cloud.getBoostFactor());
+ }
+
+ @Test
+ public void testLayout() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ List<Word> words = new ArrayList<Word>();
+ for (int i = 0; i < 10; i++) {
+ words.add(getWord());
+ }
+ // Initial position must be 0/0
+ for (Word word : words) {
+ Assert.assertTrue(word.x == 0);
+ Assert.assertTrue(word.y == 0);
+ }
+ cloud.setWords(words, null);
+ List<Rectangle> rects = new ArrayList<Rectangle>();
+ // Elements must have been placed
+ for (Word word : words) {
+ Assert.assertTrue(word.x != 0);
+ Assert.assertTrue(word.y != 0);
+ word.angle = 45f;
+ rects.add(new Rectangle(word.x, word.y, word.width, word.height));
+ }
+ cloud.layoutCloud(null, false);
+ boolean posChanged = false;
+ boolean rectChanged = false;
+ for (int i = 0; i < words.size(); i++) {
+ Word w = words.get(i);
+ Rectangle r = rects.get(i);
+ if (w.x != r.x || w.y != r.y) {
+ posChanged = true;
+ }
+ if (w.width != r.width || w.height != r.height) {
+ rectChanged = true;
+ }
+ }
+ // Positions must have been changed
+ Assert.assertTrue(posChanged);
+ // Bounds must not have been changed
+ Assert.assertFalse(rectChanged);
+ cloud.layoutCloud(null, true);
+ posChanged = false;
+ rectChanged = false;
+ for (int i = 0; i < words.size(); i++) {
+ Word w = words.get(i);
+ Rectangle r = rects.get(i);
+ if (w.x != r.x || w.y != r.y) {
+ posChanged = true;
+ }
+ if (w.width != r.width || w.height != r.height) {
+ rectChanged = true;
+ }
+ }
+ // Both positions an bounds must have changed
+ Assert.assertTrue(posChanged);
+ Assert.assertTrue(rectChanged);
+ }
+
+ // @Test
+ // public void testLayoutTooLarge() {
+ // TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ // List<Word> words = new ArrayList<Word>();
+ // Word w = getWord();
+ // words.add(w);
+ // cloud.setMaxFontSize(5000);
+ // int placed = cloud.setWords(words, null);
+ // Assert.assertEquals(0, placed);
+ // }
+
+ class UniversalListener implements MouseListener, MouseTrackListener,
+ MouseWheelListener, MouseMoveListener, SelectionListener {
+
+ private int mouseUp;
+ private int mouseDown;
+ private int mouseDC;
+ private int mouseMove;
+ private int mouseScrolled;
+ private int mouseExit;
+ private int mouseEnter;
+ private int mouseHover;
+ private Set<Word> selection;
+
+ public void mouseUp(MouseEvent e) {
+ mouseUp++;
+ }
+
+ public void mouseDown(MouseEvent e) {
+ mouseDown++;
+ }
+
+ public void mouseDoubleClick(MouseEvent e) {
+ mouseDC++;
+ }
+
+ public void mouseMove(MouseEvent e) {
+ mouseMove++;
+ System.out.println("MOVE");
+ }
+
+ public void mouseScrolled(MouseEvent e) {
+ mouseScrolled++;
+ }
+
+ public void mouseEnter(MouseEvent e) {
+ mouseEnter++;
+ }
+
+ public void mouseExit(MouseEvent e) {
+ mouseExit++;
+ }
+
+ public void mouseHover(MouseEvent e) {
+ mouseHover++;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void widgetSelected(SelectionEvent e) {
+ this.selection = (Set<Word>) e.data;
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e) {
+
+ }
+
+ }
+
+ @Test
+ public void testMouseListener() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ UniversalListener ml = new UniversalListener();
+ List<Word> words = new ArrayList<Word>();
+ Word word = getWord();
+ words.add(word);
+ cloud.setWords(words, null);
+ Event e = new Event();
+ cloud.addMouseListener(ml);
+ cloud.notifyListeners(SWT.MouseUp, e);
+ cloud.notifyListeners(SWT.MouseDoubleClick, e);
+ cloud.notifyListeners(SWT.MouseDown, e);
+ Assert.assertEquals(1, ml.mouseUp);
+ Assert.assertEquals(1, ml.mouseDC);
+ Assert.assertEquals(1, ml.mouseDown);
+ cloud.removeMouseListener(ml);
+ cloud.notifyListeners(SWT.MouseUp, e);
+ cloud.notifyListeners(SWT.MouseDoubleClick, e);
+ cloud.notifyListeners(SWT.MouseDown, e);
+ Assert.assertEquals(1, ml.mouseUp);
+ Assert.assertEquals(1, ml.mouseDC);
+ Assert.assertEquals(1, ml.mouseDown);
+ }
+
+ @Test
+ public void testMouseMoveListener() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ List<Word> words = new ArrayList<Word>();
+ Word word = getWord();
+ words.add(word);
+ cloud.setWords(words, null);
+ Event e = new Event();
+ e.x = word.x;
+ e.y = word.y;
+ UniversalListener ml = new UniversalListener();
+ cloud.addMouseMoveListener(ml);
+ cloud.notifyListeners(SWT.MouseMove, e);
+ Assert.assertEquals(1, ml.mouseMove);
+ cloud.removeMouseMoveListener(ml);
+ cloud.notifyListeners(SWT.MouseMove, e);
+ Assert.assertEquals(1, ml.mouseMove);
+ }
+
+ // @Test
+ // public void testMouseTrackListener() {
+ // // TODO: Difficult to test... involves zoom, scrollbars...
+ // }
+
+ @Test
+ public void testMouseWheelListener() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ UniversalListener ml = new UniversalListener();
+ cloud.addMouseWheelListener(ml);
+ cloud.notifyListeners(SWT.MouseWheel, new Event());
+ Assert.assertEquals(1, ml.mouseScrolled);
+ cloud.removeMouseWheelListener(ml);
+ cloud.notifyListeners(SWT.MouseWheel, new Event());
+ Assert.assertEquals(1, ml.mouseScrolled);
+ }
+
+ @Test
+ public void testSelectionListener() {
+ TagCloud cloud = new TagCloud(composite, SWT.NONE);
+ List<Word> words = new ArrayList<Word>();
+ Word word = getWord();
+ words.add(word);
+ cloud.setWords(words, null);
+ UniversalListener sl = new UniversalListener();
+ cloud.addSelectionListener(sl);
+ cloud.setSelection(new HashSet<Word>(words));
+ Assert.assertEquals(1, sl.selection.size());
+ cloud.setSelection(new HashSet<Word>());
+ Assert.assertEquals(0, sl.selection.size());
+ cloud.removeSelectionListener(sl);
+ cloud.setSelection(new HashSet<Word>(words));
+ Assert.assertEquals(0, sl.selection.size());
+
+ }
+
+}
diff --git a/org.eclipse.zest.tests/src/org/eclipse/zest/tests/cloudio/TagCloudViewerTests.java b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/cloudio/TagCloudViewerTests.java
new file mode 100644
index 0000000..035f89f
--- /dev/null
+++ b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/cloudio/TagCloudViewerTests.java
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stephan Schwiebert. 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
+ * <p/>
+ * Contributors: Stephan Schwiebert - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.zest.tests.cloudio;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.eclipse.jface.viewers.BaseLabelProvider;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.zest.cloudio.TagCloud;
+import org.eclipse.zest.cloudio.TagCloudViewer;
+import org.eclipse.zest.cloudio.Word;
+import org.eclipse.zest.cloudio.layout.DefaultLayouter;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TagCloudViewerTests {
+
+ private Display display;
+ private boolean createdDisplay = false;
+ private Composite composite;
+ private TagCloud cloud;
+
+ @Before
+ public void setUp() throws Exception {
+ display = Display.getCurrent();
+ if (display == null) {
+ display = new Display();
+ createdDisplay = true;
+ }
+ composite = new Shell(display);
+ composite.setLayout(new FillLayout());
+ cloud = new TagCloud(composite, SWT.NONE);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ composite.dispose();
+ if (createdDisplay) {
+ display.dispose();
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConstructor_NullCloud() {
+ new TagCloudViewer(null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConstructor_DisposedCloud() {
+ cloud.dispose();
+ new TagCloudViewer(cloud);
+ }
+
+ @Test
+ public void testConstructor_ValidCloud() {
+ TagCloudViewer viewer = new TagCloudViewer(cloud);
+ TagCloud cloud = viewer.getCloud();
+ Assert.assertNotNull(cloud);
+ Assert.assertEquals(this.cloud, cloud);
+ Assert.assertTrue(viewer.getSelection() != null);
+ Assert.assertTrue(viewer.getSelection().isEmpty());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidLabelProvider() {
+ TagCloudViewer viewer = new TagCloudViewer(cloud);
+ viewer.setLabelProvider(null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidLabelProvider2() {
+ TagCloudViewer viewer = new TagCloudViewer(cloud);
+ viewer.setLabelProvider(new BaseLabelProvider());
+ }
+
+ @Test
+ public void testValidLabelProvider() {
+ TagCloudViewer viewer = new TagCloudViewer(cloud);
+ TestLabelProvider labelProvider = new TestLabelProvider();
+ viewer.setLabelProvider(labelProvider);
+ Assert.assertEquals(labelProvider, viewer.getLabelProvider());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidContentProvider() {
+ TagCloudViewer viewer = new TagCloudViewer(cloud);
+ viewer.setContentProvider(null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidContentProvider2() {
+ TagCloudViewer viewer = new TagCloudViewer(cloud);
+ viewer.setContentProvider(new IContentProvider() {
+ public void inputChanged(Viewer viewer, Object oldInput,
+ Object newInput) {
+ }
+
+ public void dispose() {
+ }
+ });
+ }
+
+ private static class ListContentProvider implements ITreeContentProvider {
+
+ public void dispose() {
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ }
+
+ public Object[] getElements(Object inputElement) {
+ return ((List<?>) inputElement).toArray();
+ }
+
+ public Object[] getChildren(Object parentElement) {
+ return null;
+ }
+
+ public Object getParent(Object element) {
+ return null;
+ }
+
+ public boolean hasChildren(Object element) {
+ return false;
+ }
+
+ }
+
+ @Test
+ public void testValidContentProvider() {
+ TagCloudViewer viewer = new TagCloudViewer(cloud);
+ ListContentProvider provider = new ListContentProvider();
+ viewer.setContentProvider(provider);
+ Assert.assertEquals(provider, viewer.getContentProvider());
+ }
+
+ @Test
+ public void testValidLabelAsignment() {
+ TagCloudViewer viewer = new TagCloudViewer(cloud);
+ ListContentProvider provider = new ListContentProvider();
+ viewer.setContentProvider(provider);
+ TestLabelProvider labelProvider = new TestLabelProvider();
+ viewer.setLabelProvider(labelProvider);
+ List<String> data = new ArrayList<String>();
+ data.add("Hello");
+ data.add("World");
+ viewer.setInput(data);
+ List<Word> words = viewer.getCloud().getWords();
+ for (Word word : words) {
+ Assert.assertEquals(TestLabelProvider.COLOR, word.getColor());
+ for (int i = 0; i < TestLabelProvider.FONT_DATA.length; i++) {
+ Assert.assertEquals(TestLabelProvider.FONT_DATA[i],
+ word.getFontData()[i]);
+ }
+ Assert.assertEquals(TestLabelProvider.ANGLE, word.angle);
+ Assert.assertEquals(TestLabelProvider.WEIGHT, word.weight);
+ Assert.assertTrue(word.x != 0);
+ Assert.assertTrue(word.y != 0);
+ Assert.assertTrue(word.width != 0);
+ Assert.assertTrue(word.height != 0);
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidLayouter() {
+ TagCloudViewer viewer = new TagCloudViewer(cloud);
+ viewer.setLayouter(null);
+ }
+
+ @Test
+ public void testValidLayouter() {
+ TagCloudViewer viewer = new TagCloudViewer(cloud);
+ DefaultLayouter layouter = new DefaultLayouter(5, 5);
+ viewer.setLayouter(layouter);
+ Assert.assertEquals(layouter, viewer.getLayouter());
+ }
+
+}
diff --git a/org.eclipse.zest.tests/src/org/eclipse/zest/tests/cloudio/TestLabelProvider.java b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/cloudio/TestLabelProvider.java
new file mode 100644
index 0000000..6b37b1d
--- /dev/null
+++ b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/cloudio/TestLabelProvider.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stephan Schwiebert. 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
+ * <p/>
+ * Contributors: Stephan Schwiebert - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.zest.tests.cloudio;
+
+import org.eclipse.jface.viewers.BaseLabelProvider;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.zest.cloudio.ICloudLabelProvider;
+
+public class TestLabelProvider extends BaseLabelProvider implements
+ ICloudLabelProvider {
+
+ public static final double WEIGHT = 0.987D;
+ public static final float ANGLE = 12.34F;
+ public static Color COLOR = new Color(Display.getDefault(), new RGB(100,
+ 100, 100));
+ public static FontData[] FONT_DATA = Display.getDefault().getShells()[0]
+ .getFont().getFontData();
+
+ public String getLabel(Object element) {
+ return element.toString();
+ }
+
+ public double getWeight(Object element) {
+ return WEIGHT;
+ }
+
+ public Color getColor(Object element) {
+ return COLOR;
+ }
+
+ public FontData[] getFontData(Object element) {
+ return FONT_DATA.clone();
+ }
+
+ public float getAngle(Object element) {
+ return ANGLE;
+ }
+
+ public String getToolTip(Object element) {
+ return getLabel(element);
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index a05237d..61aee84 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,6 +38,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<modules>
+ <module>org.eclipse.zest.cloudio</module>
<module>org.eclipse.zest.core</module>
<module>org.eclipse.zest.jface</module>
<module>org.eclipse.zest.layouts</module>