1 /*
2 * SSHTools - Java SSH2 API
3 *
4 * Copyright (C) 2002-2003 Lee David Painter and Contributors.
5 *
6 * Contributions made by:
7 *
8 * Brett Smith
9 * Richard Pernavas
10 * Erwin Bolwidt
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 */
26 package com.sshtools.common.ui;
27
28 import com.sshtools.j2ssh.configuration.ConfigurationLoader;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32
33 import java.awt.Component;
34 import java.awt.LayoutManager;
35 import java.awt.event.ActionEvent;
36
37 import java.io.File;
38 import java.io.PrintWriter;
39 import java.io.StringWriter;
40
41 import java.util.Collections;
42 import java.util.Comparator;
43 import java.util.Enumeration;
44 import java.util.HashMap;
45 import java.util.Iterator;
46 import java.util.Vector;
47
48 import javax.swing.Action;
49 import javax.swing.JMenu;
50 import javax.swing.JMenuBar;
51 import javax.swing.JMenuItem;
52 import javax.swing.JOptionPane;
53 import javax.swing.JPanel;
54 import javax.swing.JPopupMenu;
55 import javax.swing.JToolBar;
56 import javax.swing.filechooser.FileFilter;
57
58
59 /**
60 *
61 *
62 * @author $author$
63 * @version $Revision: 1.23 $
64 */
65 public abstract class SshToolsApplicationPanel extends JPanel {
66 //
67
68 /** */
69 protected Log log = LogFactory.getLog(SshToolsApplicationPanel.class);
70
71 /** */
72 protected SshToolsApplication application;
73
74 /** */
75 protected JMenuBar menuBar;
76
77 /** */
78 protected JToolBar toolBar;
79
80 /** */
81 protected JPopupMenu contextMenu;
82
83 /** */
84 protected SshToolsApplicationContainer container;
85
86 /** */
87 protected Vector actions = new Vector();
88
89 /** */
90 protected HashMap actionsVisible = new HashMap();
91
92 /** */
93 protected boolean toolsVisible;
94
95 /** */
96 protected Vector actionMenus = new Vector();
97
98 /**
99 * 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 " + visible);
141 actionsVisible.put(name, new Boolean(visible));
142 }
143
144 /**
145 * Gets the container for this panel.
146 *
147 * @return
148 */
149 public SshToolsApplicationContainer getContainer() {
150 return container;
151 }
152
153 /**
154 * Sets the container for this panel
155 *
156 * @param container
157 */
158 public void setContainer(SshToolsApplicationContainer container) {
159 this.container = container;
160 }
161
162 /**
163 * Register a new menu
164 *
165 * @param actionMenu
166 */
167 public void registerActionMenu(ActionMenu actionMenu) {
168 ActionMenu current = getActionMenu(actionMenu.name);
169
170 if (current == null) {
171 actionMenus.addElement(actionMenu);
172 }
173 }
174
175 /**
176 * Gets a menu by name
177 *
178 * @param actionMenuName
179 *
180 * @return
181 */
182 public ActionMenu getActionMenu(String actionMenuName) {
183 return getActionMenu(actionMenus.iterator(), actionMenuName);
184 }
185
186 private ActionMenu getActionMenu(Iterator actions, String actionMenuName) {
187 while (actions.hasNext()) {
188 ActionMenu a = (ActionMenu) actions.next();
189
190 if (a.name.equals(actionMenuName)) {
191 return a;
192 }
193 }
194
195 return null;
196 }
197
198 /**
199 * Get an action by name
200 *
201 * @param name
202 *
203 * @return
204 */
205 public StandardAction getAction(String name) {
206 for (Iterator i = actions.iterator(); i.hasNext();) {
207 StandardAction a = (StandardAction) i.next();
208
209 if (a.getName().equals(name)) {
210 return a;
211 }
212 }
213
214 return null;
215 }
216
217 /**
218 * Deregister an action
219 *
220 * @param action
221 */
222 public void deregisterAction(StandardAction action) {
223 actions.removeElement(action);
224 }
225
226 /**
227 * Register a new action
228 *
229 * @param action
230 */
231 public void registerAction(StandardAction action) {
232 actions.addElement(action);
233 }
234
235 /**
236 * Initialize the panel
237 *
238 * @param application
239 *
240 * @throws SshToolsApplicationException
241 */
242 public void init(SshToolsApplication application)
243 throws SshToolsApplicationException {
244 this.application = application;
245 menuBar = new JMenuBar();
246
247 // Creat the tool bar
248 toolBar = new JToolBar();
249 toolBar.setFloatable(false);
250 toolBar.setBorderPainted(false);
251 toolBar.putClientProperty("JToolBar.isRollover", Boolean.TRUE);
252
253 // Create the context menu
254 contextMenu = new JPopupMenu();
255 registerActionMenu(new ActionMenu("Tools", "Tools", 't', 30));
256
257 if (PreferencesStore.isStoreAvailable()) {
258 log.debug("Preferences store is available, adding options action");
259 registerAction(new OptionsAction() {
260 public void actionPerformed(ActionEvent evt) {
261 showOptions();
262 }
263 });
264 }
265 }
266
267 /**
268 * Show the options dialog
269 */
270 public void showOptions() {
271 OptionsTab[] tabs = getApplication().getAdditionalOptionsTabs();
272 OptionsPanel.showOptionsDialog(this, tabs);
273 }
274
275 /**
276 * Rebuild all the action components such as toobar, context menu
277 */
278 public void rebuildActionComponents() {
279 // Clear the current state of the component
280 log.debug("Rebuild action components");
281 toolBar.removeAll();
282
283 //
284 Vector enabledActions = new Vector();
285
286 for (Iterator i = actions.iterator(); i.hasNext();) {
287 StandardAction a = (StandardAction) i.next();
288 String n = (String) a.getValue(Action.NAME);
289 Boolean s = (Boolean) actionsVisible.get(n);
290
291 if (s == null) {
292 s = Boolean.TRUE;
293 }
294
295 if (Boolean.TRUE.equals(s)) {
296 log.debug("Action " + n + " is enabled.");
297 enabledActions.add(a);
298 } else {
299 log.debug("Action " + n + " not enabled.");
300 }
301 }
302
303 // Build the tool bar, grouping the actions
304 Vector v = new Vector();
305
306 for (Iterator i = enabledActions.iterator(); i.hasNext();) {
307 StandardAction a = (StandardAction) i.next();
308
309 if (Boolean.TRUE.equals(
310 (Boolean) a.getValue(StandardAction.ON_TOOLBAR))) {
311 v.addElement(a);
312 }
313 }
314
315 Collections.sort(v, new ToolBarActionComparator());
316
317 Integer grp = null;
318
319 for (Iterator i = v.iterator(); i.hasNext();) {
320 StandardAction z = (StandardAction) i.next();
321
322 if ((grp != null) &&
323 !grp.equals(
324 (Integer) z.getValue(StandardAction.TOOLBAR_GROUP))) {
325 toolBar.add(new ToolBarSeparator());
326 }
327
328 if (Boolean.TRUE.equals(
329 (Boolean) z.getValue(StandardAction.IS_TOGGLE_BUTTON))) {
330 ToolToggleButton tBtn = new ToolToggleButton(z);
331 toolBar.add(tBtn);
332 } else {
333 ToolButton btn = new ToolButton(z);
334 toolBar.add(btn);
335 }
336
337 grp = (Integer) z.getValue(StandardAction.TOOLBAR_GROUP);
338 }
339
340 toolBar.revalidate();
341 toolBar.repaint();
342
343 // Build the context menu, grouping the actions
344 Vector c = new Vector();
345 contextMenu.removeAll();
346
347 for (Iterator i = enabledActions.iterator(); i.hasNext();) {
348 StandardAction a = (StandardAction) i.next();
349
350 if (Boolean.TRUE.equals(
351 (Boolean) a.getValue(StandardAction.ON_CONTEXT_MENU))) {
352 c.addElement(a);
353 }
354 }
355
356 Collections.sort(c, new ContextActionComparator());
357 grp = null;
358
359 for (Iterator i = c.iterator(); i.hasNext();) {
360 StandardAction z = (StandardAction) i.next();
361
362 if ((grp != null) &&
363 !grp.equals(
364 (Integer) z.getValue(StandardAction.CONTEXT_MENU_GROUP))) {
365 contextMenu.addSeparator();
366 }
367
368 contextMenu.add(z);
369 grp = (Integer) z.getValue(StandardAction.CONTEXT_MENU_GROUP);
370 }
371
372 contextMenu.revalidate();
373
374 // Build the menu bar
375 menuBar.removeAll();
376 v.removeAllElements();
377
378 for (Enumeration e = enabledActions.elements(); e.hasMoreElements();) {
379 StandardAction a = (StandardAction) e.nextElement();
380
381 if (Boolean.TRUE.equals(
382 (Boolean) a.getValue(StandardAction.ON_MENUBAR))) {
383 v.addElement(a);
384 }
385 }
386
387 Vector menus = (Vector) actionMenus.clone();
388 Collections.sort(menus);
389
390 HashMap map = new HashMap();
391
392 for (Iterator i = v.iterator(); i.hasNext();) {
393 StandardAction z = (StandardAction) i.next();
394 String menuName = (String) z.getValue(StandardAction.MENU_NAME);
395
396 if (menuName == null) {
397 log.error("Action " + z.getName() +
398 " doesnt specify a value for " + StandardAction.MENU_NAME);
399 } else {
400 String m = (String) z.getValue(StandardAction.MENU_NAME);
401 ActionMenu menu = getActionMenu(menus.iterator(), m);
402
403 if (menu == null) {
404 log.error("Action menu " + z.getName() + " does not exist");
405 } else {
406 Vector x = (Vector) map.get(menu.name);
407
408 if (x == null) {
409 x = new Vector();
410 map.put(menu.name, x);
411 }
412
413 x.addElement(z);
414 }
415 }
416 }
417
418 for (Iterator i = menus.iterator(); i.hasNext();) {
419 ActionMenu m = (ActionMenu) i.next();
420 Vector x = (Vector) map.get(m.name);
421
422 if (x != null) {
423 Collections.sort(x, new MenuItemActionComparator());
424
425 JMenu menu = new JMenu(m.displayName);
426 menu.setMnemonic(m.mnemonic);
427 grp = null;
428
429 for (Iterator j = x.iterator(); j.hasNext();) {
430 StandardAction a = (StandardAction) j.next();
431 Integer g = (Integer) a.getValue(StandardAction.MENU_ITEM_GROUP);
432
433 if ((grp != null) && !g.equals(grp)) {
434 menu.addSeparator();
435 }
436
437 grp = g;
438
439 if (a instanceof MenuAction) {
440 JMenu mnu = (JMenu) a.getValue(MenuAction.MENU);
441 menu.add(mnu);
442 } else {
443 JMenuItem item = new JMenuItem(a);
444 menu.add(item);
445 }
446 }
447
448 menuBar.add(menu);
449 } else {
450 log.error("Can't find menu " + m.name);
451 }
452 }
453
454 menuBar.validate();
455 menuBar.repaint();
456 }
457
458 /**
459 * Determine if the toolbar, menu and statusbar are visible
460 *
461 * @return
462 */
463 public boolean isToolsVisible() {
464 return toolsVisible;
465 }
466
467 // Adds the new favorite to the appropriate favorite menu
468 public void addFavorite(StandardAction action) {
469 for (int i = 0; i < menuBar.getMenuCount(); i++) {
470 JMenu menu = menuBar.getMenu(i);
471
472 if ((menu.getText() != null) && menu.getText().equals("Favorites")) {
473 menu.add(action);
474 }
475 }
476 }
477
478 /**
479 * Set the visible state of the menu bar
480 *
481 * @param visible
482 */
483 public void setMenuBarVisible(boolean visible) {
484 if ((getJMenuBar() != null) && (getJMenuBar().isVisible() != visible)) {
485 getJMenuBar().setVisible(visible);
486 revalidate();
487 }
488 }
489
490 /**
491 * Set the visible state of the toolbar
492 *
493 * @param visible
494 */
495 public void setToolBarVisible(boolean visible) {
496 if ((getToolBar() != null) && (getToolBar().isVisible() != visible)) {
497 getToolBar().setVisible(visible);
498 revalidate();
499 }
500 }
501
502 /**
503 * Set the visible state of the statusbar
504 *
505 * @param visible
506 */
507 public void setStatusBarVisible(boolean visible) {
508 if ((getStatusBar() != null) &&
509 (getStatusBar().isVisible() != visible)) {
510 getStatusBar().setVisible(visible);
511 revalidate();
512 }
513 }
514
515 /**
516 * Set the visible state of all tools. This will set the toolbar, menu and
517 * status bar visible states to the value provided.
518 *
519 * @param visible
520 */
521 public void setToolsVisible(boolean visible) {
522 synchronized (getTreeLock()) {
523 if ((getToolBar() != null) &&
524 (getToolBar().isVisible() != visible)) {
525 getToolBar().setVisible(visible);
526 }
527
528 if ((getJMenuBar() != null) &&
529 (getJMenuBar().isVisible() != visible)) {
530 getJMenuBar().setVisible(visible);
531 }
532
533 if ((getStatusBar() != null) &&
534 (getStatusBar().isVisible() != visible)) {
535 getStatusBar().setVisible(visible);
536 }
537
538 toolsVisible = visible;
539 revalidate();
540 }
541 }
542
543 /**
544 * Show an exception message
545 *
546 * @param title
547 * @param message
548 */
549 public void showExceptionMessage(String title, String message) {
550 JOptionPane.showMessageDialog(this, message, title,
551 JOptionPane.ERROR_MESSAGE);
552 }
553
554 /**
555 * Show an error message with detail
556 *
557 * @param parent
558 * @param title
559 * @param exception
560 */
561 public static void showErrorMessage(Component parent, String title,
562 Throwable exception) {
563 showErrorMessage(parent, null, title, exception);
564 }
565
566 /**
567 * Show an error message with toggable detail
568 *
569 * @param parent
570 * @param mesg
571 * @param title
572 * @param exception
573 */
574 public static void showErrorMessage(Component parent, String mesg,
575 String title, Throwable exception) {
576 boolean details = false;
577
578 while (true) {
579 String[] opts = new String[] {
580 details ? "Hide Details" : "Details", "Ok"
581 };
582 StringBuffer buf = new StringBuffer();
583
584 if (mesg != null) {
585 buf.append(mesg);
586 }
587
588 appendException(exception, 0, buf, details);
589
590 MultilineLabel message = new MultilineLabel(buf.toString());
591 int opt = JOptionPane.showOptionDialog(parent, message, title,
592 JOptionPane.OK_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE,
593 null, opts, opts[1]);
594
595 if (opt == 0) {
596 details = !details;
597 } else {
598 break;
599 }
600 }
601 }
602
603 private static void appendException(Throwable exception, int level,
604 StringBuffer buf, boolean details) {
605 try {
606 if (((exception != null) && (exception.getMessage() != null)) &&
607 (exception.getMessage().length() > 0)) {
608 if (details && (level > 0)) {
609 buf.append("\n \nCaused by ...\n");
610 }
611
612 buf.append(exception.getMessage());
613 }
614
615 if (details) {
616 if (exception != null) {
617 if ((exception.getMessage() != null) &&
618 (exception.getMessage().length() == 0)) {
619 buf.append("\n \nCaused by ...");
620 } else {
621 buf.append("\n \n");
622 }
623 }
624
625 StringWriter sw = new StringWriter();
626
627 if (exception != null) {
628 exception.printStackTrace(new PrintWriter(sw));
629 }
630
631 buf.append(sw.toString());
632 }
633
634 try {
635 java.lang.reflect.Method method = exception.getClass()
636 .getMethod("getCause",
637 new Class[] { });
638 Throwable cause = (Throwable) method.invoke(exception, null);
639
640 if (cause != null) {
641 appendException(cause, level + 1, buf, details);
642 }
643 } catch (Exception e) {
644 }
645 } catch (Throwable ex) {
646 }
647 }
648
649 /**
650 * Returns the connected state of the panel
651 *
652 * @return
653 */
654 public abstract boolean isConnected();
655
656 /**
657 * Set the title of the container
658 *
659 * @param file
660 */
661 public void setContainerTitle(File file) {
662 String verString = "";
663
664 if (application != null) {
665 verString = ConfigurationLoader.getVersionString(application.getApplicationName(),
666 application.getApplicationVersion());
667 }
668
669 if (container != null) {
670 container.setContainerTitle((file == null) ? verString
671 : (verString + " [" +
672 file.getName() + "]"));
673 }
674 }
675
676 /**
677 * Gets the toolbar
678 *
679 * @return
680 */
681 public JToolBar getToolBar() {
682 return toolBar;
683 }
684
685 /**
686 * Get the context menu
687 *
688 * @return
689 */
690 public JPopupMenu getContextMenu() {
691 return contextMenu;
692 }
693
694 /**
695 * Get the main menu
696 *
697 * @return
698 */
699 public JMenuBar getJMenuBar() {
700 return menuBar;
701 }
702
703 /**
704 * Get the status bar
705 *
706 * @return
707 */
708 public StatusBar getStatusBar() {
709 return null;
710 }
711
712 /**
713 * Get the application attached to the panel
714 *
715 * @return
716 */
717 public SshToolsApplication getApplication() {
718 return application;
719 }
720
721 /**
722 * Get the icon for the panel
723 *
724 * @return
725 */
726 public abstract ResourceIcon getIcon();
727
728 public static class ActionMenu implements Comparable {
729 int weight;
730 int mnemonic;
731 String name;
732 String displayName;
733
734 public ActionMenu(String name, String displayName, int mnemonic,
735 int weight) {
736 this.name = name;
737 this.displayName = displayName;
738 this.mnemonic = mnemonic;
739 this.weight = weight;
740 }
741
742 public int compareTo(Object o) {
743 int i = new Integer(weight).compareTo(new Integer(
744 ((ActionMenu) o).weight));
745
746 return (i == 0)
747 ? displayName.compareTo(((ActionMenu) o).displayName) : i;
748 }
749 }
750
751 class ToolBarActionComparator implements Comparator {
752 public int compare(Object o1, Object o2) {
753 int i = ((Integer) ((StandardAction) o1).getValue(StandardAction.TOOLBAR_GROUP)).compareTo((Integer) ((StandardAction) o2).getValue(
754 StandardAction.TOOLBAR_GROUP));
755
756 return (i == 0)
757 ? ((Integer) ((StandardAction) o1).getValue(StandardAction.TOOLBAR_WEIGHT)).compareTo((Integer) ((StandardAction) o2).getValue(
758 StandardAction.TOOLBAR_WEIGHT)) : i;
759 }
760 }
761
762 class ContextActionComparator implements Comparator {
763 public int compare(Object o1, Object o2) {
764 int i = ((Integer) ((StandardAction) o1).getValue(StandardAction.CONTEXT_MENU_GROUP)).compareTo((Integer) ((StandardAction) o2).getValue(
765 StandardAction.CONTEXT_MENU_GROUP));
766
767 return (i == 0)
768 ? ((Integer) ((StandardAction) o1).getValue(StandardAction.CONTEXT_MENU_WEIGHT)).compareTo((Integer) ((StandardAction) o2).getValue(
769 StandardAction.CONTEXT_MENU_WEIGHT)) : i;
770 }
771 }
772
773 class MenuItemActionComparator implements Comparator {
774 public int compare(Object o1, Object o2) {
775 int i = ((Integer) ((StandardAction) o1).getValue(StandardAction.MENU_ITEM_GROUP)).compareTo((Integer) ((StandardAction) o2).getValue(
776 StandardAction.MENU_ITEM_GROUP));
777
778 return (i == 0)
779 ? ((Integer) ((StandardAction) o1).getValue(StandardAction.MENU_ITEM_WEIGHT)).compareTo((Integer) ((StandardAction) o2).getValue(
780 StandardAction.MENU_ITEM_WEIGHT)) : i;
781 }
782 }
783
784 class ConnectionFileFilter extends javax.swing.filechooser.FileFilter {
785 public boolean accept(File f) {
786 return f.isDirectory() ||
787 f.getName().toLowerCase().endsWith(".xml");
788 }
789
790 public String getDescription() {
791 return "Connection files (*.xml)";
792 }
793 }
794 }