Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwbeaton2009-06-23 19:26:33 +0000
committerwbeaton2009-06-23 19:26:33 +0000
commit97c56ac8b09edf9c926bb11093b7e1978ed6d699 (patch)
tree56cdb11cfd626c47b87de81ff12d99e37581b35d
parentd7af29d298adc76722a787d2a6eec41dca26efbd (diff)
downloadarticles-97c56ac8b09edf9c926bb11093b7e1978ed6d699.tar.gz
articles-97c56ac8b09edf9c926bb11093b7e1978ed6d699.tar.xz
articles-97c56ac8b09edf9c926bb11093b7e1978ed6d699.zip
NEW - bug 251055: [Article] BIRT Extension Mechanism : Part 2
https://bugs.eclipse.org/bugs/show_bug.cgi?id=251055
-rw-r--r--Article-BIRT-ExtensionTutorial2/extension-tutorial-2.zipbin0 -> 24918 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/border-effect.pngbin0 -> 2291 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/builder-expression-support.pngbin0 -> 6298 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/builder-extension.pngbin0 -> 12811 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/builder.pngbin0 -> 6235 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/contextmenu.pngbin0 -> 8576 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/expression-builder.pngbin0 -> 19740 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/figure-ui-extension.pngbin0 -> 12019 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/image-ui-extension.pngbin0 -> 12012 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/image-ui-layout.pngbin0 -> 1880 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/image-ui-preview.pngbin0 -> 3045 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/label-border.pngbin0 -> 9227 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/label-general.pngbin0 -> 10195 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/linux_only.gifbin0 -> 174 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/menu-extension.pngbin0 -> 14925 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/properties.pngbin0 -> 3185 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/property-page-expression-support.pngbin0 -> 8811 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/property-page-extension-1.pngbin0 -> 13287 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/property-page-extension-2.pngbin0 -> 15811 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/property-page-extension-3.pngbin0 -> 13731 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/property-page-extension-4.pngbin0 -> 19818 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/property-page-extension-5.pngbin0 -> 12464 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/rotatedtext-border.pngbin0 -> 9659 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/rotatedtext-custom.pngbin0 -> 6793 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/rotatedtext-general.pngbin0 -> 9007 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/tab-structure.pngbin0 -> 13669 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/test-expression-1.pngbin0 -> 3328 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/test-expression-2.pngbin0 -> 19544 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/test-expression-3.pngbin0 -> 20067 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/text-expression-type.pngbin0 -> 13808 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/tip.gifbin0 -> 406 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/tryit.gifbin0 -> 309 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/images/win_only.gifbin0 -> 221 bytes
-rw-r--r--Article-BIRT-ExtensionTutorial2/index.html1767
34 files changed, 1767 insertions, 0 deletions
diff --git a/Article-BIRT-ExtensionTutorial2/extension-tutorial-2.zip b/Article-BIRT-ExtensionTutorial2/extension-tutorial-2.zip
new file mode 100644
index 0000000..56be45d
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/extension-tutorial-2.zip
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/border-effect.png b/Article-BIRT-ExtensionTutorial2/images/border-effect.png
new file mode 100644
index 0000000..8287c58
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/border-effect.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/builder-expression-support.png b/Article-BIRT-ExtensionTutorial2/images/builder-expression-support.png
new file mode 100644
index 0000000..4f17e59
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/builder-expression-support.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/builder-extension.png b/Article-BIRT-ExtensionTutorial2/images/builder-extension.png
new file mode 100644
index 0000000..f85e5b6
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/builder-extension.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/builder.png b/Article-BIRT-ExtensionTutorial2/images/builder.png
new file mode 100644
index 0000000..8289c49
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/builder.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/contextmenu.png b/Article-BIRT-ExtensionTutorial2/images/contextmenu.png
new file mode 100644
index 0000000..b07c6ed
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/contextmenu.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/expression-builder.png b/Article-BIRT-ExtensionTutorial2/images/expression-builder.png
new file mode 100644
index 0000000..c1afb44
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/expression-builder.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/figure-ui-extension.png b/Article-BIRT-ExtensionTutorial2/images/figure-ui-extension.png
new file mode 100644
index 0000000..58fa1ee
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/figure-ui-extension.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/image-ui-extension.png b/Article-BIRT-ExtensionTutorial2/images/image-ui-extension.png
new file mode 100644
index 0000000..6baf49c
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/image-ui-extension.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/image-ui-layout.png b/Article-BIRT-ExtensionTutorial2/images/image-ui-layout.png
new file mode 100644
index 0000000..03ed817
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/image-ui-layout.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/image-ui-preview.png b/Article-BIRT-ExtensionTutorial2/images/image-ui-preview.png
new file mode 100644
index 0000000..ac7f171
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/image-ui-preview.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/label-border.png b/Article-BIRT-ExtensionTutorial2/images/label-border.png
new file mode 100644
index 0000000..ce4c6e3
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/label-border.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/label-general.png b/Article-BIRT-ExtensionTutorial2/images/label-general.png
new file mode 100644
index 0000000..68703e4
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/label-general.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/linux_only.gif b/Article-BIRT-ExtensionTutorial2/images/linux_only.gif
new file mode 100644
index 0000000..7c135cf
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/linux_only.gif
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/menu-extension.png b/Article-BIRT-ExtensionTutorial2/images/menu-extension.png
new file mode 100644
index 0000000..9f008ac
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/menu-extension.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/properties.png b/Article-BIRT-ExtensionTutorial2/images/properties.png
new file mode 100644
index 0000000..d09e2b3
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/properties.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/property-page-expression-support.png b/Article-BIRT-ExtensionTutorial2/images/property-page-expression-support.png
new file mode 100644
index 0000000..ccb2dae
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/property-page-expression-support.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/property-page-extension-1.png b/Article-BIRT-ExtensionTutorial2/images/property-page-extension-1.png
new file mode 100644
index 0000000..d6c1c75
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/property-page-extension-1.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/property-page-extension-2.png b/Article-BIRT-ExtensionTutorial2/images/property-page-extension-2.png
new file mode 100644
index 0000000..152e570
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/property-page-extension-2.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/property-page-extension-3.png b/Article-BIRT-ExtensionTutorial2/images/property-page-extension-3.png
new file mode 100644
index 0000000..30d716f
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/property-page-extension-3.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/property-page-extension-4.png b/Article-BIRT-ExtensionTutorial2/images/property-page-extension-4.png
new file mode 100644
index 0000000..f7367d5
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/property-page-extension-4.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/property-page-extension-5.png b/Article-BIRT-ExtensionTutorial2/images/property-page-extension-5.png
new file mode 100644
index 0000000..f50e68c
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/property-page-extension-5.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/rotatedtext-border.png b/Article-BIRT-ExtensionTutorial2/images/rotatedtext-border.png
new file mode 100644
index 0000000..bb374fc
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/rotatedtext-border.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/rotatedtext-custom.png b/Article-BIRT-ExtensionTutorial2/images/rotatedtext-custom.png
new file mode 100644
index 0000000..25b6b82
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/rotatedtext-custom.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/rotatedtext-general.png b/Article-BIRT-ExtensionTutorial2/images/rotatedtext-general.png
new file mode 100644
index 0000000..33ee326
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/rotatedtext-general.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/tab-structure.png b/Article-BIRT-ExtensionTutorial2/images/tab-structure.png
new file mode 100644
index 0000000..e4cbc2e
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/tab-structure.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/test-expression-1.png b/Article-BIRT-ExtensionTutorial2/images/test-expression-1.png
new file mode 100644
index 0000000..8b969d1
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/test-expression-1.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/test-expression-2.png b/Article-BIRT-ExtensionTutorial2/images/test-expression-2.png
new file mode 100644
index 0000000..143b3bc
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/test-expression-2.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/test-expression-3.png b/Article-BIRT-ExtensionTutorial2/images/test-expression-3.png
new file mode 100644
index 0000000..deea48c
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/test-expression-3.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/text-expression-type.png b/Article-BIRT-ExtensionTutorial2/images/text-expression-type.png
new file mode 100644
index 0000000..6e270b5
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/text-expression-type.png
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/tip.gif b/Article-BIRT-ExtensionTutorial2/images/tip.gif
new file mode 100644
index 0000000..77b2451
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/tip.gif
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/tryit.gif b/Article-BIRT-ExtensionTutorial2/images/tryit.gif
new file mode 100644
index 0000000..f4927a4
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/tryit.gif
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/images/win_only.gif b/Article-BIRT-ExtensionTutorial2/images/win_only.gif
new file mode 100644
index 0000000..895f9ca
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/images/win_only.gif
Binary files differ
diff --git a/Article-BIRT-ExtensionTutorial2/index.html b/Article-BIRT-ExtensionTutorial2/index.html
new file mode 100644
index 0000000..ca0da19
--- /dev/null
+++ b/Article-BIRT-ExtensionTutorial2/index.html
@@ -0,0 +1,1767 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+ <title>BIRT Extension Mechanism : Part 2</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
+ <link href="../article.css" type="text/css" rel="stylesheet">
+</head>
+
+<body>
+
+<h1>BIRT Extension Mechanism : Part 2</h1>
+
+<div class="summary">
+<h2>Abstract</h2>
+
+<p>This article introduces the extension mechanism of BIRT report
+model, engine and designer, and shows how to create custom extended report
+items step-by-step.</p>
+
+<div class="author">By&nbsp;Zhiqiang&nbsp;Qian,&nbsp;Actuate Corporation</div>
+
+<div class="copyright">Copyright &copy; 2008
+Actuate Corporation.</div>
+
+<div class="date">September 27, 2008</div>
+
+</div>
+
+<div class="content">
+<h2>Introduction</h2>
+
+<p>For many people, <a href="http://eclipse.org/birt">BIRT(Business
+Intelligence and Reporting Tools)</a> may be only a reporting tool. But in fact,
+the real strength of BIRT is not only its built-in reporting functionality, but
+also its extension capabilities. Using extensions, a user can easily extend the
+functionality of BIRT, creating custom extended report items, adding custom
+emitters, and even having custom editor pages. The power of extensions enables
+the user to create custom reporting features that can meet very specific requirements.
+In this article, we explore how to leverage the BIRT extension mechanism to
+extend the BIRT capabilities. The provided samples cover most of the essential
+extension points that provided by BIRT report model, engine and designer.<br>
+</p>
+
+<p>In <a href="http://www.eclipse.org/articles/article.php?file=Article-BIRT-ExtensionTutorial1/index.html">Part 1</a>,
+we already learned the basics for how to create a custom extended report item for BIRT. In this part, we will further
+explore how to polish the extension UI, improving the usability, and enhancing the extension functionality.
+</p>
+
+<h2>Beyond the Basics</h2>
+
+<p>In this section, we will take a look at how to add additional UI features for custom
+report items, i.e. report item builder, context menu,
+property page, etc., which would undoubtedly improve the look and usability of your cusotm
+report items.
+</p>
+
+<h3>1) WYSIWYG</h3>
+
+<p>Remember how we implemented the look in layout editor for the
+RotatedText item in previous part? Yes, we just chose the simplest Label
+UI
+provider. The disadvantage of this approach is obvious: the label in
+layout view always shows the horizontal text, while the preview shows the angled
+text. This inconsistency may prevent the end users from getting an intuitive picture when
+designing the report. For most modern editors, WYSIWYG is already encompassed as a de facto
+standard. Ideally, we would also want to keep the consistent look between
+our layout view and
+preview. To achieve this, let's take a look at the
+other two types of the UI
+providers:</p>
+<ul>
+ <li><em>Image UI Provider</em>
+ <li><em>Figure UI Provider</em>
+</ul>
+
+<p>The Image UI provider is a straightforward choice. It simply
+requests an image for the UI presentation. So what we need to do here
+is just to render the rotated text to an image and return it.
+This is very similar to what we already have done for the engine
+presentation output.</p>
+
+<p>To register the Image UI provider, we need remove the original Label
+UI provider first, adding the new Image UI provider extension,
+and specifying the implementor class which must implement
+<code>org.eclipse.birt.report.designer.ui.extensions.IReportItemImageProvider</code>.</p>
+
+<p><img style="width: 1000px; height: 226px;" alt="" src="images/image-ui-extension.png"></p>
+
+<p>Here we specify the class as <code>org.eclipse.birt.sample.reportitem.rotatedtext.RotatedTextImageUI</code>,
+the code looks like this:</p>
+
+<pre>public class RotatedTextImageUI implements IReportItemImageProvider
+{
+ public void disposeImage( ExtendedItemHandle handle, Image image )
+ {
+ if ( image != null &amp;&amp; !image.isDisposed( ) )
+ {
+ image.dispose( );
+ }
+ }
+
+ public Image getImage( ExtendedItemHandle handle )
+ {
+ try
+ {
+ IReportItem item = handle.getReportItem( );
+
+ if ( item instanceof RotatedTextItem )
+ {
+ int angle = ( (RotatedTextItem) item ).getRotationAngle( );
+ String text = ( (RotatedTextItem) item ).getText( );
+
+ return SwtGraphicsUtil.createRotatedTextImage( text, angle, null );
+ }
+ }
+ catch ( ExtendedElementException e )
+ {
+ e.printStackTrace( );
+ }
+ return null;
+ }
+}</pre>
+
+<p>The logic seems very trivial. Here we introduced a new class <code>SwtGraphicsUtil</code>.
+Since the Eclipse UI is based on SWT (Standard Widget
+Toolkit), to make it work, we need an SWT port for
+the original <code>SwingGraphicsUtil</code> class. Here is the code for the new SWT version:</p>
+
+<pre>public class SwtGraphicsUtil
+{
+ public static Image createRotatedTextImage( String text, int angle, Font ft )
+ {
+ GC gc = null;
+ try
+ {
+ if ( text == null || text.trim( ).length( ) == 0 )
+ {
+ return null;
+ }
+
+ Display display = Display.getCurrent( );
+
+ gc = new GC( display );
+ if ( ft != null )
+ {
+ gc.setFont( ft );
+ }
+
+ Point pt = gc.textExtent( text );
+
+ gc.dispose( );
+
+ TextLayout tl = new TextLayout( display );
+ if ( ft != null )
+ {
+ tl.setFont( ft );
+ }
+ tl.setText( text );
+
+ return createRotatedImage( tl, pt.x, pt.y, angle );
+ }
+ catch ( Exception e )
+ {
+ e.printStackTrace( );
+
+ if ( gc != null &amp;&amp; !gc.isDisposed( ) )
+ {
+ gc.dispose( );
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return Returns as [rotatedWidth, rotatedHeight, xOffset, yOffset]
+ */
+ public static double[] computedRotatedInfo( int width, int height, int angle )
+ {
+ angle = angle % 360;
+
+ if ( angle &lt; 0 )
+ {
+ angle += 360;
+ }
+
+ if ( angle == 0 )
+ {
+ return new double[]{ width, height, 0, 0 };
+ }
+ else if ( angle == 90 )
+ {
+ return new double[]{ height, width, -width, 0 };
+ }
+ else if ( angle == 180 )
+ {
+ return new double[]{ width, height, -width, -height };
+ }
+ else if ( angle == 270 )
+ {
+ return new double[]{ height, width, 0, -height };
+ }
+ else if ( angle &gt; 0 &amp;&amp; angle &lt; 90 )
+ {
+ double angleInRadians = ( ( -angle * Math.PI ) / 180.0 );
+ double cosTheta = Math.abs( Math.cos( angleInRadians ) );
+ double sineTheta = Math.abs( Math.sin( angleInRadians ) );
+
+ int dW = (int) ( width * cosTheta + height * sineTheta );
+ int dH = (int) ( width * sineTheta + height * cosTheta );
+
+ return new double[]{ dW, dH, -width * sineTheta * sineTheta, width * sineTheta * cosTheta };
+ }
+ else if ( angle &gt; 90 &amp;&amp; angle &lt; 180 )
+ {
+ double angleInRadians = ( ( -angle * Math.PI ) / 180.0 );
+ double cosTheta = Math.abs( Math.cos( angleInRadians ) );
+ double sineTheta = Math.abs( Math.sin( angleInRadians ) );
+
+ int dW = (int) ( width * cosTheta + height * sineTheta );
+ int dH = (int) ( width * sineTheta + height * cosTheta );
+
+ return new double[]{ dW, dH, -( width + height * sineTheta * cosTheta ), -height / 2 };
+ }
+ else if ( angle &gt; 180 &amp;&amp; angle &lt; 270 )
+ {
+ double angleInRadians = ( ( -angle * Math.PI ) / 180.0 );
+ double cosTheta = Math.abs( Math.cos( angleInRadians ) );
+ double sineTheta = Math.abs( Math.sin( angleInRadians ) );
+
+ int dW = (int) ( width * cosTheta + height * sineTheta );
+ int dH = (int) ( width * sineTheta + height * cosTheta );
+
+ return new double[]{ dW, dH, -( width * cosTheta * cosTheta ), -( height + width * cosTheta * sineTheta ) };
+ }
+ else if ( angle &gt; 270 &amp;&amp; angle &lt; 360 )
+ {
+ double angleInRadians = ( ( -angle * Math.PI ) / 180.0 );
+ double cosTheta = Math.abs( Math.cos( angleInRadians ) );
+ double sineTheta = Math.abs( Math.sin( angleInRadians ) );
+
+ int dW = (int) ( width * cosTheta + height * sineTheta );
+ int dH = (int) ( width * sineTheta + height * cosTheta );
+
+ return new double[]{ dW, dH, ( height * cosTheta * sineTheta ), -( height * sineTheta * sineTheta ) };
+ }
+
+ return new double[]{ width, height, 0, 0 };
+ }
+
+ private static Image createRotatedImage( Object src, int width, int height, int angle )
+ {
+ angle = angle % 360;
+
+ if ( angle &lt; 0 )
+ {
+ angle += 360;
+ }
+
+ double[] info = computedRotatedInfo( width, height, angle );
+
+ return renderRotatedObject( src, -angle, (int) info[0], (int) info[1], info[2], info[3] );
+ }
+
+ private static Image renderRotatedObject( Object src, double angle, int width, int height, double tx, double ty )
+ {
+ Display display = Display.getCurrent( );
+
+ Image dest = null;
+ GC gc = null;
+ Transform tf = null;
+
+ try
+ {
+ dest = new Image( Display.getCurrent( ), width, height );
+ gc = new GC( dest );
+
+ gc.setAdvanced( true );
+ gc.setAntialias( SWT.ON );
+ gc.setTextAntialias( SWT.ON );
+
+ tf = new Transform( display );
+ tf.rotate( (float) angle );
+ tf.translate( (float) tx, (float) ty );
+
+ gc.setTransform( tf );
+
+ if ( src instanceof TextLayout )
+ {
+ TextLayout tl = (TextLayout) src;
+ tl.draw( gc, 0, 0 );
+ }
+ else if ( src instanceof Image )
+ {
+ gc.drawImage( (Image) src, 0, 0 );
+ }
+ }
+ catch ( Exception e )
+ {
+ e.printStackTrace( );
+ }
+ finally
+ {
+ if ( gc != null &amp;&amp; !gc.isDisposed( ) )
+ {
+ gc.dispose( );
+ }
+
+ if ( tf != null &amp;&amp; !tf.isDisposed( ) )
+ {
+ tf.dispose( );
+ }
+ }
+ return dest;
+ }
+}</pre>
+
+<p>As the SWT API and resource management are not
+100% same as SWING, you can see some differences between
+the code styles. Nevertheless, the core logic are the same.</p>
+
+<p>Now let's have a test.</p>
+
+<p>Insert a RotatedText item and specify the rotation angle as "45",</p>
+
+<img style="width: 721px; height: 169px;" alt="" src="images/properties.png"><br>
+
+<p>See, now the layout view looks same as the previewing result. The angle
+setting is reflected in the editor too. This kind of WYSIWYG can help the
+end user to quickly spot and identify different report items that having
+different settings.</p>
+
+<p><img style="width: 861px; height: 204px;" alt="" src="images/image-ui-layout.png"></p>
+
+<img style="width: 554px; height: 115px;" alt="" src="images/image-ui-preview.png">
+
+<p>The Image UI provides the basic possibility for
+WYSIWYG support. For most simple extended report items, this is already
+sufficient. While for some custom extended report items that requiring more
+complicated UI behavior, the Figure UI may be another
+choice.</p>
+
+<h3>2) Image VS Figure</h3>
+
+<p>The Figure UI leverages the <code>IFigure</code> interface from <a href="http://www.eclipse.org/gef/">GEF
+(Graphical Editing Framework)</a>. By using this interface, it can provide more flexibility
+and interactive possibilities than the Image UI.</p>
+
+<p>The table below shows a brief summary for major differences between Figure UI and Image UI:</p>
+
+<table style="text-align: left;" border="1" cellpadding="2" cellspacing="2">
+ <tbody>
+ <tr>
+ <td></td>
+ <td style="text-align: center;"><span style="font-weight: bold;">Figure UI</span></td>
+ <td style="text-align: center;"><span style="font-weight: bold;">Image UI</span></td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;"><span style="font-weight: bold;">Life Cycle</span></td>
+ <td style="vertical-align: top;">The life cycle of Figure UI is divided into three parts:<br>
+ <ul>
+ <li><em>Create</em></li>
+ <li><em>Update</em></li>
+ <li><em>Dispose</em></li>
+ </ul>
+The Figure UI normally creates the Figure object once. Each time
+any model state changed, the <em>Update</em> action will be invoked. <br>
+
+The advantage of this pattern is it can support incremental and selective
+update. For each time <em>Update</em> is called, the figure instance
+can check the internal model state and decide if it really needs the update
+or only need update part of the UI.
+ </td>
+
+ <td style="vertical-align: top;">The life cycle of Image UI is divided into two parts:<br>
+ <ul>
+ <li><em>Create</em></li>
+ <li><em>Dispose</em></li>
+ </ul>
+Each time any model state changed, the Image UI will recreate the image presentation.
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;"><span style="font-weight: bold;">Presentation</span></td>
+ <td style="vertical-align: top;">The Figure UI
+allows user to create a hierarchical UI structure.
+This UI structure can be controlled by the GEF layout
+mechanism.</td>
+ <td style="vertical-align: top;">The Image UI can only use one single Image for presentation.</td>
+ </tr>
+ <tr>
+ <td style="vertical-align: top;"><span style="font-weight: bold;">Interactivity</span></td>
+ <td style="vertical-align: top;">The Figure UI allows user to add listener for certain UI events. This allows custom handling for specific UI events. </td>
+ <td style="vertical-align: top;">No interactivity support</td>
+ </tr>
+ </tbody>
+</table>
+
+<p>Image or Figure? That's a question. However though Figure UI provides
+more capability than Image UI, it also requires more comprehensive understanding about the GEF framework,
+while Image UI needs nearly nothing new. So the decision still depends on your real needs.</p>
+
+<p>Here we'll give a simple example for Figure UI practice.
+In this example, we show how to handle the mouse middle-button click event. For
+each middle-button click, we will change the rotation angle by adding
+45 degrees to the original value.</p>
+
+<p>Like using the Image UI provider, we first remove any existing
+Label UI or Image UI provider, adding the new Figure UI
+provider extension,
+and specifying the implementor class which must implement
+<code>org.eclipse.birt.report.designer.ui.extensions.IReportItemFigureProvider</code>.</p>
+
+<p><img style="width: 1002px; height: 227px;" alt="" src="images/figure-ui-extension.png"></p>
+
+<p>we specify the class as <code>org.eclipse.birt.sample.reportitem.rotatedtext.RotatedTextFigureUI</code>, the code is like this:</p>
+
+<pre>public class RotatedTextFigureUI extends ReportItemFigureProvider
+{
+ public IFigure createFigure( ExtendedItemHandle handle )
+ {
+ try
+ {
+ IReportItem item = handle.getReportItem( );
+
+ if ( item instanceof RotatedTextItem )
+ {
+ return new RotatedTextFigure( (RotatedTextItem) item );
+ }
+ }
+ catch ( ExtendedElementException e )
+ {
+ e.printStackTrace( );
+ }
+ return null;
+ }
+
+ public void updateFigure( ExtendedItemHandle handle, IFigure figure )
+ {
+ try
+ {
+ IReportItem item = handle.getReportItem( );
+
+ if ( item instanceof RotatedTextItem )
+ {
+ RotatedTextFigure fig = (RotatedTextFigure) figure;
+
+ fig.setRotatedTextItem( (RotatedTextItem) item );
+ }
+ }
+ catch ( ExtendedElementException e )
+ {
+ e.printStackTrace( );
+ }
+ }
+
+ public void disposeFigure( ExtendedItemHandle handle, IFigure figure )
+ {
+ ( (RotatedTextFigure) figure ).dispose( );
+ }
+}</pre>
+
+<p>In the Figure UI provider, we create an RotatedTextFigure
+instance and delegate all the real logic to this class:</p>
+
+<pre>public class RotatedTextFigure extends Figure
+{
+ private String lastText;
+ private int lastAngle;
+ private Image cachedImage;
+ private RotatedTextItem textItem;
+
+ RotatedTextFigure( RotatedTextItem textItem )
+ {
+ super( );
+
+ this.textItem = textItem;
+
+ addMouseListener( new MouseListener.Stub( ) {
+
+ public void mousePressed( MouseEvent me )
+ {
+ if ( me.button == 2 )
+ {
+ try
+ {
+ RotatedTextFigure.this.textItem.setRotationAngle( normalize( RotatedTextFigure.this.textItem.getRotationAngle( ) + 45 ) );
+ }
+ catch ( SemanticException e )
+ {
+ e.printStackTrace( );
+ }
+ }
+ }
+ } );
+ }
+
+ private int normalize( int angle )
+ {
+ angle = angle % 360;
+
+ if ( angle &lt; 0 )
+ {
+ angle += 360;
+ }
+
+ return angle;
+ }
+
+ public Dimension getMinimumSize( int hint, int hint2 )
+ {
+ return getPreferredSize( hint, hint2 );
+ }
+
+ public Dimension getPreferredSize( int hint, int hint2 )
+ {
+ Display display = Display.getCurrent( );
+
+ GC gc = null;
+
+ try
+ {
+ String text = textItem.getText( );
+ int angle = textItem.getRotationAngle( );
+
+ gc = new GC( display );
+
+ Point pt = gc.textExtent( text == null ? "" : text ); //$NON-NLS-1$
+
+ double[] info = SwtGraphicsUtil.computedRotatedInfo( pt.x, pt.y, angle );
+
+ if ( getBorder( ) != null )
+ {
+ Insets bdInsets = getBorder( ).getInsets( this );
+
+ return new Dimension( (int) info[0] + bdInsets.getWidth( ), (int) info[1] + bdInsets.getHeight( ) );
+ }
+ return new Dimension( (int) info[0], (int) info[1] );
+ }
+ finally
+ {
+ if ( gc != null &amp;&amp; !gc.isDisposed( ) )
+ {
+ gc.dispose( );
+ }
+ }
+ }
+
+ protected void paintClientArea( Graphics graphics )
+ {
+ final Rectangle r = getClientArea( ).getCopy( );
+
+ String text = textItem.getText( );
+ int angle = textItem.getRotationAngle( );
+
+ if ( text == null )
+ {
+ text = ""; //$NON-NLS-1$
+ }
+
+ if ( !text.equals( lastText ) || angle != lastAngle || cachedImage == null || cachedImage.isDisposed( ) )
+ {
+ lastText = text;
+ lastAngle = angle;
+
+ if ( cachedImage != null &amp;&amp; !cachedImage.isDisposed( ) )
+ {
+ cachedImage.dispose( );
+ }
+
+ cachedImage = SwtGraphicsUtil.createRotatedTextImage( text, angle, null );
+ }
+
+ if ( cachedImage != null &amp;&amp; !cachedImage.isDisposed( ) )
+ {
+ graphics.drawImage( cachedImage, r.x, r.y );
+ }
+ }
+
+ void setRotatedTextItem( RotatedTextItem item )
+ {
+ this.textItem = item;
+ }
+
+ void dispose( )
+ {
+ if ( cachedImage != null &amp;&amp; !cachedImage.isDisposed( ) )
+ {
+ cachedImage.dispose( );
+ }
+ }
+}</pre>
+
+<p>You can see the core rendering logic is the same as the Image UI. We still
+call the SwtGraphicsUtil to create an image and then paint it to figure. The
+only additional logic is the part about the mouse listener, where we handle the
+middle-button click event, adding 45 degrees to the original value per click.</p>
+
+<p>Now run and test. In layout editor, each time you middle-click the
+RotatedTextItem, it will automatically change the rotation angle.</p>
+
+<p>Note this is just an example to show the simplest possibility for the Figure UI.
+Basically you can create very complex UI
+structures based on the GEF framework. As it is mostly related to GEF, we
+will not introduce more here. You can refer to <a href="http://www.eclipse.org/gef/">GEF</a> official site for more details.</p>
+
+<h3>3) The Builder</h3>
+
+<p>One common approach for property editing is the <em>Builder</em>. Most built-in
+report items in BIRT have the builder support. Each time when you create or
+double-click the report item in layout editor, the associated builder for this
+report item will pop up and allow you to edit in
+a more friendly way. In following sections, we will introduce how to add the builder support
+for custom extended report items.</p>
+
+<p>To support the builder, we need add the builder UI extension and specify
+the implementor class which must implement
+<code>org.eclipse.birt.report.designer.ui.extensions.IReportItemBuilderUI</code>:</p>
+
+<img style="width: 1000px; height: 244px;" alt="" src="images/builder-extension.png">
+
+<p>Here we specify the class as <code>org.eclipse.birt.sample.reportitem.rotatedtext.RotatedTextBuilder</code>, the code is like this:</p>
+
+<pre>public class RotatedTextBuilder extends ReportItemBuilderUI
+{
+ public int open( ExtendedItemHandle handle )
+ {
+ try
+ {
+ IReportItem item = handle.getReportItem( );
+
+ if ( item instanceof RotatedTextItem )
+ {
+ RotatedTextEditor editor = new RotatedTextEditor( Display.getCurrent( ).getActiveShell( ), (RotatedTextItem) item );
+ return editor.open( );
+ }
+ }
+ catch ( Exception e )
+ {
+ e.printStackTrace( );
+ }
+ return Window.CANCEL;
+ }
+}</pre>
+
+<p>The code is very simple, we just create another RotatedTextEditor class to populate the actual UI:</p>
+
+<pre>class RotatedTextEditor extends TrayDialog
+{
+ protected RotatedTextItem textItem;
+ protected Text txtText;
+ protected Scale sclAngle;
+ protected Label lbAngle;
+
+ protected RotatedTextEditor( Shell shell, RotatedTextItem textItem )
+ {
+ super( shell );
+ this.textItem = textItem;
+ }
+
+ protected void configureShell( Shell newShell )
+ {
+ super.configureShell( newShell );
+ newShell.setText( "Rotated Text Builder" ); //$NON-NLS-1$
+ }
+
+ protected void createTextArea( Composite parent )
+ {
+ Label lb = new Label( parent, SWT.None );
+ lb.setText( "Text Content:" ); //$NON-NLS-1$
+
+ txtText = new Text( parent, SWT.BORDER );
+ GridData gd = new GridData( GridData.FILL_HORIZONTAL );
+ gd.horizontalSpan = 2;
+ txtText.setLayoutData( gd );
+ }
+
+ protected Control createDialogArea( Composite parent )
+ {
+ Composite composite = new Composite( parent, SWT.NONE );
+ GridLayout layout = new GridLayout( 3, false );
+ layout.marginHeight = convertVerticalDLUsToPixels( IDialogConstants.VERTICAL_MARGIN );
+ layout.marginWidth = convertHorizontalDLUsToPixels( IDialogConstants.HORIZONTAL_MARGIN );
+ layout.verticalSpacing = convertVerticalDLUsToPixels( IDialogConstants.VERTICAL_SPACING );
+ layout.horizontalSpacing = convertHorizontalDLUsToPixels( IDialogConstants.HORIZONTAL_SPACING );
+ composite.setLayout( layout );
+ composite.setLayoutData( new GridData( GridData.FILL_BOTH ) );
+
+ createTextArea( composite );
+
+ Label lb = new Label( composite, SWT.None );
+ lb.setText( "Rotation Angle:" ); //$NON-NLS-1$
+
+ sclAngle = new Scale( composite, SWT.None );
+ sclAngle.setLayoutData( new GridData( GridData.FILL_HORIZONTAL ) );
+ sclAngle.setMinimum( 0 );
+ sclAngle.setMaximum( 360 );
+ sclAngle.setIncrement( 10 );
+
+ lbAngle = new Label( composite, SWT.None );
+ GridData gd = new GridData( );
+ gd.widthHint = 20;
+ lbAngle.setLayoutData( gd );
+
+ sclAngle.addSelectionListener( new SelectionListener( ) {
+
+ public void widgetDefaultSelected( SelectionEvent e )
+ {
+ lbAngle.setText( String.valueOf( sclAngle.getSelection( ) ) );
+ }
+
+ public void widgetSelected( SelectionEvent e )
+ {
+ lbAngle.setText( String.valueOf( sclAngle.getSelection( ) ) );
+ }
+ } );
+
+ applyDialogFont( composite );
+
+ initValues( );
+
+ return composite;
+ }
+
+ private void initValues( )
+ {
+ txtText.setText( textItem.getText( ) );
+ sclAngle.setSelection( textItem.getRotationAngle( ) );
+ lbAngle.setText( String.valueOf( textItem.getRotationAngle( ) ) );
+ }
+
+ protected void okPressed( )
+ {
+ try
+ {
+ textItem.setText( txtText.getText( ) );
+ textItem.setRotationAngle( sclAngle.getSelection( ) );
+ }
+ catch ( Exception ex )
+ {
+ ex.printStackTrace( );
+ }
+
+ super.okPressed( );
+ }
+}</pre>
+
+<p>In the RotatedText builder UI, we provide one <code>Text</code> control and one <code>Scale</code>
+control, so user can easily change the "text content" and "rotation
+angle" properties.</p>
+
+<p>Now let's run the designer. </p>
+
+<p>Create one RotatedText item from palette first. Good, right after the
+creation, the builder pops up. Now It's very convenient to edit the
+properties. And when you double-click the RotatedText item in layout, it
+also brings up the builder.</p>
+
+<p><img style="width: 497px; height: 380px;" alt="" src="images/builder.png"></p>
+
+<p>By this way, user can create very complicated builder UI for editing. One good example is the built-in Chart builder,
+basically it's the most complicated builder in BIRT so far.</p>
+
+<p>Regarding the builder, there are another two extensions:
+<code>org.eclipse.birt.core.ui.tasks</code> and
+<code>org.eclipse.birt.core.ui.taskWizards</code>.
+These two extensions are not directly related to the builder UI
+extension, but providing a comprehensive way to create a wizard-like
+dialog UI. This dialog UI then can be further integrated to the builder UI
+extension. Chart builder is based on this technology. Since we are not going to explore the
+details about these two extensions in this article, you may refer to the
+Chart builder implementation for more details.</p>
+
+<h3>4) Context Menu</h3>
+
+<p>Another well-known UI feature may be the context menu. Through the context menu,
+user can get the quick entry points to perform specific actions. In
+BIRT, you can also provides a customized context menu for
+your extended report item by extensions.</p>
+
+<p>To support custom context menu, you need implement the
+extension <code>org.eclipse.birt.report.designer.ui.menuBuilders</code>, the
+implementor class must implement the
+<code>org.eclipse.birt.report.designer.ui.extensions.IMenuBuilder</code> interface.</p>
+
+<img style="width: 998px; height: 278px;" alt="" src="images/menu-extension.png">
+
+<p>As usual, we specify the element name as "RotatedText" to bind it
+to the model extension and give the implementor class name as
+<code>org.eclipse.birt.sample.reportitem.rotatedtext.RotatedTextMenuBuilder</code>.</p>
+
+<p>In the menu builder extension for RotatedText item, we add four
+custom actions to perform quick rotations, respectively,
+set the angle value to -90, 90, 0 and 180 degrees. The code is like this:</p>
+
+<pre>public class RotatedTextMenuBuilder implements IMenuBuilder
+{
+ public void buildMenu( IMenuManager menu, List selectedList )
+ {
+ if ( selectedList != null &amp;&amp; selectedList.size( ) == 1 &amp;&amp; selectedList.get( 0 ) instanceof ExtendedItemHandle )
+ {
+ ExtendedItemHandle handle = (ExtendedItemHandle) selectedList.get( 0 );
+
+ if ( !RotatedTextItem.EXTENSION_NAME.equals( handle.getExtensionName( ) ) )
+ {
+ return;
+ }
+
+ RotatedTextItem item = null;
+ try
+ {
+ item = (RotatedTextItem) handle.getReportItem( );
+ }
+ catch ( ExtendedElementException e )
+ {
+ e.printStackTrace( );
+ }
+
+ if ( item == null )
+ {
+ return;
+ }
+
+ Separator separator = new Separator( "group.rotatedtext" ); //$NON-NLS-1$
+ if ( menu.getItems( ).length &gt; 0 )
+ {
+ menu.insertBefore( menu.getItems( )[0].getId( ), separator );
+ }
+ else
+ {
+ menu.add( separator );
+ }
+
+ menu.appendToGroup( separator.getId( ), new RotateAction( item, -90 ) );
+ menu.appendToGroup( separator.getId( ), new RotateAction( item, 90 ) );
+ menu.appendToGroup( separator.getId( ), new RotateAction( item, 0 ) );
+ menu.appendToGroup( separator.getId( ), new RotateAction( item, 180 ) );
+ }
+ }
+
+ static class RotateAction extends Action
+ {
+ private RotatedTextItem item;
+ private int angle;
+
+ RotateAction( RotatedTextItem item, int angle )
+ {
+ this.item = item;
+ this.angle = angle;
+
+ setText( "Rotate as " + angle + "\u00BA" ); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public void run( )
+ {
+ try
+ {
+ item.setRotationAngle( angle );
+ }
+ catch ( SemanticException e )
+ {
+ e.printStackTrace( );
+ }
+ }
+ }
+}</pre>
+
+<p>Each custom action sets the rotation angle to a most commonly
+used value, which is just like a shortcut for setting the values.</p>
+
+<p>Run the designer and check again. Right-click on any RotatedText
+extended report item in layout editor, now you can see the custom actions in the context menu.</p>
+
+<img style="width: 491px; height: 462px;" alt="" src="images/contextmenu.png">
+
+<p>Through menu builder extension, actually you are not only able to add
+actions, it's also possible to remove or overwrite actions. Anyway, this offers you another
+chance to provide a smoother UI to the end user.</p>
+
+<h3>5) Property Pages</h3>
+
+<div style="text-align: left;">
+<p>An important UI concept in BIRT designer is the <em>Property Editor</em>. The
+<em>Property Editor</em> view provides the UI for miscellaneous property editting. It
+includes both specific settings for each report item and generic settings for all report items.</p>
+
+<img style="width: 811px; height: 284px;" alt="" src="images/label-general.png"><img style="width: 811px; height: 284px;" alt="" src="images/label-border.png">
+
+<p>So can we also create a similar property UI for our
+custom extended report items? And can we reuse the
+generic property UI for custom extended report items?</p>
+
+<p>Both answers are YES. In following sections, we will introduce how to
+implement custom property pages as well as how to reuse built-in property pages for
+custom extended report items.</p>
+
+<p>Before we start, let's have a brief look at the layout structure of the <em>Property Editor</em> view.</p>
+
+<img style="width: 749px; height: 284px;" alt="" src="images/tab-structure.png">
+
+<p>Normally the Property Editor UI is in a "Tabbed" style. There are
+two kinds of tabs: the "Property Tab" and the "Category Tab".
+The "Property Tab" divides the entire Property Editor UI into several
+pages, while the "Category Tab" divides a single page into several
+categories. By default, the first "Properties" tab page is designed to
+support categories and other pages not. But this is only the default behavior, user can always
+alter the UI style and overwrite the logic, although the
+default behavior in most cases already looks good. </p>
+
+<p>Now let's go on. To support custom property pages, we need implement following extensions:</p>
+
+<img style="width: 1001px; height: 253px;" alt="" src="images/property-page-extension-1.png">
+
+<p>As you see, unlike the previous extension points, this time the
+<code>org.eclipse.birt.report.designer.ui.elementAdpaters</code> extension point
+looks a little bit different. Actually, this is a very generic extension point
+used by BIRT designer. It is not only used to support property page
+extension, but also a lot of other UI extensions in various places.</p>
+
+<p>The first thing is to specify the <code>adaptable</code> class. In this case,
+we always specify it as
+<code>org.eclipse.birt.report.model.api.ExtendedItemHandle</code>, as custom
+extended report items are always represented by this class in BIRT model.</p>
+
+<p>The next thing is to specify the <code>adapter</code> settings:</p>
+
+<img style="width: 1001px; height: 251px;" alt="" src="images/property-page-extension-2.png">
+
+<p>Here is a brief explanation for the adapter settings:</p>
+
+<ul>
+ <li><em>id</em></li>
+ <ul>
+ <li>the unique identifier for this adapter. Here we specify it as <code>ReportDesign.AttributeView.RotatedTextPageGenerator</code>.</li>
+ </ul>
+ <li><em>type</em></li>
+ <ul>
+ <li>the Java class type this adapter will adapt to. For property
+pages, we must specify it as
+<code>org.eclipse.birt.report.designer.ui.views.IPageGenerator</code>.</li>
+ </ul>
+ <li><em>class</em></li>
+ <ul>
+ <li>The Java class type for the adapter. The class must have a
+constructor without any argument. As we are using the factory mode
+here, just leave it as blank.</li>
+ </ul>
+ <li><em>factory</em></li>
+ <ul>
+ <li>The Java class type for the adapter factory. The class must
+implement <code>org.eclipse.core.runtime.IAdapterFactory</code> interface. Here we specify
+it as
+<code>org.eclipse.birt.sample.reportitem.rotatedtext.views.RotatedTextPageGeneratorFactory</code>.</li>
+ </ul>
+ <li><em>singleton</em></li>
+ <ul>
+ <li>Specifies if the adapter object is a singleton or not. For singleton, the adapter object will be cached and reused for all matching
+adaptable. Here since we are using the factory mode, just set it to
+false.</li>
+ </ul>
+ <li><em>priority</em></li>
+ <ul>
+ <li>Specifies the priority for the adapter. This will be used for sorting
+when multiple adapters are defined for same adaptable. Just use the
+default value by leaving it as blank.</li>
+ </ul>
+ <li><em>overwrite</em></li>
+ <ul>
+ <li>Specifies a semicolon separated id list that this adapter want to overwrite. Here we just leave it as blank.</li>
+ </ul>
+ <li><em>comments</em></li>
+ <ul>
+ <li>An additional field to put some description text for the adapter.</li>
+ </ul>
+</ul>
+
+<p>Regarding the <em>class</em> and <em>factory</em> setting, there are already plenty of
+documents talking about them, so we are not repeating any more here. Normally the <em>factory</em>
+mode means more flexibility and extensibility. If <em>class</em> and
+<em>factory</em> are both specified, <em>class</em> will take higher priority.</p>
+
+<p>This is not the end yet. As we only want to add custom property pages for
+the RotatedText item, we need set some additional constraints
+for the adaptable.</p>
+
+<img style="width: 1002px; height: 252px;" alt="" src="images/property-page-extension-3.png">
+
+<p>To achieve this, we simply create an <em>enablement</em> element with type
+<em>test</em> under the <em>adapter</em> node. In the settings, we specify the test property as
+<code>ExtendItemHandle.extensionName</code> and the value as <code>RotatedText</code>. As you
+can conceive, this effectively restricts the adaptable object to be only
+our RotatedText extended report items.</p>
+
+<p>Now let's look at the code of the factory class:</p>
+
+<pre>public class RotatedTextPageGeneratorFactory implements IAdapterFactory
+{
+ public Object getAdapter( Object adaptableObject, Class adapterType )
+ {
+ return new RotatedTextPageGenerator( );
+ }
+
+ public Class[] getAdapterList( )
+ {
+ return new Class[]{
+ IPageGenerator.class
+ };
+ }
+}</pre>
+
+<p>The factory class simply creates a generator instance per call. Each
+generator must implement the
+<code>org.eclipse.birt.report.designer.ui.views.IPageGenerator</code> interface,
+but usually we just extend it from
+<code>org.eclipse.birt.report.designer.ui.views.attributes.AbstractPageGenerator</code>
+class, which provides some basic support for the categorized styles.</p>
+
+<p>In this example, we want to overwrite the <code>General</code>
+category page in <code>Properties</code> tab, and reuse some built-in categories
+like <code>Border</code>, <code>Margin</code>, <code>Page Break</code>, etc. Also we want to add a
+<code>Custom</code> property tab to the property editor. Here is the code:</p>
+
+<pre>public class RotatedTextPageGenerator extends AbstractPageGenerator
+{
+ <span style="font-weight: bold;">private static final String CUSTOM_PAGE_TITLE = "Custom"; //$NON-NLS-1$</span>
+ private IPropertyTabUI generalPage;
+
+ protected void buildItemContent( CTabItem item )
+ {
+ if ( itemMap.containsKey( item ) &amp;&amp; itemMap.get( item ) == null )
+ {
+ String title = tabFolder.getSelection( ).getText( );
+
+ <span style="font-weight: bold;">if ( CUSTOM_PAGE_TITLE.equals( title ) )</span>
+ <span style="font-weight: bold;">{</span>
+ <span style="font-weight: bold;"> TabPage page = new RotatedTextCustomPage( ).getPage( );</span>
+ <span style="font-weight: bold;"> if ( page != null )</span>
+ <span style="font-weight: bold;"> {</span>
+ <span style="font-weight: bold;"> setPageInput( page );</span>
+ <span style="font-weight: bold;"> refresh( tabFolder, page, true );</span>
+ <span style="font-weight: bold;"> item.setControl( page.getControl( ) );</span>
+ <span style="font-weight: bold;"> itemMap.put( item, page );</span>
+ <span style="font-weight: bold;"> }</span>
+ <span style="font-weight: bold;">}</span>
+ }
+ else if ( itemMap.get( item ) != null )
+ {
+ setPageInput( itemMap.get( item ) );
+ refresh( tabFolder, itemMap.get( item ), false );
+ }
+ }
+
+ public void refresh( )
+ {
+ createTabItems( input );
+
+ generalPage.setInput( input );
+ addSelectionListener( this );
+ ( (TabPage) generalPage ).refresh( );
+ }
+
+ public void createTabItems( List input )
+ {
+ if ( generalPage == null || generalPage.getControl( ).isDisposed( ) )
+ {
+ tabFolder.setLayout( new FillLayout( ) );
+ <span style="font-weight: bold;">generalPage = AttributesUtil.buildGeneralPage( tabFolder,</span>
+ <span style="font-weight: bold;"> new String[]{</span>
+ <span style="font-weight: bold;"> null,</span>
+ <span style="font-weight: bold;"> AttributesUtil.BORDER,</span>
+ <span style="font-weight: bold;"> AttributesUtil.MARGIN,</span>
+ <span style="font-weight: bold;"> AttributesUtil.SECTION,</span>
+ <span style="font-weight: bold;"> AttributesUtil.VISIBILITY,</span>
+ <span style="font-weight: bold;"> AttributesUtil.TOC,</span>
+ <span style="font-weight: bold;"> AttributesUtil.BOOKMARK,</span>
+ <span style="font-weight: bold;"> AttributesUtil.USERPROPERTIES,</span>
+ <span style="font-weight: bold;"> AttributesUtil.NAMEDEXPRESSIONS,</span>
+ <span style="font-weight: bold;"> AttributesUtil.ADVANCEPROPERTY</span>
+ <span style="font-weight: bold;"> },</span>
+ <span style="font-weight: bold;"> new String[]{ "General" }, //$NON-NLS-1$</span>
+ <span style="font-weight: bold;"> new String[]{ "General" }, //$NON-NLS-1$</span>
+ <span style="font-weight: bold;"> new AttributesUtil.PageWrapper[]{ new RotatedTextGeneralPage( ) },</span>
+ <span style="font-weight: bold;"> input );</span>
+
+ CTabItem tabItem = new CTabItem( tabFolder, SWT.NONE );
+ tabItem.setText( ATTRIBUTESTITLE );
+ tabItem.setControl( generalPage.getControl( ) );
+ }
+
+ this.input = input;
+ generalPage.setInput( input );
+ addSelectionListener( this );
+ ( (TabPage) generalPage ).refresh( );
+
+ <span style="font-weight: bold;">createTabItem( CUSTOM_PAGE_TITLE, ATTRIBUTESTITLE );</span>
+
+ if ( tabFolder.getSelection( ) != null )
+ {
+ buildItemContent( tabFolder.getSelection( ) );
+ }
+ }
+}</pre>
+
+<p>Let's explain a little more here:</p>
+
+<p>In the generator class, we extend it from the <code>AbstractPageGenerator</code> and
+overwrite three methods. Among them, the <code>createTabItems</code> and
+<code>buildItemContent</code> methods
+are the central places that containing the custom logic. In
+<code>createTabItem</code> method, we overwrite the creation logic for <code>General</code>
+category page, adding interested built-in category pages as well as the
+<code>Custom</code> property page. In <code>buildItemContent</code> method, we insert the
+creation logic for the <code>Custom</code> property page. You can focus on the
+text in bold to see how these are achieved. The remaining code are trivial,
+normally you can directly use it as a template for other page generator
+extensions.</p>
+
+<p>Note one thing you need take care of is when using the built-in pages,
+you must ensure the relevant model property definitions are applicable. As for some extended
+report items, they will overwrite
+or remove some of the built-in property definitions, in this case,
+those built-in page may not work properly.</p>
+
+<p>The last thing is the property page implementation. For
+<code>General</code> category page, we simply create two text controls for
+property editing. The code is like this:</p>
+
+<pre>public class RotatedTextGeneralPage extends AttributesUtil.PageWrapper
+{
+ protected FormToolkit toolkit;
+ protected Object input;
+ protected Composite contentpane;
+ private Text txtText, txtAngle;
+
+ public void buildUI( Composite parent )
+ {
+ if ( toolkit == null )
+ {
+ toolkit = new FormToolkit( Display.getCurrent( ) );
+ toolkit.setBorderStyle( SWT.NULL );
+ }
+
+ Control[] children = parent.getChildren( );
+
+ if ( children != null &amp;&amp; children.length &gt; 0 )
+ {
+ contentpane = (Composite) children[children.length - 1];
+
+ GridLayout layout = new GridLayout( 2, false );
+ layout.marginLeft = 8;
+ layout.verticalSpacing = 12;
+ contentpane.setLayout( layout );
+
+ toolkit.createLabel( contentpane, "Text Content:" ); //$NON-NLS-1$
+ txtText = toolkit.createText( contentpane, "" ); //$NON-NLS-1$
+ GridData gd = new GridData( );
+ gd.widthHint = 200;
+
+ txtText.setLayoutData( gd );
+ txtText.addFocusListener( new FocusAdapter( ) {
+
+ public void focusLost( org.eclipse.swt.events.FocusEvent e )
+ {
+ updateModel( RotatedTextItem.TEXT_PROP );
+ };
+ } );
+
+ toolkit.createLabel( contentpane, "Rotation Angle:" ); //$NON-NLS-1$
+ txtAngle = toolkit.createText( contentpane, "" ); //$NON-NLS-1$
+ gd = new GridData( );
+ gd.widthHint = 200;
+
+ txtAngle.setLayoutData( gd );
+ txtAngle.addFocusListener( new FocusAdapter( ) {
+
+ public void focusLost( org.eclipse.swt.events.FocusEvent e )
+ {
+ updateModel( RotatedTextItem.ROTATION_ANGLE_PROP );
+ };
+ } );
+ }
+ }
+
+ public void setInput( Object input )
+ {
+ this.input = input;
+ }
+
+ public void dispose( )
+ {
+ if ( toolkit != null )
+ {
+ toolkit.dispose( );
+ }
+ }
+
+ private void adaptFormStyle( Composite comp )
+ {
+ Control[] children = comp.getChildren( );
+ for ( int i = 0; i &lt; children.length; i++ )
+ {
+ if ( children[i] instanceof Composite )
+ {
+ adaptFormStyle( (Composite) children[i] );
+ }
+ }
+
+ toolkit.paintBordersFor( comp );
+ toolkit.adapt( comp );
+ }
+
+ protected RotatedTextItem getItem( )
+ {
+ Object element = input;
+
+ if ( input instanceof List &amp;&amp; ( (List) input ).size( ) &gt; 0 )
+ {
+ element = ( (List) input ).get( 0 );
+ }
+
+ if ( element instanceof ExtendedItemHandle )
+ {
+ try
+ {
+ return (RotatedTextItem) ( (ExtendedItemHandle) element ).getReportItem( );
+ }
+ catch ( Exception e )
+ {
+ e.printStackTrace( );
+ }
+ }
+
+ return null;
+ }
+
+ public void refresh( )
+ {
+ if ( contentpane != null &amp;&amp; !contentpane.isDisposed( ) )
+ {
+ if ( toolkit == null )
+ {
+ toolkit = new FormToolkit( Display.getCurrent( ) );
+ toolkit.setBorderStyle( SWT.NULL );
+ }
+
+ adaptFormStyle( contentpane );
+
+ updateUI( );
+ }
+ }
+
+ public void postElementEvent( )
+ {
+ if ( contentpane != null &amp;&amp; !contentpane.isDisposed( ) )
+ {
+ updateUI( );
+ }
+ }
+
+ private void updateModel( String prop )
+ {
+ RotatedTextItem item = getItem( );
+
+ if ( item != null )
+ {
+ try
+ {
+ if ( RotatedTextItem.ROTATION_ANGLE_PROP.equals( prop ) )
+ {
+ item.setRotationAngle( Integer.parseInt( txtAngle.getText( ) ) );
+ }
+ else if ( RotatedTextItem.TEXT_PROP.equals( prop ) )
+ {
+ item.setText( txtText.getText( ) );
+ }
+ }
+ catch ( Exception e )
+ {
+ e.printStackTrace( );
+ }
+ }
+ }
+
+ protected void updateUI( )
+ {
+ RotatedTextItem item = getItem( );
+
+ if ( item != null )
+ {
+ String text = item.getText( );
+ txtText.setText( text == null ? "" : text ); //$NON-NLS-1$
+
+ txtAngle.setText( String.valueOf( item.getRotationAngle( ) ) );
+ }
+ }
+}</pre>
+
+<p>Note the using of <code>FormToolkit</code> is just to achieve the same look and feel as
+the built-in pages. You can always design your own UI styles if no need for
+this consistency.</p>
+
+<p>For the <code>Custom</code> property page, as we only want to show the
+extension mechanism and the possibility, to simplify, we only give a very trivial example
+by implementing a read-only version of the <code>General</code>
+category page.</p>
+
+<pre>public class RotatedTextCustomPage extends RotatedTextGeneralPage
+{
+ private Label lbText, lbAngle;
+
+ public void buildUI( Composite parent )
+ {
+ if ( toolkit == null )
+ {
+ toolkit = new FormToolkit( Display.getCurrent( ) );
+ toolkit.setBorderStyle( SWT.NULL );
+ }
+
+ Control[] children = parent.getChildren( );
+
+ if ( children != null &amp;&amp; children.length &gt; 0 )
+ {
+ contentpane = (Composite) children[children.length - 1];
+
+ GridLayout layout = new GridLayout( 2, false );
+ layout.marginTop = 8;
+ layout.marginLeft = 8;
+ layout.verticalSpacing = 12;
+ contentpane.setLayout( layout );
+
+ toolkit.createLabel( contentpane, "Text Content:" ); //$NON-NLS-1$
+ lbText = toolkit.createLabel( contentpane, "" ); //$NON-NLS-1$
+ GridData gd = new GridData( );
+ gd.widthHint = 200;
+ lbText.setLayoutData( gd );
+
+ toolkit.createLabel( contentpane, "Rotation Angle:" ); //$NON-NLS-1$
+ lbAngle = toolkit.createLabel( contentpane, "" ); //$NON-NLS-1$
+ gd = new GridData( );
+ gd.widthHint = 200;
+ lbAngle.setLayoutData( gd );
+ }
+ }
+
+ protected void updateUI( )
+ {
+ RotatedTextItem item = getItem( );
+
+ if ( item != null )
+ {
+ String text = item.getText( );
+ lbText.setText( text == null ? "" : text ); //$NON-NLS-1$
+ lbAngle.setText( String.valueOf( item.getRotationAngle( ) ) );
+ }
+ }
+}</pre>
+
+<p>OK, now let's wrap all things up and take a look.</p>
+
+<p>Run the designer, ensure the <em>Property Editor</em> view is visible and
+select one RotatedText extended report item in layout editor. </p>
+
+<p>Cool, the <code>General</code> category page shows exactly what we designed. You can view
+and edit property values in this page
+directly now.</p>
+
+<img style="width: 818px; height: 284px;" alt="" src="images/rotatedtext-general.png">
+
+<p>Click the <code>Border</code> category, it brings up the built-in border
+property page as well. You can try change some border settings.
+Immediately you can see it works perfectly in layout editor. </p>
+
+<img style="width: 799px; height: 284px;" alt="" src="images/rotatedtext-border.png"><br>
+
+<br>
+
+<img style="width: 340px; height: 265px;" alt="" src="images/border-effect.png">
+
+<p>Now try switching to the <code>Custom</code> page, it brings up our read-only
+property page. Since this is only an example, in real practice,
+you can implement any custom pages.</p>
+
+<img style="width: 818px; height: 284px;" alt="" src="images/rotatedtext-custom.png">
+
+<p>OK, now we get a nearly complete picture for how to create an
+extended report item through model, engine and designer extensions in
+BIRT, and the ways to polish the extension UI and improve the usablity. However
+there's another important topic that we'd like to talk about a little
+more, that is the <em>Data Driven</em>.</p>
+
+<h2>Data Driven</h2>
+
+<p>An essential topic in BIRT is about the data integration. In the world of
+reporting, data is always the first class citizen. Normally data
+integration can be divided into two parts: <em>Data retrieving</em> and <em>Data
+representation</em>. In this chapter, we will focus on the <em>Data representation</em> part,
+exploring how to make our RotatedText extended report item data-aware
+and capable of leveraging the common data feature in BIRT.</p>
+
+<p>Before the start, we'd like to introduce a little more about the data
+infrastructure in BIRT. One important concept in BIRT is <em>Binding</em>. All
+report data in BIRT are actually populated through bindings. Binding is an
+abstract layer that connects the <em>Data retrieving</em> layer and <em>Data
+representation</em> layer. In other words, all report items in BIRT are
+actually consuming bindings instead of raw data from data source directly.</p>
+
+<p>There are two kinds of bindings in BIRT: <em>Regular binding</em> and
+<em>Aggregation binding</em>. For <em>Regular binding</em>, it normally contains a
+JavaScript expression and a data type. For <em>Aggregation binding</em>, it
+further contains an aggregation function, aggregation filter and aggregation
+target information. By using JavaScript in expression, bindings are
+very flexible to organize the data representation logic. You can either simply write
+the expression like:</p>
+
+<pre>row["columnA"]<br></pre>
+
+<p>or like:</p>
+
+<pre>if ( someConditionIsOK )
+{
+ if ( otherConditionIsOK )
+ {
+ functionA();
+ }
+ else
+ {
+ resultB;
+ }
+}
+else
+{
+ resultC;
+}</pre>
+
+<p>Bindings are associated with report items. Every
+report item can consume its own bindings as well as its parent-level
+bindings in different scopes. Since 2.3, BIRT also support a new feature called <em>Shared
+ResultSet</em>, which allows one report item to share the bindings from another
+report item that is not necessarily in its parent tree.</p>
+
+<p>Now let's continue to explorer how we can support bindings for our
+Rotated extended report item. The basic idea is to change the <code>text</code>
+property to be binding-aware, so the text value can be controlled by
+bindings dynamically.
+</p>
+
+<h3>1) Model Change</h3>
+
+<p>To support binding for <code>text</code> property, the first thing we need
+change is the model extension definition:</p>
+
+<img style="width: 999px; height: 273px;" alt="" src="images/text-expression-type.png"><br>
+
+<p>Actually the only thing we need is changing the property type of
+<code>text</code> from <code>string</code> to <code>expression</code> and adding the quotation mark
+around the
+original default value. The change means now this property supports
+expressions, and as the original default value <code>Rotated Text</code> is
+not a valid JavaScript expression, we changed it so it follows the
+literal string syntax in JavaScript. </p>
+
+<p>That's all we need for model change, now let's look at the engine part.</p>
+
+<h3>2) Engine Change</h3>
+
+<p>To support expressions, we just need change a piece of code in the <code>onRowSets()</code> method
+of <code>RotatedTextPresentationImpl</code> like this:</p>
+
+<pre>public Object onRowSets( IBaseResultSet[] results ) throws BirtException
+{
+ if ( textItem == null )
+ {
+ return null;
+ }
+
+ int angle = textItem.getRotationAngle( );
+ String text = textItem.getText( );
+
+ <span style="font-weight: bold;">// XXX added to support expression</span>
+ <span style="font-weight: bold;">if ( results != null &amp;&amp; results.length &gt; 0 )</span>
+ <span style="font-weight: bold;">{</span>
+ <span style="font-weight: bold;"> if ( results[0] instanceof IQueryResultSet &amp;&amp; ( (IQueryResultSet) results[0] ).isBeforeFirst( ) )</span>
+ <span style="font-weight: bold;"> {</span>
+ <span style="font-weight: bold;"> ( (IQueryResultSet) results[0] ).next( );</span>
+ <span style="font-weight: bold;"> }</span>
+ <span style="font-weight: bold;"> text = String.valueOf( results[0].evaluate( text ) );</span>
+ <span style="font-weight: bold;">}</span>
+ <span style="font-weight: bold;">else</span>
+ <span style="font-weight: bold;">{</span>
+ <span style="font-weight: bold;"> text = String.valueOf( context.evaluate( text ) );</span>
+ <span style="font-weight: bold;">}</span>
+ <span style="font-weight: bold;">// end new code</span>
+
+ BufferedImage rotatedImage = SwingGraphicsUtil.createRotatedTextImage( text, angle, new Font( "Default", 0, 12 ) ); //$NON-NLS-1$
+
+ ByteArrayInputStream bis = null;
+
+ try
+ {
+ ImageIO.setUseCache( false );
+ ByteArrayOutputStream baos = new ByteArrayOutputStream( );
+ ImageOutputStream ios = ImageIO.createImageOutputStream( baos );
+
+ ImageIO.write( rotatedImage, "png", ios ); //$NON-NLS-1$
+ ios.flush( );
+ ios.close( );
+ bis = new ByteArrayInputStream( baos.toByteArray( ) );
+ }
+ catch ( IOException e )
+ {
+ e.printStackTrace( );
+ }
+ return bis;
+}</pre>
+
+<p>The new code adds the extra logic to handle the <code>text</code> property as an
+expression, and evaluating it under current engine context to get the
+final string result.</p>
+
+<h3>3) UI Change</h3>
+
+<p>Now the UI's turn. Two places need the change. The first applies to the builder, the
+second applies to the property page.</p>
+
+<p>For the builder, we want to add a button besides the text control,
+so user can invoke the standard expression builder to edit the
+expression. The standard expression builder provides the
+common JavaScript support in BIRT. </p>
+
+<img style="width: 424px; height: 380px;" alt="" src="images/builder-expression-support.png">
+<p><img style="width: 702px; height: 594px;" alt="" src="images/expression-builder.png"></p>
+
+<p>To achieve this, we change the code in <code>RotatedTextBuilder</code> class as following:</p>
+
+<pre>public class RotatedTextBuilder extends ReportItemBuilderUI
+{
+ public int open( ExtendedItemHandle handle )
+ {
+ try
+ {
+ IReportItem item = handle.getReportItem( );
+
+ if ( item instanceof RotatedTextItem )
+ {
+ RotatedTextEditor editor = new <span style="font-weight: bold;">RotatedTextEditor2</span>( Display.getCurrent( ).getActiveShell( ), (RotatedTextItem) item );
+ return editor.open( );
+ }
+ }
+ catch ( Exception e )
+ {
+ e.printStackTrace( );
+ }
+ return Window.CANCEL;
+ }
+}
+
+class RotatedTextEditor2 extends RotatedTextEditor
+{
+ protected RotatedTextEditor2( Shell shell, RotatedTextItem textItem )
+ {
+ super( shell, textItem );
+ }
+
+ protected void createTextArea( Composite parent )
+ {
+ Label lb = new Label( parent, SWT.None );
+ lb.setText( "Text Content:" ); //$NON-NLS-1$
+
+ txtText = new Text( parent, SWT.BORDER );
+ GridData gd = new GridData( GridData.FILL_HORIZONTAL );
+ txtText.setLayoutData( gd );
+
+ Button btnExp = new Button( parent, SWT.PUSH );
+ btnExp.setText( "..." ); //$NON-NLS-1$
+ btnExp.setToolTipText( "Invoke Expression Builder" ); //$NON-NLS-1$
+
+ btnExp.addSelectionListener( new SelectionAdapter( ) {
+
+ public void widgetSelected( SelectionEvent event )
+ {
+ openExpression( txtText );
+ }
+ } );
+ }
+
+ private void openExpression( Text textControl )
+ {
+ String oldValue = textControl.getText( );
+
+ ExpressionBuilder eb = new ExpressionBuilder( textControl.getShell( ), oldValue );
+ eb.setExpressionProvier( new ExpressionProvider( textItem.getModelHandle( ) ) );
+
+ String result = oldValue;
+
+ if ( eb.open( ) == Window.OK )
+ {
+ result = eb.getResult( );
+ }
+
+ if ( !oldValue.equals( result ) )
+ {
+ textControl.setText( result );
+ }
+ }
+}</pre>
+
+<p>In the above code, we create another <code>RotatedTextEditor2</code> class to overwrite the UI creation
+logic for the new expression button.</p>
+
+<p>Now let's look at the property page. For <code>General</code> property page, we
+also want to add an expression button besides the text control. Here is
+the change for the <code>RotatedTextGeneralPage</code> class:</p>
+
+<pre>public class RotatedTextGeneralPage extends AttributesUtil.PageWrapper
+{
+ //.........
+
+ public void buildUI( Composite parent )
+ {
+ if ( toolkit == null )
+ {
+ toolkit = new FormToolkit( Display.getCurrent( ) );
+ toolkit.setBorderStyle( SWT.NULL );
+ }
+
+ Control[] children = parent.getChildren( );
+
+ if ( children != null &amp;&amp; children.length &gt; 0 )
+ {
+ contentpane = (Composite) children[children.length - 1];
+
+ <span style="font-weight: bold;">GridLayout layout = new GridLayout( 3, false );</span>
+ layout.marginLeft = 8;
+ layout.verticalSpacing = 12;
+ contentpane.setLayout( layout );
+
+ toolkit.createLabel( contentpane, "Text Content:" ); //$NON-NLS-1$
+ txtText = toolkit.createText( contentpane, "" ); //$NON-NLS-1$
+ GridData gd = new GridData( );
+ gd.widthHint = 200;
+
+ txtText.setLayoutData( gd );
+ txtText.addFocusListener( new FocusAdapter( ) {
+
+ public void focusLost( org.eclipse.swt.events.FocusEvent e )
+ {
+ updateModel( RotatedTextItem.TEXT_PROP );
+ };
+ } );
+
+ <span style="font-weight: bold;">Button btnExp = toolkit.createButton( contentpane, "...", SWT.PUSH ); //$NON-NLS-1$</span>
+ <span style="font-weight: bold;">btnExp.setToolTipText( "Invoke Expression Builder" ); //$NON-NLS-1$</span>
+ <span style="font-weight: bold;">btnExp.addSelectionListener( new SelectionAdapter( ) {</span>
+
+ <span style="font-weight: bold;"> public void widgetSelected( SelectionEvent e )</span>
+ <span style="font-weight: bold;"> {</span>
+ <span style="font-weight: bold;"> openExpression( txtText );</span>
+ <span style="font-weight: bold;"> }</span>
+ <span style="font-weight: bold;">} );</span>
+
+ toolkit.createLabel( contentpane, "Rotation Angle:" ); //$NON-NLS-1$
+ txtAngle = toolkit.createText( contentpane, "" ); //$NON-NLS-1$
+ gd = new GridData( );
+ gd.widthHint = 200;
+ <span style="font-weight: bold;">gd.horizontalSpan = 2;</span>
+ txtAngle.setLayoutData( gd );
+ txtAngle.addFocusListener( new FocusAdapter( ) {
+
+ public void focusLost( org.eclipse.swt.events.FocusEvent e )
+ {
+ updateModel( RotatedTextItem.ROTATION_ANGLE_PROP );
+ };
+ } );
+ }
+ }
+
+ private void openExpression( Text textControl )
+ {
+ RotatedTextItem item = getItem( );
+
+ if ( item != null )
+ {
+ String oldValue = textControl.getText( );
+ ExpressionBuilder eb = new ExpressionBuilder( textControl.getShell( ), oldValue );
+ eb.setExpressionProvier( new ExpressionProvider( item.getModelHandle( ) ) );
+
+ String result = oldValue;
+
+ if ( eb.open( ) == Window.OK )
+ {
+ result = eb.getResult( );
+ }
+
+ if ( !oldValue.equals( result ) )
+ {
+ textControl.setText( result );
+ updateModel( RotatedTextItem.TEXT_PROP );
+ }
+ }
+ }
+
+ //..........
+}</pre>
+
+<p>You can focus on the code in bold and the newly added methods only. Now the <code>General</code> property page
+looks like this:</p>
+
+<img style="width: 713px; height: 284px;" alt="" src="images/property-page-expression-support.png">
+
+<p>OK, time for an integrated test.</p>
+
+<p>Create one Table report item first, bind it with a dataset, insert
+one RotatedText extended report item in the table detail row as well as
+several other Data items.</p>
+
+<img style="width: 628px; height: 309px;" alt="" src="images/test-expression-1.png"><br>
+
+<p>Specify the expression of the RotatedText item as:</p>
+
+<img style="width: 702px; height: 594px;" alt="" src="images/test-expression-2.png">
+
+<p>Now preview it in designer. </p>
+
+<img style="width: 688px; height: 545px;" alt="" src="images/test-expression-3.png">
+
+<p>Voila! The text data now comes from the table bindings and automatically changes per row.</p>
+
+<p>Now change the expression as:</p>
+
+<img style="width: 702px; height: 594px;" alt="" src="images/property-page-extension-4.png"><br>
+
+<p>Preview again:</p>
+
+<img style="width: 727px; height: 366px;" alt="" src="images/property-page-extension-5.png">
+
+<p>Perfect! Now we have successfully created a data-driven custom report item.</p>
+
+</div>
+
+<h2>Summary</h2>
+
+<p>BIRT provides very good extension mechanism, which
+allows user
+to create custom reporting features. This article introduced the techniques that used to
+enhance the feature of custom extended report items for BIRT.</p>
+
+<h2>Resources</h2>
+
+<p>[1] <a href="extension-tutorial-2.zip">The
+complete source code</a>: you can extract and import it into
+Eclipse workspace as a plugin project directly</p>
+
+<p>[2] BIRT Official Site: <a href="http://eclipse.org/birt">http://eclipse.org/birt</a></p>
+
+<p>[3] How to access Eclipse CVS: <a href="http://wiki.eclipse.org/CVS_Howto">http://wiki.eclipse.org/CVS_Howto</a></p>
+
+<p><br>
+
+<br>
+
+</p>
+
+<br>
+
+</div>
+
+</body>
+</html>

Back to the top