0001: /*
0002: * @(#)MultiplePageDialog.java
0003: *
0004: * Copyright 2002 - 2003 JIDE Software. All rights reserved.
0005: */
0006: package com.jidesoft.dialog;
0007:
0008: import com.jidesoft.plaf.UIDefaultsLookup;
0009: import com.jidesoft.swing.JideButton;
0010: import com.jidesoft.swing.JideScrollPane;
0011:
0012: import javax.swing.*;
0013: import javax.swing.event.*;
0014: import javax.swing.tree.*;
0015: import java.awt.*;
0016: import java.awt.event.ActionEvent;
0017: import java.beans.PropertyChangeEvent;
0018: import java.beans.PropertyChangeListener;
0019: import java.util.*;
0020:
0021: /**
0022: * MultiplePageDialogPane is a StandardDialogPane which can have multiple AbstractDialogPages.
0023: * You can choose one from four predefined styles of how to change the page visibility.
0024: * Those four styles are TAB_STYLE, ICON_STYLE, LIST_STYLE and TREE_STYLE.
0025: * <p/>
0026: * To use this class, just create a PageList of AbstractDialogPage and call setPageList() to
0027: * set to this dialog. Based on the style, the class will automatically layout those pages
0028: * correctly and hook up actions to switch based on user selection.
0029: * <p/>
0030: * As AbstractDialogPage extends AbstractPage, so you can always use PageListener to decide what to
0031: * do when page is opened, closing, closed, activated or deactivated.
0032: * <p/>
0033: * We automatically create a button panel which have three button - OK, Cancel and Apply. The
0034: * ButtonPanel listens to ButtonEvent from all the pages. You can simply fireButtonEvent in the
0035: * page to change the state of those buttons. Or if you want to create your own button panel, just
0036: * override createButtonPanel() method.
0037: * <p/>
0038: * If you choose LIST_STYLE and TREE_STYLE, you can set your own ListCellRenderer and
0039: * TreeCellRenderer. Just call setListCellRenderer() and setTreeCellRenderer(). The value passed in
0040: * the renderer is an instance of AbstractDialogPage associated with that list row or tree node.
0041: * <p/>
0042: * <code>MultiplePageDialogPane</code> has lazy loading feature. So when you are done setup the page list,
0043: * you need to call {@link #initComponents()} to initialize everything. This method will be called automatically
0044: * if the dialog pane is added to StandardDialog.
0045: */
0046: public class MultiplePageDialogPane extends StandardDialogPane {
0047: /**
0048: * Predefined style of multiple page dialog.
0049: */
0050: public static final int TAB_STYLE = 0;
0051:
0052: /**
0053: * Predefined style of multiple page dialog.
0054: */
0055: public static final int TREE_STYLE = 1;
0056:
0057: /**
0058: * Predefined style of multiple page dialog.
0059: */
0060: public static final int LIST_STYLE = 2;
0061:
0062: /**
0063: * Predefined style of multiple page dialog.
0064: */
0065: public static final int ICON_STYLE = 3;
0066:
0067: private int _style;
0068:
0069: private PageList _pageList;
0070:
0071: /**
0072: * The left pane to show the icon, list etc.
0073: * It's an index area to choose which page.
0074: */
0075: private JComponent _indexPanel;
0076:
0077: /**
0078: * The panel contains all the pages. In TAB_STYLE, it
0079: * is a tabbed pane and in other styles, it's a panel with CardLayout.
0080: */
0081: private JComponent _pagesPanel;
0082: private CardLayout _cardLayout;
0083:
0084: /**
0085: * Map that maps from page full title to tree node.
0086: * It provides a fast access from page full title to the tree node in TREE_STYLE.
0087: */
0088: private Map _titleNodeMap;
0089:
0090: private JButton _okButton;
0091: private JButton _cancelButton;
0092: private JButton _applyButton;
0093:
0094: private AbstractAction _okAction;
0095: private AbstractAction _cancelAction;
0096:
0097: private TreeCellRenderer _treeCellRenderer;
0098:
0099: private ListCellRenderer _listCellRenderer;
0100: private JTabbedPane _tabbedPane;
0101:
0102: private String _initialPageTitle;
0103: public JTree _tree;
0104:
0105: /**
0106: * Creates a non-modal MultiplePageDialog without a title and without a specified
0107: * <code>Frame</code> owner. A shared, hidden frame will be
0108: * set as the owner of the dialog.
0109: * By default TAB_STYLE is used.
0110: *
0111: * @throws java.awt.HeadlessException
0112: */
0113: public MultiplePageDialogPane() throws HeadlessException {
0114: this (TAB_STYLE);
0115: }
0116:
0117: /**
0118: * Creates a modal or non-modal MultiplePageDialog with the specified style, the specified title
0119: * and the specified owner <code>Frame</code>. If <code>owner</code>
0120: * is <code>null</code>, a shared, hidden frame will be set as the
0121: * owner of this dialog. All constructors defer to this one.
0122: *
0123: * @param style the style. It must be one of the following: TAB_STYLE, ICON_STYLE, LIST_STYLE or TREE_STYLE.
0124: * @throws java.awt.HeadlessException if GraphicsEnvironment.isHeadless()
0125: * returns true.
0126: * @see java.awt.GraphicsEnvironment#isHeadless
0127: * @see javax.swing.JComponent#getDefaultLocale
0128: */
0129: public MultiplePageDialogPane(int style) {
0130: setStyle(style);
0131: }
0132:
0133: /**
0134: * Implements the method in StandardDialog.
0135: * You can override this method to create a BannerPanel.
0136: *
0137: * @return the BannerPanel
0138: */
0139: @Override
0140: public JComponent createBannerPanel() {
0141: return null;
0142: }
0143:
0144: /**
0145: * Implements the method in StandardDialog.
0146: * You can override this method to create a ContentPanel.
0147: * By default, a JPanel with BorderLayout is created.
0148: * IndexPanel is added to WEST and PagesPanel is added to CENTER.
0149: *
0150: * @return the ContentPanel
0151: */
0152: @Override
0153: public JComponent createContentPanel() {
0154: _indexPanel = createIndexPanel();
0155: _pagesPanel = createPagesPanel();
0156: if (_pageList.getPageCount() > 0) {
0157: if (getInitialPageTitle() != null) {
0158: setCurrentPage(getInitialPageTitle());
0159: } else {
0160: setCurrentPage(_pageList.getPage(0));
0161: }
0162: }
0163: return setupContentPanel(_indexPanel, _pagesPanel);
0164: }
0165:
0166: /**
0167: * Setups the content panel. It will use the index panel and the pages panel created earlier and put it into another panel.
0168: *
0169: * @param indexPanel the index panel. It has the nagivation control to control which page to show.
0170: * @param pagesPanel the pages panel. It contains all the pages of this dialog.
0171: * @return the panel that contains both index panel and pages panel.
0172: */
0173: protected JComponent setupContentPanel(JComponent indexPanel,
0174: JComponent pagesPanel) {
0175: JPanel middlePanel = new JPanel(new BorderLayout());
0176: if (indexPanel != null) {
0177: middlePanel
0178: .add(indexPanel, BorderLayout.BEFORE_LINE_BEGINS);
0179: }
0180: if (pagesPanel != null) {
0181: middlePanel.add(pagesPanel, BorderLayout.CENTER);
0182: }
0183: return middlePanel;
0184: }
0185:
0186: /**
0187: * Creates the button panel. It has three buttons - OK, Cancel and Apply.
0188: * If you want to create your own button panel, just override this method.
0189: *
0190: * @return button panel
0191: */
0192: @Override
0193: public ButtonPanel createButtonPanel() {
0194: ButtonPanel buttonPanel = new ButtonPanel();
0195: Locale l = getLocale();
0196: _okButton = new JButton(UIDefaultsLookup.getString(
0197: "OptionPane.okButtonText", l));
0198: _cancelButton = new JButton(UIDefaultsLookup.getString(
0199: "OptionPane.cancelButtonText", l));
0200: _applyButton = new JButton();
0201: _okButton.setName(OK);
0202: _cancelButton.setName(CANCEL);
0203: _applyButton.setName(APPLY);
0204: buttonPanel
0205: .addButton(_okButton, ButtonPanel.AFFIRMATIVE_BUTTON);
0206: buttonPanel.addButton(_cancelButton, ButtonPanel.CANCEL_BUTTON);
0207: buttonPanel.addButton(_applyButton, ButtonPanel.OTHER_BUTTON);
0208:
0209: _okButton.setAction(getOKAction());
0210: _cancelButton.setAction(getCancelAction());
0211: _applyButton.setAction(new AbstractAction(ButtonResources
0212: .getResourceBundle(Locale.getDefault()).getString(
0213: "Button.apply")) {
0214: public void actionPerformed(ActionEvent e) {
0215: if (getCurrentPage() != null) {
0216: getCurrentPage().fireButtonEvent(
0217: ButtonEvent.DISABLE_BUTTON, APPLY);
0218: }
0219: }
0220: });
0221: _applyButton.setMnemonic(ButtonResources.getResourceBundle(
0222: Locale.getDefault()).getString("Button.apply.mnemonic")
0223: .charAt(0));
0224: _applyButton.setEnabled(false);
0225:
0226: setDefaultCancelAction(_cancelButton.getAction());
0227: setDefaultAction(_okButton.getAction());
0228: // getRootPane().setDefaultButton(_okButton);
0229: return buttonPanel;
0230: }
0231:
0232: /**
0233: * Gets the OK Button
0234: * only if you didn't override
0235: * the createButtonPanel() and remove the OK button.
0236: *
0237: * @return the OK Button
0238: */
0239: public JButton getOkButton() {
0240: return _okButton;
0241: }
0242:
0243: /**
0244: * Gets the cancel button.
0245: * only if you didn't override
0246: * the createButtonPanel() and remove the cancel button.
0247: *
0248: * @return the cancel button.
0249: */
0250: public JButton getCancelButton() {
0251: return _cancelButton;
0252: }
0253:
0254: /**
0255: * Gets the apply button.
0256: * only if you didn't override
0257: * the createButtonPanel() and remove the apply button.
0258: *
0259: * @return the apply button.
0260: */
0261: public JButton getApplyButton() {
0262: return _applyButton;
0263: }
0264:
0265: /**
0266: * Changes the default cancel action.
0267: *
0268: * @param cancelAction
0269: */
0270: public void setCancelAction(AbstractAction cancelAction) {
0271: if (cancelAction == null) {
0272: throw new IllegalArgumentException(
0273: "cancelAction cannot be null");
0274: } else {
0275: _cancelAction = cancelAction;
0276: setDefaultCancelAction(cancelAction);
0277: if (_cancelButton != null) {
0278: _cancelButton.setAction(cancelAction);
0279: }
0280: }
0281: }
0282:
0283: /**
0284: * Gets the cancel action.
0285: *
0286: * @return the cancel action.
0287: */
0288: public AbstractAction getCancelAction() {
0289: return _cancelAction;
0290: }
0291:
0292: /**
0293: * Changes the default OK action.
0294: *
0295: * @param okAction
0296: */
0297: public void setOKAction(AbstractAction okAction) {
0298: if (okAction == null) {
0299: throw new IllegalArgumentException(
0300: "cancelAction cannot be null");
0301: } else {
0302: _okAction = okAction;
0303: setDefaultAction(okAction);
0304: if (_okAction != null) {
0305: _okButton.setAction(okAction);
0306: }
0307: }
0308: }
0309:
0310: /**
0311: * Gets the OK action.
0312: *
0313: * @return the OK action.
0314: */
0315: public AbstractAction getOKAction() {
0316: return _okAction;
0317: }
0318:
0319: /**
0320: * Creates the pages panel. If it's TAB_STYLE, a tabbed pane will be created.
0321: * If it's any other styles, a JPanel with CardLayout will be created.
0322: *
0323: * @return a panel containing all the pages.
0324: */
0325: protected JComponent createPagesPanel() {
0326: if (_style == TAB_STYLE) {
0327: _tabbedPane = new JTabbedPane(JTabbedPane.TOP);
0328: for (int i = 0; i < _pageList.getPageCount(); i++) {
0329: AbstractDialogPage page = _pageList.getPage(i);
0330: page.addButtonListener(getButtonPanel());
0331: _tabbedPane.addTab(page.getTitle(), page.getIcon(),
0332: page, page.getDescription());
0333: _tabbedPane.setEnabledAt(i, page.isPageEnabled());
0334: final int index = i;
0335: page
0336: .addPropertyChangeListener(new PropertyChangeListener() {
0337: public void propertyChange(
0338: PropertyChangeEvent evt) {
0339: if (AbstractDialogPage.PROPERTY_PAGE_ENABLED
0340: .equals(evt.getPropertyName())) {
0341: _tabbedPane.setEnabledAt(index,
0342: Boolean.TRUE.equals(evt
0343: .getNewValue()));
0344: } else if (AbstractDialogPage.ICON_PROPERTY
0345: .equals(evt.getPropertyName())) {
0346: _tabbedPane.setIconAt(index,
0347: (Icon) evt.getNewValue());
0348: } else if (AbstractDialogPage.TITLE_PROPERTY
0349: .equals(evt.getPropertyName())) {
0350: _tabbedPane.setTitleAt(index,
0351: (String) evt.getNewValue());
0352: } else if (AbstractDialogPage.DESCRIPTION_PROPERTY
0353: .equals(evt.getPropertyName())) {
0354: _tabbedPane.setToolTipTextAt(index,
0355: (String) evt.getNewValue());
0356: }
0357: }
0358: });
0359: }
0360: _pageList.addListDataListener(new ListDataListener() {
0361: public void intervalAdded(ListDataEvent e) {
0362: for (int i = e.getIndex0(); i <= e.getIndex1(); i++) {
0363: AbstractDialogPage page = _pageList.getPage(i);
0364: _tabbedPane.insertTab(page.getTitle(), page
0365: .getIcon(), page,
0366: page.getDescription(), i);
0367: }
0368: }
0369:
0370: public void intervalRemoved(ListDataEvent e) {
0371: for (int i = e.getIndex1(); i >= e.getIndex0(); i--) {
0372: _tabbedPane.removeTabAt(i);
0373: }
0374: }
0375:
0376: public void contentsChanged(ListDataEvent e) {
0377: }
0378: });
0379: return _tabbedPane;
0380: } else {
0381: final JPanel pagesPanel = new JPanel();
0382: _cardLayout = new CardLayout();
0383: pagesPanel.setLayout(_cardLayout);
0384:
0385: for (int i = 0; i < _pageList.getPageCount(); i++) {
0386: AbstractDialogPage page = _pageList.getPage(i);
0387: page.addButtonListener(getButtonPanel());
0388: page.setName(page.getFullTitle());
0389: page
0390: .addPropertyChangeListener(new PropertyChangeListener() {
0391: public void propertyChange(
0392: PropertyChangeEvent evt) {
0393: if (AbstractDialogPage.TITLE_PROPERTY
0394: .equals(evt.getPropertyName())) {
0395: for (int j = 0; j < pagesPanel
0396: .getComponentCount(); j++) {
0397: Component c = pagesPanel
0398: .getComponent(j);
0399: Object source = evt.getSource();
0400: if (source instanceof AbstractDialogPage
0401: && c == source) {
0402: pagesPanel.remove(j);
0403: String fullTitle = ((AbstractDialogPage) source)
0404: .getFullTitle();
0405: pagesPanel
0406: .add(
0407: (AbstractDialogPage) source,
0408: fullTitle,
0409: j);
0410: ((AbstractDialogPage) source)
0411: .setName(fullTitle);
0412: getIndexPanel().repaint();
0413: break;
0414: }
0415: }
0416: }
0417: }
0418: });
0419:
0420: pagesPanel.add(page, page.getFullTitle());
0421: }
0422:
0423: _pageList.addListDataListener(new ListDataListener() {
0424: public void intervalAdded(ListDataEvent e) {
0425: for (int i = e.getIndex0(); i <= e.getIndex1(); i++) {
0426: AbstractDialogPage page = _pageList.getPage(i);
0427: page.setName(page.getFullTitle());
0428: pagesPanel.add(page, page.getFullTitle(), i);
0429: }
0430: }
0431:
0432: public void intervalRemoved(ListDataEvent e) {
0433: for (int i = e.getIndex1(); i >= e.getIndex0(); i--) {
0434: pagesPanel.remove(i);
0435: }
0436: }
0437:
0438: private void dumpPagesPanel() {
0439: for (int i = 0; i < pagesPanel.getComponentCount(); i++) {
0440: System.out.println("" + i + ": "
0441: + pagesPanel.getComponent(i).getName());
0442: }
0443: }
0444:
0445: public void contentsChanged(ListDataEvent e) {
0446: }
0447: });
0448: return pagesPanel;
0449: }
0450: }
0451:
0452: /**
0453: * Creates the index panel based on the style.
0454: *
0455: * @return the index panel.
0456: */
0457: public JComponent createIndexPanel() {
0458: switch (_style) {
0459: case ICON_STYLE:
0460: return createIconPanel();
0461: case LIST_STYLE:
0462: return createListPanel();
0463: case TREE_STYLE:
0464: return createTreePanel();
0465: case TAB_STYLE:
0466: default:
0467: return null;
0468: }
0469: }
0470:
0471: /**
0472: * Sets the page list of this dialog. User must call this method before the
0473: * dialog is set visible.
0474: *
0475: * @param pageList
0476: */
0477: public void setPageList(PageList pageList) {
0478: _pageList = pageList;
0479: }
0480:
0481: /**
0482: * Gets the page list of this dialog.
0483: */
0484: public PageList getPageList() {
0485: return _pageList;
0486: }
0487:
0488: /**
0489: * Gets the current selected page.
0490: *
0491: * @return the current selected page.
0492: */
0493: public AbstractDialogPage getCurrentPage() {
0494: return _pageList.getCurrentPage();
0495: }
0496:
0497: protected void setCurrentPage(String pageTitle) {
0498: if (_pageList != null) {
0499: setCurrentPage(_pageList.getPageByFullTitle(pageTitle));
0500: }
0501: }
0502:
0503: protected void setCurrentPage(AbstractDialogPage currentPage) {
0504: setCurrentPage(currentPage, null);
0505: }
0506:
0507: protected void setCurrentPage(AbstractDialogPage currentPage,
0508: Object source) {
0509: if (_pageList.getCurrentPage() != null
0510: && !_pageList.getCurrentPage().equals(currentPage)) {
0511: _pageList.getCurrentPage().setAllowClosing(true);
0512: _pageList.getCurrentPage().firePageEvent(source,
0513: PageEvent.PAGE_CLOSING);
0514: if (!_pageList.getCurrentPage().allowClosing()) {
0515: return;
0516: }
0517: _pageList.getCurrentPage().firePageEvent(source,
0518: PageEvent.PAGE_CLOSED);
0519: }
0520:
0521: _pageList.setCurrentPage(currentPage);
0522:
0523: if (_pageList.getCurrentPage() != null) {
0524: showCurrentPage(currentPage);
0525: if (_pageList.getCurrentPage() != null) {
0526: _pageList.getCurrentPage().firePageEvent(source,
0527: PageEvent.PAGE_OPENED);
0528: }
0529: }
0530: }
0531:
0532: /**
0533: * Displays the current page. If it is TAB_STYLE, this method
0534: * will simply select the tab that has the current page. If it is any of the other styles,
0535: * this method will show the page thiat is already added in a CardLayout in createPagePanel method.
0536: *
0537: * @param currentPage
0538: */
0539: protected void showCurrentPage(AbstractDialogPage currentPage) {
0540: if (getStyle() == TAB_STYLE) {
0541: _tabbedPane.setSelectedComponent(currentPage);
0542: } else {
0543: _cardLayout.show(_pagesPanel, currentPage.getFullTitle());
0544: }
0545: }
0546:
0547: private JComponent createTreePanel() {
0548: final DefaultMutableTreeNode root = new DefaultMutableTreeNode(
0549: "", true);
0550:
0551: _titleNodeMap = new HashMap(
0552: (int) (_pageList.getPageCount() * 0.75));
0553: for (int i = 0; i < _pageList.getPageCount(); i++) {
0554: AbstractDialogPage dialogPage = _pageList.getPage(i);
0555: addPage(dialogPage, root, false);
0556: }
0557:
0558: _tree = createTree(root);
0559: configureTree(_tree);
0560: _pageList.addListDataListener(new ListDataListener() {
0561: public void intervalAdded(ListDataEvent e) {
0562: for (int i = e.getIndex0(); i <= e.getIndex1(); i++) {
0563: AbstractDialogPage dialogPage = _pageList
0564: .getPage(i);
0565: addPage(dialogPage, (DefaultMutableTreeNode) _tree
0566: .getModel().getRoot(), true);
0567: }
0568: }
0569:
0570: public void intervalRemoved(ListDataEvent e) {
0571: // compare PageList with TitleNodeMap to find out what is missing
0572: Set set = _titleNodeMap.keySet();
0573: Vector toBeRemoved = new Vector();
0574: for (Iterator iterator = set.iterator(); iterator
0575: .hasNext();) {
0576: String title = (String) iterator.next();
0577: if (_pageList.getPageByFullTitle(title) == null) {
0578: DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) _titleNodeMap
0579: .get(title);
0580: if (treeNode != null) {
0581: toBeRemoved.add(title);
0582: DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) treeNode
0583: .getParent();
0584: if (parentNode != null) {
0585: int index = parentNode
0586: .getIndex(treeNode);
0587: parentNode.remove(treeNode);
0588: ((DefaultTreeModel) _tree.getModel())
0589: .nodesWereRemoved(
0590: parentNode,
0591: new int[] { index },
0592: new Object[] { treeNode });
0593: }
0594: }
0595: }
0596: }
0597: for (int i = 0; i < toBeRemoved.size(); i++) {
0598: _titleNodeMap.remove(toBeRemoved.get(i));
0599: }
0600: }
0601:
0602: public void contentsChanged(ListDataEvent e) {
0603: if (e.getIndex0() == -1
0604: && e.getIndex1() == -1
0605: && e.getType() == ListDataEvent.CONTENTS_CHANGED) {
0606: if (_titleNodeMap != null
0607: && _pageList.getCurrentPage() != null) {
0608: TreeNode node = (TreeNode) _titleNodeMap
0609: .get(_pageList.getCurrentPage()
0610: .getFullTitle());
0611: if (node != null) {
0612: ArrayList list = new ArrayList();
0613: while (node != null) {
0614: list.add(0, node);
0615: node = node.getParent();
0616: }
0617: TreePath treePath = new TreePath(list
0618: .toArray(new TreeNode[list.size()]));
0619: _tree.getSelectionModel().setSelectionPath(
0620: treePath);
0621: }
0622: }
0623: }
0624: }
0625: });
0626:
0627: JComponent indexPanel = new JPanel(new BorderLayout());
0628: indexPanel.add(new JScrollPane(_tree), BorderLayout.CENTER);
0629: return indexPanel;
0630: }
0631:
0632: /**
0633: * Creates tree that is used in TREE_STYLE dialog's index panel. Below is the code we used.
0634: * If you just want to have a different cell renderer, you can just call
0635: * {@link #setTreeCellRenderer(javax.swing.tree.TreeCellRenderer)} to set a new one.
0636: * <pre><code>
0637: * UIManager.put("Tree.hash", Color.white);
0638: * return new JTree(root);
0639: * </code></pre>
0640: *
0641: * @param root
0642: * @return tree
0643: */
0644: protected JTree createTree(DefaultMutableTreeNode root) {
0645: UIManager.put("Tree.hash", Color.white);
0646: return new JTree(root);
0647: }
0648:
0649: /**
0650: * Configure the JTree used in TREE_STYLE dialog. Subclass can override this method to configure the JTree
0651: * to the way you want. Below is the default implementation of this method.
0652: * <code><pre>
0653: * tree.setToggleClickCount(1);
0654: * tree.setCellRenderer(createTreeCellRenderer());
0655: * tree.setRootVisible(false);
0656: * tree.setShowsRootHandles(false);
0657: * tree.addTreeSelectionListener(new TreeSelectionListener() {
0658: * public void valueChanged(TreeSelectionEvent e) {
0659: * if (tree.getSelectionPath() == null) {
0660: * return;
0661: * }
0662: * <p/>
0663: * DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) tree.getSelectionPath().getLastPathComponent();
0664: * // comment this while block if you want the parent page shows its own page instead of showing its first child page.
0665: * while (!treeNode.isLeaf()) {
0666: * final DefaultMutableTreeNode tn = treeNode;
0667: * Runnable runnable = new Runnable() {
0668: * public void run() {
0669: * tree.expandPath(new TreePath(tn.getPath()));
0670: * }
0671: * };
0672: * SwingUtilities.invokeLater(runnable);
0673: * treeNode = (DefaultMutableTreeNode) treeNode.getChildAt(0);
0674: * }
0675: * <p/>
0676: * if (treeNode != null) {
0677: * Object userObject = treeNode.getUserObject();
0678: * if (userObject instanceof AbstractDialogPage) {
0679: * setCurrentPage((AbstractDialogPage) userObject, tree);
0680: * }
0681: * }
0682: * }
0683: * });
0684: * </pre></code>
0685: *
0686: * @param tree
0687: */
0688: protected void configureTree(final JTree tree) {
0689: tree.setToggleClickCount(1);
0690: tree.setCellRenderer(createTreeCellRenderer());
0691: tree.setRootVisible(false);
0692: tree.setShowsRootHandles(false);
0693: tree.addTreeSelectionListener(new TreeSelectionListener() {
0694: public void valueChanged(TreeSelectionEvent e) {
0695: if (tree.getSelectionPath() == null) {
0696: return;
0697: }
0698:
0699: DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) tree
0700: .getSelectionPath().getLastPathComponent();
0701:
0702: // comment this while block if you want the parent page shows its own page instead of showing its first child page.
0703: while (!treeNode.isLeaf()) {
0704: final DefaultMutableTreeNode tn = treeNode;
0705: Runnable runnable = new Runnable() {
0706: public void run() {
0707: tree.expandPath(new TreePath(tn.getPath()));
0708: }
0709: };
0710: SwingUtilities.invokeLater(runnable);
0711: treeNode = (DefaultMutableTreeNode) treeNode
0712: .getChildAt(0);
0713: }
0714:
0715: if (treeNode != null) {
0716: Object userObject = treeNode.getUserObject();
0717: if (userObject instanceof AbstractDialogPage) {
0718: setCurrentPage((AbstractDialogPage) userObject,
0719: tree);
0720: if (getCurrentPage() != userObject) {
0721: // TODO select the old path.
0722: }
0723: }
0724: }
0725: }
0726: });
0727: }
0728:
0729: private void addPage(AbstractDialogPage dialogPage,
0730: final DefaultMutableTreeNode root, boolean fireEvent) {
0731: if (dialogPage == null) {
0732: return;
0733: }
0734:
0735: if (dialogPage.getParentPage() == null) {
0736: DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(
0737: dialogPage);
0738: _titleNodeMap.put(dialogPage.getFullTitle(), treeNode);
0739: root.add(treeNode);
0740: if (fireEvent) {
0741: ((DefaultTreeModel) _tree.getModel())
0742: .nodesWereInserted(root, new int[] { root
0743: .getIndex(treeNode) });
0744: }
0745: } else {
0746: DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(
0747: dialogPage);
0748: _titleNodeMap.put(dialogPage.getFullTitle(), treeNode);
0749: DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) _titleNodeMap
0750: .get(dialogPage.getParentPage().getFullTitle());
0751: if (parentNode != null) {
0752: parentNode.add(treeNode);
0753: if (fireEvent) {
0754: ((DefaultTreeModel) _tree.getModel())
0755: .nodesWereInserted(parentNode,
0756: new int[] { parentNode
0757: .getIndex(treeNode) });
0758: }
0759: }
0760: }
0761: }
0762:
0763: private void removePage(AbstractDialogPage dialogPage,
0764: final DefaultMutableTreeNode root, boolean fireEvent) {
0765: if (dialogPage == null) {
0766: return;
0767: }
0768:
0769: DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) _titleNodeMap
0770: .get(dialogPage.getFullTitle());
0771:
0772: if (treeNode == null) {
0773: return;
0774: }
0775:
0776: if (treeNode.getChildCount() > 0) {
0777: throw new IllegalArgumentException(
0778: "Please remove all children pages before removing parent page \""
0779: + dialogPage.getFullTitle() + "\"");
0780: }
0781: _titleNodeMap.remove(dialogPage.getFullTitle());
0782: if (dialogPage.getParentPage() == null) {
0783: int index = root.getIndex(treeNode);
0784: root.remove(treeNode);
0785: if (fireEvent) {
0786: ((DefaultTreeModel) _tree.getModel()).nodesWereRemoved(
0787: root, new int[] { index },
0788: new Object[] { treeNode });
0789: }
0790: } else {
0791: DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) _titleNodeMap
0792: .get(dialogPage.getParentPage().getFullTitle());
0793: if (parentNode != null) {
0794: int index = parentNode.getIndex(treeNode);
0795: parentNode.remove(treeNode);
0796: if (fireEvent) {
0797: ((DefaultTreeModel) _tree.getModel())
0798: .nodesWereRemoved(parentNode,
0799: new int[] { index },
0800: new Object[] { treeNode });
0801: }
0802: }
0803: }
0804: }
0805:
0806: private JComponent createListPanel() {
0807: final DefaultListModel listModel = new DefaultListModel();
0808: for (int i = 0; i < _pageList.getPageCount(); i++) {
0809: AbstractDialogPage optionsPanel = _pageList.getPage(i);
0810: listModel.addElement(optionsPanel);
0811: }
0812:
0813: final JList list = createList(listModel);
0814: if (list.getModel().getSize() > 0) {
0815: list.setSelectedIndex(0);
0816: }
0817: list.addListSelectionListener(new ListSelectionListener() {
0818: public void valueChanged(ListSelectionEvent e) {
0819: if (list.getSelectedValue() == getCurrentPage()) {
0820: return;
0821: }
0822: if (e.getValueIsAdjusting()) {
0823: AbstractDialogPage page = (AbstractDialogPage) list
0824: .getSelectedValue();
0825: if (page != null) {
0826: setCurrentPage(page, list);
0827: if (getCurrentPage() != page) {
0828: list.setSelectedValue(getCurrentPage(),
0829: true);
0830: }
0831: } else {
0832: list.setSelectedIndex(e.getLastIndex());
0833: }
0834: }
0835: }
0836: });
0837: list.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 10));
0838:
0839: _pageList.addListDataListener(new ListDataListener() {
0840: public void intervalAdded(ListDataEvent e) {
0841: for (int i = e.getIndex0(); i <= e.getIndex1(); i++) {
0842: AbstractDialogPage optionsPanel = _pageList
0843: .getPage(i);
0844: listModel.add(i, optionsPanel);
0845: }
0846: }
0847:
0848: public void intervalRemoved(ListDataEvent e) {
0849: for (int i = e.getIndex1(); i >= e.getIndex0(); i--) {
0850: listModel.remove(i);
0851: }
0852: }
0853:
0854: public void contentsChanged(ListDataEvent e) {
0855: if (e.getIndex0() == -1
0856: && e.getIndex1() == -1
0857: && e.getType() == ListDataEvent.CONTENTS_CHANGED) {
0858: int index = _pageList
0859: .getPageIndexByFullTitle(_pageList
0860: .getCurrentPage().getTitle());
0861: list.setSelectedIndex(index);
0862: }
0863: }
0864: });
0865:
0866: JComponent indexPanel = new JPanel(new BorderLayout(4, 4));
0867: indexPanel.add(new JideScrollPane(list), BorderLayout.CENTER);
0868: indexPanel.setOpaque(false);
0869: return indexPanel;
0870: }
0871:
0872: /**
0873: * Creates list that is used in LIST_STYLE dialog's index panel. Below is the code we used.
0874: * If you just want to have a different cell renderer, you can just call
0875: * {@link #setListCellRenderer(javax.swing.ListCellRenderer)} to set a new one.
0876: * <pre><code>
0877: * JList list = new JList(listModel);
0878: * list.setCellRenderer(createListCellRenderer());
0879: * return list;
0880: * </code></pre>
0881: *
0882: * @param listModel
0883: * @return list.
0884: */
0885: protected JList createList(DefaultListModel listModel) {
0886: JList list = new JList(listModel);
0887: list.setCellRenderer(createListCellRenderer());
0888: return list;
0889: }
0890:
0891: /**
0892: * Creates the panel that contains several icons. Each icon represents for a page. This is only used for ICON_STYLE.
0893: *
0894: * @return a panel that contains several icons.
0895: */
0896: protected JComponent createIconPanel() {
0897: final ButtonPanel buttonsPanel = createIconButtonPanel();
0898: buttonsPanel.setGroupGap(0);
0899: buttonsPanel.setButtonGap(0);
0900:
0901: final ButtonGroup group = new ButtonGroup();
0902: for (int i = 0; i < _pageList.getPageCount(); i++) {
0903: AbstractDialogPage optionsPanel = _pageList.getPage(i);
0904: final JideButton button = createIconButton(optionsPanel
0905: .getTitle(), optionsPanel.getIcon());
0906: button.setToolTipText(optionsPanel.getDescription());
0907: button.setEnabled(optionsPanel.isPageEnabled());
0908: button.addActionListener(new AbstractAction() {
0909: public void actionPerformed(ActionEvent e) {
0910: AbstractDialogPage currentPage = _pageList
0911: .getPageByFullTitle(e.getActionCommand());
0912: setCurrentPage(currentPage, buttonsPanel);
0913: if (getCurrentPage() == currentPage) {
0914: group.setSelected(button.getModel(), true);
0915: }
0916: }
0917: });
0918: optionsPanel
0919: .addPropertyChangeListener(new PropertyChangeListener() {
0920: public void propertyChange(
0921: PropertyChangeEvent evt) {
0922: if (AbstractDialogPage.PROPERTY_PAGE_ENABLED
0923: .equals(evt.getPropertyName())) {
0924: button.setEnabled(Boolean.TRUE
0925: .equals(evt.getNewValue()));
0926: } else if (AbstractDialogPage.ICON_PROPERTY
0927: .equals(evt.getPropertyName())) {
0928: button
0929: .setIcon((Icon) evt
0930: .getNewValue());
0931: } else if (AbstractDialogPage.TITLE_PROPERTY
0932: .equals(evt.getPropertyName())) {
0933: button.setText((String) evt
0934: .getNewValue());
0935: } else if (AbstractDialogPage.DESCRIPTION_PROPERTY
0936: .equals(evt.getPropertyName())) {
0937: button.setToolTipText((String) evt
0938: .getNewValue());
0939: }
0940: }
0941: });
0942: buttonsPanel.addButton(button);
0943: group.add(button);
0944: if (i == 0) {
0945: group.setSelected(button.getModel(), true);
0946: }
0947: }
0948:
0949: buttonsPanel.setOpaque(false);
0950: buttonsPanel.setBorder(BorderFactory.createEmptyBorder(10, 10,
0951: 10, 10));
0952:
0953: final JScrollPane pane = new JScrollPane(buttonsPanel) {
0954: @Override
0955: public Dimension getPreferredSize() {
0956: if (buttonsPanel.getAlignment() == SwingConstants.TOP
0957: || buttonsPanel.getAlignment() == SwingConstants.BOTTOM)
0958: return new Dimension(
0959: buttonsPanel.getPreferredSize().width
0960: + getVerticalScrollBar()
0961: .getPreferredSize().width,
0962: 5);
0963: else
0964: return new Dimension(5, buttonsPanel
0965: .getPreferredSize().height
0966: + getHorizontalScrollBar()
0967: .getPreferredSize().height);
0968: }
0969:
0970: @Override
0971: public Dimension getMinimumSize() {
0972: return getPreferredSize();
0973: }
0974:
0975: };
0976: pane
0977: .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
0978:
0979: buttonsPanel.setOpaque(false);
0980:
0981: _pageList.addListDataListener(new ListDataListener() {
0982: public void intervalAdded(ListDataEvent e) {
0983: for (int i = e.getIndex0(); i <= e.getIndex1(); i++) {
0984: addPage(i, group, buttonsPanel);
0985: }
0986: buttonsPanel.invalidate();
0987: buttonsPanel.doLayout();
0988: }
0989:
0990: public void intervalRemoved(ListDataEvent e) {
0991: for (int i = e.getIndex1(); i >= e.getIndex0(); i--) {
0992: AbstractButton button = (AbstractButton) buttonsPanel
0993: .getComponent(i);
0994: buttonsPanel.remove(button);
0995: group.remove(button);
0996: }
0997: buttonsPanel.invalidate();
0998: buttonsPanel.doLayout();
0999: }
1000:
1001: public void contentsChanged(ListDataEvent e) {
1002: if (e.getIndex0() == -1
1003: && e.getIndex1() == -1
1004: && e.getType() == ListDataEvent.CONTENTS_CHANGED) {
1005: AbstractButton button = (AbstractButton) buttonsPanel
1006: .getButtonByName(_pageList.getCurrentPage()
1007: .getTitle());
1008: if (button != null) {
1009: group.setSelected(button.getModel(), true);
1010: }
1011: }
1012: }
1013: });
1014:
1015: pane.getViewport().setOpaque(false);
1016: return pane;
1017: }
1018:
1019: /**
1020: * Creates the ButtonPanel used by IconPanel. By default, we create it using <code>new ButtonPanel(SwingConstants.TOP, ButtonPanel.SAME_SIZE)</code>.
1021: *
1022: * @return the ButtonPanel.
1023: */
1024: protected ButtonPanel createIconButtonPanel() {
1025: return new ScrollableButtonPanel(SwingConstants.TOP,
1026: ButtonPanel.SAME_SIZE);
1027: }
1028:
1029: private JideButton addPage(int i, final ButtonGroup group,
1030: final ButtonPanel buttonsPanel) {
1031: AbstractDialogPage optionsPanel = _pageList.getPage(i);
1032: final JideButton button = createIconButton(optionsPanel
1033: .getTitle(), optionsPanel.getIcon());
1034: button.addActionListener(new AbstractAction(optionsPanel
1035: .getTitle(), optionsPanel.getIcon()) {
1036: public void actionPerformed(ActionEvent e) {
1037: group.setSelected(button.getModel(), true);
1038: setCurrentPage(_pageList.getPageByFullTitle(e
1039: .getActionCommand()), buttonsPanel);
1040: }
1041: });
1042: buttonsPanel.addButton(button, i);
1043: group.add(button);
1044: return button;
1045: }
1046:
1047: /**
1048: * Creates the button for each icon.
1049: *
1050: * @param title
1051: * @param icon
1052: * @return the button
1053: */
1054: protected JideButton createIconButton(String title, Icon icon) {
1055: final JideButton button = new JideButton(title, icon);
1056: button.setName(title);
1057: button.setHorizontalAlignment(SwingConstants.CENTER);
1058: button.setVerticalTextPosition(SwingConstants.BOTTOM);
1059: button.setHorizontalTextPosition(SwingConstants.CENTER);
1060:
1061: button.setRequestFocusEnabled(true);
1062: button.setFocusable(true);
1063: return button;
1064: }
1065:
1066: /**
1067: * Gets the style of this dialog.
1068: *
1069: * @return the style. It can be TAB_STYLE, ICON_STYLE, LIST_STYLE or TREE_STYLE.
1070: */
1071: public int getStyle() {
1072: return _style;
1073: }
1074:
1075: /**
1076: * Sets the style of this dialog. This class doesn't support change style on fly.
1077: * You can only change style before the dialog is set to visible.
1078: *
1079: * @param style It must be one of the following: TAB_STYLE, ICON_STYLE, LIST_STYLE or TREE_STYLE.
1080: */
1081: public void setStyle(int style) {
1082: if (style == TAB_STYLE || style == LIST_STYLE
1083: || style == ICON_STYLE || style == TREE_STYLE) {
1084: _style = style;
1085: } else {
1086: throw new IllegalArgumentException(
1087: "The value of style must be one of the following - TAB_STYLE, ICON_STYLE, LIST_STYLE or TREE_STYLE");
1088: }
1089: }
1090:
1091: /**
1092: * Gets the index panel.
1093: *
1094: * @return the index panel.
1095: */
1096: public JComponent getIndexPanel() {
1097: return _indexPanel;
1098: }
1099:
1100: /**
1101: * Gets the pages panel.
1102: *
1103: * @return the pages panel.
1104: */
1105: public JComponent getPagesPanel() {
1106: return _pagesPanel;
1107: }
1108:
1109: /**
1110: * Gets the cell renderer used by the tree. It's used only
1111: * when the style is TREE_STYLE.
1112: *
1113: * @return the tree cell renderer.
1114: */
1115: protected TreeCellRenderer getTreeCellRenderer() {
1116: return _treeCellRenderer;
1117: }
1118:
1119: /**
1120: * Sets the tree cell renderer that will be used by JTree when the style is TREE_STYLE.
1121: *
1122: * @param treeCellRenderer
1123: */
1124: public void setTreeCellRenderer(TreeCellRenderer treeCellRenderer) {
1125: _treeCellRenderer = treeCellRenderer;
1126: }
1127:
1128: /**
1129: * Gets the cell renderer used by the list. It's used only
1130: * when the style is LIST_STYLE.
1131: *
1132: * @return the list cell renderer.
1133: */
1134: protected ListCellRenderer getListCellRenderer() {
1135: return _listCellRenderer;
1136: }
1137:
1138: /**
1139: * Sets the list cell renderer that will be used by JList when the style is LIST_STYLE.
1140: *
1141: * @param listCellRenderer
1142: */
1143: public void setListCellRenderer(ListCellRenderer listCellRenderer) {
1144: _listCellRenderer = listCellRenderer;
1145: }
1146:
1147: /**
1148: * Creates a list cell renderer used by list in LIST_STYLE dialog's index panel.
1149: *
1150: * @return the list cell renderer.
1151: */
1152: protected ListCellRenderer createListCellRenderer() {
1153: if (getListCellRenderer() == null) {
1154: setListCellRenderer(new DialogPageListCellRenderer());
1155: }
1156: return getListCellRenderer();
1157: }
1158:
1159: /**
1160: * Creates the tree cell renderer used by tree in TREE_STYLE dialog's index panel.
1161: *
1162: * @return the tree cell renderer.
1163: */
1164: protected TreeCellRenderer createTreeCellRenderer() {
1165: if (getTreeCellRenderer() == null) {
1166: setTreeCellRenderer(new DialogPageTreeCellRenderer());
1167: }
1168: return getTreeCellRenderer();
1169: }
1170:
1171: /**
1172: * Gets the initial page title. Initial page is the page that will be selected when the dialog
1173: * is just opened. Please note the title is the full title. In most case it's just the title of the page.
1174: * Only in TREE_STYLE, it should be a list of titles that concats with '.'.
1175: *
1176: * @return the initial page title.
1177: */
1178: public String getInitialPageTitle() {
1179: return _initialPageTitle;
1180: }
1181:
1182: /**
1183: * Sets the initial page title. Initial page is the page that will be selected when the dialog.
1184: *
1185: * @param initialPageTitle
1186: */
1187: public void setInitialPageTitle(String initialPageTitle) {
1188: _initialPageTitle = initialPageTitle;
1189: }
1190: }
|