/** *######################################################################### * * A component of the Gatherer application, part of the Greenstone digital * library suite from the New Zealand Digital Library Project at the * University of Waikato, New Zealand. * * Copyright (C) 1999 New Zealand Digital Library Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *######################################################################## */ package org.greenstone.gatherer.gui; import org.greenstone.gatherer.Configuration; import org.greenstone.gatherer.Dictionary; import org.fife.ui.rsyntaxtextarea.*; import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.undo.*; /** * A textarea with the line number next to each line of the text. * It provides undo and redo buttons that are already hooked up to listeners * You can add these buttons to a JComponent and they will behave just like the * existing RSyntaxArea's Ctrl-Z and Ctrl-Y shortcuts and undo/redo popup menus. */ public class NumberedJTextArea extends RSyntaxTextArea /* JTextArea */ implements UndoableEditListener, ActionListener { String id = null; public final GLIButton undoButton; public final GLIButton redoButton; public NumberedJTextArea(String tooltip) { this("", tooltip); } public NumberedJTextArea (String id, String tooltip) { super(); this.id = id; // maybe use this textarea's id field to customise the undo/redo button tooltips? undoButton = new GLIButton(Dictionary.get("General.Undo"), Dictionary.get("General.Undo_Tooltip")); undoButton.setEnabled(false); redoButton = new GLIButton(Dictionary.get("General.Redo"), Dictionary.get("General.Redo_Tooltip")); redoButton.setEnabled(false); undoButton.addActionListener(this); redoButton.addActionListener(this); // next, initialise this RSyntaxTextArea: // Adding the UndoableEditListener has to come after instantiation of the undo and // redo buttons, since the listener expects these buttons to already exist this.getDocument().addUndoableEditListener(this); /* Fields specific to RSyntaxQuery inherited class */ setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML); setBracketMatchingEnabled(true); setAnimateBracketMatching(true); setAntiAliasingEnabled(true); setAutoIndentEnabled(true); setPaintMarkOccurrencesBorder(false); /* Standard fields to JTextArea */ setOpaque(false); setBackground(Configuration.getColor("coloring.editable_background", false)); setCaretPosition(0); setLineWrap(true); setRows(11); setWrapStyleWord(false); setToolTipText(tooltip); } public void paintComponent(Graphics g) { Insets insets = getInsets(); Rectangle rectangle = g.getClipBounds(); g.setColor(Color.white); g.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); super.paintComponent(g); if (rectangle.x < insets.left) { FontMetrics font_metrics = g.getFontMetrics(); int font_height = font_metrics.getHeight(); int y = font_metrics.getAscent() + insets.top; int line_number_start_point = ((rectangle.y + insets.top) / font_height) + 1; if (y < rectangle.y) { y = line_number_start_point * font_height - (font_height - font_metrics.getAscent()); } int y_axis_end_point = y + rectangle.height + font_height; int x_axis_start_point = insets.left; x_axis_start_point -= getFontMetrics(getFont()).stringWidth(Math.max(getRows(), getLineCount() + 1) + " "); if (!this.getText().trim().equals("")) { g.setColor(Color.DARK_GRAY); } else { g.setColor(Color.white); } int length = ("" + Math.max(getRows(), getLineCount() + 1)).length(); while (y < y_axis_end_point) { g.drawString(line_number_start_point + " ", x_axis_start_point, y); y += font_height; line_number_start_point++; } } } public Insets getInsets() { Insets insets = super.getInsets(new Insets(0, 0, 0, 0)); insets.left += getFontMetrics(getFont()).stringWidth(Math.max(getRows(), getLineCount() + 1) + " "); return insets; } // Overriding, to ensure that even if ctrl-z was pressed to undo something, // the undo and redo buttons are in sync with that. public void undoLastAction() { super.undoLastAction(); redoButton.setEnabled(true); if (!this.canUndo()) { undoButton.setEnabled(false); } else { undoButton.setEnabled(true); } } // Overriding public void redoLastAction() { super.redoLastAction(); undoButton.setEnabled(true); if (!this.canRedo()) { redoButton.setEnabled(false); } else { redoButton.setEnabled(true); } } // Overriding public void discardAllEdits() { // Buttons have to be disabled before discardAllEdits(). If done in reverse order, buttons get re-enabled undoButton.setEnabled(false); redoButton.setEnabled(false); super.discardAllEdits(); } // The RSyntaxTextarea class already provides undo and redo functionality hooked up to their // usual keyboard shortcuts and to rightclick popup menus. The actionListener below merely // reuses this behaviour and attaches it to the undoButton and redoButton. public void actionPerformed(ActionEvent event) { // actionPerformed() not defined in any of the superclasses RTextArea, RTextAreaBase, JTextArea //super.actionPerformed(event); // compile error if(event.getSource() == undoButton) { try { // calls canUndo() and undoLastAction() defined by RTextArea // internally calls its undoManager's canUndo() and undo() to handle compoundEdits if (this.canUndo()) { this.undoLastAction(); } if (!this.canUndo()) { undoButton.setEnabled(false); } else { undoButton.setEnabled(true); } } catch (Exception e) { System.err.println("Exception trying to undo: " + e.getMessage()); e.printStackTrace(); } } else if (event.getSource() == redoButton) { try { // calls canRedo() and redoLastAction() defined by RTextArea // internally calls its undoManager's canRedo() and redo() to handle compoundEdits if (this.canRedo()) { this.redoLastAction(); } if (!this.canRedo()) { redoButton.setEnabled(false); } else { redoButton.setEnabled(true); } } catch (Exception e) { System.err.println("Exception trying to redo: " + e.getMessage()); e.printStackTrace(); } } } // defined in interface UndoableEditListener public void undoableEditHappened(UndoableEditEvent evt) { undoButton.setEnabled(true); redoButton.setEnabled(false); } }