001: /*
002: * SSHTools - Java SSH2 API
003: *
004: * Copyright (C) 2002-2003 Lee David Painter and Contributors.
005: *
006: * Contributions made by:
007: *
008: * Brett Smith
009: * Richard Pernavas
010: * Erwin Bolwidt
011: *
012: * This program is free software; you can redistribute it and/or
013: * modify it under the terms of the GNU General Public License
014: * as published by the Free Software Foundation; either version 2
015: * of the License, or (at your option) any later version.
016: *
017: * This program is distributed in the hope that it will be useful,
018: * but WITHOUT ANY WARRANTY; without even the implied warranty of
019: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
020: * GNU General Public License for more details.
021: *
022: * You should have received a copy of the GNU General Public License
023: * along with this program; if not, write to the Free Software
024: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
025: */
026: package com.sshtools.common.ui;
027:
028: import com.sshtools.j2ssh.configuration.ConfigurationLoader;
029:
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032:
033: import java.awt.Component;
034: import java.awt.LayoutManager;
035: import java.awt.event.ActionEvent;
036:
037: import java.io.File;
038: import java.io.PrintWriter;
039: import java.io.StringWriter;
040:
041: import java.util.Collections;
042: import java.util.Comparator;
043: import java.util.Enumeration;
044: import java.util.HashMap;
045: import java.util.Iterator;
046: import java.util.Vector;
047:
048: import javax.swing.Action;
049: import javax.swing.JMenu;
050: import javax.swing.JMenuBar;
051: import javax.swing.JMenuItem;
052: import javax.swing.JOptionPane;
053: import javax.swing.JPanel;
054: import javax.swing.JPopupMenu;
055: import javax.swing.JToolBar;
056: import javax.swing.filechooser.FileFilter;
057:
058: /**
059: *
060: *
061: * @author $author$
062: * @version $Revision: 1.23 $
063: */
064: public abstract class SshToolsApplicationPanel extends JPanel {
065: //
066:
067: /** */
068: protected Log log = LogFactory
069: .getLog(SshToolsApplicationPanel.class);
070:
071: /** */
072: protected SshToolsApplication application;
073:
074: /** */
075: protected JMenuBar menuBar;
076:
077: /** */
078: protected JToolBar toolBar;
079:
080: /** */
081: protected JPopupMenu contextMenu;
082:
083: /** */
084: protected SshToolsApplicationContainer container;
085:
086: /** */
087: protected Vector actions = new Vector();
088:
089: /** */
090: protected HashMap actionsVisible = new HashMap();
091:
092: /** */
093: protected boolean toolsVisible;
094:
095: /** */
096: protected Vector actionMenus = new Vector();
097:
098: /**
099: * Creates a new SshToolsApplicationPanel object.
100: */
101: public SshToolsApplicationPanel() {
102: super ();
103: }
104:
105: /**
106: * Creates a new SshToolsApplicationPanel object.
107: *
108: * @param mgr
109: */
110: public SshToolsApplicationPanel(LayoutManager mgr) {
111: super (mgr);
112: }
113:
114: /**
115: * Called by the application framework to test the closing state
116: *
117: * @return
118: */
119: public abstract boolean canClose();
120:
121: /**
122: * Called by the application framework to close the panel
123: */
124: public abstract void close();
125:
126: /**
127: * Called by the application framework when a change in connection state
128: * has occured. The available actions should be enabled/disabled in this
129: * methods implementation
130: */
131: public abstract void setAvailableActions();
132:
133: /**
134: * Set an actions visible state
135: *
136: * @param name
137: * @param visible
138: */
139: public void setActionVisible(String name, boolean visible) {
140: log.debug("Setting action '" + name + "' to visibility "
141: + visible);
142: actionsVisible.put(name, new Boolean(visible));
143: }
144:
145: /**
146: * Gets the container for this panel.
147: *
148: * @return
149: */
150: public SshToolsApplicationContainer getContainer() {
151: return container;
152: }
153:
154: /**
155: * Sets the container for this panel
156: *
157: * @param container
158: */
159: public void setContainer(SshToolsApplicationContainer container) {
160: this .container = container;
161: }
162:
163: /**
164: * Register a new menu
165: *
166: * @param actionMenu
167: */
168: public void registerActionMenu(ActionMenu actionMenu) {
169: ActionMenu current = getActionMenu(actionMenu.name);
170:
171: if (current == null) {
172: actionMenus.addElement(actionMenu);
173: }
174: }
175:
176: /**
177: * Gets a menu by name
178: *
179: * @param actionMenuName
180: *
181: * @return
182: */
183: public ActionMenu getActionMenu(String actionMenuName) {
184: return getActionMenu(actionMenus.iterator(), actionMenuName);
185: }
186:
187: private ActionMenu getActionMenu(Iterator actions,
188: String actionMenuName) {
189: while (actions.hasNext()) {
190: ActionMenu a = (ActionMenu) actions.next();
191:
192: if (a.name.equals(actionMenuName)) {
193: return a;
194: }
195: }
196:
197: return null;
198: }
199:
200: /**
201: * Get an action by name
202: *
203: * @param name
204: *
205: * @return
206: */
207: public StandardAction getAction(String name) {
208: for (Iterator i = actions.iterator(); i.hasNext();) {
209: StandardAction a = (StandardAction) i.next();
210:
211: if (a.getName().equals(name)) {
212: return a;
213: }
214: }
215:
216: return null;
217: }
218:
219: /**
220: * Deregister an action
221: *
222: * @param action
223: */
224: public void deregisterAction(StandardAction action) {
225: actions.removeElement(action);
226: }
227:
228: /**
229: * Register a new action
230: *
231: * @param action
232: */
233: public void registerAction(StandardAction action) {
234: actions.addElement(action);
235: }
236:
237: /**
238: * Initialize the panel
239: *
240: * @param application
241: *
242: * @throws SshToolsApplicationException
243: */
244: public void init(SshToolsApplication application)
245: throws SshToolsApplicationException {
246: this .application = application;
247: menuBar = new JMenuBar();
248:
249: // Creat the tool bar
250: toolBar = new JToolBar();
251: toolBar.setFloatable(false);
252: toolBar.setBorderPainted(false);
253: toolBar.putClientProperty("JToolBar.isRollover", Boolean.TRUE);
254:
255: // Create the context menu
256: contextMenu = new JPopupMenu();
257: registerActionMenu(new ActionMenu("Tools", "Tools", 't', 30));
258:
259: if (PreferencesStore.isStoreAvailable()) {
260: log
261: .debug("Preferences store is available, adding options action");
262: registerAction(new OptionsAction() {
263: public void actionPerformed(ActionEvent evt) {
264: showOptions();
265: }
266: });
267: }
268: }
269:
270: /**
271: * Show the options dialog
272: */
273: public void showOptions() {
274: OptionsTab[] tabs = getApplication().getAdditionalOptionsTabs();
275: OptionsPanel.showOptionsDialog(this , tabs);
276: }
277:
278: /**
279: * Rebuild all the action components such as toobar, context menu
280: */
281: public void rebuildActionComponents() {
282: // Clear the current state of the component
283: log.debug("Rebuild action components");
284: toolBar.removeAll();
285:
286: //
287: Vector enabledActions = new Vector();
288:
289: for (Iterator i = actions.iterator(); i.hasNext();) {
290: StandardAction a = (StandardAction) i.next();
291: String n = (String) a.getValue(Action.NAME);
292: Boolean s = (Boolean) actionsVisible.get(n);
293:
294: if (s == null) {
295: s = Boolean.TRUE;
296: }
297:
298: if (Boolean.TRUE.equals(s)) {
299: log.debug("Action " + n + " is enabled.");
300: enabledActions.add(a);
301: } else {
302: log.debug("Action " + n + " not enabled.");
303: }
304: }
305:
306: // Build the tool bar, grouping the actions
307: Vector v = new Vector();
308:
309: for (Iterator i = enabledActions.iterator(); i.hasNext();) {
310: StandardAction a = (StandardAction) i.next();
311:
312: if (Boolean.TRUE.equals((Boolean) a
313: .getValue(StandardAction.ON_TOOLBAR))) {
314: v.addElement(a);
315: }
316: }
317:
318: Collections.sort(v, new ToolBarActionComparator());
319:
320: Integer grp = null;
321:
322: for (Iterator i = v.iterator(); i.hasNext();) {
323: StandardAction z = (StandardAction) i.next();
324:
325: if ((grp != null)
326: && !grp.equals((Integer) z
327: .getValue(StandardAction.TOOLBAR_GROUP))) {
328: toolBar.add(new ToolBarSeparator());
329: }
330:
331: if (Boolean.TRUE.equals((Boolean) z
332: .getValue(StandardAction.IS_TOGGLE_BUTTON))) {
333: ToolToggleButton tBtn = new ToolToggleButton(z);
334: toolBar.add(tBtn);
335: } else {
336: ToolButton btn = new ToolButton(z);
337: toolBar.add(btn);
338: }
339:
340: grp = (Integer) z.getValue(StandardAction.TOOLBAR_GROUP);
341: }
342:
343: toolBar.revalidate();
344: toolBar.repaint();
345:
346: // Build the context menu, grouping the actions
347: Vector c = new Vector();
348: contextMenu.removeAll();
349:
350: for (Iterator i = enabledActions.iterator(); i.hasNext();) {
351: StandardAction a = (StandardAction) i.next();
352:
353: if (Boolean.TRUE.equals((Boolean) a
354: .getValue(StandardAction.ON_CONTEXT_MENU))) {
355: c.addElement(a);
356: }
357: }
358:
359: Collections.sort(c, new ContextActionComparator());
360: grp = null;
361:
362: for (Iterator i = c.iterator(); i.hasNext();) {
363: StandardAction z = (StandardAction) i.next();
364:
365: if ((grp != null)
366: && !grp
367: .equals((Integer) z
368: .getValue(StandardAction.CONTEXT_MENU_GROUP))) {
369: contextMenu.addSeparator();
370: }
371:
372: contextMenu.add(z);
373: grp = (Integer) z
374: .getValue(StandardAction.CONTEXT_MENU_GROUP);
375: }
376:
377: contextMenu.revalidate();
378:
379: // Build the menu bar
380: menuBar.removeAll();
381: v.removeAllElements();
382:
383: for (Enumeration e = enabledActions.elements(); e
384: .hasMoreElements();) {
385: StandardAction a = (StandardAction) e.nextElement();
386:
387: if (Boolean.TRUE.equals((Boolean) a
388: .getValue(StandardAction.ON_MENUBAR))) {
389: v.addElement(a);
390: }
391: }
392:
393: Vector menus = (Vector) actionMenus.clone();
394: Collections.sort(menus);
395:
396: HashMap map = new HashMap();
397:
398: for (Iterator i = v.iterator(); i.hasNext();) {
399: StandardAction z = (StandardAction) i.next();
400: String menuName = (String) z
401: .getValue(StandardAction.MENU_NAME);
402:
403: if (menuName == null) {
404: log.error("Action " + z.getName()
405: + " doesnt specify a value for "
406: + StandardAction.MENU_NAME);
407: } else {
408: String m = (String) z
409: .getValue(StandardAction.MENU_NAME);
410: ActionMenu menu = getActionMenu(menus.iterator(), m);
411:
412: if (menu == null) {
413: log.error("Action menu " + z.getName()
414: + " does not exist");
415: } else {
416: Vector x = (Vector) map.get(menu.name);
417:
418: if (x == null) {
419: x = new Vector();
420: map.put(menu.name, x);
421: }
422:
423: x.addElement(z);
424: }
425: }
426: }
427:
428: for (Iterator i = menus.iterator(); i.hasNext();) {
429: ActionMenu m = (ActionMenu) i.next();
430: Vector x = (Vector) map.get(m.name);
431:
432: if (x != null) {
433: Collections.sort(x, new MenuItemActionComparator());
434:
435: JMenu menu = new JMenu(m.displayName);
436: menu.setMnemonic(m.mnemonic);
437: grp = null;
438:
439: for (Iterator j = x.iterator(); j.hasNext();) {
440: StandardAction a = (StandardAction) j.next();
441: Integer g = (Integer) a
442: .getValue(StandardAction.MENU_ITEM_GROUP);
443:
444: if ((grp != null) && !g.equals(grp)) {
445: menu.addSeparator();
446: }
447:
448: grp = g;
449:
450: if (a instanceof MenuAction) {
451: JMenu mnu = (JMenu) a.getValue(MenuAction.MENU);
452: menu.add(mnu);
453: } else {
454: JMenuItem item = new JMenuItem(a);
455: menu.add(item);
456: }
457: }
458:
459: menuBar.add(menu);
460: } else {
461: log.error("Can't find menu " + m.name);
462: }
463: }
464:
465: menuBar.validate();
466: menuBar.repaint();
467: }
468:
469: /**
470: * Determine if the toolbar, menu and statusbar are visible
471: *
472: * @return
473: */
474: public boolean isToolsVisible() {
475: return toolsVisible;
476: }
477:
478: // Adds the new favorite to the appropriate favorite menu
479: public void addFavorite(StandardAction action) {
480: for (int i = 0; i < menuBar.getMenuCount(); i++) {
481: JMenu menu = menuBar.getMenu(i);
482:
483: if ((menu.getText() != null)
484: && menu.getText().equals("Favorites")) {
485: menu.add(action);
486: }
487: }
488: }
489:
490: /**
491: * Set the visible state of the menu bar
492: *
493: * @param visible
494: */
495: public void setMenuBarVisible(boolean visible) {
496: if ((getJMenuBar() != null)
497: && (getJMenuBar().isVisible() != visible)) {
498: getJMenuBar().setVisible(visible);
499: revalidate();
500: }
501: }
502:
503: /**
504: * Set the visible state of the toolbar
505: *
506: * @param visible
507: */
508: public void setToolBarVisible(boolean visible) {
509: if ((getToolBar() != null)
510: && (getToolBar().isVisible() != visible)) {
511: getToolBar().setVisible(visible);
512: revalidate();
513: }
514: }
515:
516: /**
517: * Set the visible state of the statusbar
518: *
519: * @param visible
520: */
521: public void setStatusBarVisible(boolean visible) {
522: if ((getStatusBar() != null)
523: && (getStatusBar().isVisible() != visible)) {
524: getStatusBar().setVisible(visible);
525: revalidate();
526: }
527: }
528:
529: /**
530: * Set the visible state of all tools. This will set the toolbar, menu and
531: * status bar visible states to the value provided.
532: *
533: * @param visible
534: */
535: public void setToolsVisible(boolean visible) {
536: synchronized (getTreeLock()) {
537: if ((getToolBar() != null)
538: && (getToolBar().isVisible() != visible)) {
539: getToolBar().setVisible(visible);
540: }
541:
542: if ((getJMenuBar() != null)
543: && (getJMenuBar().isVisible() != visible)) {
544: getJMenuBar().setVisible(visible);
545: }
546:
547: if ((getStatusBar() != null)
548: && (getStatusBar().isVisible() != visible)) {
549: getStatusBar().setVisible(visible);
550: }
551:
552: toolsVisible = visible;
553: revalidate();
554: }
555: }
556:
557: /**
558: * Show an exception message
559: *
560: * @param title
561: * @param message
562: */
563: public void showExceptionMessage(String title, String message) {
564: JOptionPane.showMessageDialog(this , message, title,
565: JOptionPane.ERROR_MESSAGE);
566: }
567:
568: /**
569: * Show an error message with detail
570: *
571: * @param parent
572: * @param title
573: * @param exception
574: */
575: public static void showErrorMessage(Component parent, String title,
576: Throwable exception) {
577: showErrorMessage(parent, null, title, exception);
578: }
579:
580: /**
581: * Show an error message with toggable detail
582: *
583: * @param parent
584: * @param mesg
585: * @param title
586: * @param exception
587: */
588: public static void showErrorMessage(Component parent, String mesg,
589: String title, Throwable exception) {
590: boolean details = false;
591:
592: while (true) {
593: String[] opts = new String[] {
594: details ? "Hide Details" : "Details", "Ok" };
595: StringBuffer buf = new StringBuffer();
596:
597: if (mesg != null) {
598: buf.append(mesg);
599: }
600:
601: appendException(exception, 0, buf, details);
602:
603: MultilineLabel message = new MultilineLabel(buf.toString());
604: int opt = JOptionPane.showOptionDialog(parent, message,
605: title, JOptionPane.OK_CANCEL_OPTION,
606: JOptionPane.ERROR_MESSAGE, null, opts, opts[1]);
607:
608: if (opt == 0) {
609: details = !details;
610: } else {
611: break;
612: }
613: }
614: }
615:
616: private static void appendException(Throwable exception, int level,
617: StringBuffer buf, boolean details) {
618: try {
619: if (((exception != null) && (exception.getMessage() != null))
620: && (exception.getMessage().length() > 0)) {
621: if (details && (level > 0)) {
622: buf.append("\n \nCaused by ...\n");
623: }
624:
625: buf.append(exception.getMessage());
626: }
627:
628: if (details) {
629: if (exception != null) {
630: if ((exception.getMessage() != null)
631: && (exception.getMessage().length() == 0)) {
632: buf.append("\n \nCaused by ...");
633: } else {
634: buf.append("\n \n");
635: }
636: }
637:
638: StringWriter sw = new StringWriter();
639:
640: if (exception != null) {
641: exception.printStackTrace(new PrintWriter(sw));
642: }
643:
644: buf.append(sw.toString());
645: }
646:
647: try {
648: java.lang.reflect.Method method = exception.getClass()
649: .getMethod("getCause", new Class[] {});
650: Throwable cause = (Throwable) method.invoke(exception,
651: null);
652:
653: if (cause != null) {
654: appendException(cause, level + 1, buf, details);
655: }
656: } catch (Exception e) {
657: }
658: } catch (Throwable ex) {
659: }
660: }
661:
662: /**
663: * Returns the connected state of the panel
664: *
665: * @return
666: */
667: public abstract boolean isConnected();
668:
669: /**
670: * Set the title of the container
671: *
672: * @param file
673: */
674: public void setContainerTitle(File file) {
675: String verString = "";
676:
677: if (application != null) {
678: verString = ConfigurationLoader.getVersionString(
679: application.getApplicationName(), application
680: .getApplicationVersion());
681: }
682:
683: if (container != null) {
684: container.setContainerTitle((file == null) ? verString
685: : (verString + " [" + file.getName() + "]"));
686: }
687: }
688:
689: /**
690: * Gets the toolbar
691: *
692: * @return
693: */
694: public JToolBar getToolBar() {
695: return toolBar;
696: }
697:
698: /**
699: * Get the context menu
700: *
701: * @return
702: */
703: public JPopupMenu getContextMenu() {
704: return contextMenu;
705: }
706:
707: /**
708: * Get the main menu
709: *
710: * @return
711: */
712: public JMenuBar getJMenuBar() {
713: return menuBar;
714: }
715:
716: /**
717: * Get the status bar
718: *
719: * @return
720: */
721: public StatusBar getStatusBar() {
722: return null;
723: }
724:
725: /**
726: * Get the application attached to the panel
727: *
728: * @return
729: */
730: public SshToolsApplication getApplication() {
731: return application;
732: }
733:
734: /**
735: * Get the icon for the panel
736: *
737: * @return
738: */
739: public abstract ResourceIcon getIcon();
740:
741: public static class ActionMenu implements Comparable {
742: int weight;
743: int mnemonic;
744: String name;
745: String displayName;
746:
747: public ActionMenu(String name, String displayName,
748: int mnemonic, int weight) {
749: this .name = name;
750: this .displayName = displayName;
751: this .mnemonic = mnemonic;
752: this .weight = weight;
753: }
754:
755: public int compareTo(Object o) {
756: int i = new Integer(weight).compareTo(new Integer(
757: ((ActionMenu) o).weight));
758:
759: return (i == 0) ? displayName
760: .compareTo(((ActionMenu) o).displayName) : i;
761: }
762: }
763:
764: class ToolBarActionComparator implements Comparator {
765: public int compare(Object o1, Object o2) {
766: int i = ((Integer) ((StandardAction) o1)
767: .getValue(StandardAction.TOOLBAR_GROUP))
768: .compareTo((Integer) ((StandardAction) o2)
769: .getValue(StandardAction.TOOLBAR_GROUP));
770:
771: return (i == 0) ? ((Integer) ((StandardAction) o1)
772: .getValue(StandardAction.TOOLBAR_WEIGHT))
773: .compareTo((Integer) ((StandardAction) o2)
774: .getValue(StandardAction.TOOLBAR_WEIGHT))
775: : i;
776: }
777: }
778:
779: class ContextActionComparator implements Comparator {
780: public int compare(Object o1, Object o2) {
781: int i = ((Integer) ((StandardAction) o1)
782: .getValue(StandardAction.CONTEXT_MENU_GROUP))
783: .compareTo((Integer) ((StandardAction) o2)
784: .getValue(StandardAction.CONTEXT_MENU_GROUP));
785:
786: return (i == 0) ? ((Integer) ((StandardAction) o1)
787: .getValue(StandardAction.CONTEXT_MENU_WEIGHT))
788: .compareTo((Integer) ((StandardAction) o2)
789: .getValue(StandardAction.CONTEXT_MENU_WEIGHT))
790: : i;
791: }
792: }
793:
794: class MenuItemActionComparator implements Comparator {
795: public int compare(Object o1, Object o2) {
796: int i = ((Integer) ((StandardAction) o1)
797: .getValue(StandardAction.MENU_ITEM_GROUP))
798: .compareTo((Integer) ((StandardAction) o2)
799: .getValue(StandardAction.MENU_ITEM_GROUP));
800:
801: return (i == 0) ? ((Integer) ((StandardAction) o1)
802: .getValue(StandardAction.MENU_ITEM_WEIGHT))
803: .compareTo((Integer) ((StandardAction) o2)
804: .getValue(StandardAction.MENU_ITEM_WEIGHT))
805: : i;
806: }
807: }
808:
809: class ConnectionFileFilter extends
810: javax.swing.filechooser.FileFilter {
811: public boolean accept(File f) {
812: return f.isDirectory()
813: || f.getName().toLowerCase().endsWith(".xml");
814: }
815:
816: public String getDescription() {
817: return "Connection files (*.xml)";
818: }
819: }
820: }
|