1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.log4j.chainsaw;
19
20 import java.awt.BorderLayout;
21 import java.awt.Component;
22 import java.awt.Container;
23 import java.awt.Dimension;
24 import java.awt.Event;
25 import java.awt.Frame;
26 import java.awt.Point;
27 import java.awt.Toolkit;
28 import java.awt.event.ActionEvent;
29 import java.awt.event.ActionListener;
30 import java.awt.event.InputEvent;
31 import java.awt.event.KeyEvent;
32 import java.awt.event.MouseAdapter;
33 import java.awt.event.MouseEvent;
34 import java.awt.event.WindowAdapter;
35 import java.awt.event.WindowEvent;
36 import java.beans.PropertyChangeEvent;
37 import java.beans.PropertyChangeListener;
38 import java.io.File;
39 import java.io.IOException;
40 import java.lang.Thread.UncaughtExceptionHandler;
41 import java.lang.reflect.Method;
42 import java.net.MalformedURLException;
43 import java.net.URL;
44 import java.security.AllPermission;
45 import java.security.CodeSource;
46 import java.security.PermissionCollection;
47 import java.security.Permissions;
48 import java.security.Policy;
49 import java.util.ArrayList;
50 import java.util.Enumeration;
51 import java.util.HashMap;
52 import java.util.Iterator;
53 import java.util.List;
54 import java.util.Map;
55 import java.util.Set;
56
57 import javax.swing.AbstractAction;
58 import javax.swing.Action;
59 import javax.swing.BorderFactory;
60 import javax.swing.Box;
61 import javax.swing.Icon;
62 import javax.swing.ImageIcon;
63 import javax.swing.JButton;
64 import javax.swing.JComponent;
65 import javax.swing.JDialog;
66 import javax.swing.JEditorPane;
67 import javax.swing.JFrame;
68 import javax.swing.JLabel;
69 import javax.swing.JOptionPane;
70 import javax.swing.JPanel;
71 import javax.swing.JPopupMenu;
72 import javax.swing.JScrollPane;
73 import javax.swing.JSplitPane;
74 import javax.swing.JToolBar;
75 import javax.swing.JWindow;
76 import javax.swing.KeyStroke;
77 import javax.swing.SwingConstants;
78 import javax.swing.SwingUtilities;
79 import javax.swing.ToolTipManager;
80 import javax.swing.UIManager;
81 import javax.swing.WindowConstants;
82 import javax.swing.event.ChangeEvent;
83 import javax.swing.event.ChangeListener;
84 import javax.swing.event.EventListenerList;
85 import javax.swing.event.HyperlinkEvent;
86 import javax.swing.event.HyperlinkListener;
87
88 import org.apache.log4j.Appender;
89 import org.apache.log4j.AppenderSkeleton;
90 import org.apache.log4j.Level;
91 import org.apache.log4j.LogManager;
92 import org.apache.log4j.Logger;
93 import org.apache.log4j.LoggerRepositoryExImpl;
94 import org.apache.log4j.chainsaw.dnd.FileDnDTarget;
95 import org.apache.log4j.chainsaw.help.HelpManager;
96 import org.apache.log4j.chainsaw.help.Tutorial;
97 import org.apache.log4j.chainsaw.helper.SwingHelper;
98 import org.apache.log4j.chainsaw.icons.ChainsawIcons;
99 import org.apache.log4j.chainsaw.icons.LineIconFactory;
100 import org.apache.log4j.chainsaw.messages.MessageCenter;
101 import org.apache.log4j.chainsaw.osx.OSXIntegration;
102 import org.apache.log4j.chainsaw.plugins.PluginClassLoaderFactory;
103 import org.apache.log4j.chainsaw.prefs.LoadSettingsEvent;
104 import org.apache.log4j.chainsaw.prefs.MRUFileListPreferenceSaver;
105 import org.apache.log4j.chainsaw.prefs.SaveSettingsEvent;
106 import org.apache.log4j.chainsaw.prefs.SettingsListener;
107 import org.apache.log4j.chainsaw.prefs.SettingsManager;
108 import org.apache.log4j.chainsaw.receivers.ReceiversPanel;
109 import org.apache.log4j.chainsaw.version.VersionManager;
110 import org.apache.log4j.helpers.Constants;
111 import org.apache.log4j.net.SocketNodeEventListener;
112 import org.apache.log4j.plugins.Plugin;
113 import org.apache.log4j.xml.DOMConfigurator;
114 import org.apache.log4j.plugins.PluginEvent;
115 import org.apache.log4j.plugins.PluginListener;
116 import org.apache.log4j.plugins.PluginRegistry;
117 import org.apache.log4j.plugins.Receiver;
118 import org.apache.log4j.rewrite.PropertyRewritePolicy;
119 import org.apache.log4j.rewrite.RewriteAppender;
120 import org.apache.log4j.rule.ExpressionRule;
121 import org.apache.log4j.rule.Rule;
122 import org.apache.log4j.spi.Decoder;
123 import org.apache.log4j.spi.LoggerRepository;
124 import org.apache.log4j.spi.LoggerRepositoryEx;
125 import org.apache.log4j.spi.LoggingEvent;
126 import org.apache.log4j.spi.RepositorySelector;
127 import org.apache.log4j.xml.XMLDecoder;
128
129
130 /***
131 * The main entry point for Chainsaw, this class represents the first frame
132 * that is used to display a Welcome panel, and any other panels that are
133 * generated because Logging Events are streamed via a Receiver, or other
134 * mechanism.
135 *
136 * NOTE: Some of Chainsaw's application initialization should be performed prior
137 * to activating receivers and the logging framework used to perform self-logging.
138 *
139 * DELAY as much as possible the logging framework initialization process,
140 * currently initialized by the creation of a ChainsawAppenderHandler.
141 *
142 * @author Scott Deboy <sdeboy@apache.org>
143 * @author Paul Smith <psmith@apache.org>
144 *
145 */
146 public class LogUI extends JFrame implements ChainsawViewer, SettingsListener {
147 private static final String MAIN_WINDOW_HEIGHT = "main.window.height";
148 private static final String MAIN_WINDOW_WIDTH = "main.window.width";
149 private static final String MAIN_WINDOW_Y = "main.window.y";
150 private static final String MAIN_WINDOW_X = "main.window.x";
151 private static ChainsawSplash splash;
152 private static final double DEFAULT_MAIN_RECEIVER_SPLIT_LOCATION = .8d;
153 private final JFrame preferencesFrame = new JFrame();
154 private boolean noReceiversDefined;
155 private ReceiversPanel receiversPanel;
156 private ChainsawTabbedPane tabbedPane;
157 private JToolBar toolbar;
158 private ChainsawStatusBar statusBar;
159 private ApplicationPreferenceModel applicationPreferenceModel;
160 private ApplicationPreferenceModelPanel applicationPreferenceModelPanel;
161 private final Map tableModelMap = new HashMap();
162 private final Map tableMap = new HashMap();
163 private final List filterableColumns = new ArrayList();
164 private final Map panelMap = new HashMap();
165 ChainsawAppenderHandler handler;
166 private ChainsawToolBarAndMenus tbms;
167 private ChainsawAbout aboutBox;
168 private final SettingsManager sm = SettingsManager.getInstance();
169 private final JFrame tutorialFrame = new JFrame("Chainsaw Tutorial");
170 private JSplitPane mainReceiverSplitPane;
171 private double lastMainReceiverSplitLocation =
172 DEFAULT_MAIN_RECEIVER_SPLIT_LOCATION;
173 private final List identifierPanels = new ArrayList();
174 private int dividerSize;
175 private int cyclicBufferSize;
176 private static Logger logger;
177
178 /***
179 * Set to true, if and only if the GUI has completed it's full
180 * initialization. Any logging events that come in must wait until this is
181 * true, and if it is false, should wait on the initializationLock object
182 * until notified.
183 */
184 private boolean isGUIFullyInitialized = false;
185 private Object initializationLock = new Object();
186
187 /***
188 * The shutdownAction is called when the user requests to exit Chainsaw, and
189 * by default this exits the VM, but a developer may replace this action with
190 * something that better suits their needs
191 */
192 private Action shutdownAction = null;
193
194 /***
195 * Clients can register a ShutdownListener to be notified when the user has
196 * requested Chainsaw to exit.
197 */
198 private EventListenerList shutdownListenerList = new EventListenerList();
199 private WelcomePanel welcomePanel;
200
201 private static final Object repositorySelectorGuard = new Object();
202 private static final LoggerRepositoryExImpl repositoryExImpl = new LoggerRepositoryExImpl(LogManager.getLoggerRepository());
203
204 private PluginRegistry pluginRegistry;
205
206 /***
207 * Constructor which builds up all the visual elements of the frame including
208 * the Menu bar
209 */
210 public LogUI() {
211 super("Chainsaw v2 - Log Viewer");
212 setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
213
214 if (ChainsawIcons.WINDOW_ICON != null) {
215 setIconImage(new ImageIcon(ChainsawIcons.WINDOW_ICON).getImage());
216 }
217 }
218
219 private static final void showSplash(Frame owner) {
220 splash = new ChainsawSplash(owner);
221 SwingHelper.centerOnScreen(splash);
222 splash.setVisible(true);
223 }
224
225 private static final void removeSplash() {
226 if (splash != null) {
227 splash.setVisible(false);
228 splash.dispose();
229 }
230 }
231
232 /***
233 * Registers a ShutdownListener with this calss so that it can be notified
234 * when the user has requested that Chainsaw exit.
235 *
236 * @param l
237 */
238 public void addShutdownListener(ShutdownListener l) {
239 shutdownListenerList.add(ShutdownListener.class, l);
240 }
241
242 /***
243 * Removes the registered ShutdownListener so that the listener will not be
244 * notified on a shutdown.
245 *
246 * @param l
247 */
248 public void removeShutdownListener(ShutdownListener l) {
249 shutdownListenerList.remove(ShutdownListener.class, l);
250 }
251
252 /***
253 * Starts Chainsaw by attaching a new instance to the Log4J main root Logger
254 * via a ChainsawAppender, and activates itself
255 *
256 * @param args
257 */
258 public static void main(String[] args) {
259
260 if(OSXIntegration.IS_OSX) {
261 System.setProperty("apple.laf.useScreenMenuBar", "true");
262 }
263
264
265 LogManager.setRepositorySelector(new RepositorySelector() {
266
267 public LoggerRepository getLoggerRepository() {
268 return repositoryExImpl;
269 }}, repositorySelectorGuard);
270
271 ApplicationPreferenceModel model = new ApplicationPreferenceModel();
272
273 SettingsManager.getInstance().configure(new ApplicationPreferenceModelSaver(model));
274
275 applyLookAndFeel(model.getLookAndFeelClassName());
276
277 createChainsawGUI(model, null);
278 }
279
280 /***
281 * Creates, activates, and then shows the Chainsaw GUI, optionally showing
282 * the splash screen, and using the passed shutdown action when the user
283 * requests to exit the application (if null, then Chainsaw will exit the vm)
284 *
285 * @param model
286 * @param newShutdownAction
287 * DOCUMENT ME!
288 */
289 public static void createChainsawGUI(
290 ApplicationPreferenceModel model, Action newShutdownAction) {
291
292 if (model.isOkToRemoveSecurityManager()) {
293 MessageCenter
294 .getInstance()
295 .addMessage(
296 "User has authorised removal of Java Security Manager via preferences");
297 System.setSecurityManager(null);
298
299
300
301 Policy.setPolicy(new Policy() {
302
303 public void refresh() {
304 }
305
306 public PermissionCollection getPermissions(CodeSource codesource) {
307 Permissions perms = new Permissions();
308 perms.add(new AllPermission());
309 return (perms);
310 }
311 });
312 }
313
314 LogUI logUI = new LogUI();
315 logUI.applicationPreferenceModel = model;
316
317 if (model.isShowSplash()) {
318 showSplash(logUI);
319 }
320 logUI.cyclicBufferSize = model.getCyclicBufferSize();
321 logUI.pluginRegistry = repositoryExImpl.getPluginRegistry();
322
323 logUI.handler = new ChainsawAppenderHandler();
324 logUI.handler.addEventBatchListener(logUI.new NewTabEventBatchReceiver());
325
326 /***
327 * TODO until we work out how JoranConfigurator might be able to have
328 * configurable class loader, if at all. For now we temporarily replace the
329 * TCCL so that Plugins that need access to resources in
330 * the Plugins directory can find them (this is particularly
331 * important for the Web start version of Chainsaw
332 */
333
334 logUI.ensureChainsawAppenderHandlerAdded();
335 logger = LogManager.getLogger(LogUI.class);
336
337
338
339 PropertyRewritePolicy policy = new PropertyRewritePolicy();
340 policy.setProperties("hostname=chainsaw,application=log");
341
342 RewriteAppender rewriteAppender = new RewriteAppender();
343 rewriteAppender.setRewritePolicy(policy);
344
345 Enumeration appenders = Logger.getLogger("org.apache.log4j").getAllAppenders();
346 if (!appenders.hasMoreElements()) {
347 appenders = Logger.getRootLogger().getAllAppenders();
348 }
349 while (appenders.hasMoreElements()) {
350 Appender nextAppender = (Appender)appenders.nextElement();
351 rewriteAppender.addAppender(nextAppender);
352 }
353 Logger.getLogger("org.apache.log4j").removeAllAppenders();
354 Logger.getLogger("org.apache.log4j").addAppender(rewriteAppender);
355 Logger.getLogger("org.apache.log4j").setAdditivity(false);
356
357 Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
358 public void uncaughtException(Thread t, Throwable e) {
359 logger.error("Uncaught exception in thread " + t, e);
360 }
361 });
362
363 String config = model.getConfigurationURL();
364 if(config!=null && (!(config.trim().equals("")))) {
365 config = config.trim();
366 try {
367 URL configURL = new URL(config);
368 logUI.loadConfigurationUsingPluginClassLoader(configURL);
369 }catch(MalformedURLException e) {
370 logger.error("Failed to convert config string to url", e);
371 }
372 }
373
374 if (config == null) {
375 logger.info("No auto-configuration file found within the ApplicationPreferenceModel");
376 } else {
377 logger.info("Using '" + config + "' for auto-configuration");
378 }
379 LogManager.getRootLogger().setLevel(Level.TRACE);
380
381 logUI.activateViewer();
382
383 logger.info("SecurityManager is now: " + System.getSecurityManager());
384
385 logUI.checkForNewerVersion();
386
387 if (newShutdownAction != null) {
388 logUI.setShutdownAction(newShutdownAction);
389 } else {
390 logUI.setShutdownAction(
391 new AbstractAction() {
392 public void actionPerformed(ActionEvent e) {
393 System.exit(0);
394 }
395 });
396 }
397 }
398
399 /***
400 * Allow Chainsaw v2 to be ran in-process (configured as a ChainsawAppender)
401 * NOTE: Closing Chainsaw will NOT stop the application generating the events.
402 * @param appender
403 *
404 */
405 public void activateViewer(ChainsawAppender appender) {
406
407 LogManager.getRootLogger().setLevel(Level.TRACE);
408 ApplicationPreferenceModel model = new ApplicationPreferenceModel();
409 SettingsManager.getInstance().configure(new ApplicationPreferenceModelSaver(model));
410
411 cyclicBufferSize = model.getCyclicBufferSize();
412 applyLookAndFeel(model.getLookAndFeelClassName());
413
414 handler = new ChainsawAppenderHandler(appender);
415 handler.addEventBatchListener(new NewTabEventBatchReceiver());
416
417 logger = LogManager.getLogger(LogUI.class);
418
419 setShutdownAction(
420 new AbstractAction() {
421 public void actionPerformed(ActionEvent e) {
422 }
423 });
424
425 activateViewer();
426
427 getApplicationPreferenceModel().apply(model);
428 }
429
430 /***
431 * Initialises the menu's and toolbars, but does not actually create any of
432 * the main panel components.
433 *
434 */
435 private void initGUI() {
436
437 setupHelpSystem();
438 statusBar = new ChainsawStatusBar();
439 setupReceiverPanel();
440
441 setToolBarAndMenus(new ChainsawToolBarAndMenus(this));
442 toolbar = getToolBarAndMenus().getToolbar();
443 setJMenuBar(getToolBarAndMenus().getMenubar());
444
445 setTabbedPane(new ChainsawTabbedPane());
446 getSettingsManager().addSettingsListener(getTabbedPane());
447 getSettingsManager().configure(getTabbedPane());
448
449 /***
450 * This adds Drag & Drop capability to Chainsaw
451 */
452 FileDnDTarget dnDTarget = new FileDnDTarget(tabbedPane);
453 dnDTarget.addPropertyChangeListener("fileList", new PropertyChangeListener() {
454
455 public void propertyChange(PropertyChangeEvent evt) {
456 final List fileList = (List) evt.getNewValue();
457
458 Thread thread = new Thread(new Runnable() {
459
460 public void run() {
461 logger.debug("Loading files: " + fileList);
462 for (Iterator iter = fileList.iterator(); iter.hasNext();) {
463 File file = (File) iter.next();
464 final Decoder decoder = new XMLDecoder();
465 try {
466 getStatusBar().setMessage("Loading " + file.getAbsolutePath() + "...");
467 FileLoadAction.importURL(handler, decoder, file
468 .getName(), file.toURI().toURL());
469 } catch (Exception e) {
470 String errorMsg = "Failed to import a file";
471 logger.error(errorMsg, e);
472 getStatusBar().setMessage(errorMsg);
473 }
474 }
475
476 }});
477
478 thread.setPriority(Thread.MIN_PRIORITY);
479 thread.start();
480
481 }});
482
483 addDragDropPanel();
484 applicationPreferenceModelPanel = new ApplicationPreferenceModelPanel(applicationPreferenceModel);
485 applicationPreferenceModelPanel.setOkCancelActionListener(
486 new ActionListener() {
487 public void actionPerformed(ActionEvent e) {
488 preferencesFrame.setVisible(false);
489 }
490 });
491
492 OSXIntegration.init(this);
493
494 }
495
496 private void addDragDropPanel(){
497 final JLabel lbl = new JLabel();
498 lbl.setEnabled(false);
499 final String dndTitle = ChainsawTabbedPane.DRAG_DROP_TAB;
500 SwingUtilities.invokeLater(new Runnable() {
501 public void run() {
502 ensureWelcomePanelVisible();
503 getTabbedPane().addANewTab(dndTitle,lbl,null, "You can Drag & Drop XML log files onto the Tabbed Pane and they will be loaded into Chainsaw" );
504 getTabbedPane().setEnabledAt(getTabbedPane().indexOfTab(dndTitle), false);
505 if (!getPanelMap().containsKey(dndTitle)) {
506 getPanelMap().put(dndTitle, lbl);
507 }
508 }
509 });
510 }
511
512
513 private void initPlugins(PluginRegistry pluginRegistry) {
514 pluginRegistry.addPluginListener(
515 new PluginListener() {
516 public void pluginStarted(PluginEvent e) {
517 if (e.getPlugin() instanceof JComponent) {
518 JComponent plugin = (JComponent) e.getPlugin();
519 getTabbedPane().addANewTab(plugin.getName(), plugin, null, null);
520 }
521 }
522
523 public void pluginStopped(PluginEvent e) {
524
525 }
526 });
527
528
529
530
531
532
533 try {
534 Class pluginClass = Class.forName("org.apache.log4j.chainsaw.zeroconf.ZeroConfPlugin");
535 Plugin plugin = (Plugin) pluginClass.newInstance();
536 pluginRegistry.addPlugin(plugin);
537 plugin.activateOptions();
538 MessageCenter.getInstance().getLogger().info("Looks like ZeroConf stuff is available... WooHoo!");
539 } catch (Throwable e) {
540 MessageCenter.getInstance().getLogger().error("Doesn't look like ZeroConf is available", e);
541 }
542 }
543
544 private void setupReceiverPanel() {
545 receiversPanel = new ReceiversPanel();
546 receiversPanel.addPropertyChangeListener(
547 "visible",
548 new PropertyChangeListener() {
549 public void propertyChange(PropertyChangeEvent evt) {
550 MessageCenter.getInstance().getLogger().debug(
551 "Receiver's panel:" + evt.getNewValue());
552 getApplicationPreferenceModel().setReceivers(
553 ((Boolean) evt.getNewValue()).booleanValue());
554 }
555 });
556 }
557
558 /***
559 * Initialises the Help system and the WelcomePanel
560 *
561 */
562 private void setupHelpSystem() {
563 welcomePanel = new WelcomePanel();
564
565 JToolBar tb = welcomePanel.getToolbar();
566
567
568 tb.add(
569 new SmallButton(
570 new AbstractAction("Tutorial", new ImageIcon(ChainsawIcons.HELP)) {
571 public void actionPerformed(ActionEvent e) {
572 setupTutorial();
573 }
574 }));
575 tb.addSeparator();
576
577 final Action exampleConfigAction =
578 new AbstractAction("View example Receiver configuration") {
579 public void actionPerformed(ActionEvent e) {
580 HelpManager.getInstance().setHelpURL(
581 ChainsawConstants.EXAMLE_CONFIG_URL);
582 }
583 };
584
585 exampleConfigAction.putValue(
586 Action.SHORT_DESCRIPTION,
587 "Displays an example Log4j configuration file with several Receivers defined.");
588
589 JButton exampleButton = new SmallButton(exampleConfigAction);
590 tb.add(exampleButton);
591
592 tb.add(Box.createHorizontalGlue());
593
594 /***
595 * Setup a listener on the HelpURL property and automatically change the WelcomePages URL
596 * to it.
597 */
598 HelpManager.getInstance().addPropertyChangeListener(
599 "helpURL",
600 new PropertyChangeListener() {
601 public void propertyChange(PropertyChangeEvent evt) {
602 URL newURL = (URL) evt.getNewValue();
603
604 if (newURL != null) {
605 welcomePanel.setURL(newURL);
606 ensureWelcomePanelVisible();
607 }
608 }
609 });
610 }
611
612 private void ensureWelcomePanelVisible() {
613
614 if(!getTabbedPane().containsWelcomePanel()) {
615 addWelcomePanel();
616 }
617 getTabbedPane().setSelectedComponent(welcomePanel);
618 }
619
620 /***
621 * Given the load event, configures the size/location of the main window etc
622 * etc.
623 *
624 * @param event
625 * DOCUMENT ME!
626 */
627 public void loadSettings(LoadSettingsEvent event) {
628 setLocation(
629 event.asInt(LogUI.MAIN_WINDOW_X), event.asInt(LogUI.MAIN_WINDOW_Y));
630 setSize(
631 event.asInt(LogUI.MAIN_WINDOW_WIDTH),
632 event.asInt(LogUI.MAIN_WINDOW_HEIGHT));
633
634 getToolBarAndMenus().stateChange();
635 }
636
637 /***
638 * Ensures the location/size of the main window is stored with the settings
639 *
640 * @param event
641 * DOCUMENT ME!
642 */
643 public void saveSettings(SaveSettingsEvent event) {
644 event.saveSetting(LogUI.MAIN_WINDOW_X, (int) getLocation().getX());
645 event.saveSetting(LogUI.MAIN_WINDOW_Y, (int) getLocation().getY());
646
647 event.saveSetting(LogUI.MAIN_WINDOW_WIDTH, getWidth());
648 event.saveSetting(LogUI.MAIN_WINDOW_HEIGHT, getHeight());
649
650 }
651
652 /***
653 * Activates itself as a viewer by configuring Size, and location of itself,
654 * and configures the default Tabbed Pane elements with the correct layout,
655 * table columns, and sets itself viewable.
656 */
657 public void activateViewer() {
658 LoggerRepository repo = LogManager.getLoggerRepository();
659 if (repo instanceof LoggerRepositoryEx) {
660 this.pluginRegistry = ((LoggerRepositoryEx) repo).getPluginRegistry();
661 }
662 initGUI();
663
664 initPrefModelListeners();
665
666 /***
667 * We add a simple appender to the MessageCenter logger
668 * so that each message is displayed in the Status bar
669 */
670 MessageCenter.getInstance().getLogger().addAppender(
671 new AppenderSkeleton() {
672 protected void append(LoggingEvent event) {
673 getStatusBar().setMessage(event.getMessage().toString());
674 }
675
676 public void close() {
677 }
678
679 public boolean requiresLayout() {
680 return false;
681 }
682 });
683
684
685
686 initSocketConnectionListener();
687
688 if (pluginRegistry.getPlugins(Receiver.class).size() == 0) {
689 noReceiversDefined = true;
690 }
691
692 getFilterableColumns().add(ChainsawConstants.LEVEL_COL_NAME);
693 getFilterableColumns().add(ChainsawConstants.LOGGER_COL_NAME);
694 getFilterableColumns().add(ChainsawConstants.THREAD_COL_NAME);
695 getFilterableColumns().add(ChainsawConstants.NDC_COL_NAME);
696 getFilterableColumns().add(ChainsawConstants.PROPERTIES_COL_NAME);
697 getFilterableColumns().add(ChainsawConstants.CLASS_COL_NAME);
698 getFilterableColumns().add(ChainsawConstants.METHOD_COL_NAME);
699 getFilterableColumns().add(ChainsawConstants.FILE_COL_NAME);
700 getFilterableColumns().add(ChainsawConstants.NONE_COL_NAME);
701
702 JPanel panePanel = new JPanel();
703 panePanel.setLayout(new BorderLayout(2, 2));
704
705 getContentPane().setLayout(new BorderLayout());
706
707 getTabbedPane().addChangeListener(getToolBarAndMenus());
708
709 KeyStroke ksRight =
710 KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, Event.CTRL_MASK);
711 KeyStroke ksLeft =
712 KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, Event.CTRL_MASK);
713 KeyStroke ksGotoLine =
714 KeyStroke.getKeyStroke(KeyEvent.VK_G, Event.CTRL_MASK);
715
716 getTabbedPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
717 ksRight, "MoveRight");
718 getTabbedPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
719 ksLeft, "MoveLeft");
720 getTabbedPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
721 ksGotoLine, "GotoLine");
722
723 Action moveRight =
724 new AbstractAction() {
725 public void actionPerformed(ActionEvent e) {
726 int temp = getTabbedPane().getSelectedIndex();
727 ++temp;
728
729 if (temp != getTabbedPane().getTabCount()) {
730 getTabbedPane().setSelectedTab(temp);
731 }
732 }
733 };
734
735 Action moveLeft =
736 new AbstractAction() {
737 public void actionPerformed(ActionEvent e) {
738 int temp = getTabbedPane().getSelectedIndex();
739 --temp;
740
741 if (temp > -1) {
742 getTabbedPane().setSelectedTab(temp);
743 }
744 }
745 };
746
747 Action gotoLine =
748 new AbstractAction() {
749 public void actionPerformed(ActionEvent e) {
750 String inputLine = JOptionPane.showInputDialog(LogUI.this, "Enter the line number to go:", "Goto Line", -1);
751 try {
752 int lineNumber = Integer.parseInt(inputLine);
753 List eventList = getCurrentLogPanel().getFilteredEvents();
754
755 if (lineNumber > 0 && lineNumber <= eventList.size()) {
756 getCurrentLogPanel().setSelectedEvent(lineNumber);
757 } else {
758 JOptionPane.showMessageDialog(LogUI.this, "You have entered an invalid line number", "Error", 0);
759 }
760 } catch (NumberFormatException nfe) {
761 JOptionPane.showMessageDialog(LogUI.this, "You have entered an invalid line number", "Error", 0);
762 }
763 }
764 };
765
766
767 getTabbedPane().getActionMap().put("MoveRight", moveRight);
768 getTabbedPane().getActionMap().put("MoveLeft", moveLeft);
769 getTabbedPane().getActionMap().put("GotoLine", gotoLine);
770
771 /***
772 * We listen for double clicks, and auto-undock currently selected Tab if
773 * the mouse event location matches the currently selected tab
774 */
775 getTabbedPane().addMouseListener(
776 new MouseAdapter() {
777 public void mouseClicked(MouseEvent e) {
778 super.mouseClicked(e);
779
780 if (
781 (e.getClickCount() > 1)
782 && ((e.getModifiers() & InputEvent.BUTTON1_MASK) > 0)) {
783 int tabIndex = getTabbedPane().getSelectedIndex();
784
785 if (
786 (tabIndex != -1)
787 && (tabIndex == getTabbedPane().getSelectedIndex())) {
788 LogPanel logPanel = getCurrentLogPanel();
789
790 if (logPanel != null) {
791 logPanel.undock();
792 }
793 }
794 }
795 }
796 });
797
798 panePanel.add(getTabbedPane());
799 addWelcomePanel();
800
801 getContentPane().add(toolbar, BorderLayout.NORTH);
802 getContentPane().add(statusBar, BorderLayout.SOUTH);
803
804 mainReceiverSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, panePanel, receiversPanel);
805 dividerSize = mainReceiverSplitPane.getDividerSize();
806 mainReceiverSplitPane.setDividerLocation(-1);
807
808 getContentPane().add(mainReceiverSplitPane, BorderLayout.CENTER);
809
810 /***
811 * We need to make sure that all the internal GUI components have been added to the
812 * JFrame so that any plugns that get activated during initPlugins(...) method
813 * have access to inject menus
814 */
815 initPlugins(pluginRegistry);
816
817 mainReceiverSplitPane.setResizeWeight(1.0);
818 addWindowListener(
819 new WindowAdapter() {
820 public void windowClosing(WindowEvent event) {
821 exit();
822 }
823 });
824 preferencesFrame.setTitle("'Application-wide Preferences");
825 preferencesFrame.setIconImage(
826 ((ImageIcon) ChainsawIcons.ICON_PREFERENCES).getImage());
827 preferencesFrame.getContentPane().add(applicationPreferenceModelPanel);
828
829 preferencesFrame.setSize(640, 520);
830
831 Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
832 preferencesFrame.setLocation(
833 new Point(
834 (screenDimension.width / 2) - (preferencesFrame.getSize().width / 2),
835 (screenDimension.height / 2) - (preferencesFrame.getSize().height / 2)));
836
837 pack();
838
839 final JPopupMenu tabPopup = new JPopupMenu();
840 final Action hideCurrentTabAction =
841 new AbstractAction("Hide") {
842 public void actionPerformed(ActionEvent e) {
843 Component selectedComp = getTabbedPane().getSelectedComponent();
844 if (selectedComp instanceof LogPanel) {
845 displayPanel(getCurrentLogPanel().getIdentifier(), false);
846 tbms.stateChange();
847 } else {
848 getTabbedPane().remove(selectedComp);
849 }
850 }
851 };
852
853 final Action hideOtherTabsAction =
854 new AbstractAction("Hide Others") {
855 public void actionPerformed(ActionEvent e) {
856 Component selectedComp = getTabbedPane().getSelectedComponent();
857 String currentName;
858 if (selectedComp instanceof LogPanel) {
859 currentName = getCurrentLogPanel().getIdentifier();
860 } else if (selectedComp instanceof WelcomePanel) {
861 currentName = ChainsawTabbedPane.WELCOME_TAB;
862 } else {
863 currentName = ChainsawTabbedPane.DRAG_DROP_TAB;
864 }
865
866 int count = getTabbedPane().getTabCount();
867 int index = 0;
868
869 for (int i = 0; i < count; i++) {
870 String name = getTabbedPane().getTitleAt(index);
871
872 if (
873 getPanelMap().keySet().contains(name)
874 && !name.equals(currentName)) {
875 displayPanel(name, false);
876 tbms.stateChange();
877 } else {
878 index++;
879 }
880 }
881 }
882 };
883
884 Action showHiddenTabsAction =
885 new AbstractAction("Show All Hidden") {
886 public void actionPerformed(ActionEvent e) {
887 for (Iterator iter = getPanels().entrySet().iterator();
888 iter.hasNext();) {
889 Map.Entry entry = (Map.Entry)iter.next();
890 Boolean docked = (Boolean)entry.getValue();
891 if (docked.booleanValue()) {
892 String identifier = (String) entry.getKey();
893 int count = getTabbedPane().getTabCount();
894 boolean found = false;
895
896 for (int i = 0; i < count; i++) {
897 String name = getTabbedPane().getTitleAt(i);
898
899 if (name.equals(identifier)) {
900 found = true;
901
902 break;
903 }
904 }
905
906 if (!found) {
907 displayPanel(identifier, true);
908 tbms.stateChange();
909 }
910 }
911 }
912 }
913 };
914
915 tabPopup.add(hideCurrentTabAction);
916 tabPopup.add(hideOtherTabsAction);
917 tabPopup.addSeparator();
918 tabPopup.add(showHiddenTabsAction);
919
920 final PopupListener tabPopupListener = new PopupListener(tabPopup);
921 getTabbedPane().addMouseListener(tabPopupListener);
922
923 this.handler.addPropertyChangeListener(
924 "dataRate",
925 new PropertyChangeListener() {
926 public void propertyChange(PropertyChangeEvent evt) {
927 double dataRate = ((Double) evt.getNewValue()).doubleValue();
928 statusBar.setDataRate(dataRate);
929 }
930 });
931
932 getSettingsManager().addSettingsListener(this);
933 getSettingsManager().addSettingsListener(new ApplicationPreferenceModelSaver(applicationPreferenceModel));
934 getSettingsManager().addSettingsListener(MRUFileListPreferenceSaver.getInstance());
935 getSettingsManager().addSettingsListener(receiversPanel);
936 getSettingsManager().loadSettings();
937
938 setVisible(true);
939
940 if (applicationPreferenceModel.isReceivers()) {
941 showReceiverPanel();
942 } else {
943 hideReceiverPanel();
944 }
945
946 removeSplash();
947
948 synchronized (initializationLock) {
949 isGUIFullyInitialized = true;
950 initializationLock.notifyAll();
951 }
952
953
954
955 if (
956 noReceiversDefined
957 && applicationPreferenceModel.isShowNoReceiverWarning()) {
958 showNoReceiversWarningPanel();
959 }
960
961 Container container = tutorialFrame.getContentPane();
962 final JEditorPane tutorialArea = new JEditorPane();
963 tutorialArea.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
964 tutorialArea.setEditable(false);
965 container.setLayout(new BorderLayout());
966
967 try {
968 tutorialArea.setPage(ChainsawConstants.TUTORIAL_URL);
969 container.add(new JScrollPane(tutorialArea), BorderLayout.CENTER);
970 } catch (Exception e) {
971 MessageCenter.getInstance().getLogger().error(
972 "Error occurred loading the Tutorial", e);
973 }
974
975 tutorialFrame.setIconImage(new ImageIcon(ChainsawIcons.HELP).getImage());
976 tutorialFrame.setSize(new Dimension(640, 480));
977
978 final Action startTutorial =
979 new AbstractAction(
980 "Start Tutorial", new ImageIcon(ChainsawIcons.ICON_RESUME_RECEIVER)) {
981 public void actionPerformed(ActionEvent e) {
982 if (
983 JOptionPane.showConfirmDialog(
984 null,
985 "This will start 3 \"Generator\" receivers for use in the Tutorial. Is that ok?",
986 "Confirm", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
987 new Thread(new Tutorial()).start();
988 putValue("TutorialStarted", Boolean.TRUE);
989 } else {
990 putValue("TutorialStarted", Boolean.FALSE);
991 }
992 }
993 };
994
995 final Action stopTutorial =
996 new AbstractAction(
997 "Stop Tutorial", new ImageIcon(ChainsawIcons.ICON_STOP_RECEIVER)) {
998 public void actionPerformed(ActionEvent e) {
999 if (
1000 JOptionPane.showConfirmDialog(
1001 null,
1002 "This will stop all of the \"Generator\" receivers used in the Tutorial, but leave any other Receiver untouched. Is that ok?",
1003 "Confirm", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
1004 new Thread(
1005 new Runnable() {
1006 public void run() {
1007 LoggerRepository repo = LogManager.getLoggerRepository();
1008 if (repo instanceof LoggerRepositoryEx) {
1009 PluginRegistry pluginRegistry = ((LoggerRepositoryEx) repo).getPluginRegistry();
1010 List list = pluginRegistry.getPlugins(Generator.class);
1011
1012 for (Iterator iter = list.iterator(); iter.hasNext();) {
1013 Plugin plugin = (Plugin) iter.next();
1014 pluginRegistry.stopPlugin(plugin.getName());
1015 }
1016 }
1017 }
1018 }).start();
1019 setEnabled(false);
1020 startTutorial.putValue("TutorialStarted", Boolean.FALSE);
1021 }
1022 }
1023 };
1024
1025 stopTutorial.putValue(
1026 Action.SHORT_DESCRIPTION,
1027 "Removes all of the Tutorials Generator Receivers, leaving all other Receivers untouched");
1028 startTutorial.putValue(
1029 Action.SHORT_DESCRIPTION,
1030 "Begins the Tutorial, starting up some Generator Receivers so you can see Chainsaw in action");
1031 stopTutorial.setEnabled(false);
1032
1033 final SmallToggleButton startButton = new SmallToggleButton(startTutorial);
1034 PropertyChangeListener pcl =
1035 new PropertyChangeListener() {
1036 public void propertyChange(PropertyChangeEvent evt) {
1037 stopTutorial.setEnabled(
1038 ((Boolean) startTutorial.getValue("TutorialStarted")).equals(
1039 Boolean.TRUE));
1040 startButton.setSelected(stopTutorial.isEnabled());
1041 }
1042 };
1043
1044 startTutorial.addPropertyChangeListener(pcl);
1045 stopTutorial.addPropertyChangeListener(pcl);
1046
1047 pluginRegistry.addPluginListener(
1048 new PluginListener() {
1049 public void pluginStarted(PluginEvent e) {
1050 }
1051
1052 public void pluginStopped(PluginEvent e) {
1053 List list = pluginRegistry.getPlugins(Generator.class);
1054
1055 if (list.size() == 0) {
1056 startTutorial.putValue("TutorialStarted", Boolean.FALSE);
1057 }
1058 }
1059 });
1060
1061 final SmallButton stopButton = new SmallButton(stopTutorial);
1062
1063 final JToolBar tutorialToolbar = new JToolBar();
1064 tutorialToolbar.setFloatable(false);
1065 tutorialToolbar.add(startButton);
1066 tutorialToolbar.add(stopButton);
1067 container.add(tutorialToolbar, BorderLayout.NORTH);
1068 tutorialArea.addHyperlinkListener(
1069 new HyperlinkListener() {
1070 public void hyperlinkUpdate(HyperlinkEvent e) {
1071 if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
1072 if (e.getDescription().equals("StartTutorial")) {
1073 startTutorial.actionPerformed(null);
1074 } else if (e.getDescription().equals("StopTutorial")) {
1075 stopTutorial.actionPerformed(null);
1076 } else {
1077 try {
1078 tutorialArea.setPage(e.getURL());
1079 } catch (IOException e1) {
1080 MessageCenter.getInstance().getLogger().error(
1081 "Failed to change the URL for the Tutorial", e1);
1082 }
1083 }
1084 }
1085 }
1086 });
1087
1088 /***
1089 * loads the saved tab settings and if there are hidden tabs,
1090 * hide those tabs out of currently loaded tabs..
1091 */
1092
1093 if (!getTabbedPane().tabSetting.isWelcome()){
1094 displayPanel(ChainsawTabbedPane.WELCOME_TAB, false);
1095 }
1096 if (!getTabbedPane().tabSetting.isDragdrop()){
1097 displayPanel(ChainsawTabbedPane.DRAG_DROP_TAB, false);
1098 }
1099 tbms.stateChange();
1100
1101 }
1102
1103 /***
1104 * Checks the last run version number against this compiled version number and prompts the user
1105 * to view the release notes if the 2 strings are different.
1106 */
1107 private void checkForNewerVersion()
1108 {
1109 /***
1110 * Now check if the version they last used (if any) is
1111 * different than the version that is currently running
1112 */
1113
1114 String lastUsedVersion = getApplicationPreferenceModel().getLastUsedVersion();
1115 String currentVersionNumber = VersionManager.getInstance().getVersionNumber();
1116 if(lastUsedVersion==null || !lastUsedVersion.equals(currentVersionNumber)) {
1117 if(JOptionPane.showConfirmDialog(this, "This version looks like it is different than the version you last ran. (" + lastUsedVersion + " vs " + currentVersionNumber+")\n\nWould you like to view the Release Notes?", "Newer Version?", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
1118 SwingUtilities.invokeLater(new Runnable() {
1119
1120 public void run()
1121 {
1122 HelpManager.getInstance().setHelpURL(ChainsawConstants.RELEASE_NOTES_URL);
1123
1124 }});
1125 }
1126 }
1127
1128 getApplicationPreferenceModel().setLastUsedVersion(currentVersionNumber);
1129 }
1130
1131 /***
1132 * Display the log tree pane, using the last known divider location
1133 */
1134 private void showReceiverPanel() {
1135 mainReceiverSplitPane.setDividerSize(dividerSize);
1136 mainReceiverSplitPane.setDividerLocation(lastMainReceiverSplitLocation);
1137 receiversPanel.setVisible(true);
1138 mainReceiverSplitPane.repaint();
1139 }
1140
1141 /***
1142 * Hide the log tree pane, holding the current divider location for later use
1143 */
1144 private void hideReceiverPanel() {
1145
1146 int currentSize = mainReceiverSplitPane.getWidth() - mainReceiverSplitPane.getDividerSize();
1147 if (mainReceiverSplitPane.getDividerLocation() > -1) {
1148 if (!(((mainReceiverSplitPane.getDividerLocation() + 1) == currentSize)
1149 || ((mainReceiverSplitPane.getDividerLocation() - 1) == 0))) {
1150 lastMainReceiverSplitLocation = ((double) mainReceiverSplitPane
1151 .getDividerLocation() / currentSize);
1152 }
1153 }
1154 mainReceiverSplitPane.setDividerSize(0);
1155 receiversPanel.setVisible(false);
1156 mainReceiverSplitPane.repaint();
1157 }
1158
1159 private void initSocketConnectionListener() {
1160 final SocketNodeEventListener socketListener =
1161 new SocketNodeEventListener() {
1162 public void socketOpened(String remoteInfo) {
1163 statusBar.remoteConnectionReceived(remoteInfo);
1164 }
1165
1166 public void socketClosedEvent(Exception e) {
1167 MessageCenter.getInstance().getLogger().info(
1168 "Connection lost! :: " + e.getMessage());
1169 }
1170 };
1171
1172 PluginListener pluginListener =
1173 new PluginListener() {
1174 public void pluginStarted(PluginEvent e) {
1175 MessageCenter.getInstance().getLogger().info(
1176 e.getPlugin().getName() + " started!");
1177
1178 Method method = getAddListenerMethod(e.getPlugin());
1179
1180 if (method != null) {
1181 try {
1182 method.invoke(e.getPlugin(), new Object[] { socketListener });
1183 } catch (Exception ex) {
1184 MessageCenter.getInstance().getLogger().error(
1185 "Failed to add a SocketNodeEventListener", ex);
1186 }
1187 }
1188 }
1189
1190 Method getRemoveListenerMethod(Plugin p) {
1191 try {
1192 return p.getClass().getMethod(
1193 "removeSocketNodeEventListener",
1194 new Class[] { SocketNodeEventListener.class });
1195 } catch (Exception e) {
1196 return null;
1197 }
1198 }
1199
1200 Method getAddListenerMethod(Plugin p) {
1201 try {
1202 return p.getClass().getMethod(
1203 "addSocketNodeEventListener",
1204 new Class[] { SocketNodeEventListener.class });
1205 } catch (Exception e) {
1206 return null;
1207 }
1208 }
1209
1210 public void pluginStopped(PluginEvent e) {
1211 Method method = getRemoveListenerMethod(e.getPlugin());
1212
1213 if (method != null) {
1214 try {
1215 method.invoke(e.getPlugin(), new Object[] { socketListener });
1216 } catch (Exception ex) {
1217 MessageCenter.getInstance().getLogger().error(
1218 "Failed to remove SocketNodeEventListener", ex);
1219 }
1220 }
1221
1222 MessageCenter.getInstance().getLogger().info(
1223 e.getPlugin().getName() + " stopped!");
1224 }
1225 };
1226
1227 pluginRegistry.addPluginListener(pluginListener);
1228 }
1229
1230 private void initPrefModelListeners() {
1231 applicationPreferenceModel.addPropertyChangeListener(
1232 "identifierExpression",
1233 new PropertyChangeListener() {
1234 public void propertyChange(PropertyChangeEvent evt) {
1235 handler.setIdentifierExpression(evt.getNewValue().toString());
1236 }
1237 });
1238 handler.setIdentifierExpression(applicationPreferenceModel.getIdentifierExpression());
1239
1240
1241 applicationPreferenceModel.addPropertyChangeListener(
1242 "toolTipDisplayMillis",
1243 new PropertyChangeListener() {
1244 public void propertyChange(PropertyChangeEvent evt) {
1245 ToolTipManager.sharedInstance().setDismissDelay(
1246 ((Integer) evt.getNewValue()).intValue());
1247 }
1248 });
1249 ToolTipManager.sharedInstance().setDismissDelay(
1250 applicationPreferenceModel.getToolTipDisplayMillis());
1251
1252 applicationPreferenceModel.addPropertyChangeListener(
1253 "responsiveness",
1254 new PropertyChangeListener() {
1255 public void propertyChange(PropertyChangeEvent evt) {
1256 int value = ((Integer) evt.getNewValue()).intValue();
1257 handler.setQueueInterval((value * 1000) - 750);
1258 }
1259 });
1260 handler.setQueueInterval((applicationPreferenceModel.getResponsiveness() * 1000) - 750);
1261
1262 applicationPreferenceModel.addPropertyChangeListener(
1263 "tabPlacement",
1264 new PropertyChangeListener() {
1265 public void propertyChange(final PropertyChangeEvent evt) {
1266 SwingUtilities.invokeLater(
1267 new Runnable() {
1268 public void run() {
1269 int placement = ((Integer) evt.getNewValue()).intValue();
1270
1271 switch (placement) {
1272 case SwingConstants.TOP:
1273 case SwingConstants.BOTTOM:
1274 tabbedPane.setTabPlacement(placement);
1275
1276 break;
1277
1278 default:
1279 break;
1280 }
1281 }
1282 });
1283 }
1284 });
1285
1286 applicationPreferenceModel.addPropertyChangeListener(
1287 "statusBar",
1288 new PropertyChangeListener() {
1289 public void propertyChange(PropertyChangeEvent evt) {
1290 boolean value = ((Boolean) evt.getNewValue()).booleanValue();
1291 setStatusBarVisible(value);
1292 }
1293 });
1294 setStatusBarVisible(applicationPreferenceModel.isStatusBar());
1295
1296 applicationPreferenceModel.addPropertyChangeListener(
1297 "receivers",
1298 new PropertyChangeListener() {
1299 public void propertyChange(PropertyChangeEvent evt) {
1300 boolean value = ((Boolean) evt.getNewValue()).booleanValue();
1301
1302 if (value) {
1303 showReceiverPanel();
1304 } else {
1305 hideReceiverPanel();
1306 }
1307 }
1308 });
1309
1310
1311
1312
1313
1314
1315
1316 applicationPreferenceModel.addPropertyChangeListener(
1317 "toolbar",
1318 new PropertyChangeListener() {
1319 public void propertyChange(PropertyChangeEvent evt) {
1320 boolean value = ((Boolean) evt.getNewValue()).booleanValue();
1321 toolbar.setVisible(value);
1322 }
1323 });
1324 toolbar.setVisible(applicationPreferenceModel.isToolbar());
1325
1326 }
1327
1328 /***
1329 * Displays a warning dialog about having no Receivers defined and allows the
1330 * user to choose some options for configuration
1331 */
1332 private void showNoReceiversWarningPanel() {
1333 final NoReceiversWarningPanel noReceiversWarningPanel =
1334 new NoReceiversWarningPanel();
1335
1336 final SettingsListener sl =
1337 new SettingsListener() {
1338 public void loadSettings(LoadSettingsEvent event) {
1339 int size = event.asInt("SavedConfigs.Size");
1340 Object[] configs = new Object[size];
1341
1342 for (int i = 0; i < size; i++) {
1343 configs[i] = event.getSetting("SavedConfigs." + i);
1344 }
1345
1346 noReceiversWarningPanel.getModel().setRememberedConfigs(configs);
1347 }
1348
1349 public void saveSettings(SaveSettingsEvent event) {
1350 Object[] configs =
1351 noReceiversWarningPanel.getModel().getRememberedConfigs();
1352 event.saveSetting("SavedConfigs.Size", configs.length);
1353
1354 for (int i = 0; i < configs.length; i++) {
1355 event.saveSetting("SavedConfigs." + i, configs[i].toString());
1356 }
1357 }
1358 };
1359
1360 /***
1361 * This listener sets up the NoReciversWarningPanel and loads saves the
1362 * configs/logfiles
1363 */
1364 getSettingsManager().addSettingsListener(sl);
1365 getSettingsManager().configure(sl);
1366
1367 SwingUtilities.invokeLater(
1368 new Runnable() {
1369 public void run() {
1370 final JDialog dialog = new JDialog(LogUI.this, true);
1371 dialog.setTitle("Warning: You have no Receivers defined...");
1372 dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
1373
1374 dialog.setResizable(false);
1375
1376 noReceiversWarningPanel.setOkActionListener(
1377 new ActionListener() {
1378 public void actionPerformed(ActionEvent e) {
1379 dialog.setVisible(false);
1380 }
1381 });
1382
1383 dialog.getContentPane().add(noReceiversWarningPanel);
1384
1385 dialog.pack();
1386
1387 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
1388 dialog.setLocation(
1389 (screenSize.width / 2) - (dialog.getWidth() / 2),
1390 (screenSize.height / 2) - (dialog.getHeight() / 2));
1391 dialog.setVisible(true);
1392
1393 dialog.dispose();
1394
1395 applicationPreferenceModel.setShowNoReceiverWarning(
1396 !noReceiversWarningPanel.isDontWarnMeAgain());
1397
1398 if (noReceiversWarningPanel.getModel().isManualMode()) {
1399 applicationPreferenceModel.setReceivers(true);
1400 } else if (noReceiversWarningPanel.getModel().isSimpleReceiverMode()) {
1401 int port = noReceiversWarningPanel.getModel().getSimplePort();
1402 Class receiverClass =
1403 noReceiversWarningPanel.getModel().getSimpleReceiverClass();
1404
1405 try {
1406 Receiver simpleReceiver = (Receiver) receiverClass.newInstance();
1407 simpleReceiver.setName("Simple Receiver");
1408
1409 Method portMethod =
1410 simpleReceiver.getClass().getMethod(
1411 "setPort", new Class[] { int.class });
1412 portMethod.invoke(
1413 simpleReceiver, new Object[] { new Integer(port) });
1414
1415 simpleReceiver.setThreshold(Level.TRACE);
1416
1417 pluginRegistry.addPlugin(simpleReceiver);
1418 simpleReceiver.activateOptions();
1419 receiversPanel.updateReceiverTreeInDispatchThread();
1420 } catch (Exception e) {
1421 MessageCenter.getInstance().getLogger().error(
1422 "Error creating Receiver", e);
1423 MessageCenter.getInstance().getLogger().info(
1424 "An error occurred creating your Receiver");
1425 }
1426 } else if (noReceiversWarningPanel.getModel().isLoadConfig() ||
1427 noReceiversWarningPanel.getModel().isLoadSavedConfigs()) {
1428 final URL url;
1429 if (noReceiversWarningPanel.getModel().isLoadSavedConfigs()) {
1430 url = noReceiversWarningPanel.getModel().getSavedConfigToLoad();
1431 } else {
1432 url = noReceiversWarningPanel.getModel().getConfigToLoad();
1433 }
1434
1435 if (url != null) {
1436 MessageCenter.getInstance().getLogger().debug(
1437 "Initialiazing Log4j with " + url.toExternalForm());
1438
1439 new Thread(
1440 new Runnable() {
1441 public void run() {
1442 loadConfigurationUsingPluginClassLoader(url);
1443
1444 receiversPanel.updateReceiverTreeInDispatchThread();
1445 }
1446 }).start();
1447 }
1448 }
1449 }
1450 });
1451 }
1452
1453 /***
1454 * Exits the application, ensuring Settings are saved.
1455 *
1456 */
1457 public boolean exit() {
1458 getSettingsManager().saveSettings();
1459
1460 return shutdown();
1461 }
1462
1463 void addWelcomePanel() {
1464 getTabbedPane().insertTab(
1465 ChainsawTabbedPane.WELCOME_TAB, new ImageIcon(ChainsawIcons.ABOUT),welcomePanel,
1466 "Welcome/Help", 0);
1467 getTabbedPane().setSelectedComponent(welcomePanel);
1468 getPanelMap().put(ChainsawTabbedPane.WELCOME_TAB, welcomePanel);
1469 }
1470
1471 void removeWelcomePanel() {
1472 if (getTabbedPane().containsWelcomePanel()) {
1473 getTabbedPane().remove(
1474 getTabbedPane().getComponentAt(getTabbedPane().indexOfTab(ChainsawTabbedPane.WELCOME_TAB)));
1475 }
1476 }
1477
1478 ChainsawStatusBar getStatusBar() {
1479 return statusBar;
1480 }
1481
1482 public void showApplicationPreferences() {
1483 applicationPreferenceModelPanel.updateModel();
1484 preferencesFrame.setVisible(true);
1485 }
1486
1487 public void showAboutBox() {
1488 if (aboutBox == null) {
1489 aboutBox = new ChainsawAbout(this);
1490 }
1491
1492 aboutBox.setVisible(true);
1493 }
1494
1495 Map getPanels() {
1496 Map m = new HashMap();
1497 Set panelSet = getPanelMap().entrySet();
1498 Iterator iter = panelSet.iterator();
1499
1500 while (iter.hasNext()) {
1501 Map.Entry entry = (Map.Entry) iter.next();
1502 Object o = entry.getValue();
1503 boolean valueToSend;
1504 if (o instanceof LogPanel){
1505 valueToSend = ((DockablePanel) entry.getValue()).isDocked();
1506 } else {
1507 valueToSend = true;
1508 }
1509 m.put(entry.getKey(), new Boolean(valueToSend));
1510 }
1511
1512 return m;
1513 }
1514
1515 void displayPanel(String panelName, boolean display) {
1516 Object o = getPanelMap().get(panelName);
1517 Component p = null;
1518
1519 if (o instanceof LogPanel) {
1520 p = (LogPanel) o;
1521 } else if (o instanceof WelcomePanel) {
1522 p = (WelcomePanel) o;
1523 } else if (o instanceof JLabel) {
1524 p = (JLabel) o;
1525 }
1526
1527 int index = getTabbedPane().indexOfTab(panelName);
1528
1529 if ((index == -1) && display) {
1530 if (panelName.equals(ChainsawTabbedPane.DRAG_DROP_TAB)){
1531 addDragDropPanel();
1532 } else {
1533 getTabbedPane().addTab(panelName, p);
1534 }
1535 }
1536
1537 if ((index > -1) && !display) {
1538 getTabbedPane().removeTabAt(index);
1539 }
1540 }
1541
1542
1543 /***
1544 * Shutsdown by ensuring the Appender gets a chance to close.
1545 */
1546 public boolean shutdown() {
1547 if (getApplicationPreferenceModel().isConfirmExit()) {
1548 if (
1549 JOptionPane.showConfirmDialog(
1550 LogUI.this, "Are you sure you want to exit Chainsaw?",
1551 "Confirm Exit", JOptionPane.YES_NO_OPTION,
1552 JOptionPane.INFORMATION_MESSAGE) != JOptionPane.YES_OPTION) {
1553 return false;
1554 }
1555
1556 }
1557
1558 final JWindow progressWindow = new JWindow();
1559 final ProgressPanel panel = new ProgressPanel(1, 3, "Shutting down");
1560 progressWindow.getContentPane().add(panel);
1561 progressWindow.pack();
1562
1563 Point p = new Point(getLocation());
1564 p.move((int) getSize().getWidth() >> 1, (int) getSize().getHeight() >> 1);
1565 progressWindow.setLocation(p);
1566 progressWindow.setVisible(true);
1567
1568 Runnable runnable =
1569 new Runnable() {
1570 public void run() {
1571 try {
1572 int progress = 1;
1573 final int delay = 25;
1574
1575 handler.close();
1576 panel.setProgress(progress++);
1577
1578 Thread.sleep(delay);
1579
1580 pluginRegistry.stopAllPlugins();
1581 panel.setProgress(progress++);
1582
1583 Thread.sleep(delay);
1584
1585 panel.setProgress(progress++);
1586 Thread.sleep(delay);
1587 } catch (Exception e) {
1588 e.printStackTrace();
1589 }
1590
1591 fireShutdownEvent();
1592 performShutdownAction();
1593 progressWindow.setVisible(false);
1594 }
1595 };
1596
1597 if (OSXIntegration.IS_OSX) {
1598 /***
1599 * or OSX we do it in the current thread because otherwise returning
1600 * will exit the process before it's had a chance to save things
1601 *
1602 */
1603 runnable.run();
1604 }else {
1605 new Thread(runnable).start();
1606 }
1607 return true;
1608 }
1609
1610 /***
1611 * Ensures all the registered ShutdownListeners are notified.
1612 */
1613 private void fireShutdownEvent() {
1614 ShutdownListener[] listeners =
1615 (ShutdownListener[]) shutdownListenerList.getListeners(
1616 ShutdownListener.class);
1617
1618 for (int i = 0; i < listeners.length; i++) {
1619 listeners[i].shuttingDown();
1620 }
1621 }
1622
1623 /***
1624 * Configures LogUI's with an action to execute when the user requests to
1625 * exit the application, the default action is to exit the VM. This Action is
1626 * called AFTER all the ShutdownListeners have been notified
1627 *
1628 * @param shutdownAction
1629 */
1630 public final void setShutdownAction(Action shutdownAction) {
1631 this.shutdownAction = shutdownAction;
1632 }
1633
1634 /***
1635 * Using the current thread, calls the registed Shutdown action's
1636 * actionPerformed(...) method.
1637 *
1638 */
1639 private void performShutdownAction() {
1640 MessageCenter.getInstance().getLogger().debug(
1641 "Calling the shutdown Action. Goodbye!");
1642
1643 shutdownAction.actionPerformed(
1644 new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "Shutting Down"));
1645 }
1646
1647 /***
1648 * Returns the currently selected LogPanel, if there is one, otherwise null
1649 *
1650 * @return current log panel
1651 */
1652 LogPanel getCurrentLogPanel() {
1653 Component selectedTab = getTabbedPane().getSelectedComponent();
1654
1655 if (selectedTab instanceof LogPanel) {
1656 return (LogPanel) selectedTab;
1657 } else {
1658
1659 }
1660
1661 return null;
1662 }
1663
1664 /***
1665 * @param visible
1666 */
1667 private void setStatusBarVisible(final boolean visible) {
1668 MessageCenter.getInstance().getLogger().debug(
1669 "Setting StatusBar to " + visible);
1670 SwingUtilities.invokeLater(
1671 new Runnable() {
1672 public void run() {
1673 statusBar.setVisible(visible);
1674 }
1675 });
1676 }
1677
1678 boolean isStatusBarVisible() {
1679 return statusBar.isVisible();
1680 }
1681
1682 /***
1683 * DOCUMENT ME!
1684 *
1685 * @return DOCUMENT ME!
1686 */
1687 public String getActiveTabName() {
1688 int index = getTabbedPane().getSelectedIndex();
1689
1690 if (index == -1) {
1691 return null;
1692 } else {
1693 return getTabbedPane().getTitleAt(index);
1694 }
1695 }
1696
1697 /***
1698 * Changes the currently used Look And Feel of the App
1699 *
1700 * @param lookAndFeelClassName
1701 * The FQN of the LookANdFeel
1702 */
1703 private static void applyLookAndFeel(String lookAndFeelClassName) {
1704 if (
1705 UIManager.getLookAndFeel().getClass().getName().equals(
1706 lookAndFeelClassName)) {
1707 return;
1708 }
1709
1710 if (
1711 (lookAndFeelClassName == null) || lookAndFeelClassName.trim().equals("")) {
1712 lookAndFeelClassName = UIManager.getSystemLookAndFeelClassName();
1713 }
1714
1715 try {
1716 UIManager.setLookAndFeel(lookAndFeelClassName);
1717
1718 } catch (Exception e) {
1719 }
1720 }
1721
1722 /***
1723 * Causes the Welcome Panel to become visible, and shows the URL specified as
1724 * it's contents
1725 *
1726 * @param url
1727 * for content to show
1728 */
1729 public void showHelp(URL url) {
1730 ensureWelcomePanelVisible();
1731
1732 getWelcomePanel().setURL(url);
1733 }
1734
1735 /***
1736 * DOCUMENT ME!
1737 *
1738 * @return welcome panel
1739 */
1740 private WelcomePanel getWelcomePanel() {
1741 return welcomePanel;
1742 }
1743
1744 /***
1745 * DOCUMENT ME!
1746 *
1747 * @return log tree panel visible flag
1748 */
1749 public boolean isLogTreePanelVisible() {
1750 if (getCurrentLogPanel() == null) {
1751 return false;
1752 }
1753
1754 return getCurrentLogPanel().isLogTreeVisible();
1755 }
1756
1757 /***
1758 * DOCUMENT ME!
1759 *
1760 * @return DOCUMENT ME!
1761 */
1762 public Map getPanelMap() {
1763 return panelMap;
1764 }
1765
1766
1767
1768
1769
1770 /***
1771 * DOCUMENT ME!
1772 *
1773 * @return DOCUMENT ME!
1774 */
1775 public SettingsManager getSettingsManager() {
1776 return sm;
1777 }
1778
1779 /***
1780 * DOCUMENT ME!
1781 *
1782 * @return DOCUMENT ME!
1783 */
1784 public List getFilterableColumns() {
1785 return filterableColumns;
1786 }
1787
1788 /***
1789 * DOCUMENT ME!
1790 *
1791 * @param tbms
1792 * DOCUMENT ME!
1793 */
1794 public void setToolBarAndMenus(ChainsawToolBarAndMenus tbms) {
1795 this.tbms = tbms;
1796 }
1797
1798 /***
1799 * DOCUMENT ME!
1800 *
1801 * @return DOCUMENT ME!
1802 */
1803 public ChainsawToolBarAndMenus getToolBarAndMenus() {
1804 return tbms;
1805 }
1806
1807 /***
1808 * DOCUMENT ME!
1809 *
1810 * @return DOCUMENT ME!
1811 */
1812 public Map getTableMap() {
1813 return tableMap;
1814 }
1815
1816 /***
1817 * DOCUMENT ME!
1818 *
1819 * @return DOCUMENT ME!
1820 */
1821 public Map getTableModelMap() {
1822 return tableModelMap;
1823 }
1824
1825 /***
1826 * DOCUMENT ME!
1827 *
1828 * @param tabbedPane
1829 * DOCUMENT ME!
1830 */
1831 public void setTabbedPane(ChainsawTabbedPane tabbedPane) {
1832 this.tabbedPane = tabbedPane;
1833 }
1834
1835 /***
1836 * DOCUMENT ME!
1837 *
1838 * @return DOCUMENT ME!
1839 */
1840 public ChainsawTabbedPane getTabbedPane() {
1841 return tabbedPane;
1842 }
1843
1844 /***
1845 * @return Returns the applicationPreferenceModel.
1846 */
1847 public final ApplicationPreferenceModel getApplicationPreferenceModel() {
1848 return applicationPreferenceModel;
1849 }
1850
1851 /***
1852 * DOCUMENT ME!
1853 */
1854 public void setupTutorial() {
1855 SwingUtilities.invokeLater(
1856 new Runnable() {
1857 public void run() {
1858 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1859 setLocation(0, getLocation().y);
1860
1861 double chainsawwidth = 0.7;
1862 double tutorialwidth = 1 - chainsawwidth;
1863 setSize((int) (screen.width * chainsawwidth), getSize().height);
1864 invalidate();
1865 validate();
1866
1867 Dimension size = getSize();
1868 Point loc = getLocation();
1869 tutorialFrame.setSize(
1870 (int) (screen.width * tutorialwidth), size.height);
1871 tutorialFrame.setLocation(loc.x + size.width, loc.y);
1872 tutorialFrame.setVisible(true);
1873 }
1874 });
1875 }
1876
1877 private void buildLogPanel(
1878 boolean customExpression, final String ident, final List events)
1879 throws IllegalArgumentException {
1880 final LogPanel thisPanel = new LogPanel(getStatusBar(), ident, cyclicBufferSize);
1881
1882 /***
1883 * Now add the panel as a batch listener so it can handle it's own
1884 * batchs
1885 */
1886 if (customExpression) {
1887 handler.addCustomEventBatchListener(ident, thisPanel);
1888 } else {
1889 identifierPanels.add(thisPanel);
1890 handler.addEventBatchListener(thisPanel);
1891 }
1892
1893 TabIconHandler iconHandler = new TabIconHandler(ident);
1894 thisPanel.addEventCountListener(iconHandler);
1895
1896
1897
1898 tabbedPane.addChangeListener(iconHandler);
1899
1900 PropertyChangeListener toolbarMenuUpdateListener =
1901 new PropertyChangeListener() {
1902 public void propertyChange(PropertyChangeEvent evt) {
1903 tbms.stateChange();
1904 }
1905 };
1906
1907 thisPanel.addPropertyChangeListener(toolbarMenuUpdateListener);
1908 thisPanel.addPreferencePropertyChangeListener(toolbarMenuUpdateListener);
1909
1910 thisPanel.addPropertyChangeListener(
1911 "docked",
1912 new PropertyChangeListener() {
1913 public void propertyChange(PropertyChangeEvent evt) {
1914 LogPanel logPanel = (LogPanel) evt.getSource();
1915
1916 if (logPanel.isDocked()) {
1917 getPanelMap().put(logPanel.getIdentifier(), logPanel);
1918 getTabbedPane().addANewTab(
1919 logPanel.getIdentifier(), logPanel, null);
1920 getTabbedPane().setSelectedTab(getTabbedPane().indexOfTab(logPanel.getIdentifier()));
1921 } else {
1922 getTabbedPane().remove(logPanel);
1923 }
1924 }
1925 });
1926
1927 logger.debug("adding logpanel to tabbed pane: " + ident);
1928
1929
1930
1931
1932 getTabbedPane().add(ident, thisPanel);
1933 getPanelMap().put(ident, thisPanel);
1934
1935 getSettingsManager().addSettingsListener(thisPanel);
1936 getSettingsManager().configure(thisPanel);
1937
1938 /***
1939 * Let the new LogPanel receive this batch
1940 */
1941
1942 SwingUtilities.invokeLater(
1943 new Runnable() {
1944 public void run() {
1945 getTabbedPane().addANewTab(
1946 ident, thisPanel, new ImageIcon(ChainsawIcons.ANIM_RADIO_TOWER));
1947 thisPanel.receiveEventBatch(ident, events);
1948 if(!getTabbedPane().tabSetting.isChainsawLog()){
1949 displayPanel("chainsaw-log", false);
1950 }
1951 }
1952 });
1953
1954 String msg = "added tab " + ident;
1955 MessageCenter.getInstance().getLogger().debug(msg);
1956 }
1957
1958
1959 public void createCustomExpressionLogPanel(String ident) {
1960
1961 try {
1962 List list = new ArrayList();
1963 Rule rule = ExpressionRule.getRule(ident);
1964 Iterator iter = identifierPanels.iterator();
1965
1966 while (iter.hasNext()) {
1967 LogPanel panel = (LogPanel) iter.next();
1968 Iterator iter2 = panel.getMatchingEvents(rule).iterator();
1969
1970 while (iter2.hasNext()) {
1971 LoggingEvent e = (LoggingEvent) iter2.next();
1972 list.add(e);
1973 }
1974 }
1975
1976 buildLogPanel(true, ident, list);
1977 } catch (IllegalArgumentException iae) {
1978 MessageCenter.getInstance().getLogger().info(
1979 "Unable to add tab using expression: " + ident + ", reason: "
1980 + iae.getMessage());
1981 }
1982 }
1983
1984 /***
1985 * Loads the log4j configuration file specified by the url, using
1986 * the PluginClassLoader instance as a TCCL, but only replacing it temporarily, with the original
1987 * TCCL being restored in a finally block to ensure consitency.
1988 *
1989 * @param url
1990 */
1991 private void loadConfigurationUsingPluginClassLoader(final URL url) {
1992 ClassLoader classLoader = PluginClassLoaderFactory.getInstance().getClassLoader();
1993 ClassLoader previousTCCL = Thread.currentThread().getContextClassLoader();
1994
1995 if(url!=null) {
1996 try {
1997
1998 Thread.currentThread().setContextClassLoader(classLoader);
1999 DOMConfigurator.configure(url);
2000 }finally{
2001
2002 Thread.currentThread().setContextClassLoader(previousTCCL);
2003 }
2004 }
2005 ensureChainsawAppenderHandlerAdded();
2006 }
2007
2008 /***
2009 * Makes sure that the LoggerRepository has the ChainsawAppenderHandler
2010 * added to the root logger so Chainsaw can receive all the events.
2011 */
2012 private void ensureChainsawAppenderHandlerAdded() {
2013 if(!LogManager.getLoggerRepository().getRootLogger().isAttached(handler)) {
2014 LogManager.getLoggerRepository().getRootLogger().addAppender(handler);
2015 }
2016 }
2017
2018 /***
2019 * This class handles the recption of the Event batches and creates new
2020 * LogPanels if the identifier is not in use otherwise it ignores the event
2021 * batch.
2022 *
2023 * @author Paul Smith
2024 * <psmith@apache.org>
2025 *
2026 */
2027 private class NewTabEventBatchReceiver implements EventBatchListener {
2028 /***
2029 * DOCUMENT ME!
2030 *
2031 * @param ident
2032 * @param events
2033 */
2034 public void receiveEventBatch(
2035 final String ident, final List events) {
2036 if (events.size() == 0) {
2037 return;
2038 }
2039
2040 if (!isGUIFullyInitialized) {
2041 synchronized (initializationLock) {
2042 while (!isGUIFullyInitialized) {
2043 System.out.println(
2044 "Wanting to add a row, but GUI not initialized, waiting...");
2045
2046 /***
2047 * Lets wait 1 seconds and recheck.
2048 */
2049 try {
2050 initializationLock.wait(1000);
2051 logger.debug("waiting for initialization to complete");
2052 } catch (InterruptedException e) {
2053 }
2054 }
2055 logger.debug("out of system initialization wait loop");
2056 }
2057 }
2058
2059 if (!getPanelMap().containsKey(ident)) {
2060 logger.debug("panel " + ident + " does not exist - creating");
2061 try {
2062 buildLogPanel(false, ident, events);
2063 } catch (IllegalArgumentException iae) {
2064 logger.error("error creating log panel", iae);
2065
2066 }
2067 }
2068 }
2069
2070
2071
2072
2073
2074
2075
2076 /***
2077 * DOCUMENT ME!
2078 *
2079 * @return DOCUMENT ME!
2080 */
2081 public String getInterestedIdentifier() {
2082
2083 return null;
2084 }
2085 }
2086
2087 private class TabIconHandler implements EventCountListener, ChangeListener {
2088
2089
2090 private boolean newEvents = true;
2091 private boolean seenEvents = false;
2092 private final String ident;
2093 ImageIcon NEW_EVENTS = new ImageIcon(ChainsawIcons.ANIM_RADIO_TOWER);
2094 ImageIcon HAS_EVENTS = new ImageIcon(ChainsawIcons.INFO);
2095 Icon SELECTED = LineIconFactory.createBlankIcon();
2096
2097 public TabIconHandler(String identifier) {
2098 ident = identifier;
2099
2100 new Thread(
2101 new Runnable() {
2102 public void run() {
2103 while (true) {
2104
2105
2106 if (getTabbedPane().indexOfTab(ident) > -1 &&
2107 getTabbedPane().getSelectedIndex() == getTabbedPane()
2108 .indexOfTab(ident)) {
2109 getTabbedPane().setIconAt(
2110 getTabbedPane().indexOfTab(ident), SELECTED);
2111 newEvents = false;
2112 seenEvents = true;
2113 } else if (getTabbedPane().indexOfTab(ident) > -1) {
2114 if (newEvents) {
2115 getTabbedPane().setIconAt(
2116 getTabbedPane().indexOfTab(ident), NEW_EVENTS);
2117 newEvents = false;
2118 seenEvents = false;
2119 } else if (!seenEvents) {
2120 getTabbedPane().setIconAt(
2121 getTabbedPane().indexOfTab(ident), HAS_EVENTS);
2122 }
2123 }
2124
2125 try {
2126 Thread.sleep(handler.getQueueInterval() + 1000);
2127 } catch (InterruptedException ie) {
2128 }
2129 }
2130 }
2131 }).start();
2132 }
2133
2134 /***
2135 * DOCUMENT ME!
2136 *
2137 * @param currentCount
2138 * DOCUMENT ME!
2139 * @param totalCount
2140 * DOCUMENT ME!
2141 */
2142 public void eventCountChanged(int currentCount, int totalCount) {
2143 newEvents = true;
2144 }
2145
2146 public void stateChanged(ChangeEvent event) {
2147 if (
2148 getTabbedPane().indexOfTab(ident) > -1 && getTabbedPane().indexOfTab(ident) == getTabbedPane().getSelectedIndex()) {
2149 getTabbedPane().setIconAt(getTabbedPane().indexOfTab(ident), SELECTED);
2150 }
2151 }
2152 }
2153 }