This chapter describes how to create a BIRT extension in the Eclipse PDE using the sample report item extension, rotated label. The BIRT report item extension is presented in the following sections:
• Understanding a report item extension
• Developing the sample report item extension
• Understanding the rotated label report item extension
• Deploying and testing the rotated label report item plug-in
• Developing an advanced report item
Use a report item extension to add a new report item type to the BIRT framework by implementing multiple extension points. A plug-in defining an extension point typically contains an XML extension point schema definition (.exsd) file in a schema subdirectory. This schema describes the elements, attributes, and types used by the extension point in the Eclipse PDE environment.
To support adding a new report item, a report item extension provides the following extension points:
• Report item model
org.eclipse.birt.report.model.reportItemModel specifies the report item extension point.
The XML schema file, org.eclipse.birt.report.model/schema/reportItem.exsd describes this extension point.
• Report item user interface
org.eclipse.birt.report.designer.ui.reportitemUI specifies the report item extension point for the user interface in the report layout editor and report item palette. The XML schema file, org.eclipse.birt.report.designer.ui/schema/reportitemUI.exsd, describes this extension point.
• Report item query
org.eclipse.birt.report.engine.reportitemQuery specifies the extension point for query preparation support in the BIRT designer and report engine. A query preparation extension is optional. The XML schema file, org.eclipse.birt.report.engine/schema/reportitemQuery.exsd, describes this extension point.
• Report item run time
org.eclipse.birt.report.engine.reportitemGeneration specifies the extension point for instantiating, processing, and persisting a new report item at report generation time. A run-time extension is optional. The XML schema file, org.eclipse.birt.report.engine/schema/reportitemGeneration.exsd, describes this extension point.
• Report item presentation
org.eclipse.birt.report.engine.reportitemPresentation specifies the extension point for instantiating, processing, and rendering a new report item at report presentation time. The XML schema file, org.eclipse.birt.report.engine/schema/reportitemPresentation.exsd, describes this extension point.
• Report item UI property page adapters
org.eclipse.birt.report.designer.ui.elementAdapters specifies the extension points for the adapters for the report item property page UI. The XML schema file, org.eclipse.birt.report.designer.ui/schema/elementAdapters.exsd, describes this extension point.
• Report item emitter
org.eclipse.birt.report.engine.emitters specifies the extension point for support of a new output format in the presentation engine. An emitter extension is optional. The XML schema file, org.eclipse.birt.report.engine/schema/emitters.exsd, describes this extension point.
At run time, the BIRT Report Engine performs the following processing on a report item before rendering the final output:
• Query preparation
Gathers the data binding information and expressions defined for the report, passing the information to the report engine. The data engine prepares the data access strategy based on this information.
Creates the instances of report items and fetches the data.
• Presentation
Renders the report item to a supported data primitive, such as an image, string, HTML segment, or custom data component.
• Emitter
Converts the output to a specified format, such as HTML or PDF.
This chapter provides an example of a custom report item extension, org.eclipse.birt.sample.reportitem.rotatedlabel.
The standard report item plug-in, chart, is a more complex example of a report item extension. The BIRT chart plug-in implements user interface and report engine extensions that support a report design using any of the bar, line, pie, scatter, stock, or other chart types.
For reference documentation for the report item API, see the Javadoc for the org.eclipse.birt.report.engine.extension package in the BIRT Programmer Reference in Eclipse Help.
The Report Item extension framework supports creating a customized report item in the palette of BIRT Report Designer. You can use a report item extension in a report design in the same way that you use a standard report item, such as Label, Text, Grid, Table, or Chart.
The sample code for the rotated label report item extension creates a label element that renders text at a specified angle. To implement the rotated label report item extension, perform the following tasks:
• Configure the plug-in project.
Build the rotated label report item plug-in manually by following the instructions in this chapter.
• Add the report item to the Report Designer UI.
Use org.eclipse.birt.report.designer.ui.reportItemUI to extend the Report Item UI extension point.
• Add the report item property pages to the Report Designer UI.
Use org.eclipse.birt.report.designer.ui.elementAdapters to extend the Report Item UI property page extension point.
• Add the report item definition to the ROM.
Use org.eclipse.birt.report.model.reportItemModel to extend the Report Item Model extension point.
• Add the report item run-time behavior.
Use org.eclipse.birt.report.engine.reportItemPresentation to extend the Report Item Presentation extension point.
• Deploy the report item extension.
Export the rotated label report item plug-in folder from your workspace to the eclipseplugins folder. This step is not necessary to test the extension when you launch it as an Eclipse application in the PDE.
The source code for the rotated label report item extension example is available at http://www.actuate.com/birt/contributions.
The rotated label report item extension depends on the following Eclipse plug-ins:
• org.eclipse.core.runtime.compatibility
• org.eclipse.birt.core.ui
• org.eclipse.birt.data
• org.eclipse.birt.report.designer.core
• org.eclipse.birt.report.designer.ui
• org.eclipse.birt.report.designer.ui.views
• org.eclipse.birt.report.engine
• org.eclipse.ui
Eclipse makes BIRT source code available to the developer community in the CVS repository. BIRT source code is not needed to compile any required plug-ins.
By default, the system uses the JAR files in the $INSTALL_DIReclipseplugin folder. These plug-ins must be in the classpath to compile successfully. To debug, you may need the source code for the required BIRT plug-ins.
BIRT source code is not needed to develop the sample rotated label report item plug-in. You work only with the Java classes in the org.eclipse.birt.sample.reportitem.rotatedlabel package.
Create a new plug-in project for the rotated label report item extension in the Eclipse PDE.
How to create the plug-in project
1 In the Eclipse PDE, choose File→New→Project. New Project appears.
2 In Select a wizard, select Plug-in Project. Choose Next. New Plug-in Project appears.
3 In Plug-in Project, modify the settings, as shown in Table 18-1.
Plug-in Project appears, as shown in Figure 18-1.
Choose Next. Plug-in Content appears.
4 In Plug-in Content, modify the settings as shown in Table 18-2.
New Plug-in Content appears, as shown in Figure 18-2. Choose Finish.
The rotated label report item extension project appears in the Eclipse PDE Workbench, as shown in Figure 18-3.
In this task, specify the list of plug-ins that must be available on the classpath of the rotated label report item extension to compile and run.
How to specify the dependencies
1 On PDE Manifest Editor, choose Overview.
2 In Plug-in Content, choose Dependencies. Required Plug-ins contains the following plug-ins:
org.eclipse.core.runtime
3 In Required Plug-ins, select org.eclipse.core.runtime and choose Remove. org.eclipse.core.runtime no longer appears in Required Plug-ins.
4 In Required Plug-ins, choose Add. Plug-in Selection appears.
5 In Plug-in Selection, hold down CTRL and select the following plug-ins:
• org.eclipse.birt.core.ui
• org.eclipse.birt.data
• org.eclipse.birt.report.designer.core
• org.eclipse.birt.report.designer.ui
• org.eclipse.birt.report.designer.ui.views
• org.eclipse.birt.report.engine
• org.eclipse.core.runtime.compatibility
• org.eclipse.ui
Choose OK. Dependencies appears, as shown in Figure 18-4.
The order of the list determines the sequence in which a plug-in loads at run time. Use Up and Down to change the loading order as necessary, as shown in Figure 18-4.
On Runtime, specify exported packages, package visibility, libraries, and folders on the plug-in classpath. In the rotated label report item plug-in, make the following packages visible to other plug-ins:
• org.eclipse.birt.sample.reportitem.rotatedlabel
• org.eclipse.birt.sample.reportitem.rotatedlabel.i18n
• org.eclipse.birt.sample.reportitem.rotatedlabel.util
• org.eclipse.birt.sample.reportitem.rotatedlabel.views
On PDE Manifest Editor, choose Runtime. In Exported Packages, verify that the specified packages appear in the list, as shown in Figure 18-5.
Next, specify the extension points required to implement the rotated label report item extension and add the extension element details. The Eclipse PDE uses the XML schema defined for each extension point to provide the list of valid attributes and values specified for the extension elements. The rotated label report item extension implements the following extension points:
• org.eclipse.birt.report.designer.ui.reportitemUI
Registers the graphical user interface (GUI) to use for the report item extension
• org.eclipse.birt.report.model.reportItemModel
Specifies how to represent and persist the report item extension in the ROM
• org.eclipse.birt.report.engine.reportitemPresentation
Specifies how to instantiate, process, and render a new report item at report presentation time
• org.eclipse.birt.report.designer.ui.elementAdapters
Specifies the adapters for the report item property page UI
The XML schema specifies the following properties that identify each extension point in the run-time environment:
• ID
Optional identifier of the extension instance
• Name
Optional name of the extension instance
The extension point, org.eclipse.birt.report.designer.ui.reportitemUI, specifies the following extension elements:
• reportItemFigureUI
Fully qualified name of the Java class that implements the org.eclipse.birt.report.designer.ui.extensions. IReportItemFigureProvider interface
• reportItemLabelUI
Fully qualified name of the Java class that gets the display text for the report item component in BIRT Report Designer
• reportItemImageUI
Fully qualified name of the Java class that implements the org.eclipse.birt.report.designer.ui.extensions.IReportItemImageProvider interface
• model
ROM report item extension name that maps to this UI component
• builder
Optional component instantiated when a user drags the extended report item from the BIRT Report Designer Palette to the Editor
• palette
Icon to show and the category in which the icon appears in the Palette
• editor
Flags indicating whether the editor shows in the MasterPage and Designer UI and is resizable in the Editor
• outline
Icon to show in the outline view
• description
Optional element containing text describing what the UI extension does
The extension point, org.eclipse.birt.report.model.reportItemModel, specifies the following extension element properties:
• extensionName
Internal unique name of the report item extension
• class
Fully qualified name of the Java class that implements the org.eclipse.birt.report.model.api.extension.IReportItemFactory interface
• defaultStyle
Predefined style to use for the report item extension
• isNameRequired
Field indicating whether the report item instance name is required
• displayNameID
Resource key for the display name
• extendsFrom
Parent element from which a report item extends
• hasStyle
Flag that marks whether an extended item can have style
For RotatedLabel, reportItem specifies the following property extension elements:
• displayText
• rotationAngle
These extension elements specify the following properties:
• name
Internal unique name of the property extension element.
Data type, such as integer or string.
• displayNameID
Resource key for the display name.
• detailType
Detail data type, such as Boolean or string.
• subType
When the detailType is a list, the subType is required and must be defined as one of the restriction choices. By default the subtype is string.
• canInherit
Flag indicating whether the property extension element can inherit properties.
• defaultValue
Default value of the property extension element.
• isEncryptable
Flag indicating whether the property is encrypted.
• DisplayName
Display name to use if no localized I18N display name exists.
• isList
Indicates whether the property value is a single structure or a list.
• hasOwnModel
Indicates whether the XML property is the extension model.
• allowExpression
Indicates whether the property can be set as an expression. Each expression contains the value and the type.
The extension point, org.eclipse.birt.report.engine.reportitemPresentation, specifies the following reportItem extension elements:
• name
Unique name of the report item extension.
• class
Fully qualified name of the Java class that implements the org.eclipse.birt.report.engine.extension. IReportItemPresentation interface.
• supportedFormats
Supported rendering formats for this extended item. The value for this attribute is a comma-separated string, such as “HTML,PDF,EXCEL”. The string is case-insensitive.
The extension point, org.eclipse.birt.report.designer.ui.elementAdapters, creates an adapter class that implements the IPageGenerator interface. The org.eclipse.birt.report.model.api.ExtendedItemHandle class implements the interfaces which make the extension point adaptable. The adapter specifies the following properties for the property pages:
• id
Unique name of ReportDesign.AttributeView.RotatedLabelPageGenerator
• type
Implements org.eclipse.birt.report.designer.ui.views.IPageGenerator
• factory
Registers and creates the IPageGenerator instance
• singleton
Indicates whether one or more adapter elements can simultaneously exist
• priority
Indicates priority in a run-time environment
How to specify the extension points
1 On PDE Manifest Editor, choose Extensions.
2 In All Extensions, choose Add. New Extension appears.
3 In New Extension—Extension Points, Available extension points, select the following plug-in:
org.eclipse.birt.report.designer.ui.reportitemUI
New Extension—Extension Points appears, as shown in Figure 18-6.
In cases when the plug-in does not appear in the list, uncheck Show only extension points from the required plug-ins box.
Choose Finish.
4 Repeat steps 2 and 3 to add the following extension points to the list of required extension points in the Extensions page:
• org.eclipse.birt.report.model.reportItemModel
• org.eclipse.birt.report.engine.reportitemPresentation
• org.eclipse.birt.report.designer.ui.elementAdapters
Figure 18-7 shows the list of extension points required for the report item extension example.
How to add the extension details
Perform the following tasks:
1 On Extensions, in All Extensions, select org.eclipse.birt.report.designer.ui.reportitemUI.
2 In Extension Details, set the following property values as shown in Table 18-3.
3 Repeat step 2 to add the property values shown in Table 18-3 to the extension details for the other extension points.
The XML schema specifies a grammar that you must follow when creating an extension in the Eclipse PDE. When you select an element of an extension in the Extensions page of the PDE, Eclipse uses the XML schema to populate the Extension Element Details section with the list of valid attributes and values for the element.
How to specify the plug-in extension content
1 In PDE Manifest Editor, choose Extensions.
2 In All Extensions, right-click org.eclipse.birt.report.designer.ui.reportItemUI and choose New→reportItemLabelUI, as shown in Figure 18-8.
All Extensions adds the extension, org.eclipse.birt.sample.reportitem.rotatedlabel.ReportItemLabelUI (rotatedItemLabelUI), and Extension Element Details lists rotatedItemLabelUI properties.
3 In Extension Element Details, type the following fully qualified class name:
org.eclipse.birt.sample.reportitem.rotatedlabel
.RotatedLabelUI
4 In All Extensions, right-click org.eclipse.birt.report.designer.ui.reportitemUI again and repeatedly choose New→<extension element> to add the following extension elements, properties, and values, as shown in Table 18-4. Note that the PDE Manifest Editor creates the extension element rotated label model when adding this extension point.
Do not enter a property value in Extension Element Details if the value field for the property in the table is empty. Extension elements appear as shown in Figure 18-9.
5 In All Extensions, add the org.eclipse.birt.report.model.reportItemModel extension point. Select reportItem. In Extension Element Details, add the reportItem properties shown in Table 18-5.
6 In All Extensions, in org.eclipse.birt.report.model.reportItemModel, perform the following tasks:
1 Right-click reportItem again and choose New→property to create a new extension element property with the settings shown in Table 18-6. The displayText property contains the text that the rotated label displays.
2 Right-click reportItem and choose New→property to add an extension element property named rotationAngle. The settings for this property are shown in Table 18-7. As the property name suggests, this property contains the rotation angle of the displayed text.
7 In All Extensions, expand org.eclipse.birt.report.engine.reportitemPresentation and choose org.eclipse.birt.sample.reportitem.rotatedlabel.reportItem1(reportItem).
8 In Extension Element Details, modify the reportItem properties to the values shown in Table 18-8.
9 In All Extensions, right-click org.eclipse.birt.report.designer.ui.elementAdapters and choose org.eclipse.birt.sample.reportitem.rotatedlabel.Adaptable1(adaptable).
10 In Extension Element Details, modify the adaptable property to the following class:
org.eclipse.birt.report.model.api.ExtendedItemHandle
11 In All Extensions, right-click org.eclipse.birt.report.model.api.ExtendedItemHandle (adaptable) and choose New→adapter.
12 In Extension Element Details, add the adapter properties shown in Table 18-9.
Figure 18-10 shows the full list of extension points and elements required for the example, org.eclipse.birt.sample.reportitem.rotatedlabel.
The rotated label report item plug-in provides the functionality required at run time to render the label of a report item as an image in the report design and display the label at the specified angle. The following sections contain implementation details for the most important classes in the rotated label report item extension.
The RotatedLabelItemFactoryImpl class instantiates a new report item when the user drags a rotated label report item from Palette and drops the report item in the BIRT Report Designer Editor. RotatedLabelItemFactoryImpl extends the adapter class, org.eclipse.birt.report.model.api.extension.ReportItemFactory.
The newReportItem( ) method receives a reference to DesignElementHandle, which provides the interface to the BIRT report model. The newReportItem( ) method instantiates the new report item, as shown in Listing 18-1.
public class RotatedTextItemFactoryImpl
extends ReportItemFactory implements IMessages
{
public IReportItem newReportItem( DesignElementHandle deh )
{
return new ReportItem( );
...
}
In the RotatedLabelUI class, the RotatedLabelUI.getLabel( ) method provides the text representation for the label to BIRT Report Designer. RotatedLabelUI extends the adapter class, org.eclipse.birt.report.designer.ui.extensions.ReportItemLabelProvider. Listing 18-2 shows the code for the getLabel( ) method.
public class RotatedLabelUI extends ReportItemLabelProvider
{
public String getLabel( ExtendedItemHandle handle )
{
if ( handle.getProperty( "displayText" ) != null ) {
return ( String ) handle.getProperty( "displayText" );
} else {
return "Rotated Label";
}
}
The RotatedLabelPresentationImpl class specifies how to process and render the report item at presentation time. RotatedLabelPresentationImpl extends the org.eclipse.birt.report.engine.extension.ReportItemPresentationBase class.
The method, onRowSets( ), renders the rotated label report item as an image, rotated by the angle specified in the report design, as shown in Listing 18-3.
public Object onRowSets(IRowSet[ ] rowSets) throws BirtException
{
if ( modelHandle == null )
{
return null;
}
graphicsUtil = new GraphicsUtil( );
org.eclipse.swt.graphics.Image rotatedImage =
graphicsUtil.createRotatedText( modelHandle );
ImageLoader imageLoader = new ImageLoader( );
imageLoader.data = new ImageData[ ]
{ rotatedImage.getImageData( ) };
ByteArrayOutputStream baos =
new ByteArrayOutputStream( );
imageLoader.save( baos, SWT.IMAGE_JPEG );
return baos.toByteArray( );
}
The GraphicsUtil class creates the image containing the specified text and rotates the text image to the specified angle, using the following methods:
• createRotatedText( )
This method performs the following operations:
• Gets the display text and rotation angle properties
• Sets the display text font and determines the font metrics
• Creates an image the same size as the display text String
• Draws the display text as an image
• Calls the rotateImage( ) method to rotate the image at the specified angle
• Disposes of the operating system resources used to render the image
• Returns the image object
Listing 18-4 shows the code for createRotatedText( ) method.
public Image createRotatedText( ExtendedItemHandle
modelHandle )
{
Image stringImage;
Image image;
GC gc;
String text = "";
if ( modelHandle.getProperty( "displayText" ) != null ) {
text = ( String ) modelHandle.getProperty
( "displayText" );
}
Integer angle = -45;
if ( modelHandle.getProperty( "rotationAngle" ) != null ) {
try {
angle = Integer.parseInt( (String)
modelHandle.getProperty( "rotationAngle" ));
}
catch( NumberFormatException e ) {
angle = -45;
}
}
String fontFamily = "Arial";
if ( modelHandle.getProperty(Style.FONT_FAMILY_PROP ) !=
null ) {
fontFamily = ( String ) modelHandle.getProperty
( Style.FONT_FAMILY_PROP );
}
StyleHandle labelStyle = modelHandle.getPrivateStyle();
DimensionHandle fontSize =
(DimensionHandle) labelStyle.getFontSize();
int height = 12;
String units = fontSize.getUnits( );
if ( units.compareToIgnoreCase("pt") == 0 )
{
height = (int) fontSize.getMeasure();
}
if ( display == null ) SWT.error
( SWT.ERROR_THREAD_INVALID_ACCESS );
FontData fontData = new FontData( fontFamily, height, 0 );
Font font = new Font( display, fontData );
try
{
gc = new GC( display );
gc.setFont( font );
gc.getFontMetrics( );
Point pt = gc.textExtent( text );
gc.dispose( );
stringImage = new Image( display, pt.x, pt.y );
gc = new GC( stringImage );
gc.setFont( font );
gc.drawText( text, 0, 0 );
image = rotateImage( stringImage, angle.doubleValue( ) );
gc.dispose( );
stringImage.dispose( );
return image;
}
catch( Exception e )
{
e.printStackTrace( );
}
return null;
}
• rotateImage( )
This method rotates the image and determines the height, width, and point of origin for the image, as shown in Listing 18-5.
private Image rotateImage ( Image img, double degrees )
{
double positiveDegrees = ( degrees % 360 ) +
( ( degrees < 0 ) ? 360 : 0 );
double degreesMod90 = positiveDegrees % 90;
double radians = Math.toRadians( positiveDegrees );
double radiansMod90 = Math.toRadians( degreesMod90 );
if ( positiveDegrees == 0 )
return img;
int quadrant = 0;
if ( positiveDegrees < 90 )
quadrant = 1;
else if ( ( positiveDegrees >= 90 ) &&
( positiveDegrees < 180 ) )
quadrant = 2;
else if ( ( positiveDegrees >= 180 ) &&
( positiveDegrees < 270 ) )
quadrant = 3;
else if ( positiveDegrees >= 270 )
quadrant = 4;
int height = img.getBounds( ).height;
int width = img.getBounds( ).width;
double side1 = ( Math.sin( radiansMod90 ) * height ) +
( Math.cos( radiansMod90 ) * width );
double side2 = ( Math.cos( radiansMod90 ) * height ) +
( Math.sin( radiansMod90 ) * width );
double h = 0;
int newWidth = 0, newHeight = 0;
if ( ( quadrant == 1 ) || ( quadrant == 3) ) {
h = ( Math.sin( radiansMod90) * height );
newWidth = ( int )side1;
newHeight = ( int )side2;
} else {
h = ( Math.sin( radiansMod90 ) * width );
newWidth = ( int )side2;
newHeight = ( int )side1;
}
int shiftX = ( int )( Math.cos( radians ) * h ) -
( ( quadrant == 3 ) || ( quadrant == 4 )
? width : 0 );
int shiftY = ( int )( Math.sin( radians ) * h ) +
( ( quadrant == 2) || ( quadrant == 3 )
? height : 0 );
Image newImg = new Image( display, newWidth, newHeight );
GC newGC = new GC( newImg );
Transform tr = new Transform( display );
tr.rotate( ( float )positiveDegrees );
newGC.setTransform( tr );
newGC.setBackground( display.getSystemColor
( SWT.COLOR_WHITE ) );
newGC.drawImage( img, shiftX, -shiftY );
newGC.dispose( );
return newImg;
}
In this class, the getCategoryProvider( ) method provides the category information for Property Editor pages such as Bookmark, Borders, Margin, Section, and Table of Contents for the rotated label report item. Listing 18-6 shows the code for the getCategoryProvider( ) method, which specifies the category properties and associated classes.
public ICategoryProvider getCategoryProvider( Object input )
{
CategoryProvider provider = new CategoryProvider(
new String[ ]{
CategoryProviderFactory.CATEGORY_KEY_GENERAL,
CategoryProviderFactory.CATEGORY_KEY_BORDERS,
CategoryProviderFactory.CATEGORY_KEY_MARGIN,
CategoryProviderFactory.CATEGORY_KEY_ALTTEXT,
CategoryProviderFactory.CATEGORY_KEY_SECTION,
CategoryProviderFactory.CATEGORY_KEY_VISIBILITY,
CategoryProviderFactory.CATEGORY_KEY_TOC,
CategoryProviderFactory.CATEGORY_KEY_BOOKMARK,
CategoryProviderFactory.CATEGORY_KEY_USERPROPERTIES,
CategoryProviderFactory
.CATEGORY_KEY_NAMEDEXPRESSIONS,
CategoryProviderFactory
.CATEGORY_KEY_ADVANCEPROPERTY,
}, new String[ ]{
"General",
"Borders",
"Margin",
"AltText",
"Section",
"Visibility",
"TOC",
"Bookmark",
"UserProperties",
"NamedExpressions",
"AdvancedProperty",
}, new Class[ ]{
RotatedLabelGeneralPage.class,
BordersPage.class,
ItemMarginPage.class,
AlterPage.class,
SectionPage.class,
VisibilityPage.class,
TOCExpressionPage.class,
BookMarkExpressionPage.class,
UserPropertiesPage.class,
NamedExpressionsPage.class,
AdvancePropertyPage.class,
} );
return provider;
}
In this class, the buildUI( ) method creates the content for the General page in the Property Editor, adding the following display text controls:
• Font
• Rotation Angle
• Size
Listing 18-7 shows the code for the buildUI( ) method.
public void buildUI( Composite parent )
{
super.buildUI( parent );
container.setLayout( WidgetUtil.createGridLayout( 5, 15 ) );
LibraryDescriptorProvider provider = new
LibraryDescriptorProvider( );
librarySection = new TextSection(
provider.getDisplayName( ),
container,
true );
librarySection.setProvider( provider );
librarySection.setGridPlaceholder( 2, true );
addSection( RotatedLabelPageSectionID.LIBRARY,
librarySection );
// display text property
separatorSection = new SeperatorSection(
container, SWT.HORIZONTAL );
addSection( RotatedLabelPageSectionID.SEPARATOR,
separatorSection );
TextPropertyDescriptorProvider nameProvider =
new TextPropertyDescriptorProvider( "displayText",
ReportDesignConstants.EXTENDED_ITEM );
TextSection nameSection = new TextSection(
"Display text:",
container,
true );
nameSection.setProvider( nameProvider );
nameSection.setGridPlaceholder( 3, true );
nameSection.setWidth(200 );
addSection( RotatedLabelPageSectionID.DISPLAY_TEXT,
nameSection );
// rotation angle property
TextPropertyDescriptorProvider angleProvider =
new TextPropertyDescriptorProvider( "rotationAngle",
ReportDesignConstants.EXTENDED_ITEM );
TextSection angleSection =
new TextSection( "Rotation Angle:",
container,
true );
angleSection.setProvider( angleProvider );
angleSection.setGridPlaceholder( 3, true );
angleSection.setWidth( 200 );
addSection( RotatedLabelPageSectionID.ROTATION_ANGLE,
angleSection );
//font family property
ComboPropertyDescriptorProvider fontFamilyProvider =
new ComboPropertyDescriptorProvider(
StyleHandle.FONT_FAMILY_PROP,
ReportDesignConstants.STYLE_ELEMENT );
ComboSection fontFamilySection =
new ComboSection( fontFamilyProvider.getDisplayName( ),
container,
true );
fontFamilySection.setProvider( fontFamilyProvider );
fontFamilySection.setLayoutNum( 2 );
fontFamilySection.setWidth( 200 );
addSection( PageSectionId.LABEL_FONT_FAMILY,
fontFamilySection );
//font size property
FontSizePropertyDescriptorProvider fontSizeProvider =
new FontSizePropertyDescriptorProvider(
StyleHandle.FONT_SIZE_PROP,
ReportDesignConstants.STYLE_ELEMENT );
FontSizeSection fontSizeSection = new FontSizeSection(
fontSizeProvider.getDisplayName( ),
container,
true );
fontSizeSection.setProvider( fontSizeProvider );
fontSizeSection.setLayoutNum( 4 );
fontSizeSection.setGridPlaceholder( 2, true );
fontSizeSection.setWidth( 200 );
addSection( PageSectionId.LABEL_FONT_SIZE,
fontSizeSection );
createSections( );
layoutSections( );
}
After building the plug-in, the Eclipse PDE provides support for deploying and testing the plug-in in a run-time environment. The following sections describe the steps to deploy and test the rotated label report item plug-in example.
To deploy the rotated label report item plug-in and integrate the extension with the BIRT Report Designer, use the Export wizard or manually copy the org.eclipse.birt.sample.reportitem.rotatedtext plug-in from your workspace to the eclipseplugins directory. For testing, you can create a Run configuration and include the rotated label plug-in from the workspace in the launch plug-in configuration.
On PDE Manifest Editor, in Overview, the Testing section contains links to launch a plug-in as a separate Eclipse application in either Run or Debug mode. Figure 18-11 shows Overview for the rotated label report item extension example in the host instance of the PDE Workbench.
How to launch a run-time workbench
1 From the Eclipse SDK menu, choose Run—Run Configurations. In Run Configurations, right-click Eclipse Application. Choose New.
2 Create a configuration to launch an Eclipse application by performing the following tasks:
1 In Name, type:
RotatedLabel
2 On Main, in Location, type:
C:TestTestRotatedLabel
Run Configurations appears as shown in Figure 18-12.
3 Choose the Plug-ins tab to select the list of plug-ins that you want to launch with the Run configuration.
4 As shown in Figure 18-13, choose
plug-ins selected below only
5 Choose Run to launch the run-time workbench.
6 In the run-time workbench, choose the Report Design perspective.
7 In Report Design, choose File→New→Project. New Project appears. In Wizards, choose Report Project, as shown in Figure 18-14.
Choose Next. New Report Project appears.
8 In Project name, type:
testRotatedLabel
Choose Finish.
9 In Report Design—Eclipse Platform, choose File→New→Report. New Report appears, as shown in Figure 18-15.
10 In File name, type a file name to change the default file name. Choose Next. New Report displays the report templates.
11 In Report templates, choose Blank Report. Choose Finish. The layout editor displays the report design, new_report.rptdesign. Palette contains the RotatedText report item.
12 From Palette, drag RotatedLabel to Layout, as shown in Figure 18-16.
13 In new_report.rptdesign, choose Preview. The preview appears, displaying the rotated label report item, as shown in Figure 18-17.
The rotated label example, discussed earlier in this chapter in “Developing the sample report item extension,” outlines the basic steps in creating a custom report item in BIRT. This section extends that earlier discussion by implementing more advanced functionality to enhance the report item usability and behavior.
The elements of the advanced example demonstrate how to develop additional user interface features for a custom rotated report item. The example shows how to implement a report item builder, a context menu, a custom property page, and data binding.
The example begins by defining a simple report model, design, and behavior. These steps are familiar from the previous example. This example uses more extension points to develop more advanced features. Creating a custom report item involves extending at least these three extension points:
• org.eclipse.birt.report.model.reportItemModel, which provides the report model extensibility
• org.eclipse.birt.report.designer.ui.reportitemUI, which provides the report designer extensibility
• org.eclipse.birt.report.engine.reportitemPresentation, which provides the report engine extensibility
The BIRT Report Model provides the org.eclipse.birt.report.model.reportItemModel extension point. As shown in Figure 18-18, defining the report item model involves creating a reportItem element under the extension and specifying the extensionName property as RotatedText. The extension name is the identifier for the extended item and serves as a connection between the report model and the engine and the designer extensions.
As shown in Figure 18-19, the model factory class org.eclipse.birt.sample.reportitem.rotatedtext.RotatedTextItemFactory must be defined.
This factory class creates and initializes the IReportItem instance, and provides optional localization support.
Listing 18-8 shows the code for the RotatedTextItemFactory class.
public class RotatedTextItemFactory extends ReportItemFactory
{
public IReportItem newReportItem( DesignElementHandle
modelHandle )
{
if ( modelHandle instanceof ExtendedItemHandle &&
RotatedTextItem.EXTENSION_NAME.equals( ( (ExtendedItemHandle)
modelHandle ).getExtensionName( ) ) )
{
return new RotatedTextItem( (ExtendedItemHandle)
modelHandle );
}
return null;
}
public IMessages getMessages( )
{
// TODO implement this to support localization
return null;
}
}
The reportItem properties page contains more properties like defaultStyle, extendsFrom, hasStyle, and others, which can be used to define additional features. This example does not extend these features; it uses the default values for these properties.
Like the Rotated Label example, the Rotated text element has two properties:
• rotationAngle, which defines the text rotation angle, using type integer and a default value 0, as shown in Figure 18-20.
• text, which defines the report item text content, using type expression and a default value “Rotated Text”, as shown in Figure 18-21.
These settings complete the report model definition. The next step is to create a class RotatedTextItem. This class defines the basic methods for accessing the report item properties. The code is shown in Listing 18-9.
public class RotatedTextItem extends ReportItem
{
public static final String EXTENSION_NAME = "RotatedText"; //
$NON-NLS-1$
public static final String TEXT_PROP = "text"; //$NON-NLS-1$
public static final String ROTATION_ANGLE_PROP =
"rotationAngle"; //$NON-NLS-1$
private ExtendedItemHandle modelHandle;
RotatedTextItem( ExtendedItemHandle modelHandle )
{
this.modelHandle = modelHandle;
}
public String getText( )
{
return modelHandle.getStringProperty( TEXT_PROP );
}
public int getRotationAngle( )
{
return modelHandle.getIntProperty( ROTATION_ANGLE_PROP );
}
public void setText( String value ) throws SemanticException
{
modelHandle.setProperty( TEXT_PROP, value );
}
public void setRotationAngle( int value ) throws
SemanticException
{
modelHandle.setProperty( ROTATION_ANGLE_PROP, value );
}
}
The Designer provides the org.eclipse.birt.report.designer.ui.reportitemUI extension point to define the user interface of extended report items. Add the org.eclipse.birt.report.designer.ui.reportitemUI extension point and create the design properties to define the user interface, as shown in Figure 18-22.
The design properties are as follows:
• RotatedText (model)
• palette
• editor
• outline
• The first property, RotatedText (model), binds the Designer extension to the Model extension. The RotatedText’s extensionName is RotatedText, the same as the model extension name, as shown in Figure 18-23.
• Palette properties specify the icon displayed in Palette view and the palette category. An empty category value, as shown in Figure 18-24, causes the report item to use the default category of Report Item.
• Editor properties define the report item visibility in different editor pages and whether the resizing control is enabled, as shown in Figure 18-25.
• Outline specifies the icon shown in Outline view, as shown in Figure 18-26. Usually the icon is the same as the one in Palette view.
• The User Interface (UI) provider defines how to display and interact with the extended report item within the editor. The BIRT designer supports three types of UI providers:
• The Label UI Provider implements the IReportItemLabelProvider interface, which manipulates and displays a text content.
• The Image UI Provider implements the IReportItemImageProvider interface, which manipulates and displays an image content.
• The Figure UI Provider implements the IReportItemFigureProvider, which uses the Figure interface from Graphical Editing Framework (GEF), which provides flexibility and interactivity support.
This example introduces all three providers, starting with the simplest Label UI Provider. The property reportItemLabelUI, created under the extension, registers the Label UI provider, and specifies the implementer class org.eclipse.birt.sample.reportitem.rotatedtext.RotatedTextLabelUI, as shown in Figure 18-27.
The code for RotatedTextLabelUI class, shown in Listing 18-10, reads the text property value from the model and returns it as a string.
public class RotatedTextLabelUI implements
IReportItemLabelProvider
{ public String getLabel( ExtendedItemHandle handle )
{ try {
IReportItem item = handle.getReportItem( );
if ( item instanceof RotatedTextItem )
{
return ( (RotatedTextItem) item ).getText( );
}
} catch ( ExtendedElementException e )
{
e.printStackTrace( );
}
return null;
}
}
The final step is to test the completed report item definition in the BIRT Designer. For more information about testing the report item definition, see “Deploying and testing the rotated label report item plug-in,” earlier in this chapter. Open a new Eclipse instance in Report Design perspective. Open the Palette view, as shown in Figure 18-28.
The Palette contains the new RotatedText extended report item.
Right-click anywhere in the Layout, and the RotatedText item is now also available in the Insert context menu, as shown in Figure 18-29.
A RotatedText report item inserted in the layout also appears in the Outline view as shown in Figure 18-30.
Select the RotatedText item in the Layout and open the Properties view. The properties of the item appear as shown in Figure 18-31.
The Properties view displays all the properties that belong to the RotatedText item. Among them are the Rotation Angle and Text Content properties, as well as other inherited properties. The Properties view supports editing these property values.
BIRT Report Engine provides the org.eclipse.birt.report.engine.reportitemPresentation extension point, which defines the presentation of extended report items.
The extension property RotatedText binds the engine extension to the model extension. The property name must be set to RotatedText to match the name of the model’s RotatedText property.
The presentation implementer class is set to org.eclipse.birt.sample.reportitem.rotatedtext.RotatedTextPresentationImpl and must implement the IReportItemPresentation interface. The implementation of the class creates the text image dynamically, and uses Swing graphics API to render the rotated text. Listing 18-11 shows the code for this class.
public class RotatedTextPresentationImpl
extends ReportItemPresentationBase
{
private RotatedTextItem textItem;
public void setModelObject( ExtendedItemHandle modelHandle )
{
try{
textItem = (RotatedTextItem) modelHandle.getReportItem();
}
catch ( ExtendedElementException e )
{
e.printStackTrace( );
}
}
public int getOutputType( )
{
return OUTPUT_AS_IMAGE;
}
public Object onRowSets( IRowSet[] rowSets )
throws BirtException
{
if ( textItem == null )
{
return null;
}
int angle = textItem.getRotationAngle( );
String text = textItem.getText( );
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;
}
}
This implementation generates an image dynamically in the onRowSets() method. An in-memory stream returns the image content to the caller. The utility class that implements the key rotation rendering algorithm is shown in Listing 18-12.
public class SwingGraphicsUtil
{
public static BufferedImage createRotatedTextImage(
String text, int angle, Font ft )
{
Graphics2D g2d = null;
try {
if ( text == null || text.trim( ).length( ) == 0 )
{
return null;
}
BufferedImage stringImage = new BufferedImage( 1, 1,
BufferedImage.TYPE_INT_ARGB );
g2d = (Graphics2D) stringImage.getGraphics( );
g2d.setFont( ft );
FontMetrics fm = g2d.getFontMetrics( );
Rectangle2D bounds = fm.getStringBounds( text, g2d );
TextLayout tl = new TextLayout( text, ft,
g2d.getFontRenderContext( ) );
g2d.dispose( );
g2d = null;
return createRotatedImage( tl, (int) bounds.getWidth( ),
(int) bounds.getHeight( ), angle );
}
catch ( Exception e )
{
e.printStackTrace( );
if ( g2d != null )
{
g2d.dispose( );
}
}
return null;
}
private static BufferedImage createRotatedImage( Object src,
int width, int height, int angle )
{
angle = angle % 360;
if ( angle < 0 )
{
angle += 360;
}
if ( angle == 0 )
{
return renderRotatedObject( src, 0, width, height, 0, 0);
}
else if ( angle == 90 )
{
return renderRotatedObject( src, -Math.PI / 2, height,
width, -width, 0 );
}
else if ( angle == 180 )
{
return renderRotatedObject( src, Math.PI, width, height,
-width, -height );
}
else if ( angle == 270 )
{
return renderRotatedObject( src, Math.PI / 2, height,
width, 0, -height );
}
else if ( angle > 0 && angle < 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 renderRotatedObject( src, angleInRadians, dW, dH,
-width * sineTheta * sineTheta,
width * sineTheta * cosTheta );
}
else if ( angle > 90 && angle < 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 renderRotatedObject( src, angleInRadians, dW, dH,
-( width + height * sineTheta * cosTheta ),
-height / 2 );
}
else if ( angle > 180 && angle < 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 renderRotatedObject( src, angleInRadians, dW, dH,
-( width * cosTheta * cosTheta ),
-( height + width * cosTheta * sineTheta ) );
}
else if ( angle > 270 && angle < 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 renderRotatedObject( src, angleInRadians, dW, dH,
( height * cosTheta * sineTheta ),
-( height * sineTheta * sineTheta ) );
}
return renderRotatedObject( src, 0, width, height, 0, 0 );
}
private static BufferedImage renderRotatedObject( Object src,
double angle, int width, int height, double tx, double ty )
{
BufferedImage dest = new BufferedImage( width, height,
BufferedImage.TYPE_INT_ARGB );
Graphics2D g2d = (Graphics2D) dest.getGraphics( );
g2d.setColor( Color.black );
g2d.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON );
AffineTransform at = AffineTransform.getRotateInstance(
angle );
at.translate( tx, ty );
g2d.setTransform( at );
if ( src instanceof TextLayout )
{
TextLayout tl = (TextLayout) src;
tl.draw( g2d, 0, tl.getAscent( ) );
}
else if ( src instanceof Image )
{
g2d.drawImage( (Image) src, 0, 0, null );
}
g2d.dispose( );
return dest;
}
}
To test the report item, open a new Eclipse instance in the Report Design perspective. Create a report and insert a grid having nine cells. Insert a RotatedText item in each cell, as shown in Figure 18-33, and use the Properties view to set a different rotation angle for each report item.
A preview of the report as HTML is shown in Figure 18-34.
As you see in the report layout in Figure 18-33, all RotatedText report items in the report design look the same, displayed as horizontal text. Look at the Properties view to see the different angles at which the text would be displayed in the preview. This inconsistency prevents the report developer from getting an intuitive picture of the final report at the design phase. One obvious improvement is to make the report item image appear in the design in the same way in which it appears in the preview. This technique is called WYSIWYG, the familiar acronym for what you see is what you get, that is adopted by most contemporary editors.
To comply with the WYSIWYG paradigm, change the report item presentation to display the rotated text in the layout. Use the Image UI provider or the Figure UI provider instead of the Label UI Provider to implement this change.
The Image UI provider requires an image for the UI presentation. The implementation renders the rotated text as an image and returns it to the caller.
To register the Image UI provider, remove the original Label UI provider first, and then add the new Image UI provider extension, as shown in Figure 18-35.
The implementer class code is shown in Listing 18-13.
public class RotatedTextImageUI
implements IReportItemImageProvider
{
public void disposeImage( ExtendedItemHandle handle,
Image image )
{
if ( image != null && !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;
}
}
The image handler uses Standard Widget Toolkit (SWT) to process and rotate the image in a new version of the SwingGraphicsUtil class, shown in Listing 18-14.
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 && !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 < 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 > 0 && angle < 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 > 90 && angle < 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 > 180 && angle < 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 > 270 && angle < 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 < 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 && !gc.isDisposed( ) )
{
gc.dispose( );
}
if ( tf != null && !tf.isDisposed( ) )
{
tf.dispose( );
}
}
return dest;
}
}
Run another test and insert a RotatedText item in the layout as shown in Figure 18-36. Specify the rotation angle as 45.
After implementing the new Image UI provider, the rotated text item in the report layout appears angled as in the preview, as shown in Figure 18-37.
The Image UI provides the basic functionality for implementing WYSIWYG support for the rotated report item.
The Figure UI provider is typically used in cases where the requirements are more complex. The Figure UI leverages the IFigure interface from Graphical Editing Framework (GEF) and provides more flexibility and interactivity than the Image UI. Table 18-10 shows a comparison between Figure UI and Image UI.
A good use case for the Figure UI Provider is one where a mouse middle-button click controls the report item rotation angle. Each middle-button click adds 45 degrees to the value of the rotation angle.
To implement the Figure UI provider, first remove any existing Label UI or Image UI provider in the extension points. Then, add the new Figure UI provider extension, and specify the implementer class as org.eclipse.birt.sample.reportitem.rotatedtext.RotatedTextFigureUI. The code for the class is in Listing 18-15.
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( );
}
}
The Figure UI provider creates a RotatedTextFigure instance. The RotatedTextFigure class implements the rendering following an algorithm similar to the Image UI provider. The only addition is using a listener to handle the mouse middle-button click event. The code for this class is in Listing 18-16.
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 < 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 && !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 && !cachedImage.isDisposed( ) )
{
cachedImage.dispose( );
}
cachedImage = SwtGraphicsUtil.createRotatedTextImage(
text, angle, null );
}
if ( cachedImage != null && !cachedImage.isDisposed( ) )
{
graphics.drawImage( cachedImage, r.x, r.y );
}
}
void setRotatedTextItem( RotatedTextItem item )
{
this.textItem = item;
}
void dispose( )
{
if ( cachedImage != null && !cachedImage.isDisposed( ) )
{
cachedImage.dispose( );
}
}
}
To test the RotatedText report item, insert a RotatedText item in the layout editor. Each time you middle-click the RotatedText item, the image rotates by 45 degrees.
In the implementation so far, the only way to manipulate the property values of the RotatedText report item is from Property view. Another more sophisticated and user friendly way to set the properties is to use a builder. A builder is a user interface, designed to populate the properties and create a new instance of a selected item. Some of the report items in BIRT, like the Chart, for example, have associated builders. The builders appear every time you double-click or add a report item to the layout.
To create a builder for the RotatedText item, add the builder UI extension and specify the implementer class as org.eclipse.birt.sample.reportitem.rotatedtext.RotatedTextBuilder, as shown in Figure 18-38.
The class RotatedTextBuilder extends the org.eclipse.birt.report.designer.ui.extensions.IReportItemBuilderUI interface. The code is in Listing 18-17.
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;
}
}
The code instantiates an object of RotatedTextEditor class to populate the actual UI. The code of the RotatedTextEditor class is in Listing 18-18.
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( );
}
}
The RotatedText builder UI, as shown in Figure 18-39, consists of a Text control and a Scale control, corresponding to the text content and rotation angle properties. The builder appears every time you add a RotatedText item to the layout or double-click a RotatedText item in the layout.
Another attractive and convenient UI feature is the context menu. The context menu displays links to specific actions and appears when a user right-clicks a selected object with the mouse.
To support a custom context menu, implement the extension org.eclipse.birt.report.designer.ui.menuBuilders. The implementer class implements the org.eclipse.birt.report.designer.ui.extensions.IMenuBuilder interface, as shown in Figure 18-40.
As shown in Figure 18-41, the RotatedText elementName binds menuBuilders extension to the model extension. The implementer class name is org.eclipse.birt.sample.reportitem.rotatedtext.RotatedTextMenuBuilder.
The menu builder extension for RotatedText item contains four custom actions to perform quick rotations at angle values of -90, 90, 0 and 180 degrees. The code is shown in Listing 18-19.
public class RotatedTextMenuBuilder implements IMenuBuilder
{
public void buildMenu( IMenuManager menu, List selectedList )
{
if ( selectedList != null && selectedList.size( ) == 1
&& 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 > 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( );
}
}
}
}
Run the designer to test the context menu. Right-click any RotatedText report item in the layout editor. The context menu appears as shown in Figure 18-42.
The property editor in BIRT Designer provides a user friendly interface for property editing. The property editor controls two types of settings, generic settings for all report items, and specific settings for each report item.
Property Editor, as shown in Figure 18-43, appears as a separate view having a tabbed style in BIRT Designer.
The property tabs, positioned horizontally, organize the property settings into several pages. The category tabs, positioned vertically, group the properties in a single page into different categories. By default, only the first Properties tab supports categories. You can alter the style and overwrite the logic behind this user interface.
You can create a similar Property Editor for the RotatedText report item. The new Property Editor should create a user interface for the rotated text and rotated angle properties and reuse the generic property user interface for report items.
To support custom property pages, implement the org.eclipse.birt.report.designer.ui.elementAdapters extension, as shown in Figure 18-44.
The org.eclipse.birt.report.designer.ui.elementAdapters extension point is a generic extension point used by BIRT Designer to support property page and other UI extensions.
In the BIRT model, the org.eclipse.birt.report.model.api.ExtendedItemHandle class represents all custom extended report items.
Specify the adaptable class as org.eclipse.birt.report.model.api.ExtendedItemHandle.
The next step is to specify the adapter settings, as shown in Figure 18-45.
Table 18-11 lists and explains the adapter settings
The adapter settings describe the generic behavior of the property editor. The next step is to create custom property pages for the RotatedText item, and set additional constraints for the adaptable. To accomplish this task, create an enablement element using type test under the adapter node. In the settings, as shown in Figure 18-46, specify the test property as ExtendItemHandle.extensionName and the value as RotatedText. This approach restricts the adaptable object to be the RotatedText report items only.
The code of the factory class is shown in Listing 18-20.
public class RotatedTextPageGeneratorFactory implements
IAdapterFactory
{
public Object getAdapter( Object adaptableObject, Class
adapterType )
{
return new RotatedTextPageGenerator( );
}
public Class[] getAdapterList( )
{
return new Class[]{
IPageGenerator.class
};
}
}
The factory class creates a generator instance per call. Each generator implements the org.eclipse.birt.report.designer.ui.views.IPageGenerator interface. Extend the org.eclipse.birt.report.designer.ui.views.attributes.AbstractPageGenerator class to use the basic support for categorized styles created by this class.
The implementation of the RotatedTextPageGenerator class is shown in Listing 18-21. The class overwrites the General category page in the Properties tab and reuses some of the built-in categories like Border, Margin, and Page Break. The code also adds a Custom property tab to the property editor. The RotatedTextPageGenerator class overwrites three methods, buildItemContent( ), createTabItems( ), and refresh( ). The methods createTabItems( ) and buildItemContent( ) implement the custom logic. The createTabItem( ) method overwrites the creation logic for the General category page to add the reused built-in pages and the new Custom property page. The buildItemContent( ) method contains the code to create the Custom property page.
public class RotatedTextPageGenerator
extends AbstractPageGenerator
{
private static final String CUSTOM_PAGE_TITLE = "Custom";
//$NON-NLS-1$
private IPropertyTabUI generalPage;
protected void buildItemContent( CTabItem item )
{
if ( itemMap.containsKey( item )
&& itemMap.get( item ) == null )
{
String title = tabFolder.getSelection( ).getText( );
if ( CUSTOM_PAGE_TITLE.equals( title ) )
{
TabPage page = new RotatedTextCustomPage( ).getPage( );
if ( page != null )
{
setPageInput( page );
refresh( tabFolder, page, true );
item.setControl( page.getControl( ) );
itemMap.put( item, page );
}
}
}
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( ) );
generalPage = AttributesUtil.buildGeneralPage( tabFolder,
new String[]{
null,
AttributesUtil.BORDER,
AttributesUtil.MARGIN,
AttributesUtil.SECTION,
AttributesUtil.VISIBILITY,
AttributesUtil.TOC,
AttributesUtil.BOOKMARK,
AttributesUtil.USERPROPERTIES,
AttributesUtil.NAMEDEXPRESSIONS,
AttributesUtil.ADVANCEPROPERTY
},
new String[]{ "General" }, //$NON-NLS-1$
new String[]{ "General" }, //$NON-NLS-1$
new AttributesUtil.PageWrapper[]{
new RotatedTextGeneralPage( )
},
input );
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( );
createTabItem( CUSTOM_PAGE_TITLE, ATTRIBUTESTITLE );
if ( tabFolder.getSelection( ) != null )
{
buildItemContent( tabFolder.getSelection( ) );
}
}
}
The General category page contains two text controls for editing: one for the text content and one for the rotation angle of the RotatedText report item. Listing 18-22 shows the code.
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 && children.length > 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 < 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 && ( (List) input ).size( ) > 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 && !contentpane.isDisposed( ) )
{
if ( toolkit == null )
{
toolkit = new FormToolkit( Display.getCurrent( ) );
toolkit.setBorderStyle( SWT.NULL );
}
adaptFormStyle( contentpane );
updateUI( );
}
}
public void postElementEvent( )
{
if ( contentpane != null && !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 );
txtAngle.setText( String.valueOf( item.getRotationAngle(
) ) );
}
}
}
The RotatedTextGeneralPage class uses FormToolkit to achieve the same look and feel as the built-in pages in the Property editor. Alternatively, you can design your own UI styles.
The Custom property page, shown in Listing 18-23, implements a read-only version of the General category page just to illustrate the extension mechanism.
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 && children.length > 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( )
) );
}
}
}
The newly designed Property editor is shown in Figure 18-47. The General category page displays the two editable properties.
The reused Border category brings up the built-in border property page where the report developer can change the report item border settings. The changes immediately appear in the layout editor.
The Custom page, shown in Figure 18-48, displays the read-only properties.
BIRT report items access data only through column bindings. Column bindings form an intermediate layer between data set data and report items that display data. Column bindings can also access data derived from functions or user-defined formulas.
To support data bindings, the RotatedText property must be binding-aware, so the text value can change dynamically. The implementation involves changes in the model, the presentation, and the user interface of the RotatedText report item. The user interface modifications include changes in the RotatedText item builder and the Property editor.
To support data binding for the text property, change the model extension definition. Change the property type of the text from string to expression by adding quotation marks around the original default value, as shown in Figure 18-49. This change indicates that the property supports expressions. As the original default value, Rotated Text, is not a valid JavaScript expression, change it to use JavaScript literal string syntax.
To support expressions, the code of the onRowSets( ) method of RotatedTextPresentationImpl class must be changed, as shown in Listing 18-24. The new code adds logic to handle the text property as an expression. The code evaluates the text value under the current engine context to get the final string result.
public Object onRowSets( IBaseResultSet[] results )
throws BirtException
{
if ( textItem == null )
{
return null;
}
int angle = textItem.getRotationAngle( );
String text = textItem.getText( );
// XXX added to support expression
if ( results != null && results.length > 0 )
{
if ( results[0] instanceof IQueryResultSet
&& ( (IQueryResultSet) results[0] ).isBeforeFirst( ) )
{
( (IQueryResultSet) results[0] ).next( );
}
text = String.valueOf( results[0].evaluate( text ) );
} else
{
text = String.valueOf( context.evaluate( text ) );
}
// end new code
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;
}
To update the user interface you must make changes in two places—the builder and the property page.
To enable report developers to use the standard BIRT expression builder to construct a data-binding expression, the report item builder must provide a button to lunch the expression builder. Place the button beside the text control, as shown in Figure 18-50.
The standard BIRT expression builder, shown in Figure 18-51, provides JavaScript support.
The code of the RotatedTextBuilder class changes, as shown in Listing 18-25. The newly created RotatedTextEditor2 class overwrites the UI creation logic for the new expression button.
public class RotatedTextBuilder extends ReportItemBuilderUI
{
public int open( ExtendedItemHandle handle )
{
try {
IReportItem item = handle.getReportItem( );
if ( item instanceof RotatedTextItem )
{
RotatedTextEditor editor = new RotatedTextEditor2(
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 );
}
}
}
A similar change must be made to the Property page. The General property page needs a button, beside the text control, that calls the expression builder. The code implementing the change to the RotatedTextGeneralPage class is shown in Listing 18-26.
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 && children.length > 0 )
{
contentpane = (Composite) children[children.length - 1];
GridLayout layout = new GridLayout( 3, 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 );
};
} );
Button btnExp = toolkit.createButton( contentpane, "...",
SWT.PUSH ); //$NON-NLS-1$
btnExp.setToolTipText( "Invoke Expression Builder" );
//$NON-NLS-1$
btnExp.addSelectionListener( new SelectionAdapter( ) {
public void widgetSelected( SelectionEvent e )
{
openExpression( txtText );
}
} );
toolkit.createLabel( contentpane, "Rotation Angle:" );
//$NON-NLS-1$
txtAngle = toolkit.createText( contentpane, "" );
//$NON-NLS-1$
gd = new GridData( );
gd.widthHint = 200;
gd.horizontalSpan = 2;
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 );
}
}
}
//..........
}
The new appearance of the General property page is shown in Figure 18-52.
To test the implementation, run a new report design instance and create a new report, as explained earlier in this chapter in “Deploying and testing the rotated label report item plug-in.” Insert a ClassicModels data source and data set. Create a table having three columns, and bind the table to the data set. Insert one RotatedText report item in the table detail row, and two more data items, as shown in Figure 18-53.
Specify the expression of the RotatedText item as shown in Figure 18-54.
The preview in the Report Designer, shown in Figure 18-55, displays values from the database for the RotatedText report item. The text data comes from the column bindings and automatically changes per row.