001 package jigcell.compare.ui; 002 003 import java.awt.BorderLayout; 004 import java.awt.Component; 005 import java.awt.Container; 006 import java.awt.Cursor; 007 import java.awt.Dimension; 008 import java.awt.EventQueue; 009 import java.awt.Frame; 010 import java.awt.Toolkit; 011 import java.awt.Window; 012 import java.awt.event.ActionEvent; 013 import java.awt.event.ActionListener; 014 import java.awt.event.ComponentEvent; 015 import java.awt.event.ComponentListener; 016 import java.awt.event.MouseEvent; 017 import java.awt.event.MouseListener; 018 import java.awt.event.WindowEvent; 019 import java.awt.event.WindowListener; 020 import java.awt.event.WindowStateListener; 021 import java.beans.PropertyChangeEvent; 022 import java.lang.reflect.Field; 023 import java.util.ArrayList; 024 import java.util.Arrays; 025 import java.util.HashSet; 026 import java.util.Iterator; 027 import java.util.List; 028 import java.util.Map; 029 import javax.swing.BorderFactory; 030 import javax.swing.ImageIcon; 031 import javax.swing.JButton; 032 import javax.swing.JComponent; 033 import javax.swing.JDialog; 034 import javax.swing.JFrame; 035 import javax.swing.JLabel; 036 import javax.swing.JMenu; 037 import javax.swing.JMenuBar; 038 import javax.swing.JPanel; 039 import javax.swing.JScrollPane; 040 import javax.swing.JTabbedPane; 041 import javax.swing.JToolBar; 042 import javax.swing.JWindow; 043 import javax.swing.LookAndFeel; 044 import javax.swing.SwingUtilities; 045 import javax.swing.Timer; 046 import javax.swing.UIManager; 047 import javax.swing.event.ChangeEvent; 048 import javax.swing.event.ChangeListener; 049 import jigcell.compare.IConfigEditor; 050 import jigcell.compare.ITab; 051 import jigcell.compare.impl.Compare; 052 import jigcell.compare.impl.Config; 053 import jigcell.compare.impl.ProxyBuilder; 054 055 /** 056 * The Comparator frontend. 057 * 058 * <p> 059 * This code is licensed under the DARPA BioCOMP Open Source License. See LICENSE for more details. 060 * </p> 061 * 062 * @author Nicholas Allen 063 */ 064 065 public class CompareFrontEnd extends Compare implements ChangeListener, ComponentListener, ITab, WindowListener, WindowStateListener { 066 067 /** 068 * Property key for look and feel class 069 */ 070 071 public final static String CONFIG_LOOKFEEL = "CompareFrontEnd.lookAndFeel"; 072 073 /** 074 * Property key for tab config names 075 */ 076 077 public final static String CONFIG_TABCONFIG = "CompareFrontEnd.tabs"; 078 079 /** 080 * Property key for window coordinates 081 */ 082 083 public final static String CONFIG_WINDOWCOORDINATES = "CompareFrontEnd.windowCoordinates"; 084 085 /** 086 * Property key for window maximized state 087 */ 088 089 public final static String CONFIG_WINDOWMAXIMIZED = "CompareFrontEnd.windowMaximized"; 090 091 /** 092 * Initial dimensions of the Comparator window 093 */ 094 095 protected final static Dimension DIMENSION_WINDOW = new Dimension (880, 660); 096 097 /** 098 * Number of screen buffers to request 099 */ 100 101 protected final static int COUNT_FRAMEBUFFER = 2; 102 103 /** 104 * Default menu bar name 105 */ 106 107 protected final static String MENUBAR_DEFAULT = "default"; 108 109 /** 110 * Method signature of tabs 111 */ 112 113 private final static Class SIGNATURE_TAB [] = new Class [] {Compare.class, String.class}; 114 115 /** 116 * Provides interface help for the view 117 */ 118 119 protected InterfaceBuilder manager; 120 121 /** 122 * Main interface 123 */ 124 125 protected JTabbedPane pane; 126 127 /** 128 * Toolbar 129 */ 130 131 protected JToolBar toolbar; 132 133 /** 134 * Menu builder 135 */ 136 137 protected MenuBuilder menuManager; 138 139 /** 140 * Component of the tab to add to pane 141 */ 142 143 private Component tabComponent; 144 145 /** 146 * Currently selected tab in the display frame 147 */ 148 149 private ITab selectedTab; 150 151 /** 152 * Tab to add to pane 153 */ 154 155 private ITab tabImplementation; 156 157 /** 158 * Current tab accessory 159 */ 160 161 private ITabAccessory currentTabAccessory; 162 163 /** 164 * Default tab accessory 165 */ 166 167 private ITabAccessory defaultTabAccessory; 168 169 /** 170 * Display frame for frontend shell 171 */ 172 173 private JFrame displayFrame; 174 175 /** 176 * Splash screen while loading 177 */ 178 179 private SplashScreen splash; 180 181 /** 182 * Information about a Comparator tab. 183 */ 184 185 protected static class FrontEndTabInfo extends TabInfo { 186 187 /** 188 * Configures the tab 189 */ 190 191 private final IConfigEditor config; 192 193 /** 194 * Tab toolbar tools 195 */ 196 197 private final JButton tools []; 198 199 /** 200 * About the tab 201 */ 202 203 private final JDialog about; 204 205 /** 206 * Tab menus 207 */ 208 209 private final JMenu menus []; 210 211 /** 212 * Tab accessory 213 */ 214 215 private ITabAccessory accessory; 216 217 /** 218 * Creates a new tab description. 219 * 220 * @param tab Tab 221 * @param menus Tab menus 222 * @param tools Tab toolbar tools 223 * @param about About dialog for the tab 224 * @param config Configure dialog the tab 225 */ 226 227 public FrontEndTabInfo (ITab tab, JMenu menus [], JButton tools [], JDialog about, IConfigEditor config) { 228 super (tab); 229 this.menus = menus; 230 this.tools = tools; 231 this.about = about; 232 this.config = config; 233 } 234 235 /** 236 * About the tab. 237 */ 238 239 public JDialog getAbout () { 240 return about; 241 } 242 243 /** 244 * The tab accessory. 245 */ 246 247 public ITabAccessory getAccessory () { 248 return accessory; 249 } 250 251 /** 252 * Configures the tab. 253 */ 254 255 public IConfigEditor getConfigEditor () { 256 return config; 257 } 258 259 /** 260 * The tab menus. 261 */ 262 263 public JMenu [] getMenus () { 264 return menus; 265 } 266 267 /** 268 * The tab toolbar tools. 269 */ 270 271 public JButton [] getTools () { 272 return tools; 273 } 274 275 /** 276 * Sets the tab accessory. If the accessory is null, the default accessory will be used. 277 * 278 * @param accessory Tab accessory 279 */ 280 281 public void setAccessory (ITabAccessory accessory) { 282 this.accessory = accessory; 283 } 284 } 285 286 /** 287 * A splash screen to display while things are loading. 288 */ 289 290 protected class SplashScreen extends JWindow implements ActionListener, MouseListener, Runnable { 291 292 /** 293 * Length to display splash screen after loading is done 294 */ 295 296 protected final static int TIMER_LENGTH = 1000; 297 298 /** 299 * Lock for controlling the splash screen 300 */ 301 302 public final Object SPLASH_LOCK = new Object (); 303 304 /** 305 * Minimum size splash to display 306 */ 307 308 protected Dimension SIZE_MINIMUM = new Dimension (400, 300); 309 310 /** 311 * Status display 312 */ 313 314 protected JLabel messageLabel; 315 316 /** 317 * Status text to update to 318 */ 319 320 protected String message; 321 322 /** 323 * Delay timer 324 */ 325 326 protected Timer timer; 327 328 /** 329 * Lock for controlling update manipulation 330 */ 331 332 private final Object UPDATE_LOCK = new Object (); 333 334 public SplashScreen (JFrame compare, String message) { 335 super (compare); 336 SplashScreen.this.setCursor (Cursor.getPredefinedCursor (Cursor.WAIT_CURSOR)); 337 timer = new Timer (TIMER_LENGTH, this); 338 Container pane = this.getContentPane (); 339 pane.add (new JLabel (UIManager.getIcon ("jigcell.compare.splashScreen")), BorderLayout.CENTER); 340 messageLabel = new JLabel (message); 341 pane.add (messageLabel, BorderLayout.SOUTH); 342 this.getRootPane ().setBorder (BorderFactory.createRaisedBevelBorder ()); 343 this.pack (); 344 Dimension size = getSize (); 345 if (size.width < SIZE_MINIMUM.width) 346 size.width = SIZE_MINIMUM.width; 347 if (size.height < SIZE_MINIMUM.height) 348 size.height = SIZE_MINIMUM.height; 349 setSize (size); 350 this.setLocationRelativeTo (null); 351 this.addWindowListener (CompareFrontEnd.this); 352 this.addMouseListener (this); 353 this.setVisible (true); 354 } 355 356 public void actionPerformed (ActionEvent e) { 357 this.setVisible (false); 358 this.dispose (); 359 } 360 361 /** 362 * Destroys the splash screen. 363 */ 364 365 public void dispose () { 366 synchronized (SPLASH_LOCK) { 367 this.removeMouseListener (this); 368 if (timer != null) { 369 timer.stop (); 370 timer.removeActionListener (this); 371 timer = null; 372 } 373 super.dispose (); 374 } 375 } 376 377 public void mouseClicked (MouseEvent e) {} 378 379 public void mouseEntered (MouseEvent e) {} 380 381 public void mouseExited (MouseEvent e) {} 382 383 public void mousePressed (MouseEvent e) { 384 this.setVisible (false); 385 this.dispose (); 386 } 387 388 public void mouseReleased (MouseEvent e) {} 389 390 public void run () { 391 messageLabel.setText (message); 392 message = null; 393 } 394 395 /** 396 * Changes the status message displayed on the splash screen. 397 * 398 * @param message Message to display 399 */ 400 401 public void updateStatus (String message) { 402 updateStatus (message, false); 403 } 404 405 /** 406 * Changes the status message displayed on the splash screen. 407 * 408 * @param message Message to display 409 * @param done Whether the load has finished 410 */ 411 412 public void updateStatus (String message, boolean done) { 413 synchronized (UPDATE_LOCK) { 414 if (done && timer != null) 415 timer.start (); 416 if (EventQueue.isDispatchThread ()) 417 messageLabel.setText (message); 418 else 419 try { 420 this.message = message; 421 message (message); 422 EventQueue.invokeAndWait (this); 423 } catch (Exception e) { 424 assertion (getString ("CompareFrontEnd.splashUpdateError"), e); 425 } 426 } 427 } 428 } 429 430 static { 431 new IconLoader (CompareFrontEnd.class); 432 } 433 434 /** 435 * Starts a new graphical Comparator. 436 * 437 * @param args Program arguments 438 */ 439 440 public static void main (String args []) { 441 new CompareFrontEnd (args == null || args.length == 0 ? DEFAULT_CONFIG : args [0]); 442 } 443 444 /** 445 * Finds the class for a given look and feel name. 446 * 447 * @param name Name of look and feel 448 */ 449 450 protected static Class findLookAndFeelClass (String name) { 451 if (name == null) 452 return null; 453 UIManager.LookAndFeelInfo looks [] = UIManager.getInstalledLookAndFeels (); 454 for (int looksCount = 0, looksLength = looks.length; looksCount < looksLength; looksCount++) { 455 UIManager.LookAndFeelInfo look = looks [looksCount]; 456 if (name.equals (look.getName ())) 457 try { 458 return Class.forName (look.getClassName ()); 459 } catch (Exception e) { 460 assertion (getString ("CompareFrontEnd.lookFeelCreateError"), e); 461 return null; 462 } 463 } 464 return null; 465 } 466 467 /** 468 * Installs a new look and feel if it has not already been registered. 469 * 470 * @param name Name of new look and feel 471 * @param className Class for new look and feel 472 */ 473 474 protected static void installLookAndFeel (String name, String className) { 475 if (name == null || className == null) 476 throw new IllegalArgumentException (); 477 UIManager.LookAndFeelInfo looks [] = UIManager.getInstalledLookAndFeels (); 478 int looksLength = looks.length; 479 for (int looksCount = 0; looksCount < looksLength; looksCount++) 480 if (className.equals (looks [looksCount].getClassName ())) 481 return; 482 UIManager.LookAndFeelInfo newLooks [] = new UIManager.LookAndFeelInfo [looksLength + 1]; 483 System.arraycopy (looks, 0, newLooks, 0, looksLength); 484 newLooks [looksLength] = new UIManager.LookAndFeelInfo (name, className); 485 UIManager.setInstalledLookAndFeels (newLooks); 486 } 487 488 /** 489 * Creates a new graphical Comparator. 490 * 491 * @param configFile Name of configuration file 492 */ 493 494 public CompareFrontEnd (String configFile) { 495 super (configFile); 496 try { 497 Config config = getConfig (); 498 Config compareConfig = config.getConfig (Config.convertToClassMarker (this)); 499 for (Iterator iterator = Config.convertToList (config.findValue (configMarkers, CONFIG_TABCONFIG, false, false, false)).iterator (); 500 iterator.hasNext (); ) 501 try { 502 String marker = Config.createInstanceMarker (); 503 Config tabConfig = compareConfig.getConfig ((String) iterator.next ()); 504 String name = tabConfig.findValue (null, CONFIG_CLASSNAME, false, false, false); 505 config.setConfig (marker, tabConfig); 506 if (splash != null) 507 synchronized (splash.SPLASH_LOCK) { 508 splash.updateStatus (formatString ("CompareFrontEnd.tabLoad", name)); 509 } 510 ITab tab = (ITab) Class.forName (name).getDeclaredConstructor (SIGNATURE_TAB).newInstance (new Object [] {this, marker}); 511 tab.readConfiguration (ITab.STATE_ENDINITIALIZE); 512 } catch (Exception e) { 513 shellHandleException (MESSAGE_WARNING, getString ("CompareFrontEnd.tabLoadError"), e); 514 } 515 } catch (Exception e) { 516 assertion (getString ("CompareFrontEnd.tabListError"), e); 517 } 518 if (splash != null) 519 synchronized (splash.SPLASH_LOCK) { 520 splash.updateStatus (getString ("CompareFrontEnd.startupFinished"), true); 521 splash = null; 522 } 523 } 524 525 /** 526 * Adds a tab to the main Comparator display. 527 * 528 * @param component Contents of the tab 529 */ 530 531 public TabInfo addTab (ITab component) { 532 return addTab (component, component instanceof Component, null, null); 533 } 534 535 /** 536 * Adds a tab to the main Comparator display. 537 * 538 * @param component Contents of the tab 539 * @param wrapped Whether component should be wrapped in a scrollpane 540 * @param menus Menus to be displayed when tab is active 541 * @param tools Tools to place in toolbar 542 */ 543 544 public TabInfo addTab (ITab component, boolean wrapped, JMenu menus [], JButton tools []) { 545 TabInfo info = new FrontEndTabInfo (component, menus, tools, component.createAboutDialog (), component.createConfigEditor ()); 546 synchronized (TAB_LOCK) { 547 updateResourceMap (RESOURCE_TABS, component, info); 548 if (!(component instanceof Component)) 549 return info; 550 tabImplementation = component; 551 if (wrapped) { 552 JScrollPane pane = new JScrollPane ((Component) component, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, 553 JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); 554 pane.setBorder (InterfaceBuilder.BORDER_NOTHING); 555 tabComponent = pane; 556 } else 557 tabComponent = (Component) component; 558 if (EventQueue.isDispatchThread ()) 559 addTabToView (); 560 else 561 try { 562 EventQueue.invokeAndWait (ProxyBuilder.proxyRunnable (this, "addTabToView")); 563 } catch (Exception e) { 564 assertion (getString ("CompareFrontEnd.tabAddError"), e); 565 } 566 } 567 return info; 568 } 569 570 public void componentHidden (ComponentEvent e) {} 571 572 public void componentMoved (ComponentEvent e) { 573 saveWindowSettings (); 574 } 575 576 public void componentResized (ComponentEvent e) { 577 saveWindowSettings (); 578 } 579 580 public void componentShown (ComponentEvent e) {} 581 582 /** 583 * Starts configuring a Comparator tab. 584 */ 585 586 public void configure (ITab tab) { 587 IConfigEditor configEditor = getTabInfo (tab).getConfigEditor (); 588 if (configEditor == null) 589 return; 590 JDialog configDialog = configEditor.createConfigDialog (); 591 configDialog.setLocationRelativeTo (null); 592 if (!configDialog.isVisible ()) 593 configDialog.setVisible (true); 594 configDialog.requestFocus (); 595 } 596 597 /** 598 * {@inheritDoc} 599 */ 600 601 public JDialog createAboutDialog () { 602 return PanelTab.createAboutDialogForTab (CompareFrontEnd.class, manager); 603 } 604 605 /** 606 * {@inheritDoc} 607 */ 608 609 public IConfigEditor createConfigEditor () { 610 IConfigEditor editor = new ConfigEditor (this, this, configMarkers, getConfig ().getConfig (Config.convertToClassMarker (this))); 611 editor.addOption (CONFIG_DATASOURCE, getString ("CompareFrontEnd.optionData"), String [].class); 612 UIManager.LookAndFeelInfo looks [] = UIManager.getInstalledLookAndFeels (); 613 int looksLength = looks.length; 614 String lookNames [] = new String [looksLength]; 615 for (int looksCount = 0; looksCount < looksLength; looksCount++) 616 lookNames [looksCount] = looks [looksCount].getName (); 617 lookNames = (String []) new HashSet (Arrays.asList (lookNames)).toArray (new String [0]); 618 Arrays.sort (lookNames); 619 editor.addOption (CONFIG_LOOKFEEL, getString ("CompareFrontEnd.optionLook"), lookNames, true, Class.class); 620 return editor; 621 } 622 623 /** 624 * The frontend shell display frame. Returns null if this is a headless Comparator. 625 */ 626 627 public JFrame getDisplayFrame () { 628 return displayFrame; 629 } 630 631 /** 632 * {@inheritDoc} 633 */ 634 635 public String getName () { 636 return getString ("CompareFrontEnd.tabName"); 637 } 638 639 /** 640 * The accessory component for a tab. 641 */ 642 643 public ITabAccessory getTabAccessory (ITab tab) { 644 ITabAccessory accessory = getTabInfo (tab).getAccessory (); 645 return accessory == null ? defaultTabAccessory : accessory; 646 } 647 648 /** 649 * The currently selected tab in the display frame. 650 */ 651 652 public ITab getSelectedTab () { 653 return selectedTab; 654 } 655 656 protected void propertyChange (PropertyChangeEvent e) { 657 if (PROPERTY_CONFIG_EDIT.equals (e.getPropertyName ())) 658 readConfiguration (STATE_RUNNING); 659 super.propertyChange (e); 660 } 661 662 /** 663 * {@inheritDoc} 664 */ 665 666 public void readConfiguration (String state) { 667 updateLookAndFeel (state); 668 } 669 670 /** 671 * Selects one of the tabs for the display frame. 672 * 673 * @param tab Underlying tab component 674 */ 675 676 public void selectTab (ITab tab) { 677 if (selectedTab == tab) 678 return; 679 for (Iterator iterator = getTabMap ().keySet ().iterator (); iterator.hasNext (); ) { 680 Object component = iterator.next (); 681 if (component == tab || component instanceof JScrollPane && ((JScrollPane) component).getViewport ().getView () == tab) { 682 pane.setSelectedComponent ((Component) component); 683 return; 684 } 685 } 686 } 687 688 /** 689 * Sets the accessory component displayed for a tab. If the accessory is null, the default accessory will be displayed. 690 * 691 * @param tab Comparator tab 692 * @param accessory Accessory 693 */ 694 695 public void setTabAccessory (ITab tab, ITabAccessory accessory) { 696 FrontEndTabInfo info = getTabInfo (tab); 697 info.setAccessory (accessory); 698 if (tab == getSelectedTab ()) 699 updateViewAccessory (info); 700 displayFrame.validate (); 701 displayFrame.repaint (); 702 } 703 704 /** 705 * Handle an exception in a Comparator component. 706 * 707 * @param type Exception type 708 * @param message Display message 709 * @param t Throwable 710 */ 711 712 public void shellHandleException (String type, Object message, Throwable t) { 713 manager.showErrorDialog (type, message, t); 714 assertion (message instanceof String ? (String) message : null, t); 715 } 716 717 /** 718 * Handle an informational message in a Comparator component. 719 * 720 * @param type Message type 721 * @param message Display message 722 */ 723 724 public void shellHandleMessage (String type, Object message) { 725 manager.showMessageDialog (type, message); 726 if (message instanceof String) 727 message ((String) message); 728 } 729 730 public void stateChanged (ChangeEvent e) { 731 if (e.getSource () != pane) 732 return; 733 Map tabMap = getTabMap (); 734 Component component = pane.getSelectedComponent (); 735 FrontEndTabInfo info = (FrontEndTabInfo) tabMap.get (component); 736 if (info == null && component instanceof JScrollPane) 737 info = (FrontEndTabInfo) tabMap.get (((JScrollPane) component).getViewport ().getView ()); 738 selectedTab = info.getTab (); 739 updateViewTools (info); 740 updateViewMenus (info); 741 updateViewAccessory (info); 742 displayFrame.validate (); 743 displayFrame.repaint (); 744 } 745 746 public void windowActivated (WindowEvent e) {} 747 748 public void windowClosed (WindowEvent e) { 749 if (e.getWindow () instanceof SplashScreen) 750 displayFrame.setVisible (true); 751 } 752 753 public void windowClosing (WindowEvent e) { 754 if (e.getWindow () == displayFrame) 755 exit (0); 756 } 757 758 public void windowDeactivated (WindowEvent e) {} 759 760 public void windowDeiconified (WindowEvent e) {} 761 762 public void windowIconified (WindowEvent e) {} 763 764 public void windowOpened (WindowEvent e) { 765 if (e.getWindow () == displayFrame) { 766 try { 767 Thread.currentThread ().setContextClassLoader (Compare.class.getClassLoader ()); 768 } catch (Exception _e) { 769 assertion (getString ("Compare.startupLoaderError"), _e); 770 } 771 if (splash != null) 772 synchronized (splash.SPLASH_LOCK) { 773 Window children [] = displayFrame.getOwnedWindows (); 774 for (int i = 0, l = children.length; i < l; i++) { 775 Window child = children [i]; 776 if (child != null && child instanceof SplashScreen) { 777 child.setVisible (false); 778 child.dispose (); 779 } 780 } 781 } 782 displayFrame.createBufferStrategy (COUNT_FRAMEBUFFER); 783 displayFrame.toFront (); 784 displayFrame.requestFocusInWindow (); 785 } 786 } 787 788 public void windowStateChanged (WindowEvent e) { 789 saveWindowSettings (); 790 } 791 792 protected void saveWindowSettings () { 793 Config config = getConfig ().getConfig (Config.convertToClassMarker (this)); 794 int state = displayFrame.getExtendedState (); 795 config.setValue (CONFIG_WINDOWMAXIMIZED, (state & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH); 796 if (state == JFrame.NORMAL) 797 config.setValue (CONFIG_WINDOWCOORDINATES, displayFrame.getBounds ()); 798 firePropertyChange (Compare.PROPERTY_CONFIG_EDIT); 799 } 800 801 /** 802 * Displays information about a Comparator tab. 803 */ 804 805 protected void about (ITab tab) { 806 JDialog aboutDialog = getTabInfo (tab).getAbout (); 807 if (aboutDialog == null) 808 return; 809 aboutDialog.setLocationRelativeTo (null); 810 if (!aboutDialog.isVisible ()) 811 aboutDialog.setVisible (true); 812 aboutDialog.requestFocus (); 813 } 814 815 /** 816 * Inserts a new tab into the interface. 817 */ 818 819 protected void addTabToView () { 820 pane.addTab (tabImplementation.getName (), tabComponent); 821 if (pane.getTabCount () == 1) 822 selectTab (tabImplementation); 823 tabImplementation = null; 824 tabComponent = null; 825 } 826 827 /** 828 * Creates the menus that will appear for all tabs. 829 */ 830 831 protected JMenu [] createSharedMenus () { 832 return menuManager.createMenus (MENUBAR_DEFAULT); 833 } 834 835 /** 836 * Exits from the Comparator. 837 * 838 * @param status Status code 839 */ 840 841 protected void exit (int status) { 842 if (manager.showChoiceDialog (getString ("CompareFrontEnd.exitTitle"), getString ("CompareFrontEnd.exitQuestion"), 843 new String [] {getString ("CompareFrontEnd.exitButtonYes"), getString ("CompareFrontEnd.exitButtonNo")}, 1) != 0) 844 return; 845 displayFrame.setVisible (false); 846 displayFrame.dispose (); 847 super.exit (status); 848 } 849 850 /** 851 * The tab info for a tab. 852 * 853 * @param tab Tab 854 */ 855 856 protected FrontEndTabInfo getTabInfo (ITab tab) { 857 for (Iterator iterator = getTabMap ().entrySet ().iterator (); iterator.hasNext (); ) { 858 Map.Entry entry = (Map.Entry) iterator.next (); 859 Object component = entry.getKey (); 860 if (component == tab || component instanceof JScrollPane && ((JScrollPane) component).getViewport ().getView () == tab) 861 return (FrontEndTabInfo) entry.getValue (); 862 } 863 return null; 864 } 865 866 /** 867 * {@inheritDoc} 868 */ 869 870 protected void shellStableHook () { 871 super.shellStableHook (); 872 updateLookAndFeel (STATE_INITIALIZE); 873 splash = new SplashScreen (displayFrame, getString ("CompareFrontEnd.splashStartup")); 874 displayFrame = new JFrame (getString ("CompareFrontEnd.title")); 875 displayFrame.setIconImage (((ImageIcon) UIManager.getIcon ("jigcell.compare.programIcon")).getImage ()); 876 Config config = getConfig ().getConfig (Config.convertToClassMarker (this)); 877 try { 878 displayFrame.setBounds (Config.convertToRectangle (config.findValue (configMarkers, CONFIG_WINDOWCOORDINATES, false, false, false))); 879 } catch (Exception e) { 880 Compare.warning ("Unable to restore window position.", e); 881 displayFrame.setSize (DIMENSION_WINDOW); 882 displayFrame.setLocationRelativeTo (null); 883 } 884 if (Config.convertToBoolean (config.findValue (configMarkers, CONFIG_WINDOWMAXIMIZED, false, false, false), false)) 885 displayFrame.setExtendedState (JFrame.MAXIMIZED_BOTH); 886 displayFrame.getToolkit ().setDynamicLayout (true); 887 displayFrame.setDefaultCloseOperation (JFrame.DO_NOTHING_ON_CLOSE); 888 displayFrame.addComponentListener (this); 889 displayFrame.addWindowListener (this); 890 displayFrame.addWindowStateListener (this); 891 manager = new InterfaceBuilder (this, displayFrame); 892 defaultTabAccessory = new MemoryMonitorAccessory (); 893 JPanel contentPane = new JPanel (new BorderLayout ()); 894 pane = new JTabbedPane (JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT); 895 contentPane.add (pane, BorderLayout.CENTER); 896 pane.addChangeListener (this); 897 toolbar = new JToolBar (); 898 contentPane.add (toolbar, BorderLayout.NORTH); 899 toolbar.setRollover (true); 900 displayFrame.setContentPane (contentPane); 901 menuManager = new MenuBuilder (this); 902 synchronized (TAB_LOCK) { 903 updateResourceMap (RESOURCE_TABS, this, 904 new FrontEndTabInfo (this, createSharedMenus (), null, createAboutDialog (), createConfigEditor ())); 905 } 906 } 907 908 /** 909 * {@inheritDoc} 910 */ 911 912 protected void shellStartHook () { 913 super.shellStartHook (); 914 try { 915 class FrontEndThreadGroup extends ThreadGroup { 916 public FrontEndThreadGroup () { 917 super ("compare"); 918 } 919 920 public void uncaughtException (Thread thread, Throwable t) { 921 shellHandleException (t instanceof AssertionError ? getString ("CompareFrontEnd.assertionError") : 922 getString ("CompareFrontEnd.uncaughtError"), t.getMessage (), t); 923 } 924 } 925 926 Class unsafeClass = Class.forName ("sun.misc.Unsafe"); 927 Field unsafeField = unsafeClass.getDeclaredField ("theUnsafe"); 928 unsafeField.setAccessible (true); 929 Object unsafe = unsafeField.get (null); 930 Long offset = (Long) unsafeClass.getMethod ("objectFieldOffset", new Class [] {Field.class}).invoke (unsafe, 931 new Object [] {EventQueue.class.getDeclaredField ("threadGroup")}); 932 unsafeClass.getMethod ("putObject", new Class [] {Object.class, Long.TYPE, Object.class}).invoke (unsafe, 933 new Object [] {Toolkit.getDefaultToolkit ().getSystemEventQueue (), offset, new FrontEndThreadGroup ()}); 934 } catch (Exception e) { 935 assertion (getString ("CompareFrontEnd.startupGroupError"), e); 936 } 937 } 938 939 /** 940 * Modifies the application look and feel to match the current configuration. 941 * 942 * @param state Application state 943 */ 944 945 protected void updateLookAndFeel (String state) { 946 String lookFeelName = getConfig ().findValue (configMarkers, CONFIG_LOOKFEEL, false, false, false); 947 if (lookFeelName == null) 948 return; 949 Class lookFeelClass = findLookAndFeelClass (lookFeelName); 950 if (lookFeelClass == null) 951 return; 952 LookAndFeel currentLook = UIManager.getLookAndFeel (); 953 if (currentLook != null && currentLook.getClass () == lookFeelClass) 954 return; 955 try { 956 UIManager.setLookAndFeel ((LookAndFeel) lookFeelClass.newInstance ()); 957 } catch (Exception e) { 958 shellHandleException (MESSAGE_WARNING, getString ("CompareFrontEnd.lookFeelLoadError"), e); 959 } 960 if (state != STATE_RUNNING) 961 return; 962 List windows = new ArrayList (); 963 windows.addAll (Arrays.asList (Frame.getFrames ())); 964 while (!windows.isEmpty ()) { 965 Window window = (Window) windows.remove (0); 966 windows.addAll (Arrays.asList (window.getOwnedWindows ())); 967 SwingUtilities.updateComponentTreeUI (window); 968 } 969 updateViewTools (getTabInfo (getSelectedTab ())); 970 } 971 972 /** 973 * Fills in the tab accessory using the provided tab info. 974 * 975 * @param info Tab info 976 */ 977 978 protected void updateViewAccessory (FrontEndTabInfo info) { 979 ITab tab = info.getTab (); 980 ITabAccessory accessory = getTabAccessory (tab); 981 Container contentPane = displayFrame.getContentPane (); 982 if (currentTabAccessory != null) 983 try { 984 contentPane.remove (currentTabAccessory.getDisplay ()); 985 currentTabAccessory.uninstall (); 986 } finally { 987 currentTabAccessory = null; 988 } 989 if (accessory == null) 990 return; 991 JComponent display = accessory.getDisplay (); 992 SwingUtilities.updateComponentTreeUI (display); 993 contentPane.add (display, BorderLayout.SOUTH); 994 currentTabAccessory = accessory; 995 accessory.install (this, tab); 996 } 997 998 /** 999 * Fills in the menu bar using the provided tab info. 1000 * 1001 * @param info Tab info 1002 */ 1003 1004 protected void updateViewMenus (FrontEndTabInfo info) { 1005 JMenuBar menuBar = new JMenuBar (); 1006 JMenu menus [] = getTabInfo (this).getMenus (); 1007 if (menus != null) 1008 for (int i = 0, l = menus.length; i < l; i++) 1009 menuBar.add (menus [i]); 1010 menus = info.getMenus (); 1011 if (menus != null) 1012 for (int i = 0, l = menus.length; i < l; i++) 1013 menuBar.add (menus [i]); 1014 SwingUtilities.updateComponentTreeUI (menuBar); 1015 displayFrame.setJMenuBar (menuBar); 1016 } 1017 1018 /** 1019 * Fills in the toolbar using the provided tab info. 1020 * 1021 * @param info Tab info 1022 */ 1023 1024 protected void updateViewTools (FrontEndTabInfo info) { 1025 JButton tools [] = info.getTools (); 1026 toolbar.removeAll (); 1027 if (tools == null) 1028 return; 1029 for (int i = 0, l = tools.length; i < l; i++) { 1030 JButton tool = tools [i]; 1031 if (tool == null) 1032 toolbar.addSeparator (); 1033 else { 1034 SwingUtilities.updateComponentTreeUI (tool); 1035 toolbar.add (tool); 1036 } 1037 } 1038 } 1039 }