1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.xnap.gui;
21
22 import java.awt.BorderLayout;
23 import java.awt.Component;
24 import java.awt.event.WindowAdapter;
25 import java.awt.event.WindowEvent;
26 import java.beans.PropertyChangeEvent;
27 import java.beans.PropertyChangeListener;
28 import java.io.*;
29
30 import javax.help.HelpBroker;
31 import javax.swing.Action;
32 import javax.swing.*;
33 import javax.swing.ImageIcon;
34 import javax.swing.JComponent;
35 import javax.swing.JFrame;
36 import javax.swing.JLabel;
37 import javax.swing.JSplitPane;
38 import javax.swing.JTabbedPane;
39 import javax.swing.JToolBar;
40 import javax.swing.SwingUtilities;
41 import javax.swing.ToolTipManager;
42 import javax.swing.UIManager;
43 import javax.swing.WindowConstants;
44 import javax.swing.event.ChangeEvent;
45 import javax.swing.event.ChangeListener;
46
47 import org.apache.log4j.Logger;
48 import org.xnap.XNap;
49 import org.xnap.action.ToggleAction;
50 import org.xnap.gui.component.ToggleableIconPane;
51 import org.xnap.gui.component.XNapToolBarButton;
52 import org.xnap.gui.component.XNapToolBarToggleButton;
53 import org.xnap.gui.theme.ThemeManager;
54 import org.xnap.gui.util.HelpManager;
55 import org.xnap.gui.util.*;
56 import org.xnap.io.Library;
57 import org.xnap.plugin.PluginManager;
58 import org.xnap.util.Preferences;
59 import org.xnap.util.SystemHelper;
60 import org.xnap.util.Updater;
61
62 import ziga.gui.WindowsDesktopIndicator;
63
64 public class XNapFrame extends JFrame
65 implements ChangeListener, PropertyChangeListener
66 {
67
68
69
70
71
72 private static Logger logger = Logger.getLogger(XNapFrame.class);
73 private static Preferences prefs = Preferences.getInstance();
74 private static XNapFrame singleton = null;
75
76 private ToggleableIconPane pane;
77 private JSplitPane jspV;
78
79 private MainToolBar jtbMain;
80
81 private ChatPanel jpChat;
82 private HotlistPanel jpHotlist;
83 private LibraryPanel jpLibrary;
84 private SearchPanel jpSearch;
85 private TransferPanel jpTransfer;
86
87 private WindowsDesktopIndicator wdi;
88
89
90
91
92
93 public XNapFrame()
94 {
95 singleton = this;
96 setContentPane(new JPanel());
97
98
99 try {
100 SplashWindow.incProgress(1, XNap.tr("Enabling Window system tray icon"));
101
102 if (SystemHelper.isZigaDllLoaded) {
103 wdi = new WindowsDesktopIndicator(this);
104 wdi.show("xnap.ico", XNap.tr("XNap"));
105 wdi.hide();
106 }
107 }
108 catch (Exception e) {
109 logger.warn("Could not enable system tray through ziga.dll", e);
110 }
111
112
113 if (SystemHelper.isMacOSX) {
114 SplashWindow.incProgress(1, XNap.tr("Registering Mac OS handler"));
115
116 try {
117 org.xnap.platform.macos.MacOS141Handler.register();
118 }
119 catch (Exception e) {
120 logger.warn("Could not register MacOSX 1.4.1 handler", e);
121
122 try {
123 org.xnap.platform.macos.MacOSHandler.register();
124 }
125 catch (Exception e2) {
126 logger.warn("could not register MacOSX handler", e2);
127 }
128 }
129 }
130
131
132 ToolTipManager.sharedInstance().setDismissDelay(60 * 1000);
133
134 setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
135
136
137
138
139
140
141
142 if (!prefs.getSeenStartupWizard()) {
143 SplashWindow.incProgress(5, XNap.tr("Loading Wizard"));
144 StartupWizardDialog.showDialog(this, true);
145 }
146
147 initialize();
148
149 SplashWindow.incProgress(5, XNap.tr("Enabling plugins"));
150
151 PluginManager.getInstance().guiStarted();
152 Updater.guiStarted();
153
154
155
156
157
158
159 SplashWindow.setProgress(100, XNap.tr("Done"));
160 }
161
162
163
164 private void initialize()
165 {
166
167 SplashWindow.incProgress(5, XNap.tr("Setting up Toolbars"));
168 initializeToolbars();
169
170
171
172 SplashWindow.incProgress(5, XNap.tr("Setting up Menu"));
173 initializeMenu();
174
175 SplashWindow.incProgress(5, XNap.tr("Setting up GUI"));
176 initializePanels();
177
178 SplashWindow.incProgress(5, XNap.tr("Setting up Status Bar"));
179 initializeStatusBar();
180
181 SplashWindow.incProgress(5, XNap.tr("Setting up Application"));
182 setTitle(XNap.tr("XNap - Java Peer-To-Peer Client"));
183
184 ImageIcon icon = IconHelper.getImage("16/xnap.png");
185 if (icon != null) {
186 setIconImage(icon.getImage());
187 }
188
189 SplashWindow.incProgress(5, XNap.tr("Setting up Hotlist"));
190
191 jpHotlist.add(new UploadSettingsPanel(), BorderLayout.SOUTH);
192
193 jspV = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
194 jspV.setBorder(GUIHelper.createEmptyBorder());
195 jspV.setDividerLocation(prefs.getHotlistDividerLocation());
196 jspV.setDividerSize(1);
197
198 jspV.setResizeWeight(1.0);
199 jspV.add(pane);
200 jspV.add(jpHotlist);
201
202 SplashWindow.incProgress(5, XNap.tr("Setting up Main Window"));
203
204 getContentPane().setLayout(new BorderLayout());
205 getContentPane().add(jspV, BorderLayout.CENTER);
206 getContentPane().add(jtbMain, BorderLayout.NORTH);
207 getContentPane().add(StatusBar.getInstance(), BorderLayout.SOUTH);
208 pack();
209
210 setBounds(prefs.getWindowX(), prefs.getWindowY(),
211 prefs.getWindowWidth(), prefs.getWindowHeight());
212
213
214 addWindowListener(new WindowListener());
215
216 HelpManager.enableHelpKeys(getRootPane(), "introduction", null);
217
218
219 prefs.addPropertyChangeListener(this);
220 }
221
222 private void initializeMenu()
223 {
224 setJMenuBar(new MainMenuBar());
225 }
226
227 private void initializePanels()
228 {
229
230 pane = new ToggleableIconPane(prefs.getUseTabbedPane());
231 pane.addChangeListener(this);
232 updatePaneBorder();
233
234
235 jpSearch = new SearchPanel();
236 jpSearch.setName("search");
237 insertTab(XNap.tr("Search"), IconHelper.getListIcon("find.png"),
238 jpSearch, 0);
239
240 SplashWindow.incProgress(5);
241
242
243 jpTransfer = new TransferPanel();
244 jpTransfer.setName("transfer");
245 insertTab(XNap.tr("Transfer"),
246 IconHelper.getListIcon("connect_established.png"),
247 jpTransfer, 1);
248
249 SplashWindow.incProgress(5, XNap.tr("Setting up Chat Panel"));
250
251
252 updateChatPanel();
253
254 SplashWindow.incProgress(5, XNap.tr("Setting up Library Panel"));
255
256
257 updateLibraryPanel();
258
259
260 jpHotlist = new HotlistPanel();
261 jpHotlist.setBorder(GUIHelper.createRaisedBorder());
262 jpHotlist.setVisible(prefs.getBoolean("showHotlist"));
263 }
264
265 private void initializeStatusBar()
266 {
267 JLabel jlPresence = new PresenceStatusPanel();
268 jlPresence.setName(XNap.tr("Presence"));
269 StatusBar.getInstance().appendComponent(jlPresence);
270
271 StatusPanel spLibrary = new StatusPanel();
272 spLibrary.setIcon(IconHelper.getStatusBarIcon("contents.png"));
273 spLibrary.setName(XNap.tr("Library"));
274 Library.getInstance().setStatusListener(spLibrary);
275 StatusBar.getInstance().appendComponent(spLibrary);
276 }
277
278 private void initializeToolbars()
279 {
280
281 jtbMain = new MainToolBar();
282 jtbMain.setVisible(prefs.getBoolean("showMainToolbar"));
283 }
284
285 /***
286 * Kills the instance of XNapFrame.
287 */
288 public static void disposeInstance()
289 {
290 Library.getInstance().setStatusListener(null);
291
292 if (singleton.wdi != null) {
293 singleton.wdi.hide();
294 Thread.yield();
295 }
296
297 singleton.dispose();
298 singleton = null;
299 }
300
301 /***
302 * Inserts a tab at <code>index</code>.
303 */
304 public void insertTab(String title, Icon icon, JComponent component,
305 int index)
306 {
307 pane.insertTab(title, icon, component, index);
308
309 if (component instanceof JTabbedPane) {
310 ((JTabbedPane)component).addChangeListener(this);
311 }
312 }
313
314 /***
315 * Adds a tab above the chat panel.
316 */
317 public void insertTab(String title, Icon icon, JComponent component)
318 {
319 insertTab(title, icon, component, pane.getTabCount());
320 }
321
322 /***
323 * Removes <code>component</code>.
324 */
325 public void removeTab(Component component)
326 {
327 int index = pane.indexOfComponent(component);
328 if (index != -1) {
329 pane.remove(component);
330
331 if (component instanceof JTabbedPane) {
332 ((JTabbedPane)component).removeChangeListener(this);
333 }
334 }
335 }
336
337 /***
338 * Returns the instance of XNapFrame.
339 *
340 * @return null, if the gui was not started
341 */
342 public static XNapFrame getInstance()
343 {
344 return singleton;
345 }
346
347 /***
348 * Returns the chat panel.
349 */
350 public ChatPanel getChatPanel()
351 {
352 if (jpChat == null) {
353 jpChat = new ChatPanel();
354 jpChat.setName("chat");
355 jpChat.addChangeListener(this);
356 }
357 return jpChat;
358 }
359
360 /***
361 * Returns the library panel.
362 */
363 public LibraryPanel getLibraryPanel()
364 {
365 if (jpLibrary == null) {
366 jpLibrary = new LibraryPanel();
367 jpLibrary.setName("library");
368 }
369 return jpLibrary;
370 }
371
372 /***
373 * Returns the hotlist panel.
374 */
375 public HotlistPanel getHotlistPanel()
376 {
377 return jpHotlist;
378 }
379
380 /***
381 * Returns the main menu bar.
382 */
383 public MainMenuBar getMainMenuBar()
384 {
385 return (MainMenuBar)getJMenuBar();
386 }
387
388 /***
389 * Returns the main tool bar.
390 */
391 public MainToolBar getMainToolBar()
392 {
393 return jtbMain;
394 }
395
396 /***
397 * Returns the main pane.
398 */
399 public ToggleableIconPane getPane()
400 {
401 return pane;
402 }
403
404 /***
405 * Returns the search panel.
406 */
407 public SearchPanel getSearchPanel()
408 {
409 return jpSearch;
410 }
411
412 /***
413 * Returns the transfer panel.
414 */
415 public TransferPanel getTransferPanel()
416 {
417 return jpTransfer;
418 }
419
420 /***
421 * Invoked by {@link XNap#startGUI(boolean)} after gui is
422 * visible. Checks if important preferences like directory
423 * settings are valid.
424 */
425 public void guiVisible()
426 {
427 boolean showPrefs = false;
428
429 File f = new File(prefs.getDownloadDir());
430 if (!f.isDirectory() && !f.mkdirs()) {
431 Dialogs.error
432 (this,
433 XNap.tr("Could not create download directory."));
434 showPrefs = true;
435 }
436
437 f = new File(prefs.getIncompleteDir());
438 if (!f.isDirectory() && !f.mkdirs()) {
439 Dialogs.error
440 (this,
441 XNap.tr("Could not create incomplete directory."));
442 showPrefs = true;
443 }
444
445 if (showPrefs) {
446 PreferencesDialog.showDialog(this);
447 }
448 }
449
450 public void chatBlink()
451 {
452 if (pane.getSelectedComponent() != jpChat) {
453 int i = pane.indexOfComponent(jpChat);
454 if (i != -1) {
455 pane.blink(i, IconHelper.getListIcon("penguin.png"));
456 }
457 }
458 }
459
460 public void propertyChange(PropertyChangeEvent e)
461 {
462 String p = e.getPropertyName();
463
464 if (p.equals("lookAndFeel") || p.equals("theme")) {
465
466
467 pane.setTabbed(true);
468
469 updateLookAndFeel();
470
471
472 pane.setTabbed(prefs.getUseTabbedPane());
473 }
474 else if (p.equals("useTabbedPane")) {
475 pane.setTabbed(prefs.getUseTabbedPane());
476 }
477 else if (p.equals("showHotlist")) {
478 if (jpHotlist.isVisible()) {
479 prefs.setHotlistDividerLocation(jspV.getDividerLocation());
480 }
481 else {
482 jspV.setDividerLocation(prefs.getHotlistDividerLocation());
483 }
484 jpHotlist.setVisible(prefs.getBoolean("showHotlist"));
485
486 if (jpHotlist.getWidth()
487 < jpHotlist.getMinimumSize().getWidth()) {
488
489 jspV.setDividerLocation
490 ((int)(jspV.getWidth() - jpHotlist.getMinimumSize().getWidth()));
491 }
492 }
493 else if (p.equals("showChatPanel")) {
494 updateChatPanel();
495 }
496 else if (p.equals("showLibraryPanel")) {
497 updateLibraryPanel();
498 }
499 else if (p.equals("showMainToolbar")) {
500 jtbMain.setVisible(prefs.getBoolean("showMainToolbar"));
501 }
502 }
503
504 /***
505 * Cleanly exits XNap.
506 *
507 * @param showCloseDialog if true, a confirmation dialog will be shown
508 * @return false, if exit was cancelled by user
509 */
510 public boolean exit(boolean showCloseDialog)
511 {
512 if (showCloseDialog && !Dialogs.showCloseDialog(this)) {
513 return false;
514 }
515
516 XNap.exit();
517
518 return true;
519 }
520
521 /***
522 * Cleanly exits the application. Prompts the user for confirmation if
523 * confirmation is turned on.
524 *
525 * @return false, if exit was cancelled by user
526 * @see #exit(boolean)
527 */
528 public boolean exit()
529 {
530 return exit(Dialogs.getShowDialog("Close"));
531 }
532
533 public void stop()
534 {
535 PluginManager.getInstance().guiStopped();
536
537
538 jpSearch.savePrefs();
539 jpTransfer.savePrefs();
540 if (jpChat != null) {
541 jpChat.savePrefs();
542 }
543 if (jpLibrary != null) {
544 jpLibrary.savePrefs();
545 }
546
547
548 if (jpHotlist.isVisible()) {
549 prefs.setHotlistDividerLocation(jspV.getDividerLocation());
550 }
551
552
553
554 prefs.setWindowHeight(getBounds().getSize().height
555 + prefs.getCorrectivePixels());
556 prefs.setWindowWidth(getBounds().getSize().width);
557 prefs.setWindowX(getBounds().getLocation().x);
558 prefs.setWindowY(getBounds().getLocation().y);
559
560 disposeInstance();
561 }
562
563 /***
564 * Changes the icons on the main toolbar if the panel is switched.
565 */
566 public void stateChanged(ChangeEvent e)
567 {
568 Object source = pane.getSelectedComponent();
569
570
571 getMainToolBar().removeAllTemporaries();
572
573 if (source instanceof JTabbedPane) {
574 if (((JTabbedPane)source).getSelectedComponent() != null) {
575 source = ((JTabbedPane)source).getSelectedComponent();
576 }
577 }
578 else if (source == jpChat) {
579 if (pane.getSelectedComponent() != jpChat) {
580 return;
581 }
582
583
584
585 source = jpChat.getSelectedTab();
586 }
587
588 if (source instanceof ActionProvider) {
589 Action[] actions = ((ActionProvider)source).getActions();
590 if (actions != null) {
591 for (int i = 0; i < actions.length; i++) {
592 if (actions[i] != null) {
593 if (actions[i] instanceof ToggleAction) {
594 getMainToolBar().addTemporary
595 (new XNapToolBarToggleButton
596 ((ToggleAction)actions[i]));
597 }
598 else {
599 getMainToolBar().addTemporary
600 (new XNapToolBarButton(actions[i]));
601 }
602 }
603 else {
604 JSeparator js = new JToolBar.Separator();
605 js.setOrientation(SwingConstants.VERTICAL);
606 getMainToolBar().addTemporary(js);
607 }
608 }
609 }
610 }
611
612
613 getMainToolBar().updateUI();
614 }
615
616 private void updateChatPanel()
617 {
618 if (prefs.getBoolean("showChatPanel")) {
619 insertTab(XNap.tr("Chat"),
620 IconHelper.getListIcon("mail_generic2.png"),
621 getChatPanel(), 2);
622 }
623 else if (jpChat != null) {
624 pane.remove(jpChat);
625 }
626 }
627
628 private void updateLibraryPanel()
629 {
630 if (prefs.getBoolean("showLibraryPanel")) {
631 boolean chatVisible
632 = (jpChat != null && pane.indexOfComponent(jpChat) != -1);
633 insertTab(XNap.tr("Library"),
634 IconHelper.getListIcon("contents.png"),
635 getLibraryPanel(), chatVisible ? 3 : 2);
636 }
637 else if (jpLibrary != null) {
638 pane.remove(jpLibrary);
639 }
640 }
641
642 /***
643 * Switches the look and feel. */
644 public static void updateLookAndFeel()
645 {
646 ThemeManager.setTheme(prefs.getIconTheme());
647 if (!ThemeManager.setTheme(prefs.getTheme())) {
648 ThemeManager.setTheme(Preferences.DEFAULT_THEME);
649 }
650
651 try {
652 Class.forName(prefs.getLookAndFeel());
653 UIManager.setLookAndFeel(prefs.getLookAndFeel());
654
655 updatePaneBorder();
656 }
657 catch (Exception e) {
658 logger.warn("could not change look and feel", e);
659 }
660
661 if (getInstance() != null) {
662 SwingUtilities.updateComponentTreeUI(getInstance());
663 }
664 SplashWindow.updateComponentTree();
665 }
666
667 /***
668 * The jgoodies l&fs use embedded tabs, so we need an extra border.
669 */
670 private static void updatePaneBorder()
671 {
672 if (getInstance() != null && getInstance().pane != null) {
673 getInstance().pane.setBorder
674 (prefs.getLookAndFeel().startsWith("com.jgoodies.plaf")
675 ? GUIHelper.createRaisedBorder()
676 : GUIHelper.createEmptyBorder());
677 }
678 }
679
680
681
682 /***
683 * Handles the Windows system tray icon.
684 */
685 private class WindowListener extends WindowAdapter
686 {
687
688 public void windowDeiconified(WindowEvent e)
689 {
690 }
691
692 public void windowIconified(WindowEvent e)
693 {
694 if (wdi != null) {
695 wdi.show("xnap.ico", XNap.tr("XNap"));
696 Thread.yield();
697 wdi.hideFrame();
698 }
699 }
700
701 public void windowClosing (WindowEvent evt)
702 {
703 exit();
704 }
705
706 }
707
708 }