Chapter 18. Developing a Report Item Extension

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

Understanding a report item extension

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.

• Generation

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.

Developing the sample report item extension

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

Downloading BIRT source code from the CVS repository

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.

Creating a rotated label report item plug-in project

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.

Table 18-1 Settings for the Report Item Plug-in Project

image

Plug-in Project appears, as shown in Figure 18-1.

Figure 18-1 Specifying Report Item Plug-in Project settings

image

Choose Next. Plug-in Content appears.

4 In Plug-in Content, modify the settings as shown in Table 18-2.

Table 18-2 Settings for the Report Item Plug-in Content

image

New Plug-in Content appears, as shown in Figure 18-2. Choose Finish.

Figure 18-2 Specifying Report Item Plug-in Project content settings

image

The rotated label report item extension project appears in the Eclipse PDE Workbench, as shown in Figure 18-3.

Figure 18-3 Viewing Report Item Plug-in Project Overview

image

Defining the dependencies for the rotated label report item extension

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.

Figure 18-4 Viewing Dependencies showing required plug-ins

image

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.

Specifying the run-time package for the rotated label report item extension

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.

Figure 18-5 The Runtime page

image

Declaring the report item extension points

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.

• type

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.

Figure 18-6 Extension points in New Extension

image

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.

Figure 18-7 Required extension points for a report item extension

image

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.

Table 18-3 Properties for the reportItemUI extension

image

3 Repeat step 2 to add the property values shown in Table 18-3 to the extension details for the other extension points.

Creating the plug-in extension content

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.

Figure 18-8 Selecting an report item extension element

image

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.

Table 18-4 Properties for other reportitemUI extension elements

image

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.

Figure 18-9 Specifying properties for rotatedItemLabelUI

image

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.

Table 18-5 Property values for the reportItem

image

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.

Table 18-6 Property values for the displayText

image

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.

Table 18-7 Property values for the rotationAngle

image

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.

Table 18-8 Property values for the reportItem

image

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.

Table 18-9 Property values for the adapter

image

Figure 18-10 shows the full list of extension points and elements required for the example, org.eclipse.birt.sample.reportitem.rotatedlabel.

Figure 18-10 Viewing extension points for rotated label report item

image

Understanding the rotated label report item extension

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.

Understanding RotatedLabelItemFactoryImpl

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.

Listing 18-1 The newReportItem( ) method


public class RotatedTextItemFactoryImpl
   extends ReportItemFactory implements IMessages
{
   public IReportItem newReportItem( DesignElementHandle deh )
   {
      return  new ReportItem( );
   ...
}

Understanding RotatedLabelUI

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.

Listing 18-2 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";
      }
   }

Understanding RotatedLabelPresentationImpl

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.

Listing 18-3 The onRowSets( ) method


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( );
}

Understanding GraphicsUtil

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.

Listing 18-4 The 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.

Listing 18-5 The rotateImage( ) method


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;
   }

Understanding RotatedLabelCategoryProviderFactory

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.

Listing 18-6 The getCategoryProvider( ) method


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;
   }

Understanding RotatedLabelGeneralPage

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.

Listing 18-7 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( );
   }

Deploying and testing the rotated label report item plug-in

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.

Deploying a report item extension

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.

Launching the rotated label report item plug-in

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.

Figure 18-11 Viewing Overview for the rotated label report item extension

image

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.

Figure 18-12 Creating the RotatedLabel run configuration

image

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

Figure 18-13 Adding the rotated label plug-in to the launch configuration

image

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.

Figure 18-14 Selecting Report Project in New Project

image

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.

Figure 18-15 Creating a New Report for a rotated label

image

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.

Figure 18-16 Rotated label report item in the report design

image

13 In new_report.rptdesign, choose Preview. The preview appears, displaying the rotated label report item, as shown in Figure 18-17.

Figure 18-17 The rotated label in the report preview

image

Developing an advanced report item

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

Defining the report item model

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.

Figure 18-18 Adding the basic extension points

image

As shown in Figure 18-19, the model factory class org.eclipse.birt.sample.reportitem.rotatedtext.RotatedTextItemFactory must be defined.

Figure 18-19 Defining the report model

image

This factory class creates and initializes the IReportItem instance, and provides optional localization support.

Listing 18-8 shows the code for the RotatedTextItemFactory class.

Listing 18-8 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.

Figure 18-20 Defining the rotationAngle properties

image

• text, which defines the report item text content, using type expression and a default value “Rotated Text”, as shown in Figure 18-21.

Figure 18-21 Defining the text properties

image

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.

Listing 18-9 RotatedTextItem class


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 );
   }
}

Defining the report item UI design

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.

Figure 18-22 Defining the design

image

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.

Figure 18-23 RotatedText (model) name

image

• 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.

Figure 18-24 Palette settings

image

• Editor properties define the report item visibility in different editor pages and whether the resizing control is enabled, as shown in Figure 18-25.

Figure 18-25 Editor settings

image

• 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.

Figure 18-26 Outline settings

image

• 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.

Figure 18-27 reportItemLabelUI setting

image

The code for RotatedTextLabelUI class, shown in Listing 18-10, reads the text property value from the model and returns it as a string.

Listing 18-10 RotatedTextLabelUI class


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.

Figure 18-28 Palette view showing the RotatedText report item

image

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.

Figure 18-29 Insert context menu showing the RotatedText report item

image

A RotatedText report item inserted in the layout also appears in the Outline view as shown in Figure 18-30.

Figure 18-30 Outline view showing a RotatedText report item

image

Select the RotatedText item in the Layout and open the Properties view. The properties of the item appear as shown in Figure 18-31.

Figure 18-31 Properties view of a RotatedText report item

image

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.

Defining the report item presentation

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.

Figure 18-32 Defining the presentation

image

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.

Listing 18-11 RotatedTextPresentationImpl 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.

Listing 18-12 SwingGraphicsUtil class


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.

Figure 18-33 Report layout having nine RotatedText items

image

A preview of the report as HTML is shown in Figure 18-34.

Figure 18-34 Preview in HTML

image

Improving the report item UI design

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.

Using the Image UI provider

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.

Figure 18-35 Changing the UI provider

image

The implementer class code is shown in Listing 18-13.

Listing 18-13 RotatedTextImageUI class


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.

Listing 18-14 SwingGraphicsUtil class


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.

Figure 18-36 Report layout of RotatedText implementing Image UI

image

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.

Figure 18-37 Report preview of RotatedText implementing Image UI

image

Using the Figure UI Provider

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.

Table 18-10 Comparing Figure UI and Image UI

image

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.

Listing 18-15 RotatedTextFigureUI class


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.

Listing 18-16 RotatedTextFigure class


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.

Creating a report item builder

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.

Figure 18-38 Defining the builder support

image

The class RotatedTextBuilder extends the org.eclipse.birt.report.designer.ui.extensions.IReportItemBuilderUI interface. The code is in Listing 18-17.

Listing 18-17 RotatedTextBuilder class


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.

Listing 18-18 RotatedTextEditor class


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.

Figure 18-39 RotatedText builder

image

Creating a context menu

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.

Figure 18-40 Adding the menuBuilders extension

image

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.

Figure 18-41 Setting the menuBuilder properties

image

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.

Listing 18-19 RotatedTextMenuBuilder class


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.

Figure 18-42 Testing the RotatedText item context menu

image

Creating a property editor

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.

Figure 18-43 Property Editor for a table element

image

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.

Figure 18-44 Adding the elementAdapters extension

image

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.

Figure 18-45 Adapter extension

image

Table 18-11 lists and explains the adapter settings

Table 18-11 Adapter settings

image

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.

Figure 18-46 Enablement element settings

image

The code of the factory class is shown in Listing 18-20.

Listing 18-20 RotatedTextPageGeneratorFactory class


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.

Listing 18-21 RotatedTextPageGenerator class


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.

Listing 18-22 RotatedTextGeneralPage class


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.

Listing 18-23 RotatedTextCustomPage class


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.

Figure 18-47 General property page for the RotatedText item

image

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.

Figure 18-48 Custom property page for the RotatedText item

image

Binding the report item to data

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.

Changes in the model definition

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.

Figure 18-49 Setting the text property definition to an expression

image

Changes in the report item presentation

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.

Listing 18-24 onRowSets method of the RotatedTextPresentationImpl class


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;
}

Changing the report item UI to support expressions

To update the user interface you must make changes in two places—the builder and the property page.

Adding data binding to the builder

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.

Figure 18-50 RotatedText builder

image

The standard BIRT expression builder, shown in Figure 18-51, provides JavaScript support.

Figure 18-51 BIRT expression builder

image

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.

Listing 18-25 RotatedTextBuilder class


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 );
      }
   }
}

Adding data binding to the property page

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.

Listing 18-26 RotatedTextGeneralPage class


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.

Figure 18-52 Final appearance of the RotatedText item’s General property page

image

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.

Figure 18-53 Report layout showing the final RotatedText item

image

Specify the expression of the RotatedText item as shown in Figure 18-54.

Figure 18-54 Defining a data-binding expression for the RotatedText item

image

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.

Figure 18-55 Report preview showing data values in RotatedText items

image

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset