KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > de > qfs > apps > qflog > logview > LogTableView


1 // {{{ copyright
2

3 /********************************************************************
4  *
5  * $Id: LogTableView.java,v 1.21 2000/07/05 14:07:44 gs Exp $
6  *
7  * The contents of this file are subject to the Mozilla Public
8  * License Version 1.1 (the "License"); you may not use this file
9  * except in compliance with the License. You may obtain a copy of
10  * the License at http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS
13  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14  * implied. See the License for the specific language governing
15  * rights and limitations under the License.
16  *
17  * The Original Code is qfs.de code.
18  *
19  * The Initial Developer of the Original Code is Gregor Schmid.
20  * Portions created by Gregor Schmid are
21  * Copyright (C) 1999 Quality First Software, Gregor Schmid.
22  * All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  *******************************************************************/

27
28 // }}}
29

30 package de.qfs.apps.qflog.logview;
31
32 // {{{ imports
33

34 import java.awt.BorderLayout JavaDoc;
35 import java.awt.Color JavaDoc;
36 import java.awt.Component JavaDoc;
37 import java.awt.Frame JavaDoc;
38 import java.awt.Dimension JavaDoc;
39 import java.awt.Point JavaDoc;
40 import java.awt.Rectangle JavaDoc;
41 import java.awt.Toolkit JavaDoc;
42 import java.awt.Window JavaDoc;
43 import java.awt.event.ActionEvent JavaDoc;
44 import java.awt.event.ActionListener JavaDoc;
45 import java.awt.event.FocusAdapter JavaDoc;
46 import java.awt.event.FocusEvent JavaDoc;
47 import java.awt.event.KeyAdapter JavaDoc;
48 import java.awt.event.KeyEvent JavaDoc;
49 import java.awt.event.MouseAdapter JavaDoc;
50 import java.awt.event.MouseEvent JavaDoc;
51
52 import java.util.Enumeration JavaDoc;
53 import java.util.Hashtable JavaDoc;
54 import java.util.Observable JavaDoc;
55 import java.util.Observer JavaDoc;
56 import java.util.ResourceBundle JavaDoc;
57
58 import javax.swing.Action JavaDoc;
59 import javax.swing.AbstractAction JavaDoc;
60 import javax.swing.BorderFactory JavaDoc;
61 import javax.swing.ButtonModel JavaDoc;
62 import javax.swing.Icon JavaDoc;
63 import javax.swing.KeyStroke JavaDoc;
64 import javax.swing.JCheckBoxMenuItem JavaDoc;
65 import javax.swing.JLabel JavaDoc;
66 import javax.swing.JMenu JavaDoc;
67 import javax.swing.JMenuItem JavaDoc;
68 import javax.swing.JPanel JavaDoc;
69 import javax.swing.JPopupMenu JavaDoc;
70 import javax.swing.JScrollPane JavaDoc;
71 import javax.swing.JSplitPane JavaDoc;
72 import javax.swing.JTable JavaDoc;
73 import javax.swing.JViewport JavaDoc;
74 import javax.swing.SwingUtilities JavaDoc;
75 import javax.swing.border.Border JavaDoc;
76 import javax.swing.event.ListSelectionEvent JavaDoc;
77 import javax.swing.event.TableModelEvent JavaDoc;
78 import javax.swing.event.TableModelListener JavaDoc;
79 import javax.swing.table.DefaultTableCellRenderer JavaDoc;
80 import javax.swing.table.JTableHeader JavaDoc;
81 import javax.swing.table.TableColumnModel JavaDoc;
82 import javax.swing.table.TableModel JavaDoc;
83 import java.awt.datatransfer.StringSelection JavaDoc;
84
85 import de.qfs.lib.command.CommandDistributor;
86 import de.qfs.lib.config.Configurator;
87 import de.qfs.lib.config.ConfigWrapper;
88 import de.qfs.lib.config.JSplitPaneConfigWrapper;
89 import de.qfs.lib.gui.DelayedListSelectionListener;
90 import de.qfs.lib.gui.FilteredAndSortedTableModel;
91 import de.qfs.lib.gui.Message;
92 import de.qfs.lib.gui.MultiPane;
93 import de.qfs.lib.gui.MultiPaneEvent;
94 import de.qfs.lib.gui.MultiPaneListener;
95 import de.qfs.lib.gui.SortedTableHelper;
96 import de.qfs.lib.gui.SortedTableHeaderCellRenderer;
97 import de.qfs.lib.gui.StatusLine;
98 import de.qfs.lib.gui.SwingUtil;
99 import de.qfs.lib.gui.TableModelFilter;
100 import de.qfs.lib.log.Log;
101 import de.qfs.lib.log.LogEntry;
102 import de.qfs.lib.log.LogFilter;
103 import de.qfs.lib.log.Logger;
104 import de.qfs.lib.option.BooleanOption;
105 import de.qfs.lib.option.IntegerOption;
106 import de.qfs.lib.option.Option;
107 import de.qfs.lib.option.OptionDialog;
108 import de.qfs.lib.option.OptionSet;
109 import de.qfs.lib.util.DelayedAction;
110 import de.qfs.lib.util.MapResourceBundle;
111
112 // }}}
113

114 /**
115  * This is a view that displays log messages in a table. The table can be
116  * filtered and sorted in various ways. Also included is a detail view for the
117  * currently selected message. A LogTableView is usually used as part of the
118  * {@link LogView LogView} component, that integrates the LogTableView with
119  * other logging related views. <p>
120  *
121  * The LogTableView must be initialized by a call to {@link #init init} after
122  * creation. It uses its own resources for localization, which can be modified
123  * via {@link #setResources setResources}. <p>
124  *
125  * The {@link Configurable Configurable} members of the LogTableView are
126  * collected in a {@link Configurator Configurator}. They include the table
127  * layout, the sort order and the state and location of the detail view. The
128  * Configurator can be retrieved via {@link #getConfigurator getConfigurator}
129  * and must be registered by the caller, unless the LogTableView is part of a
130  * LogView.
131  *
132  * @author Gregor Schmid
133  * @version $Revision: 1.21 $
134  */

135 public class LogTableView
136     extends JPanel JavaDoc
137     implements Observer JavaDoc
138 {
139     // {{{ variables
140

141     /**
142      * The Logger used for logging.
143      */

144     private final static Logger logger = new Logger (LogTableView.class);
145
146     /**
147      * The names of the icon resources.
148      */

149     private final static String JavaDoc iconNames[] = {
150         "logTableView.icon.err", "logTableView.icon.errDetail",
151         "logTableView.icon.wrn", "logTableView.icon.wrnDetail",
152         "logTableView.icon.msg", "logTableView.icon.msgDetail",
153         "logTableView.icon.mtd", "logTableView.icon.mtdDetail",
154         "logTableView.icon.dbg", "logTableView.icon.dbgDetail"};
155
156     /**
157      * The icons to render.
158      */

159     private final static Icon JavaDoc[] icons = new Icon JavaDoc[iconNames.length];
160
161     /**
162      * The model of log entries.
163      */

164     private transient LogTableModel model;
165
166     /**
167      * Whether the view has been initialized.
168      */

169     private transient boolean initialized;
170
171     /**
172      * Title label for the view.
173      */

174     private JLabel JavaDoc titleLabel;
175
176     /**
177      * The base name for the title.
178      */

179     private String JavaDoc titleBase;
180
181     /**
182      * The table component.
183      */

184     private transient JTable JavaDoc table;
185
186     /**
187      * The scroll pane for the table.
188      */

189     private transient JScrollPane JavaDoc tablePane;
190
191     /**
192      * The sorter for the table.
193      */

194     private transient LogSorter sorter;
195
196     /**
197      * A filter for the table.
198      */

199     private transient LogTableFilter filter;
200
201     /**
202      * Flags for the filters.
203      */

204     private transient boolean filterEnabled[] =
205         new boolean [LogTableModel.NUM_COLUMNS];
206
207     /**
208      * The helper for sorting the table.
209      */

210     private transient SortedTableHelper tableHelper;
211
212     /**
213      * Listener for table selection and changes.
214      */

215     private transient CellSelectionListener selListener;
216
217     /**
218      * Focus listener for initial row selection.
219      */

220     private transient InitialFocusListener focusListener;
221
222     /**
223      * Key listener for fast scrolling.
224      */

225     private transient TableKeyListener tableKeyListener;
226
227     /**
228      * Popup menu mouse listener.
229      */

230     private transient PopupListener popupListener;
231
232     /**
233      * The markers for the table rows.
234      */

235     private Markers markers;
236
237     /**
238      * The view for the entry details.
239      */

240     private transient LogDetailView detailView;
241
242     /**
243      * Multi pane used to toggle the detail view.
244      */

245     private transient MultiPane multiPane;
246
247     /**
248      * Status line to use for search state display. If null, use the
249      * table view's title.
250      */

251     private transient StatusLine statusLine;
252
253     /**
254      * The search object for the table.
255      */

256     private transient Search search;
257
258     /**
259      * The Configurator used by the LogTableView.
260      */

261     private transient Configurator configurator =
262         new Configurator ("LogTable");
263
264     /**
265      * The CommandDistributor to use for command dispatch (optional).
266      */

267     private transient CommandDistributor commandDistributor;
268
269     /**
270      * The resources to use.
271      */

272     private transient MapResourceBundle resources;
273
274     /**
275      * The options of the LogTableView.
276      */

277     private OptionSet options = new OptionSet ("Options");
278
279     /**
280      * Popup menu for context menu.
281      */

282     private transient JPopupMenu JavaDoc popup;
283
284     /**
285      * Popup sub menu set markers.
286      */

287     private transient JMenu JavaDoc setMenu;
288
289     /**
290      * Popup sub menu goto markers.
291      */

292     private transient JMenu JavaDoc goMenu;
293
294     /**
295      * Dialog to edit the options.
296      */

297     private OptionDialog optionDialog;
298
299     /**
300      * Whether logging is enabled (true) or disabled (false);
301      */

302     private transient boolean logging;
303
304     //----------------------------------------------------------------------
305
// Actions
306
//----------------------------------------------------------------------
307

308     /**
309      * Copy selected rows to the clipboard.
310      */

311     private Action JavaDoc copySelectedAction;
312
313     /**
314      * Copy visible rows to the clipboard.
315      */

316     private Action JavaDoc copyVisibleAction;
317
318     /**
319      * Copy all rows to the clipboard.
320      */

321     private Action JavaDoc copyAllAction;
322
323     /**
324      * Clear all rows.
325      */

326     private Action JavaDoc clearAction;
327
328     /**
329      * Clear all invisible rows.
330      */

331     private Action JavaDoc reduceToVisibleAction;
332
333     /**
334      * Clear all visible rows.
335      */

336     private Action JavaDoc removeVisibleAction;
337
338     /**
339      * Toggle filter on column.
340      */

341     private Action JavaDoc toggleColumnFilterAction;
342
343     /**
344      * Clear column filters.
345      */

346     private Action JavaDoc clearColumnFiltersAction;
347
348     /**
349      * Set the sort column.
350      */

351     private Action JavaDoc setSortColumnAction;
352
353     /**
354      * Set a marker.
355      */

356     private Action JavaDoc[] setMarkerActions;
357
358     /**
359      * Goto a marker.
360      */

361     private Action JavaDoc[] gotoMarkerActions;
362
363     /**
364      * Edit the options.
365      */

366     private Action JavaDoc editOptionsAction;
367
368     // }}}
369

370     // {{{ constructor
371

372     /**
373      * Create a new LogTableView.
374      *
375      * @param model The mode to use for the tree.
376      */

377     public LogTableView(LogTableModel model)
378     {
379         this.model = model;
380         resources = new MapResourceBundle ();
381         resources.fetchProperties
382             ("/de/qfs/apps/qflog/logview/resources/properties/logtableview",
383              LogTableView.class);
384     }
385
386     // }}}
387

388     //----------------------------------------------------------------------
389
// Initialization
390
//----------------------------------------------------------------------
391
// {{{ init
392

393     /**
394      * Initialize the components of the view. This method is not called by the
395      * constructor to enable further configuration like setting the resources,
396      * before the view is initialized.
397      */

398     public void init()
399     {
400         if (initialized) {
401             return;
402         }
403         initialized = true;
404
405         Option option =
406             new IntegerOption ("MaxRows", 3000, true, false);
407         option.addObserver(this);
408         options.add(option);
409
410         option = new BooleanOption ("ShowLines", true);
411         option.addObserver(this);
412         options.add(option);
413
414         initIcons();
415
416         setLayout(new BorderLayout JavaDoc ());
417         setBorder(null);
418
419         JPanel JavaDoc top = new JPanel JavaDoc ();
420         top.setLayout(new BorderLayout JavaDoc ());
421         top.setBorder(null);
422
423         titleBase = resources.getString("logTableView.title", "Messages");
424         titleLabel = new JLabel JavaDoc (titleBase);
425         titleLabel.setBorder(BorderFactory.createEtchedBorder());
426         titleLabel.setHorizontalTextPosition(titleLabel.LEADING);
427         top.add(titleLabel, BorderLayout.NORTH);
428
429         String JavaDoc[] names = {
430             resources.getString("logTableView.table.level.title", "Level"),
431             resources.getString("logTableView.table.time.title", "Time"),
432             resources.getString("logTableView.table.thread.title", "Thread"),
433             resources.getString("logTableView.table.class.title", "Class"),
434             resources.getString("logTableView.table.method.title", "Method"),
435             resources.getString("logTableView.table.message.title", "Message")
436             };
437         model.setColumnNames(names);
438
439         table = new JTable JavaDoc (model) {
440             public boolean isManagingFocus()
441             {
442                 return false;
443             }
444         };
445         JTableHeader JavaDoc header = new JTableHeader JavaDoc (table.getColumnModel()) {
446             public boolean isFocusTraversable()
447             {
448                 return false;
449             }
450         };
451         header.setRequestFocusEnabled(false);
452         table.setTableHeader(header);
453         table.setBorder(null);
454         setBackground(table.getBackground());
455         table.setColumnSelectionAllowed(false);
456         table.setRowMargin(1);
457         table.setRowHeight(16);
458         sorter = new LogSorter ("Sorter");
459         configurator.add(sorter);
460
461         // The renderer for the level icons.
462
table.setDefaultRenderer(Integer JavaDoc.class, new LevelRenderer (true));
463         table.setDefaultRenderer(Object JavaDoc.class, new LevelRenderer (false));
464
465         if (filter == null) {
466             filter = new LogTableFilter ();
467             filter.setLoggingEnabled(logging);
468         }
469         tableHelper = new SortedTableHelper (table, filter, sorter);
470         tableHelper.prepareTable();
471
472         // table Columns
473
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
474         TableColumnModel JavaDoc tcm = table.getColumnModel();
475         tcm.getColumn(model.COL_LEVEL).setIdentifier("Level");
476         tcm.getColumn(model.COL_LEVEL).setPreferredWidth(24);
477         tcm.getColumn(model.COL_TIME).setIdentifier("Time");
478         tcm.getColumn(model.COL_TIME).setPreferredWidth(94);
479         tcm.getColumn(model.COL_THREAD).setIdentifier("Thread");
480         tcm.getColumn(model.COL_THREAD).setPreferredWidth(84);
481         tcm.getColumn(model.COL_CLASS).setIdentifier("Class");
482         tcm.getColumn(model.COL_CLASS).setPreferredWidth(100);
483         tcm.getColumn(model.COL_METHOD).setIdentifier("Method");
484         tcm.getColumn(model.COL_METHOD).setPreferredWidth(100);
485         tcm.getColumn(model.COL_DETAIL).setIdentifier("Message");
486         tcm.getColumn(model.COL_DETAIL).setPreferredWidth(380);
487
488         configurator.add(ConfigWrapper.makeWrapper(table, "Table"));
489
490         // Selection is difficult. We don't know anything about the
491
// filter settings and sort order after the configuration is
492
// restored, so we cannot select the first row here.
493
table.getSelectionModel().setAnchorSelectionIndex(-1);
494         focusListener = new InitialFocusListener ();
495         table.addFocusListener(focusListener);
496         tableHelper.saveSelection(true);
497         tableHelper.setHeaderCellRenderer (new HeaderRenderer());
498         tablePane = new JScrollPane JavaDoc (table);
499         SwingUtil.constrainScroll(tablePane);
500         tablePane.setBorder(BorderFactory.createLoweredBevelBorder());
501         tablePane.getViewport().putClientProperty("EnableWindowBlit", "true");
502
503         top.add(tablePane, BorderLayout.CENTER);
504
505         // The detail view
506
detailView = new LogDetailView ();
507         detailView.setLoggingEnabled(logging);
508         detailView.setResources(resources);
509         detailView.init();
510
511         // Updater for the detail view
512
selListener = new CellSelectionListener ();
513         table.getSelectionModel().addListSelectionListener(selListener);
514         table.getColumnModel().getSelectionModel()
515             .addListSelectionListener(selListener);
516         table.getModel().addTableModelListener(selListener);
517         model.addTableModelListener(selListener);
518
519         // The markers
520
markers = new Markers ();
521         model.addTableModelListener(markers);
522
523         // The split pane
524
multiPane = new MultiPane
525             ("MultiPane", MultiPane.VERTICAL,
526              top, detailView, 0.8, MultiPane.SHOW_BOTH);
527         configurator.add(multiPane);
528         add(multiPane, BorderLayout.CENTER);
529
530         // key listeners
531
tableKeyListener = new TableKeyListener ();
532         table.addKeyListener(tableKeyListener);
533         search = new Search ();
534         table.addKeyListener(search);
535         table.registerKeyboardAction
536             (search, "backsearch",
537              KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0),
538              table.WHEN_FOCUSED);
539         table.registerKeyboardAction
540             (search, "copysearch",
541              KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0),
542              table.WHEN_FOCUSED);
543         table.registerKeyboardAction
544             (search, "searchagain",
545              KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0),
546              table.WHEN_FOCUSED);
547         table.registerKeyboardAction
548             (search, "clearsearch",
549              KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
550              table.WHEN_FOCUSED);
551         table.registerKeyboardAction
552             (search, "revertsearch",
553              KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.CTRL_MASK),
554              table.WHEN_FOCUSED);
555
556         // setup and bind actions
557
setupActions();
558         table.registerKeyboardAction
559             (toggleColumnFilterAction, "toggleFilter",
560              KeyStroke.getKeyStroke(KeyEvent.VK_F, KeyEvent.CTRL_MASK),
561              table.WHEN_FOCUSED);
562
563         table.registerKeyboardAction
564             (clearColumnFiltersAction, "clearFilters",
565              KeyStroke.getKeyStroke(KeyEvent.VK_K, KeyEvent.CTRL_MASK),
566              table.WHEN_FOCUSED);
567
568         table.registerKeyboardAction
569             (setSortColumnAction, "setSortColumn",
570              KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_MASK),
571              table.WHEN_FOCUSED);
572
573         // popup menu
574
popup = new JPopupMenu JavaDoc ();
575         fillMenu(popup);
576         popupListener = new PopupListener ();
577         table.addMouseListener(popupListener);
578
579         configurator.add(options);
580     }
581
582     // }}}
583
// {{{ cleanup
584

585     /**
586      * Clear everything that might prevent garbage collection.
587      */

588     public void cleanup()
589     {
590         if (logging && logger.level >= Log.MTD) {
591             logger.log(Log.MTD, "cleanup()", "");
592         }
593
594         SwingUtil.cleanup(this);
595
596         statusLine = null;
597
598         table.removeFocusListener(focusListener);
599         focusListener = null;
600
601         table.getSelectionModel().removeListSelectionListener(selListener);
602         table.getColumnModel().getSelectionModel()
603             .removeListSelectionListener(selListener);
604         table.getModel().removeTableModelListener(selListener);
605         model.removeTableModelListener(selListener);
606         selListener = null;
607
608         model.removeTableModelListener(markers);
609         markers = null;
610
611         table.removeKeyListener(tableKeyListener);
612         tableKeyListener = null;
613         table.removeKeyListener(search);
614         search = null;
615
616         SwingUtil.cleanup(popup);
617         popup = null;
618         setMenu = null;
619         goMenu = null;
620         table.removeMouseListener(popupListener);
621         popupListener = null;
622
623         detailView.cleanup();
624         detailView = null;
625
626         model.cleanup();
627         model = null;
628
629         tableHelper = null;
630         table.resetKeyboardActions();
631         table = null;
632         Option[] opts = options.getOptions();
633         for (int i = 0; i < opts.length; i++) {
634             opts[i].deleteObservers();
635         }
636         options = null;
637         resources = null;
638     }
639
640     // }}}
641
// {{{ finalize
642

643     /**
644      * Finalize the LogTableView.
645      */

646     public void finalize()
647     {
648         if (logging && logger.level >= Log.MTD) {
649             logger.log(Log.MTD, "finalize()", "");
650         }
651     }
652
653     // }}}
654

655     //----------------------------------------------------------------------
656
// Various getters and setters
657
//----------------------------------------------------------------------
658
// {{{ getModel
659

660     /**
661      * Get the view's associated LogTableModel.
662      *
663      * @return The model of the view.
664      */

665     public final LogTableModel getModel()
666     {
667         return model;
668     }
669
670     // }}}
671
// {{{ getTable
672

673     /**
674      * Get the JTable component of the view.
675      *
676      * @return The table.
677      */

678     public final JTable JavaDoc getTable()
679     {
680         return table;
681     }
682
683     // }}}
684
// {{{ getMultiPane
685

686     /**
687      * Get the MultiPane used for the display of the table and the detail view.
688      *
689      * @return The MultiPane used by the LogTableView.
690      */

691     public final MultiPane getMultiPane()
692     {
693         return multiPane;
694     }
695
696     // }}}
697
// {{{ fillMenu
698

699     /**
700      * Fill a JMenu with menu items for the LogTableView.
701      *
702      * @param menu The JMenu to fill.
703      */

704     public void fillMenu(JMenu JavaDoc menu)
705     {
706         JMenuItem JavaDoc mi = menu.add(copySelectedAction);
707         setMnemonic(mi, "copySelected");
708         mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C,
709                                                  KeyEvent.CTRL_MASK));
710         mi = menu.add(copyVisibleAction);
711         setMnemonic(mi, "copyVisible");
712         mi = menu.add(copyAllAction);
713         setMnemonic(mi, "copyAll");
714
715         mi = menu.add(clearAction);
716         setMnemonic(mi, "clear");
717         mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L,
718                                                  KeyEvent.CTRL_MASK));
719         mi = menu.add(reduceToVisibleAction);
720         setMnemonic(mi, "reduceToVisible");
721         mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I,
722                                                  KeyEvent.CTRL_MASK));
723         mi = menu.add(removeVisibleAction);
724         setMnemonic(mi, "removeVisible");
725         menu.addSeparator();
726
727         mi = menu.add(setSortColumnAction);
728         setMnemonic(mi, "setSortColumn");
729         mi = menu.add(toggleColumnFilterAction);
730         setMnemonic(mi, "toggleColumnFilter");
731         mi = menu.add(clearColumnFiltersAction);
732         setMnemonic(mi, "clearColumnFilters");
733
734         menu.addSeparator();
735
736         JMenu JavaDoc set = new JMenu JavaDoc (resources.getString
737                                ("logTableView.action.setMarker.name",
738                                 "Set Mark"));
739         String JavaDoc mnemo = resources.getString
740             ("logTableView.action.setMarker.name", null);
741         if (mnemo != null) {
742             set.setMnemonic(mnemo.charAt(0));
743         }
744         for (int i = 0; i <= 9; i++) {
745             mi = set.add(setMarkerActions[i]);
746             setMnemonic(mi, "setMarker" + i);
747         }
748         menu.add(set);
749
750         JMenu JavaDoc go = new JMenu JavaDoc (resources.getString
751                                ("logTableView.action.gotoMarker.name",
752                                 "Goto Mark"));
753         mnemo = resources.getString
754             ("logTableView.action.gotoMarker.name", null);
755         if (mnemo != null) {
756             go.setMnemonic(mnemo.charAt(0));
757         }
758         for (int i = 0; i <= 9; i++) {
759             mi = go.add(gotoMarkerActions[i]);
760             setMnemonic(mi, "gotoMarker" + i);
761         }
762         menu.add(go);
763
764         menu.addSeparator();
765
766         mi = menu.add(editOptionsAction);
767         setMnemonic(mi, "editOptions");
768     }
769
770     /**
771      * Fill a JPopupMenu with menu items for the LogTableView.
772      *
773      * @param menu The JPopupMenu to fill.
774      */

775     public void fillMenu(JPopupMenu JavaDoc menu)
776     {
777         menu.add(copySelectedAction);
778         menu.add(copyVisibleAction);
779         menu.add(copyAllAction);
780
781         menu.add(clearAction);
782         menu.add(reduceToVisibleAction);
783         menu.add(removeVisibleAction);
784
785         menu.addSeparator();
786
787         menu.add(setSortColumnAction);
788         menu.add(toggleColumnFilterAction);
789         menu.add(clearColumnFiltersAction);
790
791         menu.addSeparator();
792
793         setMenu = new JMenu JavaDoc (resources.getString
794                                ("logTableView.action.setMarker.name",
795                                 "Set Mark"));
796         for (int i = 0; i <= 9; i++) {
797             setMenu.add(setMarkerActions[i]);
798         }
799         menu.add(setMenu);
800
801         goMenu = new JMenu JavaDoc (resources.getString
802                             ("logTableView.action.gotoMarker.name",
803                              "Goto Mark"));
804         for (int i = 0; i <= 9; i++) {
805             goMenu.add(gotoMarkerActions[i]);
806         }
807         menu.add(goMenu);
808
809         menu.addSeparator();
810
811         menu.add(editOptionsAction);
812     }
813
814     // }}}
815
// {{{ getConfigurator
816

817     /**
818      * Get a {@link de.qfs.lib.config.Configurator Configurator} for the
819      * LogTableView.
820      *
821      * @return The Configurator for the LogTableView.
822      */

823     public Configurator getConfigurator()
824     {
825         return configurator;
826     }
827
828     // }}}
829
// {{{ getOptions
830

831     /**
832      * Get the options of the LogTableView.
833      *
834      * @return The options of the LogTableView.
835      */

836     public final OptionSet getOptions()
837     {
838         return options;
839     }
840
841
842     // }}}
843
// {{{ setLoggingEnabled
844

845     /**
846      * Enable or disable logging output caused directly or indirectly by
847      * LogTableView methods. If logging is disabled, all calls to methods that
848      * might create log messages will be protected with {@link
849      * de.qfs.lib.log.Log#excludeThread de.qfs.lib.log.Log.excludeThread}.
850      * This is necessary, if the LogFilterTreeModel is used inside the
851      * application whose logs it is supposed to filter. <p>
852      *
853      * The default value is false.
854      *
855      * @param enable True to enable logging, false to disable it.
856      */

857     public final void setLoggingEnabled(boolean enable)
858     {
859         logging = enable;
860         model.setLoggingEnabled(enable);
861         if (detailView != null) {
862             detailView.setLoggingEnabled(logging);
863         }
864         if (filter != null) {
865             filter.setLoggingEnabled(logging);
866         }
867     }
868
869     // }}}
870
// {{{ setStatusLine
871

872     /**
873      * Set the StatusLine to use for informative display.
874      *
875      * @param statusLine
876      */

877     public void setStatusLine(StatusLine statusLine)
878     {
879         this.statusLine = statusLine;
880         if (search != null) {
881             search.setTitle();
882         }
883     }
884
885     // }}}
886

887     //----------------------------------------------------------------------
888
// Configuration
889
//----------------------------------------------------------------------
890
// {{{ setCommandDistributor
891

892     /**
893      * Set the CommandDistributor to use for command dispatch.
894      *
895      * @param cd The CommandDistributor to use.
896      */

897     public void setCommandDistributor(CommandDistributor cd)
898     {
899         commandDistributor = cd;
900     }
901
902     // }}}
903
// {{{ getCommandDistributor
904

905     /**
906      * Get the CommandDistributor to use for command dispatch.
907      *
908      * @return The CommandDistributor to use.
909      */

910     private CommandDistributor getCommandDistributor()
911     {
912         return commandDistributor == null
913             ? CommandDistributor.getDefaultDistributor()
914             : commandDistributor;
915     }
916
917     // }}}
918
// {{{ setResources
919

920     /**
921      * Override the default resources used by the LogTableView. If this method
922      * is not called, the LogTableView will use its default resource settings.
923      *
924      * @param rb ResourceBundle containing the resources to use.
925      */

926     public void setResources(ResourceBundle JavaDoc rb)
927     {
928         resources.addResources(rb);
929     }
930
931
932     // }}}
933
// {{{ setFilter
934

935     /**
936      * Set the filter for the table.
937      *
938      * @param filter The new filter.
939      */

940     public final void setFilter(TableModelFilter filter)
941     {
942         if (this.filter == null) {
943             this.filter = new LogTableFilter (filter);
944             this.filter.setLoggingEnabled(logging);
945         } else {
946             this.filter.setFilter(filter);
947         }
948     }
949
950     // }}}
951

952     //----------------------------------------------------------------------
953
// The clipboard
954
//----------------------------------------------------------------------
955
// {{{ copySelectedToClipboard
956

957     /**
958      * Copy the messages in the selected rows of the table to the clipboard.
959      */

960     public void copySelectedToClipboard()
961     {
962         int[] rows = table.getSelectedRows();
963
964         if (rows.length > 0) {
965             TableModel JavaDoc tm = table.getModel();
966             StringBuffer JavaDoc sb = new StringBuffer JavaDoc ();
967             for (int i = 0; i < rows.length; i++) {
968                 sb.append(((LogEntry) tm.getValueAt(rows[i], model.COL_ENTRY))
969                           .toString());
970                 sb.append("\n");
971             }
972             StringSelection JavaDoc ss = new StringSelection JavaDoc (sb.toString());
973             Toolkit.getDefaultToolkit().getSystemClipboard()
974                 .setContents(ss, ss);
975         }
976     }
977
978     // }}}
979
// {{{ copyVisibleToClipboard
980

981     /**
982      * Copy the messages from all visible rows of the table to the clipboard.
983      */

984     public void copyVisibleToClipboard()
985     {
986         if (table.getRowCount() > 0) {
987             TableModel JavaDoc tm = table.getModel();
988             StringBuffer JavaDoc sb = new StringBuffer JavaDoc ();
989             for (int i = 0; i < table.getRowCount(); i++) {
990                 sb.append(((LogEntry) tm.getValueAt(i, model.COL_ENTRY))
991                           .toString());
992                 sb.append("\n");
993             }
994             StringSelection JavaDoc ss = new StringSelection JavaDoc (sb.toString());
995             Toolkit.getDefaultToolkit().getSystemClipboard()
996                 .setContents(ss, ss);
997         }
998     }
999
1000    // }}}
1001
// {{{ copyAllToClipboard
1002

1003    /**
1004     * Copy the messages from all visible or invisible rows of the table to
1005     * the clipboard.
1006     */

1007    public void copyAllToClipboard()
1008    {
1009        LogEntry[] data = model.getData();
1010        if (data.length > 0) {
1011            StringBuffer JavaDoc sb = new StringBuffer JavaDoc ();
1012            for (int i = 0; i < data.length; i++) {
1013                sb.append(data[i].toString());
1014                sb.append("\n");
1015            }
1016            StringSelection JavaDoc ss = new StringSelection JavaDoc (sb.toString());
1017            Toolkit.getDefaultToolkit().getSystemClipboard()
1018                .setContents(ss, ss);
1019        }
1020    }
1021
1022
1023
1024    // }}}
1025

1026    //----------------------------------------------------------------------
1027
// Removing rows
1028
//----------------------------------------------------------------------
1029
// {{{ clearAll
1030

1031    /**
1032     * Remove all rows from the table model.
1033     */

1034    public void clearAll()
1035    {
1036        selListener.forgetSavedRow();
1037        model.setData(new LogEntry[0]);
1038    }
1039
1040    // }}}
1041
// {{{ reduceToVisible
1042

1043    /**
1044     * Remove all invisible rows from the table model.
1045     */

1046    public void reduceToVisible()
1047    {
1048        selListener.forgetSavedRow();
1049        model.reduceToVisible(filter);
1050    }
1051
1052    // }}}
1053
// {{{ removeVisible
1054

1055    /**
1056     * Remove all visible rows from the table model.
1057     */

1058    public void removeVisible()
1059    {
1060        selListener.forgetSavedRow();
1061        model.removeVisible(filter);
1062    }
1063
1064    // }}}
1065

1066    //----------------------------------------------------------------------
1067
// The detail view
1068
//----------------------------------------------------------------------
1069
// {{{ showDetail
1070

1071    /**
1072     * Tell the LogTableView whether the detail view should be displayed.
1073     *
1074     * @param show True to display the details.
1075     */

1076    public void showDetail(boolean show)
1077    {
1078        if (logging && logger.level >= Log.MTD) {
1079            logger.log(Log.MTD, "showDetail(boolean)",
1080                       logger.level < Log.MTDDETAIL ? "" :
1081                       "show: " + show);
1082        }
1083        multiPane.setShowing(show
1084                             ? multiPane.SHOW_BOTH
1085                             : multiPane.SHOW_FIRST);
1086    }
1087
1088    // }}}
1089
// {{{ toggleDetail
1090

1091    /**
1092     * Toggle display of the detail view.
1093     */

1094    private void toggleDetail()
1095    {
1096        if (logging && logger.level >= Log.MTD) {
1097            logger.log(Log.MTD, "showDetail()", "");
1098        }
1099        showDetail(multiPane.getShowing() != multiPane.SHOW_BOTH);
1100    }
1101
1102    // }}}
1103

1104    //----------------------------------------------------------------------
1105
// Observer interface
1106
//----------------------------------------------------------------------
1107
// {{{ update
1108

1109    /**
1110     * Notify the LogTableView that one of its Observables has changed.
1111     *
1112     * @param obj The Observable that changed.
1113     * @param arg Details about the change.
1114     */

1115    public void update(Observable JavaDoc obj, Object JavaDoc arg)
1116    {
1117        if (logging && logger.level >= Log.MTD) {
1118            logger.log(Log.MTD, "update(Observable,Object)",
1119                       logger.level < Log.MTDDETAIL ? "" :
1120                       "obj: " + obj + ", " +
1121                       "arg: " + arg);
1122        }
1123        if (obj instanceof Option) {
1124            boolean excluded = ! logging;
1125            if (excluded) {
1126                Log.excludeThread();
1127            }
1128            try {
1129                Option option = (Option) obj;
1130                if (option.getName().equals("MaxRows")) {
1131                    model.setMaxRows(options.getInt("MaxRows", 0));
1132                } else if (option.getName().equals("ShowLines")) {
1133                    if (options.getBoolean("ShowLines", true)) {
1134                        table.getColumnModel().setColumnMargin(1);
1135                        table.setRowMargin(1);
1136                        table.setRowHeight(16);
1137                        table.setShowVerticalLines(true);
1138                        table.setShowHorizontalLines(true);
1139                    } else {
1140                        table.getColumnModel().setColumnMargin(0);
1141                        table.setRowMargin(0);
1142                        table.setRowHeight(17);
1143                        table.setShowVerticalLines(false);
1144                        table.setShowHorizontalLines(false);
1145                    }
1146                }
1147            } finally {
1148                if (excluded) {
1149                    Log.includeThread();
1150                }
1151            }
1152        }
1153    }
1154
1155    // }}}
1156

1157    //----------------------------------------------------------------------
1158
// Helper methods
1159
//----------------------------------------------------------------------
1160
// {{{ initIcons
1161

1162    /**
1163     * Fetch the icons.
1164     */

1165    private void initIcons()
1166    {
1167        if (icons[0] == null) {
1168            for (int i = 0; i < icons.length; i++) {
1169                icons[i] = resources.getIcon(iconNames[i], null);
1170            }
1171        }
1172    }
1173
1174    // }}}
1175
// {{{ setupActions
1176

1177    /**
1178     * Set up the actions for the LogTableView.
1179     */

1180    private void setupActions()
1181    {
1182        // do this only once
1183
if (copySelectedAction == null) {
1184            copySelectedAction = new AbstractAction JavaDoc () {
1185                public void actionPerformed (ActionEvent JavaDoc e) {
1186                    copySelectedToClipboard();
1187                }
1188            };
1189            setActionProperties(copySelectedAction, "copySelected");
1190
1191            copyVisibleAction = new AbstractAction JavaDoc () {
1192                public void actionPerformed (ActionEvent JavaDoc e) {
1193                    copyVisibleToClipboard();
1194                }
1195            };
1196            setActionProperties(copyVisibleAction, "copyVisible");
1197
1198            copyAllAction = new AbstractAction JavaDoc () {
1199                public void actionPerformed (ActionEvent JavaDoc e) {
1200                    copyAllToClipboard();
1201                }
1202            };
1203            setActionProperties(copyAllAction, "copyAll");
1204
1205            clearAction = new AbstractAction JavaDoc () {
1206                public void actionPerformed (ActionEvent JavaDoc e) {
1207                    clearAll();
1208                }
1209            };
1210            setActionProperties(clearAction, "clear");
1211
1212            reduceToVisibleAction = new AbstractAction JavaDoc () {
1213                public void actionPerformed (ActionEvent JavaDoc e) {
1214                    reduceToVisible();
1215                }
1216            };
1217            setActionProperties(reduceToVisibleAction, "reduceToVisible");
1218
1219            removeVisibleAction = new AbstractAction JavaDoc () {
1220                public void actionPerformed (ActionEvent JavaDoc e) {
1221                    removeVisible();
1222                }
1223            };
1224            setActionProperties(removeVisibleAction, "removeVisible");
1225
1226            toggleColumnFilterAction = new AbstractAction JavaDoc () {
1227                public void actionPerformed (ActionEvent JavaDoc e) {
1228                    int row = table.getSelectionModel()
1229                        .getAnchorSelectionIndex();
1230                    int column = table.getColumnModel().getSelectionModel()
1231                        .getAnchorSelectionIndex();
1232                    if (row == -1 || column == -1) {
1233                        return;
1234                    }
1235                    int ccol = table.convertColumnIndexToModel(column);
1236                    filterCell(row, ccol);
1237                }
1238            };
1239            setActionProperties(toggleColumnFilterAction,
1240                                "toggleColumnFilter");
1241
1242            clearColumnFiltersAction = new AbstractAction JavaDoc () {
1243                public void actionPerformed (ActionEvent JavaDoc e) {
1244                    filter.clearFilters();
1245                    for (int i = 0; i < filterEnabled.length; i++) {
1246                        filterEnabled[i] = false;
1247                        table.getTableHeader().repaint();
1248                    }
1249                }
1250            };
1251            setActionProperties(clearColumnFiltersAction,
1252                                "clearColumnFilters");
1253
1254            setSortColumnAction = new AbstractAction JavaDoc () {
1255                public void actionPerformed (ActionEvent JavaDoc e) {
1256                    boolean excluded = ! logging;
1257                    if (excluded) {
1258                        Log.excludeThread();
1259                    }
1260                    try {
1261                        int column = table.getColumnModel().getSelectionModel()
1262                        .getAnchorSelectionIndex();
1263                        if (column == -1) {
1264                            return;
1265                        }
1266                        int ccol = table.convertColumnIndexToModel(column);
1267                        sorter.setSortColumn(ccol);
1268                    } finally {
1269                        if (excluded) {
1270                            Log.includeThread();
1271                        }
1272                    }
1273                }
1274            };
1275            setActionProperties(setSortColumnAction,
1276                                "setSortColumn");
1277
1278            setMarkerActions = new Action JavaDoc[10];
1279            gotoMarkerActions = new Action JavaDoc[10];
1280            for (int i = 0; i <= 9; i++) {
1281                setMarkerActions[i] = new SetMarkerAction ("" + i);
1282                setActionProperties(setMarkerActions[i], "setMarker" + i);
1283                gotoMarkerActions[i] = new GotoMarkerAction ("" + i);
1284                setActionProperties(gotoMarkerActions[i], "gotoMarker" + i);
1285            }
1286
1287            editOptionsAction = new AbstractAction JavaDoc () {
1288                public void actionPerformed (ActionEvent JavaDoc e) {
1289                    editOptions();
1290                }
1291            };
1292            setActionProperties(editOptionsAction,
1293                                "editOptions");
1294
1295        }
1296    }
1297
1298    // }}}
1299
// {{{ setActionProperties
1300

1301    /**
1302     * Set name and icon for an action from the LogTableView's resources.
1303     *
1304     * @param action The action to prepare.
1305     * @param resource Identifier for the action's resources.
1306     */

1307    private void setActionProperties(Action JavaDoc action, String JavaDoc resource)
1308    {
1309        String JavaDoc name = resources.getString
1310            ("logTableView.action." + resource + ".name", null);
1311        if (name != null) {
1312            action.putValue(Action.NAME, name);
1313        }
1314
1315        String JavaDoc iconName = resources.getString
1316            ("logTableView.action." + resource + ".icon", null);
1317        if (iconName != null) {
1318            Icon JavaDoc icon = resources.getIcon (iconName, null);
1319            if (icon != null) {
1320                action.putValue(Action.SMALL_ICON, icon);
1321            }
1322        }
1323    }
1324
1325    // }}}
1326
// {{{ setMnemonic
1327

1328    /**
1329     * Set the mnemonic shortcut for a menu item.
1330     *
1331     * @param mi The menu item.
1332     * @param resource The identifier to look up the mnemonic
1333     * resource.
1334     */

1335    private void setMnemonic(JMenuItem JavaDoc mi, String JavaDoc resource)
1336    {
1337        String JavaDoc mnemo = resources.getString
1338            ("logTableView.action." + resource + ".mnemonic", null);
1339        if (mnemo != null) {
1340            mi.setMnemonic(mnemo.charAt(0));
1341        }
1342    }
1343
1344    // }}}
1345
// {{{ editOptions
1346

1347    /**
1348     * Bring up a dialog to edit the options for the LogTableView.
1349     */

1350    private void editOptions()
1351    {
1352        if (logging && logger.level >= Log.MTD) {
1353            logger.log(Log.MTD, "editOptions()", "");
1354        }
1355
1356        boolean excluded = ! logging;
1357        if (excluded) {
1358            Log.excludeThread();
1359        }
1360        try {
1361            Frame JavaDoc frame;
1362            Window JavaDoc win = SwingUtilities.windowForComponent(this);
1363            if (win instanceof Frame JavaDoc) {
1364                frame = (Frame JavaDoc) win;
1365            } else {
1366                // create a dummy parent
1367
frame = new Frame JavaDoc();
1368            }
1369            if (optionDialog == null) {
1370                optionDialog = new OptionDialog
1371                    (frame, resources.getString
1372                         ("logTableView.optionDialog.title", "MISSING"),
1373                     options, "tableview");
1374            } else {
1375                optionDialog.update(options);
1376            }
1377            optionDialog.setLocationRelativeTo(this);
1378            optionDialog.doModal();
1379        } finally {
1380            if (excluded) {
1381                Log.includeThread();
1382            }
1383        }
1384    }
1385
1386    // }}}
1387

1388    // {{{ restoreRow
1389

1390    /**
1391     * Restore the last row known to be selected. If the row is invisible find
1392     * a row as close as possible.
1393     *
1394     * @param row The row to search for.
1395     * @param column The column to select.
1396     */

1397    private void restoreRow(int row, int column)
1398    {
1399        if (logging && logger.level >= Log.MTD) {
1400            logger.log(Log.MTD, "restoreRow(int,int)",
1401                       logger.level < Log.MTDDETAIL ? "" :
1402                       "row: " + row + ", " +
1403                       "column: " + column);
1404        }
1405        if (row >= 0) {
1406            int i;
1407            FilteredAndSortedTableModel filteredTableModel =
1408                (FilteredAndSortedTableModel) table.getModel();
1409            // first try: exact match
1410
for (i = 0; i < filteredTableModel.getRowCount(); i++) {
1411                if (row == filteredTableModel.getMappedRow(i)) {
1412                    break;
1413                }
1414            }
1415            // second try: nearest match
1416
if (i == filteredTableModel.getRowCount()) {
1417                for (i = 0; i < filteredTableModel.getRowCount(); i++) {
1418                    if (sorter.compare
1419                        (model, row,
1420                         filteredTableModel.getMappedRow(i)) <= 0) {
1421                        break;
1422                    }
1423                }
1424            }
1425
1426            if (i == filteredTableModel.getRowCount()) {
1427                i--;
1428            }
1429
1430            jumpToRow(i, false);
1431
1432            if (column >= 0) {
1433                table.addColumnSelectionInterval(column, column);
1434            }
1435        }
1436    }
1437
1438    // }}}
1439
// {{{ jumpToRow
1440

1441    /**
1442     * Select a row and center it on the table.
1443     *
1444     * @param row The row to select.
1445     * @param always If true, always center the row, if false, recenter only
1446     * if the row is not currently visible.
1447     */

1448    private void jumpToRow(int row, boolean always)
1449    {
1450            // scroll so selected row is centered vertically
1451
Rectangle JavaDoc rect = table.getCellRect(row, 0, false);
1452            JViewport JavaDoc vp = (JViewport JavaDoc) table.getParent();
1453            Dimension JavaDoc vs = vp.getSize();
1454            Point JavaDoc pos = vp.getViewPosition();
1455            if (!always) {
1456                if (pos.y <= rect.y
1457                    && pos.y + vs.height >= rect.y + rect.height) {
1458                    // already visible
1459
table.addRowSelectionInterval(row, row);
1460                    return;
1461                }
1462            }
1463            Dimension JavaDoc size = table.getSize();
1464            pos.y = rect.y - (vs.height - rect.height) / 2;
1465            if (pos.y + vs.height > size.height) {
1466                pos.y = size.height - vs.height;
1467            }
1468            if (pos.y < 0) {
1469                pos.y = 0;
1470            }
1471            ((JViewport JavaDoc) table.getParent()).setViewPosition(pos);
1472            table.getParent().repaint();
1473
1474            table.addRowSelectionInterval(row, row);
1475            table.getSelectionModel().setAnchorSelectionIndex(row);
1476    }
1477
1478    // }}}
1479
// {{{ filterCell
1480

1481    /**
1482     * Toggle a filter for a column.
1483     *
1484     * @param row The selected row that determines the filter value.
1485     * @param column The column whose filter to toggle.
1486     */

1487    private void filterCell(int row, int column)
1488    {
1489        LogEntry entry = (LogEntry) table.getValueAt(row, model.COL_ENTRY);
1490        switch (column) {
1491        case model.COL_LEVEL:
1492            filter.setFilterLevel(entry.getLevel());
1493            filterEnabled[model.COL_LEVEL] = ! filterEnabled[model.COL_LEVEL];
1494            table.getTableHeader().repaint();
1495            break;
1496        case model.COL_THREAD:
1497            filter.setFilterThread(entry.getThread());
1498            filterEnabled[model.COL_THREAD] =
1499                ! filterEnabled[model.COL_THREAD];
1500            table.getTableHeader().repaint();
1501            break;
1502        case model.COL_CLASS:
1503            filter.setFilterClass(entry.getClazz());
1504            filterEnabled[model.COL_CLASS] = ! filterEnabled[model.COL_CLASS];
1505            table.getTableHeader().repaint();
1506            break;
1507        case model.COL_METHOD:
1508            filter.setFilterMethod(entry.getMethod());
1509            filterEnabled[model.COL_METHOD] =
1510                ! filterEnabled[model.COL_METHOD];
1511            table.getTableHeader().repaint();
1512            break;
1513        }
1514    }
1515
1516    // }}}
1517

1518    //----------------------------------------------------------------------
1519
// Markers
1520
//----------------------------------------------------------------------
1521
// {{{ class Markers
1522

1523    // {{{ static Markers variables
1524

1525    /**
1526     * The Logger used for logging Markers methods.
1527     */

1528    private final static Logger mLogger = new Logger (Markers.class);
1529
1530    // }}}
1531

1532    /**
1533     * Markers for the table. Markers can be added, removed, cleared and
1534     * jumped to. They are updated according to changes in the table model.
1535     */

1536    private class Markers
1537        implements TableModelListener JavaDoc
1538    {
1539        // {{{ variables
1540

1541        /**
1542         * Hashtable mapping from identifier to row.
1543         */

1544        private Hashtable JavaDoc markers = new Hashtable JavaDoc ();
1545
1546        // }}}
1547

1548        // {{{ constructor
1549

1550        /**
1551         * Create a new Markers object.
1552         */

1553        public Markers ()
1554        {
1555        }
1556
1557        // }}}
1558

1559        // {{{ addMarker
1560

1561        /**
1562         * Add a marker.
1563         *
1564         * @param id Identifier for the marker.
1565         * @param row The row to mark.
1566         */

1567        public void addMarker(String JavaDoc id, int row)
1568        {
1569            if (logging && mLogger.level >= Log.MTD) {
1570                mLogger.log(Log.MTD, "addMarker(String,int)",
1571                           mLogger.level < Log.MTDDETAIL ? "" :
1572                           "id: " + id + ", " +
1573                           "row: " + row);
1574            }
1575            markers.put(id, new Integer JavaDoc(row));
1576            if (statusLine != null) {
1577                statusLine.setTemporaryMessage
1578                    (Message.format("logTableView.setMark",
1579                                    new String JavaDoc[] {id}));
1580            }
1581        }
1582
1583
1584        // }}}
1585
// {{{ removeMarker
1586

1587        /**
1588         * Remove a marker.
1589         *
1590         * @param id Identifier of the marker.
1591         */

1592        public void removeMarker(String JavaDoc id)
1593        {
1594            if (logging && mLogger.level >= Log.MTD) {
1595                mLogger.log(Log.MTD, "removeMarker(String)",
1596                           mLogger.level < Log.MTDDETAIL ? "" :
1597                           "id: " + id);
1598            }
1599            markers.remove(id);
1600        }
1601
1602        // }}}
1603
// {{{ clearMarkers
1604

1605        /**
1606         * Remove all markers.
1607         */

1608        public void clearMarkers()
1609        {
1610            if (logging && mLogger.level >= Log.MTD) {
1611                mLogger.log(Log.MTD, "clearMarkers()", "");
1612            }
1613            markers.clear();
1614        }
1615
1616        // }}}
1617
// {{{ jumpToMarker
1618

1619        /**
1620         * Jump to the row pointed to by a marker.
1621         *
1622         * @param id Identifier for the marker.
1623         */

1624        public void jumpToMarker(String JavaDoc id)
1625        {
1626            if (logging && mLogger.level >= Log.MTD) {
1627                mLogger.log(Log.MTD, "jumpToMarker(String)",
1628                           mLogger.level < Log.MTDDETAIL ? "" :
1629                           "id: " + id);
1630            }
1631            Integer JavaDoc row = (Integer JavaDoc) markers.get(id);
1632            if (row != null) {
1633                if (statusLine != null) {
1634                    statusLine.setTemporaryMessage
1635                        (Message.format("logTableView.jumpToMark",
1636                                        new String JavaDoc[] {id}));
1637                }
1638                table.clearSelection();
1639                restoreRow(row.intValue(), -1);
1640            }
1641        }
1642
1643        // }}}
1644

1645        // {{{ tableChanged
1646

1647        /**
1648         * Notification of a change in the table model's contents.
1649         *
1650         * @param event The event containing the details.
1651         */

1652        public void tableChanged(TableModelEvent JavaDoc event)
1653        {
1654            int start = event.getFirstRow();
1655            int end = event.getLastRow();
1656            if (logging && mLogger.level >= Log.MTD) {
1657                mLogger.log(Log.MTD, "tableChanged(TableModelEvent)",
1658                           mLogger.level < Log.MTDDETAIL ? "" :
1659                           ", start: " + start +
1660                           ", end: " + end);
1661            }
1662            if (markers.size() >= 0) {
1663                int type = event.getType();
1664                int diff = end - start + 1;
1665                if (start == event.HEADER_ROW
1666                    || (start == 0 && end >= model.getRowCount()
1667                        && type == event.UPDATE)) {
1668                    // complete change
1669
if (logging && mLogger.level >= Log.DBG) {
1670                        mLogger.log(Log.DBG,
1671                                   "tableChanged(TableModelEvent)",
1672                                   "markers cleared");
1673                    }
1674                    clearMarkers();
1675                } else if (type == event.INSERT) {
1676                    for (Enumeration JavaDoc e = markers.keys();
1677                         e.hasMoreElements(); ) {
1678                        String JavaDoc id = (String JavaDoc) e.nextElement();
1679                        Integer JavaDoc row = (Integer JavaDoc) markers.get(id);
1680                        if (start <= row.intValue()) {
1681                            markers.put(id,
1682                                        new Integer JavaDoc (row.intValue() + diff));
1683                            if (logging && mLogger.level >= Log.DBG) {
1684                                mLogger.log(Log.DBG,
1685                                           "tableChanged(TableModelEvent)",
1686                                           "marker " + id + " moved up");
1687                            }
1688                        }
1689                    }
1690                } else if (type == event.DELETE) {
1691                    for (Enumeration JavaDoc e = markers.keys();
1692                         e.hasMoreElements(); ) {
1693                        String JavaDoc id = (String JavaDoc) e.nextElement();
1694                        Integer JavaDoc row = (Integer JavaDoc) markers.get(id);
1695                        if (start <= row.intValue()) {
1696                            if (end >= row.intValue()) {
1697                                markers.remove(id);
1698                                if (logging && mLogger.level >= Log.DBG) {
1699                                    mLogger.log(Log.DBG,
1700                                               "tableChanged(TableModelEvent)",
1701                                               "marker " + id + " removed");
1702                                }
1703                            } else {
1704                                markers.put(id, new Integer JavaDoc
1705                                            (row.intValue() - diff));
1706                                if (logging && mLogger.level >= Log.DBG) {
1707                                    mLogger.log(Log.DBG,
1708                                               "tableChanged(TableModelEvent)",
1709                                               "marker " + id + " moved down");
1710                                }
1711                            }
1712                        }
1713                    }
1714                }
1715            }
1716        }
1717
1718        // }}}
1719
}
1720
1721    // }}}
1722
// {{{ class SetMarkerAction
1723

1724    private class SetMarkerAction
1725        extends AbstractAction JavaDoc
1726    {
1727        /**
1728         * The marker identifier for this action.
1729         */

1730        private String JavaDoc marker;
1731
1732        // {{{ constructor
1733

1734        public SetMarkerAction (String JavaDoc marker)
1735        {
1736            this.marker = marker;
1737        }
1738
1739        // }}}
1740

1741        // {{{ actionPerformed
1742

1743        /**
1744         * Perform the action.
1745         *
1746         * @param e Ignored.
1747         */

1748        public void actionPerformed(ActionEvent JavaDoc e)
1749        {
1750            int row = table.getSelectionModel().getAnchorSelectionIndex();
1751            if (row >= 0) {
1752                row = ((FilteredAndSortedTableModel) table.getModel())
1753                    .getMappedRow(row);
1754                markers.addMarker(marker, row);
1755            }
1756        }
1757
1758        // }}}
1759
}
1760
1761    // }}}
1762
// {{{ class GotoMarkerAction
1763

1764    private class GotoMarkerAction
1765        extends AbstractAction JavaDoc
1766    {
1767        /**
1768         * The marker identifier for this action.
1769         */

1770        private String JavaDoc marker;
1771
1772        // {{{ constructor
1773

1774        public GotoMarkerAction (String JavaDoc marker)
1775        {
1776            this.marker = marker;
1777        }
1778
1779        // }}}
1780

1781        // {{{ actionPerformed
1782

1783        /**
1784         * Perform the action.
1785         *
1786         * @param e Ignored.
1787         */

1788        public void actionPerformed(ActionEvent JavaDoc e)
1789        {
1790            int row = table.getSelectionModel().getAnchorSelectionIndex();
1791            markers.jumpToMarker(marker);
1792        }
1793
1794        // }}}
1795
}
1796
1797    // }}}
1798

1799    //----------------------------------------------------------------------
1800
// Key listeners
1801
//----------------------------------------------------------------------
1802
// {{{ class TableKeyListener
1803

1804    // {{{ static variables for TableKeyListener
1805

1806    /**
1807     * Modifier mask to filter unwanted detail.
1808     */

1809    private final static int KEY_MASK =
1810        KeyEvent.SHIFT_MASK | KeyEvent.CTRL_MASK | KeyEvent.ALT_MASK;
1811
1812    // }}}
1813
/**
1814     * A listener for key presses on the table. Used for fast scrolling via
1815     * Alt-up/Alt-down.
1816     */

1817    protected class TableKeyListener
1818        extends KeyAdapter JavaDoc
1819    {
1820        // {{{ variables
1821

1822        /**
1823         * The selected row during fast scrolling, restored after the key
1824         * is released.
1825         */

1826        private int row = -1;
1827
1828        /**
1829         * Delayed action for restoring selection after fast scroll.
1830         */

1831        DelayedAction delayed = new DelayedAction (300);
1832
1833        /**
1834         * Action for delayed selection restore.
1835         */

1836        private Runnable JavaDoc restoreSel = new Runnable JavaDoc () {
1837            public void run()
1838            {
1839                if (row >= 0) {
1840                    table.getSelectionModel().setSelectionInterval(row, row);
1841                    row = -1;
1842                }
1843            }
1844        };
1845
1846        // }}}
1847

1848        // {{{ keyPressed
1849

1850        /**
1851         * Fast scrolling for Alt-Up, Alt-Down
1852         *
1853         * @param e KeyPressed event.
1854         */

1855        public void keyPressed(KeyEvent JavaDoc e) {
1856            if (e.getKeyCode() == e.VK_UP
1857                && (e.getModifiers() & KEY_MASK) == e.ALT_MASK) {
1858                e.consume();
1859                fastUp();
1860            } else if (e.getKeyCode() == e.VK_DOWN
1861                && (e.getModifiers() & KEY_MASK) == e.ALT_MASK) {
1862                e.consume();
1863                fastDown();
1864            } else if (e.getKeyCode() >= e.VK_0 && e.getKeyCode() <= e.VK_9
1865                       && (e.getModifiers() & KEY_MASK) == e.ALT_MASK) {
1866                // set mark
1867
e.consume();
1868                int row = table.getSelectionModel().getAnchorSelectionIndex();
1869                if (row >= 0) {
1870                    row = ((FilteredAndSortedTableModel) table.getModel())
1871                        .getMappedRow(row);
1872                    char c = (char) ('0' + e.getKeyCode() - e.VK_0);
1873                    markers.addMarker("" + c, row);
1874                }
1875            } else if (e.getKeyCode() >= e.VK_0 && e.getKeyCode() <= e.VK_9
1876                       && (e.getModifiers() & KEY_MASK) == e.CTRL_MASK) {
1877                e.consume();
1878                char c = (char) ('0' + e.getKeyCode() - e.VK_0);
1879                markers.jumpToMarker("" + c);
1880            }
1881        }
1882
1883        // }}}
1884

1885        // {{{ fastUp
1886

1887        private void fastUp()
1888        {
1889            if (row < 0) {
1890                row = table.getSelectionModel().getAnchorSelectionIndex();
1891            }
1892            if (row == 0) {
1893                return;
1894            }
1895            row--;
1896            JViewport JavaDoc vp = tablePane.getViewport();
1897            Point JavaDoc pos = vp.getViewPosition();
1898            if (pos.y > 0) {
1899                pos.y -= table.getRowHeight() + table.getRowMargin();
1900                if (pos.y < 0) {
1901                    pos.y = 0;
1902                }
1903                vp.setViewPosition(pos);
1904                delayed.perform(restoreSel);
1905            } else {
1906                table.getSelectionModel()
1907                    .setSelectionInterval(row, row);
1908                row = -1;
1909            }
1910        }
1911
1912        // }}}
1913
// {{{ fastDown
1914

1915        private void fastDown()
1916        {
1917            if (row < 0) {
1918                row = table.getSelectionModel().getAnchorSelectionIndex();
1919            }
1920            if (row == table.getRowCount() - 1) {
1921                return;
1922            }
1923            row++;
1924            JViewport JavaDoc vp = tablePane.getViewport();
1925            Point JavaDoc pos = vp.getViewPosition();
1926            Dimension JavaDoc vs = vp.getViewSize();
1927            if (pos.y + vp.getSize().height < vs.height) {
1928                pos.y += table.getRowHeight() + table.getRowMargin();
1929                if (pos.y + vp.getSize().height > vs.height) {
1930                    pos.y = vs.height - vp.getSize().height;
1931                }
1932                vp.setViewPosition(pos);
1933                delayed.perform(restoreSel);
1934            } else {
1935                table.getSelectionModel()
1936                    .setSelectionInterval(row, row);
1937                row = -1;
1938            }
1939        }
1940
1941        // }}}
1942
}
1943
1944    // }}}
1945
// {{{ class Search
1946

1947    /**
1948     * This class implements a simple search mechanism on the table.
1949     */

1950    private class Search
1951        extends KeyAdapter JavaDoc
1952        implements ActionListener JavaDoc
1953    {
1954        // {{{ variables
1955

1956        /**
1957         * Search timestamp.
1958         */

1959        private long lastTimestamp = 0;
1960
1961        /**
1962         * Search string.
1963         */

1964        private StringBuffer JavaDoc search = new StringBuffer JavaDoc ();
1965
1966        /**
1967         * Search level.
1968         */

1969        private int lastLevel = -1;
1970
1971        /**
1972         * Forward or reverse search.
1973         */

1974        private boolean reverse = false;
1975
1976        /**
1977         * Whether last search wrapped around.
1978         */

1979        private boolean wrapped = false;
1980
1981        /**
1982         * Whether last search was successful.
1983         */

1984        private boolean found = false;
1985
1986        /**
1987         * Title string for search.
1988         */

1989        private String JavaDoc searchTitle =
1990            resources.getString("logTableView.search.title", "search");
1991
1992        /**
1993         * Title string for reverse search.
1994         */

1995        private String JavaDoc rsearchTitle =
1996            resources.getString("logTableView.rsearch.title", "rsearch");
1997
1998        /**
1999         * Title string for wrapped search.
2000         */

2001        private String JavaDoc wrappedTitle =
2002            resources.getString("logTableView.wrapped.title", "wrapped");
2003
2004        /**
2005         * Title string for unsuccessful search.
2006         */

2007        private String JavaDoc notFoundTitle =
2008            resources.getString("logTableView.notFound.title", "not found");
2009
2010        // }}}
2011

2012        // {{{ keyPressed
2013

2014        /**
2015         * Search in current column.
2016         *
2017         * @param e KeyPressed event.
2018         */

2019        public void keyPressed(KeyEvent JavaDoc e)
2020        {
2021            char c = e.getKeyChar();
2022            if ((e.getModifiers() & (e.ALT_MASK | e.CTRL_MASK)) == 0
2023                && c != e.CHAR_UNDEFINED
2024                && ! Character.isISOControl(c)) {
2025                e.consume();
2026
2027                c = Character.toLowerCase(c);
2028
2029                int row = table.getSelectionModel().getAnchorSelectionIndex();
2030                int column = table.getColumnModel().getSelectionModel()
2031                    .getAnchorSelectionIndex();
2032                if (row == -1 || column == -1) {
2033                    return;
2034                }
2035
2036                int ccol = table.convertColumnIndexToModel(column);
2037                wrapped = false;
2038                if (ccol == model.COL_LEVEL) {
2039                    if (Character.isDigit(c)) {
2040                        lastLevel = c == '0' ? 10 : c - '0';
2041                        searchRow(row, lastLevel, reverse);
2042                    }
2043                    search.setLength(0);
2044                } else {
2045                    lastLevel = -1;
2046                    search.append(c);
2047                    // need unconverted column for getValueAt
2048
searchRow(row, column, search.toString(), true, reverse);
2049                }
2050                lastTimestamp = e.getWhen();
2051                setTitle();
2052            }
2053        }
2054
2055        // }}}
2056
// {{{ actionPerformed
2057

2058        /**
2059         * Bindings for various KeyStrokes.
2060         *
2061         * @param event The ActionEvent.
2062         */

2063        public void actionPerformed (ActionEvent JavaDoc event)
2064        {
2065            int pos = event.paramString().lastIndexOf ("=");
2066            if (pos == -1) {
2067                return;
2068            }
2069
2070            int row = table.getSelectionModel().getAnchorSelectionIndex();
2071            int column = table.getColumnModel().getSelectionModel()
2072                .getAnchorSelectionIndex();
2073            if (row == -1 || column == -1) {
2074                return;
2075            }
2076            int ccol = table.convertColumnIndexToModel(column);
2077
2078            boolean excluded = ! logging;
2079            if (excluded) {
2080                Log.excludeThread();
2081            }
2082            try {
2083                String JavaDoc cmd = event.paramString().substring (pos + 1);
2084                if (cmd.equals("backsearch")) {
2085                    wrapped = false;
2086                    found = true;
2087                    if (ccol != model.COL_LEVEL && search.length() > 0) {
2088                        search.setLength(search.length() - 1);
2089                    }
2090                    setTitle();
2091                } else if (cmd.equals("copysearch")) {
2092                    if (ccol != model.COL_LEVEL) {
2093                        wrapped = false;
2094                        found = true;
2095                        search = new StringBuffer JavaDoc
2096                            (((String JavaDoc) table.getValueAt(row, column))
2097                                .toLowerCase());
2098                        setTitle();
2099                    }
2100                } else if (cmd.equals("searchagain")) {
2101                    if (ccol == model.COL_LEVEL) {
2102                        if (lastLevel >= 0) {
2103                            wrapped = false;
2104                            searchRow(row, lastLevel, reverse);
2105                            setTitle();
2106                        }
2107                    } else if (search.length() > 0) {
2108                        wrapped = false;
2109                        searchRow(row, column, search.toString(), false,
2110                                  reverse);
2111                        setTitle();
2112                    }
2113                } else if (cmd.equals("clearsearch")) {
2114                    wrapped = false;
2115                    search.setLength(0);
2116                    lastLevel = -1;
2117                    setTitle();
2118                } else if (cmd.equals("revertsearch")) {
2119                    wrapped = false;
2120                    found = true;
2121                    reverse = !reverse;
2122                    setTitle();
2123                }
2124            } finally {
2125                if (excluded) {
2126                    Log.includeThread();
2127                }
2128            }
2129        }
2130
2131        // }}}
2132

2133        // {{{ searchRow
2134

2135        /**
2136         * Search for a row in the table matching certain criteria.
2137         *
2138         * @param row The row to start from.
2139         * @param column The column to search in.
2140         * @param find The string to find.
2141         * @param expand Whether the string was just expanded, in which
2142         * case tha start row is a valid target.
2143         * @param reverse Whether the search direction is reversed.
2144         */

2145        private void searchRow(int row, int column, String JavaDoc find,
2146                               boolean expand, boolean reverse)
2147        {
2148            found = false;
2149            int len = find.length();
2150            if (reverse) {
2151                for (int i = row - (expand ? 0 : 1); i >= 0; i--) {
2152                    String JavaDoc val = (String JavaDoc) table.getValueAt(i, column);
2153                    if (val.length() >= len &&
2154                        val.toLowerCase().indexOf(find) >= 0) {
2155                        jumpToRow(i, false);
2156                        table.getSelectionModel().setSelectionInterval(i, i);
2157                        found = true;
2158                        break;
2159                    }
2160                }
2161                for (int i = table.getRowCount() - 1; !found && i > row; i--) {
2162                    String JavaDoc val = (String JavaDoc) table.getValueAt(i, column);
2163                    if (val.length() >= len &&
2164                        val.toLowerCase().indexOf(find) >= 0) {
2165                        jumpToRow(i, false);
2166                        table.getSelectionModel().setSelectionInterval(i, i);
2167                        found = true;
2168                        wrapped = true;
2169                        break;
2170                    }
2171                }
2172            } else {
2173                for (int i = row + (expand ? 0 : 1);
2174                     i < table.getRowCount(); i++) {
2175                    String JavaDoc val = (String JavaDoc) table.getValueAt(i, column);
2176                    if (val.length() >= len &&
2177                        val.toLowerCase().indexOf(find) >= 0) {
2178                        jumpToRow(i, false);
2179                        table.getSelectionModel().setSelectionInterval(i, i);
2180                        found = true;
2181                        break;
2182                    }
2183                }
2184                for (int i = 0; !found && i < row; i++) {
2185                    String JavaDoc val = (String JavaDoc) table.getValueAt(i, column);
2186                    if (val.length() >= len &&
2187                        val.toLowerCase().indexOf(find) >= 0) {
2188                        jumpToRow(i, false);
2189                        table.getSelectionModel().setSelectionInterval(i, i);
2190                        found = true;
2191                        wrapped = true;
2192                        break;
2193                    }
2194                }
2195            }
2196        }
2197
2198        /**
2199         * Search for the next row in the table with a given level.
2200         *
2201         * @param row The start row.
2202         * @param level The level to look for.
2203         * @param reverse Whether the search direction is reversed.
2204         */

2205        private void searchRow(int row, int level, boolean reverse)
2206        {
2207            found = false;
2208            if (reverse) {
2209                for (int i = row - 1; i >= 0; i--) {
2210                    if (((LogEntry) table.getValueAt(i, model.COL_ENTRY))
2211                        .getLevel() == level) {
2212                        jumpToRow(i, false);
2213                        table.getSelectionModel().setSelectionInterval(i, i);
2214                        found = true;
2215                        break;
2216                    }
2217                }
2218                for (int i = table.getRowCount() - 1; !found && i > row; i--) {
2219                    if (((LogEntry) table.getValueAt(i, model.COL_ENTRY))
2220                        .getLevel() == level) {
2221                        jumpToRow(i, false);
2222                        table.getSelectionModel().setSelectionInterval(i, i);
2223                        found = true;
2224                        wrapped = true;
2225                        break;
2226                    }
2227                }
2228            } else {
2229                for (int i = row + 1; i < table.getRowCount(); i++) {
2230                    if (((LogEntry) table.getValueAt(i, model.COL_ENTRY))
2231                        .getLevel() == level) {
2232                        jumpToRow(i, false);
2233                        table.getSelectionModel().setSelectionInterval(i, i);
2234                        found = true;
2235                        break;
2236                    }
2237                }
2238                for (int i = 0; !found && i < row; i++) {
2239                    if (((LogEntry) table.getValueAt(i, model.COL_ENTRY))
2240                        .getLevel() == level) {
2241                        jumpToRow(i, false);
2242                        table.getSelectionModel().setSelectionInterval(i, i);
2243                        found = true;
2244                        wrapped = true;
2245                        break;
2246                    }
2247                }
2248            }
2249        }
2250
2251        // }}}
2252
// {{{ setTitle
2253

2254        /**
2255         * Set the title of the table part of the LogTableView. Depends on
2256         * the current search string, direction and result.
2257         */

2258        private void setTitle()
2259        {
2260            StringBuffer JavaDoc sb = new StringBuffer JavaDoc ();
2261            StatusLine statusLine = null;
2262            if (statusLine == null) {
2263                sb.append(titleBase);
2264            }
2265            if (lastLevel >= 0
2266                || search.length() > 0) {
2267                sb.append(" ");
2268                sb.append(reverse ? rsearchTitle : searchTitle);
2269                if (wrapped) {
2270                    sb.append(" (");
2271                    sb.append(wrappedTitle);
2272                    sb.append(")");
2273                } else if (! found) {
2274                    sb.append(" (");
2275                    sb.append(notFoundTitle);
2276                    sb.append(")");
2277                }
2278                sb.append(": ");
2279                if (lastLevel > 0) {
2280                    if (statusLine == null) {
2281                        titleLabel.setIcon(icons[lastLevel - 1]);
2282                    } else {
2283                        statusLine.getMessageLabel()
2284                            .setIcon(icons[lastLevel - 1]);
2285                        statusLine.getMessageLabel()
2286                            .setHorizontalTextPosition(JLabel.LEADING);
2287                    }
2288                } else {
2289                    if (statusLine == null) {
2290                        titleLabel.setIcon(null);
2291                    } else {
2292                        statusLine.getMessageLabel().setIcon(null);
2293                    }
2294                    sb.append(search);
2295                }
2296            } else if (statusLine == null) {
2297                titleLabel.setIcon(null);
2298            }
2299            if (statusLine == null) {
2300                titleLabel.setText(sb.toString());
2301            } else {
2302                titleLabel.setText(titleBase);
2303                titleLabel.setIcon(null);
2304                if (sb.length() == 0) {
2305                    statusLine.popMessage();
2306                } else {
2307                    statusLine.pushMessage(sb.toString());
2308                }
2309            }
2310        }
2311
2312        // }}}
2313
}
2314
2315    // }}}
2316

2317    //----------------------------------------------------------------------
2318
// Popup menu listener
2319
//----------------------------------------------------------------------
2320
// {{{ class PopupListener
2321

2322    /**
2323     * Listener for mouseclicks that brings up a popup menu.
2324     */

2325    class PopupListener
2326        extends MouseAdapter JavaDoc
2327    {
2328        /**
2329         * Notify the listener that a mouse button has been pressed.
2330         *
2331         * @param event Details aobut the event.
2332         */

2333        public void mousePressed(MouseEvent JavaDoc event)
2334        {
2335            maybePopup(event);
2336        }
2337
2338        /**
2339         * Notify the listener that a mouse button has been released.
2340         *
2341         * @param event Details aobut the event.
2342         */

2343        public void mouseReleased(MouseEvent JavaDoc event)
2344        {
2345            maybePopup(event);
2346        }
2347
2348        /**
2349         * Check if the event is a popup trigger and bring up a popup menu if
2350         * required. If the click happens on a table row that is selected, the
2351         * selection doesn't change. If the row is not selected, it will be
2352         * made the only selected row.
2353         *
2354         * @param event The MouseEvent containing the details.
2355         */

2356        private void maybePopup(MouseEvent JavaDoc event)
2357        {
2358            if (event.isPopupTrigger()) {
2359                int row = table.rowAtPoint
2360                    (new Point JavaDoc (event.getX(), event.getY()));
2361                int column = table.columnAtPoint
2362                    (new Point JavaDoc (event.getX(), event.getY()));
2363                if (row >= 0) {
2364                    if (! table.isRowSelected(row)) {
2365                        table.setRowSelectionInterval(row, row);
2366                    }
2367                }
2368                if (column >= 0) {
2369                    table.setColumnSelectionInterval(column, column);
2370                }
2371                SwingUtil.showPopup(popup, table, event.getX(), event.getY(),
2372                                    false);
2373            }
2374        }
2375    }
2376
2377    // }}}
2378

2379    //----------------------------------------------------------------------
2380
// Selection and model listener for the table
2381
//----------------------------------------------------------------------
2382
// {{{ class CellSelectionListener
2383

2384    // {{{ static variables for CellSelectionListener
2385

2386    /**
2387     * Default dealy for selection and table actions.
2388     */

2389    private final static int DELAY = 200;
2390
2391    /**
2392     * The Logger used for logging CellSelectionListener methods.
2393     */

2394    private final static Logger cslLogger =
2395        new Logger (CellSelectionListener.class);
2396
2397    // }}}
2398

2399    /**
2400     * Listener for changes in the table's selection or model contents that
2401     * updates the detail view accordingly.
2402     */

2403    protected class CellSelectionListener
2404        extends DelayedListSelectionListener
2405        implements TableModelListener JavaDoc, Runnable JavaDoc
2406    {
2407        // {{{ variables
2408

2409        /**
2410         * Delayed action for table updates.
2411         */

2412        private DelayedAction delay;
2413
2414        /**
2415         * The last known row to be selected (in the true model).
2416         */

2417        private int lastRow = -1;
2418
2419        /**
2420         * The last known column to be selected.
2421         */

2422        private int lastColumn;
2423
2424        // }}}
2425

2426        // {{{ constructor
2427

2428        /**
2429         * Create a new CellSelectionListener.
2430         */

2431        public CellSelectionListener()
2432        {
2433            super(DELAY);
2434            delay = new DelayedAction (DELAY);
2435        }
2436
2437        // }}}
2438

2439        // {{{ forgetSavedRow
2440

2441        /**
2442         * Must be called when rows are deleted from the table model.
2443         */

2444        public void forgetSavedRow()
2445        {
2446            lastRow = -1;
2447        }
2448
2449        // }}}
2450

2451        // {{{ delayedValueChanged
2452

2453        /**
2454         * Delayed notification of a change in the table's selection.
2455         *
2456         * @param e The event containing the details.
2457         */

2458        public void delayedValueChanged(ListSelectionEvent JavaDoc e)
2459        {
2460            if (logging && cslLogger.level >= Log.MTD) {
2461                cslLogger.log(Log.MTD, "delayedValueChanged(ListSelectionEvent)",
2462                           cslLogger.level < Log.MTDDETAIL ? "" :
2463                           "e: " + e);
2464            }
2465            if (e.getValueIsAdjusting()) {
2466                return;
2467            }
2468            int row = table.getSelectionModel().getAnchorSelectionIndex();
2469            lastColumn = table.getColumnModel().getSelectionModel()
2470                .getAnchorSelectionIndex();
2471            if (row >= 0 && row < table.getRowCount()) {
2472                detailView.setEntry((LogEntry) table.getModel().getValueAt
2473                                    (row, model.COL_ENTRY));
2474                lastRow = ((FilteredAndSortedTableModel) table.getModel())
2475                    .getMappedRow(row);
2476            } else {
2477                lastRow = -1;
2478            }
2479        }
2480
2481        // }}}
2482
// {{{ tableChanged
2483

2484        /**
2485         * Notification of a change in the table model's contents.
2486         *
2487         * @param e The event containing the details.
2488         */

2489        public void tableChanged(TableModelEvent JavaDoc e)
2490        {
2491            int start = e.getFirstRow();
2492            int end = e.getLastRow();
2493            if (logging && cslLogger.level >= Log.MTD) {
2494                cslLogger.log(Log.MTD, "tableChanged(TableModelEvent)",
2495                           cslLogger.level < Log.MTDDETAIL ? "" :
2496                           "source: " +
2497                           (e.getSource() == model
2498                            ? "model" : "filteredModel") +
2499                           ", start: " + start +
2500                           ", end: " + end +
2501                           ", lastRow: " + lastRow);
2502            }
2503            if (e.getSource() == model) {
2504                // A change in the original model - adapt lastRow value
2505
if (lastRow >= 0) {
2506                    int type = e.getType();
2507                    if (start == e.HEADER_ROW
2508                        || (start == 0 && end >= model.getRowCount()
2509                            && type == e.UPDATE)) {
2510                        // complete change
2511
if (logging && cslLogger.level >= Log.DBG) {
2512                            cslLogger.log(Log.DBG,
2513                                       "tableChanged(TableModelEvent)",
2514                                       "lastRow invalidated");
2515                        }
2516                        lastRow = -1;
2517                    } else if (type == e.INSERT) {
2518                        if (start <= lastRow) {
2519                            lastRow += end - start + 1;
2520                            if (logging && cslLogger.level >= Log.DBG) {
2521                                cslLogger.log(Log.DBG,
2522                                           "tableChanged(TableModelEvent)",
2523                                           "lastRow moved up");
2524                            }
2525                        }
2526                    } else if (type == e.DELETE) {
2527                        if (start <= lastRow) {
2528                            if (end >= lastRow) {
2529                                lastRow = -1;
2530                                if (logging && cslLogger.level >= Log.DBG) {
2531                                    cslLogger.log(Log.DBG,
2532                                               "tableChanged(TableModelEvent)",
2533                                               "lastRow invalidated");
2534                                }
2535                            } else {
2536                                lastRow -= end - start + 1;
2537                                if (logging && cslLogger.level >= Log.DBG) {
2538                                    cslLogger.log(Log.DBG,
2539                                               "tableChanged(TableModelEvent)",
2540                                               "lastRow moved down");
2541                                }
2542                            }
2543                        }
2544                    }
2545                }
2546            } else {
2547                // A change in the FilteredAndSortedTableModel
2548
delay.perform(this);
2549            }
2550        }
2551
2552        // }}}
2553
// {{{ run
2554

2555        /**
2556         * Run method for the delayed table changed action.
2557         */

2558        public void run()
2559        {
2560            if (logging && cslLogger.level >= Log.DBG) {
2561                cslLogger.log(Log.DBG, "run()",
2562                           "lastRow: " + lastRow +
2563                           ", lastColumn: " + lastColumn);
2564            }
2565            if (lastRow >= 0) {
2566                restoreRow(lastRow, lastColumn);
2567            }
2568            int row = table.getSelectedRow();
2569            if (row >= 0 && row < table.getRowCount()) {
2570                detailView.setEntry
2571                    ((LogEntry) table.getModel().getValueAt
2572                     (row, model.COL_ENTRY));
2573            }
2574        }
2575
2576        // }}}
2577
}
2578
2579    // }}}
2580

2581    //----------------------------------------------------------------------
2582
// Simple focus listener for the table
2583
//----------------------------------------------------------------------
2584
// {{{ class InitialFocusListener
2585

2586    /**
2587     * Listen for focus gained and select the first row if the selection is
2588     * empty.
2589     */

2590    private class InitialFocusListener
2591        extends FocusAdapter JavaDoc
2592    {
2593        /**
2594         * Notify the listener that the table has gained focus.
2595         *
2596         * @param e Ignored.
2597         */

2598        public void focusGained(FocusEvent JavaDoc e) {
2599            if (table.getSelectionModel().getAnchorSelectionIndex() < 0
2600                && table.getRowCount() > 0) {
2601                table.setRowSelectionInterval(0, 0);
2602            }
2603        }
2604    }
2605
2606    // }}}
2607

2608    //----------------------------------------------------------------------
2609
// The CellRenderer for the table.
2610
//----------------------------------------------------------------------
2611
// {{{ class LevelRenderer
2612

2613    /**
2614     * Renderer that displays the icons for the level column and draws a black
2615     * border around the selected cell instead of the default border.
2616     */

2617    public class LevelRenderer extends de.qfs.lib.gui.WindowsTableCellRenderer
2618    {
2619        /**
2620         * Whether the renderer is used for the level column.
2621         */

2622        private boolean isLevel;
2623
2624        /**
2625         * Border for a selected focused cell.
2626         */

2627        private Border JavaDoc selectedFocusBorder;
2628
2629        /**
2630         * Border for a non-selected focused cell.
2631         */

2632        private Border JavaDoc focusBorder;
2633
2634        // {{{ constructor
2635

2636        /**
2637         * Create a new LevelRenderer.
2638         *
2639         * @param level Whether the renderer is used for the level
2640                                column.
2641         */

2642        public LevelRenderer (boolean level)
2643        {
2644            isLevel = level;
2645            initIcons();
2646        }
2647
2648        // }}}
2649

2650        // {{{ getTableCellRendererComponent
2651

2652        /**
2653         * Get the Component to render the cell.
2654         *
2655         * @param table The table being painted.
2656         * @param value The cell's value.
2657         * @param isSelected Whether the cell is selected.
2658         * @param hasFocus Whether the cell has the focus.
2659         * @param row The cell's row.
2660         * @param column The cell's column.
2661         *
2662         * @return A label with either an icon for the row's level, or the
2663         * text contents of the cell.
2664         */

2665        public Component JavaDoc getTableCellRendererComponent
2666                (JTable JavaDoc table, Object JavaDoc value, boolean isSelected,
2667                 boolean hasFocus, int row, int column)
2668        {
2669            JLabel JavaDoc label = (JLabel JavaDoc) super.getTableCellRendererComponent
2670                (table, value, isSelected, hasFocus, row, column);
2671            if (isLevel) {
2672                int level = ((Integer JavaDoc) value).intValue();
2673                Icon JavaDoc icon = null;
2674                if (level >= Log.ERR && level <= Log.DBGDETAIL) {
2675                    icon = icons[level - 1];
2676                }
2677                if (icon != null) {
2678                    label.setIcon(icon);
2679                    label.setText(null);
2680                }
2681            }
2682            // if (hasFocus) {
2683
// if (isSelected) {
2684
// if (selectedFocusBorder == null) {
2685
// selectedFocusBorder = BorderFactory.createLineBorder
2686
// (table.getSelectionForeground());
2687
// }
2688
// label.setBorder(selectedFocusBorder);
2689
// } else {
2690
// if (focusBorder == null) {
2691
// focusBorder = BorderFactory.createLineBorder
2692
// (table.getForeground());
2693
// }
2694
// label.setBorder(focusBorder);
2695
// }
2696
// }
2697
return label;
2698        }
2699
2700        // }}}
2701
}
2702
2703    // }}}
2704

2705    //----------------------------------------------------------------------
2706
// The HeaderCellRenderer for the table.
2707
//----------------------------------------------------------------------
2708
// {{{ class HeaderRenderer
2709

2710    /**
2711     * Renderer that displays column headers in red, if the correspoding
2712     * filter is enabled.
2713     */

2714    private class HeaderRenderer
2715        extends SortedTableHeaderCellRenderer
2716    {
2717        // {{{ constructor
2718

2719        /**
2720         * Create a new HeaderRenderer.
2721         */

2722        public HeaderRenderer()
2723        {
2724            super(LogTableView.this.table);
2725        }
2726
2727        // }}}
2728

2729        // {{{ getTableCellRendererComponent
2730

2731        /**
2732         * Get the Component to render the header.
2733         *
2734         * @param table The table being painted.
2735         * @param value The cell's value.
2736         * @param isSelected Whether the cell is selected.
2737         * @param hasFocus Whether the cell has the focus.
2738         * @param row The cell's row.
2739         * @param column The cell's column.
2740         *
2741         * @return A label with the text in red, if the column is
2742         * currently used as a filter.
2743         */

2744        public Component JavaDoc getTableCellRendererComponent
2745                (JTable JavaDoc table, Object JavaDoc value, boolean isSelected,
2746                 boolean hasFocus, int row, int column)
2747        {
2748            JLabel JavaDoc label = (JLabel JavaDoc) super.getTableCellRendererComponent
2749                (table, value, isSelected, hasFocus, row, column);
2750            int idx = table.convertColumnIndexToModel(column);
2751            if (filterEnabled[idx]) {
2752                label.setForeground(Color.red);
2753            } else {
2754                label.setForeground(Color.black);
2755            }
2756            return label;
2757        }
2758
2759        // }}}
2760
}
2761
2762    // }}}
2763
}
2764
Popular Tags