blob: 556bfffa0a1f9070d32bb5bf57a7f301b32257ad [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * - Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * - Neither the name of Sun Microsystems nor the names of its
16 * contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 */
34
35import java.awt.*;
36import java.awt.event.*;
37import java.beans.*;
38import java.io.*;
39import java.net.URL;
40import java.util.*;
41
42import javax.swing.text.*;
43import javax.swing.undo.*;
44import javax.swing.event.*;
45import javax.swing.*;
46
47/**
48 * Sample application using the simple text editor component that
49 * supports only one font.
50 *
51 * @author Timothy Prinzing
52 */
53class Notepad extends JPanel {
54
55 private static ResourceBundle resources;
56 private final static String EXIT_AFTER_PAINT = new String("-exit");
57 private static boolean exitAfterFirstPaint;
58
59 static {
60 try {
61 resources = ResourceBundle.getBundle("resources.Notepad",
62 Locale.getDefault());
63 } catch (MissingResourceException mre) {
64 System.err.println("resources/Notepad.properties not found");
65 System.exit(1);
66 }
67 }
68
69 public void paintChildren(Graphics g) {
70 super.paintChildren(g);
71 if (exitAfterFirstPaint) {
72 System.exit(0);
73 }
74 }
75
76 Notepad() {
77 super(true);
78
79 // Force SwingSet to come up in the Cross Platform L&F
80 try {
81 UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
82 // If you want the System L&F instead, comment out the above line and
83 // uncomment the following:
84 // UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
85 } catch (Exception exc) {
86 System.err.println("Error loading L&F: " + exc);
87 }
88
89 setBorder(BorderFactory.createEtchedBorder());
90 setLayout(new BorderLayout());
91
92 // create the embedded JTextComponent
93 editor = createEditor();
94 // Add this as a listener for undoable edits.
95 editor.getDocument().addUndoableEditListener(undoHandler);
96
97 // install the command table
98 commands = new Hashtable();
99 Action[] actions = getActions();
100 for (int i = 0; i < actions.length; i++) {
101 Action a = actions[i];
102 //commands.put(a.getText(Action.NAME), a);
103 commands.put(a.getValue(Action.NAME), a);
104 }
105
106 JScrollPane scroller = new JScrollPane();
107 JViewport port = scroller.getViewport();
108 port.add(editor);
109 try {
110 String vpFlag = resources.getString("ViewportBackingStore");
111 Boolean bs = Boolean.valueOf(vpFlag);
112 port.setBackingStoreEnabled(bs.booleanValue());
113 } catch (MissingResourceException mre) {
114 // just use the viewport default
115 }
116
117 menuItems = new Hashtable();
118 JPanel panel = new JPanel();
119 panel.setLayout(new BorderLayout());
120 panel.add("North",createToolbar());
121 panel.add("Center", scroller);
122 add("Center", panel);
123 add("South", createStatusbar());
124 }
125
126 public static void main(String[] args) {
127 try {
128 String vers = System.getProperty("java.version");
129 if (vers.compareTo("1.1.2") < 0) {
130 System.out.println("!!!WARNING: Swing must be run with a " +
131 "1.1.2 or higher version VM!!!");
132 }
133 if (args.length > 0 && args[0].equals(EXIT_AFTER_PAINT)) {
134 exitAfterFirstPaint = true;
135 }
136 JFrame frame = new JFrame();
137 frame.setTitle(resources.getString("Title"));
138 frame.setBackground(Color.lightGray);
139 frame.getContentPane().setLayout(new BorderLayout());
140 Notepad notepad = new Notepad();
141 frame.getContentPane().add("Center", notepad);
142 frame.setJMenuBar(notepad.createMenubar());
143 frame.addWindowListener(new AppCloser());
144 frame.pack();
145 frame.setSize(500, 600);
146 frame.show();
147 } catch (Throwable t) {
148 System.out.println("uncaught exception: " + t);
149 t.printStackTrace();
150 }
151 }
152
153 /**
154 * Fetch the list of actions supported by this
155 * editor. It is implemented to return the list
156 * of actions supported by the embedded JTextComponent
157 * augmented with the actions defined locally.
158 */
159 public Action[] getActions() {
160 return TextAction.augmentList(editor.getActions(), defaultActions);
161 }
162
163 /**
164 * Create an editor to represent the given document.
165 */
166 protected JTextComponent createEditor() {
167 JTextComponent c = new JTextArea();
168 c.setDragEnabled(true);
169 c.setFont(new Font("monospaced", Font.PLAIN, 12));
170 return c;
171 }
172
173 /**
174 * Fetch the editor contained in this panel
175 */
176 protected JTextComponent getEditor() {
177 return editor;
178 }
179
180 /**
181 * To shutdown when run as an application. This is a
182 * fairly lame implementation. A more self-respecting
183 * implementation would at least check to see if a save
184 * was needed.
185 */
186 protected static final class AppCloser extends WindowAdapter {
187 public void windowClosing(WindowEvent e) {
188 System.exit(0);
189 }
190 }
191
192 /**
193 * Find the hosting frame, for the file-chooser dialog.
194 */
195 protected Frame getFrame() {
196 for (Container p = getParent(); p != null; p = p.getParent()) {
197 if (p instanceof Frame) {
198 return (Frame) p;
199 }
200 }
201 return null;
202 }
203
204 /**
205 * This is the hook through which all menu items are
206 * created. It registers the result with the menuitem
207 * hashtable so that it can be fetched with getMenuItem().
208 * @see #getMenuItem
209 */
210 protected JMenuItem createMenuItem(String cmd) {
211 JMenuItem mi = new JMenuItem(getResourceString(cmd + labelSuffix));
212 URL url = getResource(cmd + imageSuffix);
213 if (url != null) {
214 mi.setHorizontalTextPosition(JButton.RIGHT);
215 mi.setIcon(new ImageIcon(url));
216 }
217 String astr = getResourceString(cmd + actionSuffix);
218 if (astr == null) {
219 astr = cmd;
220 }
221 mi.setActionCommand(astr);
222 Action a = getAction(astr);
223 if (a != null) {
224 mi.addActionListener(a);
225 a.addPropertyChangeListener(createActionChangeListener(mi));
226 mi.setEnabled(a.isEnabled());
227 } else {
228 mi.setEnabled(false);
229 }
230 menuItems.put(cmd, mi);
231 return mi;
232 }
233
234 /**
235 * Fetch the menu item that was created for the given
236 * command.
237 * @param cmd Name of the action.
238 * @returns item created for the given command or null
239 * if one wasn't created.
240 */
241 protected JMenuItem getMenuItem(String cmd) {
242 return (JMenuItem) menuItems.get(cmd);
243 }
244
245 protected Action getAction(String cmd) {
246 return (Action) commands.get(cmd);
247 }
248
249 protected String getResourceString(String nm) {
250 String str;
251 try {
252 str = resources.getString(nm);
253 } catch (MissingResourceException mre) {
254 str = null;
255 }
256 return str;
257 }
258
259 protected URL getResource(String key) {
260 String name = getResourceString(key);
261 if (name != null) {
262 URL url = this.getClass().getResource(name);
263 return url;
264 }
265 return null;
266 }
267
268 protected Container getToolbar() {
269 return toolbar;
270 }
271
272 protected JMenuBar getMenubar() {
273 return menubar;
274 }
275
276 /**
277 * Create a status bar
278 */
279 protected Component createStatusbar() {
280 // need to do something reasonable here
281 status = new StatusBar();
282 return status;
283 }
284
285 /**
286 * Resets the undo manager.
287 */
288 protected void resetUndoManager() {
289 undo.discardAllEdits();
290 undoAction.update();
291 redoAction.update();
292 }
293
294 /**
295 * Create the toolbar. By default this reads the
296 * resource file for the definition of the toolbar.
297 */
298 private Component createToolbar() {
299 toolbar = new JToolBar();
300 String[] toolKeys = tokenize(getResourceString("toolbar"));
301 for (int i = 0; i < toolKeys.length; i++) {
302 if (toolKeys[i].equals("-")) {
303 toolbar.add(Box.createHorizontalStrut(5));
304 } else {
305 toolbar.add(createTool(toolKeys[i]));
306 }
307 }
308 toolbar.add(Box.createHorizontalGlue());
309 return toolbar;
310 }
311
312 /**
313 * Hook through which every toolbar item is created.
314 */
315 protected Component createTool(String key) {
316 return createToolbarButton(key);
317 }
318
319 /**
320 * Create a button to go inside of the toolbar. By default this
321 * will load an image resource. The image filename is relative to
322 * the classpath (including the '.' directory if its a part of the
323 * classpath), and may either be in a JAR file or a separate file.
324 *
325 * @param key The key in the resource file to serve as the basis
326 * of lookups.
327 */
328 protected JButton createToolbarButton(String key) {
329 URL url = getResource(key + imageSuffix);
330 JButton b = new JButton(new ImageIcon(url)) {
331 public float getAlignmentY() { return 0.5f; }
332 };
333 b.setRequestFocusEnabled(false);
334 b.setMargin(new Insets(1,1,1,1));
335
336 String astr = getResourceString(key + actionSuffix);
337 if (astr == null) {
338 astr = key;
339 }
340 Action a = getAction(astr);
341 if (a != null) {
342 b.setActionCommand(astr);
343 b.addActionListener(a);
344 } else {
345 b.setEnabled(false);
346 }
347
348 String tip = getResourceString(key + tipSuffix);
349 if (tip != null) {
350 b.setToolTipText(tip);
351 }
352
353 return b;
354 }
355
356 /**
357 * Take the given string and chop it up into a series
358 * of strings on whitespace boundaries. This is useful
359 * for trying to get an array of strings out of the
360 * resource file.
361 */
362 protected String[] tokenize(String input) {
363 Vector v = new Vector();
364 StringTokenizer t = new StringTokenizer(input);
365 String cmd[];
366
367 while (t.hasMoreTokens())
368 v.addElement(t.nextToken());
369 cmd = new String[v.size()];
370 for (int i = 0; i < cmd.length; i++)
371 cmd[i] = (String) v.elementAt(i);
372
373 return cmd;
374 }
375
376 /**
377 * Create the menubar for the app. By default this pulls the
378 * definition of the menu from the associated resource file.
379 */
380 protected JMenuBar createMenubar() {
381 JMenuItem mi;
382 JMenuBar mb = new JMenuBar();
383
384 String[] menuKeys = tokenize(getResourceString("menubar"));
385 for (int i = 0; i < menuKeys.length; i++) {
386 JMenu m = createMenu(menuKeys[i]);
387 if (m != null) {
388 mb.add(m);
389 }
390 }
391 this.menubar = mb;
392 return mb;
393 }
394
395 /**
396 * Create a menu for the app. By default this pulls the
397 * definition of the menu from the associated resource file.
398 */
399 protected JMenu createMenu(String key) {
400 String[] itemKeys = tokenize(getResourceString(key));
401 JMenu menu = new JMenu(getResourceString(key + "Label"));
402 for (int i = 0; i < itemKeys.length; i++) {
403 if (itemKeys[i].equals("-")) {
404 menu.addSeparator();
405 } else {
406 JMenuItem mi = createMenuItem(itemKeys[i]);
407 menu.add(mi);
408 }
409 }
410 return menu;
411 }
412
413 // Yarked from JMenu, ideally this would be public.
414 protected PropertyChangeListener createActionChangeListener(JMenuItem b) {
415 return new ActionChangedListener(b);
416 }
417
418 // Yarked from JMenu, ideally this would be public.
419 private class ActionChangedListener implements PropertyChangeListener {
420 JMenuItem menuItem;
421
422 ActionChangedListener(JMenuItem mi) {
423 super();
424 this.menuItem = mi;
425 }
426 public void propertyChange(PropertyChangeEvent e) {
427 String propertyName = e.getPropertyName();
428 if (e.getPropertyName().equals(Action.NAME)) {
429 String text = (String) e.getNewValue();
430 menuItem.setText(text);
431 } else if (propertyName.equals("enabled")) {
432 Boolean enabledState = (Boolean) e.getNewValue();
433 menuItem.setEnabled(enabledState.booleanValue());
434 }
435 }
436 }
437
438 private JTextComponent editor;
439 private Hashtable commands;
440 private Hashtable menuItems;
441 private JMenuBar menubar;
442 private JToolBar toolbar;
443 private JComponent status;
444 private JFrame elementTreeFrame;
445 protected ElementTreePanel elementTreePanel;
446
447 protected FileDialog fileDialog;
448
449 /**
450 * Listener for the edits on the current document.
451 */
452 protected UndoableEditListener undoHandler = new UndoHandler();
453
454 /** UndoManager that we add edits to. */
455 protected UndoManager undo = new UndoManager();
456
457 /**
458 * Suffix applied to the key used in resource file
459 * lookups for an image.
460 */
461 public static final String imageSuffix = "Image";
462
463 /**
464 * Suffix applied to the key used in resource file
465 * lookups for a label.
466 */
467 public static final String labelSuffix = "Label";
468
469 /**
470 * Suffix applied to the key used in resource file
471 * lookups for an action.
472 */
473 public static final String actionSuffix = "Action";
474
475 /**
476 * Suffix applied to the key used in resource file
477 * lookups for tooltip text.
478 */
479 public static final String tipSuffix = "Tooltip";
480
481 public static final String openAction = "open";
482 public static final String newAction = "new";
483 public static final String saveAction = "save";
484 public static final String exitAction = "exit";
485 public static final String showElementTreeAction = "showElementTree";
486
487 class UndoHandler implements UndoableEditListener {
488
489 /**
490 * Messaged when the Document has created an edit, the edit is
491 * added to <code>undo</code>, an instance of UndoManager.
492 */
493 public void undoableEditHappened(UndoableEditEvent e) {
494 undo.addEdit(e.getEdit());
495 undoAction.update();
496 redoAction.update();
497 }
498 }
499
500 /**
501 * FIXME - I'm not very useful yet
502 */
503 class StatusBar extends JComponent {
504
505 public StatusBar() {
506 super();
507 setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
508 }
509
510 public void paint(Graphics g) {
511 super.paint(g);
512 }
513
514 }
515
516 // --- action implementations -----------------------------------
517
518 private UndoAction undoAction = new UndoAction();
519 private RedoAction redoAction = new RedoAction();
520
521 /**
522 * Actions defined by the Notepad class
523 */
524 private Action[] defaultActions = {
525 new NewAction(),
526 new OpenAction(),
527 new SaveAction(),
528 new ExitAction(),
529 new ShowElementTreeAction(),
530 undoAction,
531 redoAction
532 };
533
534 class UndoAction extends AbstractAction {
535 public UndoAction() {
536 super("Undo");
537 setEnabled(false);
538 }
539
540 public void actionPerformed(ActionEvent e) {
541 try {
542 undo.undo();
543 } catch (CannotUndoException ex) {
544 System.out.println("Unable to undo: " + ex);
545 ex.printStackTrace();
546 }
547 update();
548 redoAction.update();
549 }
550
551 protected void update() {
552 if(undo.canUndo()) {
553 setEnabled(true);
554 putValue(Action.NAME, undo.getUndoPresentationName());
555 }
556 else {
557 setEnabled(false);
558 putValue(Action.NAME, "Undo");
559 }
560 }
561 }
562
563 class RedoAction extends AbstractAction {
564 public RedoAction() {
565 super("Redo");
566 setEnabled(false);
567 }
568
569 public void actionPerformed(ActionEvent e) {
570 try {
571 undo.redo();
572 } catch (CannotRedoException ex) {
573 System.out.println("Unable to redo: " + ex);
574 ex.printStackTrace();
575 }
576 update();
577 undoAction.update();
578 }
579
580 protected void update() {
581 if(undo.canRedo()) {
582 setEnabled(true);
583 putValue(Action.NAME, undo.getRedoPresentationName());
584 }
585 else {
586 setEnabled(false);
587 putValue(Action.NAME, "Redo");
588 }
589 }
590 }
591
592 class OpenAction extends NewAction {
593
594 OpenAction() {
595 super(openAction);
596 }
597
598 public void actionPerformed(ActionEvent e) {
599 Frame frame = getFrame();
600 JFileChooser chooser = new JFileChooser();
601 int ret = chooser.showOpenDialog(frame);
602
603 if (ret != JFileChooser.APPROVE_OPTION) {
604 return;
605 }
606
607 File f = chooser.getSelectedFile();
608 if (f.isFile() && f.canRead()) {
609 Document oldDoc = getEditor().getDocument();
610 if(oldDoc != null)
611 oldDoc.removeUndoableEditListener(undoHandler);
612 if (elementTreePanel != null) {
613 elementTreePanel.setEditor(null);
614 }
615 getEditor().setDocument(new PlainDocument());
616 frame.setTitle(f.getName());
617 Thread loader = new FileLoader(f, editor.getDocument());
618 loader.start();
619 } else {
620 JOptionPane.showMessageDialog(getFrame(),
621 "Could not open file: " + f,
622 "Error opening file",
623 JOptionPane.ERROR_MESSAGE);
624 }
625 }
626 }
627
628 class SaveAction extends AbstractAction {
629
630 SaveAction() {
631 super(saveAction);
632 }
633
634 public void actionPerformed(ActionEvent e) {
635 Frame frame = getFrame();
636 JFileChooser chooser = new JFileChooser();
637 int ret = chooser.showSaveDialog(frame);
638
639 if (ret != JFileChooser.APPROVE_OPTION) {
640 return;
641 }
642
643 File f = chooser.getSelectedFile();
644 frame.setTitle(f.getName());
645 Thread saver = new FileSaver(f, editor.getDocument());
646 saver.start();
647 }
648 }
649
650 class NewAction extends AbstractAction {
651
652 NewAction() {
653 super(newAction);
654 }
655
656 NewAction(String nm) {
657 super(nm);
658 }
659
660 public void actionPerformed(ActionEvent e) {
661 Document oldDoc = getEditor().getDocument();
662 if(oldDoc != null)
663 oldDoc.removeUndoableEditListener(undoHandler);
664 getEditor().setDocument(new PlainDocument());
665 getEditor().getDocument().addUndoableEditListener(undoHandler);
666 resetUndoManager();
667 getFrame().setTitle(resources.getString("Title"));
668 revalidate();
669 }
670 }
671
672 /**
673 * Really lame implementation of an exit command
674 */
675 class ExitAction extends AbstractAction {
676
677 ExitAction() {
678 super(exitAction);
679 }
680
681 public void actionPerformed(ActionEvent e) {
682 System.exit(0);
683 }
684 }
685
686 /**
687 * Action that brings up a JFrame with a JTree showing the structure
688 * of the document.
689 */
690 class ShowElementTreeAction extends AbstractAction {
691
692 ShowElementTreeAction() {
693 super(showElementTreeAction);
694 }
695
696 ShowElementTreeAction(String nm) {
697 super(nm);
698 }
699
700 public void actionPerformed(ActionEvent e) {
701 if(elementTreeFrame == null) {
702 // Create a frame containing an instance of
703 // ElementTreePanel.
704 try {
705 String title = resources.getString
706 ("ElementTreeFrameTitle");
707 elementTreeFrame = new JFrame(title);
708 } catch (MissingResourceException mre) {
709 elementTreeFrame = new JFrame();
710 }
711
712 elementTreeFrame.addWindowListener(new WindowAdapter() {
713 public void windowClosing(WindowEvent weeee) {
714 elementTreeFrame.setVisible(false);
715 }
716 });
717 Container fContentPane = elementTreeFrame.getContentPane();
718
719 fContentPane.setLayout(new BorderLayout());
720 elementTreePanel = new ElementTreePanel(getEditor());
721 fContentPane.add(elementTreePanel);
722 elementTreeFrame.pack();
723 }
724 elementTreeFrame.show();
725 }
726 }
727
728 /**
729 * Thread to load a file into the text storage model
730 */
731 class FileLoader extends Thread {
732
733 FileLoader(File f, Document doc) {
734 setPriority(4);
735 this.f = f;
736 this.doc = doc;
737 }
738
739 public void run() {
740 try {
741 // initialize the statusbar
742 status.removeAll();
743 JProgressBar progress = new JProgressBar();
744 progress.setMinimum(0);
745 progress.setMaximum((int) f.length());
746 status.add(progress);
747 status.revalidate();
748
749 // try to start reading
750 Reader in = new FileReader(f);
751 char[] buff = new char[4096];
752 int nch;
753 while ((nch = in.read(buff, 0, buff.length)) != -1) {
754 doc.insertString(doc.getLength(), new String(buff, 0, nch), null);
755 progress.setValue(progress.getValue() + nch);
756 }
757 }
758 catch (IOException e) {
759 final String msg = e.getMessage();
760 SwingUtilities.invokeLater(new Runnable() {
761 public void run() {
762 JOptionPane.showMessageDialog(getFrame(),
763 "Could not open file: " + msg,
764 "Error opening file",
765 JOptionPane.ERROR_MESSAGE);
766 }
767 });
768 }
769 catch (BadLocationException e) {
770 System.err.println(e.getMessage());
771 }
772 doc.addUndoableEditListener(undoHandler);
773 // we are done... get rid of progressbar
774 status.removeAll();
775 status.revalidate();
776
777 resetUndoManager();
778
779 if (elementTreePanel != null) {
780 SwingUtilities.invokeLater(new Runnable() {
781 public void run() {
782 elementTreePanel.setEditor(getEditor());
783 }
784 });
785 }
786 }
787
788 Document doc;
789 File f;
790 }
791
792 /**
793 * Thread to save a document to file
794 */
795 class FileSaver extends Thread {
796 Document doc;
797 File f;
798
799 FileSaver(File f, Document doc) {
800 setPriority(4);
801 this.f = f;
802 this.doc = doc;
803 }
804
805 public void run() {
806 try {
807 // initialize the statusbar
808 status.removeAll();
809 JProgressBar progress = new JProgressBar();
810 progress.setMinimum(0);
811 progress.setMaximum((int) doc.getLength());
812 status.add(progress);
813 status.revalidate();
814
815 // start writing
816 Writer out = new FileWriter(f);
817 Segment text = new Segment();
818 text.setPartialReturn(true);
819 int charsLeft = doc.getLength();
820 int offset = 0;
821 while (charsLeft > 0) {
822 doc.getText(offset, Math.min(4096, charsLeft), text);
823 out.write(text.array, text.offset, text.count);
824 charsLeft -= text.count;
825 offset += text.count;
826 progress.setValue(offset);
827 try {
828 Thread.sleep(10);
829 } catch (InterruptedException e) {
830 e.printStackTrace();
831 }
832 }
833 out.flush();
834 out.close();
835 }
836 catch (IOException e) {
837 final String msg = e.getMessage();
838 SwingUtilities.invokeLater(new Runnable() {
839 public void run() {
840 JOptionPane.showMessageDialog(getFrame(),
841 "Could not save file: " + msg,
842 "Error saving file",
843 JOptionPane.ERROR_MESSAGE);
844 }
845 });
846 }
847 catch (BadLocationException e) {
848 System.err.println(e.getMessage());
849 }
850 // we are done... get rid of progressbar
851 status.removeAll();
852 status.revalidate();
853 }
854 }
855}