Project Notes


Project: dbSwing: JTextPane Sample
Author: JBuilder Team
Company: borland.com
Description:
This application uses Swing JInternalFrames to display product descriptions from the IntlDemo sample application. The descriptions are stored in a data set and displayed be a JdbTextPane. In addition to standard editing, font sizes and styles can be set on blocks of text.

What this sample demonstrates

Creating a multiple document interface with JDesktopPane and JInternalFrame

Implementing a floating toolbar that detects the active data set automatically

Displaying images from a data set in a JdbStatusLabel

Loading text from a resource file into a data set

Editing text in a JdbTextPane

Creating a toolbar of text-editing actions

Other design notes

Setup

This sample requires that you have JDataStore installed and read/write access to the sample IntlDemo.jds JDataStore database in the samples/JDataStore/datastores directory. The sample tries to dynamically determine the proper URL to IntlDemo.jds, but since the location is dependent upon the location of your JBuilder installation, it might not always succeed. It writes a message indicating the URL which it is trying to use. If you are unable to run or design the sample, you should explicitly specify the URL to IntlDemo.jds correct for your installation in TextPaneDesktop.java.

If you have not installed JDataStore, please install it before running this sample. Follow the instructions on Installing JDataStore in Developing Database Applications.

Creating a multiple document interface with JDesktopPane and JInternalFrame

Swing's JDesktopPane and JInternalFrame classes make it easy to create an application in which you can work with several "windows" of data on a "desktop". JDesktopPane provides the "desktop". When instances of JInternalFrame are added to a JDesktopPane, each one can be moved, minimized, resized, and maximized independently. This is an MDI (multiple document interface) application.

To create the skeleton of an MDI application in JBuilder, use the Desktop Pane and Internal Frame snippets on the first tab of the File | New gallery. Here, TextPaneDesktop.java, created with the Desktop Pane snippet, is our runnable file. It opens as an empty "desktop". Open a "window" (really an internal frame) that displays product descriptions by selecting a language from the menu. The code for these internal frames is in TextPaneInternalFrame.java, which began as an Internal Frame snippet. Because this is an MDI application, you can open several frames for different languages and move or resize each one as you like. Each frame has its own data set of product information, with text in the selected language.

Internal frames can be layered, but we put all our product information frames in the same layer, so the one that has focus is always on top. We do take advantage of layering when we create our toolbar, however. This is described in the next section.

Implementing a floating toolbar that detects the active data set automatically

When working with product information in this application, it's convenient to have a navigation toolbar. To save space, we'd like to have just one toolbar even when two or more product information frames are open. We accomplish this by defining another JInternalFrame class, TextPaneToolBar.java, that contains only a JdbNavToolBar. We create just one instance of this class when the first product information frame is opened, and we don't allow it to be resized or closed. We put the toolbar's frame in a higher layer than the product information frames, so it stays on top of them.

By default, a toolbar's floatable property is true, allowing it to be dragged to a new position in its layout (if the layout can accept it) or floated in its own window. Our toolbar is floatable, but there's nowhere in our application to dock it, so there are only two choices: leave it in its internal frame, or float it in a separate window. When not floated, the toolbar must stay within the desktop, like any internal frame; when floated, it can be placed anywhere but is not guaranteed to stay on top of the desktop or any other window. To float the toolbar, place the mouse over a spot on the toolbar that's outside of any button and drag. To return a floating toolbar to its frame, close it.

When the toolbar is floated, its internal frame becomes empty. We watch its ancestorRemoved event to know when it is floated/docked so we can make the internal frame invisible when it's empty.

By default, a toolbar automatically detects the data set of the active data-aware component. This is what we want: each product information frame has its own data set, and we want the toolbar to navigate the data set of the frame that has focus. We just help it out in one small way: when a frame itself - not a data-aware component on the frame - gets focus, we pass the focus on to the JdbTextPane on the frame. This causes the toolbar to bind to the frame's data set. We treat JdbStatusLabels differently, however. We want each frame's status label to report only on that frame's data set, so we set the status label's dataSet property explicitly.

Displaying images from a data set in a JdbLabel

Like Swing's JLabel, JdbLabel can display both text and images. Being data-aware, JdbLabel can get its data from a data set. To support images, it has two extra properties, columnNameIcon and iconEditable. These properties have the same meaning for the JdbLabel's icon as the standard columnName and editable properties do for its text value. For an image, "editable" means that you can double-click to open a file chooser dialog and use it to load a different file into the JdbLabel. In this application, we use a JdbLabel called itemImageJdbLbl in TextPaneInternalFrame.java to display images of product. It's not editable by default, but you can set its iconEditable property to true if you want to experiment.

Loading text from a resource file into a data set

IntlDemo stores text in resource files - one resource file for each supported national language. Each product's Item Number and Description form a key/value pair in the resource file. In this application, we create a small data set containing Item Number, Image, and Description information. We query the PRODUCTS table in intldemo.gdb for item numbers (from the "SKU" field) and images, then fill in the corresponding descriptions from the resource file for the desired language. The descriptions are plain text in the resource file, but we want to turn them into styled text, so we have to do a little work as we load them into the data set. This is explained in the next section.

Because this is a sample application and we want to show off JdbTextPane, we allow the descriptions to be edited. But we've disabled insertion and deletion of rows and removed the Save and Refresh buttons, among others, from the toolbar because we don't have any code to write modified product descriptions back to the resource file.

This sample uses resource files from the IntlDemo application - not copies of them, but the same files, from the samples\com\samples\borland\intl\applicaton\resources directory. We include them all in the project so they'll be compiled (for .java files) or copied to the myclasses directory (for .properties files) when we make the project. If the product descriptions are blank, IntlDemo's TextRes.java resource file probably was not found. If the descriptions are in English regardless what language is chosen, TextRes.java was found, but not the language-specific properties files such as TextRes_fr.properties.

Editing text in a JdbTextPane

Once we have product descriptions loaded from the resource files, we can look at JdbTextPane's capabilities. Please keep in mind here that this is only a sample, not a real application. The scenario is that we start with plain text and store it in a data set where we have the ability to set fonts, sizes, colors, and attributes like bold and italic on blocks of text. We can do all that in this sample, but we have no provision to save the styled text when we close the application. In a more realistic case, we'd save the data set - in a server table or a JDataStore, for instance - when the application shuts down. (JDataStore is not available in all versions of JBuilder.)

First there is the matter of loading the plain text into the data set. Because we want to be able to style the text, we have to use a column whose data type is Object. The objects we put in this column will be handled by a Swing-based text component, so they should be Swing document models. Specifically, for styled text, they should be DefaultStyledDocuments. In TextPaneDesktop's internalFrameSetup() method we instantiate a DefaultStyledDocument for each product description, put the description in it, and load it into our data set.

Right-click in the text pane for a menu of text operations. In addition to standard edit operations such as cut, copy, and paste, you can set font and color properties on the selected text and undo and redo changes. dbSwing's menus for text components vary according to the component, its editability, its data-awareness, and the class of its document model.

It's the font and color properties we're mostly interested in. They apply to the selected block of text, not to the component as a whole, so you can use as many fonts and colors as you like within a product description. Background color is saved in the document but not displayed in the component. Our text pane is bound to a data set, so style properties are saved in the data set - if you navigate off a row and then back to it, they're still there.

By default, JdbTextPane's popup menu allows you to load data from a text file or a Java serialized file and to save to the same formats. If you save styled text to a text file, only plain text is written out and the styling is lost. Unlike JdbEditorPane, JdbTextPane cannot open and save RTF and HTML files. JdbTextPane is a good choice if you plan to keep styled text in a data set and display and edit it through Java, but not a good choice if you want to output to other file formats. For this reason, we have disabled the Open and Save menu commands in this application.

Creating a toolbar of text-editing actions

Operations on JdbTextPane's menu are implemented by actions, defined either in DBTextDataBinder or in JTextComponent. In Swing, actions can easily be added to a menu or toolbar. Those in DBTextDataBinder are especially easy to use because they're defined as public constants and have icons. We've put some of the most useful menu actions on our toolbar. The undo, redo, font dialog, and foreground color dialog buttons on our toolbar are from DBTextDataBinder. The others are JTextComponent actions.

Actions can't be added to TextPaneToolBar.java through JBuilder's UI Designer. However, the code is easy to write. We add an action to the toolbar, get back a button, set properties on that button, then reuse the button object for the next action. The file can still be opened in the UI Designer, but the added buttons aren't visible until we run the application.

System and local fonts are displayed in the font dialog. The toolbar buttons that set bold and italic attributes will work for system fonts only, but you can get the same effect by choosing the bold or italic version of the current font, if there is one, from the font dialog.

Other design notes

Menus: The simple menus in this application were constructed using the JBuilder Menu Designer. If you have more complex menus and will be translating your application, you might want to use dbSwing menu components such as IntlMenu and IntlMenuItem. These components offer the textWithMnemonic property, which allows a translator to see the context for a mnemonic while translating it. There are two ways to work with dbSwing menu components in the Menu Designer: In either case, you will be able to view your menu in the Menu Designer and set properties in the Inspector. You can even do a few operatons (for example, adding separators) in the Menu Designer. However, any operation that changes a menu object's class, such as turning a regular menu item into a checkable one, will create a Swing object.

Designability: Using JDesktopPane and JInternalFrame to build an MDI application reduces the amount of development we can do through JBuilder's UI Designer a little. All of our classes are designable; however, some significant code in each was written manually:

dbSwing tools: Three useful and easy-to-use dbSwing components are used throughout the dbSwing samples: