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.common.mru.MRUList;
29 import com.sshtools.common.mru.MRUListModel;
30 import com.sshtools.common.util.BrowserLauncher;
31
32 import com.sshtools.j2ssh.configuration.ConfigurationLoader;
33 import com.sshtools.j2ssh.io.IOUtil;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37
38 import java.awt.Color;
39 import java.awt.Component;
40 import java.awt.Cursor;
41 import java.awt.Font;
42 import java.awt.GridBagConstraints;
43 import java.awt.GridBagLayout;
44 import java.awt.Insets;
45 import java.awt.event.MouseAdapter;
46 import java.awt.event.MouseEvent;
47
48 import java.io.File;
49 import java.io.FileInputStream;
50 import java.io.FileOutputStream;
51 import java.io.FilePermission;
52 import java.io.IOException;
53 import java.io.InputStream;
54 import java.io.PrintWriter;
55
56 import java.security.AccessControlException;
57 import java.security.AccessController;
58
59 import java.util.Iterator;
60 import java.util.Vector;
61
62 import javax.swing.BorderFactory;
63 import javax.swing.Icon;
64 import javax.swing.JLabel;
65 import javax.swing.JOptionPane;
66 import javax.swing.JPanel;
67 import javax.swing.LookAndFeel;
68 import javax.swing.UIManager;
69
70
71 /**
72 * An abstract application class that provides container management, look
73 * and feel configuration and most recently used menus.
74 *
75 * @author Brett Smith
76 * @version $Revision: 1.19 $
77 */
78 public abstract class SshToolsApplication {
79 /** */
80 public final static String PREF_CONNECTION_LAST_HOST = "apps.connection.lastHost";
81
82 /** */
83 public final static String PREF_CONNECTION_LAST_USER = "apps.connection.lastUser";
84
85 /** */
86 public final static String PREF_CONNECTION_LAST_PORT = "apps.connection.lastPort";
87
88 /** */
89 public final static String PREF_CONNECTION_LAST_KEY = "apps.connection.lastKey";
90
91 /** */
92 public final static String PREF_LAF = "apps.laf";
93
94 /** */
95 public final static String CROSS_PLATFORM_LAF = "CROSS_PLATFORM";
96
97 /** */
98 public final static String DEFAULT_LAF = "DEFAULT";
99
100 /** */
101 public final static String SYSTEM_LAF = "SYSTEM";
102
103 /** */
104 protected static Vector containers = new Vector();
105
106 /** */
107 protected static Log log = LogFactory.getLog(SshToolsApplication.class);
108
109 /** */
110 protected static MRUListModel mruModel;
111 private static UIManager.LookAndFeelInfo[] allLookAndFeelInfo;
112
113 static {
114 UIManager.LookAndFeelInfo[] i;
115
116 try {
117 i = UIManager.getInstalledLookAndFeels();
118 } catch (Throwable t) {
119 i = new UIManager.LookAndFeelInfo[0];
120 }
121
122 allLookAndFeelInfo = new UIManager.LookAndFeelInfo[i.length + 3];
123 System.arraycopy(i, 0, allLookAndFeelInfo, 0, i.length);
124 allLookAndFeelInfo[i.length] = new UIManager.LookAndFeelInfo("Default",
125 DEFAULT_LAF);
126
127 allLookAndFeelInfo[i.length + 1] = new UIManager.LookAndFeelInfo("Cross Platform",
128 CROSS_PLATFORM_LAF);
129 allLookAndFeelInfo[i.length + 2] = new UIManager.LookAndFeelInfo("System",
130 SYSTEM_LAF);
131 }
132
133 /** */
134 protected Class panelClass;
135
136 /** */
137 protected Class defaultContainerClass;
138
139 /** */
140 protected java.util.List additionalOptionsTabs;
141
142 /**
143 * Creates a new SshToolsApplication object.
144 *
145 * @param panelClass
146 * @param defaultContainerClass
147 */
148 public SshToolsApplication(Class panelClass, Class defaultContainerClass) {
149 this.panelClass = panelClass;
150 this.defaultContainerClass = defaultContainerClass;
151 additionalOptionsTabs = new java.util.ArrayList();
152
153 try {
154 if (System.getSecurityManager() != null) {
155 AccessController.checkPermission(new FilePermission(
156 "<<ALL FILES>>", "write"));
157 }
158
159 File a = getApplicationPreferencesDirectory();
160
161 if (a == null) {
162 throw new AccessControlException(
163 "Application preferences directory not specified.");
164 }
165
166 InputStream in = null;
167 MRUList mru = new MRUList();
168
169 try {
170 File f = new File(a, getApplicationName() + ".mru");
171
172 if (f.exists()) {
173 if (log.isDebugEnabled()) {
174 log.debug("Loading MRU from " + f.getAbsolutePath());
175 }
176
177 in = new FileInputStream(f);
178 mru.reload(in);
179 } else {
180 if (log.isDebugEnabled()) {
181 log.debug("MRU file " + f.getAbsolutePath() +
182 " doesn't exist, creating empty list");
183 }
184 }
185 } catch (Exception e) {
186 log.error("Could not load MRU list.", e);
187 } finally {
188 IOUtil.closeStream(in);
189 }
190
191 mruModel = new MRUListModel();
192 mruModel.setMRUList(mru);
193 } catch (AccessControlException ace) {
194 log.error("Could not load MRU.", ace);
195 }
196 }
197
198 /**
199 *
200 *
201 * @return
202 */
203 public static UIManager.LookAndFeelInfo[] getAllLookAndFeelInfo() {
204 return allLookAndFeelInfo;
205 }
206
207 /**
208 *
209 *
210 * @return
211 */
212 public MRUListModel getMRUModel() {
213 return mruModel;
214 }
215
216 /**
217 *
218 *
219 * @return
220 */
221 public abstract String getApplicationName();
222
223 /**
224 *
225 *
226 * @return
227 */
228 public abstract String getApplicationVersion();
229
230 /**
231 *
232 *
233 * @return
234 */
235 public abstract Icon getApplicationLargeIcon();
236
237 /**
238 *
239 *
240 * @return
241 */
242 public abstract String getAboutLicenseDetails();
243
244 /**
245 *
246 *
247 * @return
248 */
249 public abstract String getAboutURL();
250
251 /**
252 *
253 *
254 * @return
255 */
256 public abstract String getAboutAuthors();
257
258 /**
259 *
260 *
261 * @return
262 */
263 public abstract File getApplicationPreferencesDirectory();
264
265 /**
266 *
267 *
268 * @return
269 */
270 public OptionsTab[] getAdditionalOptionsTabs() {
271 OptionsTab[] t = new OptionsTab[additionalOptionsTabs.size()];
272 additionalOptionsTabs.toArray(t);
273
274 return t;
275 }
276
277 /**
278 *
279 *
280 * @param tab
281 */
282 public void addAdditionalOptionsTab(OptionsTab tab) {
283 if (!additionalOptionsTabs.contains(tab)) {
284 additionalOptionsTabs.add(tab);
285 }
286 }
287
288 /**
289 *
290 *
291 * @param tab
292 */
293 public void removeAdditionalOptionsTab(OptionsTab tab) {
294 additionalOptionsTabs.remove(tab);
295 }
296
297 /**
298 *
299 *
300 * @param title
301 */
302 public void removeAdditionalOptionsTab(String title) {
303 OptionsTab t = getOptionsTab(title);
304
305 if (t != null) {
306 removeAdditionalOptionsTab(t);
307 }
308 }
309
310 /**
311 *
312 *
313 * @param title
314 *
315 * @return
316 */
317 public OptionsTab getOptionsTab(String title) {
318 for (Iterator i = additionalOptionsTabs.iterator(); i.hasNext();) {
319 OptionsTab t = (OptionsTab) i.next();
320
321 if (t.getTabTitle().equals(title)) {
322 return t;
323 }
324 }
325
326 return null;
327 }
328
329 /**
330 *
331 */
332 public void exit() {
333 log.debug("Exiting application");
334 PreferencesStore.savePreferences();
335
336 FileOutputStream out = null;
337 File a = getApplicationPreferencesDirectory();
338
339 if (a != null) {
340 try {
341 File f = new File(getApplicationPreferencesDirectory(),
342 getApplicationName() + ".mru");
343 ;
344
345 if (log.isDebugEnabled()) {
346 log.debug("Saving MRU to " + f.getAbsolutePath());
347 }
348
349 out = new FileOutputStream(f);
350
351 PrintWriter w = new PrintWriter(out, true);
352 w.println(mruModel.getMRUList().toString());
353 } catch (IOException ioe) {
354 log.error("Could not save MRU. ", ioe);
355 } finally {
356 IOUtil.closeStream(out);
357 }
358 } else {
359 log.debug(
360 "Not saving preferences because no preferences directory is available.");
361 }
362
363 System.exit(0);
364 }
365
366 /**
367 *
368 *
369 * @return
370 */
371 public int getContainerCount() {
372 return containers.size();
373 }
374
375 /**
376 *
377 *
378 * @param idx
379 *
380 * @return
381 */
382 public SshToolsApplicationContainer getContainerAt(int idx) {
383 return (SshToolsApplicationContainer) containers.elementAt(idx);
384 }
385
386 /**
387 *
388 *
389 * @param panel
390 *
391 * @return
392 */
393 public SshToolsApplicationContainer getContainerForPanel(
394 SshToolsApplicationPanel panel) {
395 for (Iterator i = containers.iterator(); i.hasNext();) {
396 SshToolsApplicationContainer c = (SshToolsApplicationContainer) i.next();
397
398 if (c.getApplicationPanel() == panel) {
399 return c;
400 }
401 }
402
403 return null;
404 }
405
406 /**
407 *
408 *
409 * @param container
410 */
411 public void closeContainer(SshToolsApplicationContainer container) {
412 if (log.isDebugEnabled()) {
413 log.debug("Asking " + container + " if it can close");
414 }
415
416 if (container.getApplicationPanel().canClose()) {
417 if (log.isDebugEnabled()) {
418 log.debug("Closing");
419
420 for (Iterator i = containers.iterator(); i.hasNext();) {
421 log.debug(i.next() + " is currently open");
422 }
423 }
424
425 container.getApplicationPanel().close();
426 container.closeContainer();
427 containers.removeElement(container);
428
429 if (containers.size() == 0) {
430 exit();
431 } else {
432 log.debug(
433 "Not closing completely because there are containers still open");
434
435 for (Iterator i = containers.iterator(); i.hasNext();) {
436 log.debug(i.next() + " is still open");
437 }
438 }
439 }
440 }
441
442 /**
443 * Show an 'About' dialog
444 *
445 *
446 */
447 public void showAbout(Component parent) {
448 JPanel p = new JPanel(new GridBagLayout());
449 p.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
450
451 GridBagConstraints gBC = new GridBagConstraints();
452 gBC.anchor = GridBagConstraints.CENTER;
453 gBC.fill = GridBagConstraints.HORIZONTAL;
454 gBC.insets = new Insets(1, 1, 1, 1);
455
456 JLabel a = new JLabel(getApplicationName());
457 a.setFont(a.getFont().deriveFont(24f));
458 UIUtil.jGridBagAdd(p, a, gBC, GridBagConstraints.REMAINDER);
459
460 JLabel v = new JLabel(ConfigurationLoader.getVersionString(
461 getApplicationName(), getApplicationVersion()));
462 v.setFont(v.getFont().deriveFont(10f));
463 UIUtil.jGridBagAdd(p, v, gBC, GridBagConstraints.REMAINDER);
464
465 MultilineLabel x = new MultilineLabel(getAboutAuthors());
466 x.setBorder(BorderFactory.createEmptyBorder(8, 0, 8, 0));
467 x.setFont(x.getFont().deriveFont(12f));
468 UIUtil.jGridBagAdd(p, x, gBC, GridBagConstraints.REMAINDER);
469
470 MultilineLabel c = new MultilineLabel(getAboutLicenseDetails());
471 c.setFont(c.getFont().deriveFont(10f));
472 UIUtil.jGridBagAdd(p, c, gBC, GridBagConstraints.REMAINDER);
473
474 final JLabel h = new JLabel(getAboutURL());
475 h.setForeground(Color.blue);
476 h.setFont(new Font(h.getFont().getName(), Font.BOLD, 10));
477 h.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
478 h.addMouseListener(new MouseAdapter() {
479 public void mouseClicked(MouseEvent evt) {
480 try {
481 BrowserLauncher.openURL(getAboutURL());
482 } catch (IOException ioe) {
483 ioe.printStackTrace();
484 }
485 }
486 });
487 UIUtil.jGridBagAdd(p, h, gBC, GridBagConstraints.REMAINDER);
488 JOptionPane.showMessageDialog(parent, p, "About",
489 JOptionPane.PLAIN_MESSAGE, getApplicationLargeIcon());
490 }
491
492 /**
493 *
494 *
495 * @return
496 *
497 * @throws SshToolsApplicationException
498 */
499 public SshToolsApplicationContainer newContainer()
500 throws SshToolsApplicationException {
501 SshToolsApplicationContainer container = null;
502
503 try {
504 container = (SshToolsApplicationContainer) defaultContainerClass.newInstance();
505 newContainer(container);
506
507 return container;
508 } catch (Throwable t) {
509 throw new SshToolsApplicationException(t);
510 }
511 }
512
513 /**
514 *
515 *
516 * @param container
517 *
518 * @throws SshToolsApplicationException
519 */
520 public void newContainer(SshToolsApplicationContainer container)
521 throws SshToolsApplicationException {
522 try {
523 SshToolsApplicationPanel panel = (SshToolsApplicationPanel) panelClass.newInstance();
524 panel.init(this);
525 panel.rebuildActionComponents();
526 panel.setAvailableActions();
527 container.init(this, panel);
528 panel.setContainer(container);
529
530 if (!container.isContainerVisible()) {
531 container.setContainerVisible(true);
532 }
533
534 containers.addElement(container);
535 } catch (Throwable t) {
536 throw new SshToolsApplicationException(t);
537 }
538 }
539
540 /**
541 *
542 *
543 * @param container
544 * @param newContainerClass
545 *
546 * @return
547 *
548 * @throws SshToolsApplicationException
549 */
550 public SshToolsApplicationContainer convertContainer(
551 SshToolsApplicationContainer container, Class newContainerClass)
552 throws SshToolsApplicationException {
553 log.info("Converting container of class " +
554 container.getClass().getName() + " to " +
555 newContainerClass.getName());
556
557 int idx = containers.indexOf(container);
558
559 if (idx == -1) {
560 throw new SshToolsApplicationException(
561 "Container is not being manager by the application.");
562 }
563
564 SshToolsApplicationContainer newContainer = null;
565
566 try {
567 container.closeContainer();
568
569 SshToolsApplicationPanel panel = container.getApplicationPanel();
570 newContainer = (SshToolsApplicationContainer) newContainerClass.newInstance();
571 newContainer.init(this, panel);
572 panel.setContainer(newContainer);
573
574 if (!newContainer.isContainerVisible()) {
575 newContainer.setContainerVisible(true);
576 }
577
578 containers.setElementAt(newContainer, idx);
579
580 return newContainer;
581 } catch (Throwable t) {
582 throw new SshToolsApplicationException(t);
583 }
584 }
585
586 /**
587 *
588 *
589 * @param args
590 *
591 * @throws SshToolsApplicationException
592 */
593 public void init(String[] args) throws SshToolsApplicationException {
594 File f = getApplicationPreferencesDirectory();
595
596 if (f != null) {
597 //
598 PreferencesStore.init(new File(f,
599 getApplicationName() + ".properties"));
600 log.info("Preferences will be saved to " + f.getAbsolutePath());
601 } else {
602 log.warn("No preferences can be saved.");
603 }
604
605 try {
606 setLookAndFeel(PreferencesStore.get(PREF_LAF, SYSTEM_LAF));
607 UIManager.put("OptionPane.errorIcon",
608 new ResourceIcon(SshToolsApplication.class, "dialog-error4.png"));
609 UIManager.put("OptionPane.informationIcon",
610 new ResourceIcon(SshToolsApplication.class,
611 "dialog-information.png"));
612 UIManager.put("OptionPane.warningIcon",
613 new ResourceIcon(SshToolsApplication.class,
614 "dialog-warning2.png"));
615 UIManager.put("OptionPane.questionIcon",
616 new ResourceIcon(SshToolsApplication.class,
617 "dialog-question3.png"));
618 } catch (Throwable t) {
619 log.error(t);
620 }
621 }
622
623 /**
624 *
625 *
626 * @param className
627 *
628 * @throws Exception
629 */
630 public static void setLookAndFeel(String className)
631 throws Exception {
632 LookAndFeel laf = null;
633
634 if (!className.equals(DEFAULT_LAF)) {
635 if (className.equals(SYSTEM_LAF)) {
636 String systemLaf = UIManager.getSystemLookAndFeelClassName();
637 log.debug("System Look And Feel is " + systemLaf);
638 laf = (LookAndFeel) Class.forName(systemLaf).newInstance();
639 } else if (className.equals(CROSS_PLATFORM_LAF)) {
640 String crossPlatformLaf = UIManager.getCrossPlatformLookAndFeelClassName();
641 log.debug("Cross Platform Look And Feel is " +
642 crossPlatformLaf);
643 laf = (LookAndFeel) Class.forName(crossPlatformLaf).newInstance();
644 } else {
645 laf = (LookAndFeel) Class.forName(className).newInstance();
646 }
647 }
648
649 // Now actually set the look and feel
650 if (laf != null) {
651 log.info("Setting look and feel " + laf.getName() + " (" +
652 laf.getClass().getName() + ")");
653 UIManager.setLookAndFeel(laf);
654 UIManager.put("EditorPane.font", UIManager.getFont("TextArea.font"));
655 }
656 }
657 }