1 22 package salsa.richtext; 23 24 import java.util.*; 25 import java.awt.*; 26 import java.awt.event.*; 27 import java.io.*; 28 import java.net.*; 29 import javax.swing.*; 30 import javax.swing.event.*; 31 import javax.swing.text.*; 32 import javax.swing.text.html.*; 33 import javax.swing.undo.*; 34 35 import houston.*; 36 import houston.swing.*; 37 38 import salsa.menu.*; 39 import salsa.font.*; 40 import salsa.util.*; 41 import salsa.html.*; 42 43 class HtmlStyle 44 { 45 HTML.Tag _tag; 46 String _name; 47 48 49 public HtmlStyle( HTML.Tag tag, String name ) 50 { 51 _tag = tag; 52 _name = name; 53 } 54 55 56 public HTML.Tag getTag() 57 { 58 return _tag; 59 } 60 61 62 public String getName() 63 { 64 return _name; 65 } 66 67 68 public String toString() 69 { 70 return getName() + " <" + getTag() + ">"; 71 } 72 } 73 74 abstract class SimpleAction extends AbstractAction 75 { 76 77 78 public SimpleAction( String name ) 79 { 80 super( name ); 81 } 82 83 84 public abstract void execute(); 85 86 87 public void actionPerformed( ActionEvent ev ) 88 { 89 execute(); 90 } 91 } 92 93 public class RichTextBox extends JPanel 94 implements ChangeListener 95 { 96 97 98 static Logger T = Logger.getLogger( RichTextBox.class ); 99 100 public final static String APP_TITLE = "Rich Text Box"; 101 102 JFrame _frame; 103 104 105 public JFrame getFrame() 106 { 107 return _frame; 108 } 109 110 111 JTextPane _editor; 112 113 114 public JTextPane getEditor() 115 { 116 return _editor; 117 } 118 119 120 CustomHTMLEditorKit _kit; 121 MutableHTMLDocument _doc; 122 StyleSheet _styleSheet; 123 124 JTextArea _source; 125 JScrollPane _sourceTab; 126 JTabbedPane _tab; 127 128 JComboBox _fontNamesCombo; 129 JComboBox _fontSizesCombo; 130 JComboBox _stylesCombo; 131 132 String _fontNames[]; 133 String _fontSizes[]; 134 135 public static HtmlStyle[] STYLES = 136 { 137 new HtmlStyle( HTML.Tag.P, "Paragraph" ), 138 new HtmlStyle( HTML.Tag.H1, "Heading 1" ), 139 new HtmlStyle( HTML.Tag.H2, "Heading 2" ), 140 new HtmlStyle( HTML.Tag.H3, "Heading 3" ), 141 new HtmlStyle( HTML.Tag.H4, "Heading 4" ), 142 new HtmlStyle( HTML.Tag.H5, "Heading 5" ), 143 new HtmlStyle( HTML.Tag.H6, "Heading 6" ), 144 new HtmlStyle( HTML.Tag.ADDRESS, "Address" ), 145 new HtmlStyle( HTML.Tag.PRE, "Preformat" ) 146 }; 147 148 FontChooser _fontChooser; 149 150 JToggleButton _boldButton; 151 JToggleButton _italicButton; 152 JToggleButton _underlineButton; 153 154 ColorMenu _foregroundMenu; 155 156 Action _cmdCut; 157 Action _cmdCopy; 158 Action _cmdPaste; 159 Action _cmdUndo; 160 Action _cmdRedo; 161 162 Action _cmdOpen; 163 Action _cmdSaveAs; 164 Action _cmdSave; 165 Action _cmdNew; 166 Action _cmdInsertImage; 167 Action _cmdInsertHyperlink; 168 Action _cmdFont; 169 Action _cmdFind; 170 Action _cmdReplace; 171 172 UndoManager _undoManager = new UndoManager(); 173 174 boolean _skipUpdate = false; 175 176 String _fontName = ""; 177 int _fontSize = 0; 178 179 int _xStart = -1; 180 int _xEnd = -1; 182 184 FindDialog _findDialog; 185 JFileChooser _chooser; 186 File _file; 187 188 boolean _isDirty = false; 189 191 WebAddressDialog _addressDialog; 192 193 194 public JFileChooser getFileChooser() 195 { 196 return _chooser; 197 } 198 199 200 public File getFile() 201 { 202 return _file; 203 } 204 205 206 public void newFile() 207 { 208 _doc = ( MutableHTMLDocument ) _kit.createDocument(); 209 _styleSheet = _doc.getStyleSheet(); 210 _editor.setDocument( _doc ); 211 212 syncStyleAttributes( 0 ); 213 214 _doc.addUndoableEditListener( new Undoer() ); 215 _doc.addDocumentListener( new UpdateListener() ); 216 _isDirty = false; 217 218 setFile( null ); 219 } 220 221 222 public void openFile() 223 { 224 if( !canCloseDocument() ) 225 return; 226 227 if( _chooser.showOpenDialog( getFrame() ) != JFileChooser.APPROVE_OPTION ) 228 return; 229 230 File file = _chooser.getSelectedFile(); 231 232 if( file == null || !file.isFile() ) 234 return; 235 236 openFileWorker( file ); 237 } 238 239 240 public void openFileWorker( File file ) 241 { 242 Reader r = null; 243 try 244 { 245 setCursor( Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) ); 246 247 r = new InputStreamReader( new FileInputStream( file ) ); 248 _doc = ( MutableHTMLDocument ) _kit.createDocument(); 249 _styleSheet = _doc.getStyleSheet(); 250 _kit.read( r, _doc, 0 ); 251 _editor.setDocument( _doc ); 252 253 _editor.setCaretPosition( 1 ); 254 syncStyleAttributes( 1 ); 255 256 _doc.addUndoableEditListener( new Undoer() ); 257 _doc.addDocumentListener( new UpdateListener() ); 258 _isDirty = false; 259 260 setFile( file ); 261 Status.info( "file '" + file.getName() + "' successfully opened" ); 262 } 263 catch( BadLocationException bex ) 264 { 265 Status.error( "*** failed to open file '" + file.getName() + "': " + bex.toString() ); 266 } 267 catch( IOException ioex ) 268 { 269 Status.error( "*** failed to open file '" + file.getName() + "': " + ioex.toString() ); 270 } 271 finally 272 { 273 if( r != null ) 274 try 275 { 276 r.close(); 277 } 278 catch( IOException ioex ) 279 {} 280 281 setCursor( Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR ) ); 282 } 283 } 284 285 286 public boolean saveFile() 287 { 288 return saveFileWorker( false ); 289 } 290 291 292 public boolean saveFileAs() 293 { 294 return saveFileWorker( true ); 295 } 296 297 298 private boolean saveFileWorker( boolean saveAs ) 299 { 300 303 if( !saveAs && !_isDirty ) 304 return true; 305 306 if( saveAs || _file == null ) 307 { 308 if( _chooser.showSaveDialog( getFrame() ) != JFileChooser.APPROVE_OPTION ) 309 return false; 310 311 File file = _chooser.getSelectedFile(); 312 313 if( file == null || !file.isFile() ) 314 return false; 315 316 setFile( file ); 317 } 318 319 Writer w = null; 320 try 321 { 322 setCursor( Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) ); 323 324 w = new OutputStreamWriter( new FileOutputStream( _file ) ); 325 326 _kit.write( w, _doc, 0, _doc.getLength() ); 327 _isDirty = false; 328 329 Status.info( "file '" + _file.getName() + "' successfully saved" ); 330 return true; 331 } 332 catch( BadLocationException bex ) 333 { 334 Status.error( "*** failed to save file '" + _file.getName() + "': " + bex.toString() ); 335 return false; 336 } 337 catch( IOException ioex ) 338 { 339 Status.error( "*** failed to save file '" + _file.getName() + "': " + ioex.toString() ); 340 return false; 341 } 342 finally 343 { 344 if( w != null ) 345 try 346 { 347 w.close(); 348 } 349 catch( IOException ioex ) 350 {} 351 352 setCursor( Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR ) ); 353 } 354 } 355 356 357 public void setFile( File file ) 358 { 359 _file = file; 360 361 363 getFrame().setTitle( getDocumentName() + " - " + APP_TITLE ); 364 } 365 366 367 public String getDocumentName() 368 { 369 String name; 370 371 if( _file == null ) 372 name = "Untitled"; 373 else 374 { 375 name = _file.getName(); 376 377 if( _file.getParentFile() != null ) 379 name += " (" + _file.getParentFile().getAbsolutePath() + ")"; 380 } 381 382 return name; 383 } 384 385 386 public RichTextBox( JFrame frame ) 387 { 388 _frame = frame; 389 390 _source = new JTextArea(); 391 _source.setFont( FontUtils.getMonospacedFont( 12 ) ); 392 393 createEditor(); 394 395 _chooser = new JFileChooser(); 396 _chooser.setCurrentDirectory( new File( "." ) ); 398 399 _addressDialog = new WebAddressDialog( getFrame(), APP_TITLE, _chooser ); 400 401 _tab = new JTabbedPane( JTabbedPane.BOTTOM ); 402 _tab.add( new JScrollPane( _editor ), "Normal" ); 403 _tab.add( _sourceTab = new JScrollPane( _source ), "Source" ); 404 405 _tab.addChangeListener( this ); 406 407 setLayout( new BorderLayout() ); 408 add( _tab, BorderLayout.CENTER ); 409 410 GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment(); 411 _fontNames = genv.getAvailableFontFamilyNames(); 412 413 _fontSizes = new String []{"8", "9", "10", "11", "12", 414 "14", "16", "18", "20", "22", "24", "26", "28", "36", "48", "72"}; 415 416 _fontChooser = new FontChooser( getFrame(), _fontNames, _fontSizes ); 417 418 createActions(); 419 420 JToolBar mainToolBar = createMainToolBar(); 422 JToolBar styleToolBar = createStyleToolBar(); 423 424 JPanel toolBarPanel = new JPanel( new BorderLayout() ); 425 toolBarPanel.add( mainToolBar, BorderLayout.NORTH ); 426 toolBarPanel.add( styleToolBar, BorderLayout.SOUTH ); 427 428 JMenuBar menuBar = createMenuBar(); 429 430 JPanel topPanel = new JPanel( new BorderLayout() ); 431 topPanel.add( menuBar, BorderLayout.NORTH ); 432 topPanel.add( toolBarPanel, BorderLayout.SOUTH ); 433 434 add( topPanel, BorderLayout.NORTH ); 435 436 _editor.addCaretListener( 437 new CaretListener() 438 { 439 public void caretUpdate( CaretEvent ev ) 440 { 441 syncStyleAttributes( ev.getDot() ); 442 } 443 } ); 444 445 newFile(); 447 } 448 449 450 private void createEditor() 451 { 452 _editor = new JTextPane(); 453 _kit = new CustomHTMLEditorKit(); 454 _editor.setEditorKit( _kit ); 455 _doc = ( MutableHTMLDocument ) _kit.createDocument(); 456 _styleSheet = _doc.getStyleSheet(); 457 _editor.setDocument( _doc ); 458 459 _doc.addDocumentListener( new UpdateListener() ); 460 _isDirty = false; 461 462 _editor.addFocusListener( 464 new FocusListener() 465 { 466 public void focusGained( FocusEvent ev ) 467 { 468 int len = _editor.getDocument().getLength(); 469 470 if( _xStart >= 0 && _xEnd >= 0 && _xStart < len && _xEnd < len ) 471 { 472 if( _editor.getCaretPosition() == _xStart ) 473 { 474 _editor.setCaretPosition( _xEnd ); 475 _editor.moveCaretPosition( _xStart ); 476 } 477 else 478 _editor.select( _xStart, _xEnd ); 479 } 480 } 481 482 483 public void focusLost( FocusEvent ev ) 484 { 485 _xStart = _editor.getSelectionStart(); 486 _xEnd = _editor.getSelectionEnd(); 487 } 488 } ); 489 } 490 491 492 private JMenuBar createMenuBar() 493 { 494 496 _foregroundMenu = new ColorMenu( "Text Color" ); 497 _foregroundMenu.setColor( _editor.getForeground() ); 498 _foregroundMenu.setMnemonic( 'C' ); 499 _foregroundMenu.addActionListener( 500 new ActionListener() 501 { 502 public void actionPerformed( ActionEvent ev ) 503 { 504 MutableAttributeSet attr = new SimpleAttributeSet(); 505 StyleConstants.setForeground( attr, _foregroundMenu.getColor() ); 506 setStyleAttributes( attr ); 507 } 508 } ); 509 510 _foregroundMenu.addMenuListener( 511 new MenuListener() 512 { 513 public void menuSelected( MenuEvent ev ) 514 { 515 int pos = _editor.getCaretPosition(); 516 AttributeSet attr = _doc.getCharacterElement( pos ).getAttributes(); 517 Color color = StyleConstants.getForeground( attr ); 518 _foregroundMenu.setColor( color ); 519 } 520 521 522 public void menuDeselected( MenuEvent ev ) { } 523 524 525 public void menuCanceled( MenuEvent ev ) { } 526 } ); 527 528 JMenuBar menuBar = new JMenuBar(); 529 530 JMenu fileMenu = new JMenu( "File" ); 531 fileMenu.setMnemonic( 'F' ); 532 533 JMenuItem mi = new JMenuItem( "New" ); 534 mi.setMnemonic( 'N' ); 535 mi.addActionListener( _cmdNew ); 536 fileMenu.add( mi ); 537 538 mi = new JMenuItem( "Open..." ); 539 mi.setMnemonic( 'O' ); 540 mi.addActionListener( _cmdOpen ); 541 fileMenu.add( mi ); 542 543 mi = new JMenuItem( "Save" ); 544 mi.setMnemonic( 'S' ); 545 mi.addActionListener( _cmdSave ); 546 fileMenu.add( mi ); 547 548 mi = new JMenuItem( "Save As..." ); 549 mi.setMnemonic( 'A' ); 550 mi.addActionListener( _cmdSaveAs ); 551 fileMenu.add( mi ); 552 553 fileMenu.addSeparator(); 554 555 mi = new JMenuItem( "Page Layout..." ); 556 mi.setMnemonic( 'Y' ); 557 fileMenu.add( mi ); 558 559 mi = new JMenuItem( "Print..." ); 560 mi.setMnemonic( 'P' ); 561 fileMenu.add( mi ); 562 563 JMenu editMenu = new JMenu( "Edit" ); 564 editMenu.setMnemonic( 'E' ); 565 566 mi = new JMenuItem( "Undo" ); 567 mi.setMnemonic( 'U' ); 568 mi.addActionListener( _cmdUndo ); 569 editMenu.add( mi ); 570 571 mi = new JMenuItem( "Redo" ); 572 mi.setMnemonic( 'R' ); 573 mi.addActionListener( _cmdRedo ); 574 editMenu.add( mi ); 575 576 editMenu.addSeparator(); 577 578 mi = new JMenuItem( "Cut" ); 579 mi.setMnemonic( 'T' ); 580 mi.addActionListener( _cmdCut ); 581 editMenu.add( mi ); 582 583 mi = new JMenuItem( "Copy" ); 584 mi.setMnemonic( 'C' ); 585 mi.addActionListener( _cmdCopy ); 586 editMenu.add( mi ); 587 588 mi = new JMenuItem( "Paste" ); 589 mi.setMnemonic( 'P' ); 590 mi.addActionListener( _cmdPaste ); 591 editMenu.add( mi ); 592 593 mi = new JMenuItem( "Delete" ); 594 mi.setMnemonic( 'D' ); 595 editMenu.add( mi ); 596 597 editMenu.addSeparator(); 598 599 mi = new JMenuItem( "Select All" ); 600 mi.setMnemonic( 'S' ); 601 editMenu.add( mi ); 602 603 editMenu.addSeparator(); 604 605 mi = new JMenuItem( "Find..." ); 606 mi.setMnemonic( 'F' ); 607 mi.addActionListener( _cmdFind ); 608 editMenu.add( mi ); 609 610 mi = new JMenuItem( "Replace..." ); 611 mi.setMnemonic( 'R' ); 612 mi.addActionListener( _cmdReplace ); 613 editMenu.add( mi ); 614 615 JMenu insertMenu = new JMenu( "Insert" ); 616 insertMenu.setMnemonic( 'I' ); 617 618 mi = new JMenuItem( "Hyperlink..." ); 619 mi.setMnemonic( 'L' ); 620 mi.addActionListener( _cmdInsertHyperlink ); 621 insertMenu.add( mi ); 622 623 mi = new JMenuItem( "Image..." ); 624 mi.setMnemonic( 'I' ); 625 mi.addActionListener( _cmdInsertImage ); 626 insertMenu.add( mi ); 627 628 JMenu formatMenu = new JMenu( "Format" ); 629 formatMenu.setMnemonic( 'F' ); 630 631 mi = new JMenuItem( "Font..." ); 632 mi.setMnemonic( 'F' ); 633 mi.addActionListener( _cmdFont ); 634 formatMenu.add( mi ); 635 636 formatMenu.add( _foregroundMenu ); 637 638 menuBar.add( fileMenu ); 639 menuBar.add( editMenu ); 640 menuBar.add( insertMenu ); 641 menuBar.add( formatMenu ); 642 643 return menuBar; 644 } 645 646 647 private JToolBar createMainToolBar() 648 { 649 JToolBar toolBar = new JToolBar(); 650 toolBar.setFloatable( false ); 651 652 JButton bt = new JButton( "New" ); 653 bt.addActionListener( _cmdNew ); 654 bt.setToolTipText( "New File" ); 655 toolBar.add( bt ); 656 657 bt = new JButton( "Open" ); 658 bt.addActionListener( _cmdOpen ); 659 bt.setToolTipText( "Open File" ); 660 toolBar.add( bt ); 661 662 bt = new JButton( "Save" ); 663 bt.addActionListener( _cmdSave ); 664 bt.setToolTipText( "Save File" ); 665 toolBar.add( bt ); 666 667 toolBar.addSeparator(); 668 669 bt = new JButton( "Cut" ); 670 bt.addActionListener( _cmdCut ); 671 bt.setToolTipText( "Cut Selection To Clipboard" ); 672 toolBar.add( bt ); 673 674 bt = new JButton( "Copy" ); 675 bt.addActionListener( _cmdCopy ); 676 bt.setToolTipText( "Copy Selection To Clipboard" ); 677 toolBar.add( bt ); 678 679 bt = new JButton( "Paste" ); 680 bt.addActionListener( _cmdPaste ); 681 bt.setToolTipText( "Paste From Clipboard" ); 682 toolBar.add( bt ); 683 684 return toolBar; 685 } 686 687 688 private JToolBar createStyleToolBar() 689 { 690 JToolBar toolBar = new JToolBar(); 691 toolBar.setFloatable( false ); 692 693 _stylesCombo = new JComboBox( STYLES ); 694 _stylesCombo.setMaximumSize( new Dimension( 150, 23 ) ); 695 _stylesCombo.setRequestFocusEnabled( false ); 696 _stylesCombo.addActionListener( 697 new ActionListener() 698 { 699 public void actionPerformed( ActionEvent e ) 700 { 701 HtmlStyle style = ( HtmlStyle ) _stylesCombo.getSelectedItem(); 702 if( style == null ) 703 return; 704 705 MutableAttributeSet attr = new SimpleAttributeSet(); 706 attr.addAttribute( StyleConstants.NameAttribute, style.getTag() ); 707 setStyleAttributes( attr, true ); 708 _editor.grabFocus(); 709 } 710 } ); 711 712 _fontNamesCombo = new JComboBox( _fontNames ); 713 _fontNamesCombo.setMaximumSize( new Dimension( 200, 23 ) ); 714 715 _fontNamesCombo.addActionListener( 716 new ActionListener() 717 { 718 public void actionPerformed( ActionEvent ev ) 719 { 720 _fontName = _fontNamesCombo.getSelectedItem().toString(); 721 MutableAttributeSet attr = new SimpleAttributeSet(); 722 StyleConstants.setFontFamily( attr, _fontName ); 723 setStyleAttributes( attr ); 724 _editor.grabFocus(); 725 } 726 } ); 727 728 _fontSizesCombo = new JComboBox( _fontSizes ); 729 _fontSizesCombo.setMaximumSize( new Dimension( 50, 23 ) ); 730 _fontSizesCombo.setEditable( true ); 731 732 _fontSizesCombo.addActionListener( 733 new ActionListener() 734 { 735 public void actionPerformed( ActionEvent ev ) 736 { 737 int fontSize = 0; 738 try 739 { 740 fontSize = Integer.parseInt( _fontSizesCombo.getSelectedItem().toString() ); 741 } 742 catch( NumberFormatException nex ) 743 { 744 Status.error( "*** selected font size is not a number: " + nex.toString() ); 745 return; 746 } 747 748 _fontSize = fontSize; 749 MutableAttributeSet attr = new SimpleAttributeSet(); 750 StyleConstants.setFontSize( attr, fontSize ); 751 setStyleAttributes( attr ); 752 _editor.grabFocus(); 753 } 754 } ); 755 756 _boldButton = new JToggleButton( "Bold" ); 757 _boldButton.addActionListener( 758 new ActionListener() 759 { 760 public void actionPerformed( ActionEvent ev ) 761 { 762 MutableAttributeSet attr = new SimpleAttributeSet(); 763 StyleConstants.setBold( attr, _boldButton.isSelected() ); 764 setStyleAttributes( attr ); 765 _editor.grabFocus(); 766 } 767 } ); 768 769 _italicButton = new JToggleButton( "Italic" ); 770 _italicButton.addActionListener( 771 new ActionListener() 772 { 773 public void actionPerformed( ActionEvent ev ) 774 { 775 MutableAttributeSet attr = new SimpleAttributeSet(); 776 StyleConstants.setItalic( attr, _italicButton.isSelected() ); 777 setStyleAttributes( attr ); 778 _editor.grabFocus(); 779 } 780 } ); 781 782 _underlineButton = new JToggleButton( "Underline" ); 783 _underlineButton.addActionListener( 784 new ActionListener() 785 { 786 public void actionPerformed( ActionEvent ev ) 787 { 788 MutableAttributeSet attr = new SimpleAttributeSet(); 789 StyleConstants.setUnderline( attr, _underlineButton.isSelected() ); 790 setStyleAttributes( attr ); 791 _editor.grabFocus(); 792 } 793 } ); 794 795 toolBar.add( _stylesCombo ); 796 toolBar.addSeparator(); 797 toolBar.add( _boldButton ); 798 toolBar.add( _italicButton ); 799 toolBar.add( _underlineButton ); 800 toolBar.addSeparator(); 801 toolBar.add( _fontNamesCombo ); 802 toolBar.add( _fontSizesCombo ); 803 toolBar.addSeparator(); 804 toolBar.add( new JButton( "Left" ) ); 805 toolBar.add( new JButton( "Center" ) ); 806 toolBar.add( new JButton( "Right" ) ); 807 toolBar.add( new JButton( "Justify" ) ); 808 809 return toolBar; 810 } 811 812 813 private void createActions() 814 { 815 _cmdOpen = 816 new SimpleAction( "Open..." ) 817 { 818 public void execute() 819 { 820 openFile(); 821 } 822 }; 823 824 _cmdSaveAs = 825 new SimpleAction( "Save As..." ) 826 { 827 public void execute() 828 { 829 saveFileAs(); 830 } 831 }; 832 833 _cmdSave = 834 new SimpleAction( "Save" ) 835 { 836 public void execute() 837 { 838 saveFile(); 839 } 840 }; 841 842 _cmdNew = 843 new SimpleAction( "New" ) 844 { 845 public void execute() 846 { 847 if( !canCloseDocument() ) 848 return; 849 850 newFile(); 851 } 852 }; 853 854 _cmdCut = 855 new SimpleAction( "Cut" ) 856 { 857 public void execute() 858 { 859 getEditor().cut(); 860 } 861 }; 862 863 _cmdCopy = 864 new SimpleAction( "Copy" ) 865 { 866 public void execute() 867 { 868 getEditor().copy(); 869 } 870 }; 871 872 _cmdPaste = 873 new SimpleAction( "Paste" ) 874 { 875 public void execute() 876 { 877 getEditor().paste(); 878 } 879 }; 880 881 _cmdUndo = 882 new SimpleAction( "Undo" ) 883 { 884 public void execute() 885 { 886 onUndo(); 887 } 888 }; 889 890 _cmdRedo = 891 new SimpleAction( "Redo" ) 892 { 893 public void execute() 894 { 895 onRedo(); 896 } 897 }; 898 899 _cmdInsertImage = 900 new SimpleAction( "Image..." ) 901 { 902 public void execute() 903 { 904 if( _addressDialog.showDialog( 905 "Please enter the image's web address (URL):", "" ) != JOptionPane.OK_OPTION ) 906 return; 907 908 String url = _addressDialog.getAddress(); 909 910 try 911 { 912 ImageIcon icon = new ImageIcon( new URL( url ) ); 913 914 int w = icon.getIconWidth(); 915 int h = icon.getIconHeight(); 916 917 if( w <= 0 || h <= 0 ) 918 { 919 Status.error( "*** failed to read image using URL " + url ); 920 return; 921 } 922 923 MutableAttributeSet attr = new SimpleAttributeSet(); 924 attr.addAttribute( StyleConstants.NameAttribute, HTML.Tag.IMG ); 925 attr.addAttribute( HTML.Attribute.SRC, url ); 926 attr.addAttribute( HTML.Attribute.HEIGHT, Integer.toString( h ) ); 927 attr.addAttribute( HTML.Attribute.WIDTH, Integer.toString( w ) ); 928 929 int p = _editor.getCaretPosition(); 930 _doc.insertString( p, " ", attr ); 931 } 932 catch( Exception ex ) 933 { 934 Status.error( "*** failed to insert image: " + ex.toString() ); 935 } 936 } 937 }; 938 939 _cmdInsertHyperlink = 940 new SimpleAction( "Hyperlink..." ) 941 { 942 public void execute() 943 { 944 String oldHref = null; 945 int p = _editor.getCaretPosition(); 946 AttributeSet attr = _doc.getCharacterElement( p ).getAttributes(); 947 AttributeSet anchor = ( AttributeSet ) attr.getAttribute( HTML.Tag.A ); 948 if( anchor != null ) 949 oldHref = ( String ) anchor.getAttribute( HTML.Attribute.HREF ); 950 951 if( _addressDialog.showDialog( 952 "Please enter the hyperlink's web address (URL):", oldHref ) != JOptionPane.OK_OPTION ) 953 return; 954 955 String newHref = _addressDialog.getAddress(); 956 957 SimpleAttributeSet attr2 = new SimpleAttributeSet(); 958 attr2.addAttribute( StyleConstants.NameAttribute, HTML.Tag.A ); 959 attr2.addAttribute( HTML.Attribute.HREF, newHref ); 960 setStyleAttributes( attr2, true ); 961 _editor.grabFocus(); 962 } 963 }; 964 965 _cmdFont = 966 new SimpleAction( "Font..." ) 967 { 968 public void execute() 969 { 970 AttributeSet attr = _doc.getCharacterElement( 971 _editor.getCaretPosition() ).getAttributes(); 972 _fontChooser.setAttributes( attr ); 973 974 WindowUtils.centerOnWindow( getFrame(), _fontChooser ); 975 _fontChooser.show(); 976 977 if( _fontChooser.getOption() == JOptionPane.OK_OPTION ) 978 { 979 setStyleAttributes( _fontChooser.getAttributes() ); 980 syncStyleAttributes( _editor.getCaretPosition() ); 981 } 982 } 983 }; 984 985 _cmdFind = 986 new SimpleAction( "Find..." ) 987 { 988 public void execute() 989 { 990 if( _findDialog == null ) 991 _findDialog = new FindDialog( getFrame(), 0 ); 992 else 993 _findDialog.setSelectedTabIndex( 0 ); 994 995 WindowUtils.centerOnWindow( getFrame(), _findDialog ); 996 _findDialog.show(); 997 } 998 }; 999 1000 _cmdReplace = 1001 new SimpleAction( "Replace..." ) 1002 { 1003 public void execute() 1004 { 1005 if( _findDialog == null ) 1006 _findDialog = new FindDialog( getFrame(), 1 ); 1007 else 1008 _findDialog.setSelectedTabIndex( 1 ); 1009 1010 WindowUtils.centerOnWindow( getFrame(), _findDialog ); 1011 _findDialog.show(); 1012 } 1013 }; 1014 } 1015 1016 1017 private void onUndo() 1018 { 1019 try 1020 { 1021 _undoManager.undo(); 1022 } 1023 catch( CannotUndoException cex ) 1024 { 1025 Status.error( "*** unable to undo: " + cex.toString() ); 1026 } 1027 updateUndoMenus(); 1028 } 1029 1030 1031 private void onRedo() 1032 { 1033 try 1034 { 1035 _undoManager.redo(); 1036 } 1037 catch( CannotRedoException cex ) 1038 { 1039 Status.error( "*** undable to redo: " + cex.toString() ); 1040 } 1041 updateUndoMenus(); 1042 } 1043 1044 1045 private void updateUndoMenus() 1046 { 1047 if( _undoManager.canUndo() ) 1048 { 1049 _cmdUndo.setEnabled( true ); 1050 _cmdUndo.putValue( Action.NAME, _undoManager.getUndoPresentationName() ); 1051 } 1052 else 1053 { 1054 _cmdUndo.setEnabled( false ); 1055 _cmdUndo.putValue( Action.NAME, "Undo" ); 1056 } 1057 1058 if( _undoManager.canRedo() ) 1059 { 1060 _cmdRedo.setEnabled( true ); 1061 _cmdRedo.putValue( Action.NAME, _undoManager.getRedoPresentationName() ); 1062 } 1063 else 1064 { 1065 _cmdRedo.setEnabled( false ); 1066 _cmdRedo.putValue( Action.NAME, "Redo" ); 1067 } 1068 } 1069 1070 1071 class Undoer implements UndoableEditListener 1072 { 1073 public Undoer() 1074 { 1075 _undoManager.die(); 1076 updateUndoMenus(); 1077 } 1078 1079 1080 public void undoableEditHappened( UndoableEditEvent ev ) 1081 { 1082 _undoManager.addEdit( ev.getEdit() ); 1083 updateUndoMenus(); 1084 } 1085 } 1086 1087 1088 private void syncStyleAttributes( int pos ) 1089 { 1090 _skipUpdate = true; 1091 1092 Element ep = _doc.getParagraphElement( pos ); 1093 HTML.Tag attrName = ( HTML.Tag ) ep.getAttributes().getAttribute( StyleConstants.NameAttribute ); 1094 int index = -1; 1095 if( attrName != null ) 1096 { 1097 for( int i = 0; i < STYLES.length; i++ ) 1098 { 1099 if( STYLES[i].getTag().equals( attrName ) ) 1100 { 1101 index = i; 1102 break; 1103 } 1104 } 1105 } 1106 _stylesCombo.setSelectedIndex( index ); 1107 1108 AttributeSet attr = _doc.getCharacterElement( pos ).getAttributes(); 1109 String fontName = StyleConstants.getFontFamily( attr ); 1110 if( !_fontName.equals( fontName ) ) 1111 { 1112 _fontName = fontName; 1113 _fontNamesCombo.setSelectedItem( _fontName ); 1114 } 1115 1116 int fontSize = StyleConstants.getFontSize( attr ); 1117 if( _fontSize != fontSize ) 1118 { 1119 _fontSize = fontSize; 1120 _fontSizesCombo.setSelectedItem( "" + _fontSize ); 1121 } 1122 1123 boolean isBold = StyleConstants.isBold( attr ); 1124 if( isBold != _boldButton.isSelected() ) 1125 _boldButton.setSelected( isBold ); 1126 1127 boolean isItalic = StyleConstants.isItalic( attr ); 1128 if( isItalic != _italicButton.isSelected() ) 1129 _italicButton.setSelected( isItalic ); 1130 1131 boolean isUnderline = StyleConstants.isUnderline( attr ); 1132 if( isUnderline != _underlineButton.isSelected() ) 1133 _underlineButton.setSelected( isUnderline ); 1134 1135 Style style = _doc.getLogicalStyle( pos ); 1136 if( style != null ) 1137 { 1138 String styleName = style.getName(); 1139 _stylesCombo.setSelectedItem( styleName ); 1140 } 1141 else 1142 { 1143 } 1145 1146 _skipUpdate = false; 1147 } 1148 1149 1150 private void setStyleAttributes( AttributeSet attr ) 1151 { 1152 setStyleAttributes( attr, false ); 1153 } 1154 1155 1156 private void setStyleAttributes( AttributeSet attr, boolean setParagraphAttributes ) 1157 { 1158 if( _skipUpdate ) 1160 return; 1161 1162 int xStart = _editor.getSelectionStart(); 1163 int xEnd = _editor.getSelectionEnd(); 1164 1165 if( !_editor.hasFocus() ) 1166 { 1167 xStart = _xStart; 1168 xEnd = _xEnd; 1169 } 1170 1171 if( setParagraphAttributes ) 1172 _doc.setParagraphAttributes( xStart, xEnd - xStart, attr, false ); 1173 else if( xStart != xEnd ) 1174 _doc.setCharacterAttributes( xStart, xEnd - xStart, attr, false ); 1175 else 1176 _kit.getInputAttributes().addAttributes( attr ); 1178 } 1179 1180 1181 boolean canCloseDocument() 1182 { 1183 if( !_isDirty ) 1184 return true; 1185 1186 int result = JOptionPane.showConfirmDialog( this, 1187 "Save changes to " + getDocumentName() + " before closing?", APP_TITLE, 1188 JOptionPane.YES_NO_CANCEL_OPTION, 1189 JOptionPane.INFORMATION_MESSAGE ); 1190 1191 switch ( result ) 1192 { 1193 case JOptionPane.YES_OPTION: 1194 if( !saveFile() ) 1195 return false; 1197 else 1198 return true; 1199 case JOptionPane.NO_OPTION: 1200 return true; 1201 case JOptionPane.CANCEL_OPTION: 1202 default: 1203 return false; 1204 } 1205 } 1206 1207 1208 class UpdateListener implements DocumentListener 1209 { 1210 public void insertUpdate( DocumentEvent ev ) 1211 { 1212 _isDirty = true; 1213 } 1214 1215 1216 public void removeUpdate( DocumentEvent ev ) 1217 { 1218 _isDirty = true; 1219 } 1220 1221 1222 public void changedUpdate( DocumentEvent ev ) 1223 { 1224 _isDirty = true; 1225 } 1226 } 1227 1228 1229 Component _prevSelectedTab; 1230 Component _selectedTab; 1231 1232 1233 public void stateChanged( ChangeEvent ev ) 1234 { 1235 T.debug( "tab changed - user clicked on tab" ); 1236 1237 1239 _prevSelectedTab = _selectedTab; 1240 _selectedTab = _tab.getSelectedComponent(); 1241 1242 if( _prevSelectedTab == _sourceTab ) 1243 { 1244 1246 try 1247 { 1248 StringReader reader = new StringReader( _source.getText() ); 1249 1250 _doc = ( MutableHTMLDocument ) _kit.createDocument(); 1251 _styleSheet = _doc.getStyleSheet(); 1252 _kit.read( reader, _doc, 0 ); 1253 reader.close(); 1254 1255 _editor.setDocument( _doc ); 1256 _editor.revalidate(); 1257 _editor.repaint(); 1258 1259 _isDirty = true; 1260 } 1261 catch( Exception ex ) 1262 { 1263 Status.error( "*** failed to update HTML document using HTML source: " + ex.toString() ); 1264 } 1265 } 1266 else if( _selectedTab == _sourceTab ) 1267 { 1268 try 1269 { 1270 _source.setText( "" ); 1271 1272 StringWriter writer = new StringWriter(); 1273 _kit.write( writer, _doc, 0, _doc.getLength() ); 1274 writer.close(); 1275 1276 _source.setText( writer.toString() ); 1277 } 1278 catch( Exception ex ) 1279 { 1280 Status.error( "*** failed to get HTML source: " + ex.toString() ); 1281 } 1282 } 1283 } 1284} 1285 | Popular Tags |