Source for org.jfree.ui.tabbedui.AbstractTabbedUI

   1: /* ========================================================================
   2:  * JCommon : a free general purpose class library for the Java(tm) platform
   3:  * ========================================================================
   4:  *
   5:  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
   6:  * 
   7:  * Project Info:  http://www.jfree.org/jcommon/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  * 
  27:  * ---------------------
  28:  * AbstractTabbedUI.java
  29:  * ---------------------
  30:  * (C)opyright 2004, by Thomas Morgner and Contributors.
  31:  *
  32:  * Original Author:  Thomas Morgner;
  33:  * Contributor(s):   David Gilbert (for Object Refinery Limited);
  34:  *
  35:  * $Id: AbstractTabbedUI.java,v 1.10 2007/11/02 17:50:37 taqua Exp $
  36:  *
  37:  * Changes
  38:  * -------------------------
  39:  * 16-Feb-2004 : Initial version
  40:  * 07-Jun-2004 : Added standard header (DG);
  41:  */
  42: 
  43: package org.jfree.ui.tabbedui;
  44: 
  45: import java.awt.BorderLayout;
  46: import java.awt.Component;
  47: import java.awt.Window;
  48: import java.awt.event.ActionEvent;
  49: import java.beans.PropertyChangeEvent;
  50: import java.beans.PropertyChangeListener;
  51: import java.util.ArrayList;
  52: import javax.swing.AbstractAction;
  53: import javax.swing.Action;
  54: import javax.swing.JComponent;
  55: import javax.swing.JMenu;
  56: import javax.swing.JMenuBar;
  57: import javax.swing.JPanel;
  58: import javax.swing.JTabbedPane;
  59: import javax.swing.SwingConstants;
  60: import javax.swing.SwingUtilities;
  61: import javax.swing.event.ChangeEvent;
  62: import javax.swing.event.ChangeListener;
  63: 
  64: import org.jfree.util.Log;
  65: 
  66: /**
  67:  * A tabbed GUI. All views on the data are contained in tabs. 
  68:  *
  69:  * @author Thomas Morgner
  70:  */
  71: public abstract class AbstractTabbedUI extends JComponent {
  72: 
  73:     /** The menu bar property key. */
  74:     public static final String JMENUBAR_PROPERTY = "jMenuBar";
  75:     
  76:     /** The global menu property. */
  77:     public static final String GLOBAL_MENU_PROPERTY = "globalMenu";
  78: 
  79:     /**
  80:      * An exit action.
  81:      */
  82:     protected class ExitAction extends AbstractAction {
  83: 
  84:         /**
  85:          * Defines an <code>Action</code> object with a default
  86:          * description string and default icon.
  87:          */
  88:         public ExitAction() {
  89:             putValue(NAME, "Exit");
  90:         }
  91: 
  92:         /**
  93:          * Invoked when an action occurs.
  94:          *
  95:          * @param e the event.
  96:          */
  97:         public void actionPerformed(final ActionEvent e) {
  98:             attempExit();
  99:         }
 100: 
 101:     }
 102: 
 103:     /**
 104:      * A tab change handler.
 105:      */
 106:     private class TabChangeHandler implements ChangeListener {
 107: 
 108:         /** The tabbed pane to which this handler is registered. */
 109:         private final JTabbedPane pane;
 110: 
 111:         /**
 112:          * Creates a new handler.
 113:          *
 114:          * @param pane the pane.
 115:          */
 116:         public TabChangeHandler(final JTabbedPane pane) {
 117:             this.pane = pane;
 118:         }
 119: 
 120:         /**
 121:          * Invoked when the target of the listener has changed its state.
 122:          *
 123:          * @param e a ChangeEvent object
 124:          */
 125:         public void stateChanged(final ChangeEvent e) {
 126:             setSelectedEditor(this.pane.getSelectedIndex());
 127:         }
 128:     }
 129: 
 130:     /**
 131:      * A tab enable change listener.
 132:      */
 133:     private class TabEnableChangeListener implements PropertyChangeListener {
 134:         
 135:         /**
 136:          * Default constructor.
 137:          */
 138:         public TabEnableChangeListener() {
 139:         }
 140: 
 141:         /**
 142:          * This method gets called when a bound property is changed.
 143:          *
 144:          * @param evt A PropertyChangeEvent object describing the event source
 145:          *            and the property that has changed.
 146:          */
 147:         public void propertyChange(final PropertyChangeEvent evt) {
 148:             if (evt.getPropertyName().equals("enabled") == false) {
 149:                 Log.debug ("PropertyName");
 150:                 return;
 151:             }
 152:             if (evt.getSource() instanceof RootEditor == false) {
 153:                 Log.debug ("Source");
 154:                 return;
 155:             }
 156:             final RootEditor editor = (RootEditor) evt.getSource();
 157:             updateRootEditorEnabled(editor);
 158:         }
 159:     }
 160: 
 161:     /** The list of root editors. One for each tab. */
 162:     private ArrayList rootEditors;
 163:     /** The tabbed pane filling the content area. */
 164:     private JTabbedPane tabbedPane;
 165:     /** The index of the currently selected root editor. */
 166:     private int selectedRootEditor;
 167:     /** The current toolbar. */
 168:     private JComponent currentToolbar;
 169:     /** The container component for the toolbar. */
 170:     private JPanel toolbarContainer;
 171:     /** The close action assigned to this UI. */
 172:     private Action closeAction;
 173:     /** The current menu bar. */
 174:     private JMenuBar jMenuBar;
 175:     /** Whether the UI should build a global menu from all root editors. */
 176:     private boolean globalMenu;
 177: 
 178:     /**
 179:      * Default constructor.
 180:      */
 181:     public AbstractTabbedUI() {
 182:         this.selectedRootEditor = -1;
 183: 
 184:         this.toolbarContainer = new JPanel();
 185:         this.toolbarContainer.setLayout(new BorderLayout());
 186: 
 187:         this.tabbedPane = new JTabbedPane(SwingConstants.BOTTOM);
 188:         this.tabbedPane.addChangeListener(new TabChangeHandler(this.tabbedPane));
 189: 
 190:         this.rootEditors = new ArrayList();
 191: 
 192:         setLayout(new BorderLayout());
 193:         add(this.toolbarContainer, BorderLayout.NORTH);
 194:         add(this.tabbedPane, BorderLayout.CENTER);
 195: 
 196:         this.closeAction = createCloseAction();
 197:     }
 198: 
 199:     /**
 200:      * Returns the tabbed pane.
 201:      * 
 202:      * @return The tabbed pane.
 203:      */
 204:     protected JTabbedPane getTabbedPane() {
 205:         return this.tabbedPane;
 206:     }
 207: 
 208:     /**
 209:      * Defines whether to use a global unified menu bar, which contains
 210:      * all menus from all tab-panes or whether to use local menubars.
 211:      * <p>
 212:      * From an usability point of view, global menubars should be preferred,
 213:      * as this way users always see which menus are possibly available and
 214:      * do not wonder where the menus are disappearing.
 215:      *
 216:      * @return true, if global menus should be used, false otherwise.
 217:      */
 218:     public boolean isGlobalMenu() {
 219:         return this.globalMenu;
 220:     }
 221: 
 222:     /**
 223:      * Sets the global menu flag.
 224:      * 
 225:      * @param globalMenu  the flag.
 226:      */
 227:     public void setGlobalMenu(final boolean globalMenu) {
 228:         this.globalMenu = globalMenu;
 229:         if (isGlobalMenu()) {
 230:             setJMenuBar(updateGlobalMenubar());
 231:         }
 232:         else {
 233:             if (getRootEditorCount () > 0) {
 234:               setJMenuBar(createEditorMenubar(getRootEditor(getSelectedEditor())));
 235:             }
 236:         }
 237:     }
 238: 
 239:     /**
 240:      * Returns the menu bar.
 241:      * 
 242:      * @return The menu bar.
 243:      */
 244:     public JMenuBar getJMenuBar() {
 245:         return this.jMenuBar;
 246:     }
 247: 
 248:     /**
 249:      * Sets the menu bar.
 250:      * 
 251:      * @param menuBar  the menu bar.
 252:      */
 253:     protected void setJMenuBar(final JMenuBar menuBar) {
 254:         final JMenuBar oldMenuBar = this.jMenuBar;
 255:         this.jMenuBar = menuBar;
 256:         firePropertyChange(JMENUBAR_PROPERTY, oldMenuBar, menuBar);
 257:     }
 258: 
 259:     /**
 260:      * Creates a close action.
 261:      * 
 262:      * @return A close action.
 263:      */
 264:     protected Action createCloseAction() {
 265:         return new ExitAction();
 266:     }
 267: 
 268:     /**
 269:      * Returns the close action.
 270:      * 
 271:      * @return The close action.
 272:      */
 273:     public Action getCloseAction() {
 274:         return this.closeAction;
 275:     }
 276: 
 277:     /**
 278:      * Returns the prefix menus.
 279:      *
 280:      * @return The prefix menus.
 281:      */
 282:     protected abstract JMenu[] getPrefixMenus();
 283: 
 284:     /**
 285:      * The postfix menus.
 286:      *
 287:      * @return The postfix menus.
 288:      */
 289:     protected abstract JMenu[] getPostfixMenus();
 290: 
 291:     /**
 292:      * Adds menus.
 293:      *
 294:      * @param menuBar the menu bar
 295:      * @param customMenus the menus that should be added.
 296:      */
 297:     private void addMenus(final JMenuBar menuBar, final JMenu[] customMenus) {
 298:         for (int i = 0; i < customMenus.length; i++) {
 299:             menuBar.add(customMenus[i]);
 300:         }
 301:     }
 302: 
 303:     /**
 304:      * Updates the global menu bar.
 305:      * @return the fully initialized menu bar.
 306:      */
 307:     private JMenuBar updateGlobalMenubar () {
 308:       JMenuBar menuBar = getJMenuBar();
 309:       if (menuBar == null) {
 310:           menuBar = new JMenuBar();
 311:       }
 312:       else {
 313:           menuBar.removeAll();
 314:       }
 315: 
 316:       addMenus(menuBar, getPrefixMenus());
 317:       for (int i = 0; i < this.rootEditors.size(); i++)
 318:       {
 319:           final RootEditor editor = (RootEditor) this.rootEditors.get(i);
 320:           addMenus(menuBar, editor.getMenus());
 321:       }
 322:       addMenus(menuBar, getPostfixMenus());
 323:       return menuBar;
 324:     }
 325: 
 326:     /**
 327:      * Creates a menu bar.
 328:      *
 329:      * @param root
 330:      * @return A menu bar.
 331:      */
 332:     private JMenuBar createEditorMenubar(final RootEditor root) {
 333: 
 334:         JMenuBar menuBar = getJMenuBar();
 335:         if (menuBar == null) {
 336:             menuBar = new JMenuBar();
 337:         }
 338:         else {
 339:             menuBar.removeAll();
 340:         }
 341: 
 342:         addMenus(menuBar, getPrefixMenus());
 343:         if (isGlobalMenu())
 344:         {
 345:             for (int i = 0; i < this.rootEditors.size(); i++)
 346:             {
 347:                 final RootEditor editor = (RootEditor) this.rootEditors.get(i);
 348:                 addMenus(menuBar, editor.getMenus());
 349:             }
 350:         }
 351:         else
 352:         {
 353:             addMenus(menuBar, root.getMenus());
 354:         }
 355:         addMenus(menuBar, getPostfixMenus());
 356:         return menuBar;
 357:     }
 358: 
 359:     /**
 360:      * Adds a root editor.
 361:      *
 362:      * @param rootPanel the root panel.
 363:      */
 364:     public void addRootEditor(final RootEditor rootPanel) {
 365:         this.rootEditors.add(rootPanel);
 366:         this.tabbedPane.add(rootPanel.getEditorName(), rootPanel.getMainPanel());
 367:         rootPanel.addPropertyChangeListener("enabled", new TabEnableChangeListener());
 368:         updateRootEditorEnabled(rootPanel);
 369:         if (getRootEditorCount () == 1) {
 370:             setSelectedEditor(0);
 371:         }
 372:         else if (isGlobalMenu()) {
 373:             setJMenuBar(updateGlobalMenubar());
 374:         }
 375:     }
 376: 
 377:     /**
 378:      * Returns the number of root editors.
 379:      * 
 380:      * @return The count.
 381:      */
 382:     public int getRootEditorCount () {
 383:         return this.rootEditors.size();
 384:     }
 385: 
 386:     /**
 387:      * Returns the specified editor.
 388:      * 
 389:      * @param pos  the position index.
 390:      *
 391:      * @return The editor at the given position.
 392:      */
 393:     public RootEditor getRootEditor(final int pos) {
 394:         return (RootEditor) this.rootEditors.get(pos);
 395:     }
 396: 
 397:     /**
 398:      * Returns the selected editor.
 399:      * 
 400:      * @return The selected editor.
 401:      */
 402:     public int getSelectedEditor() {
 403:         return this.selectedRootEditor;
 404:     }
 405: 
 406:     /**
 407:      * Sets the selected editor.
 408:      *
 409:      * @param selectedEditor the selected editor.
 410:      */
 411:     public void setSelectedEditor(final int selectedEditor) {
 412:         final int oldEditor = this.selectedRootEditor;
 413:         if (oldEditor == selectedEditor) {
 414:             // no change - so nothing to do!
 415:             return;
 416:         }
 417:         this.selectedRootEditor = selectedEditor;
 418:         // make sure that only the selected editor is active.
 419:         // all other editors will be disabled, if needed and
 420:         // not touched if they are already in the correct state
 421: 
 422:         for (int i = 0; i < this.rootEditors.size(); i++) {
 423:             final boolean shouldBeActive = (i == selectedEditor);
 424:             final RootEditor container =
 425:                 (RootEditor) this.rootEditors.get(i);
 426:             if (container.isActive() && (shouldBeActive == false)) {
 427:                 container.setActive(false);
 428:             }
 429:         }
 430: 
 431:         if (this.currentToolbar != null) {
 432:             closeToolbar();
 433:             this.toolbarContainer.removeAll();
 434:             this.currentToolbar = null;
 435:         }
 436: 
 437:         for (int i = 0; i < this.rootEditors.size(); i++) {
 438:             final boolean shouldBeActive = (i == selectedEditor);
 439:             final RootEditor container =
 440:                 (RootEditor) this.rootEditors.get(i);
 441:             if ((container.isActive() == false) && (shouldBeActive == true)) {
 442:                 container.setActive(true);
 443:                 setJMenuBar(createEditorMenubar(container));
 444:                 this.currentToolbar = container.getToolbar();
 445:                 if (this.currentToolbar != null) {
 446:                     this.toolbarContainer.add
 447:                         (this.currentToolbar, BorderLayout.CENTER);
 448:                     this.toolbarContainer.setVisible(true);
 449:                     this.currentToolbar.setVisible(true);
 450:                 }
 451:                 else {
 452:                     this.toolbarContainer.setVisible(false);
 453:                 }
 454: 
 455:                 this.getJMenuBar().repaint();
 456:             }
 457:         }
 458:     }
 459: 
 460:     /**
 461:      * Closes the toolbar.
 462:      */
 463:     private void closeToolbar() {
 464:         if (this.currentToolbar != null) {
 465:             if (this.currentToolbar.getParent() != this.toolbarContainer) {
 466:                 // ha!, the toolbar is floating ...
 467:                 // Log.debug (currentToolbar.getParent());
 468:                 final Window w = SwingUtilities.windowForComponent(this.currentToolbar);
 469:                 if (w != null) {
 470:                     w.setVisible(false);
 471:                     w.dispose();
 472:                 }
 473:             }
 474:             this.currentToolbar.setVisible(false);
 475:         }
 476:     }
 477: 
 478:     /**
 479:      * Attempts to exit.
 480:      */
 481:     protected abstract void attempExit();
 482: 
 483:     /**
 484:      * Update handler for the enable state of the root editor.
 485:      * 
 486:      * @param editor  the editor.
 487:      */
 488:     protected void updateRootEditorEnabled(final RootEditor editor) {
 489: 
 490:         final boolean enabled = editor.isEnabled();
 491:         for (int i = 0; i < this.tabbedPane.getTabCount(); i++) {
 492:             final Component tab = this.tabbedPane.getComponentAt(i);
 493:             if (tab == editor.getMainPanel()) {
 494:                 this.tabbedPane.setEnabledAt(i, enabled);
 495:                 return;
 496:             }
 497:         }
 498:     }
 499: }