When the user selects a new file name in the combo box, the selected file becomes the active file and the dialog box displays the statistics for the new file in the editor.
This tutorial teaches these primary skills:
You can find the source code for this OpenTool sample in the samples/OpenToolsAPI/EditorStats
directory.
EditorStats.jpr
.
For more information on performing these tasks, see JBuilder OpenTools basics.
This OpenTool requires you to create two classes:
EditorStatsDialog
EditorStats
Use the Implement Interface wizard to have the class implement the java.awt.ActionListener
interface:
java.awt.event.ActionListener
interface.
Your code in the editor should look like this:
package EditorStats; import javax.swing.JDialog; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class EditorStats extends JDialog implements ActionListener { public EditorStats() { } public void actionPerformed(ActionEvent e) { //TODO: Implement this java.awt.event.ActionListener method } }
The class now extends JDialog
and implements the ActionListener
interface. Note that the Implement Interface wizard imported the required classes and interfaces.
Implementing the ActionListener
interface makes the dialog box capable of responding when the user selects a different file in the combo box. You'll see the code to add to the actionPerformed()
method in Listening for file name changes in the combo box.
import com.borland.primetime.editor.EditorManager; import com.borland.primetime.editor.EditorAction; import com.borland.primetime.editor.EditorPane; import com.borland.primetime.ide.ProjectView; import com.borland.primetime.node.Project; import com.borland.primetime.node.Node; import com.borland.primetime.viewer.TextNodeViewer; import javax.swing.text.Document; import javax.swing.text.Element;
All of the components you use must be Swing components or components derived from Swing such as JBuilder's dbSwing components.
The EditorStats OpenTool sample uses a GridBagLayout
to place the components on the dialog box. You can do the same, or you can lay out the components any way you see fit for this tutorial. If you want more information about using GridBagLayout
, see the GridBagLayout tutorial.
Using the Inspector, make these changes for these components:
jLabel1
name
property value to ProjectName.
font
property value to Dialog, bold, 14 point.
horizontalAlignment
property value to CENTER.
jComboBox1
name
property value to fileNamesCombo.
jLabel2
name
property value to TotalLines.
text
property value to Total Lines.
horizontalAlignment
property value to CENTER.
jLabel3
name
property value to TotalWords.
text
property value to Total Words.
horizontalAlignment
property value to CENTER.
jLabel4
name
property value of jLabel4 to TotalCharacters.
text
property value of TotalCharacters to Total Characters.
horizontalAlignment
property value to CENTER.
jButton1
name
property value to doneButton.
text
property value to Done.
mnemonic
property value to D.
To make the dialog box modal, first select this in the structure pane.
Then, in the Inspector, set the modal
property value to true.
openFileCount
instance variable after the component declarations but before the constructor:
// The components on the dialog JPanel panel1 = new JPanel(); JLabel ProjectName = new JLabel(); ... int openFileCount;
Create the default constructor first. If you used the Class wizard to begin the EditorStatsDialog
class, you'll already have the outline of a default constructor. Modify it so it looks like this code:
public EditorStatsDialog() { this(null, "", false); }The
owner
parameter is null, the string to display on the title bar is empty, and the modal
parameter is false.
The second constructor is far more interesting. It accomplishes these tasks:
jbInit()
, which initializes the dialog box with all the settings you made in the UI designer and Inspector.
The code that creates a modal dialog calls its superclass and then calls the jbInit()
method. jbInit()
was created as you used the UI designer and modified property values in the Inspector. If jbInit()
fails, an exception occurs. Begin creating the second constructor:
public EditorStatsDialog(Frame frame, String title, boolean modal) { super(frame, title, modal); try { jbInit(); pack(); } catch(Exception ex) { ex.printStackTrace(); }
Next, make the doneButton the default button. Although doneButton is the only button in the dialog box, making it the default lets the user quickly close the dialog box by pressing Enter. Add this code to the constructor:
doneButton.setDefaultCapable(true); getRootPane().setDefaultButton(doneButton);
To actually close the dialog box, however, you must create a close action and link it with the doneButton so the event is triggered when the user clicks the button:
ActionListener closeAction = new ActionListener() { public void actionPerformed(ActionEvent e) { EditorStatsDialog.this.setVisible(false); EditorStatsDialog.this.dispose(); } }; doneButton.addActionListener(closeAction);
The next step is to have the constructor call a method that returns all the current open files in the browser:
getOpenFiles();
You'll implement getOpenFiles()
later.
Next, you need a way to determine when the selection in the combo box changes, so this code adds a listener for events that occur in the combo box:
fileNamesCombo.addActionListener(this);
The final step in the constructor is to call the method that gathers and reports the number of characters, words, and lines are in the current open file:
getCurrentStats();
getOpenFiles()
method that asks the browser for all the files that are currently open and displays them in the combo box. getOpenFiles()
also sets the openFileCount
instance variable to the number of open files. Create the method with this code:
void getOpenFiles() { Node[] openFiles = Browser.getActiveBrowser().getOpenNodes(); for (int i=0; i < openFiles.length; i++) { fileNamesCombo.addItem(openFiles[i].getDisplayName()); if (openFiles[i].equals(Browser.getActiveBrowser().getActiveNode())) fileNamesCombo.setSelectedItem(openFiles[i].getDisplayName()); openFileCount++; } }
To enable getOpenFiles()
to access the Browser
class, add this import statement to the EditorStatsDialog
class:
import com.borland.primetime.ide.Browser;
getCurrentStats()
method, which counts the number of characters, lines, and words and displays the totals in the dialog box.
Begin the method by declaring variables to hold the totals:
void getCurrentStats() { int characterCount = 0, wordCount = 0, lineCount = 0;
To get access to the document in the editor so the dialog can count its elements, you first need to obtain the editor of the current active node in the active browser. This code does that:
Node node = Browser.getActiveBrowser().getActiveNode(); TextNodeViewer viewer = (TextNodeViewer) Browser.getActiveBrowser().getViewerOfType(node, TextNodeViewer.class); EditorPane editor = (viewer == null) ? null : viewer.getEditor();
The first line obtains the active node in the active browser. The second line returns the first TextNodeViewer
found for that active node. Finally, if a TextNodeViewer
was successfully found, the viewer's editor is retrieved.
When you have the right editor for the current node, you need access to the document itself, starting at the beginning. Here is the code to do that:
// If there is an editor if (editor != null) { Document doc = editor.getDocument(); Element rootElement = doc.getDefaultRootElement();
There are existing methods to return the number of characters and the number of lines in the document, so use them to store the character and line count in the instance variables you created earlier:
characterCount = doc.getLength(); lineCount = rootElement.getElementCount();
Getting the number of words in the document requires more effort. Add the following code to your getCurrentStats()
method implementation:
int elementCount = rootElement.getElementCount(); for (int i = 0; i < elementCount; i++) { try { // For each line, count the words. // We assume words are bordered by whitespace Element element = rootElement.getElement(i); int startOffset = element.getStartOffset(); int endOffset = element.getEndOffset(); String text = doc.getText(startOffset, endOffset - startOffset); char[] textArray = text.toCharArray(); boolean inWhiteSpace = true; for (int j = 0; j < textArray.length - 1; j++) { boolean isWhiteSpace = Character.isWhitespace(textArray[j]); if (isWhiteSpace != inWhiteSpace) { if (inWhiteSpace == true) wordCount++; inWhiteSpace = isWhiteSpace; } } } catch(Exception b) { break; } } }
The code contains two for loops, one nested within the other because the words are counted one line at a time. The logic assumes that a word is a series of characters surrounded by whitespace. So the first for loop begins by establishing the beginning and the end of a line and then retrieving the text of the line. The second for loop then takes over and examines each character of that text and increments the word count each time it encounters another whitespace character. Lines are counted until the end of the document is reached.
Now that you have the number of characters, lines, and words in the document, all you need to do is to display them in the dialog box using the JLabel
fields. The first field, which you named ProjectName
, displays the name of the current project. Here is the logic to retrieve the current project name and display it in the ProjectName
field:
ProjectView pv = Browser.getActiveBrowser().getProjectView(); Project project = pv.getActiveProject(); ProjectName.setText("Current project: " + project.getDisplayName());
To complete the getCurrentStats()
method, call the setText()
methods of the TotalLines
, TotalWords
, and TotalCharacters
fields:
TotalLines.setText("Total Lines: " + lineCount); TotalWords.setText("Total Words: " + wordCount); TotalCharacters.setText("Total Characters: " + characterCount); }
actionPerformed()
method that is required to implement the ActiveListener
interface. A skeleton of the actionPerformed()
method was added when you used the Implement Interface wizard. Now is the time to fill it in. Because you're interested in events that occur only in the combo box, the code begins by screening for just those events:
public void actionPerformed(ActionEvent e) { Object source = e.getSource(); if (source == fileNamesCombo) { try {
Then the method attempts to find the open file in the browser whose name matches the user's select in the combo box. If it's found, it makes that file the active file and updates the statistics on it.
// Try to find the open file in the browser whose name // matches the current selection in the ComboBox, and if // it is found, make that file the active file in the // Browser and update the statistics. Node[] openFiles = Browser.getActiveBrowser().getOpenNodes(); for (int i=0; i < openFiles.length; i++) { if (openFiles[i].getDisplayName().equals(fileNamesCombo.getSelectedItem())) { Browser.getActiveBrowser().setActiveNode(openFiles[i], true); getCurrentStats(); break; } } } catch(Exception ex) { } } }
EditorStats
class is the class loaded by JBuilder that adds a new menu item to the Tools menu. When the user chooses the menu item, the action defined in EditorStats
is called, displaying the EditorStatsDialog
dialog box.
To begin creating the EditorStats
class, use the Class wizard:
package EditorStats; public class EditorStats { public EditorStats() { }
The EditorStats
class accomplishes two primary tasks:
To enable the action to access all the necessary classes, add the following import statements to the class:
import com.borland.primetime.ide.Browser; import com.borland.primetime.ide.BrowserAction; import com.borland.primetime.PrimeTime; import java.awt.Rectangle; import java.awt.Dimension; import java.awt.Toolkit;
initOpenTool()
method that is called as JBuilder is loading. The initOpenTool()
method of EditorStats
adds an Action
to the JBuilder Tools menu group:
public static void initOpenTool(byte majorVersion, byte minorVersion) { if (majorVersion == PrimeTime.CURRENT_MAJOR_VERSION) JBuilderMenu.GROUP_Tools.add(ACTION_StatsDialog); }
The JBuilderMenu
class defines the menu groups that make up the menu system in the JBuilder IDE. The initOpenTool(
) method adds a single action to the GROUP_Tools
group, which is the Tools menu. Because the action isn't added to one of the subgroups, such as GROUP_ToolsOptions
, the new menu item will appear at the top of the Tools menu. If you did add an action to a subgroup, the menu item would appear within specified subgroup on the menu.
If your OpenTool requires several new menu items, you might want to create a new menu group, add actions for each menu item, and then add the new group to an existing menu.
For more information about adding and removing menu items, see Adding and removing individual menu Actions or ActionGroups.
You can also add a new menu to the JBuilder menu bar. For information on how to do that, see Adding and removing menu bar groups.
To provide access to the JBuilderMenu
class, add this import statement to the EditorStats
class:
import com.borland.jbuilder.JBuilderMenu;
ACTION_StatsDialog
is a new BrowserAction
that creates the EditorStatsDialog
dialog box and displays it, centering it onscreen:
public static /*final*/ BrowserAction ACTION_StatsDialog = // A new action with short menu string, mnemonic, and long menu string new BrowserAction("Editor Statistics", 'E', "Displays statistics about an editor file") { // The function called when the menu is selected public void actionPerformed(Browser browser) { // Create the modal dialog box and center it on the screen EditorStatsDialog dialog = new EditorStatsDialog(browser, "Editor Statistics", true); Rectangle screenBounds = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); Dimension dialogSize = dialog.getSize(); dialog.setLocation( screenBounds.x + (screenBounds.width - dialogSize.width) / 2, screenBounds.y + (screenBounds.height - dialogSize.height) / 2); dialog.setVisible(true); } };
A new BrowserAction
requires three parameters, the string that appears on the menu, a mnemonic for the accelerator key for the menu option, and a long menu string.
The definition of the dialog
variable calls the constructor of EditorStatsDialog
and passes along three parameters: the owner of the dialog (the browser), the title bar text (Editor Statistics), and a boolean value that indicates whether the dialog is modal.
OpenTools-UI: EditorStats.EditorStats
EditorStats\classes.opentools
.
bin
directory. Add EditorStats\classes to the Java classpath.
For more detailed information on these final steps, see OpenTools basics.