initial import
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/.classpath b/bundles/org.eclipse.rap.rwt.custom.demo/.classpath
new file mode 100644
index 0000000..2fbb7a2
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/.project b/bundles/org.eclipse.rap.rwt.custom.demo/.project
new file mode 100644
index 0000000..9233b75
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.rap.rwt.custom.demo</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/bundles/org.eclipse.rap.rwt.custom.demo/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.rap.rwt.custom.demo/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..4cd99ad
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Wed Nov 04 14:37:25 CET 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
+org.eclipse.jdt.core.compiler.source=1.3
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/META-INF/MANIFEST.MF b/bundles/org.eclipse.rap.rwt.custom.demo/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..842b380
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/META-INF/MANIFEST.MF
@@ -0,0 +1,8 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Demo
+Bundle-SymbolicName: org.eclipse.rap.rwt.custom.demo;singleton:=true
+Bundle-Version: 1.0.0.qualifier
+Require-Bundle: org.eclipse.rap.ui;bundle-version="1.3.0",
+ org.eclipse.rap.rwt.custom;bundle-version="0.0.0"
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/build.properties b/bundles/org.eclipse.rap.rwt.custom.demo/build.properties
new file mode 100644
index 0000000..e4addeb
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/build.properties
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ theme/
diff --git "a/bundles/org.eclipse.rap.rwt.custom.demo/launch/SpreadSheet Demo \050Profiled\051.launch" "b/bundles/org.eclipse.rap.rwt.custom.demo/launch/SpreadSheet Demo \050Profiled\051.launch"
new file mode 100644
index 0000000..41fbd1a
--- /dev/null
+++ "b/bundles/org.eclipse.rap.rwt.custom.demo/launch/SpreadSheet Demo \050Profiled\051.launch"
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.rap.ui.launch.RAPLauncher">
+<booleanAttribute key="append.args" value="true"/>
+<booleanAttribute key="automaticAdd" value="false"/>
+<booleanAttribute key="automaticValidate" value="true"/>
+<stringAttribute key="bootstrap" value=""/>
+<stringAttribute key="browserMode" value="INTERNAL"/>
+<stringAttribute key="checked" value="[NONE]"/>
+<booleanAttribute key="clearConfig" value="true"/>
+<stringAttribute key="configLocation" value="${workspace_loc}/.metadata/.plugins/org.eclipse.pde.core/SpreadSheet Demo (Profiled)"/>
+<booleanAttribute key="default_auto_start" value="true"/>
+<intAttribute key="default_start_level" value="4"/>
+<stringAttribute key="entryPoint" value="default"/>
+<booleanAttribute key="includeOptional" value="false"/>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -console -consolelog"/>
+<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-agentlib:"C:\\Program Files\\YourKit Java Profiler 8.0.5\\bin\\win32\\yjpagent" -Declipse.ignoreApp=true -Dosgi.noShutdown=true -Xmx512m -Dorg.eclipse.equinox.http.jetty.context.sessioninactiveinterval=360"/>
+<stringAttribute key="org.eclipse.rap.launch.browserMode" value="INTERNAL"/>
+<stringAttribute key="org.eclipse.rap.launch.entryPoint" value="spreadsheet"/>
+<stringAttribute key="org.eclipse.rap.launch.libraryVariant" value="STANDARD"/>
+<stringAttribute key="org.eclipse.rap.launch.logLevel" value="OFF"/>
+<booleanAttribute key="org.eclipse.rap.launch.openBrowser" value="true"/>
+<intAttribute key="org.eclipse.rap.launch.port" value="2011"/>
+<stringAttribute key="org.eclipse.rap.launch.servletName" value="spreadsheet"/>
+<booleanAttribute key="org.eclipse.rap.launch.terminatePrevious" value="true"/>
+<booleanAttribute key="org.eclipse.rap.launch.useManualPort" value="true"/>
+<stringAttribute key="pde.version" value="3.3"/>
+<booleanAttribute key="show_selected_only" value="false"/>
+<stringAttribute key="target_bundles" value="org.mortbay.jetty.server@default:default,org.eclipse.core.commands@default:default,org.eclipse.equinox.app@default:default,org.eclipse.equinox.http.servlet@default:default,org.eclipse.equinox.http.jetty@default:default,org.eclipse.help.base@default:default,org.eclipse.core.databinding.observable@default:default,org.eclipse.core.databinding@default:default,org.eclipse.core.contenttype@default:default,org.eclipse.equinox.http.registry@default:default,org.eclipse.core.resources@default:default,org.eclipse.core.expressions@default:default,org.eclipse.equinox.jsp.jasper.registry@default:default,org.eclipse.osgi@-1:true,org.apache.lucene@default:default,org.apache.jasper@default:default,org.apache.commons.el@default:default,org.eclipse.core.filesystem@default:default,org.eclipse.core.jobs@default:default,javax.servlet.jsp@default:default,org.eclipse.help.webapp@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.help.appserver@default:default,org.eclipse.equinox.common@2:true,org.mortbay.jetty.util@default:default,org.eclipse.core.runtime@default:true,org.eclipse.osgi.services@default:default,org.eclipse.equinox.jsp.jasper@default:default,com.ibm.icu@default:default,org.eclipse.equinox.preferences@default:default,javax.servlet@default:default,org.eclipse.core.variables@default:default,org.eclipse.help@default:default,org.eclipse.core.databinding.property@default:default,org.apache.lucene.analysis@default:default,org.apache.commons.logging@default:default,org.eclipse.ant.core@default:default"/>
+<booleanAttribute key="terminatePrevious" value="true"/>
+<booleanAttribute key="tracing" value="false"/>
+<booleanAttribute key="useDefaultConfigArea" value="true"/>
+<stringAttribute key="workspace_bundles" value="org.eclipse.rap.rwt@default:default,org.eclipse.rap.jface.databinding@default:default,org.eclipse.rap.rwt.custom.demo@default:default,org.eclipse.rap.ui.views@default:default,org.eclipse.rap.jface@default:default,org.eclipse.rap.ui.workbench@default:default,org.eclipse.rap.ui@default:default,org.eclipse.rap.rwt.q07@default:false,org.eclipse.rap.rwt.custom@default:default"/>
+</launchConfiguration>
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/launch/SpreadSheet Demo.launch b/bundles/org.eclipse.rap.rwt.custom.demo/launch/SpreadSheet Demo.launch
new file mode 100644
index 0000000..65636b3
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/launch/SpreadSheet Demo.launch
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.rap.ui.launch.RAPLauncher">
+<booleanAttribute key="append.args" value="true"/>
+<booleanAttribute key="automaticAdd" value="false"/>
+<booleanAttribute key="automaticValidate" value="true"/>
+<stringAttribute key="bootstrap" value=""/>
+<stringAttribute key="browserMode" value="INTERNAL"/>
+<stringAttribute key="checked" value="[NONE]"/>
+<booleanAttribute key="clearConfig" value="true"/>
+<stringAttribute key="configLocation" value="${workspace_loc}/.metadata/.plugins/org.eclipse.pde.core/SpreadSheet Demo"/>
+<booleanAttribute key="default_auto_start" value="true"/>
+<intAttribute key="default_start_level" value="4"/>
+<stringAttribute key="entryPoint" value="default"/>
+<booleanAttribute key="includeOptional" value="false"/>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -console -consolelog"/>
+<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Declipse.ignoreApp=true -Dosgi.noShutdown=true -Xmx512m -Dorg.eclipse.equinox.http.jetty.context.sessioninactiveinterval=360"/>
+<stringAttribute key="org.eclipse.rap.launch.browserMode" value="INTERNAL"/>
+<stringAttribute key="org.eclipse.rap.launch.entryPoint" value="spreadsheet"/>
+<stringAttribute key="org.eclipse.rap.launch.libraryVariant" value="STANDARD"/>
+<stringAttribute key="org.eclipse.rap.launch.logLevel" value="OFF"/>
+<booleanAttribute key="org.eclipse.rap.launch.openBrowser" value="true"/>
+<intAttribute key="org.eclipse.rap.launch.port" value="2010"/>
+<stringAttribute key="org.eclipse.rap.launch.servletName" value="spreadsheet"/>
+<booleanAttribute key="org.eclipse.rap.launch.terminatePrevious" value="true"/>
+<booleanAttribute key="org.eclipse.rap.launch.useManualPort" value="true"/>
+<stringAttribute key="pde.version" value="3.3"/>
+<booleanAttribute key="show_selected_only" value="false"/>
+<stringAttribute key="target_bundles" value="org.mortbay.jetty.server@default:default,org.eclipse.core.commands@default:default,org.eclipse.equinox.app@default:default,org.eclipse.equinox.http.servlet@default:default,org.eclipse.equinox.http.jetty@default:default,org.eclipse.help.base@default:default,org.eclipse.core.databinding.observable@default:default,org.eclipse.core.databinding@default:default,org.eclipse.core.contenttype@default:default,org.eclipse.equinox.http.registry@default:default,org.eclipse.core.resources@default:default,org.eclipse.core.expressions@default:default,org.eclipse.equinox.jsp.jasper.registry@default:default,org.eclipse.osgi@-1:true,org.apache.lucene@default:default,org.apache.jasper@default:default,org.apache.commons.el@default:default,org.eclipse.core.filesystem@default:default,org.eclipse.core.jobs@default:default,javax.servlet.jsp@default:default,org.eclipse.help.webapp@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.help.appserver@default:default,org.eclipse.equinox.common@2:true,org.mortbay.jetty.util@default:default,org.eclipse.core.runtime@default:true,org.eclipse.osgi.services@default:default,org.eclipse.equinox.jsp.jasper@default:default,com.ibm.icu@default:default,org.eclipse.equinox.preferences@default:default,javax.servlet@default:default,org.eclipse.core.variables@default:default,org.eclipse.help@default:default,org.eclipse.core.databinding.property@default:default,org.apache.lucene.analysis@default:default,org.apache.commons.logging@default:default,org.eclipse.ant.core@default:default"/>
+<booleanAttribute key="terminatePrevious" value="true"/>
+<booleanAttribute key="tracing" value="false"/>
+<booleanAttribute key="useDefaultConfigArea" value="true"/>
+<stringAttribute key="workspace_bundles" value="org.eclipse.rap.rwt@default:default,org.eclipse.rap.jface.databinding@default:default,org.eclipse.rap.rwt.custom.demo@default:default,org.eclipse.rap.ui.views@default:default,org.eclipse.rap.jface@default:default,org.eclipse.rap.ui.workbench@default:default,org.eclipse.rap.ui@default:default,org.eclipse.rap.rwt.q07@default:false,org.eclipse.rap.rwt.custom@default:default"/>
+</launchConfiguration>
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/plugin.xml b/bundles/org.eclipse.rap.rwt.custom.demo/plugin.xml
new file mode 100644
index 0000000..2d2cc69
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/plugin.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension
+ point="org.eclipse.rap.ui.branding">
+ <branding
+ defaultEntrypointId="org.eclipse.rap.rwt.custom.demo.spreadsheet"
+ id="org.eclipse.rap.rwt.custom.demo.spreadsheet"
+ servletName="spreadsheet"
+ themeId="org.eclipse.rap.rwt.custom.demo.theme"
+ title="Spreadsheet Demo">
+ </branding>
+ </extension>
+ <extension
+ point="org.eclipse.rap.ui.entrypoint">
+ <entrypoint
+ class="org.eclipse.rap.rwt.custom.demo.spreadsheet.SpreadSheetEntryPoint"
+ id="org.eclipse.rap.rwt.custom.demo.spreadsheet"
+ parameter="spreadsheet">
+ </entrypoint>
+ </extension>
+ <extension
+ point="org.eclipse.rap.ui.themes">
+ <theme
+ file="theme/theme.css"
+ id="org.eclipse.rap.rwt.custom.demo.theme"
+ name="Custom Demo Theme">
+ </theme>
+ </extension>
+ <extension
+ point="org.eclipse.ui.perspectives">
+ <perspective
+ class="org.eclipse.rap.rwt.custom.demo.spreadsheet.SpreadSheetPerspective"
+ id="org.eclipse.rap.rwt.custom.demo.perspectives.spreadsheet"
+ name="Spreadsheet Perspective">
+ </perspective>
+ </extension>
+ <extension
+ point="org.eclipse.ui.views">
+ <view
+ class="org.eclipse.rap.rwt.custom.demo.spreadsheet.SpreadSheetView"
+ id="org.eclipse.rap.rwt.custom.demo.views.spreadsheet"
+ name="Spreadsheet"
+ restorable="true">
+ </view>
+ </extension>
+</plugin>
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/src/org/eclipse/rap/rwt/custom/demo/spreadsheet/SpreadSheetEntryPoint.java b/bundles/org.eclipse.rap.rwt.custom.demo/src/org/eclipse/rap/rwt/custom/demo/spreadsheet/SpreadSheetEntryPoint.java
new file mode 100644
index 0000000..d9485a0
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/src/org/eclipse/rap/rwt/custom/demo/spreadsheet/SpreadSheetEntryPoint.java
@@ -0,0 +1,15 @@
+package org.eclipse.rap.rwt.custom.demo.spreadsheet;
+
+import org.eclipse.rwt.lifecycle.IEntryPoint;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+
+
+public class SpreadSheetEntryPoint implements IEntryPoint {
+
+ public int createUI() {
+ Display display = PlatformUI.createDisplay();
+ SpreadSheetWorkbenchAdvisor advisor = new SpreadSheetWorkbenchAdvisor();
+ return PlatformUI.createAndRunWorkbench( display, advisor );
+ }
+}
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/src/org/eclipse/rap/rwt/custom/demo/spreadsheet/SpreadSheetPerspective.java b/bundles/org.eclipse.rap.rwt.custom.demo/src/org/eclipse/rap/rwt/custom/demo/spreadsheet/SpreadSheetPerspective.java
new file mode 100644
index 0000000..071f706
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/src/org/eclipse/rap/rwt/custom/demo/spreadsheet/SpreadSheetPerspective.java
@@ -0,0 +1,18 @@
+package org.eclipse.rap.rwt.custom.demo.spreadsheet;
+
+import org.eclipse.ui.*;
+
+
+public class SpreadSheetPerspective implements IPerspectiveFactory {
+
+ public void createInitialLayout( final IPageLayout layout ) {
+ String editorArea = layout.getEditorArea();
+ layout.setEditorAreaVisible( false );
+ String viewId = "org.eclipse.rap.rwt.custom.demo.views.spreadsheet";
+ layout.addStandaloneView( viewId,
+ false,
+ IPageLayout.LEFT,
+ 0.75f,
+ editorArea );
+ }
+}
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/src/org/eclipse/rap/rwt/custom/demo/spreadsheet/SpreadSheetView.java b/bundles/org.eclipse.rap.rwt.custom.demo/src/org/eclipse/rap/rwt/custom/demo/spreadsheet/SpreadSheetView.java
new file mode 100644
index 0000000..05efd42
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/src/org/eclipse/rap/rwt/custom/demo/spreadsheet/SpreadSheetView.java
@@ -0,0 +1,25 @@
+package org.eclipse.rap.rwt.custom.demo.spreadsheet;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SpreadSheet;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.part.ViewPart;
+
+
+public class SpreadSheetView extends ViewPart {
+
+ private SpreadSheet spreadSheet;
+
+ public void createPartControl( final Composite parent ) {
+ spreadSheet = new SpreadSheet( parent, SWT.BORDER );
+// for( int i = 0; i < 100; i++ ) {
+// for( int j = 0; j < 100; j++ ) {
+// spreadSheet.setText( i + "_" + j, i, j );
+// }
+// }
+ }
+
+ public void setFocus() {
+ spreadSheet.setFocus();
+ }
+}
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/src/org/eclipse/rap/rwt/custom/demo/spreadsheet/SpreadSheetWorkbenchAdvisor.java b/bundles/org.eclipse.rap.rwt.custom.demo/src/org/eclipse/rap/rwt/custom/demo/spreadsheet/SpreadSheetWorkbenchAdvisor.java
new file mode 100644
index 0000000..dc41035
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/src/org/eclipse/rap/rwt/custom/demo/spreadsheet/SpreadSheetWorkbenchAdvisor.java
@@ -0,0 +1,17 @@
+package org.eclipse.rap.rwt.custom.demo.spreadsheet;
+
+import org.eclipse.ui.application.*;
+
+
+public class SpreadSheetWorkbenchAdvisor extends WorkbenchAdvisor {
+
+ public String getInitialWindowPerspectiveId() {
+ return "org.eclipse.rap.rwt.custom.demo.perspectives.spreadsheet";
+ }
+
+ public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(
+ final IWorkbenchWindowConfigurer configurer )
+ {
+ return new SpreadSheetWorkbenchWindowAdvisor( configurer );
+ }
+}
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/src/org/eclipse/rap/rwt/custom/demo/spreadsheet/SpreadSheetWorkbenchWindowAdvisor.java b/bundles/org.eclipse.rap.rwt.custom.demo/src/org/eclipse/rap/rwt/custom/demo/spreadsheet/SpreadSheetWorkbenchWindowAdvisor.java
new file mode 100644
index 0000000..f2cb7e8
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/src/org/eclipse/rap/rwt/custom/demo/spreadsheet/SpreadSheetWorkbenchWindowAdvisor.java
@@ -0,0 +1,32 @@
+package org.eclipse.rap.rwt.custom.demo.spreadsheet;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
+import org.eclipse.ui.application.WorkbenchWindowAdvisor;
+
+
+public class SpreadSheetWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor {
+
+ public SpreadSheetWorkbenchWindowAdvisor(
+ final IWorkbenchWindowConfigurer configurer )
+ {
+ super( configurer );
+ }
+
+
+ public void preWindowOpen() {
+ IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
+ Rectangle bounds = Display.getCurrent().getBounds();
+ configurer.setInitialSize( new Point( bounds.width, bounds.height ) );
+ configurer.setShowCoolBar( false );
+ configurer.setShowPerspectiveBar( false );
+ configurer.setShowProgressIndicator( false );
+ configurer.setShowStatusLine( false );
+ configurer.setShowMenuBar( false );
+ configurer.setTitle( "Spreadsheet Demo" );
+ configurer.setShellStyle( SWT.TITLE | SWT.RESIZE );
+ }
+}
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-grayed-hover.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-grayed-hover.png
new file mode 100644
index 0000000..7297274
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-grayed-hover.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-grayed.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-grayed.png
new file mode 100644
index 0000000..08ed526
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-grayed.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-selected-hover.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-selected-hover.png
new file mode 100644
index 0000000..472a085
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-selected-hover.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-selected.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-selected.png
new file mode 100644
index 0000000..69d2b70
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-selected.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-unselected-hover.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-unselected-hover.png
new file mode 100644
index 0000000..2193e15
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-unselected-hover.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-unselected.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-unselected.png
new file mode 100644
index 0000000..eb270d2
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/check-unselected.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/chevron-down-small.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/chevron-down-small.png
new file mode 100644
index 0000000..0cc88fe
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/chevron-down-small.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/chevron-down.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/chevron-down.png
new file mode 100644
index 0000000..e3a1adc
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/chevron-down.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/chevron-up-small.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/chevron-up-small.png
new file mode 100644
index 0000000..c13542d
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/chevron-up-small.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/expanditem-collapse.gif b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/expanditem-collapse.gif
new file mode 100644
index 0000000..5ddb8b8
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/expanditem-collapse.gif
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/expanditem-expand-hover.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/expanditem-expand-hover.png
new file mode 100644
index 0000000..98336d2
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/expanditem-expand-hover.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/expanditem-expand.gif b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/expanditem-expand.gif
new file mode 100644
index 0000000..307395c
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/expanditem-expand.gif
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/radio-selected-hover.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/radio-selected-hover.png
new file mode 100644
index 0000000..6c7c702
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/radio-selected-hover.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/radio-selected.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/radio-selected.png
new file mode 100644
index 0000000..44a72e6
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/radio-selected.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/radio-unselected-hover.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/radio-unselected-hover.png
new file mode 100644
index 0000000..51e6e90
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/radio-unselected-hover.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/radio-unselected.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/radio-unselected.png
new file mode 100644
index 0000000..54f8268
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/radio-unselected.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-active-background.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-active-background.png
new file mode 100644
index 0000000..96798d4
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-active-background.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-close-hover.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-close-hover.png
new file mode 100644
index 0000000..2df60b3
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-close-hover.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-close.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-close.png
new file mode 100644
index 0000000..13de98d
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-close.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-inactive-background.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-inactive-background.png
new file mode 100644
index 0000000..a146a32
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-inactive-background.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-max-hover.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-max-hover.png
new file mode 100644
index 0000000..6958bf3
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-max-hover.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-max.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-max.png
new file mode 100644
index 0000000..24bdbc4
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-max.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-min-hover.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-min-hover.png
new file mode 100644
index 0000000..ccd4958
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-min-hover.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-min.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-min.png
new file mode 100644
index 0000000..7230432
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/shell-min.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_active_close_active.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_active_close_active.png
new file mode 100644
index 0000000..d0b4147
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_active_close_active.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_active_close_active_hover.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_active_close_active_hover.png
new file mode 100644
index 0000000..b4771f3
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_active_close_active_hover.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_inactive_close_active.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_inactive_close_active.png
new file mode 100644
index 0000000..ae5a824
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_inactive_close_active.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_inactive_close_active_hover.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_inactive_close_active_hover.png
new file mode 100644
index 0000000..5737864
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_inactive_close_active_hover.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_overflow_active.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_overflow_active.png
new file mode 100644
index 0000000..d270caa
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_overflow_active.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_overflow_active_hover.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_overflow_active_hover.png
new file mode 100644
index 0000000..1beb59a
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_overflow_active_hover.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_overflow_inactive.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_overflow_inactive.png
new file mode 100644
index 0000000..1ea81f9
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_overflow_inactive.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_overflow_inactive_hover.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_overflow_inactive_hover.png
new file mode 100644
index 0000000..0b9f3a5
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/stack_tab_overflow_inactive_hover.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/toolbarButtonBg.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/toolbarButtonBg.png
new file mode 100644
index 0000000..aed08df
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/toolbarButtonBg.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/toolbar_overflow.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/toolbar_overflow.png
new file mode 100644
index 0000000..1958365
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/toolbar_overflow.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/toolbar_overflow_active.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/toolbar_overflow_active.png
new file mode 100644
index 0000000..d856c24
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/toolbar_overflow_active.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/toolbar_overflow_hover.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/toolbar_overflow_hover.png
new file mode 100644
index 0000000..57ec1ef
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/toolbar_overflow_hover.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/toolbar_overflow_hover_active.png b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/toolbar_overflow_hover_active.png
new file mode 100644
index 0000000..75bcb42
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/icons/toolbar_overflow_hover_active.png
Binary files differ
diff --git a/bundles/org.eclipse.rap.rwt.custom.demo/theme/theme.css b/bundles/org.eclipse.rap.rwt.custom.demo/theme/theme.css
new file mode 100644
index 0000000..4e466f7
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom.demo/theme/theme.css
@@ -0,0 +1,501 @@
+
+* {
+ color: #4a4a4a;
+ background-color: white;
+ font: 12px "Trebuchet MS", Arial, Helvetica, sans-serif;
+}
+
+*:disabled {
+ color: #CFCFCF;
+}
+
+*[BORDER] {
+ border: 1px solid #C1C1C1;
+}
+
+
+/* Button */
+
+Button[PUSH], Button[TOGGLE], Button[BORDER] {
+ border: 1px solid #A4A4A4;
+ border-radius: 3px;
+ padding: 2px 5px;
+ /* background-color: #f5f6f6; */
+ background-image: gradient(
+ linear, left top, left bottom,
+ from( #ffffff ),
+ color-stop( 48%, #f0f0f0 ),
+ color-stop( 52%, #e0e0e0 ),
+ to( #cccccc )
+ );
+}
+
+Button[PUSH]:pressed, Button[TOGGLE]:pressed {
+ background-image: gradient(
+ linear, left top, left bottom,
+ from( #e0e0e0 ),
+ color-stop( 52%, #e0e0e0 ),
+ to( #b0b0b0 )
+ );
+ padding: 4px 5px 2px 7px;
+}
+
+Button[TOGGLE]:selected {
+ /* background-color: #d3d3d3; */
+ background-image: gradient(
+ linear, left top, left bottom,
+ from( #e0e0e0 ),
+ color-stop( 52%, #e0e0e0 ),
+ to( #b0b0b0 )
+ );
+}
+
+Button:hover {
+ background-color: white;
+}
+
+Button.clearButton {
+ background-color: transparent;
+ background-image: none;
+ border: none;
+ padding: 2px;
+}
+
+Button.viewClose {
+ background-color: transparent;
+ background-image: url( /theme/icons/stack_tab_active_close_active.png );
+ border: none;
+ padding: 2px;
+}
+
+Button.viewClose:hover {
+ background-image: url( /theme/icons/stack_tab_active_close_active_hover.png );
+}
+
+Button.viewCloseInactive {
+ background-color: transparent;
+ background-image: url( /theme/icons/stack_tab_inactive_close_active.png );
+ border: none;
+}
+
+Button.viewCloseInactive:hover {
+ background-image: url( /theme/icons/stack_tab_inactive_close_active_hover.png );
+}
+
+Button.toolbarOverflowInactive {
+ background-color: transparent;
+ background-image: url( /theme/icons/toolbar_overflow.png );
+ border: none;
+ padding: 2px;
+}
+
+Button.toolbarOverflowInactive:hover {
+ background-image: url( /theme/icons/toolbar_overflow_hover.png );
+}
+
+Button.toolbarOverflowActive {
+ background-color: transparent;
+ background-image: url( /theme/icons/toolbar_overflow_active.png );
+ border: none;
+ padding: 2px;
+}
+
+Button.toolbarOverflowActive:hover {
+ background-image: url( /theme/icons/toolbar_overflow_hover_active.png );
+}
+
+Button.menuBar {
+ background-color: transparent;
+ background-image: none;
+ border: none;
+ color: rgb( 0, 89, 165 );
+ font: 13px Arial, Helvetica, sans-serif;
+}
+
+Button.coolBar {
+ background-color: transparent;
+ background-image: none;
+ border: none;
+ spacing: 6px;
+ color: rgb( 255, 255, 255 );
+ font: 12px "Trebuchet MS", Arial, Helvetica, sans-serif;
+}
+
+Button.coolBar:hover {
+ background-image: url( /theme/icons/toolbarButtonBg.png );
+}
+
+Button.coolBarPulldown {
+ background-color: transparent;
+ background-image: none;
+ border: none;
+ color: rgb( 255, 255, 255 );
+ font: 12px "Trebuchet MS", Arial, Helvetica, sans-serif;
+}
+
+Button.coolBarPulldown:hover {
+ background-image: url( /theme/icons/toolbarButtonBg.png );
+}
+
+Button.partActive {
+ background-color: transparent;
+ background-image: none;
+ border: none;
+ color: rgb( 252, 252, 252 );
+ font: 11px "Trebuchet MS", Arial, Helvetica, sans-serif;
+}
+
+Button.partInactive {
+ background-color: transparent;
+ background-image: none;
+ border: none;
+ color: rgb( 102, 102, 102 );
+ font: 11px "Trebuchet MS", Arial, Helvetica, sans-serif;
+}
+
+Button.inactivePerspective {
+ background-color: transparent;
+ background-image: none;
+ border: none;
+ color: rgb( 0, 80, 145 );
+}
+
+Button.tabOverflowActive {
+ background-color: transparent;
+ border: none;
+ padding: 2px;
+ background-image: url( /theme/icons/stack_tab_overflow_active.png );
+}
+
+Button.tabOverflowActive:hover {
+ background-image: url( /theme/icons/stack_tab_overflow_active_hover.png );
+}
+
+Button.tabOverflowInactive {
+ background-color: transparent;
+ border: none;
+ padding: 2px;
+ background-image: url( /theme/icons/stack_tab_overflow_inactive.png );
+}
+
+Button.tabOverflowInactive:hover {
+ background-image: url( /theme/icons/stack_tab_overflow_inactive_hover.png );
+}
+
+
+/* Shell */
+
+Shell-Titlebar {
+ color: white;
+ /* background-image: url( /theme/icons/shell-active-background.png ); */
+ background-image: gradient( linear, left top, left bottom,
+ from( #005fac ), to( #002092 ) );
+ padding: 2px 5px 2px;
+ margin: 0px;
+ height: 27px;
+ font: bold 14px Verdana, "Lucida Sans", Arial, Helvetica, sans-serif;
+ border: none;
+ border-radius: 5px 5px 0px 0px;
+}
+
+Shell-Titlebar:inactive {
+ color: #aaaaaa;
+ /* background-image: url( /theme/icons/shell-inactive-background.png ); */
+ background-image: gradient( linear, left top, left bottom,
+ from( #595959 ), to( #4b4b4b ) );
+}
+
+Shell {
+ background-color: white;
+}
+
+Shell[BORDER], Shell[TITLE] {
+ border: 1px solid #005092;
+ border-radius: 6px;
+ padding: 5px;
+}
+
+Shell[BORDER]:inactive, Shell[TITLE]:inactive {
+ border: 1px solid #4b4b4b;
+}
+
+Shell-CloseButton {
+ background-image: url( "/theme/icons/shell-close.png" );
+ margin: 0px 5px 0px 0px;
+}
+
+Shell-MaxButton {
+ background-image: url( "/theme/icons/shell-max.png" );
+ margin: 0px 6px 0px 0px;
+}
+
+Shell-MinButton {
+ background-image: url( "/theme/icons/shell-min.png" );
+ margin: 0px 6px 0px 0px;
+}
+
+Shell-CloseButton:hover {
+ background-image: url( "/theme/icons/shell-close-hover.png" );
+}
+
+Shell-MaxButton:hover {
+ background-image: url( "/theme/icons/shell-max-hover.png" );
+}
+
+Shell-MinButton:hover {
+ background-image: url( "/theme/icons/shell-min-hover.png" );
+}
+
+Shell.shellGray {
+ background-color: rgb( 235, 235, 235 );
+}
+
+Shell.toolbarLayer {
+ background-color: rgb( 255, 255, 255 );
+ border: none;
+}
+
+
+/* Table */
+
+Table-Column.overflow {
+ background-color: transparent;
+}
+
+TableItem.overflow {
+ background-color: transparent;
+}
+
+TableItem.overflow:even {
+ background-color: transparent;
+}
+
+Table-Column {
+ background-color: #f3f3f4;
+}
+
+Table-Cell {
+ spacing: 3px;
+ padding: 5px;
+}
+
+List-Item, TableItem {
+ background-color: transparent;
+}
+
+/*
+ disable alt row colors for Lists as they look weird inside examples' ExpandBar:
+ List-Item:even,
+ */
+TableItem:even {
+ background-color: #f3f3f4;
+}
+
+
+/* Check Buttons */
+
+Button-CheckIcon {
+ background-image: url( /theme/icons/check-unselected.png );
+}
+
+Button-CheckIcon:selected {
+ background-image: url( /theme/icons/check-selected.png );
+}
+
+Button-CheckIcon:grayed {
+ background-image: url( /theme/icons/check-grayed.png );
+}
+
+Button-CheckIcon:hover {
+ background-image: url( /theme/icons/check-unselected-hover.png );
+}
+
+Button-CheckIcon:selected:hover {
+ background-image: url( /theme/icons/check-selected-hover.png );
+}
+
+Button-CheckIcon:grayed:hover {
+ background-image: url( /theme/icons/check-grayed-hover.png );
+}
+
+
+/* Radio Buttons */
+
+Button-RadioIcon {
+ background-image: url( /theme/icons/radio-unselected.png );
+}
+
+Button-RadioIcon:selected {
+ background-image: url( /theme/icons/radio-selected.png );
+}
+
+Button-RadioIcon:hover {
+ background-image: url( /theme/icons/radio-unselected-hover.png );
+}
+
+Button-RadioIcon:selected:hover {
+ background-image: url( /theme/icons/radio-selected-hover.png );
+}
+
+
+/* Menu */
+
+Menu {
+ color: rgb( 0, 89, 165 );;
+ background-color: white;
+ border: 1px rgb( 0, 89, 165 );
+}
+
+
+/* Selection color */
+
+List-Item:selected, TableItem:selected, TreeItem:selected {
+ background-color: #D2D2D2;
+ color: #4a4a4a;
+}
+
+.menuBarPopup {
+ border: none;
+}
+
+Label.menuBorder {
+ background-color: rgb( 221, 221, 221 );
+}
+
+Label.stackBorder {
+ background-color: rgb( 235, 235, 235 );
+}
+
+Label.standaloneView {
+ background-color: transparent;
+ border: none;
+ color: rgb( 102, 102, 102 );
+ font: 11px "Trebuchet MS", Arial, Helvetica, sans-serif;
+}
+
+Tree {
+ border: none;
+}
+
+.compGray {
+ background-color: rgb( 235, 235, 235 );
+}
+
+.compTrans {
+ background-color: transparent;
+}
+
+.partBorder {
+ border: rgb( 235, 0, 0 );
+}
+
+.compTransNoBorder {
+ border: none;
+ background-color: rgb( 252, 252, 252 );
+}
+
+List, Text, Combo, Tree, Table {
+ background-color: rgb( 252, 252, 252 );
+}
+
+.formKey {
+ font: 11px "Trebuchet MS", Arial, Helvetica, sans-serif;
+ color: gray;
+}
+
+.formValue {
+ font: bold 14px "Trebuchet MS", Arial, Helvetica, sans-serif;
+}
+
+
+/* Expand Bar */
+
+ExpandBar {
+ color: white;
+}
+
+ExpandItem-Header {
+ background-color: #005397;
+}
+
+ExpandItem-Button {
+ background-image: url( /theme/icons/expanditem-expand.gif );
+}
+
+ExpandItem-Button:hover {
+ background-image: url( /theme/icons/expanditem-expand-hover.png );
+}
+
+ExpandItem-Button:expanded {
+ background-image: url( /theme/icons/expanditem-collapse.gif );
+}
+
+
+/* Text, Combo, Spinner, DateTime */
+
+/* Combo always shows a border, the BORDER style flag is ignored by SWT */
+Text[BORDER], Combo, CCombo[BORDER], Spinner[BORDER], DateTime[BORDER] {
+ border: 1px solid #c1c1c1;
+ border-radius: 2px;
+}
+
+Combo-Button, CCombo-Button, Spinner-UpButton, Spinner-DownButton, DateTime-UpButton, DateTime-DownButton {
+ background-color: transparent;
+ border: none;
+}
+
+Spinner-UpButton, DateTime-UpButton {
+ background-image: url( /theme/icons/chevron-up-small.png );
+}
+
+Spinner-DownButton, DateTime-DownButton {
+ background-image: url( /theme/icons/chevron-down-small.png );
+ /* background-color: #c1c1c1; */
+}
+
+Combo-Button:hover, CCombo-Button:hover,
+Spinner-UpButton:hover, Spinner-DownButton:hover,
+DateTime-UpButton:hover, DateTime-DownButton:hover {
+ background-color: #e2e2e2;
+}
+
+Combo-Button, CCombo-Button {
+ background-image: url( /theme/icons/chevron-down.png );
+}
+
+DateTime-Field:selected, DateTime-Calendar-Day:selected {
+ background-color: #D2D2D2;
+ color: #4a4a4a;
+}
+
+DateTime-Calendar-Day:otherMonth {
+ background-color: transparent;
+ color: rgb( 128, 128, 128 );
+}
+
+
+/* Group */
+
+Group-Frame {
+ padding: 5px;
+ border-radius: 5px 5px 0px 0px;
+}
+
+
+/* Link */
+
+Link-Hyperlink {
+ color: #195d94;
+}
+
+
+/* Progress Bar */
+
+ProgressBar {
+ background-color: #ffffff;
+ border: 1px solid #747a81;
+}
+
+ProgressBar-Indicator {
+ background-color: #7bb0db;
+}
diff --git a/bundles/org.eclipse.rap.rwt.custom/.classpath b/bundles/org.eclipse.rap.rwt.custom/.classpath
new file mode 100644
index 0000000..2fbb7a2
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.rap.rwt.custom/.project b/bundles/org.eclipse.rap.rwt.custom/.project
new file mode 100644
index 0000000..8bb70a0
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.rap.rwt.custom</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/bundles/org.eclipse.rap.rwt.custom/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.rap.rwt.custom/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..711c15d
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Fri Aug 07 21:31:03 CEST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
+org.eclipse.jdt.core.compiler.source=1.3
diff --git a/bundles/org.eclipse.rap.rwt.custom/META-INF/MANIFEST.MF b/bundles/org.eclipse.rap.rwt.custom/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..7f29cd4
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom/META-INF/MANIFEST.MF
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Bundle-SymbolicName: org.eclipse.rap.rwt.custom
+Bundle-Version: 0.0.0.qualifier
+Bundle-Vendor: %Bundle-Vendor
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
+Export-Package: org.eclipse.swt.custom
+Require-Bundle: org.eclipse.rap.rwt;bundle-version="1.3.0"
diff --git a/bundles/org.eclipse.rap.rwt.custom/about.html b/bundles/org.eclipse.rap.rwt.custom/about.html
new file mode 100644
index 0000000..248f88c
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>August 08, 2009</p>
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, "Program" will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party ("Redistributor") and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
diff --git a/bundles/org.eclipse.rap.rwt.custom/build.properties b/bundles/org.eclipse.rap.rwt.custom/build.properties
new file mode 100644
index 0000000..34d2e4d
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/bundles/org.eclipse.rap.rwt.custom/plugin.properties b/bundles/org.eclipse.rap.rwt.custom/plugin.properties
new file mode 100644
index 0000000..7ac0385
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom/plugin.properties
@@ -0,0 +1,3 @@
+
+Bundle-Name = RWT Custom
+Bundle-Vendor = Eclipse.org - RAP
diff --git a/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/CellController.java b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/CellController.java
new file mode 100644
index 0000000..5845dcf
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/CellController.java
@@ -0,0 +1,360 @@
+// Created on 16.08.2009
+package org.eclipse.swt.custom;
+
+import java.util.*;
+
+import org.eclipse.rwt.graphics.Graphics;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SpreadSheetModel.Adapter;
+import org.eclipse.swt.custom.SpreadSheetModel.Event;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.*;
+
+
+class CellController {
+ static final int DEFAULT_GRID_LINE_BREADTH = 1;
+ private static final String CELL_CONTROL_MARKER
+ = SpreadSheetLayout.class.getName() + "#Cell";
+
+ private final Composite spreadSheet;
+ private final Composite cellContainer;
+ private final SpreadSheetModel model;
+ private Label[] gridLines;
+
+
+ private final class ModelAdapter extends Adapter {
+ public void textChanged( final Event evt ) {
+ int row = evt.rowIndex;
+ int column = evt.columnIndex;
+ CellPosition position
+ = new CellPosition( row, column );
+ setText( evt.text, position );
+ }
+ }
+
+
+ CellController( final Composite spreadSheet,
+ final SpreadSheetModel model )
+ {
+ SpreadSheetUtils.checkNotNull( spreadSheet, "spreadSheet" );
+ SpreadSheetUtils.checkNotNull( model, "model" );
+
+ this.spreadSheet = spreadSheet;
+ cellContainer = new Composite( spreadSheet, SWT.NONE );
+ cellContainer.setBackground( spreadSheet.getBackground() );
+ this.model = model;
+ this.gridLines = new Label[ 0 ];
+ model.addListener( new ModelAdapter() );
+ }
+
+ void adjustCellControls( ) {
+ // TODO [fappel]: check the algorithm in relation to memory consumption
+ // and performance
+ cellContainer.moveBelow( null );
+ computeCellContainerBounds();
+ adjustGrid();
+ adjustCellLayoutData();
+ Map buffer = bufferExistingCells();
+ createMissingCells( buffer );
+ disposeOfSpareCells( buffer );
+ Rectangle clientArea = cellContainer.getClientArea();
+ Control[] children = cellContainer.getChildren();
+ for( int i = 0; i < children.length; i++ ) {
+ SpreadSheetUtils.computeBounds( clientArea, children[ i ], model );
+ }
+ updateText();
+ }
+
+ private void adjustGrid() {
+ adjustGridControlNumber();
+
+ int columnOffset = model.getColumnOffset();
+ int rowOffset = model.getRowOffset();
+ int xBase = SpreadSheetUtils.calcXPosition( 0, 0, columnOffset, model );
+ int yBase = SpreadSheetUtils.calcYPosition( 0, 0, rowOffset, model );
+ int gridLineIndex = 0;
+
+ for( int i = 1; i < model.getVisibleRows(); i++ ) {
+ int index = rowOffset + i;
+ int yPos = yBase;
+ yPos = SpreadSheetUtils.calcYPosition( yPos, rowOffset, index, model )
+ - DEFAULT_GRID_LINE_BREADTH;
+ int width = cellContainer.getClientArea().width;
+ Label line = gridLines[ gridLineIndex ];
+ line.setBounds( xBase, yPos, width, DEFAULT_GRID_LINE_BREADTH );
+ gridLineIndex++;
+ }
+
+ for( int i = 1; i < model.getVisibleColumns(); i++ ) {
+ int index = columnOffset + i;
+ int xPos = xBase;
+ xPos
+ = SpreadSheetUtils.calcXPosition( xBase, columnOffset, index, model )
+ - DEFAULT_GRID_LINE_BREADTH;
+ int height = cellContainer.getClientArea().height;
+ Label line = gridLines[ gridLineIndex ];
+ line.setBounds( xPos, yBase, DEFAULT_GRID_LINE_BREADTH, height );
+ gridLineIndex++;
+ }
+ }
+
+ private void adjustGridControlNumber() {
+ int visibleRows = model.getVisibleRows();
+ int visibleColumns = model.getVisibleColumns();
+ int newGridLineCount = Math.max( 0, visibleColumns + visibleRows - 2 );
+ Label[] newGridLines = new Label[ newGridLineCount ];
+ int linesToAdjust = Math.max( newGridLineCount, gridLines.length );
+ for( int i = 0; i < linesToAdjust; i++ ) {
+ if( i < gridLines.length && i < newGridLineCount ) {
+ // copy existing lines to new array for reuse
+ newGridLines[ i ] = gridLines[ i ];
+ } else if( i >= gridLines.length ) {
+ // the client area has been enlarged, so more lines are needed
+ newGridLines[ i ] = new Label( cellContainer, SWT.NONE );
+ newGridLines[ i ].setBackground( Graphics.getColor( 0, 0, 255 ) );
+ } else if( i >= newGridLineCount ) {
+ // the client area has been shrunken, so dispose of the spare ones.
+ gridLines[ i ].dispose();
+ }
+ }
+ gridLines = newGridLines;
+ }
+
+ private void adjustCellLayoutData() {
+ Set bufferedPositions = new HashSet();
+ Control[] children = cellContainer.getChildren();
+ for( int i = 0; i < children.length; i++ ) {
+ SpreadSheetData data = ( SpreadSheetData )children[ i ].getLayoutData();
+ if( data != null ) {
+ CellPosition position = data.getPosition();
+ bufferedPositions.add( position );
+ }
+ }
+
+ for( int i = 0; i < children.length; i++ ) {
+ SpreadSheetData data = ( SpreadSheetData )children[ i ].getLayoutData();
+ if( data != null ) {
+ CellPosition position = data.getPosition();
+ int oldRowIndex = position.getRowIndex();
+ int oldColumnIndex = position.getColumnIndex();
+ if( needsNewRowIndex( oldRowIndex )
+ || needsNewColumnIndex( oldColumnIndex ) )
+ {
+ int rowIndex = calculateRowIndex( oldRowIndex );
+ int columnIndex = calculateColumn( oldColumnIndex );
+ CellPosition newPosition = new CellPosition( rowIndex, columnIndex );
+ if( !bufferedPositions.contains( newPosition ) ) {
+ SpreadSheetData newData = new SpreadSheetData( newPosition );
+ children[ i ].setLayoutData( newData );
+ }
+ }
+ }
+ }
+ }
+
+ private int calculateRowIndex( final int oldRowIndex ) {
+ int result = oldRowIndex;
+ if( needsNewRowIndex( oldRowIndex ) ) {
+ if( isInUpperOffset( oldRowIndex ) ) {
+ result = oldRowIndex + model.getVisibleRows();
+ } else {
+ result = oldRowIndex - model.getVisibleRows();
+ }
+ }
+ return result;
+ }
+
+ private int calculateColumn( final int oldColumnIndex ) {
+ int result = oldColumnIndex;
+ if( needsNewColumnIndex( oldColumnIndex ) ) {
+ if( isInLeftOffset( oldColumnIndex ) ) {
+ result = oldColumnIndex + model.getVisibleColumns();
+ } else {
+ result = oldColumnIndex - model.getVisibleColumns();
+ }
+ }
+ return result;
+ }
+
+ private boolean needsNewRowIndex( final int oldRowIndex ) {
+ return isInUpperOffset( oldRowIndex )
+ || isInLowerOffset( oldRowIndex );
+ }
+
+ private boolean isInLowerOffset( final int oldRowIndex ) {
+ return oldRowIndex >= model.getVisibleRows() + model.getRowOffset();
+ }
+
+ private boolean isInUpperOffset( final int oldRowIndex ) {
+ return oldRowIndex < model.getRowOffset();
+ }
+
+ private boolean needsNewColumnIndex( final int oldColumnIndex ) {
+ return isInLeftOffset( oldColumnIndex )
+ || isInRightOffset( oldColumnIndex );
+ }
+
+ private boolean isInRightOffset( final int oldColumnIndex ) {
+ int visibleColumns = model.getVisibleColumns();
+ return oldColumnIndex >= visibleColumns + model.getColumnOffset();
+ }
+
+ private boolean isInLeftOffset( final int oldColumnIndex ) {
+ return oldColumnIndex < model.getColumnOffset();
+ }
+
+ private void computeCellContainerBounds() {
+ Rectangle clientArea = spreadSheet.getClientArea();
+ int rowOffset = model.getRowOffset();
+ int yOffset = 0;
+ for( int i = 0; i < rowOffset; i++ ) {
+ yOffset += model.getRowHeight( i );
+ }
+ int columnOffset = model.getColumnOffset();
+ int xOffset = 0;
+ for( int i = 0; i < columnOffset; i++ ) {
+ xOffset += model.getColumnWidth( i );
+ }
+ cellContainer.setBounds( clientArea.x - xOffset,
+ clientArea.y - yOffset,
+ clientArea.width + xOffset,
+ clientArea.height + yOffset );
+ }
+
+ private void updateText() {
+ Control[] children = cellContainer.getChildren();
+ for( int i = 0; i < children.length; i++ ) {
+ if( children[ i ].getData( CELL_CONTROL_MARKER ) != null ) {
+ SpreadSheetData data = ( SpreadSheetData )children[ i ].getLayoutData();
+ CellPosition position = data.getPosition();
+ Label control = ( Label )children[ i ];
+ String text = model.getText( position );
+ control.setText( text );
+ }
+ }
+ }
+
+ Map bufferExistingCells() {
+ Control[] children = cellContainer.getChildren();
+ Map result = new HashMap();
+ for( int i = 0; i < children.length; i++ ) {
+ Control control = children[ i ];
+ if( isCellControl( control ) ) {
+ SpreadSheetData data = ( SpreadSheetData )control.getLayoutData();
+ result.put( data.getPosition(), control );
+ }
+ }
+ return result;
+ }
+
+ private void createMissingCells( final Map buffer )
+ {
+ int rowOffset = model.getRowOffset();
+ int rows = model.getVisibleRows() + rowOffset;
+ int columnOffset = model.getColumnOffset();
+ int columns = model.getVisibleColumns() + columnOffset;
+ for( int i = rowOffset; i < rows; i++ ) {
+ for( int j = columnOffset; j < columns; j++ ) {
+ CellPosition position = new CellPosition( i, j );
+ Control control = ( Control )buffer.get( position );
+ if( control == null ) {
+ if( model.getText( position ) != "" ) {
+ createCellControl( position );
+ }
+ }
+ }
+ }
+ }
+
+ private Control createCellControl( final CellPosition position )
+ {
+ Label result = new Label( cellContainer, SWT.NONE );
+ result.setLayoutData( new SpreadSheetData( position ) );
+ result.setData( CELL_CONTROL_MARKER, CELL_CONTROL_MARKER );
+ Display current = Display.getCurrent();
+ Color bgColor = current.getSystemColor( SWT.COLOR_WHITE );
+ result.setBackground( bgColor );
+ result.addMouseListener( new MouseAdapter() {
+ public void mouseUp( final MouseEvent evt ) {
+ SpreadSheet sheet = ( SpreadSheet )spreadSheet;
+ sheet.getCellEditorController().handleMouseUp( position );
+ }
+ public void mouseDown( final MouseEvent evt ) {
+ SpreadSheet sheet = ( SpreadSheet )spreadSheet;
+ sheet.getCellEditorController().handleMouseDown( position );
+ }
+ } );
+ return result;
+ }
+
+ static boolean isCellControl( final Control control ) {
+ Object data = control.getData( CELL_CONTROL_MARKER );
+ return control instanceof Label
+ && control.getLayoutData() != null
+ && CELL_CONTROL_MARKER.equals( data );
+ }
+
+ void setText( final String text,
+ final CellPosition position )
+ {
+ Label cell = findCellControl( position );
+ if( cell != null && "".equals( text ) ) {
+ cell.dispose();
+ } else if( cell != null && !"".equals( text ) ) {
+ cell.setText( text );
+ } else if( cell == null && !"".equals( text ) ) {
+ cell = ( Label )createCellControl( position );
+ Rectangle clientArea = cellContainer.getClientArea();
+ SpreadSheetUtils.computeBounds( clientArea, cell, model );
+ cell.setText( text );
+ }
+ }
+
+ private Label findCellControl( final CellPosition position )
+ {
+ Label result = null;
+ Control[] children = cellContainer.getChildren();
+ for( int i = 0; result == null && i < children.length; i++ ) {
+ SpreadSheetData data = ( SpreadSheetData )children[ i ].getLayoutData();
+ // TODO [fappel]: cellContainer should contain cell controls only
+ // -> check for cell control can be removed
+ if( data != null
+ && position.equals( data.getPosition() )
+ && isCellControl( children[ i ] ) )
+ {
+ result = ( Label )children[ i ];
+ }
+ }
+ return result;
+ }
+
+ void disposeOfSpareCells( final Map buffer )
+ {
+ int rowOffset = model.getRowOffset();
+ int rows = model.getVisibleRows() + rowOffset;
+ int columnOffset = model.getColumnOffset();
+ int columns = model.getVisibleColumns() + columnOffset;
+ for( int i = rowOffset; i < rows; i++ ) {
+ for( int j = columnOffset; j < columns; j++ ) {
+ CellPosition position = new CellPosition( i, j );
+ buffer.remove( position );
+ }
+ }
+ Iterator iterator = buffer.values().iterator();
+ while( iterator.hasNext() ) {
+ Control control = ( Control )iterator.next();
+ control.dispose();
+ }
+ }
+
+ Composite getCellContainer() {
+ return cellContainer;
+ }
+
+ Label[] getGridLines() {
+ return gridLines;
+ }
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/CellEditorController.java b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/CellEditorController.java
new file mode 100644
index 0000000..300cb14
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/CellEditorController.java
@@ -0,0 +1,117 @@
+// Created on 16.08.2009
+package org.eclipse.swt.custom;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SpreadSheetModel.Adapter;
+import org.eclipse.swt.custom.SpreadSheetModel.Event;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Text;
+
+
+class CellEditorController {
+ private final SpreadSheet spreadSheet;
+ private final Text control;
+ private final SpreadSheetModel model;
+
+
+ private final class EditorKeyAdapter extends KeyAdapter {
+ public void keyPressed( final KeyEvent evt ) {
+ handleKeyPressed( evt );
+ }
+ }
+
+ private final class ModelAdapter extends Adapter {
+ public void textChanged( final Event evt ) {
+ int row = evt.rowIndex;
+ int column = evt.columnIndex;
+ CellPosition position
+ = new CellPosition( row, column );
+ setText( evt.text, position );
+ }
+ }
+
+
+ CellEditorController( final SpreadSheet spreadSheet,
+ final SpreadSheetModel model )
+ {
+ SpreadSheetUtils.checkNotNull( spreadSheet, "spreadSheet" );
+ SpreadSheetUtils.checkNotNull( model, "model" );
+
+ this.spreadSheet = spreadSheet;
+ this.model = model;
+ model.addListener( new ModelAdapter() );
+ control = new Text( spreadSheet, SWT.BORDER );
+ control.setLayoutData( new SpreadSheetData( 0, 0 ) );
+ control.addKeyListener( new EditorKeyAdapter() );
+ }
+
+ void setText( final String text, final CellPosition position ) {
+ SpreadSheetData editorData = ( SpreadSheetData )control.getLayoutData();
+ if( position.equals( editorData.getPosition() ) ) {
+ setText( text );
+ }
+ }
+
+ void setText( final String text ) {
+ control.setText( text );
+ }
+
+ String getText() {
+ return control.getText();
+ }
+
+ boolean setFocus() {
+ return control.setFocus();
+ }
+
+ void handleKeyPressed( final KeyEvent evt ) {
+ switch( evt.keyCode ) {
+ case SWT.ARROW_DOWN:
+ moveCellEditor( 1, 0 );
+ break;
+ case SWT.ARROW_UP:
+ moveCellEditor( -1, 0 );
+ break;
+ case SWT.ARROW_LEFT:
+ moveCellEditor( 0, -1 );
+ break;
+ case SWT.ARROW_RIGHT:
+ moveCellEditor( 0, 1 );
+ break;
+ }
+ }
+
+ void moveCellEditor( final int rowChange, final int columnChange )
+ {
+ SpreadSheetData old = ( SpreadSheetData )control.getLayoutData();
+ int newRow = old.getRowIndex() + rowChange;
+ int newColumn = old.getColumnIndex() + columnChange;
+ if( newRow >= 0 && newColumn >= 0 ) {
+ moveCellEditor( new CellPosition( newRow, newColumn ) );
+ }
+ }
+
+ void moveCellEditor( final CellPosition position ) {
+ SpreadSheetData oldData = ( SpreadSheetData )control.getLayoutData();
+ spreadSheet.setText( control.getText(), oldData.getPosition() );
+ control.setText( spreadSheet.getText( position ) );
+ control.setLayoutData( new SpreadSheetData( position ) );
+ Rectangle clientArea = spreadSheet.getClientArea();
+ SpreadSheetUtils.computeBounds( clientArea, control, model );
+ }
+
+ Control getControl() {
+ return control;
+ }
+
+ void handleMouseUp( final CellPosition position ) {
+ moveCellEditor( position );
+ setFocus();
+ }
+
+ void handleMouseDown( final CellPosition cell ) {
+ }
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/CellPosition.java b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/CellPosition.java
new file mode 100644
index 0000000..6f999ab
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/CellPosition.java
@@ -0,0 +1,64 @@
+// Created on 15.08.2009
+package org.eclipse.swt.custom;
+
+
+public class CellPosition {
+ private final int columnIndex;
+ private final int rowIndex;
+
+ public CellPosition( final int rowIndex, final int columnIndex ) {
+ SpreadSheetUtils.checkNotNegative( rowIndex, "rowIndex" );
+ SpreadSheetUtils.checkNotNegative( columnIndex, "columnIndex" );
+
+ this.rowIndex = rowIndex;
+ this.columnIndex = columnIndex;
+ }
+
+ public int getColumnIndex() {
+ return columnIndex;
+ }
+
+ public int getRowIndex() {
+ return rowIndex;
+ }
+
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + columnIndex;
+ result = prime * result + rowIndex;
+ return result;
+ }
+
+
+ public boolean equals( final Object obj ) {
+ // genereated code
+ if( this == obj ) {
+ return true;
+ }
+ if( obj == null ) {
+ return false;
+ }
+ if( getClass() != obj.getClass() ) {
+ return false;
+ }
+ CellPosition other = ( CellPosition )obj;
+ if( columnIndex != other.columnIndex ) {
+ return false;
+ }
+ if( rowIndex != other.rowIndex ) {
+ return false;
+ }
+ return true;
+ }
+
+ public String toString() {
+ return "CellPosition [columnIndex="
+ + columnIndex
+ + ", rowIndex="
+ + rowIndex
+ + "]";
+ }
+}
+
+
diff --git a/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/HeaderController.java b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/HeaderController.java
new file mode 100644
index 0000000..dfba086
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/HeaderController.java
@@ -0,0 +1,273 @@
+// Created on 22.08.2009
+package org.eclipse.swt.custom;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.*;
+
+
+class HeaderController {
+ static final int DEFAULT_ROW_HEADER_WIDTH = 25;
+ static final int DEFAULT_COLUMN_HEADER_HEIGHT
+ = SpreadSheetModel.DEFAULT_ROW_HEIGHT;
+ static final String ROW_HEADER_MARKER
+ = SpreadSheetLayout.class.getName() + "#RowHeader";
+ static final String COLUMN_HEADER_MARKER
+ = SpreadSheetLayout.class.getName() + "#ColumnHeader";
+ private static final String SASH
+ = SpreadSheetLayout.class.getName() + "#Sash";
+ private static final int SASH_BREADTH = 5;
+ private static final int SASH_OFFSET = 2;
+
+ private final Color headerColor;
+ private final Composite spreadSheet;
+ private final SpreadSheetModel model;
+ private Control[] rowHeaders;
+ private Control[] columnHeaders;
+ private Composite rowContainer;
+ private Composite columnContainer;
+ private int bufferedRowOffset;
+ private int bufferedColumnOffset;
+
+
+ public HeaderController( final Composite spreadSheet,
+ final SpreadSheetModel model )
+ {
+ SpreadSheetUtils.checkNotNull( spreadSheet, "spreadSheet" );
+ SpreadSheetUtils.checkNotNull( model, "model" );
+
+ this.spreadSheet = spreadSheet;
+ this.model = model;
+ this.bufferedRowOffset = model.getRowOffset();
+ this.bufferedColumnOffset = model.getColumnOffset();
+ this.rowHeaders = new Control[ 0 ];
+ this.columnHeaders = new Control[ 0 ];
+ rowContainer = new Composite( spreadSheet, SWT.NONE );
+ columnContainer = new Composite( spreadSheet, SWT.NONE );
+ Display display = Display.getDefault();
+ this.headerColor = display.getSystemColor( SWT.COLOR_WIDGET_LIGHT_SHADOW );
+ rowContainer.setBackground( display.getSystemColor( SWT.COLOR_RED ) );
+ columnContainer.setBackground( display.getSystemColor( SWT.COLOR_RED ) );
+ }
+
+ void adjustHeaderControls() {
+ adjustRowHeaderContainer();
+ adjustColumnHeaderContainer();
+ adjustRowHeaders();
+ adjustColumnHeaders();
+ }
+
+ private void adjustRowHeaderContainer() {
+ Rectangle clientArea = spreadSheet.getClientArea();
+ int xPos = clientArea.x - DEFAULT_ROW_HEADER_WIDTH;
+ int rowOffset = model.getRowOffset();
+ int yOffset = 0;
+ for( int i = 0; i < rowOffset; i++ ) {
+ yOffset += model.getRowHeight( i );
+ }
+ int yPos = clientArea.y - yOffset;
+ int width = DEFAULT_ROW_HEADER_WIDTH
+ - CellController.DEFAULT_GRID_LINE_BREADTH;
+ int height = clientArea.height + yOffset;
+ rowContainer.setBounds( xPos, yPos, width, height );
+ }
+
+ private void adjustColumnHeaderContainer() {
+ Rectangle clientArea = spreadSheet.getClientArea();
+ int columnOffset = model.getColumnOffset();
+ int xOffset = 0;
+ for( int i = 0; i < columnOffset; i++ ) {
+ xOffset += model.getColumnWidth( i );
+ }
+ int xPos = clientArea.x - xOffset;
+ int yPos = clientArea.y - DEFAULT_COLUMN_HEADER_HEIGHT;
+ int width = clientArea.width + xOffset;
+ int height = DEFAULT_COLUMN_HEADER_HEIGHT
+ - CellController.DEFAULT_GRID_LINE_BREADTH;
+ columnContainer.setBounds( xPos, yPos, width, height );
+ }
+
+ private void adjustColumnHeaders() {
+ adjustColumnHeaderNumber();
+ adjustColumnHeaderBounds();
+ }
+
+ private void adjustRowHeaders() {
+ adjustRowHeaderNumber();
+ adjustRowHeaderBounds();
+ }
+
+ private void adjustRowHeaderBounds() {
+ int yBase = 0;
+ int lowerBound = 0;
+ for( int i = 0; i < rowHeaders.length; i++ ) {
+ int row = i + model.getRowOffset();
+ int xPos = 0;
+ int yPos
+ = SpreadSheetUtils.calcYPosition( yBase, lowerBound, row, model );
+ yBase = yPos;
+ lowerBound = row;
+ int width = getRowHeaderWidth()
+ - CellController.DEFAULT_GRID_LINE_BREADTH;
+ int height = model.getRowHeight( row )
+ - CellController.DEFAULT_GRID_LINE_BREADTH;
+ rowHeaders[ i ].setBounds( xPos, yPos, width, height );
+ Sash sash = ( Sash )rowHeaders[ i ].getData( SASH );
+ sash.setBounds( 0, yPos + height - SASH_OFFSET, width, SASH_BREADTH );
+ }
+ }
+
+ private void adjustRowHeaderNumber() {
+ final Rectangle clientArea = spreadSheet.getClientArea();
+ int rows = model.getVisibleRows();
+ Control[] newRowHeaders = new Control[ rows ];
+ int maxRow = Math.max( rows, rowHeaders.length );
+ for( int i = 0; i < maxRow; i++ ) {
+ if( i < rowHeaders.length && i < newRowHeaders.length ) {
+ newRowHeaders[ i ] = rowHeaders[ i ];
+ } else if( i < newRowHeaders.length ) {
+ Label rowHeader = new Label( rowContainer, SWT.NONE );
+ rowHeader.setBackground( headerColor );
+ rowHeader.setData( ROW_HEADER_MARKER, String.valueOf( i ) );
+ newRowHeaders[ i ] = rowHeader;
+ Sash sash = new Sash( rowContainer, SWT.HORIZONTAL );
+ rowHeader.setData( SASH, sash );
+ final int rowIndex = i;
+ sash.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected( final SelectionEvent evt ) {
+ int yOffset = clientArea.y - DEFAULT_COLUMN_HEADER_HEIGHT;
+ int yPos = SpreadSheetUtils.calcYPosition( yOffset,
+ 0,
+ rowIndex,
+ model );
+ int newRowHeight = Math.max( 0, evt.y - yPos + SASH_OFFSET + 1 );
+ model.setRowHeight( rowIndex, newRowHeight );
+ };
+ } );
+ } else {
+ Sash sash = ( Sash )rowHeaders[ i ].getData( SASH );
+ sash.dispose();
+ rowHeaders[ i ].dispose();
+ }
+ }
+ if( bufferedRowOffset != model.getRowOffset() ) {
+ int offSetChange = bufferedRowOffset - model.getRowOffset();
+ newRowHeaders = reorderHeaders( offSetChange, newRowHeaders );
+ bufferedRowOffset = model.getRowOffset();
+ }
+ rowHeaders = newRowHeaders;
+ }
+
+ private Control[] reorderHeaders( int offSetChange, Control[] headerControls )
+ {
+ Control[] offSetHeaders = new Control[ headerControls.length ];
+ for( int i = 0; i < headerControls.length; i++ ) {
+ int position = i + offSetChange;
+ if( position < 0 ) {
+ offSetHeaders[ headerControls.length + position ] = headerControls[ i ];
+ } else if ( position >= headerControls.length ) {
+ offSetHeaders[ position - headerControls.length ] = headerControls[ i ];
+ } else {
+ offSetHeaders[ position ] = headerControls[ i ];
+ }
+ }
+ return offSetHeaders;
+ }
+
+ private void adjustColumnHeaderBounds() {
+ int xBase = 0;
+ int lowerBound = 0;
+ for( int i = 0; i < columnHeaders.length; i++ ) {
+ int col = i + model.getColumnOffset();
+ int xPos
+ = SpreadSheetUtils.calcXPosition( xBase, lowerBound, col, model );
+ xBase = xPos;
+ lowerBound = col;
+ int yPos = 0;
+ int width = model.getColumnWidth( col )
+ - CellController.DEFAULT_GRID_LINE_BREADTH;
+ int height = getColumnHeaderHeight()
+ - CellController.DEFAULT_GRID_LINE_BREADTH;
+ columnHeaders[ i ].setBounds( xPos, yPos, width, height );
+ Sash sash = ( Sash )columnHeaders[ i ].getData( SASH );
+ sash.setBounds( xPos + width - SASH_OFFSET, 0, SASH_BREADTH, height );
+ }
+ }
+
+ private void adjustColumnHeaderNumber() {
+ final Rectangle clientArea = spreadSheet.getClientArea();
+ int columns = model.getVisibleColumns();
+ Control[] newColumnHeaders = new Control[ columns ];
+ int maxColumns = Math.max( columns, columnHeaders.length );
+ for( int i = 0; i < maxColumns; i++ ) {
+ if( i < columnHeaders.length && i < newColumnHeaders.length ) {
+ newColumnHeaders[ i ] = columnHeaders[ i ];
+ } else if( i < newColumnHeaders.length ) {
+ Label columnHeader = new Label( columnContainer, SWT.NONE );
+ columnHeader.setBackground( headerColor );
+ columnHeader.setData( COLUMN_HEADER_MARKER, String.valueOf( i ) );
+ newColumnHeaders[ i ] = columnHeader;
+ Sash sash = new Sash( columnContainer, SWT.VERTICAL );
+ columnHeader.setData( SASH, sash );
+ final int columnIndex = i;
+ sash.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected( final SelectionEvent evt ) {
+ int xOffset = clientArea.x - DEFAULT_ROW_HEADER_WIDTH;
+ int xPos = SpreadSheetUtils.calcXPosition( xOffset,
+ 0,
+ columnIndex,
+ model );
+ int newColumnWidth = Math.max( 0, evt.x - xPos + SASH_OFFSET + 1 );
+ model.setColumnWidth( columnIndex, newColumnWidth );
+ };
+ } );
+
+ } else {
+ Sash sash = ( Sash )columnHeaders[ i ].getData( SASH );
+ sash.dispose();
+ columnHeaders[ i ].dispose();
+ }
+ }
+ if( bufferedColumnOffset != model.getColumnOffset() ) {
+ int offSetChange = bufferedColumnOffset - model.getColumnOffset();
+ newColumnHeaders = reorderHeaders( offSetChange, newColumnHeaders );
+ bufferedColumnOffset = model.getColumnOffset();
+ }
+ columnHeaders = newColumnHeaders;
+ }
+
+ Composite getColumnContainer() {
+ return columnContainer;
+ }
+
+ Control[] getColumnHeaderControls() {
+ return columnHeaders;
+ }
+
+ Composite getRowContainer() {
+ return rowContainer;
+ }
+
+ Control[] getRowHeaderControls() {
+ return rowHeaders;
+ }
+
+ int getRowHeaderWidth() {
+ return DEFAULT_ROW_HEADER_WIDTH;
+ }
+
+ int getColumnHeaderHeight() {
+ return DEFAULT_COLUMN_HEADER_HEIGHT;
+ }
+
+ static boolean isRowHeaderControl( final Control control ) {
+ return control.getData( ROW_HEADER_MARKER ) != null;
+ }
+
+ static boolean isColumnHeaderControl( final Control control ) {
+ return control.getData( COLUMN_HEADER_MARKER ) != null;
+ }
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SliderController.java b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SliderController.java
new file mode 100644
index 0000000..37440c1
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SliderController.java
@@ -0,0 +1,81 @@
+// Created on 22.08.2009
+package org.eclipse.swt.custom;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Slider;
+
+
+public class SliderController {
+ static final int PAGE_INCREMENT_OFFSET = 2;
+ static final int SLIDER_BREADTH = 20;
+
+ private final Composite spreadSheet;
+ private final HeaderController headerController;
+ private final Slider horizontalSlider;
+ private final Slider verticalSlider;
+ private final SpreadSheetModel model;
+
+
+ protected SliderController( final Composite spreadSheet,
+ final HeaderController headerController,
+ final SpreadSheetModel model )
+ {
+ SpreadSheetUtils.checkNotNull( spreadSheet, "spreadSheet" );
+ SpreadSheetUtils.checkNotNull( headerController, "headerController" );
+
+ this.spreadSheet = spreadSheet;
+ this.headerController = headerController;
+ this.model = model;
+ horizontalSlider = new Slider( spreadSheet, SWT.H_SCROLL );
+ horizontalSlider.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected( final SelectionEvent evt ) {
+ int selection = SliderController.this.horizontalSlider.getSelection();
+ SliderController.this.model.setColumnOffset( selection );
+ }
+ } );
+
+ verticalSlider = new Slider( spreadSheet, SWT.V_SCROLL );
+ verticalSlider.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected( final SelectionEvent evt ) {
+ int selection = SliderController.this.verticalSlider.getSelection();
+ SliderController.this.model.setRowOffset( selection );
+ };
+ } );
+ }
+
+ int getBreadth() {
+ return SliderController.SLIDER_BREADTH;
+ }
+
+ void adjustSlider() {
+ Rectangle clientArea = spreadSheet.getClientArea();
+ adjustHorizontalSlider( clientArea );
+ adjustVerticalSlider( clientArea );
+ }
+
+ private void adjustVerticalSlider( final Rectangle clientArea ) {
+ int vX = clientArea.width + headerController.getRowHeaderWidth();
+ int vHeight = clientArea.height + getBreadth();
+ verticalSlider.setBounds( vX, 0, getBreadth(), vHeight );
+ int rows = model.getVisibleRows();
+ verticalSlider.setMinimum( 0 );
+ verticalSlider.setMaximum( rows + PAGE_INCREMENT_OFFSET );
+ verticalSlider.setThumb( rows );
+ verticalSlider.setSelection( model.getRowOffset() );
+ }
+
+ private void adjustHorizontalSlider( final Rectangle clientArea ) {
+ int hY = clientArea.height + getBreadth();
+ int hWidth = clientArea.width + headerController.getRowHeaderWidth();
+ horizontalSlider.setBounds( 0, hY, hWidth, getBreadth() );
+ int columns = model.getVisibleColumns();
+ horizontalSlider.setMinimum( 0 );
+ horizontalSlider.setMaximum( columns + PAGE_INCREMENT_OFFSET );
+ horizontalSlider.setThumb( columns );
+ horizontalSlider.setSelection( model.getColumnOffset() );
+ }
+}
diff --git a/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SpreadSheet.java b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SpreadSheet.java
new file mode 100644
index 0000000..78ca715
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SpreadSheet.java
@@ -0,0 +1,128 @@
+// Created on 08.08.2009
+package org.eclipse.swt.custom;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SpreadSheetModel.Adapter;
+import org.eclipse.swt.custom.SpreadSheetModel.Event;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Layout;
+
+
+public class SpreadSheet extends Composite {
+ private final SpreadSheetLayout layout;
+ private final SpreadSheetModel model;
+ private final CellController cellController;
+ private final CellEditorController cellEditorController;
+ private final SliderController sliderController;
+ private final HeaderController headerController;
+
+
+ private final class ModelAdapter extends Adapter {
+ public void columnWidthChanged( final Event evt ) {
+ triggerLayout();
+ }
+ public void rowHeightChanged( final Event evt ) {
+ triggerLayout();
+ }
+ public void columnOffsetChanged( final Event event ) {
+ triggerLayout();
+ }
+ public void rowOffsetChanged( final Event event ) {
+ triggerLayout();
+ }
+ }
+
+
+ public SpreadSheet( final Composite parent, final int style ) {
+ // TODO [fappel]: checkStyle( style ) implementation
+ super( parent, style );
+ setBackground( getDisplay().getSystemColor( SWT.COLOR_GRAY ) );
+
+ model = new SpreadSheetModel();
+ cellEditorController = new CellEditorController( this, model );
+ cellController = new CellController( this, model );
+ headerController = new HeaderController( this, model );
+ sliderController = new SliderController( this, headerController, model );
+ model.addListener( new ModelAdapter() );
+ layout = new SpreadSheetLayout( model,
+ cellController,
+ sliderController,
+ headerController );
+ super.setLayout( layout );
+ }
+
+ private void triggerLayout() {
+ layout();
+ cellEditorController.setFocus();
+ }
+
+ public Rectangle getClientArea() {
+ checkWidget();
+ Rectangle clientArea = super.getClientArea();
+ int xPos = headerController.getRowHeaderWidth();
+ int yPos = headerController.getColumnHeaderHeight();
+ int width = clientArea.width
+ - sliderController.getBreadth()
+ - headerController.getRowHeaderWidth();
+ int height = clientArea.height
+ - sliderController.getBreadth()
+ - headerController.getColumnHeaderHeight();
+ return new Rectangle( xPos, yPos, width, height );
+ }
+
+ public String getText( final int rowIndex, final int columnIndex ) {
+ checkWidget();
+ return model.getText( rowIndex, columnIndex );
+ }
+
+ public String getText( final CellPosition position ) {
+ checkWidget();
+ return getText( position.getRowIndex(), position.getColumnIndex() );
+ }
+
+ public void setText( final String text,
+ final int rowIndex,
+ final int columnIndex )
+ {
+ checkWidget();
+ model.setText( text, rowIndex, columnIndex );
+ }
+
+ public void setText( final String text, final CellPosition position ) {
+ checkWidget();
+ setText( text, position.getRowIndex(), position.getColumnIndex() );
+ }
+
+ public void setColumnOffset( final int columnOffset ) {
+ checkWidget();
+ model.setColumnOffset( columnOffset );
+ }
+
+ public void setRowOffset( final int rowOffset ) {
+ checkWidget();
+ model.setRowOffset( rowOffset );
+ }
+
+ CellEditorController getCellEditorController() {
+ return cellEditorController;
+ }
+
+ /////////////////
+ // start override
+
+ public boolean setFocus() {
+ return cellEditorController.setFocus();
+ }
+
+ public void setLayout( final Layout layout ) {
+ checkWidget();
+
+ String msg = "Changing the layout algorithm is not supported.";
+ throw new UnsupportedOperationException( msg );
+ }
+
+ // end override
+ ///////////////
+}
diff --git a/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SpreadSheetData.java b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SpreadSheetData.java
new file mode 100644
index 0000000..09c2ac5
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SpreadSheetData.java
@@ -0,0 +1,29 @@
+// Created on 07.08.2009
+package org.eclipse.swt.custom;
+
+
+
+class SpreadSheetData {
+
+ private final CellPosition position;
+
+ SpreadSheetData( final CellPosition position ) {
+ this.position = position;
+ }
+
+ SpreadSheetData( final int rowIndex, final int columnIndex ) {
+ this( new CellPosition( rowIndex, columnIndex ) );
+ }
+
+ int getColumnIndex() {
+ return position.getColumnIndex();
+ }
+
+ int getRowIndex() {
+ return position.getRowIndex();
+ }
+
+ CellPosition getPosition() {
+ return position;
+ }
+}
diff --git a/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SpreadSheetLayout.java b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SpreadSheetLayout.java
new file mode 100644
index 0000000..5c8e5ba
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SpreadSheetLayout.java
@@ -0,0 +1,45 @@
+// Created on 07.08.2009
+package org.eclipse.swt.custom;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.*;
+
+class SpreadSheetLayout extends Layout {
+ private final SpreadSheetModel model;
+ private final CellController cellController;
+ private final SliderController sliderController;
+ private final HeaderController headerController;
+
+
+ SpreadSheetLayout( final SpreadSheetModel model,
+ final CellController cellController,
+ final SliderController sliderController,
+ final HeaderController headerController )
+ {
+ this.cellController = cellController;
+ this.sliderController = sliderController;
+ this.headerController = headerController;
+ this.model = model;
+ }
+
+ protected Point computeSize( final Composite composite,
+ final int wHint,
+ final int hHint,
+ final boolean flushCache )
+ {
+ return null;
+ }
+
+ protected void layout( final Composite composite, final boolean flushCache ) {
+ Rectangle clientArea = composite.getClientArea();
+ model.updateVisibleRowAndColumns( clientArea );
+ sliderController.adjustSlider();
+ headerController.adjustHeaderControls();
+ cellController.adjustCellControls();
+ Control[] children = composite.getChildren();
+ for( int i = 0; i < children.length; i++ ) {
+ SpreadSheetUtils.computeBounds( clientArea, children[ i ], model );
+ }
+ }
+}
diff --git a/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SpreadSheetModel.java b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SpreadSheetModel.java
new file mode 100644
index 0000000..1257a82
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SpreadSheetModel.java
@@ -0,0 +1,269 @@
+// Created on 22.08.2009
+package org.eclipse.swt.custom;
+
+import java.text.MessageFormat;
+import java.util.*;
+
+import org.eclipse.swt.graphics.Rectangle;
+
+
+class SpreadSheetModel {
+ static final int DEFAULT_ROW_HEIGHT = 20;
+ static final int DEFAULT_COLUMN_WIDTH = 100;
+
+ private final Map rows;
+ private final Map columns;
+ private final Map texts;
+ private final Set listeners;
+ private int rowOffset;
+ private int columnOffset;
+ private int visibleColumns;
+ private int visibleRows;
+
+
+ static class Event {
+ private final static int ROW_HEIGHT_CHANGED = 0;
+ private final static int COLUMN_WIDTH_CHANGED = 1;
+ private final static int TEXT_CHANGED = 2;
+ private final static int ROW_OFFSET_CHANGED = 3;
+ private final static int COLUMN_OFFSET_CHANGED = 4;
+
+ int type;
+ int rowIndex;
+ int columnIndex;
+ int rowHeight;
+ int columnWidth;
+ String text;
+ int rowOffset;
+ int columnOffset;
+
+ public void fire( final Listener listener ) {
+ switch( type ) {
+ case ROW_HEIGHT_CHANGED:
+ listener.rowHeightChanged( this );
+ break;
+
+ case COLUMN_WIDTH_CHANGED:
+ listener.columnWidthChanged( this );
+ break;
+
+ case TEXT_CHANGED:
+ listener.textChanged( this );
+ break;
+
+ case ROW_OFFSET_CHANGED:
+ listener.rowOffsetChanged( this );
+ break;
+
+ case COLUMN_OFFSET_CHANGED:
+ listener.columnOffsetChanged( this );
+ break;
+
+ default:
+ String txt = "Event Type ''{0}''not supported";
+ Object[] param = new Object[] { new Integer( type ) };
+ String msg = MessageFormat.format( txt, param );
+ throw new IllegalStateException( msg );
+ }
+ }
+ }
+
+ static interface Listener {
+ void rowHeightChanged( Event evt );
+ void columnOffsetChanged( Event evt );
+ void rowOffsetChanged( Event evt );
+ void columnWidthChanged( Event evt );
+ void textChanged( Event evt );
+ }
+
+ static abstract class Adapter implements Listener {
+ public void rowHeightChanged( final Event evt ) {};
+ public void columnWidthChanged( final Event evt ) {};
+ public void textChanged( final Event evt ) {};
+ public void columnOffsetChanged( Event evt ) {};
+ public void rowOffsetChanged( Event evt ) {};
+ }
+
+
+ public SpreadSheetModel() {
+ rows = new HashMap();
+ columns = new HashMap();
+ texts = new HashMap();
+ listeners = new HashSet();
+ visibleColumns = 0;
+ visibleRows = 0;
+ }
+
+ int getRowHeight( final int rowIndex ) {
+ int result = DEFAULT_ROW_HEIGHT;
+ Integer key = new Integer( rowIndex );
+ Integer height = ( Integer )rows.get( key );
+ if( height != null ) {
+ result = height.intValue();
+ }
+ return result;
+ }
+
+ void setRowHeight( final int rowIndex, final int rowHeight ) {
+ SpreadSheetUtils.checkNotNegative( rowIndex, "rowIndex" );
+ SpreadSheetUtils.checkNotNegative( rowHeight, "rowHeight" );
+
+ Integer key = new Integer( rowIndex );
+ Integer oldRowHeight = ( Integer )rows.get( key );
+ Integer newRowHeight = new Integer( rowHeight );
+
+ if( rowHeight == SpreadSheetModel.DEFAULT_ROW_HEIGHT ) {
+ rows.remove( key );
+ } else {
+ rows.put( key, newRowHeight );
+ }
+
+ if( !newRowHeight.equals( oldRowHeight ) ) {
+ Event event = new Event();
+ event.type = Event.ROW_HEIGHT_CHANGED;
+ event.rowIndex = rowIndex;
+ event.rowHeight = rowHeight;
+ fireEvent( event );
+ }
+ }
+
+ int getColumnWidth( final int columnIndex ) {
+ int result = DEFAULT_COLUMN_WIDTH;
+ Integer key = new Integer( columnIndex );
+ Integer width = ( Integer )columns.get( key );
+ if( width != null ) {
+ result = width.intValue();
+ }
+ return result;
+ }
+
+ void setColumnWidth( final int columnIndex, final int columnWidth ) {
+ SpreadSheetUtils.checkNotNegative( columnIndex, "columnIndex" );
+ SpreadSheetUtils.checkNotNegative( columnWidth, "columnWidth" );
+
+ Integer key = new Integer( columnIndex );
+ Integer newColumnWidth = new Integer( columnWidth );
+ Integer oldColumnWidth = ( Integer )columns.get( key );
+
+ if( columnWidth == DEFAULT_COLUMN_WIDTH ) {
+ columns.remove( key );
+ } else {
+ columns.put( key, newColumnWidth );
+ }
+
+ if( !newColumnWidth.equals( oldColumnWidth ) ) {
+ Event event = new Event();
+ event.type = Event.COLUMN_WIDTH_CHANGED;
+ event.columnIndex = columnIndex;
+ event.columnWidth = columnWidth;
+ fireEvent( event );
+ }
+ }
+
+ String getText( final CellPosition position ) {
+ return getText( position.getRowIndex(), position.getColumnIndex() );
+ }
+
+ String getText( final int rowIndex, final int columnIndex ) {
+ SpreadSheetUtils.checkNotNegative( rowIndex, "rowIndex" );
+ SpreadSheetUtils.checkNotNegative( columnIndex, "columnIndex" );
+
+ CellPosition position = new CellPosition( rowIndex, columnIndex );
+ String result = ( String )texts.get( position );
+ return result == null ? "" : result;
+ }
+
+ void setText( final String text, final CellPosition position ) {
+ setText( text, position.getRowIndex(), position.getColumnIndex() );
+ }
+
+ void setText( final String text,
+ final int rowIndex,
+ final int columnIndex )
+ {
+ SpreadSheetUtils.checkNotNull( text, "text" );
+ SpreadSheetUtils.checkNotNegative( rowIndex, "rowIndex" );
+ SpreadSheetUtils.checkNotNegative( columnIndex, "columnIndex" );
+
+ CellPosition position = new CellPosition( rowIndex, columnIndex );
+ Object oldText = texts.get( position );
+
+ if( "".equals( text ) ) {
+ texts.remove( position );
+ } else {
+ texts.put( position, text );
+ }
+
+ if( !text.equals( oldText ) ) {
+ Event event = new Event();
+ event.type = Event.TEXT_CHANGED;
+ event.rowIndex = rowIndex;
+ event.columnIndex = columnIndex;
+ event.text = text;
+ fireEvent( event );
+ }
+ }
+
+ void setColumnOffset( final int columnOffset ) {
+ SpreadSheetUtils.checkNotNegative( columnOffset, "columnOffset" );
+ int oldOffset = this.columnOffset;
+ this.columnOffset = columnOffset;
+ if( oldOffset != columnOffset ) {
+ Event event = new Event();
+ event.type = Event.COLUMN_OFFSET_CHANGED;
+ event.columnOffset = columnOffset;
+ fireEvent( event );
+ }
+ }
+
+ int getColumnOffset() {
+ return columnOffset;
+ }
+
+ void setRowOffset( final int rowOffset ) {
+ SpreadSheetUtils.checkNotNegative( rowOffset, "rowOffset" );
+ int oldOffset = this.rowOffset;
+ this.rowOffset = rowOffset;
+ if( oldOffset != rowOffset ) {
+ Event event = new Event();
+ event.type = Event.ROW_OFFSET_CHANGED;
+ event.rowOffset = rowOffset;
+ fireEvent( event );
+ }
+
+ }
+
+ int getRowOffset() {
+ return rowOffset;
+ }
+
+ void updateVisibleRowAndColumns( final Rectangle clientArea ) {
+ visibleColumns
+ = SpreadSheetUtils.calcVisibleColumns( clientArea.width, this );
+ visibleRows = SpreadSheetUtils.calcVisibleRows( clientArea.height, this );
+ }
+
+ int getVisibleRows() {
+ return visibleRows;
+ }
+
+ int getVisibleColumns() {
+ return visibleColumns;
+ }
+
+ void addListener( final Listener listener ) {
+ listeners.add( listener );
+ }
+
+ void removeListener( final Listener listener ) {
+ listeners.remove( listener );
+ }
+
+ private void fireEvent( final Event event ) {
+ Listener[] lsnrs = new Listener[ listeners.size() ];
+ listeners.toArray( lsnrs );
+ for( int i = 0; i < lsnrs.length; i++ ) {
+ event.fire( lsnrs[ i ] );
+ }
+ }
+}
diff --git a/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SpreadSheetUtils.java b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SpreadSheetUtils.java
new file mode 100644
index 0000000..2e2e79d
--- /dev/null
+++ b/bundles/org.eclipse.rap.rwt.custom/src/org/eclipse/swt/custom/SpreadSheetUtils.java
@@ -0,0 +1,107 @@
+// Created on 09.08.2009
+package org.eclipse.swt.custom;
+
+import java.text.MessageFormat;
+
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+
+
+class SpreadSheetUtils {
+
+ static void checkNotNegative( final int parameter,
+ final String parameterName )
+ {
+ if( parameter < 0 ) {
+ String txt = "A negative value for parameter ''{0}'' is not allowed.";
+ String msg = MessageFormat.format( txt, new Object[] { parameterName } );
+ throw new IllegalArgumentException( msg );
+ }
+ }
+
+ static void checkNotNull( Object parameter, String parameterName ) {
+ if( parameter == null ) {
+ String txt = "Parameter ''{0}'' must not be null.";
+ String msg = MessageFormat.format( txt, new Object[]{ parameterName } );
+ throw new IllegalArgumentException( msg );
+ }
+ }
+
+ static int calcVisibleRows( final int height, final SpreadSheetModel model ) {
+ int rowIndex = 0;
+ int rowOffset = model.getRowOffset();
+ int calculatedHeight = model.getRowHeight( rowIndex + rowOffset );
+ while( calculatedHeight < height ) {
+ rowIndex++;
+ calculatedHeight += model.getRowHeight( rowIndex + rowOffset );
+ }
+ // we asume that the visible area most probably will show at least
+ // one row;
+ return rowIndex + 1;
+ }
+
+ static int calcVisibleColumns( final int width,
+ final SpreadSheetModel model )
+ {
+ int columnIndex = 0;
+ int columnOffset = model.getColumnOffset();
+ int calculatedWidth = model.getColumnWidth( columnIndex + columnOffset );
+ while( calculatedWidth < width ) {
+ columnIndex++;
+ calculatedWidth += model.getColumnWidth( columnIndex + columnOffset );
+ }
+ // we asume that the visible area most probably will show at least
+ // one column;
+ return columnIndex + 1;
+ }
+
+ static int calcYPosition( final int yOffset,
+ final int startIndex,
+ final int rowIndex,
+ final SpreadSheetModel model )
+ {
+ int result = yOffset;
+ for( int i = startIndex; i < rowIndex; i++ ) {
+ result += model.getRowHeight( i );
+ }
+ return result;
+ }
+
+ static int calcXPosition( final int xOffset,
+ final int startIndex,
+ final int columnIndex,
+ final SpreadSheetModel model )
+ {
+ int result = xOffset;
+ for( int i = startIndex; i < columnIndex; i++ ) {
+ result += model.getColumnWidth( i );
+ }
+ return result;
+ }
+
+ static void computeBounds( final Rectangle clientArea,
+ final Control control,
+ final SpreadSheetModel model )
+ {
+ SpreadSheetData data = ( SpreadSheetData )control.getLayoutData();
+ if( data != null ) {
+ int x = calcXPosition( clientArea.x,
+ 0,
+ data.getColumnIndex(),
+ model );
+ int y = calcYPosition( clientArea.y,
+ 0,
+ data.getRowIndex(),
+ model );
+ int width = model.getColumnWidth( data.getColumnIndex() )
+ - CellController.DEFAULT_GRID_LINE_BREADTH;
+ int height = model.getRowHeight( data.getRowIndex() )
+ - CellController.DEFAULT_GRID_LINE_BREADTH;
+ control.setBounds( x, y, width, height );
+ }
+ }
+
+ private SpreadSheetUtils() {
+ // prevent instance creation
+ }
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.rap.rwt.custom.test/.classpath b/tests/org.eclipse.rap.rwt.custom.test/.classpath
new file mode 100644
index 0000000..2fbb7a2
--- /dev/null
+++ b/tests/org.eclipse.rap.rwt.custom.test/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tests/org.eclipse.rap.rwt.custom.test/.project b/tests/org.eclipse.rap.rwt.custom.test/.project
new file mode 100644
index 0000000..00b16f0
--- /dev/null
+++ b/tests/org.eclipse.rap.rwt.custom.test/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.rap.rwt.custom.test</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/tests/org.eclipse.rap.rwt.custom.test/.settings/org.eclipse.jdt.core.prefs b/tests/org.eclipse.rap.rwt.custom.test/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..996653a
--- /dev/null
+++ b/tests/org.eclipse.rap.rwt.custom.test/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Fri Aug 07 21:40:34 CEST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
+org.eclipse.jdt.core.compiler.source=1.3
diff --git a/tests/org.eclipse.rap.rwt.custom.test/META-INF/MANIFEST.MF b/tests/org.eclipse.rap.rwt.custom.test/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..30f327c
--- /dev/null
+++ b/tests/org.eclipse.rap.rwt.custom.test/META-INF/MANIFEST.MF
@@ -0,0 +1,8 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Test
+Bundle-SymbolicName: org.eclipse.rap.rwt.custom.test
+Bundle-Version: 0.0.0.qualifier
+Fragment-Host: org.eclipse.rap.rwt.custom;bundle-version="0.0.0.qualifier"
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
+Require-Bundle: org.junit;bundle-version="3.8.2"
diff --git a/tests/org.eclipse.rap.rwt.custom.test/about.html b/tests/org.eclipse.rap.rwt.custom.test/about.html
new file mode 100644
index 0000000..248f88c
--- /dev/null
+++ b/tests/org.eclipse.rap.rwt.custom.test/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>August 08, 2009</p>
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, "Program" will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party ("Redistributor") and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
diff --git a/tests/org.eclipse.rap.rwt.custom.test/build.properties b/tests/org.eclipse.rap.rwt.custom.test/build.properties
new file mode 100644
index 0000000..34d2e4d
--- /dev/null
+++ b/tests/org.eclipse.rap.rwt.custom.test/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/tests/org.eclipse.rap.rwt.custom.test/src/org/eclipse/swt/custom/AllCustomRWTTests.java b/tests/org.eclipse.rap.rwt.custom.test/src/org/eclipse/swt/custom/AllCustomRWTTests.java
new file mode 100644
index 0000000..ded613f
--- /dev/null
+++ b/tests/org.eclipse.rap.rwt.custom.test/src/org/eclipse/swt/custom/AllCustomRWTTests.java
@@ -0,0 +1,18 @@
+// Created on 07.08.2009
+package org.eclipse.swt.custom;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+
+public class AllCustomRWTTests {
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "Test for org.eclipse.swt.custom" );
+ //$JUnit-BEGIN$
+ suite.addTestSuite( SpreadSheetLayout_Test.class );
+ suite.addTestSuite( SpreadSheet_Test.class );
+ //$JUnit-END$
+ return suite;
+ }
+}
diff --git a/tests/org.eclipse.rap.rwt.custom.test/src/org/eclipse/swt/custom/SpreadSheetFixture.java b/tests/org.eclipse.rap.rwt.custom.test/src/org/eclipse/swt/custom/SpreadSheetFixture.java
new file mode 100644
index 0000000..7ec59c8
--- /dev/null
+++ b/tests/org.eclipse.rap.rwt.custom.test/src/org/eclipse/swt/custom/SpreadSheetFixture.java
@@ -0,0 +1,25 @@
+// Created on 07.11.2009
+package org.eclipse.swt.custom;
+
+
+public class SpreadSheetFixture {
+
+ private static final int COLUMN_COUNT = 25;
+ private static final int ROW_COUNT = 50;
+
+ static void populateModel( final SpreadSheetModel model ) {
+ for( int i = 0; i < ROW_COUNT; i++ ) {
+ for( int j = 0; j < 25; j++ ) {
+ model.setText( i + "_" + j, i, j );
+ }
+ }
+ }
+
+ static void populateModel( final SpreadSheet spreadSheet ) {
+ for( int i = 0; i < ROW_COUNT; i++ ) {
+ for( int j = 0; j < COLUMN_COUNT; j++ ) {
+ spreadSheet.setText( i + "_" + j, i, j );
+ }
+ }
+ }
+}
diff --git a/tests/org.eclipse.rap.rwt.custom.test/src/org/eclipse/swt/custom/SpreadSheetLayout_Test.java b/tests/org.eclipse.rap.rwt.custom.test/src/org/eclipse/swt/custom/SpreadSheetLayout_Test.java
new file mode 100644
index 0000000..e182db5
--- /dev/null
+++ b/tests/org.eclipse.rap.rwt.custom.test/src/org/eclipse/swt/custom/SpreadSheetLayout_Test.java
@@ -0,0 +1,451 @@
+// Created on 07.08.2009
+package org.eclipse.swt.custom;
+
+import java.math.BigDecimal;
+
+import junit.framework.TestCase;
+
+import org.eclipse.swt.RWTFixture;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.*;
+
+
+/*
+ * TODO [fappel]: implement flushCache functionality of SpreadSheetLayout#layout
+ * TODO [fappel]: check that only correct layout data types are used
+ */
+public class SpreadSheetLayout_Test extends TestCase {
+ private static final int DEFAULT_SHELL_HEIGHT = 400;
+ private static final int DEFAULT_SHELL_WIDTH = 400;
+ private static final int DEFAULT_COLUMN_HEADER_HEIGHT
+ = HeaderController.DEFAULT_COLUMN_HEADER_HEIGHT;
+ private static final int DEFAULT_ROW_HEADER_WIDTH
+ = HeaderController.DEFAULT_ROW_HEADER_WIDTH;
+ private static final int DEFAULT_GRID_LINE_BREADTH
+ = CellController.DEFAULT_GRID_LINE_BREADTH;
+ private static final int DEFAULT_ROW_HEIGHT
+ = SpreadSheetModel.DEFAULT_ROW_HEIGHT;
+ private static final int DEFAULT_COLUMN_WIDTH
+ = SpreadSheetModel.DEFAULT_COLUMN_WIDTH;
+ private static final int DEFAULT_CELL_HEIGHT
+ = SpreadSheetModel.DEFAULT_ROW_HEIGHT
+ - DEFAULT_GRID_LINE_BREADTH;
+ private static final int DEFAULT_CELL_WIDTH
+ = SpreadSheetModel.DEFAULT_COLUMN_WIDTH
+ - DEFAULT_GRID_LINE_BREADTH;
+ private static final int SASH_FACTOR = 2;
+ private static final int EXPECTED_SLIDER_COUNT = 2;
+ private static final int EXPECTED_CONTAINER_COUNT = 1;
+ private static final int EXPECTED_HEADER_COUNT = 2;
+ private static final int ROW_TO_TEST = 4;
+ private static final int ROW_TO_TEST_HEIGHT = 40;
+ private static final int COLUMN_TO_TEST = 5;
+ private static final int COLUMN_TO_TEST_WIDTH = 150;
+ private Display display;
+ private Shell shell;
+ private SpreadSheetModel model;
+ private CellController cellController;
+ private HeaderController headerController;
+ private SliderController sliderController;
+ private SpreadSheetLayout layout;
+
+ public void testEmptyGrid() {
+ layout.layout( shell, false );
+ Composite cellContainer = cellController.getCellContainer();
+ Rectangle clientArea = shell.getClientArea();
+ int expectedColCount = calculateColumnCount( clientArea );
+ int expectedRowCount = calculateRowCount( clientArea );
+ Control[] children = cellContainer.getChildren();
+ assertEquals( expectedColCount + expectedRowCount - 2, children.length );
+
+ Label[] gridLines = cellController.getGridLines();
+ Label line = gridLines[ 0 ];
+ assertEquals( clientArea.x, line.getBounds().x );
+ assertEquals( DEFAULT_CELL_HEIGHT, line.getBounds().y );
+ assertEquals( clientArea.width, line.getBounds().width );
+ assertEquals( DEFAULT_GRID_LINE_BREADTH, line.getBounds().height );
+
+ // change row and column offset
+ model.setRowOffset( 1 );
+ model.setColumnOffset( 1 );
+ layout.layout( shell, false );
+
+ // number of children must not change
+ children = cellContainer.getChildren();
+ assertEquals( expectedColCount + expectedRowCount - 2, children.length );
+
+ gridLines = cellController.getGridLines();
+ line = gridLines[ 0 ];
+ assertEquals( clientArea.x + DEFAULT_COLUMN_WIDTH, line.getBounds().x );
+ assertEquals( DEFAULT_CELL_HEIGHT * 2 + DEFAULT_GRID_LINE_BREADTH,
+ line.getBounds().y );
+ assertEquals( cellContainer.getBounds().width,
+ line.getBounds().width );
+ assertEquals( DEFAULT_GRID_LINE_BREADTH, line.getBounds().height );
+
+ }
+
+ public void testLayoutCompleteClientArea() {
+ Composite cellContainer = cellController.getCellContainer();
+ Composite rowHeaderContainer = headerController.getRowContainer();
+ Composite columnHeaderContainer = headerController.getColumnContainer();
+
+ int expectedControlCount = EXPECTED_CONTAINER_COUNT
+ + EXPECTED_SLIDER_COUNT
+ + EXPECTED_HEADER_COUNT;
+ int sheetChildCount = shell.getChildren().length;
+ assertEquals( "Sliders or container are missing",
+ expectedControlCount,
+ sheetChildCount );
+
+ for( int repetitions = 0; repetitions < 4; repetitions++ ) {
+ switch( repetitions ) {
+ case 1:
+ shell.setSize( 800, 800 );
+ break;
+ case 2:
+ shell.setSize( DEFAULT_SHELL_WIDTH, DEFAULT_SHELL_HEIGHT );
+ break;
+ case 3:
+ SpreadSheetFixture.populateModel( model );
+ break;
+ }
+ layout.layout( shell, false );
+
+ Rectangle clientArea = shell.getClientArea();
+ assertEquals( clientArea, cellContainer.getBounds() );
+ int expectedColCount = calculateColumnCount( clientArea );
+ int expectedRowCount = calculateRowCount( clientArea );
+ int expectedCellCount = 0;
+ if( repetitions == 3 ) {
+ expectedCellCount = expectedColCount * expectedRowCount;
+ }
+ int expectedChildCount = EXPECTED_SLIDER_COUNT
+ + EXPECTED_CONTAINER_COUNT
+ + EXPECTED_HEADER_COUNT;
+ assertEquals( expectedChildCount, shell.getChildren().length );
+
+ Control[] children = cellContainer.getChildren();
+ int cellCount = 0;
+ int headerCount = 0;
+ for( int i = 0; i < children.length; i++ ) {
+ if( CellController.isCellControl( children[ i ] ) ) {
+ cellCount++;
+ }
+ }
+ Control[] rowHeaderControls = rowHeaderContainer.getChildren();
+ Control[] columnHeaderControls = columnHeaderContainer.getChildren();
+ headerCount = rowHeaderControls.length + columnHeaderControls.length;
+ assertEquals( expectedCellCount, cellCount );
+
+ int expectedHeaderCount
+ = ( expectedRowCount + expectedColCount ) * SASH_FACTOR;
+ assertEquals( expectedHeaderCount, headerCount );
+ }
+ }
+
+ private int calculateRowCount( final Rectangle clientArea ) {
+ BigDecimal height = new BigDecimal( clientArea.height );
+ BigDecimal rowHeight = new BigDecimal( DEFAULT_ROW_HEIGHT );
+ return height.divide( rowHeight, BigDecimal.ROUND_UP ).intValue();
+ }
+
+ private int calculateColumnCount( final Rectangle clientArea ) {
+ BigDecimal width = new BigDecimal( clientArea.width );
+ BigDecimal columnWidth = new BigDecimal( DEFAULT_COLUMN_WIDTH );
+ return width.divide( columnWidth, BigDecimal.ROUND_UP ).intValue();
+ }
+
+ public void testHeaderPositions() {
+ Composite rowHeaderContainer = headerController.getRowContainer();
+ Composite columnHeaderContainer = headerController.getColumnContainer();
+
+ layout.layout( shell, false );
+ Rectangle clientArea = shell.getClientArea();
+
+ // Note that the headers belong to the trimmings of the surrounding
+ // composite and therefore they are not rendered into the clientarea
+ // of the shell. This may not feels reasonable but the spreadsheet is
+ // not intended to be used in another composites besides the SpreadSheet.
+ // TODO [fappel]: This construct may needs a second thought though.
+ Rectangle rowBounds = rowHeaderContainer.getBounds();
+ assertEquals( clientArea.x - DEFAULT_ROW_HEADER_WIDTH, rowBounds.x );
+ assertEquals( clientArea.y, rowBounds.y );
+ assertEquals( DEFAULT_ROW_HEADER_WIDTH - DEFAULT_GRID_LINE_BREADTH,
+ rowBounds.width );
+ assertEquals( clientArea.height, rowBounds.height );
+
+ Rectangle columnBounds = columnHeaderContainer.getBounds();
+ assertEquals( clientArea.x, columnBounds.x );
+ assertEquals( clientArea.y - DEFAULT_COLUMN_HEADER_HEIGHT, columnBounds.y );
+ assertEquals( clientArea.width, columnBounds.width );
+ assertEquals( DEFAULT_COLUMN_HEADER_HEIGHT - DEFAULT_GRID_LINE_BREADTH,
+ columnBounds.height);
+
+ // test initial position of the header controls
+ Control[] rowHeaders = headerController.getRowHeaderControls();
+ assertEquals( model.getVisibleRows(), rowHeaders.length );
+ assertEquals( 0, rowHeaders[ 0 ].getBounds().x );
+ assertEquals( 0, rowHeaders[ 0 ].getBounds().y );
+
+ Control[] columnHeaders = headerController.getColumnHeaderControls();
+ assertEquals( model.getVisibleColumns(), columnHeaders.length );
+ assertEquals( 0, columnHeaders[ 0 ].getBounds().x );
+ assertEquals( 0, columnHeaders[ 0 ].getBounds().y );
+
+ // change row and column offset
+ model.setRowOffset( 1 );
+ model.setColumnOffset( 1 );
+ layout.layout( shell, false );
+
+ // test header container bounds with row and column offset
+ rowBounds = rowHeaderContainer.getBounds();
+ assertEquals( clientArea.x - DEFAULT_ROW_HEADER_WIDTH, rowBounds.x );
+ assertEquals( clientArea.y - DEFAULT_ROW_HEIGHT, rowBounds.y );
+ assertEquals( DEFAULT_ROW_HEADER_WIDTH - DEFAULT_GRID_LINE_BREADTH,
+ rowBounds.width );
+ assertEquals( clientArea.height + DEFAULT_ROW_HEIGHT, rowBounds.height );
+
+ columnBounds = columnHeaderContainer.getBounds();
+ assertEquals( clientArea.x - DEFAULT_COLUMN_WIDTH, columnBounds.x );
+ assertEquals( clientArea.y - DEFAULT_COLUMN_HEADER_HEIGHT, columnBounds.y );
+ assertEquals( clientArea.width + DEFAULT_COLUMN_WIDTH, columnBounds.width );
+ assertEquals( DEFAULT_COLUMN_HEADER_HEIGHT - DEFAULT_GRID_LINE_BREADTH,
+ columnBounds.height);
+
+ // test header controls positions with row and column offset
+ rowHeaders = headerController.getRowHeaderControls();
+ assertEquals( model.getVisibleRows(), rowHeaders.length );
+ assertEquals( 0, rowHeaders[ 0 ].getBounds().x );
+ assertEquals( DEFAULT_ROW_HEIGHT, rowHeaders[ 0 ].getBounds().y );
+ assertEquals( DEFAULT_ROW_HEIGHT * 2, rowHeaders[ 1 ].getBounds().y );
+
+ columnHeaders = headerController.getColumnHeaderControls();
+ assertEquals( model.getVisibleColumns(), columnHeaders.length );
+ assertEquals( DEFAULT_COLUMN_WIDTH, columnHeaders[ 0 ].getBounds().x );
+ assertEquals( DEFAULT_COLUMN_WIDTH * 2, columnHeaders[ 1 ].getBounds().x );
+ assertEquals( 0, columnHeaders[ 0 ].getBounds().y );
+ }
+
+ public void testCellBoundsAndPositionCalculation() {
+ SpreadSheetFixture.populateModel( model );
+ layout.layout( shell, false );
+ Composite container = cellController.getCellContainer();
+ assertEquals( shell.getClientArea(), container.getBounds() );
+
+ Control[] children = container.getChildren();
+ Control cell = null;
+ Control cell_0_0 = null;
+ for( int i = 0; i < children.length; i++ ) {
+ if( CellController.isCellControl( children[ i ] ) ) {
+ SpreadSheetData data = ( SpreadSheetData )children[ i ].getLayoutData();
+ if( new CellPosition( 0, 0 ).equals( data.getPosition() ) ) {
+ cell_0_0 = children[ i ];
+ }
+ if( new CellPosition( 2, 1 ).equals( data.getPosition() ) ) {
+ cell = children[ i ];
+ }
+ }
+ }
+
+ // test cell position
+ Rectangle cellBounds = cell.getBounds();
+ Rectangle clientArea = container.getClientArea();
+ assertEquals( clientArea.x + DEFAULT_COLUMN_WIDTH, cellBounds.x );
+ assertEquals( clientArea.y + DEFAULT_ROW_HEIGHT * 2, cellBounds.y );
+ assertEquals( DEFAULT_CELL_WIDTH, cellBounds.width );
+ assertEquals( DEFAULT_CELL_HEIGHT, cellBounds.height );
+
+ // test cellcontainer bounds with row and column offset
+ model.setRowOffset( 1 );
+ model.setColumnOffset( 1 );
+ layout.layout( shell, false );
+ int columnWidth = model.getColumnWidth( 0 );
+ int rowHeight = model.getRowHeight( 0 );
+ Rectangle cellContainerBounds = container.getBounds();
+ Rectangle shellClientArea = shell.getClientArea();
+ assertEquals( shellClientArea.x - columnWidth, cellContainerBounds.x );
+ assertEquals( shellClientArea.y - rowHeight, cellContainerBounds.y );
+ assertEquals( shellClientArea.width + columnWidth,
+ cellContainerBounds.width );
+ assertEquals( shellClientArea.height + rowHeight,
+ cellContainerBounds.height );
+
+ // test movement of upper left cell in case of row and column offset
+ SpreadSheetData data = ( SpreadSheetData )cell_0_0.getLayoutData();
+ int visibleRows = model.getVisibleRows();
+ int visibleColumns = model.getVisibleColumns();
+ CellPosition expectedPosition
+ = new CellPosition( visibleRows, visibleColumns );
+ assertEquals( expectedPosition, data.getPosition() );
+
+ // ensure position of upper left cell after offsets have been removed
+ model.setRowOffset( 0 );
+ model.setColumnOffset( 0 );
+ layout.layout( shell, false );
+ data = ( SpreadSheetData )cell_0_0.getLayoutData();
+ assertEquals( new CellPosition( 0, 0 ), data.getPosition() );
+
+ // test cell bounds with offset and changed size
+ int height = 150;
+ model.setRowHeight( 2, height );
+ int width = 300;
+ model.setColumnWidth( 1, width );
+ layout.layout( shell, false );
+ cellBounds = cell.getBounds();
+ clientArea = container.getClientArea();
+ assertEquals( clientArea.x + DEFAULT_COLUMN_WIDTH, cellBounds.x );
+ assertEquals( clientArea.y + DEFAULT_ROW_HEIGHT * 2, cellBounds.y );
+ assertEquals( width - DEFAULT_GRID_LINE_BREADTH , cellBounds.width );
+ assertEquals( height - DEFAULT_GRID_LINE_BREADTH, cellBounds.height );
+
+ model.setColumnOffset( 1 );
+ model.setRowOffset( 1 );
+ layout.layout( shell, false );
+ cellBounds = cell.getBounds();
+ clientArea = container.getClientArea();
+ assertEquals( clientArea.x + DEFAULT_COLUMN_WIDTH, cellBounds.x );
+ assertEquals( clientArea.y + DEFAULT_ROW_HEIGHT * 2, cellBounds.y );
+ assertEquals( width - DEFAULT_GRID_LINE_BREADTH , cellBounds.width );
+ assertEquals( height - DEFAULT_GRID_LINE_BREADTH, cellBounds.height );
+ }
+
+ public void testLayoutingOfAdditionalChild() {
+ Label label = new Label( shell, SWT.NONE );
+
+ // root coordinates
+ label.setLayoutData( new SpreadSheetData( 0, 0 ) );
+ layout.layout( shell, false );
+ Rectangle clientArea = shell.getClientArea();
+ Rectangle bounds = label.getBounds();
+ assertEquals( clientArea.x, bounds.x );
+ assertEquals( clientArea.y, bounds.y );
+ assertEquals( DEFAULT_CELL_WIDTH, bounds.width );
+ assertEquals( DEFAULT_CELL_HEIGHT, bounds.height );
+
+
+ // arbitrary coordinates
+ label.setLayoutData( new SpreadSheetData( 2, 1 ) );
+ layout.layout( shell, false );
+ bounds = label.getBounds();
+ assertEquals( clientArea.x + DEFAULT_COLUMN_WIDTH, bounds.x );
+ assertEquals( clientArea.y + DEFAULT_ROW_HEIGHT * 2, bounds.y );
+ assertEquals( DEFAULT_CELL_WIDTH, bounds.width );
+ assertEquals( DEFAULT_CELL_HEIGHT, bounds.height );
+
+
+ // change height of a certain row
+ assertEquals( DEFAULT_ROW_HEIGHT, model.getRowHeight( ROW_TO_TEST ) );
+ model.setRowHeight( ROW_TO_TEST, ROW_TO_TEST_HEIGHT );
+ label.setLayoutData( new SpreadSheetData( ROW_TO_TEST, 0 ) );
+ layout.layout( shell, false );
+ bounds = label.getBounds();
+ assertEquals( clientArea.x, bounds.x );
+ assertEquals( clientArea.y + DEFAULT_ROW_HEIGHT * ROW_TO_TEST, bounds.y );
+ assertEquals( DEFAULT_CELL_WIDTH, bounds.width );
+ assertEquals( ROW_TO_TEST_HEIGHT, model.getRowHeight( ROW_TO_TEST ) );
+
+ label.setLayoutData( new SpreadSheetData( ROW_TO_TEST + 1, 0 ) );
+ layout.layout( shell, false );
+ bounds = label.getBounds();
+ assertEquals( clientArea.x, bounds.x );
+ int yPos
+ = clientArea.y + DEFAULT_ROW_HEIGHT * ROW_TO_TEST + ROW_TO_TEST_HEIGHT;
+ assertEquals( yPos, bounds.y );
+ assertEquals( DEFAULT_CELL_WIDTH, bounds.width );
+ assertEquals( ROW_TO_TEST_HEIGHT, model.getRowHeight( ROW_TO_TEST ) );
+
+ model.setRowHeight( ROW_TO_TEST, SpreadSheetModel.DEFAULT_ROW_HEIGHT );
+ assertEquals( DEFAULT_ROW_HEIGHT, model.getRowHeight( ROW_TO_TEST ) );
+
+
+ // change width of a certain column
+ assertEquals( DEFAULT_COLUMN_WIDTH,
+ model.getColumnWidth( COLUMN_TO_TEST ) );
+ model.setColumnWidth( COLUMN_TO_TEST, COLUMN_TO_TEST_WIDTH );
+ label.setLayoutData( new SpreadSheetData( 0, COLUMN_TO_TEST ) );
+ layout.layout( shell, false );
+ bounds = label.getBounds();
+ int xPos = clientArea.x + DEFAULT_COLUMN_WIDTH * COLUMN_TO_TEST;
+ assertEquals( xPos, bounds.x );
+ assertEquals( clientArea.y, bounds.y );
+ assertEquals( COLUMN_TO_TEST_WIDTH,
+ model.getColumnWidth( COLUMN_TO_TEST ) );
+ assertEquals( DEFAULT_CELL_HEIGHT, bounds.height );
+
+ label.setLayoutData( new SpreadSheetData( 0, COLUMN_TO_TEST + 1 ) );
+ layout.layout( shell, false );
+ bounds = label.getBounds();
+ xPos = clientArea.x
+ + DEFAULT_COLUMN_WIDTH * COLUMN_TO_TEST
+ + COLUMN_TO_TEST_WIDTH;
+ assertEquals( xPos, bounds.x );
+ assertEquals( clientArea.y, bounds.y );
+ assertEquals( COLUMN_TO_TEST_WIDTH,
+ model.getColumnWidth( COLUMN_TO_TEST ) );
+ assertEquals( DEFAULT_CELL_HEIGHT, bounds.height );
+
+ model.setColumnWidth( COLUMN_TO_TEST, DEFAULT_COLUMN_WIDTH );
+ assertEquals( DEFAULT_COLUMN_WIDTH,
+ model.getColumnWidth( COLUMN_TO_TEST ) );
+ }
+
+ public void testParams() {
+ try {
+ model.setColumnWidth( -1 , 4 );
+ fail( "Negative column index isn't allowed." );
+ } catch( final IllegalArgumentException iae ) {
+ // expected
+ }
+ try {
+ model.setColumnWidth( 1 , -4 );
+ fail( "Negative column width isn't allowed." );
+ } catch( final IllegalArgumentException iae ) {
+ // expected
+ }
+ try {
+ model.setRowHeight( -1 , 4 );
+ fail( "Negative row index isn't allowed." );
+ } catch( final IllegalArgumentException iae ) {
+ // expected
+ }
+ try {
+ model.setRowHeight( 1 , -4 );
+ fail( "Negative row height isn't allowed." );
+ } catch( final IllegalArgumentException iae ) {
+ // expected
+ }
+ }
+
+ protected void setUp() throws Exception {
+ RWTFixture.setUp();
+ display = new Display();
+ shell = new Shell( display, SWT.SHELL_TRIM );
+ shell.setSize( DEFAULT_SHELL_WIDTH, DEFAULT_SHELL_HEIGHT );
+
+ model = new SpreadSheetModel();
+ cellController = new CellController( shell, model );
+ headerController = new HeaderController( shell, model );
+ sliderController = new SliderController( shell, headerController, model );
+ layout = new SpreadSheetLayout( model,
+ cellController,
+ sliderController,
+ headerController );
+ }
+
+ protected void tearDown() throws Exception {
+ model = null;
+ cellController = null;
+ headerController = null;
+ sliderController = null;
+ layout = null;
+
+ if( display != null && !display.isDisposed() ) {
+ display.dispose();
+ shell = null;
+ display = null;
+ }
+ RWTFixture.tearDown();
+ }
+}
diff --git a/tests/org.eclipse.rap.rwt.custom.test/src/org/eclipse/swt/custom/SpreadSheet_Test.java b/tests/org.eclipse.rap.rwt.custom.test/src/org/eclipse/swt/custom/SpreadSheet_Test.java
new file mode 100644
index 0000000..7100fbc
--- /dev/null
+++ b/tests/org.eclipse.rap.rwt.custom.test/src/org/eclipse/swt/custom/SpreadSheet_Test.java
@@ -0,0 +1,348 @@
+// Created on 08.08.2009
+package org.eclipse.swt.custom;
+
+
+import junit.framework.TestCase;
+
+import org.eclipse.rwt.lifecycle.PhaseId;
+import org.eclipse.swt.RWTFixture;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.*;
+
+/*
+ * TODO [fappel]: complete MouseDown + MouseUp handling
+ * TODO [fappel]: check Display#map
+ * TODO [fappel]: visibility of trimmings (header, slider)
+ */
+public class SpreadSheet_Test extends TestCase {
+ private static final int DEFAULT_COLUMN_HEADER_HEIGHT
+ = HeaderController.DEFAULT_COLUMN_HEADER_HEIGHT;
+ private static final int DEFAULT_ROW_HEADER_WIDTH
+ = HeaderController.DEFAULT_ROW_HEADER_WIDTH;
+ private static final int SLIDER_BREADTH = SliderController.SLIDER_BREADTH;
+ private static final String TEST_TEXT = "TextToSet";
+ private static final int ROW_TO_SELECT = 2;
+ private static final int COLUMN_TO_SELECT = 4;
+
+ private Display display;
+ private Shell shell;
+
+ public void testTextSetterAndGetter() {
+ SpreadSheet sheet = new SpreadSheet( shell, SWT.NONE );
+
+ try {
+ sheet.setText( null, ROW_TO_SELECT, COLUMN_TO_SELECT );
+ fail( "Parameter text must not be null." );
+ } catch( final IllegalArgumentException iae ) {
+ // expected
+ }
+ try {
+ sheet.setText( TEST_TEXT, -1, COLUMN_TO_SELECT );
+ fail( "Parameter rowIndex must not be negative." );
+ } catch( final IllegalArgumentException iae ) {
+ // expected
+ }
+ try {
+ sheet.setText( TEST_TEXT, ROW_TO_SELECT, -1 );
+ fail( "Parameter columnIndex must not be negative." );
+ } catch( final IllegalArgumentException iae ) {
+ // expected
+ }
+ try {
+ sheet.getText( -1, COLUMN_TO_SELECT );
+ fail( "Parameter columnIndex must not be negative." );
+ } catch( final IllegalArgumentException iae ) {
+ // expected
+ }
+ try {
+ sheet.getText( ROW_TO_SELECT, -1 );
+ fail( "Parameter rowIndex must not be negative." );
+ } catch( final IllegalArgumentException iae ) {
+ // expected
+ }
+
+ assertEquals( "", sheet.getText( ROW_TO_SELECT, COLUMN_TO_SELECT ) );
+ sheet.setText( TEST_TEXT, ROW_TO_SELECT, COLUMN_TO_SELECT );
+ assertEquals( TEST_TEXT,
+ sheet.getText( ROW_TO_SELECT, COLUMN_TO_SELECT ) );
+ assertEquals( "", sheet.getText( ROW_TO_SELECT, COLUMN_TO_SELECT + 1 ) );
+ sheet.setText( "", ROW_TO_SELECT, COLUMN_TO_SELECT );
+ assertEquals( "", sheet.getText( ROW_TO_SELECT, COLUMN_TO_SELECT ) );
+
+ sheet.setText( TEST_TEXT, ROW_TO_SELECT, COLUMN_TO_SELECT );
+ RWTFixture.fakePhase( PhaseId.PREPARE_UI_ROOT );
+ shell.layout( true, true );
+ CellPosition position
+ = new CellPosition( ROW_TO_SELECT, COLUMN_TO_SELECT );
+ Label control = findCellControl( sheet, position );
+ String text = control.getText();
+ assertEquals( TEST_TEXT, text );
+
+ }
+
+ public void testSlider() {
+ SpreadSheet spreadSheet = new SpreadSheet( shell, SWT.NONE );
+ RWTFixture.fakePhase( PhaseId.PREPARE_UI_ROOT );
+ shell.layout();
+
+ Slider hScroll = null;
+ Slider vScroll = null;
+ Control[] children = spreadSheet.getChildren();
+ for( int i = 0; i < children.length; i++ ) {
+ if( children[ i ] instanceof Slider ) {
+ Slider slider = ( Slider )children[ i ];
+ if( ( slider.getStyle() & SWT.H_SCROLL ) > 0 ) {
+ assertNull( hScroll );
+ hScroll = slider;
+ } else {
+ assertNull( vScroll );
+ vScroll = slider;
+ }
+ }
+ }
+ assertNotNull( hScroll );
+ assertNotNull( vScroll );
+ Rectangle shellClientArea = shell.getClientArea();
+
+
+ // slider positions
+ assertEquals( 0, hScroll.getLocation().x );
+ int yPosHScroll = shellClientArea.height - SLIDER_BREADTH;
+ assertEquals( yPosHScroll, hScroll.getLocation().y );
+ int widthHScroll = shellClientArea.width - SLIDER_BREADTH;
+ assertEquals( widthHScroll, hScroll.getSize().x );
+ assertEquals( SLIDER_BREADTH, hScroll.getSize().y );
+
+ int xPosVScroll = shellClientArea.width - SLIDER_BREADTH;
+ assertEquals( xPosVScroll, vScroll.getLocation().x );
+ assertEquals( 0, vScroll.getLocation().y );
+ assertEquals( SLIDER_BREADTH, vScroll.getSize().x );
+ int heightVScroll = shellClientArea.height - SLIDER_BREADTH;
+ assertEquals( heightVScroll, vScroll.getSize().y );
+
+
+ // initial thumb sizes
+ int hMax = hScroll.getThumb() + SliderController.PAGE_INCREMENT_OFFSET;
+ assertEquals( hMax, hScroll.getMaximum() );
+ assertEquals( 0, hScroll.getSelection() );
+ int vMax = vScroll.getThumb() + SliderController.PAGE_INCREMENT_OFFSET;
+ assertEquals( vMax, vScroll.getMaximum() );
+ assertEquals( 0, vScroll.getSelection() );
+
+ CellPosition position
+ = new CellPosition( ROW_TO_SELECT, COLUMN_TO_SELECT );
+ spreadSheet.setText( TEST_TEXT, position );
+ Label cell = findCellControl( spreadSheet, position );
+ assertSame( TEST_TEXT, cell.getText() );
+ SpreadSheetData cellData = ( SpreadSheetData )cell.getLayoutData();
+ assertEquals( position, cellData.getPosition() );
+
+
+ // row- and column-offset
+ CellPosition position_0_0 = new CellPosition( 0, 0 );
+ Label cell_0_0 = findCellControl( spreadSheet, position_0_0 );
+ assertNull( cell_0_0 );
+ spreadSheet.setText( TEST_TEXT, position_0_0 );
+ cell_0_0 = findCellControl( spreadSheet, position_0_0 );
+ assertSame( TEST_TEXT, cell_0_0.getText() );
+
+ spreadSheet.setColumnOffset( 1 );
+ assertSame( "", cell_0_0.getText() );
+ assertSame( TEST_TEXT, cell.getText() );
+ assertEquals( 1, hScroll.getSelection() );
+
+ spreadSheet.setColumnOffset( 0 );
+ assertSame( TEST_TEXT, cell_0_0.getText() );
+ assertSame( TEST_TEXT, cell.getText() );
+ assertEquals( 0, hScroll.getSelection() );
+
+ spreadSheet.setRowOffset( 1 );
+ assertSame( "", cell_0_0.getText() );
+ assertSame( TEST_TEXT, cell.getText() );
+ assertEquals( 1, vScroll.getSelection() );
+
+ spreadSheet.setRowOffset( 0 );
+ assertSame( TEST_TEXT, cell_0_0.getText() );
+ assertSame( TEST_TEXT, cell.getText() );
+ assertEquals( 0, vScroll.getSelection() );
+ }
+
+ public void testClientArea() {
+ SpreadSheet spreadSheet = new SpreadSheet( shell, SWT.NONE );
+ RWTFixture.fakePhase( PhaseId.PREPARE_UI_ROOT );
+ shell.layout();
+
+ Rectangle shellClientArea = shell.getClientArea();
+ Rectangle clientArea = spreadSheet.getClientArea();
+ assertEquals( DEFAULT_ROW_HEADER_WIDTH, clientArea.x );
+ assertEquals( DEFAULT_COLUMN_HEADER_HEIGHT, clientArea.y );
+ int width = shellClientArea.width
+ - SLIDER_BREADTH
+ - DEFAULT_ROW_HEADER_WIDTH;
+ assertEquals( width, clientArea.width );
+ int height = shellClientArea.height
+ - SLIDER_BREADTH
+ - DEFAULT_COLUMN_HEADER_HEIGHT;
+ assertEquals( height, clientArea.height );
+ }
+
+ public void testMouseHandler() {
+ SpreadSheet sheet = new SpreadSheet( shell, SWT.NONE );
+ CellPosition position
+ = new CellPosition( ROW_TO_SELECT, COLUMN_TO_SELECT );
+ sheet.getCellEditorController().handleMouseDown( position );
+ sheet.getCellEditorController().handleMouseUp( position );
+ CellEditorController controller = sheet.getCellEditorController();
+ Control control = controller.getControl();
+ SpreadSheetData data = ( SpreadSheetData )control.getLayoutData();
+ assertEquals( ROW_TO_SELECT, data.getRowIndex() );
+ assertEquals( COLUMN_TO_SELECT, data.getColumnIndex() );
+ }
+
+ public void testCellEditorTextInAndOutput() {
+ SpreadSheet sheet = new SpreadSheet( shell, SWT.NONE );
+ shell.layout( true, true );
+
+ CellEditorController controller = sheet.getCellEditorController();
+ assertEquals( "", controller.getText() );
+
+ sheet.setText( TEST_TEXT, ROW_TO_SELECT, COLUMN_TO_SELECT );
+ CellPosition position
+ = new CellPosition( ROW_TO_SELECT, COLUMN_TO_SELECT );
+ Label cellControl = findCellControl( sheet, position );
+ assertEquals( TEST_TEXT, cellControl.getText() );
+ assertEquals( "", controller.getText() );
+ sheet.getCellEditorController().handleMouseDown( position );
+ sheet.getCellEditorController().handleMouseUp( position );
+ assertEquals( TEST_TEXT, controller.getText() );
+
+ controller.setText( "" );
+ assertEquals( TEST_TEXT, sheet.getText( ROW_TO_SELECT, COLUMN_TO_SELECT ) );
+ assertEquals( TEST_TEXT, cellControl.getText() );
+ KeyEvent evt = new KeyEvent( controller.getControl(),
+ KeyEvent.KEY_PRESSED );
+ evt.keyCode = SWT.ARROW_DOWN;
+ controller.handleKeyPressed( evt );
+ assertEquals( "", sheet.getText( ROW_TO_SELECT, COLUMN_TO_SELECT ) );
+ assertTrue( "", cellControl.isDisposed() );
+ assertEquals( "", controller.getText() );
+
+ sheet.setText( TEST_TEXT, ROW_TO_SELECT + 1, COLUMN_TO_SELECT );
+ assertEquals( TEST_TEXT, controller.getText() );
+ }
+
+ private Label findCellControl( final SpreadSheet sheet,
+ final CellPosition position )
+ {
+ Label result = null;
+ Control[] children = sheet.getChildren();
+ for( int i = 0; result == null && i < children.length; i++ ) {
+ if( children[ i ] instanceof Composite ) {
+ Composite cellContainer = ( Composite )children[ i ];
+ Control[] cellControls = cellContainer.getChildren();
+ for( int j = 0; j < cellControls.length; j++ ) {
+ SpreadSheetData data
+ = ( SpreadSheetData )cellControls[ j ].getLayoutData();
+ Control cellEditorControl
+ = sheet.getCellEditorController().getControl();
+ if( data != null
+ && position.equals( data.getPosition() )
+ && cellControls[ j ] != cellEditorControl )
+ {
+ result = ( Label )cellControls[ j ];
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ public void testKeyHandler() {
+ SpreadSheet sheet = new SpreadSheet( shell, SWT.NONE );
+ CellEditorController controller = sheet.getCellEditorController();
+ Control cellEditor = controller.getControl();
+ SpreadSheetData data = ( SpreadSheetData )cellEditor.getLayoutData();
+ assertEquals( 0, data.getRowIndex() );
+ assertEquals( 0, data.getColumnIndex() );
+
+ KeyEvent evt = new KeyEvent( cellEditor, KeyEvent.KEY_PRESSED );
+ evt.keyCode = SWT.ARROW_DOWN;
+ controller.handleKeyPressed( evt );
+ data = ( SpreadSheetData )cellEditor.getLayoutData();
+ assertEquals( 1, data.getRowIndex() );
+ assertEquals( 0, data.getColumnIndex() );
+
+ evt = new KeyEvent( cellEditor, KeyEvent.KEY_PRESSED );
+ evt.keyCode = SWT.ARROW_RIGHT;
+ controller.handleKeyPressed( evt );
+ data = ( SpreadSheetData )cellEditor.getLayoutData();
+ assertEquals( 1, data.getRowIndex() );
+ assertEquals( 1, data.getColumnIndex() );
+
+ evt = new KeyEvent( cellEditor, KeyEvent.KEY_PRESSED );
+ evt.keyCode = SWT.ARROW_UP;
+ controller.handleKeyPressed( evt );
+ data = ( SpreadSheetData )cellEditor.getLayoutData();
+ assertEquals( 0, data.getRowIndex() );
+ assertEquals( 1, data.getColumnIndex() );
+
+ evt = new KeyEvent( cellEditor, KeyEvent.KEY_PRESSED );
+ evt.keyCode = SWT.ARROW_LEFT;
+ controller.handleKeyPressed( evt );
+ data = ( SpreadSheetData )cellEditor.getLayoutData();
+ assertEquals( 0, data.getRowIndex() );
+ assertEquals( 0, data.getColumnIndex() );
+
+ evt = new KeyEvent( cellEditor, KeyEvent.KEY_PRESSED );
+ evt.keyCode = SWT.ARROW_LEFT;
+ controller.handleKeyPressed( evt );
+ data = ( SpreadSheetData )cellEditor.getLayoutData();
+ assertEquals( 0, data.getRowIndex() );
+ assertEquals( 0, data.getColumnIndex() );
+
+ evt = new KeyEvent( cellEditor, KeyEvent.KEY_PRESSED );
+ evt.keyCode = SWT.ARROW_UP;
+ controller.handleKeyPressed( evt );
+ data = ( SpreadSheetData )cellEditor.getLayoutData();
+ assertEquals( 0, data.getRowIndex() );
+ assertEquals( 0, data.getColumnIndex() );
+ }
+
+ public void testFocusHandling() {
+ shell.open();
+ SpreadSheet sheet = new SpreadSheet( shell, SWT.NONE );
+ CellEditorController controller = sheet.getCellEditorController();
+ Control cellEditor = controller.getControl();
+ assertFalse( cellEditor.isFocusControl() );
+ sheet.setFocus();
+ assertTrue( cellEditor.isFocusControl() );
+ Text text = new Text( shell, SWT.NONE );
+ text.setFocus();
+ assertFalse( cellEditor.isFocusControl() );
+ CellPosition position = new CellPosition( 1, 1 );
+ sheet.getCellEditorController().handleMouseDown( position );
+ sheet.getCellEditorController().handleMouseUp( position );
+ assertTrue( cellEditor.isFocusControl() );
+ }
+
+ protected void setUp() throws Exception {
+ RWTFixture.setUp();
+ display = new Display();
+ shell = new Shell( display, SWT.SHELL_TRIM );
+ shell.setSize( 600, 400 );
+ shell.setLayout( new FillLayout() );
+ }
+
+ protected void tearDown() throws Exception {
+ if( display != null && !display.isDisposed() ) {
+ display.dispose();
+ shell = null;
+ display = null;
+ }
+ RWTFixture.tearDown();
+ }
+
+}