KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > bluej > editor > moe > MoeEditor


1 // Copyright (c) 2000, 2005 BlueJ Group, Deakin University
2
//
3
// This software is made available under the terms of the "MIT License"
4
// A copy of this license is included with this source distribution
5
// in "license.txt" and is also available at:
6
// http://www.opensource.org/licenses/mit-license.html
7
// Any queries should be directed to Michael Kolling mik@bluej.org
8

9 package bluej.editor.moe;
10
11 import bluej.editor.*;
12 import java.awt.*;
13 import java.awt.event.*;
14 import java.awt.print.PageFormat JavaDoc;
15 import java.awt.print.PrinterJob JavaDoc;
16 import java.io.*;
17 import java.net.URL JavaDoc;
18 import java.util.*;
19 import java.util.List JavaDoc;
20
21 import javax.swing.*;
22 import javax.swing.border.EmptyBorder JavaDoc;
23 import javax.swing.event.*;
24 import javax.swing.text.*;
25 import javax.swing.text.html.HTMLDocument JavaDoc;
26 import javax.swing.text.html.HTMLEditorKit JavaDoc;
27 import javax.swing.text.html.HTMLFrameHyperlinkEvent JavaDoc;
28 import javax.swing.undo.UndoManager JavaDoc;
29
30 import org.syntax.jedit.tokenmarker.JavaTokenMarker;
31
32 import bluej.BlueJEvent;
33 import bluej.BlueJEventListener;
34 import bluej.Config;
35 import bluej.editor.EditorWatcher;
36 import bluej.pkgmgr.PkgMgrFrame;
37 import bluej.prefmgr.PrefMgr;
38 import bluej.utility.Debug;
39 import bluej.utility.DialogManager;
40 import bluej.utility.FileUtility;
41 import bluej.utility.Utility;
42
43 /**
44  * Moe is the editor of the BlueJ environment. This class is the main class of
45  * this editor and implements the top-level functionality.
46  *
47  * MoeEditor implements the Editor interface, which defines the interface to the
48  * rest of the BlueJ system.
49  *
50  * @author Michael Kolling
51  * @author Bruce Quig
52  * @author Damiano Bolla
53  */

54
55 public final class MoeEditor extends JFrame
56     implements bluej.editor.Editor, BlueJEventListener, HyperlinkListener, DocumentListener
57 {
58     // -------- CONSTANTS --------
59

60     // version number
61
final static int version = 200;
62     final static String JavaDoc versionString = "2.0";
63
64     // colours
65
final static Color textColor = new Color(0, 0, 0); // normal text
66
final static Color textBgColor = Config.getItemColour("colour.text.bg"); // background
67
final static Color cursorColor = new Color(255, 0, 100); // cursor
68

69     final static Color frameBgColor = new Color(196, 196, 196);
70     final static Color infoColor = new Color(240, 240, 240);
71     final static Color lightGrey = new Color(224, 224, 224);
72     final static Color selectionColour = Config.getSelectionColour();
73     final static Color titleCol = Config.getItemColour("colour.text.fg");
74     final static Color envOpColour = Config.getItemColour("colour.menu.environOp");
75
76     // Icons
77
final static Image iconImage = Config.getImageAsIcon("image.icon.editor").getImage();
78
79     // Fonts
80
public static int printFontSize = Config.getDefaultPropInteger("bluej.fontsize.printText", 10);
81     public static Font printFont = new Font("Monospaced", Font.PLAIN, printFontSize);
82
83     // Strings
84
String JavaDoc implementationString = Config.getString("editor.implementationLabel");
85     String JavaDoc interfaceString = Config.getString("editor.interfaceLabel");
86
87     // suffixes for resources
88
final static String JavaDoc LabelSuffix = "Label";
89     final static String JavaDoc ActionSuffix = "Action";
90     final static String JavaDoc TooltipSuffix = "Tooltip";
91     final static String JavaDoc AcceleratorSuffix = "Accelerator";
92
93     // file suffixes
94
final static String JavaDoc CRASHFILE_SUFFIX = "#";
95     final static String JavaDoc BACKUP_SUFFIX = "~";
96
97     final static String JavaDoc spaces = " ";
98
99     final static String JavaDoc COMPILED = "compiled";
100
101     // PageFormat object for printing page format
102
private static PageFormat JavaDoc pageFormat;
103
104     private static boolean matchBrackets = false;
105
106     // -------- INSTANCE VARIABLES --------
107

108     private EditorWatcher watcher;
109     private Properties resources;
110
111     private AbstractDocument document;
112     private MoeSyntaxDocument sourceDocument;
113     private HTMLDocument JavaDoc htmlDocument;
114
115     private MoeActions actions;
116     public UndoManager JavaDoc undoManager;
117     public List JavaDoc undoComponents; // components bound to "undo"
118
public List JavaDoc redoComponents; // components bound to "redo"
119

120     JEditorPane currentTextPane; // text component currently dislayed
121
private JEditorPane sourcePane; // the component holding the source text
122

123     private JEditorPane htmlPane; // the component holding the javadoc html
124
private MoeCaret moeCaret;
125
126     private Info info; // the info number label
127
private JPanel statusArea; // the status area
128
private StatusLabel saveState; // the status label
129
private JComboBox interfaceToggle;
130     private GoToLineDialog goToLineDialog;
131
132     private JScrollPane scrollPane;
133     private JComponent toolbar; // The toolbar
134

135     private String JavaDoc filename; // name of file or null
136
private long lastModified; // time of last modification of file
137
private String JavaDoc windowTitle; // title of editor window
138
private String JavaDoc docFilename; // path to javadoc html file
139

140     private boolean sourceIsCode; // true if current buffer is code
141
private boolean viewingHTML;
142
143     private int currentStepPos; // position of step mark (or -1)
144
private boolean mayHaveBreakpoints; // true if there were BP here
145
private boolean ignoreChanges = false;
146     private boolean tabsAreExpanded = false;
147
148     private MoePrinter printer;
149
150     private TextInsertNotifier doTextInsert = new TextInsertNotifier();
151
152     private ClassLoader JavaDoc projectClassLoader;
153     private HashMap propertyMap = new HashMap();
154
155     // =========================== NESTED CLASSES ===========================
156

157     // inner class for listening for undoable edits in text
158

159     /**
160      * Class that listens for edits and plaves them onto the undo stack
161      */

162     private class MoeUndoableEditListener
163         implements UndoableEditListener
164     {
165         public void undoableEditHappened(UndoableEditEvent e)
166         {
167             undoManager.addEdit(e.getEdit());
168             updateUndoControls();
169             updateRedoControls();
170         }
171     }
172
173     /**
174      * Constructor. Title may be null
175      */

176     public MoeEditor(String JavaDoc title, boolean isCode, EditorWatcher watcher, boolean showToolbar,
177                      boolean showLineNum, Properties resources, ClassLoader JavaDoc aProjectClassLoader)
178     {
179         super("Moe");
180         this.watcher = watcher;
181         this.resources = resources;
182         filename = null;
183         windowTitle = title;
184         sourceIsCode = isCode;
185         viewingHTML = false;
186         currentStepPos = -1;
187         mayHaveBreakpoints = false;
188         matchBrackets = PrefMgr.getFlag(PrefMgr.MATCH_BRACKETS);
189         projectClassLoader = aProjectClassLoader;
190         undoManager = new UndoManager JavaDoc();
191         undoComponents = new ArrayList(1);
192         redoComponents = new ArrayList(1);
193
194         initWindow();
195     }
196
197     // --------------------------------------------------------------------
198

199     /**
200      * Update the state of controls bound to "undo".
201      */

202     public void updateUndoControls()
203     {
204         boolean canUndo = undoManager.canUndo();
205         Iterator i = undoComponents.iterator();
206         while (i.hasNext()) {
207             ((JComponent) i.next()).setEnabled(canUndo);
208         }
209     }
210
211     /**
212      * Update the state of controls bound to "redo".
213      */

214     public void updateRedoControls()
215     {
216         boolean canRedo = undoManager.canRedo();
217         Iterator i = redoComponents.iterator();
218         while (i.hasNext()) {
219             ((JComponent) i.next()).setEnabled(canRedo);
220         }
221     }
222
223     /**
224      * Returns the projectClassLoader. Can return null if no classloader is
225      * available.
226      *
227      * @return The projectClassLoader value
228      */

229     public ClassLoader JavaDoc getProjectClassLoader()
230     {
231         return projectClassLoader;
232     }
233
234     /**
235      * Load the file "filename" and show the editor window.
236      */

237     public boolean showFile(String JavaDoc filename, boolean compiled, // inherited from Editor, redefined
238
String JavaDoc docFilename, Rectangle bounds)
239     {
240         this.filename = filename;
241         this.docFilename = docFilename;
242
243         if (bounds != null) {
244             setBounds(bounds);
245         }
246
247         boolean loaded = false;
248         boolean readError = false;
249
250         if (filename != null) {
251
252             try {
253                 // check for crash file
254
String JavaDoc crashFilename = filename + CRASHFILE_SUFFIX;
255                 String JavaDoc backupFilename = crashFilename + "backup";
256                 File crashFile = new File(crashFilename);
257                 if (crashFile.exists()) {
258                     File backupFile = new File(backupFilename);
259                     backupFile.delete();
260                     crashFile.renameTo(backupFile);
261                     DialogManager.showMessage(this, "editor-crashed");
262                 }
263
264                 FileReader reader = new FileReader(filename);
265                 sourcePane.read(reader, null);
266                 reader.close();
267                 File file = new File(filename);
268                 lastModified = file.lastModified();
269
270                 sourceDocument = (MoeSyntaxDocument) sourcePane.getDocument();
271
272                 // set TokenMarker for syntax highlighting if desired
273
checkSyntaxStatus();
274
275                 sourceDocument.addDocumentListener(this);
276                 sourceDocument.addUndoableEditListener(new MoeUndoableEditListener());
277                 document = sourceDocument;
278                 loaded = true;
279             }
280             catch (FileNotFoundException ex) {
281                 clear();
282             }
283             catch (IOException ex) {
284                 readError = true;
285             }
286         }
287
288         if (!loaded) // should exist, but didn't
289
return false;
290
291         // if (loaded) ## NYI
292
// if (newFile.canWrite()) { // have write permission
293
// save_state = Saved;
294
// statusLabel.setText("saved");
295
// }
296
// else {
297
// save_state = ReadOnly;
298
// statusLabel.setText("read only");
299
// }
300
// else
301
// save_state = Saved;
302

303         if (loaded)
304             info.message(Config.getString("editor.info.version") + " " + versionString);
305         else if (readError)
306             info.warning(Config.getString("editor.info.readingProblem"),
307                          Config.getString("editor.info.regularFile"));
308         else
309             info.message(Config.getString("editor.info.version" + versionString),
310                          Config.getString("editor.info.newFile"));
311
312         setWindowTitle();
313         sourcePane.setFont(PrefMgr.getStandardEditorFont());
314         sourcePane.setSelectionColor(selectionColour);
315
316         setCompileStatus(compiled);
317
318         return true;
319     }
320
321     /**
322      * Reload the editor content from the associated file, discarding unsaved
323      * edits.
324      */

325     public void reloadFile() // inherited from Editor, redefined
326
{
327         doReload();
328     }
329
330     /**
331      * Wipe out contents of the editor.
332      */

333     public void clear() // inherited from Editor, redefined
334
{
335         ignoreChanges = true;
336         sourcePane.setText("");
337         ignoreChanges = false;
338     }
339
340     /**
341      * Insert a string into the buffer. The editor is not immediately
342      * redisplayed. This function is typically used in a sequence "clear;
343      * [insertText]*; setVisible(true)". If the selection is on, it is replaced
344      * by the new text.
345      *
346      * @param text the text to be inserted
347      * @param caretBack move the caret to the beginning of the inserted text
348      */

349     public void insertText(String JavaDoc text, boolean caretBack) // inherited from Editor, redefined
350
{
351         sourcePane.replaceSelection(text);
352         if (caretBack) {
353             sourcePane.setCaretPosition(sourcePane.getCaretPosition() - text.length());
354         }
355     }
356
357     /**
358      * Show the editor window. This includes whatever is necessary of the
359      * following: make visible, de-iconify, bring to front of window stack.
360      *
361      * @param vis The new visible value
362      */

363     public void setVisible(boolean vis) // inherited from Editor, redefined
364
{
365         if (vis) {
366             currentTextPane.setFont(PrefMgr.getStandardEditorFont());
367             checkSyntaxStatus();
368             checkBracketStatus();
369             setState(Frame.NORMAL); // de-iconify
370
toFront(); // window to front
371
}
372         Utility.bringToFront();
373         super.setVisible(vis); // show the window
374
}
375
376     /**
377      * Refresh the editor window.
378      */

379     public void refresh() // inherited from Editor, redefined
380
{
381         currentTextPane.setFont(PrefMgr.getStandardEditorFont());
382         checkBracketStatus();
383         checkSyntaxStatus();
384         currentTextPane.repaint();
385     }
386
387     /**
388      * True is the editor is on screen.
389      *
390      * @return The showing value
391      */

392     public boolean isShowing() // inherited from Editor, redefined
393
{
394         if (isVisible() != super.isShowing()) {
395             Debug.message("isVisible is not isShowing!");
396         }
397         return super.isShowing();
398     }
399
400     /**
401      * Save the buffer to disk under current filename. This is often called from
402      * the outside - just in case. Save only if really necessary, otherwise we
403      * save much too often. PRE: filename != null
404      */

405     public void save() // inherited from Editor, redefined
406
throws IOException
407     {
408         IOException failureException = null;
409         if (saveState.isChanged()) {
410             BufferedWriter writer = null;
411             try {
412                 // The crash file is used during writing and will remain in
413
// case of a crash during the write operation. The backup
414
// file always contains the last version.
415
String JavaDoc crashFilename = filename + CRASHFILE_SUFFIX;
416                 String JavaDoc backupFilename = filename + BACKUP_SUFFIX;
417
418                 // make a backup to the crash file
419
FileUtility.copyFile(filename, crashFilename);
420
421                 writer = new BufferedWriter(new FileWriter(filename));
422                 sourcePane.write(writer);
423                 writer.close();
424                 setSaved();
425                 File file = new File(filename);
426                 lastModified = file.lastModified();
427
428                 if (PrefMgr.getFlag(PrefMgr.MAKE_BACKUP)) {
429                     // if all went well, rename the crash file as a normal
430
// backup
431
File crashFile = new File(crashFilename);
432                     File backupFile = new File(backupFilename);
433                     crashFile.renameTo(backupFile);
434                 }
435                 else {
436                     File crashFile = new File(crashFilename);
437                     crashFile.delete();
438                 }
439             }
440             catch (IOException ex) {
441                 failureException = ex;
442                 info.warning(Config.getString("editor.info.errorSaving") + " - " + ex.getLocalizedMessage());
443             }
444             finally {
445                 try {
446                    if(writer != null)
447                       writer.close();
448                 }
449                 catch (IOException ex) {
450                     failureException = ex;
451                 }
452             }
453         }
454         
455         // If an error occurred, set a message in the editor status bar, and
456
// re-throw the exception.
457
if (failureException != null) {
458             info.warning(Config.getString("editor.info.errorSaving")
459                     + " - " + failureException.getLocalizedMessage());
460             throw failureException;
461         }
462     }
463
464     /**
465      * The editor wants to close. Do this through the EditorManager so that we
466      * can be removed from the list of open editors.
467      */

468     public void close() // inherited from Editor, redefined
469
{
470         try {
471             save();
472         }
473         catch (IOException ioe) {}
474         // temporary - should really be done by watcher from outside
475
doClose();
476     }
477
478     /**
479      * Display a message (used for compile/runtime errors). An editor must
480      * support at least two lines of message text, so the message can contain a
481      * newline character.
482      *
483      * @param message the message to be displayed
484      * @param lineNumber The line to highlight
485      * @param column the column to move the cursor to
486      * @param beep if true, do a system beep
487      * @param setStepMark if true, set step mark (for single stepping)
488      * @param help name of help group (may be null)
489      */

490     public void displayMessage(String JavaDoc message, int lineNumber, int column, boolean beep,
491                                boolean setStepMark, String JavaDoc help) // inherited from Editor
492
{
493         switchToSourceView();
494
495         Element line = getLine(lineNumber);
496         int pos = line.getStartOffset();
497
498         if (setStepMark) {
499             setStepMark(pos);
500         }
501
502         // highlight the line
503

504         currentTextPane.setCaretPosition(pos);
505         currentTextPane.moveCaretPosition(line.getEndOffset() - 1);
506         moeCaret.setPersistentHighlight();
507         // w/o line break
508

509         // display the message
510

511         if (beep)
512             info.warning(message);
513         else
514             info.message(message);
515
516         if (help != null)
517             info.setHelp(help);
518     }
519
520     /**
521      * Set the selection of the editor to be a len characters on the line
522      * lineNumber, starting with column columnNumber
523      *
524      * @param lineNumber the line to select characters on
525      * @param columnNumber the column to start selection at (1st column is 1 - not 0)
526      * @param len the number of characters to select
527      */

528     public void setSelection(int lineNumber, int columnNumber, int len)
529     {
530         Element line = getLine(lineNumber);
531
532         currentTextPane.select(line.getStartOffset() + columnNumber - 1,
533                                line.getStartOffset() + columnNumber + len - 1);
534     }
535
536     /**
537      * Select a specified area of text.
538      *
539      * @param lineNumber1 The new selection value
540      * @param columnNumber1 The new selection value
541      * @param lineNumber2 The new selection value
542      * @param columnNumber2 The new selection value
543      */

544     public void setSelection(int lineNumber1, int columnNumber1, int lineNumber2, int columnNumber2)
545     {
546         /*
547          * if (lineNumber2 < lineNumber1) return; if (lineNumber2 == lineNumber1 &&
548          * (columnNumber2 < columnNumber1)) return;
549          */

550         Element line1 = getLine(lineNumber1);
551         Element line2 = getLine(lineNumber2);
552
553         currentTextPane.select(line1.getStartOffset() + columnNumber1 - 1, line2.getStartOffset() + columnNumber2 - 1);
554     }
555
556     /**
557      * Get the text currently selected.
558      *
559      * @return The selected text.
560      */

561     public String JavaDoc getSelectedText()
562     {
563         return currentTextPane.getSelectedText();
564     }
565
566     /**
567      * Remove the step mark (the mark that shows the current line when
568      * single-stepping through code). If it is not currently displayed, do
569      * nothing.
570      */

571     public void removeStepMark() // inherited from Editor
572
{
573         if (currentStepPos != -1) {
574             SimpleAttributeSet a = new SimpleAttributeSet();
575             a.addAttribute(MoeSyntaxView.STEPMARK, Boolean.FALSE);
576             sourceDocument.setParagraphAttributes(currentStepPos, a);
577             currentStepPos = -1;
578             // remove highlight as well
579
sourcePane.setCaretPosition(sourcePane.getCaretPosition());
580             // force an update of UI
581
repaint();
582         }
583     }
584
585     /**
586      * Change class name.
587      *
588      * @param title new window title
589      * @param filename new file name
590      */

591     public void changeName(String JavaDoc title, String JavaDoc filename) // inherited from Editor
592
{
593         this.filename = filename;
594         // error ## - need to add full path
595
windowTitle = title;
596         setWindowTitle();
597     }
598
599     /**
600      * Set the "compiled" status
601      *
602      * @param compiled True if the class has been compiled.
603      */

604     public void setCompiled(boolean compiled)
605     {
606         setCompileStatus(compiled);
607         if (compiled) {
608             info.message(Config.getString("editor.info.compiled"));
609         }
610     }
611
612     /**
613      * Remove all breakpoints in this editor.
614      */

615     public void removeBreakpoints()
616     {
617         // This may be a callback in response to a modification event.
618
// If we try to remove breakpoints during the modification notification,
619
// AbstractDocument throws an exception.
620
EventQueue.invokeLater(new Runnable JavaDoc() {
621             public void run()
622             {
623                 clearAllBreakpoints();
624             }
625         });
626     }
627
628     /**
629      * Determine whether this buffer has been modified.
630      *
631      * @return a boolean indicating whether the file is modified
632      */

633     public boolean isModified() // inherited from Editor
634
{
635         return (saveState.isChanged());
636     }
637
638     /**
639      * Set this editor to read-only.
640      *
641      * @param readOnly The new readOnly value
642      */

643     public void setReadOnly(boolean readOnly)
644     {
645         if (readOnly) {
646             saveState.setState(StatusLabel.READONLY);
647             undoManager.discardAllEdits();
648             updateUndoControls();
649             updateRedoControls();
650         }
651         currentTextPane.setEditable(!readOnly);
652     }
653
654     /**
655      * Returns if this editor is read-only. Accessor for the setReadOnly
656      * property.
657      *
658      * @return a boolean indicating whether the editor is read-only.
659      */

660     public boolean isReadOnly()
661     {
662         return !currentTextPane.isEditable();
663     }
664
665     /**
666      * Set this editor to display either the interface or the source code of
667      * this class
668      *
669      * @param interfaceStatus If true, display class interface, otherwise source.
670      */

671     public void showInterface(boolean interfaceStatus)
672     {
673         interfaceToggle.setSelectedIndex(interfaceStatus ? 1 : 0);
674     }
675
676     /**
677      * Tell whether the editor is currently displaying the interface or the
678      * source of the class.
679      *
680      * @return True, if interface is currently shown, false otherwise.
681      */

682     public boolean isShowingInterface()
683     {
684         return viewingHTML;
685     }
686
687     /**
688      * Check whether the source file has changed on disk. If it has, reload.
689      */

690     private void checkForChangeOnDisk()
691     {
692         File file = new File(filename);
693         long modified = file.lastModified();
694         if(modified != lastModified) {
695             if (saveState.isChanged()) {
696                 int answer = DialogManager.askQuestion(this, "changed-on-disk");
697                 if (answer == 0)
698                     doReload();
699                 else
700                     lastModified = modified; // don't ask again for this change
701
}
702             else {
703                 doReload();
704             }
705         }
706     }
707
708     /**
709      * Returns the current caret location within the edited text.
710      *
711      * @return An object describing the current caret location.
712      */

713     public LineColumn getCaretLocation()
714     {
715         int caretOffset = currentTextPane.getCaretPosition();
716         return getLineColumnFromOffset(caretOffset);
717     }
718
719     /**
720      * Returns the LineColumn object from the given offset in the text.
721      *
722      * @param offset The number of characters from the beginning of text (startng
723      * from zero)
724      * @return the LineColumn object or null if the offset points outside the
725      * text.
726      */

727     public LineColumn getLineColumnFromOffset(int offset)
728     {
729         int lineNumber = document.getDefaultRootElement().getElementIndex(offset);
730
731         if (lineNumber < 0) {
732             return null;
733         }
734
735         Element lineElement = getLineAt(offset);
736         int column = offset - lineElement.getStartOffset();
737
738         if (column < 0) {
739             return null;
740         }
741
742         return new LineColumn(lineNumber, column);
743     }
744
745     /**
746      * Sets the current Caret location within the edited text.
747      *
748      * @param location The location in the text to set the Caret to.
749      * @throws IllegalArgumentException
750      * if the specified TextLocation represents a position which
751      * does not exist in the text.
752      */

753     public void setCaretLocation(LineColumn location)
754     {
755         currentTextPane.setCaretPosition(getOffsetFromLineColumn(location));
756     }
757
758     /**
759      * Returns the location where the current selection begins.
760      *
761      * @return the current beginning of the selection or null if no text is
762      * selected.
763      */

764     public LineColumn getSelectionBegin()
765     {
766         Caret aCaret = currentTextPane.getCaret();
767
768         // If the dot is == as the mark then there is no selection.
769
if (aCaret.getDot() == aCaret.getMark()) {
770             return null;
771         }
772
773         int beginOffset = Math.min(aCaret.getDot(), aCaret.getMark());
774
775         return getLineColumnFromOffset(beginOffset);
776     }
777
778     /**
779      * Returns the location where the current selection ends.
780      *
781      * @return the current end of the selection or null if no text is selected.
782      */

783     public LineColumn getSelectionEnd()
784     {
785         Caret aCaret = currentTextPane.getCaret();
786
787         // If the dot is == as the mark then there is no selection.
788
if (aCaret.getDot() == aCaret.getMark()) {
789             return null;
790         }
791
792         int endOffset = Math.max(aCaret.getDot(), aCaret.getMark());
793
794         return getLineColumnFromOffset(endOffset);
795     }
796
797     /**
798      * Returns the text which lies between the two LineColumn.
799      *
800      * @param begin The beginning of the text to get
801      * @param end The end of the text to get
802      * @return The text between the 'begin' and 'end' positions.
803      * @throws IllegalArgumentException
804      * if either of the specified TextLocations represent a position
805      * which does not exist in the text.
806      */

807     public String JavaDoc getText(LineColumn begin, LineColumn end)
808     {
809         int first = getOffsetFromLineColumn(begin);
810         int last = getOffsetFromLineColumn(end);
811         int beginOffset = Math.min(first, last);
812         int endOffset = Math.max(first, last);
813
814         try {
815             return document.getText(beginOffset, endOffset - beginOffset);
816         }
817         catch (BadLocationException exc) {
818             throw new IllegalArgumentException JavaDoc(exc.getMessage());
819         }
820     }
821
822     /**
823      * Request to the editor to replace the text between 'begin' and 'end' with
824      * the given newText. If begin and end point to the same location, the text
825      * is inserted.
826      *
827      * @param begin The start position of text to replace
828      * @param end The end position of text to replace
829      * @param newText The text to insert
830      * @throws IllegalArgumentException
831      * if either of the specified LineColumn represent a position
832      * which does not exist in the text.
833      * @throws BadLocationException
834      * if internally the text points outside a location in the text.
835      */

836     public void setText(LineColumn begin, LineColumn end, String JavaDoc newText)
837         throws BadLocationException
838     {
839         int start = getOffsetFromLineColumn(begin);
840         int finish = getOffsetFromLineColumn(end);
841
842         int beginOffset = Math.min(start, finish);
843         int endOffset = Math.max(start, finish);
844
845         if (beginOffset != endOffset) {
846             document.remove(beginOffset, endOffset - beginOffset);
847         }
848
849         document.insertString(beginOffset, newText, null);
850     }
851
852     /**
853      * Request to the editor to mark the text between begin and end as selected.
854      *
855      * @param begin The start position of the selection
856      * @param end The end position of the selection
857      * @throws IllegalArgumentException
858      * if either of the specified TextLocations represent a position
859      * which does not exist in the text.
860      */

861     public void setSelection(LineColumn begin, LineColumn end)
862     {
863         int start = getOffsetFromLineColumn(begin);
864         int finish = getOffsetFromLineColumn(end);
865
866         int selectionStart = Math.min(start, finish);
867         int selectionEnd = Math.max(start, finish);
868
869         currentTextPane.setCaretPosition(selectionStart);
870         currentTextPane.moveCaretPosition(selectionEnd);
871     }
872
873     /**
874      * Translates a LineColumn into an offset into the text held by the editor.
875      *
876      * @param location position to be translated
877      * @return the offset into the content of this editor
878      * @throws IllegalArgumentException
879      * if the specified LineColumn represent a position which does
880      * not exist in the text.
881      */

882     public int getOffsetFromLineColumn(LineColumn location)
883     {
884         if (location.getLine() < 0) {
885             throw new IllegalArgumentException JavaDoc("line < 0");
886         }
887
888         Element lineElement = document.getDefaultRootElement().getElement(location.getLine());
889         if (lineElement == null) {
890             throw new IllegalArgumentException JavaDoc("line=" + location.getLine() + " is out of bound");
891         }
892
893         int lineOffset = lineElement.getStartOffset();
894
895         if (location.getColumn() < 0) {
896             throw new IllegalArgumentException JavaDoc("column < 0 ");
897         }
898
899         int lineLen = lineElement.getEndOffset() - lineOffset;
900
901         if (location.getColumn() >= lineLen) {
902             throw new IllegalArgumentException JavaDoc("column=" + location.getColumn() + " greater than line len=" + lineLen);
903         }
904
905         return lineOffset + location.getColumn();
906     }
907
908     /**
909      * Returns a property of the current editor.
910      *
911      * @param propertyKey The propertyKey of the property to retrieve.
912      * @return the property value or null if it is not found
913      */

914     public Object JavaDoc getProperty(String JavaDoc propertyKey)
915     {
916         return propertyMap.get(propertyKey);
917     }
918
919
920     /**
921      * Set a property for the current editor. Any existing property with
922      * this key will be overwritten.
923      *
924      * @param propertyKey The property key of the new property
925      * @param value The new property value
926      */

927     public void setProperty(String JavaDoc propertyKey, Object JavaDoc value)
928     {
929         if ( propertyKey == null ) {
930             return;
931         }
932         
933         propertyMap.put(propertyKey,value);
934     }
935
936    /**
937      * Returns the length of the line indicated in the edited text.
938      * Zero is a valid value if the given line has no characters in it.
939      *
940      * @param line the line in the text for which the length should be calculated, starting from 0
941      * @return the length of the line, -1 if line is invalid
942      */

943     public int getLineLength(int line)
944     {
945         if (line < 0) {
946             return -1;
947         }
948
949         Element lineElement = document.getDefaultRootElement().getElement(line);
950         if (lineElement == null) {
951             return -1;
952         }
953
954         int startOffset = lineElement.getStartOffset();
955         
956         return lineElement.getEndOffset() - startOffset;
957     }
958
959
960     /**
961      * Returns the length of the data. This is the number of
962      * characters of content that represents the users data.
963      *
964      * It is possible to obtain the line and column of the last character of text by using
965      * the getLineColumnFromOffset() method.
966      *
967      * @return the length >= 0
968      */

969     public int getTextLength ()
970     {
971         return document.getLength();
972     }
973     
974     /**
975      * Return the number of lines in the documant.
976      */

977     public int numberOfLines()
978     {
979         return document.getDefaultRootElement().getElementCount();
980     }
981     
982
983     // --------------------------------------------------------------------
984
// ------------ end of interface inherited from Editor ----------------
985
// --------------------------------------------------------------------
986

987     // ---- BlueJEventListener interface ----
988

989     /**
990      * A BlueJEvent was raised. Check whether it is one that we're interested
991      * in.
992      */

993     public void blueJEvent(int eventId, Object JavaDoc arg)
994     {
995         switch(eventId) {
996             case BlueJEvent.DOCU_GENERATED :
997                 BlueJEvent.removeListener(this);
998                 displayInterface(true);
999                 break;
1000            case BlueJEvent.DOCU_ABORTED :
1001                BlueJEvent.removeListener(this);
1002                info.warning(Config.getString("editor.info.docAborted"));
1003                break;
1004        }
1005    }
1006
1007    // -------- DocumentListener interface --------
1008

1009    /**
1010     * A text insertion has taken place.
1011     */

1012    public void insertUpdate(DocumentEvent e)
1013    {
1014        if (!saveState.isChanged()) {
1015            saveState.setState(StatusLabel.CHANGED);
1016            setChanged();
1017        }
1018        actions.userAction();
1019        doTextInsert.setEvent(e, currentTextPane);
1020        SwingUtilities.invokeLater(doTextInsert);
1021    }
1022
1023    /**
1024     * A text removal has taken place.
1025     */

1026    public void removeUpdate(DocumentEvent e)
1027    {
1028        if (!saveState.isChanged()) {
1029            saveState.setState(StatusLabel.CHANGED);
1030            setChanged();
1031        }
1032        actions.userAction();
1033    }
1034
1035    /**
1036     * Document properties have changed - ignore
1037     */

1038    public void changedUpdate(DocumentEvent e)
1039    {}
1040
1041    // --------------------------------------------------------------------
1042
/**
1043     * Clear the message in the info area.
1044     */

1045    public void clearMessage()
1046    {
1047        info.clear();
1048    }
1049
1050    /**
1051     * Display a message into the info area.
1052     *
1053     * @param msg the message to display
1054     */

1055    public void writeMessage(String JavaDoc msg)
1056    {
1057        info.message(msg);
1058    }
1059
1060    /**
1061     * Write a warning message into the info area. Typically some form of
1062     * unexpected behaviour has occurred.
1063     *
1064     * @param msg Description of the Parameter
1065     */

1066    public void writeWarningMessage(String JavaDoc msg)
1067    {
1068        info.warning(msg);
1069    }
1070
1071    // ==================== USER ACTION IMPLEMENTATIONS ===================
1072

1073    // --------------------------------------------------------------------
1074
/**
1075     */

1076    public void userSave()
1077    {
1078        if (saveState.isSaved())
1079            info.message(Config.getString("editor.info.noChanges"));
1080        else {
1081            try {
1082                save();
1083            }
1084            catch (IOException ioe) {}
1085            // Note we can safely ignore the exception here: a message has
1086
// already been displayed in the editor status bar
1087
}
1088    }
1089
1090    // --------------------------------------------------------------------
1091
/**
1092     */

1093    public void reload()
1094    {
1095        if (filename == null) {
1096            info.warning(Config.getString("editor.info.cannotReload"),
1097                         Config.getString("editor.info.reload"));
1098        }
1099        else if (saveState.isChanged()) {
1100            int answer = DialogManager.askQuestion(this, "really-reload");
1101            if (answer == 0)
1102                doReload();
1103        }
1104        else {
1105            doReload();
1106        }
1107    }
1108
1109    // --------------------------------------------------------------------
1110

1111    /**
1112     * Prints source code from Editor
1113     *
1114     * @param printerJob A PrinterJob to print to.
1115     */

1116    public void print(PrinterJob JavaDoc printerJob)
1117    {
1118        PrintHandler pt = new PrintHandler(printerJob);
1119        pt.print();
1120    }
1121
1122    /**
1123     * Generalised version of print function. This is what is typically called
1124     * when print is initiated from within the source code editor menu. This
1125     * sets up and runs the print process as a separate lower priority thread.
1126     */

1127    public void print()
1128    {
1129        // create a printjob
1130
PrinterJob JavaDoc job = PrinterJob.getPrinterJob();
1131
1132        // make sure the pageformat is ok
1133
if(pageFormat == null) {
1134            pageFormat = PkgMgrFrame.getPageFormat();
1135        }
1136        pageFormat = job.validatePage(pageFormat);
1137        if (job.printDialog()) {
1138            PrintHandler pt = new PrintHandler(job);
1139            Thread JavaDoc printJobThread = new Thread JavaDoc(pt);
1140            printJobThread.setPriority((Thread.currentThread().getPriority() - 1));
1141            printJobThread.start();
1142        }
1143    }
1144
1145    // --------------------------------------------------------------------
1146
/**
1147     * Implementation of the "page setup" user function. This provides a dialog
1148     * for print page setup.
1149     */

1150    public void pageSetup()
1151    {
1152        PrinterJob JavaDoc job = PrinterJob.getPrinterJob();
1153        pageFormat = job.pageDialog(PkgMgrFrame.getPageFormat());
1154        PkgMgrFrame.setPageFormat(pageFormat);
1155    }
1156
1157    // --------------------------------------------------------------------
1158
/**
1159     * The editor has been closed. Hide the editor window now.
1160     */

1161    public void doClose()
1162    {
1163        setVisible(false);
1164        if (watcher != null) {
1165            watcher.closeEvent(this);
1166        }
1167    }
1168
1169    // --------------------------------------------------------------------
1170
/**
1171     * Check whether TABs need expanding in this editor. If they do, return
1172     * true. At the same time, set this flag to true.
1173     *
1174     * @return Description of the Return Value
1175     */

1176    public boolean checkExpandTabs()
1177    {
1178        if (tabsAreExpanded)
1179            return false;
1180
1181        else {
1182            tabsAreExpanded = true;
1183            return true;
1184        }
1185    }
1186
1187    // --------------------------------------------------------------------
1188
/**
1189     * Implementation of "find" user function.
1190     */

1191    public void find()
1192    {
1193        Finder finder = MoeEditorManager.editorManager.getFinder();
1194        finder.show(this, currentTextPane.getSelectedText(), false);
1195    }
1196
1197    // --------------------------------------------------------------------
1198
/**
1199     * Implementation of "replace" user function. Replace adds extra
1200     * functionality to that of a find dialog, as well as altered behaviour. It
1201     * can remain open for multiple functions.
1202     */

1203    public void replace()
1204    {
1205        Finder finder = MoeEditorManager.editorManager.getFinder();
1206        finder.show(this, currentTextPane.getSelectedText(), true);
1207    }
1208
1209    // --------------------------------------------------------------------
1210
/**
1211     * Implementation of "find-next" user function.
1212     */

1213    public void findNext()
1214    {
1215        Finder finder = MoeEditorManager.editorManager.getFinder();
1216        String JavaDoc s = currentTextPane.getSelectedText();
1217        if (s == null) {
1218            s = finder.getSearchString();
1219            if (s == null) {
1220                info.warning(DialogManager.getMessage("no-search-string"));
1221                return;
1222            }
1223        }
1224        findNextString(finder, s, false);
1225    }
1226
1227    // --------------------------------------------------------------------
1228
/**
1229     * Implementation of "find-next-reverse" user function.
1230     */

1231    public void findNextBackward()
1232    {
1233        Finder finder = MoeEditorManager.editorManager.getFinder();
1234        String JavaDoc s = currentTextPane.getSelectedText();
1235        if (s == null) {
1236            s = finder.getSearchString();
1237            if (s == null) {
1238                info.warning(DialogManager.getMessage("no-search-string"));
1239                return;
1240            }
1241        }
1242        findNextString(finder, s, true);
1243    }
1244
1245    // --------------------------------------------------------------------
1246
/**
1247     * Do a find with info in the info area.
1248     */

1249    private void findNextString(Finder finder, String JavaDoc s, boolean backward)
1250    {
1251        boolean found = findString(s, backward, finder.getIgnoreCase(),
1252                                   finder.getWholeWord(), (!finder.getSearchFound()));
1253
1254        finder.setSearchString(s);
1255        finder.setSearchFound(found);
1256    }
1257
1258    // --------------------------------------------------------------------
1259
/**
1260     * Do a find with info in the info area.
1261     */

1262    boolean findString(String JavaDoc s, boolean backward, boolean ignoreCase,
1263                       boolean wholeWord, boolean wrap)
1264    {
1265        if (s.length() == 0) {
1266            info.warning(Config.getString("editor.info.emptySearchString"));
1267            return false;
1268        }
1269        
1270        boolean found;
1271        if (backward)
1272            found = doFindBackward(s, ignoreCase, wholeWord, wrap);
1273        else
1274            found = doFind(s, ignoreCase, wholeWord, wrap);
1275        
1276        StringBuffer JavaDoc msg = new StringBuffer JavaDoc(Config.getString("editor.find.find.label") + " ");
1277        msg.append(backward ? Config.getString("editor.find.backward") : Config.getString("editor.find.forward"));
1278        if (ignoreCase || wholeWord || wrap)
1279            msg.append(" (");
1280
1281        if (ignoreCase)
1282            msg.append(Config.getString("editor.find.ignoreCase").toLowerCase() + ", ");
1283        
1284        if (wholeWord)
1285            msg.append(Config.getString("editor.find.wholeWord").toLowerCase() + ", ");
1286        
1287        if (wrap)
1288            msg.append(Config.getString("editor.find.wrapAround").toLowerCase() + ", ");
1289        
1290        if (ignoreCase || wholeWord || wrap)
1291            msg.replace(msg.length() - 2, msg.length(), "): ");
1292        else
1293            msg.append(": ");
1294        
1295        msg.append(s);
1296
1297        if (found)
1298            info.message(msg.toString());
1299        else
1300            info.warning(msg.toString(), Config.getString("editor.info.notFound"));
1301
1302        return found;
1303    }
1304
1305    // --------------------------------------------------------------------
1306
/**
1307     * doFind - do a find without visible feedback. Returns false if not found.
1308     */

1309    boolean doFind(String JavaDoc s, boolean ignoreCase, boolean wholeWord, boolean wrap)
1310    {
1311        int docLength = document.getLength();
1312        int startPosition = currentTextPane.getCaretPosition();
1313        int endPos = docLength;
1314
1315        boolean found = false;
1316        boolean finished = false;
1317
1318        // first line searched starts from current caret position
1319
int start = startPosition;
1320        Element line = getLineAt(start);
1321        int lineEnd = Math.min(line.getEndOffset(), endPos);
1322
1323        // following lines search from start of line
1324
try {
1325            while (!found && !finished) {
1326                String JavaDoc lineText = document.getText(start, lineEnd - start);
1327                if (lineText != null && lineText.length() > 0) {
1328                    int foundPos = findSubstring(lineText, s, ignoreCase, wholeWord, false);
1329                    if (foundPos != -1) {
1330                        currentTextPane.select(start + foundPos, start + foundPos + s.length());
1331                        found = true;
1332                    }
1333                }
1334                if (lineEnd >= endPos) {
1335                    if (wrap) {
1336                        // do the wrapping
1337
endPos = startPosition;
1338                        line = document.getParagraphElement(0);
1339                        start = line.getStartOffset();
1340                        lineEnd = Math.min(line.getEndOffset(), endPos);
1341                        wrap = false;
1342                        // don't wrap again
1343
}
1344                    else {
1345                        finished = true;
1346                    }
1347                }
1348                else {
1349                    // go to next line
1350
line = document.getParagraphElement(lineEnd + 1);
1351                    start = line.getStartOffset();
1352                    lineEnd = Math.min(line.getEndOffset(), endPos);
1353                }
1354            }
1355        }
1356        catch (BadLocationException ex) {
1357            Debug.message("error in editor find operation");
1358        }
1359        return found;
1360    }
1361
1362    // --------------------------------------------------------------------
1363
/**
1364     * doFindBackward - do a find backwards without visible feedback. Returns
1365     * false if not found.
1366     */

1367    boolean doFindBackward(String JavaDoc s, boolean ignoreCase, boolean wholeWord, boolean wrap)
1368    {
1369        int docLength = document.getLength();
1370        int startPosition = currentTextPane.getCaretPosition() - 1;
1371        if (startPosition < 0) {
1372            startPosition = docLength;
1373        }
1374        int endPos = 0; // where the search ends
1375

1376        boolean found = false;
1377        boolean finished = false;
1378
1379        int start = startPosition; // start of next partial search
1380
Element line = getLineAt(start);
1381        int lineStart = Math.max(line.getStartOffset(), endPos);
1382
1383        try {
1384            while (!found && !finished) {
1385                String JavaDoc lineText = document.getText(lineStart, start - lineStart);
1386                if (lineText != null && lineText.length() > 0) {
1387                    int foundPos = findSubstring(lineText, s, ignoreCase, wholeWord, true);
1388                    if (foundPos != -1) {
1389                        currentTextPane.select(lineStart + foundPos, lineStart + foundPos + s.length());
1390                        found = true;
1391                    }
1392                }
1393                if (lineStart <= endPos) { // reached end of search
1394
if (wrap) { // do the wrapping around
1395
endPos = startPosition;
1396                        line = document.getParagraphElement(docLength);
1397                        start = line.getEndOffset();
1398                        lineStart = Math.max(line.getStartOffset(), endPos);
1399                        wrap = false; // don't wrap again
1400
}
1401                    else {
1402                        finished = true;
1403                    }
1404                }
1405                else { // go to next line
1406
line = document.getParagraphElement(lineStart - 1);
1407                    start = line.getEndOffset();
1408                    lineStart = Math.max(line.getStartOffset(), endPos);
1409                }
1410            }
1411        }
1412        catch (BadLocationException ex) {
1413            Debug.reportError("error in editor find operation");
1414            ex.printStackTrace();
1415        }
1416        return found;
1417    }
1418
1419    /**
1420     * Transfers caret to user specified line number location.
1421     */

1422    public void goToLine()
1423    {
1424        if (goToLineDialog == null) {
1425            goToLineDialog = new GoToLineDialog(this);
1426        }
1427
1428        DialogManager.centreDialog(goToLineDialog);
1429        goToLineDialog.showDialog(numberOfLines());
1430        int newPosition = goToLineDialog.getLineNumber();
1431        if (newPosition > 0) {
1432            setSelection(newPosition, 1, 0);
1433        }
1434    }
1435
1436    /**
1437     * Find the position of a substring in a given string, ignoring case or searching for
1438     * whole words if desired. Return the position of the substring or -1.
1439     *
1440     * @param text the full string to be searched
1441     * @param sub the substring that we're looking for
1442     * @param ignoreCase if true, case is ignored
1443     * @param wholeWord if true, and the search string resembles something like a word,
1444     * find only whole-word ocurrences
1445     * @param backwards Description of the Parameter
1446     * @return Description of the Return Value
1447     * @returns the index of the substring, or -1 if not found
1448     */

1449    private int findSubstring(String JavaDoc text, String JavaDoc sub, boolean ignoreCase,
1450                              boolean wholeWord, boolean backwards)
1451    {
1452        int strlen = text.length();
1453        int sublen = sub.length();
1454
1455        if (sublen == 0) {
1456            return -1;
1457        }
1458
1459        // 'wholeWord' search does not make much sense when the search string is
1460
// not a word
1461
// (ar at least the first and last character is a letter). Check that.
1462
if (!Character.isJavaIdentifierPart(sub.charAt(0)) || !Character.isJavaIdentifierPart(sub.charAt(sublen - 1))) {
1463            wholeWord = false;
1464        }
1465
1466        boolean found = false;
1467        int pos = (backwards ? strlen - sublen : 0);
1468        boolean itsOver = (backwards ? (pos < 0) : (pos + sublen > strlen));
1469
1470        while (!found && !itsOver) {
1471            found = text.regionMatches(ignoreCase, pos, sub, 0, sublen);
1472            if (found && wholeWord) {
1473                found = ((pos == 0) || !Character.isJavaIdentifierPart(text.charAt(pos - 1)))
1474                        && ((pos + sublen >= strlen) || !Character.isJavaIdentifierPart(text.charAt(pos + sublen)));
1475            }
1476            if (!found) {
1477                pos = (backwards ? pos - 1 : pos + 1);
1478                itsOver = (backwards ? (pos < 0) : (pos + sublen > strlen));
1479            }
1480        }
1481        if (found) {
1482            return pos;
1483        }
1484        else {
1485            return -1;
1486        }
1487    }
1488
1489    // --------------------------------------------------------------------
1490
/**
1491     * Implementation of "compile" user function.
1492     */

1493    public void compile()
1494    {
1495        if (watcher == null) {
1496            return;
1497        }
1498        if (!viewingCode()) {
1499            info.warning(" ");
1500            return;
1501        }
1502
1503        info.message(Config.getString("editor.info.compiling"));
1504        watcher.compile(this);
1505    }
1506
1507    // --------------------------------------------------------------------
1508
/**
1509     * Toggle the interface popup menu. This is used when using keys to toggle
1510     * the interface view. Toggling the menu will result in invoking the action.
1511     */

1512    public void toggleInterfaceMenu()
1513    {
1514        if (!sourceIsCode)
1515            return;
1516
1517        if (interfaceToggle.getSelectedIndex() == 0)
1518            interfaceToggle.setSelectedIndex(1);
1519        else
1520            interfaceToggle.setSelectedIndex(0);
1521    }
1522
1523    // --------------------------------------------------------------------
1524
/**
1525     * Implementation of "toggle-interface-view" user function. The menu has
1526     * already been changed - now see what it is and do it.
1527     */

1528    public void toggleInterface()
1529    {
1530        if (!sourceIsCode)
1531            return;
1532
1533        boolean wantHTML = (interfaceToggle.getSelectedItem() == interfaceString);
1534        if (wantHTML && !viewingHTML)
1535            switchToInterfaceView();
1536        else if (!wantHTML && viewingHTML)
1537            switchToSourceView();
1538    }
1539
1540    /**
1541     * Allow the enabling/disabling of print menu option. Added to disable the
1542     * printing og javadoc html for the time being until until implemented.
1543     * (This is reliant on the use of j2sdk1.4 and Java Unified Print Service
1544     * implementation JSR 6)
1545     *
1546     * @param flag true to enable printing from menu.
1547     */

1548    public void enablePrinting(boolean flag)
1549    {
1550        Action printAction = actions.getActionByName("print");
1551        if (printAction != null) {
1552            printAction.setEnabled(flag);
1553        }
1554        Action pageSetupAction = actions.getActionByName("page-setup");
1555        if (pageSetupAction != null) {
1556            pageSetupAction.setEnabled(flag);
1557        }
1558
1559    }
1560
1561    // --------------------------------------------------------------------
1562
/**
1563     * Switch on the source view (it it isn't showing already).
1564     */

1565    private void switchToSourceView()
1566    {
1567        if (!viewingHTML) {
1568            return;
1569        }
1570
1571        // enable print option
1572
enablePrinting(true);
1573        document = sourceDocument;
1574        currentTextPane = sourcePane;
1575        viewingHTML = false;
1576        scrollPane.setViewportView(currentTextPane);
1577        checkSyntaxStatus();
1578        currentTextPane.requestFocus();
1579    }
1580
1581    // --------------------------------------------------------------------
1582
/**
1583     * Switch on the javadoc interface view (it it isn't showing already). If
1584     * necessary, generate it first.
1585     */

1586    private void switchToInterfaceView()
1587    {
1588        if (viewingHTML) {
1589            return;
1590        }
1591
1592        // disable print menu option until implemented
1593
enablePrinting(false);
1594        try {
1595            save();
1596            if (docUpToDate()) {
1597                displayInterface(false);
1598            }
1599            else {
1600                // interface needs to be re-generated
1601
info.message(Config.getString("editor.info.generatingDoc"));
1602                BlueJEvent.addListener(this);
1603                watcher.generateDoc();
1604            }
1605        }
1606        catch (IOException ioe) {
1607            // Could display a dialog here. However, the error message
1608
// (from save() call) will already be displayed in the editor
1609
// status bar.
1610
}
1611    }
1612
1613    // --------------------------------------------------------------------
1614
/**
1615     * Check whether javadoc file is up to date.
1616     *
1617     * @return True is the currently existing documentation is up-to-date.
1618     */

1619    private boolean docUpToDate()
1620    {
1621        try {
1622            File src = new File(filename);
1623            File doc = new File(docFilename);
1624
1625            if (!doc.exists() || (src.exists() && (src.lastModified() > doc.lastModified()))) {
1626                return false;
1627            }
1628        }
1629        catch (Exception JavaDoc e) {
1630            e.printStackTrace();
1631            return false;
1632        }
1633        return true;
1634    }
1635
1636    // --------------------------------------------------------------------
1637
/**
1638     * We want to display the interface view. We have checked (or waited) that
1639     * the html file is available. It is there now, ready to be displayed.
1640     * Display it.
1641     *
1642     * Don't call this directly to switch to the interface view. Call
1643     * switchToInterfaceView() instead.
1644     */

1645    private void displayInterface(boolean reload)
1646    {
1647        info.message(Config.getString("editor.info.loadingDoc"));
1648
1649        // start the call in a separate thread to allow fast return to GUI.
1650
Thread JavaDoc loadThread = new HTMLDisplayThread(reload);
1651        //loadThread.setPriority(Thread.MIN_PRIORITY);
1652
loadThread.start();
1653    }
1654
1655    // --------------------------------------------------------------------
1656
/**
1657     */

1658    public void createHTMLPane()
1659    {
1660        htmlPane = new JEditorPane();
1661        htmlPane.setEditorKit(new HTMLEditorKit JavaDoc());
1662        htmlPane.setEditable(false);
1663        htmlPane.addHyperlinkListener(this);
1664    }
1665
1666    // --------------------------------------------------------------------
1667
/**
1668     * A hyperlink was activated in the document. Do something appropriate.
1669     */

1670    public void hyperlinkUpdate(HyperlinkEvent JavaDoc e)
1671    {
1672        info.clear();
1673        if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
1674            JEditorPane pane = (JEditorPane) e.getSource();
1675            if (e instanceof HTMLFrameHyperlinkEvent JavaDoc) {
1676                HTMLFrameHyperlinkEvent JavaDoc evt = (HTMLFrameHyperlinkEvent JavaDoc) e;
1677                HTMLDocument JavaDoc doc = (HTMLDocument JavaDoc) pane.getDocument();
1678                doc.processHTMLFrameHyperlinkEvent(evt);
1679            }
1680            else {
1681                try {
1682                    pane.setPage(e.getURL());
1683                }
1684                catch (Throwable JavaDoc t) {
1685                    info.warning("cannot display hyperlink: " + e.getURL());
1686                    Debug.reportError("hyperlink failed: " + t);
1687                }
1688            }
1689        }
1690    }
1691
1692    // --------------------------------------------------------------------
1693
/**
1694     * Implementation of "toggle-breakpoint" user function.
1695     */

1696    public void toggleBreakpoint()
1697    {
1698        if (!viewingCode()) {
1699            info.warning(" "); // cause a beep
1700
return;
1701        }
1702        toggleBreakpoint(sourcePane.getCaretPosition());
1703    }
1704
1705    // --------------------------------------------------------------------
1706
/**
1707     * Toggle a breakpoint at a given position.
1708     */

1709    public void toggleBreakpoint(int pos)
1710    {
1711        if (positionHasBreakpoint(pos))
1712            setUnsetBreakpoint(pos, false); // remove
1713
else
1714            setUnsetBreakpoint(pos, true); // set
1715
}
1716
1717    // --------------------------------------------------------------------
1718
/**
1719     * Clear all known breakpoints.
1720     */

1721    private void clearAllBreakpoints()
1722    {
1723        if (mayHaveBreakpoints) {
1724
1725            for (int i = 1; i <= numberOfLines(); i++) {
1726                if (lineHasBreakpoint(i)) {
1727                    doRemoveBreakpoint(getPositionInLine(i));
1728                }
1729            }
1730            mayHaveBreakpoints = false;
1731        }
1732    }
1733
1734    // --------------------------------------------------------------------
1735
/**
1736     * Check weather a position has a breakpoint set
1737     */

1738    private boolean positionHasBreakpoint(int pos)
1739    {
1740        Element line = getLineAt(pos);
1741        return Boolean.TRUE.equals(line.getAttributes().getAttribute(MoeSyntaxView.BREAKPOINT));
1742    }
1743
1744    // --------------------------------------------------------------------
1745
/**
1746     * Check weather a line has a breakpoint set
1747     */

1748    private boolean lineHasBreakpoint(int lineNo)
1749    {
1750        Element line = getLine(lineNo);
1751        return (Boolean.TRUE.equals(line.getAttributes().getAttribute(MoeSyntaxView.BREAKPOINT)));
1752    }
1753
1754    // --------------------------------------------------------------------
1755
/**
1756     * Try to set or remove a breakpoint (depending on the parameter) at the
1757     * given position. Informs the watcher.
1758     */

1759    private void setUnsetBreakpoint(int pos, boolean set)
1760    {
1761        if (watcher != null) {
1762            int line = getLineNumberAt(pos);
1763            String JavaDoc result = watcher.breakpointToggleEvent(this, line, set);
1764
1765            if (result == null) {
1766                // no problem, go ahead
1767
SimpleAttributeSet a = new SimpleAttributeSet();
1768                if (set) {
1769                    a.addAttribute(MoeSyntaxView.BREAKPOINT, Boolean.TRUE);
1770                    mayHaveBreakpoints = true;
1771                }
1772                else {
1773                    a.addAttribute(MoeSyntaxView.BREAKPOINT, Boolean.FALSE);
1774                }
1775
1776                sourceDocument.setParagraphAttributes(pos, a);
1777            }
1778            else {
1779                info.warning(result);
1780            }
1781
1782            // force an update of UI
1783
repaint();
1784        }
1785        else {
1786            info.warning(Config.getString("editor.info.cannotSetBreak"));
1787        }
1788
1789    }
1790
1791    // --------------------------------------------------------------------
1792
/**
1793     * Remove a breakpoint without question.
1794     */

1795    private void doRemoveBreakpoint(int pos)
1796    {
1797        SimpleAttributeSet a = new SimpleAttributeSet();
1798        a.addAttribute(MoeSyntaxView.BREAKPOINT, Boolean.FALSE);
1799        sourceDocument.setParagraphAttributes(pos, a);
1800        repaint();
1801    }
1802
1803    // --------------------------------------------------------------------
1804
/**
1805     * Try to set or remove a step mark (depending on the parameter) at the
1806     * given position.
1807     *
1808     * @param pos A position in the line where we'd like the step mark.
1809     */

1810    private void setStepMark(int pos)
1811    {
1812        removeStepMark();
1813        SimpleAttributeSet a = new SimpleAttributeSet();
1814        a.addAttribute(MoeSyntaxView.STEPMARK, Boolean.TRUE);
1815        sourceDocument.setParagraphAttributes(pos, a);
1816        currentStepPos = pos;
1817        // force an update of UI
1818
repaint();
1819    }
1820
1821    // ========================= SUPPORT ROUTINES ==========================
1822

1823    // --------------------------------------------------------------------
1824
/**
1825     * return a boolean representing whether in source editing view
1826     */

1827    private boolean viewingCode()
1828    {
1829        return sourceIsCode && (!viewingHTML);
1830    }
1831
1832
1833    // --------------------------------------------------------------------
1834
/**
1835     * Return the current line.
1836     */

1837    // private Element getCurrentLine()
1838
// {
1839
// return document.getParagraphElement(currentTextPane.getCaretPosition());
1840
// }
1841

1842    // --------------------------------------------------------------------
1843
/**
1844     * Find and return a line by line number
1845     */

1846    private Element getLine(int lineNo)
1847    {
1848        return document.getDefaultRootElement().getElement(lineNo - 1);
1849    }
1850
1851    // --------------------------------------------------------------------
1852
/**
1853     * Find and return a line by text position
1854     */

1855    private Element getLineAt(int pos)
1856    {
1857        return document.getParagraphElement(pos);
1858    }
1859
1860    // --------------------------------------------------------------------
1861
/**
1862     * Find and return a position in a line.
1863     */

1864    private int getPositionInLine(int lineNo)
1865    {
1866        return getLine(lineNo).getStartOffset();
1867    }
1868
1869    // --------------------------------------------------------------------
1870
/**
1871     * Return the number of the current line.
1872     */

1873    // private int getCurrentLineNo()
1874
// {
1875
// return document.getDefaultRootElement().getElementIndex(
1876
// currentTextPane.getCaretPosition()) + 1;
1877
// }
1878

1879    // --------------------------------------------------------------------
1880
/**
1881     * Return the number of the line containing position 'pos'.
1882     */

1883    private int getLineNumberAt(int pos)
1884    {
1885        return document.getDefaultRootElement().getElementIndex(pos) + 1;
1886    }
1887
1888    // --------------------------------------------------------------------
1889
/**
1890     * Revert the buffer contents to the last saved version. Do not ask any
1891     * question - just do it. Must have a file name.
1892     */

1893    public void doReload()
1894    {
1895        FileReader reader = null;
1896        try {
1897            reader = new FileReader(filename);
1898            sourcePane.read(reader, null);
1899            reader.close();
1900            File file = new File(filename);
1901            lastModified = file.lastModified();
1902
1903            sourceDocument = (MoeSyntaxDocument) sourcePane.getDocument();
1904            if(!viewingHTML)
1905                document = sourceDocument;
1906            
1907            // flag document type as a java file by associating a
1908
// JavaTokenMarker for syntax colouring if specified
1909
checkSyntaxStatus();
1910            sourceDocument.addDocumentListener(this);
1911            sourceDocument.addUndoableEditListener(new MoeUndoableEditListener());
1912            
1913            // We want to inform the watcher that the editor content has changed,
1914
// and then inform it that we are in "saved" state (synced with file).
1915
// But first set state to saved to avoid unnecessary writes to disk.
1916
saveState.setState(StatusLabel.SAVED);
1917            setChanged(); // contents may have changed - notify watcher
1918
setSaved(); // notify watcher that we are saved
1919
}
1920        catch (FileNotFoundException ex) {
1921            info.warning(Config.getString("editor.info.fileDisappeared"));
1922        }
1923        catch (IOException ex) {
1924            info.warning(Config.getString("editor.info.fileReadError"));
1925            setChanged();
1926        }
1927        finally {
1928            try {
1929                if (reader != null)
1930                    reader.close();
1931            }
1932            catch (IOException ioe) {}
1933        }
1934    }
1935
1936    // --------------------------------------------------------------------
1937
/**
1938     * Checks that current status of syntax highlighting option is consistent
1939     * with desired option eg off/on.
1940     */

1941    private void checkSyntaxStatus()
1942    {
1943        if (sourceDocument != null) {
1944
1945            // flag document type as a java file by associating a
1946
// JavaTokenMarker for syntax colouring if specified
1947
if (viewingCode() && PrefMgr.getFlag(PrefMgr.HILIGHTING)) {
1948                if (sourceDocument.getTokenMarker() == null) {
1949                    sourceDocument.setTokenMarker(new JavaTokenMarker());
1950                }
1951            }
1952            else {
1953                sourceDocument.setTokenMarker(null);
1954            }
1955        }
1956        // else ??
1957
}
1958
1959    /**
1960     * Checks that current status of syntax highlighting option is consistent
1961     * with desired option eg off/on. Called when refreshing or making visible
1962     * to pick up any Preference Manager changes to this functionality
1963     */

1964    private void checkBracketStatus()
1965    {
1966        matchBrackets = PrefMgr.getFlag(PrefMgr.MATCH_BRACKETS);
1967        // tidies up leftover highlight if matching is switched off
1968
// while highlighting a valid bracket or refreshes bracket in open
1969
// editor
1970
if (matchBrackets)
1971            doBracketMatch();
1972        else
1973            moeCaret.removeBracket();
1974    }
1975
1976    /**
1977     * Tell whether we are currently matching brackets.
1978     *
1979     * @return True, if we are matching brackets, otherwise false.
1980     */

1981    public boolean matchBrackets()
1982    {
1983        return matchBrackets;
1984    }
1985
1986    // --------------------------------------------------------------------
1987
/**
1988     * Toggle the editor's 'compiled' status. If compiled, enable the breakpoint
1989     * function.
1990     */

1991    private void setCompileStatus(boolean compiled)
1992    {
1993        actions.getActionByName("toggle-breakpoint").setEnabled(compiled && viewingCode());
1994        if (compiled)
1995            document.putProperty(COMPILED, Boolean.TRUE);
1996        else
1997            document.putProperty(COMPILED, Boolean.FALSE);
1998
1999        currentTextPane.repaint();
2000    }
2001
2002    // --------------------------------------------------------------------
2003
/**
2004     * Set the saved/changed status of this buffer to SAVED.
2005     */

2006    private void setSaved()
2007    {
2008        info.message(Config.getString("editor.info.saved"));
2009        saveState.setState(StatusLabel.SAVED);
2010        if (watcher != null) {
2011            watcher.saveEvent(this);
2012        }
2013    }
2014
2015    // --------------------------------------------------------------------
2016
/**
2017     * Buffer just went from saved to changed state (called by StatusLabel)
2018     */

2019    private void setChanged()
2020    {
2021        if (ignoreChanges) {
2022            return;
2023        }
2024        setCompileStatus(false);
2025        if (watcher != null) {
2026            watcher.modificationEvent(this);
2027        }
2028    }
2029
2030    // --------------------------------------------------------------------
2031
/**
2032     * Clear the message in the info area.
2033     */

2034    void caretMoved()
2035    {
2036        clearMessage();
2037        if (matchBrackets) {
2038            doBracketMatch();
2039        }
2040        actions.userAction();
2041    }
2042
2043    /**
2044     * returns the position of the matching bracket for the source pane's
2045     * current caret position. Returns -1 if not found or not valid/appropriate
2046     *
2047     * @return the int representing bracket position
2048     */

2049    public int getBracketMatch()
2050    {
2051        int pos = -1;
2052        try {
2053            int caretPos = sourcePane.getCaretPosition();
2054            if (caretPos != 0) {
2055                caretPos--;
2056            }
2057            pos = TextUtilities.findMatchingBracket(sourceDocument, caretPos);
2058        }
2059        catch (BadLocationException ble) {
2060            Debug.reportError("Bad document location reached while trying to match brackets");
2061        }
2062        return pos;
2063    }
2064
2065    /**
2066     * delegates bracket matching to the source pane's caret
2067     */

2068    private void doBracketMatch()
2069    {
2070        Caret caret = sourcePane.getCaret();
2071        if (caret instanceof MoeCaret) {
2072            ((MoeCaret) caret).paintMatchingBracket();
2073        }
2074    }
2075
2076    /**
2077     * Set the window title to show the defined title, or else the file name.
2078     */

2079    private void setWindowTitle()
2080    {
2081        String JavaDoc title = windowTitle;
2082
2083        if (title == null) {
2084            if (filename == null)
2085                title = "Moe: <no name>";
2086            else
2087                title = "Moe: " + filename;
2088        }
2089        setTitle(title);
2090    }
2091
2092    // --------------------------------------------------------------------
2093
/**
2094     * Return the path to the class documentation.
2095     */

2096    private String JavaDoc getDocPath()
2097    {
2098        return docFilename;
2099    }
2100
2101    // --------------------------------------------------------------------
2102

2103    /**
2104     * Gets the resource attribute of the MoeEditor object
2105     */

2106    private String JavaDoc getResource(String JavaDoc name)
2107    {
2108        return resources.getProperty(name);
2109    }
2110
2111    // --------------------------------------------------------------------
2112

2113    /**
2114     * Tokenize a string.
2115     */

2116    private String JavaDoc[] tokenize(String JavaDoc input)
2117    {
2118        List JavaDoc list = new ArrayList();
2119        StringTokenizer t = new StringTokenizer(input);
2120        String JavaDoc tokens[];
2121
2122        while (t.hasMoreTokens()) {
2123            list.add(t.nextToken());
2124        }
2125
2126        tokens = new String JavaDoc[list.size()];
2127        list.toArray(tokens);
2128        return tokens;
2129    }
2130
2131    // ======================= WINDOW INITIALISATION =======================
2132

2133    // --------------------------------------------------------------------
2134
/**
2135     * Create all the Window components
2136     */

2137    private void initWindow()
2138    {
2139        setIconImage(iconImage);
2140
2141        // prepare the content pane
2142

2143        JPanel contentPane = new JPanel(new BorderLayout(5, 5));
2144        contentPane.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
2145        setContentPane(contentPane);
2146
2147        // create and add info and status areas
2148

2149        JPanel bottomArea = new JPanel();
2150        
2151        // create panel for info/status
2152
bottomArea.setLayout(new BorderLayout(5, 5));
2153
2154        info = new Info();
2155        bottomArea.add(info, BorderLayout.CENTER);
2156
2157        statusArea = new JPanel();
2158        statusArea.setLayout(new GridLayout(0, 1));
2159        // one column, many rows
2160
statusArea.setBackground(infoColor);
2161        statusArea.setBorder(BorderFactory.createLineBorder(Color.black));
2162
2163        saveState = new StatusLabel(StatusLabel.SAVED);
2164        statusArea.add(saveState);
2165        bottomArea.add(statusArea, BorderLayout.EAST);
2166
2167        contentPane.add(bottomArea, BorderLayout.SOUTH);
2168
2169        // create the text document
2170

2171        sourceDocument = new MoeSyntaxDocument();
2172        sourceDocument.addDocumentListener(this);
2173        sourceDocument.addUndoableEditListener(new MoeUndoableEditListener());
2174
2175        // create the text pane
2176

2177        MoeSyntaxEditorKit kit = new MoeSyntaxEditorKit(false);
2178        sourcePane = new MoeEditorPane();
2179
2180        sourcePane.setDocument(sourceDocument);
2181        sourcePane.setCaretPosition(0);
2182        sourcePane.setMargin(new Insets(2, 2, 2, 2));
2183        sourcePane.setOpaque(true);
2184        sourcePane.setEditorKit(kit);
2185        moeCaret = new MoeCaret(this);
2186        sourcePane.setCaret(moeCaret);
2187        sourcePane.setBackground(textBgColor);
2188        // sourcePane.setSelectionColor(selectionColour);
2189
sourcePane.setCaretColor(cursorColor);
2190
2191        // default showing:
2192
document = sourceDocument;
2193        currentTextPane = sourcePane;
2194        
2195        scrollPane = new JScrollPane(currentTextPane);
2196        scrollPane.setPreferredSize(new Dimension(598, 400));
2197        scrollPane.getVerticalScrollBar().setUnitIncrement(16);
2198        
2199        contentPane.add(scrollPane, BorderLayout.CENTER);
2200
2201        // get table of edit actions
2202

2203        actions = MoeActions.getActions(sourcePane);
2204
2205        // **** temporary: disable all unimplemented actions ****
2206
actions.getActionByName("show-manual").setEnabled(false);
2207        // ****
2208

2209        // create menubar and menus
2210

2211        JMenuBar menubar = createMenuBar();
2212        setJMenuBar(menubar);
2213
2214        // create toolbar
2215

2216        toolbar = createToolbar();
2217        contentPane.add(toolbar, BorderLayout.NORTH);
2218
2219        // add event listener to handle the window close requests
2220

2221        addWindowListener(new WindowAdapter() {
2222            public void windowClosing(WindowEvent e) {
2223                close();
2224            }
2225            public void windowActivated(WindowEvent e) {
2226                checkForChangeOnDisk();
2227            }
2228        });
2229
2230        setFocusTraversalPolicy(new MoeFocusTraversalPolicy());
2231        
2232        setWindowTitle();
2233        pack();
2234    }
2235
2236    // --------------------------------------------------------------------
2237

2238    /**
2239     * Create the editor's menu bar.
2240     */

2241    private JMenuBar createMenuBar()
2242    {
2243        JMenuBar menubar = new JMenuBar();
2244        JMenu menu = null;
2245
2246        String JavaDoc[] menuKeys = tokenize(getResource("menubar"));
2247        for (int i = 0; i < menuKeys.length; i++) {
2248            menu = createMenu(menuKeys[i]);
2249            if (menu != null) {
2250                menubar.add(menu);
2251            }
2252        }
2253        return menubar;
2254    }
2255
2256    // --------------------------------------------------------------------
2257

2258    /**
2259     * Create a single menu for the editor's menu bar. The key for the menu (as
2260     * defined in moe.properties) is supplied.
2261     */

2262    private JMenu createMenu(String JavaDoc key)
2263    {
2264        JMenuItem item;
2265        String JavaDoc label;
2266
2267        // get menu title
2268
JMenu menu = new JMenu(Config.getString("editor." + key + LabelSuffix));
2269
2270        // get menu definition
2271
String JavaDoc itemString = getResource(key);
2272        if (itemString == null) {
2273            Debug.message("Moe: cannot find menu definition for " + key);
2274            return null;
2275        }
2276
2277        // cut menu definition into separate items
2278
String JavaDoc[] itemKeys = tokenize(itemString);
2279
2280        // create menu item for each item
2281
for (int i = 0; i < itemKeys.length; i++) {
2282            if (itemKeys[i].equals("-")) {
2283                menu.addSeparator();
2284            }
2285            else {
2286                Action action = actions.getActionByName(itemKeys[i]);
2287                if (action == null) {
2288                    Debug.message("Moe: cannot find action " + itemKeys[i]);
2289                }
2290                else {
2291                    item = menu.add(action);
2292                    if (action == actions.undoAction) {
2293                        undoComponents.add(item);
2294                    }
2295                    if (action == actions.redoAction) {
2296                        redoComponents.add(item);
2297                    }
2298                    label = Config.getString("editor." + itemKeys[i] + LabelSuffix);
2299                    if (label != null) {
2300                        item.setText(label);
2301                    }
2302                    KeyStroke[] keys = actions.getKeyStrokesForAction(action);
2303                    if (keys != null) {
2304                        item.setAccelerator(chooseKey(keys));
2305                    }
2306                }
2307            }
2308        }
2309        return menu;
2310    }
2311
2312    /**
2313     * Choose a key to use in the menu from all defined keys.
2314     */

2315    private KeyStroke chooseKey(KeyStroke[] keys)
2316    {
2317        if (keys.length == 1) {
2318            return keys[0];
2319        }
2320        else {
2321            KeyStroke key = keys[0];
2322            // give preference to shortcuts using letter keys (CTRL-V, rather
2323
// than F2)
2324
for (int i = 1; i < keys.length; i++) {
2325                if (keys[i].getKeyCode() >= 'A' && keys[i].getKeyCode() <= 'Z') {
2326                    key = keys[i];
2327                }
2328            }
2329            return key;
2330        }
2331    }
2332
2333    // --------------------------------------------------------------------
2334

2335    /**
2336     * Create the toolbar.
2337     *
2338     * @return The toolbar component, ready made.
2339     */

2340    private JComponent createToolbar()
2341    {
2342        JPanel toolbar = new JPanel();
2343        toolbar.setLayout(new BoxLayout(toolbar, BoxLayout.X_AXIS));
2344        //((FlowLayout)toolbar.getLayout()).setAlignment(FlowLayout.LEFT);
2345

2346        String JavaDoc[] toolKeys = tokenize(getResource("toolbar"));
2347        for (int i = 0; i < toolKeys.length; i++) {
2348            toolbar.add(createToolbarButton(toolKeys[i], false));
2349            toolbar.add(Box.createHorizontalStrut(4));
2350        }
2351
2352        toolbar.add(Box.createHorizontalGlue());
2353        toolbar.add(Box.createHorizontalGlue());
2354        toolbar.add(createInterfaceSelector());
2355
2356        return toolbar;
2357    }
2358
2359    // --------------------------------------------------------------------
2360

2361    /**
2362     * Create a button on the toolbar.
2363     */

2364    private AbstractButton createToolbarButton(String JavaDoc key, boolean isToggle)
2365    {
2366        String JavaDoc label = Config.getString("editor." + key + LabelSuffix);
2367        AbstractButton button;
2368
2369        if (isToggle)
2370            button = new JToggleButton(label);
2371        else
2372            button = new JButton(label);
2373
2374        button.setRequestFocusEnabled(false);
2375        // never get keyboard focus
2376

2377        Insets margin = button.getMargin();
2378        button.setMargin(new Insets(margin.top, 3, margin.bottom, 3));
2379
2380        button.setFont(PrefMgr.getStandardFont());
2381
2382        String JavaDoc actionName = getResource(key + ActionSuffix);
2383        if (actionName == null) {
2384            actionName = key;
2385        }
2386        Action action = actions.getActionByName(actionName);
2387        if (action != null) { // should never be null...
2388
if (action == actions.undoAction) {
2389                undoComponents.add(button);
2390                button.setEnabled(false);
2391            }
2392            if (action == actions.redoAction) {
2393                redoComponents.add(button);
2394                button.setEnabled(false);
2395            }
2396            button.addActionListener(action);
2397            button.setActionCommand(actionName);
2398        }
2399        else {
2400            button.setEnabled(false);
2401            Debug.message("Moe: action not found for button " + label);
2402        }
2403
2404        // MacOS property to change button shape
2405
button.putClientProperty("JButton.buttonType", "toolbar");
2406
2407        return button;
2408    }
2409
2410    // --------------------------------------------------------------------
2411

2412    /**
2413     * Create a combo box for the toolbar
2414     */

2415    private JComboBox createInterfaceSelector()
2416    {
2417        String JavaDoc[] choiceStrings = {implementationString, interfaceString};
2418        interfaceToggle = new JComboBox(choiceStrings);
2419
2420        interfaceToggle.setRequestFocusEnabled(false);
2421        interfaceToggle.setFont(PrefMgr.getStandardFont());
2422        interfaceToggle.setBorder(new EmptyBorder JavaDoc(2, 2, 2, 2));
2423        interfaceToggle.setForeground(envOpColour);
2424
2425        String JavaDoc actionName = "toggle-interface-view";
2426        Action action = actions.getActionByName(actionName);
2427        if (action != null) { // should never be null...
2428
interfaceToggle.setAction(action);
2429        }
2430        else {
2431            interfaceToggle.setEnabled(false);
2432            Debug.message("Moe: action not found: " + actionName);
2433        }
2434        if (!sourceIsCode) {
2435            interfaceToggle.setEnabled(false);
2436        }
2437        return interfaceToggle;
2438    }
2439
2440    // --------------------------------------------------------------------
2441

2442    /**
2443     * Inner class for printing thread to allow printing to occur as a
2444     * background operation.
2445     *
2446     * @author Bruce Quig
2447     */

2448    class PrintHandler
2449        implements Runnable JavaDoc
2450    {
2451        PrinterJob JavaDoc printJob;
2452
2453        /**
2454         * Construct the PrintHandler.
2455         */

2456        public PrintHandler(PrinterJob JavaDoc pj)
2457        {
2458            super();
2459            printJob = pj;
2460        }
2461
2462        /**
2463         * Implementation of Runnable interface
2464         */

2465        public void run()
2466        {
2467            print();
2468        }
2469
2470        /**
2471         * Create MoePrinter and then invoke print method
2472         */

2473        public void print()
2474        {
2475            if (printer == null) {
2476                printer = new MoePrinter();
2477            }
2478
2479            // print document, using new pageformat object at present
2480
info.message(Config.getString("editor.info.printing"));
2481            if (printer.printDocument(printJob, sourceDocument, windowTitle, printFont, pageFormat)) {
2482                info.message(Config.getString("editor.info.printed"));
2483            }
2484            else {
2485                info.message(Config.getString("editor.info.cancelled"));
2486            }
2487
2488        }
2489
2490    }
2491
2492    // --------------------------------------------------------------------
2493

2494    /**
2495     * Inner class for loading HTML documentation
2496     */

2497    class HTMLDisplayThread extends Thread JavaDoc
2498    {
2499        private boolean reload;
2500
2501        /**
2502         */

2503        HTMLDisplayThread(boolean load)
2504        {
2505            reload = load;
2506        }
2507
2508        /**
2509         * Main processing method for the HTMLDisplayThread object
2510         */

2511        public void run()
2512        {
2513            if (htmlDocument == null) {
2514                createHTMLPane();
2515                reload = true;
2516            }
2517
2518            if (reload) {
2519                try {
2520                    try {
2521                        // this statement fails, but it is needed to avoid
2522
// caching of html page
2523
htmlPane.setPage("file:/dummy");
2524                    }
2525                    catch (Exception JavaDoc e) {}
2526
2527                    File urlFile = new File(getDocPath());
2528                    URL JavaDoc myURL = urlFile.toURI().toURL();
2529                    htmlPane.setPage(myURL);
2530                    htmlDocument = (HTMLDocument JavaDoc) htmlPane.getDocument();
2531                    htmlDocument.setBase(myURL);
2532                    info.message(Config.getString("editor.info.docLoaded"));
2533                }
2534                catch (Exception JavaDoc exc) {
2535                    info.warning(Config.getString("editor.info.docDisappeared"), getDocPath());
2536                    Debug.reportError("loading class interface failed: " + exc);
2537                }
2538            }
2539            document = htmlDocument;
2540            currentTextPane = htmlPane;
2541            viewingHTML = true;
2542            scrollPane.setViewportView(currentTextPane);
2543            currentTextPane.requestFocus();
2544        }
2545
2546    }
2547
2548    // --------------------------------------------------------------------
2549

2550    /**
2551     * Class for thread listening to edit changes.
2552     */

2553    class TextInsertNotifier
2554        implements Runnable JavaDoc
2555    {
2556        private DocumentEvent evt;
2557        private JEditorPane editorPane;
2558
2559        /**
2560         * Sets the event attribute of the TextInsertNotifier object
2561         */

2562        public void setEvent(DocumentEvent e, JEditorPane editorPane)
2563        {
2564            evt = e;
2565            this.editorPane = editorPane;
2566        }
2567
2568        /**
2569         * Main processing method for the TextInsertNotifier object
2570         */

2571        public void run()
2572        {
2573            actions.textInsertAction(evt, editorPane);
2574        }
2575    }
2576
2577    /**
2578     * Custom focus traversal implementation to make sure that the text area
2579     * gets and never loses focus.
2580     */

2581    class MoeFocusTraversalPolicy extends FocusTraversalPolicy
2582    {
2583        public Component getComponentAfter(Container focusCycleRoot, Component aComponent) {
2584            return currentTextPane;
2585        }
2586
2587        public Component getComponentBefore(Container focusCycleRoot, Component aComponent) {
2588            return currentTextPane;
2589        }
2590
2591        public Component getDefaultComponent(Container focusCycleRoot) {
2592            return currentTextPane;
2593        }
2594
2595        public Component getFirstComponent(Container focusCycleRoot) {
2596            return currentTextPane;
2597        }
2598
2599        public Component getInitialComponent(Window window) {
2600            return currentTextPane;
2601        }
2602
2603        public Component getLastComponent(Container focusCycleRoot) {
2604            return currentTextPane;
2605        }
2606    }
2607}
2608
Popular Tags