KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > tasklist > core > TaskListView


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.tasklist.core;
21
22
23 import java.io.ObjectInput JavaDoc;
24 import java.io.ObjectOutput JavaDoc;
25
26 import java.awt.*;
27 import java.awt.image.BufferedImage JavaDoc;
28 import java.beans.PropertyVetoException JavaDoc;
29 import java.io.IOException JavaDoc;
30 import java.util.*;
31 import java.util.List JavaDoc;
32 import java.lang.ref.WeakReference JavaDoc;
33 import java.lang.ref.Reference JavaDoc;
34 import java.util.logging.Level JavaDoc;
35 import java.util.logging.Logger JavaDoc;
36 import javax.swing.*;
37 import javax.swing.table.TableColumn JavaDoc;
38 import javax.swing.table.TableColumnModel JavaDoc;
39 import javax.swing.tree.TreeNode JavaDoc;
40
41 import org.netbeans.modules.tasklist.core.columns.ColumnsConfiguration;
42 import org.netbeans.modules.tasklist.core.filter.Filter;
43 import org.netbeans.modules.tasklist.core.filter.FilterAction;
44 import org.netbeans.modules.tasklist.core.filter.FilterRepository;
45 import org.netbeans.modules.tasklist.core.filter.FilteredTopComponent;
46 import org.netbeans.modules.tasklist.core.filter.RemoveFilterAction;
47 import org.openide.filesystems.FileObject;
48 import org.openide.filesystems.Repository;
49 import org.openide.explorer.ExplorerManager;
50 import org.openide.nodes.FilterNode;
51 import org.openide.nodes.Node;
52 import org.openide.util.HelpCtx;
53 import org.openide.util.Lookup;
54 import org.openide.util.NbBundle;
55 import org.openide.awt.StatusDisplayer;
56 import org.openide.ErrorManager;
57 import org.openide.explorer.ExplorerUtils;
58 import org.openide.explorer.view.Visualizer;
59 import org.openide.actions.FindAction;
60 import org.openide.cookies.InstanceCookie;
61 import org.openide.filesystems.FileSystem;
62 import org.openide.loaders.DataObject;
63 import org.openide.loaders.DataObjectNotFoundException;
64 import org.openide.util.actions.SystemAction;
65 import org.openide.windows.Mode;
66 import org.openide.windows.WindowManager;
67 import org.openide.windows.TopComponent;
68
69
70
71 /**
72  * View showing the task list items
73  * @author Tor Norbye, Trond Norbye
74  * @author tl
75  */

76 public abstract class TaskListView extends TopComponent
77 implements TaskListener, ExplorerManager.Provider, TaskSelector,
78 FilteredTopComponent
79 {
80
81     private static final long serialVersionUID = 1;
82
83     private static final Logger JavaDoc LOGGER = TLUtils.getLogger(TaskListView.class);
84     
85     static {
86         LOGGER.setLevel(Level.OFF);
87     }
88     
89     /** Keeps track of the category tabs we've added
90      * <category, reference to topcomponent>
91      */

92     private transient static Map views = null;
93
94     /** Expected border height */
95     private static int TOOLBAR_HEIGHT_ADJUSTMENT = 4;
96
97     /** Cached toolbar height */
98     private static int toolbarHeight = -1;
99
100     public static final String JavaDoc DEFAULT_FILTER_NAME =
101         NbBundle.getMessage(TaskListView.class, "default-filter-name");
102
103
104     /**
105      * Registers a view
106      *
107      * @param view a view to be registered
108      */

109     protected static void registerTaskListView(TaskListView view) {
110         synchronized (TaskListView.class) {
111             if (views == null) {
112                 views = new HashMap();
113             }
114             views.put(view.category, new WeakReference JavaDoc(view));
115         }
116     }
117     
118     public static TaskListView getTaskListView(String JavaDoc category) {
119         assert category != null;
120         
121         if (views == null) {
122             return null;
123         }
124
125         Reference JavaDoc ref = (Reference JavaDoc) views.get(category);
126         if (ref == null) return null;
127         return (TaskListView) ref.get();
128     }
129     
130     /** Property "task summary" */
131     public static final String JavaDoc PROP_TASK_SUMMARY = "taskDesc"; // NOI18N
132

133     /** String (category of a view) -> ColumnsConfiguration */
134     private static Map defColumns = new HashMap();
135
136     transient protected Node JavaDoc rootNode = null;
137     transient protected MyTreeTableView treeTable;
138
139     protected transient ColumnProperty[] columns = null;
140
141     transient private boolean initialized = false;
142
143     transient protected String JavaDoc category = null;
144
145     protected transient ObservableList tasklist = null;
146
147     transient protected FilterRepository filters = null;
148     transient protected Filter activeFilter = null;
149     
150     /** Annotation showing the current position */
151     transient protected TaskAnnotation taskMarker = null;
152
153     private transient Component centerCmp;
154
155     transient private JPanel centerPanel;
156     transient private Component northCmp;
157     transient private boolean northCmpCreated;
158     transient private JLabel miniStatus;
159
160     private transient ExplorerManager manager;
161
162     protected boolean persistent = false;
163     
164     /**
165      * Construct a new TaskListView. Most work is deferred to
166      * componentOpened. NOTE: this is only for use by the window
167      * system when deserializing windows. Client code should not call
168      * it; use the constructor which takes category, title and icon
169      * parameters. But the code relies on
170      * readExternal getting called after this constructor to finalize
171      * construction of the window.
172      *
173      * todo make private
174      */

175     public TaskListView() {
176         init_();
177     }
178
179     /**
180      * @param category view's category. This value will be used as the name
181      * for a subdirectory of "SystemFileSystem/TaskList/" for columns settings
182      */

183     protected TaskListView(String JavaDoc category, String JavaDoc title, Image JavaDoc icon,
184         boolean persistent, ObservableList tasklist) {
185         init_();
186
187         assert category != null : "category == null";
188
189         this.category = category;
190         setName(title);
191         this.persistent = persistent;
192         setIcon(icon);
193
194         registerTaskListView(this);
195         setModel(tasklist);
196     }
197
198     /**
199      * Common part for all constructors
200      */

201     private void init_() {
202         manager = new ExplorerManager();
203         ActionMap map = getActionMap();
204         map.put(javax.swing.text.DefaultEditorKit.copyAction,
205             ExplorerUtils.actionCopy(manager));
206         map.put(javax.swing.text.DefaultEditorKit.cutAction,
207             ExplorerUtils.actionCut(manager));
208         map.put(javax.swing.text.DefaultEditorKit.pasteAction,
209             ExplorerUtils.actionPaste(manager));
210         map.put("delete", ExplorerUtils.actionDelete(manager, true)); // NOI18N
211

212         map.put("jumpNext", new PrevNextAction (false)); // NOI18N
213
map.put("jumpPrev", new PrevNextAction (true)); // NOI18N
214

215         // following line tells the top component which lookup should be associated with it
216
associateLookup(ExplorerUtils.createLookup(manager, map));
217     }
218
219     public int getPersistenceType() {
220         if (persistent) {
221             // Only persist window info if the window is opened on exit.
222
return TopComponent.PERSISTENCE_ONLY_OPENED;
223         } else {
224             return TopComponent.PERSISTENCE_NEVER;
225         }
226
227     }
228
229     public ExplorerManager getExplorerManager() {
230         return manager;
231     }
232
233     /**
234      * Updates the label showing the number of filtered tasks
235      */

236     public void updateFilterCount() {
237         JLabel filterLabel = (JLabel) getMiniStatus();
238         if (getFilter() == null) {
239             filterLabel.setText("");
240         } else {
241             int all = TLUtils.getChildrenCountRecursively(rootNode);
242             int shown = TLUtils.getChildrenCountRecursively(
243                     getExplorerManager().getExploredContext());
244             filterLabel.setText(NbBundle.getMessage(TaskListView.class,
245                     "FilterCount", new Integer JavaDoc(shown), new Integer JavaDoc(all))); // NOI18N
246
}
247     }
248
249     /**
250      * Hides/shows component that will be shown over the TTV
251      *
252      * @param v true = visible
253      */

254     protected void setNorthComponentVisible(boolean v) {
255         if (v) {
256             Component cmp = getNorthComponent();
257             if (cmp != null) {
258                 centerPanel.add(cmp, BorderLayout.NORTH);
259                 centerPanel.validate();
260             }
261         } else {
262             if (northCmp != null && northCmp.getParent() != null) {
263                 northCmp.getParent().remove(northCmp);
264             }
265         }
266     }
267
268     /**
269      * Is the component above the tree table visible?
270      * Visible means it is in the TopComponent
271      *
272      * @return true = yes
273      */

274     public boolean isNorthComponentVisible() {
275         return northCmp != null && northCmp.getParent() != null;
276     }
277
278     /**
279      * Returns component that will be shown over the TTV
280      *
281      * @return component or null
282      */

283     protected Component getNorthComponent() {
284         if (!northCmpCreated) {
285             northCmp = createNorthComponent();
286             northCmpCreated = true;
287         }
288         return northCmp;
289     }
290
291     /**
292      * Creates component that will be shown over the TTV
293      *
294      * @return created component or null
295      */

296     protected Component createNorthComponent() {
297         return new JLabel(""); // NOI18N
298
}
299
300     /**
301      * Returns component visualizing view status messages.
302      */

303     protected JLabel getMiniStatus() {
304         if (miniStatus == null) {
305             miniStatus = createMiniStatus();
306         }
307         return miniStatus;
308     }
309
310     private JLabel createMiniStatus() {
311         JLabel ret = new JLabel();
312         Dimension dim = ret.getPreferredSize();
313         dim.height = getToolbarHeight();
314         ret.setPreferredSize(dim);
315         return ret;
316     }
317
318     protected final void setMiniStatus(String JavaDoc text) {
319         getMiniStatus().setText(text);
320     }
321
322     /**
323      * Computes vertical toolbar components height that can used for layout manager hinting.
324      * @return size based on font size and expected border.
325      */

326     public static int getToolbarHeight() {
327
328         if (toolbarHeight == -1) {
329             BufferedImage JavaDoc image = new BufferedImage JavaDoc(1,1,BufferedImage.TYPE_BYTE_GRAY);
330             Graphics2D g = image.createGraphics();
331             UIDefaults def = UIManager.getLookAndFeelDefaults();
332
333             int height = 0;
334             String JavaDoc[] fonts = {"Label.font", "Button.font", "ToggleButton.font"}; // NOI18N
335
for (int i=0; i<fonts.length; i++) {
336                 Font f = def.getFont(fonts[i]);
337                 FontMetrics fm = g.getFontMetrics(f);
338                 height = Math.max(height, fm.getHeight());
339             }
340             toolbarHeight = height + TOOLBAR_HEIGHT_ADJUSTMENT;
341         }
342
343         return toolbarHeight;
344     }
345
346
347     // XXX probably new instance per view would be better
348
// or explicit hideTaskInEditor should not hide foreign annotations
349
// but showTaskInEditor's call to hideTaskInEditor should hide them
350
private TaskEditorListener annotationManager = TaskEditorListener.getDefault();
351
352     /** Show the given task. "Showing" means getting the editor to
353      * show the associated file position, and open up an area in the
354      * tasklist view where the details of the task can be fully read.
355      *
356      * @param item selected task (or null for hiding last)
357      * @param annotation annotation to use or null for
358      * default view provided annotation.
359      */

360     public void showTaskInEditor(Task item, TaskAnnotation annotation) {
361         if (annotation == null) annotation = getAnnotation(item);
362         annotationManager.showTask(item, annotation);
363     }
364
365     /**
366      * Called to indicate that a particular task should be hidden.
367      * This typically means that the task was deleted so it should
368      * no longer have any visual cues. The task referred to is the
369      * most recent task passed to showTaskInEditor.
370      */

371     public void hideTaskInEditor() {
372         annotationManager.hideTask();
373     }
374
375     /**
376      * Could be overridden to change actions for the toolbar.
377      * @return actions for the toolbar or null
378      */

379     protected SystemAction[] getToolBarActions() {
380         return null;
381     }
382
383     /**
384      * Could be overriden to change actions on second toolbar row.
385      * @return
386      */

387     protected SystemAction[] getGlobalToolBarActions() {
388         return null;
389     }
390
391     /**
392      * Returns the <code>TableColumnModel</code> that contains all column information
393      * of the table header.
394      *
395      * @return model for columns
396      */

397     public TableColumnModel JavaDoc getColumnModel() {
398         return treeTable.getHeaderModel();
399     }
400
401     /**
402      * Returns columns definition for the TreeTable view.
403      *
404      * @return columns
405      */

406     public final ColumnProperty[] getColumns() {
407         if (columns == null)
408             columns = createColumns();
409         return columns;
410     }
411
412     /**
413      * Returns default configuration for visible columns
414      *
415      * @return default columns configuration
416      */

417     protected ColumnsConfiguration getDefaultColumns() {
418         ColumnsConfiguration cc = (ColumnsConfiguration) defColumns.get(category);
419         if (cc != null)
420             return cc;
421
422         FileSystem fs = Repository.getDefault().getDefaultFileSystem();
423         FileObject fo = fs.findResource("TaskList/" + category + "/columns.settings"); // NOI18N
424
assert fo != null : "Missing config TaskList/" + category + "/columns.settings"; // NOI18N
425

426         try {
427             DataObject dobj = DataObject.find(fo);
428             InstanceCookie ic = (InstanceCookie) dobj.getCookie(InstanceCookie.class);
429             cc = (ColumnsConfiguration) ic.instanceCreate();
430         } catch (ClassNotFoundException JavaDoc e) {
431             ErrorManager.getDefault().notify(e);
432         } catch (DataObjectNotFoundException e) {
433             ErrorManager.getDefault().notify(e);
434         } catch (IOException JavaDoc e) {
435             ErrorManager.getDefault().notify(e);
436         }
437         return cc;
438     }
439
440
441
442     protected void loadFilters() {
443         FileSystem fs = Repository.getDefault().getDefaultFileSystem();
444         FileObject fo = fs.findResource("TaskList/" + category + "/filters.settings"); // NOI18N
445
assert fo != null : "Missing config TaskList/" + category + "/filters.settings"; // NOI18N
446

447         try {
448             DataObject dobj = DataObject.find(fo);
449             InstanceCookie ic = (InstanceCookie) dobj.getCookie(InstanceCookie.class);
450             filters = (FilterRepository) ic.instanceCreate();
451             
452             // filters.addPropertyChangeListener(new PropertyChangeListener() {
453
// public void propertyChange(PropertyChangeEvent evt) {
454
// if (evt.getPropertyName().equals(FilterRepository.PROP_ACTIVE_FILTER)) {
455
// setFilter(filters.getActive());
456
// // setFiltered();
457
// }
458
// }
459
// });
460
filters.setActive(null);
461             
462             // create a default filter if there is none
463
if (filters.size() == 0) {
464                 Filter f = createFilter();
465                 f.setName(DEFAULT_FILTER_NAME);
466                 filters.add(f);
467             }
468             
469         } catch (ClassNotFoundException JavaDoc e) {
470             ErrorManager.getDefault().notify(e);
471         } catch (DataObjectNotFoundException e) {
472             ErrorManager.getDefault().notify(e);
473         } catch (IOException JavaDoc e) {
474             ErrorManager.getDefault().notify(e);
475         }
476         
477     }
478     
479     /**
480      * Called when the object is opened. Add the GUI.
481      * @todo Trigger source listening on window getting VISIBLE instead
482      * of getting opened.
483      */

484     protected void componentOpened() {
485         LOGGER.fine("");
486         // Register listeningViews, such as the editor support bridge module
487
// TODO: Listeners from Lookup will not be collected
488
// registerListeners();
489

490         if (initialized) {
491             return;
492         }
493         initialized = true;
494
495         FindAction find = (FindAction) FindAction.get(FindAction.class);
496         FilterAction filter = (FilterAction) FilterAction.get(FilterAction.class);
497         getActionMap().put(find.getActionMapKey(), filter);
498
499         setLayout(new BorderLayout());
500
501         centerPanel = new JPanel();
502         centerPanel.setLayout(new BorderLayout());
503         centerCmp = createCenterComponent();
504         
505         centerPanel.add(centerCmp, BorderLayout.CENTER);
506         add(centerPanel, BorderLayout.CENTER);
507
508         loadColumnsConfiguration();
509         
510         Component north = getNorthComponent();
511         if (north != null)
512             add(north, BorderLayout.NORTH);
513         
514         JPanel toolbars = new JPanel();
515         toolbars.setLayout(new FlowLayout(FlowLayout.LEADING, 0, 0));
516
517         SystemAction[] actions = getGlobalToolBarActions();
518         if (actions != null) {
519             JToolBar toolbar = SystemAction.createToolbarPresenter(actions);
520             toolbar.setFloatable(false);
521             toolbar.putClientProperty("JToolBar.isRollover", Boolean.TRUE); // NOI18N
522
toolbar.setOrientation(JToolBar.VERTICAL);
523             toolbars.add(toolbar);
524         }
525
526         actions = getToolBarActions();
527         if (actions != null) {
528             JToolBar toolbar = SystemAction.createToolbarPresenter(actions);
529             toolbar.setFloatable(false);
530             toolbar.putClientProperty("JToolBar.isRollover", Boolean.TRUE); // NOI18N
531
toolbar.setOrientation(JToolBar.VERTICAL);
532             toolbars.add(toolbar);
533         }
534
535
536         add(toolbars, BorderLayout.WEST);
537
538         // Populate the view
539
setModel(tasklist);
540     }
541
542     /**
543      * Creates the component that will be placed in the middle of the TC
544      *
545      * @return created component != null
546      */

547     protected Component createCenterComponent() {
548         treeTable = new MyTreeTableView();
549         if (columns == null) {
550             columns = createColumns();
551         }
552         treeTable.setProperties(columns);
553
554         // Column widths
555
// How the heck do I set the width of the leftmost column???
556
// treeTable.setTableColumnPreferredWidth(0, 800); // Description
557
// AHHHH... there's a separate method for that:
558
treeTable.setTreePreferredWidth(columns[0].getWidth());
559         TableColumnModel JavaDoc tcm = treeTable.getHeaderModel();
560         LOGGER.fine("number of columns " + tcm.getColumnCount());
561         for (int i = 0; i < tcm.getColumnCount(); i++) {
562             TableColumn JavaDoc tc = tcm.getColumn(i);
563             ColumnProperty cp = null;
564             for (int j = 0; j < columns.length; j++) {
565                 if (columns[j].getDisplayName().equals(tc.getHeaderValue())) {
566                     cp = columns[j];
567                     break;
568                 }
569             }
570             if (cp != null)
571                 tc.setPreferredWidth(cp.width);
572         }
573         
574         treeTable.getTable().getTableHeader().setReorderingAllowed(false);
575         treeTable.setRootVisible(false);
576         treeTable.setVerticalScrollBarPolicy(
577                 JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
578         treeTable.setHorizontalScrollBarPolicy(
579                 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
580
581         return (Component) treeTable;
582     }
583
584     /** Called when the window is closed. Cleans up. */
585     protected void componentClosed() {
586         hideTaskInEditor();
587         hideList();
588
589         // Remove any task markers we've added to the editor
590
if (unshowItem != null) {
591             removedTask(null, unshowItem, 0); // TODO cannot find the parent of unshowItem
592
}
593
594         // Unregister listeningViews
595
unregisterListeners();
596         
597         storeColumnsConfiguration();
598     }
599
600     protected void componentActivated() {
601         super.componentActivated();
602         assert initialized :
603             "#37438 dangling componentActivated event, no componentOpened()" + // NOI18N
604
" called at " + this;
605         ExplorerUtils.activateActions(manager, true);
606         RemoveFilterAction removeFilter =
607                 (RemoveFilterAction) SystemAction.get(RemoveFilterAction.class);
608         removeFilter.enable();
609     }
610
611     protected void componentDeactivated() {
612         super.componentDeactivated();
613         assert initialized : "#37438 dangling componentDeactivated event, no componentOpened() called at " + this;
614         ExplorerUtils.activateActions(manager, false);
615         storeColumnsConfiguration();
616     }
617
618     /**
619      * Store current column configuration to settings
620      */

621     protected void storeColumnsConfiguration() {
622         ColumnsConfiguration columns = getDefaultColumns();
623         ColumnsConfiguration.loadColumnsFrom(this, columns);
624     }
625
626     /**
627      * Restore column configuration from settings
628      */

629     protected void loadColumnsConfiguration() {
630         ColumnsConfiguration cc = getDefaultColumns();
631         ColumnsConfiguration.configureColumns(this, cc);
632     }
633
634     /** Create the root node to be used in this view */
635     abstract protected Node JavaDoc createRootNode();
636
637     /**
638      * Start showing new tasklist.
639      *
640      * @param list new tree
641      */

642     protected void setModel(ObservableList list) {
643         hideList();
644         tasklist = list;
645         if (list != null) {
646             getModel().addTaskListener(this);
647             setRoot();
648         }
649     }
650
651     private void setRoot() {
652         rootNode = createRootNode();
653     // TODO: usertasks module sets the display name of the root node to
654
// "Task List"
655
rootNode.setDisplayName(getMainColumn(-1).getDisplayName());
656
657         LOGGER.fine("root created " + rootNode);
658
659         Node JavaDoc prevRoot = getExplorerManager().getRootContext();
660
661         if (isFiltered()) {
662             // Create filtered view of the tasklist
663
FilteredTaskChildren children =
664                 new FilteredTaskChildren(this, rootNode, getFilter());
665             FilterNode n = new FilterTaskNode(rootNode, children, false);
666             getExplorerManager().setRootContext(n);
667         } else {
668             getExplorerManager().setRootContext(rootNode);
669         }
670
671         try {
672             if (prevRoot != null) prevRoot.destroy();
673         } catch (IOException JavaDoc ex) {
674             throw new IllegalStateException JavaDoc("Unexpected IOex in " + prevRoot); // NOI18N
675
}
676     }
677
678     protected void hideList() {
679         ObservableList prev = getModel();
680         if (prev != null) {
681             prev.removeTaskListener(this);
682         }
683     }
684
685     /**
686      * Returns the root node. It is never a filtered node.
687      *
688      * @return root node
689      */

690     public Node JavaDoc getRootNode() {
691         return rootNode;
692     }
693
694     /** Ensures that even if no node is selected in the view,
695      * some help specific to this view will still be available.
696      */

697     public HelpCtx getHelpCtx() {
698         return getHelpCtx(
699                 getExplorerManager().getSelectedNodes(),
700                 getExplorerManager().getRootContext().getHelpCtx()
701         );
702     }
703
704     // XXX #37543 copied from ExplorerPanel ast was deprecated without replacement
705
private static HelpCtx getHelpCtx(Node JavaDoc[] sel, HelpCtx def) {
706         HelpCtx result = null;
707         for (int i = 0; i < sel.length; i++) {
708             HelpCtx attempt = sel[i].getHelpCtx();
709             if (attempt != null && !attempt.equals(HelpCtx.DEFAULT_HELP)) {
710                 if (result == null || result.equals(attempt)) {
711                     result = attempt;
712                 } else {
713                     // More than one found, and they conflict. Get general help on the Explorer instead.
714
result = null;
715                     break;
716                 }
717             }
718         }
719         if (result != null)
720             return result;
721         else
722             return def;
723     }
724
725
726     /**
727      * Returns table with tasks.
728      *
729      * @return table
730      */

731     public JTable getTable() {
732         return treeTable.getTable();
733     }
734
735     /**
736      * Read in a serialized version of the tasklist
737      * and reads in sorting preferences etc. such that
738      * we use the same preferences now.
739      * @param objectInput object stream to read from
740      * @todo Use a more robust serialization format (not int uid based)
741      * @throws IOException
742      * @throws ClassNotFoundException
743      */

744     public void readExternal(ObjectInput JavaDoc objectInput) throws IOException JavaDoc, java.lang.ClassNotFoundException JavaDoc {
745         // Don't call super!
746
// See writeExternal for justification
747
//super.readExternal(objectInput);
748

749         int ver = objectInput.read();
750         //assert ver <= 4 : "serialization version incorrect; should be 1 to 4";
751

752         // Read in the UID of the currently selected task, or null if none
753
// TODO: Not yet implemented
754
String JavaDoc selUID = (String JavaDoc) objectInput.readObject();
755
756         if (ver == 4)
757             return;
758
759         int sortingColumn = objectInput.read();
760         int sortAscendingInt = objectInput.read();
761         int numVisible = objectInput.read();
762
763         // Account for conversion to unsigned byte in writeExternal
764
if (sortingColumn == 255) {
765             sortingColumn = -1;
766         }
767
768         /*
769         System.out.println("readExternal: " + getClass().getName()); // NOI18N
770         System.out.println("numVisible is " + numVisible); // NOI18N
771         System.out.println("sortingColumn is " + sortingColumn); // NOI18N
772         System.out.println("ascending is " + ascending); // NOI18N
773         */

774
775         if (numVisible > 0) {
776             if (columns == null) {
777                 columns = createColumns();
778             }
779             int numColumns = columns.length;
780             boolean[] columnVisible = new boolean[numColumns];
781             for (int i = 0; i < numColumns; i++) {
782                 columnVisible[i] = false;
783             }
784             for (int i = 0; i < numVisible; i++) {
785                 int uid = objectInput.read();
786 // int index;
787
if ((uid < numColumns) && (columns[uid].uid == uid)) {
788                     // UID == column index. This is the scenario for now
789
// until we delete columns in the middle etc.
790
// index = uid;
791
} else {
792                     // Have to search for the uid
793
// index = -1;
794
for (int j = 0; j < numColumns; j++) {
795                         if (columns[j].uid == uid) {
796 // index = j;
797
break;
798                         }
799                     }
800                 }
801
802                 /* don't do anything. we just read the bytes
803     if (index != -1) {
804                     columnVisible[index] = true;
805
806                     // Set sorting attribute
807                     if (sortingColumn == uid) {
808                     columns[index].setValue("SortingColumnTTV", // NOI18N
809                                 Boolean.TRUE);
810                     // Descending sort?
811                     if (!ascending) {
812                         columns[index].setValue("DescendingOrderTTV", // NOI18N
813                                     Boolean.TRUE);
814                     }
815                     }
816                 }
817     */

818             }
819
820             //System.out.print("Column visibility: {");
821
/* we don't do anything. we just read the bytes
822 for (int i = 0; i < columns.length; i++) {
823 //System.out.print(" " + columnVisible[i]);
824
825             // Is this column visible?
826             if (columnVisible[i]) {
827                 columns[i].setValue("InvisibleInTreeTableView", // NOI18N
828                         // NOTE reverse logic: this is INvisible
829                         Boolean.FALSE);
830             } else {
831                 // Necessary because by default some columns
832                 // set invisible by default, so I have to
833                 // override these
834                 columns[i].setValue("InvisibleInTreeTableView", // NOI18N
835                         // NOTE reverse logic: this is INvisible
836                         Boolean.TRUE);
837             }
838             }
839 */

840 //System.out.println(" }");
841
}
842
843         if (ver >= 2) {
844             category = (String JavaDoc) objectInput.readObject();
845             objectInput.readObject(); // ignoring title
846
int persistentInt = objectInput.read();
847             persistent = (persistentInt != 0);
848         } else {
849             category = TaskList.USER_CATEGORY; // for compatibility only
850
}
851
852         synchronized (TaskListView.class) {
853             if (views == null) {
854                 views = new HashMap();
855             }
856             views.put(category, new WeakReference JavaDoc(this));
857         }
858     }
859
860     /**
861      * Write out relevant settings in the window (visible
862      * columns, sorting order, etc.) such that they can
863      * be reconstructed the next time the IDE is started.
864      * @todo Use a more robust serialization format (not int uid based)
865      * @param objectOutput Object stream to write to
866      * @throws IOException
867      */

868     public void writeExternal(ObjectOutput JavaDoc objectOutput) throws IOException JavaDoc {
869         if (!persistent) {
870             ErrorManager.getDefault().log(
871                     ErrorManager.INFORMATIONAL,
872                     "Warning: This tasklist window (" + getName() + ") should not have been persisted!");
873             return;
874         }
875
876         // Don't call super.writeExternal.
877
// Our parents are ExplorerPanel and TopComponent.
878
// ExplorerPanel writes out the ExplorerManager, which tries
879
// to write out the node selection, explored content, etc.
880
// We don't want that. Specific tasklist children, such as
881
// the usertasklist, may want to persist the selection but
882
// most (such as the suggestions view, source scan etc,
883
// don't want this.)
884
// TopComponent persists the name and tooltip text; we
885
// don't care about that either.
886
//super.writeExternal(objectOutput);
887

888         // Version 1 format:
889
// String: selected uid
890
// byte: sortingColumn (255: no sort, otherwise, sorting id)
891
// byte: sort ascending (0:false or 1:true)
892
// byte: number of visible columns (N)
893
// N bytes: visible column uids
894

895         // Version 4 format:
896
// String: selected UID
897
// String: category
898
// String: title
899
// byte: persistent
900

901         // TODO Additional:
902
// String Object: selected task? (should this be a multi-selection?)
903

904         // Write out the UID of the currently selected task, or null if none
905
objectOutput.write(4); // SERIAL VERSION
906

907         // Write out the UID of the currently selected task, or null if none
908
objectOutput.writeObject(null); // Not yet implemented
909

910         // Write out the window's properties:
911
// TODO: do we really need these properties?
912
// objectOutput.writeObject(category);
913
// objectOutput.writeObject(title);
914
// objectOutput.write(persistent ? 1 : 0);
915
}
916
917     /** Create the list of columns to be used in the view.
918      NOTE: The first column SHOULD be a tree column.
919      */

920     abstract protected ColumnProperty[] createColumns();
921
922     protected ColumnProperty getMainColumn(int width) {
923         // Tree column
924
// NOTE: Task.getDisplayName() must also be kept in sync here
925
return new ColumnProperty(
926                 0, // UID -- never change (part of serialization
927
PROP_TASK_SUMMARY,
928                 NbBundle.getMessage(TaskListView.class, "Description"), // NOI18N
929
NbBundle.getMessage(TaskListView.class, "Description"), // NOI18N
930
true,
931                 width
932         );
933     }
934
935     private Task unshowItem = null;
936
937     /** Show the given todolist item. "Showing" means getting the
938      * editor to show the associated file position, and open up an
939      * area in the todolist view where the details of the todolist
940      * item can be fully read.
941      */

942     /*
943     public void show(Task item, Annotation anno) {
944     if (listeningViews != null) {
945         // Stash item so I can notify of deletion -- see TaskViewListener
946         // doc
947         unshowItem = item;
948         int n = listeningViews.size();
949         for (int i = 0; i < n; i++) {
950         TaskViewListener tl = (TaskViewListener)listeningViews.get(i);
951         tl.showTaskInEditor(item, anno);
952         }
953     }
954     }
955     */

956
957     /** Unshow the given task, if it's the one currently showing */
958     /*
959     public void unshow(Task item) {
960         if (item != unshowItem) {
961             return;
962         }
963     if (listeningViews != null) {
964         // Stash item so I can notify of deletion -- see TaskViewListener
965         // doc
966         unshowItem = null;
967         int n = listeningViews.size();
968         for (int i = 0; i < n; i++) {
969         TaskViewListener tl = (TaskViewListener)listeningViews.get(i);
970         tl.hideTaskInEditor();
971         }
972     }
973     }
974     */

975
976     /** Expand nodes and select the particular todo item, IF the todolist
977      * view is showing
978      * @param item The item to be shown
979      */

980     public void select(final Task item) {
981
982         if (isShowing() == false) return;
983
984         // Expand nodes to show the given item
985

986         // XXX HACK HACK HACK
987
// This fails for the case where you add a new subitem to a parent
988
// node where it's the first time the parent is getting a subtask
989
// (e.g. the node was a leaf). In that case I'm doing a trick
990
// (see TodoItem's addSubtask method) which causes the node to be
991
// replaced, and the end result is that when this code runs I'm
992
// operating on the old node because the node recreation from keys
993
// hasn't happened yet. So the hack solution: defer expanding the
994
// nodes a brief interval. Not sure how long to wait - since I don't
995
// think the node refreshing is happening on the AWT thread but on
996
// a separate thread. Perhaps there's a method I can call to force
997
// a refresh before this happens? Not sure, so for now I just hack
998
// it by a delayed call
999
SwingUtilities.invokeLater(new Runnable JavaDoc() {
1000            public void run() {
1001                Node JavaDoc n = TaskNode.find(getExplorerManager().getRootContext(),
1002                        item);
1003
1004                Node JavaDoc[] sel;
1005                if (n == null) {
1006                    sel = new Node JavaDoc[0];
1007                } else {
1008                    sel = new Node JavaDoc[]{n};
1009                }
1010                try {
1011                    getExplorerManager().setSelectedNodes(sel);
1012                } catch (PropertyVetoException JavaDoc e) {
1013                    ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
1014                }
1015            }
1016        }); // End hack
1017
}
1018
1019    public void expandAll() {
1020        if (treeTable != null) {
1021            treeTable.expandAll();
1022        }
1023    }
1024
1025    /**
1026     * Return the "current" component. Ideally, my context menu
1027     * actions can figure out which component they're invoked from.
1028     * But I can't do that (the way I did this in the ifdef module
1029     * was inserting some code in my own popup performer, but in this
1030     * case I don't have access to the popup performer - it's created
1031     * and managed privately by the TreeTableView.
1032     * <p>
1033     * So instead try some likely choices, like the currently active
1034     * component and history of recently visible components
1035     * (may be necessary for main toolbar actions).
1036     *
1037     * @return most appropriate view or <code>null</code>
1038     */

1039    public static TaskListView getCurrent() {
1040
1041        TopComponent activated = WindowManager.getDefault().getRegistry().getActivated();
1042        if (activated instanceof TaskListView) {
1043            return (TaskListView) activated;
1044        }
1045
1046        // Try to figure out which view is current. If none is found to
1047
// be visible, guess one.
1048
if (views == null) {
1049            return null;
1050        }
1051        Collection vs = views.values();
1052        Iterator it = vs.iterator();
1053        TaskListView first = null;
1054        while (it.hasNext()) {
1055            Reference JavaDoc ref = (Reference JavaDoc) it.next();
1056            TaskListView tlv = (TaskListView) ref.get();
1057            if (tlv == null) continue; // remove the key?
1058
if (tlv.isShowing()) {
1059                return tlv;
1060            }
1061            
1062            // TODO: it seems to be a bad idea to return the first non-null
1063
// component here
1064
if (first == null) {
1065                first = tlv;
1066            }
1067        }
1068        return first;
1069    }
1070
1071    /** List of TaskViewListener object listening on the tasklist view
1072     for visibility, selection, etc. */

1073    private List JavaDoc listeningViews = null;
1074
1075    protected final List JavaDoc getListeningViews() {
1076        return (listeningViews == null) ? Collections.EMPTY_LIST : listeningViews;
1077    }
1078
1079    /**
1080     * Locate all tasklist listeningViews and add them to our property
1081     * change listener setup.
1082     * @todo Decide whether to unregister and reregister repeatedly;
1083     * this has the advantage of working with dynamic module
1084     * installs/uninstalls.
1085     * @todo Consider using a lookup listener such that I'm notified of
1086     * later additions/removals
1087     * @todo Consider doing a factory lookup instead of creating a new
1088     * instance each time. Would allow better coordination between
1089     * the listener instance and ScanView in the editor package.
1090     */

1091    void registerListeners() {
1092        // TODO Ensure that this doesn't get called from other windows...
1093
// find TaskViewListeners
1094
Lookup l = Lookup.getDefault();
1095        Lookup.Template template = new Lookup.Template(TaskViewListener.class);
1096        Iterator it = l.lookup(template).allInstances().iterator();
1097        if (it.hasNext()) {
1098            listeningViews = new ArrayList(4);
1099        }
1100        while (it.hasNext()) {
1101            TaskViewListener tl = (TaskViewListener) it.next();
1102            listeningViews.add(tl);
1103        }
1104    }
1105
1106    /** Unregister the listeningViews registered in registerListener such
1107     that they are no longer notified of changes */

1108    private void unregisterListeners() {
1109        listeningViews = null;
1110    }
1111
1112    // TODO Pick a better name!
1113
// TODO make method package private! Can't yet - used in editor/
1114
public void showInMode() {
1115        if (!isOpened()) {
1116            Mode mode = WindowManager.getDefault().findMode("output"); // NOI18N
1117
if (mode != null) {
1118                mode.dockInto(TaskListView.this);
1119            }
1120        }
1121        open();
1122        requestVisible();
1123        requestActive();
1124    }
1125
1126    /**
1127     * Called to indicate that a particular task is made current.
1128     * Do what you can to "select" this task.
1129     *
1130     * <p>
1131     * Dispatches {@link TaskViewListener#showTask} to all registered views.
1132     */

1133    public void selectedTask(Task item) {
1134        if (listeningViews != null) {
1135            // Stash item so I can notify of deletion -- see TaskViewListener
1136
// doc
1137
unshowItem = item;
1138            int n = listeningViews.size();
1139            for (int i = 0; i < n; i++) {
1140                TaskViewListener tl = (TaskViewListener) listeningViews.get(i);
1141                tl.showTask(item, getAnnotation(item));
1142            }
1143        }
1144    }
1145
1146    /**
1147     * Called to indicate that a particular task has been "warped to".
1148     * Do what you can to "warp to" this task. Typically means show
1149     * associated fileposition in the editor.
1150     */

1151    public void warpedTask(Task item) {
1152        // XXX currently identical to selectedTask above!
1153
if (listeningViews != null) {
1154            // Stash item so I can notify of deletion -- see TaskViewListener
1155
// doc
1156
unshowItem = item;
1157            int n = listeningViews.size();
1158            for (int i = 0; i < n; i++) {
1159                TaskViewListener tl = (TaskViewListener) listeningViews.get(i);
1160                tl.showTask(item, null);
1161            }
1162        }
1163    }
1164
1165    public void addedTask(Task t) {
1166        // Nothing to do?
1167
}
1168
1169    public void removedTask(Task pt, Task task, int index) {
1170        if ((task == unshowItem) && (listeningViews != null)) {
1171            unshowItem = null;
1172            int n = listeningViews.size();
1173            for (int i = 0; i < n; i++) {
1174                TaskViewListener tl = (TaskViewListener) listeningViews.get(i);
1175                tl.hideTask();
1176            }
1177        }
1178    }
1179
1180    public void structureChanged(Task t) {
1181    }
1182
1183    /**
1184     * Return the tasklist shown in this view
1185     */

1186    public TaskList getList() {
1187        // XXX FilteredTasksList may appear here for TODOs Current File
1188
ObservableList model = getModel();
1189        assert model instanceof TaskList : "CCE " + model;
1190        return (TaskList) model;
1191    }
1192
1193    public ObservableList getModel() {
1194        return tasklist;
1195    }
1196
1197    /** Pulled straight out of TreeTableView in openide
1198     Except don't cache rowComparator, and don't case
1199     to VisualizerNode, cast to Node
1200     */

1201    private synchronized Comparator getRowComparator(
1202            final Node.Property JavaDoc sortedByProperty,
1203            final boolean sortAscending,
1204            final boolean sortedByName,
1205            final boolean noSorting) {
1206        Comparator rowComparator = new Comparator() {
1207            public int compare(Object JavaDoc o1, Object JavaDoc o2) {
1208                if (o1 == o2) {
1209                    return -1;
1210                }
1211
1212                Node JavaDoc n1 = (Node JavaDoc) o1;
1213                Node JavaDoc n2 = (Node JavaDoc) o2;
1214
1215                if (sortedByProperty == null && !sortedByName) {
1216                    Node JavaDoc[] nodes = n1.getParentNode().getChildren().getNodes();
1217                    for (int i = 0; i < nodes.length; i++) {
1218                        if (nodes[i].equals(n1))
1219                            return -1;
1220                        else if (nodes[i].equals(n2))
1221                            return 1;
1222                    }
1223                    return 0;
1224                }
1225
1226                int res;
1227                if (sortedByName) {
1228                    res = n1.getDisplayName().compareTo(n2.getDisplayName());
1229                    return sortAscending ? res : -res;
1230                }
1231
1232                Node.Property JavaDoc p1 = getNodeProperty(n1, sortedByProperty);
1233                Node.Property JavaDoc p2 = getNodeProperty(n2, sortedByProperty);
1234
1235                if (p1 == null && p2 == null)
1236                    return 0;
1237
1238                try {
1239                    if (p1 == null)
1240                        res = -1;
1241                    else if (p2 == null)
1242                        res = 1;
1243                    else {
1244                        Object JavaDoc v1 = p1.getValue();
1245                        Object JavaDoc v2 = p2.getValue();
1246                        if (v1 == null && v2 == null)
1247                            return 0;
1248                        else if (v1 == null)
1249                            res = -1;
1250                        else if (v2 == null)
1251                            res = 1;
1252                        else {
1253                            if (v1.getClass() != v2.getClass() || !(v1 instanceof Comparable JavaDoc)) {
1254                                v1 = v1.toString();
1255                                v2 = v2.toString();
1256                            }
1257                            res = ((Comparable JavaDoc) v1).compareTo(v2);
1258                        }
1259                    }
1260                    return sortAscending ? res : -res;
1261                } catch (Exception JavaDoc ex) {
1262                    // PENDING
1263
return 0;
1264                }
1265            }
1266        };
1267        return rowComparator;
1268    }
1269
1270    private Node.Property JavaDoc getNodeProperty(Node JavaDoc node, Node.Property JavaDoc prop) {
1271        Node.PropertySet JavaDoc[] propsets = node.getPropertySets();
1272        for (int i = 0, n = propsets.length; i < n; i++) {
1273            Node.Property JavaDoc[] props = propsets[i].getProperties();
1274
1275            for (int j = 0, m = props.length; j < m; j++) {
1276                if (props[j].equals(prop)) {
1277                    return props[j];
1278                }
1279            }
1280        }
1281        return null;
1282    }
1283
1284
1285    // End of stuff taken from TreeTableView
1286

1287    /**
1288     * Get the toggle filter for this view. It's
1289     * applied if {@link #isFiltered} returns true.
1290     *
1291     * @return The toggle filter or <code>null</code> if not defined.
1292     */

1293    public final Filter getFilter() {
1294      // return getFilters().getActive();
1295
return activeFilter;
1296    }
1297
1298    /**
1299     * Returns the collection of filters assiciated with this view.
1300     * @return FilterRepository, never null
1301     */

1302    public FilterRepository getFilters() {
1303        if (filters == null) loadFilters();
1304        assert filters != null : "Missing FilterRepository"; // NOI18N
1305

1306        return filters;
1307    }
1308    
1309    /** Create filter template. */
1310    public abstract Filter createFilter();
1311
1312    /** Tests if any real filter is applied. */
1313    public final boolean isFiltered() {
1314        return getFilter() != null;
1315    }
1316
1317    protected void setFiltered() {
1318        if (getFilter() != null) {
1319            ((RemoveFilterAction) SystemAction.get(RemoveFilterAction.class)).enable();
1320        }
1321
1322        // update view accordingly
1323

1324        try {
1325            getExplorerManager().setSelectedNodes(new Node JavaDoc[0]);
1326        } catch (PropertyVetoException JavaDoc e) {
1327            ErrorManager.getDefault().notify(e);
1328        }
1329
1330        setRoot();
1331        updateFilterCount();
1332    }
1333
1334    /**
1335     * Set the filter to be used (determined by isFiltered) in this view.
1336     * @param filter The filter to be set, or null, to remove filtering.
1337     */

1338    public void setFilter(Filter filter) {
1339        if (filter == null || getFilters().contains(filter)) {
1340            getFilters().setActive(filter);
1341        }
1342
1343        this.activeFilter = filter;
1344        setFiltered();
1345    }
1346
1347    /**
1348     * @param node it's children will be sorted
1349     */

1350    public List JavaDoc getSortedChildren(Node JavaDoc node,
1351                                  Node.Property JavaDoc sortedByProperty,
1352                                  boolean sortAscending,
1353                                  boolean sortedByName,
1354                                  boolean noSorting) {
1355        // Only sort by the top/first level nodes
1356
Node JavaDoc[] firstNodes = node.getChildren().getNodes();
1357        ArrayList nodes = new ArrayList(firstNodes.length);
1358        for (int i = 0; i < firstNodes.length; i++) {
1359            nodes.add(firstNodes[i]);
1360        }
1361
1362        Comparator comparator = getRowComparator(sortedByProperty,
1363                sortAscending,
1364                sortedByName,
1365                noSorting);
1366        Collections.sort(nodes, comparator);
1367        return nodes;
1368    }
1369
1370    private boolean lookupAttempted = false;
1371
1372    private static void invokeLater(Runnable JavaDoc runnable) {
1373        if (SwingUtilities.isEventDispatchThread()) {
1374            runnable.run();
1375        } else {
1376            SwingUtilities.invokeLater(runnable);
1377        }
1378    }
1379
1380    /** When true, we've already warned about the need to wrap */
1381    private boolean wrapWarned = false;
1382
1383    /** @return next visitable node or null */
1384    private TreeNode JavaDoc findNextVisitable(TreeNode JavaDoc last, boolean wrapAround) {
1385
1386        // find next candidate
1387

1388        TreeNode JavaDoc next = null;
1389        if (last.getChildCount() > 0) {
1390            next = last.getChildAt(0);
1391        } else {
1392            TreeNode JavaDoc parent = last.getParent();
1393            if (parent == null) {
1394                if (parent.getChildCount() > 0) {
1395                    next = parent.getChildAt(0);
1396                }
1397            } else {
1398                int index = parent.getIndex(last);
1399                assert index != -1;
1400                index++;
1401                if (index < last.getParent().getChildCount()) {
1402                    next = last.getParent().getChildAt(index);
1403                }
1404            }
1405        }
1406
1407        // try wrap around if warned
1408

1409        if (next == null && wrapAround) {
1410            TreeNode JavaDoc parent = last;
1411            while (parent.getParent() != null) {
1412               parent = parent.getParent();
1413            }
1414            if (parent.getChildCount() > 0) {
1415                next = parent.getChildAt(0);
1416            }
1417        }
1418
1419        if (next == null) return null;
1420
1421        // assure it's visitable
1422

1423        if (isVisitable(next)) {
1424            return next;
1425        } else {
1426            return findNextVisitable(next, wrapAround);
1427        }
1428
1429    }
1430
1431    /** Test that assogiated task is visitable. */
1432    private static boolean isVisitable(TreeNode JavaDoc node) {
1433        Node JavaDoc nextNode = Visualizer.findNode(node);
1434        if (nextNode == null) return false;
1435        Task task = (Task) nextNode.getCookie(Task.class);
1436        return task != null && task.isVisitable();
1437    }
1438
1439    /** Try to select nodes in all known views. */
1440    private void selectNode(Node JavaDoc node) {
1441        if (node != null) {
1442            Task nextTask = (Task) node.getCookie(Task.class);
1443            if (nextTask.getLine() != null) {
1444                TaskAnnotation anno = getAnnotation(nextTask);
1445                if (anno != null) {
1446                    showTaskInEditor(nextTask, anno);
1447                }
1448            }
1449            select(nextTask); // XXX call EM directly
1450
}
1451    }
1452
1453    /** Test membership */
1454    private static boolean isChildOf(TreeNode JavaDoc child, TreeNode JavaDoc root) {
1455        if (child == null) return false;
1456        if (child == root) return true;
1457        TreeNode JavaDoc parent = child.getParent();
1458        while (parent != null) {
1459            if (parent == root) return true;
1460            parent = parent.getParent();
1461        }
1462        return false;
1463    }
1464
1465    // last selected node helps in situations situations when
1466
// selection disappears due to resolving suggestion
1467
private TreeNode JavaDoc nextCandidate;
1468    private TreeNode JavaDoc prevCandidate;
1469
1470    /**
1471     * @param tail if true take the last one from multiple selection
1472     * othervise the first one
1473     * @return last selected or null
1474     */

1475    private Node JavaDoc currentlySelected(boolean tail) {
1476        Node JavaDoc[] selected = getExplorerManager().getSelectedNodes();
1477        if (selected != null && selected.length != 0) {
1478            if (tail) {
1479                return selected[selected.length -1];
1480            } else {
1481                return selected[0];
1482            }
1483        } else {
1484            return null;
1485        }
1486    }
1487
1488    /** Show the next task in the view */
1489    protected final void nextTask() {
1490
1491        Node JavaDoc currentlySelected = currentlySelected(true);
1492        Node JavaDoc next = null;
1493        TreeNode JavaDoc nextVisitable = null;
1494
1495        if (currentlySelected == null) {
1496
1497            // guess new selection
1498

1499            TreeNode JavaDoc root = Visualizer.findVisualizer(getExplorerManager().getRootContext());
1500            if (isChildOf(nextCandidate, root)) {
1501                if (isVisitable(nextCandidate)) {
1502                    nextVisitable = nextCandidate;
1503                } else {
1504                    nextVisitable = findNextVisitable(nextCandidate, wrapWarned);
1505                }
1506            } else {
1507                // none selected take fisrt on in all cases
1508
Node JavaDoc[] all = getExplorerManager().getRootContext().getChildren().getNodes();
1509                if (all != null && all.length > 0) {
1510                    nextVisitable = Visualizer.findVisualizer(all[0]);
1511                }
1512            }
1513        } else {
1514            TreeNode JavaDoc curr = Visualizer.findVisualizer(currentlySelected);
1515            prevCandidate = curr;
1516            nextVisitable = findNextVisitable(curr, wrapWarned);
1517        }
1518
1519        if (nextVisitable != null) {
1520            nextCandidate = findNextVisitable(nextVisitable, false);
1521            next = Visualizer.findNode(nextVisitable);
1522        } else {
1523            nextCandidate = null;
1524        }
1525
1526        if ((next == null) && !wrapWarned) {
1527            String JavaDoc msg = NbBundle.getBundle(TaskListView.class).
1528                    getString("MSG_AtLastError"); // NOI18N
1529
StatusDisplayer.getDefault().setStatusText(msg);
1530            Toolkit.getDefaultToolkit().beep();
1531            wrapWarned = true;
1532        } else {
1533            wrapWarned = false;
1534        }
1535
1536        selectNode(next);
1537    }
1538
1539
1540    private TreeNode JavaDoc findPrevVisitable(TreeNode JavaDoc last, boolean wrapAround) {
1541
1542        // find prev candidate
1543

1544        TreeNode JavaDoc prev;
1545        int index = last.getParent().getIndex(last);
1546        assert index != -1;
1547        index--;
1548        if (index >= 0) {
1549            prev = last.getParent().getChildAt(index);
1550        } else {
1551            prev = last.getParent();
1552            if (prev.getParent() == null) {
1553                prev = null; // never select root node
1554
}
1555        }
1556
1557        // try wrap around to last if warned
1558

1559        if (prev == null && wrapAround) {
1560            TreeNode JavaDoc parent = last;
1561            while (parent.getParent() != null) {
1562               parent = parent.getParent();
1563            }
1564            prev = parent;
1565            while (prev.getChildCount() > 0) {
1566                prev = prev.getChildAt(prev.getChildCount() -1);
1567            }
1568        }
1569
1570        if (prev == null) return null;
1571
1572        // assure it's visitable
1573

1574        if (isVisitable(prev)) {
1575            return prev;
1576        } else {
1577            return findPrevVisitable(prev, wrapAround);
1578        }
1579    }
1580
1581    /** Show the previous task in the view */
1582    protected final void prevTask() {
1583
1584        Node JavaDoc currentlySelected = currentlySelected(false);
1585        Node JavaDoc prev = null;
1586        TreeNode JavaDoc prevVisitable = null;
1587
1588        if (currentlySelected == null) {
1589
1590            // guess new selection
1591

1592            TreeNode JavaDoc root = Visualizer.findVisualizer(getExplorerManager().getRootContext());
1593            if (isChildOf(prevCandidate, root)) {
1594                if (isVisitable(prevCandidate)) {
1595                    prevVisitable = prevCandidate;
1596                } else {
1597                    prevVisitable = findPrevVisitable(prevCandidate, wrapWarned);
1598                }
1599            } else {
1600                // none selected take last on in all cases
1601
Node JavaDoc[] all = getExplorerManager().getRootContext().getChildren().getNodes();
1602                if (all != null && all.length > 0) {
1603                    prevVisitable = Visualizer.findVisualizer(all[all.length-1]);
1604                }
1605            }
1606        } else {
1607            TreeNode JavaDoc curr = Visualizer.findVisualizer(currentlySelected);
1608            nextCandidate = curr;
1609            prevVisitable = findPrevVisitable(curr, wrapWarned);
1610        }
1611
1612        if (prevVisitable != null) {
1613            prevCandidate = findNextVisitable(prevVisitable, false);
1614            prev = Visualizer.findNode(prevVisitable);
1615        } else {
1616            prevCandidate = null;
1617        }
1618
1619        if ((prev == null) && !wrapWarned) {
1620            String JavaDoc msg = NbBundle.getBundle(TaskListView.class).
1621                    getString("MSG_AtFirstError"); // NOI18N
1622
StatusDisplayer.getDefault().setStatusText(msg);
1623            Toolkit.getDefaultToolkit().beep();
1624            wrapWarned = true;
1625        } else {
1626            wrapWarned = false;
1627        }
1628
1629        selectNode(prev);
1630    }
1631
1632    /**
1633     * Return an editor annotation to use to show the given task
1634     *
1635     * @return created annotation or null
1636     */

1637    protected TaskAnnotation getAnnotation(Task task) {
1638        return new TaskAnnotation(task, this);
1639    }
1640
1641    protected void componentHidden() {
1642        hideTaskInEditor();
1643    }
1644
1645    /** Return true iff the given node is expanded */
1646    protected final boolean isExpanded(Node JavaDoc n) {
1647        return treeTable.isExpanded(n);
1648    }
1649
1650    /** Collapse or expand a given node */
1651    protected final void setExpanded(Node JavaDoc n, boolean expanded) {
1652        if (expanded) {
1653            treeTable.expandNode(n);
1654        } else {
1655            treeTable.collapseNode(n);
1656        }
1657    }
1658
1659    // TODO - get rid of this when you clean up TaskListView.getRootNode() to
1660
// do the Right Thing(tm) - always return effective explorer context
1661
/** Return the actual root of the node tree shown in this view.
1662     * May be a filternode when Filtering is in place.
1663     */

1664    public final Node JavaDoc getEffectiveRoot() {
1665        return getExplorerManager().getRootContext();
1666    }
1667
1668    /** Schedule a particular target suggestion to be expanded (if it's
1669     a parent node. This delay is necessary since the node may not
1670     yet have been created (and it's being created by a different
1671     thread, so we're essentially busy waiting, checking each time
1672     around the event dispatch loop up to a maximum number of checks.
1673     @param target The task we want expanded
1674     @param hops Current hop count. Clients should pass in 0 here.
1675     Used to bounce out of recursion.
1676     */

1677    public void scheduleNodeExpansion(final Task target,
1678                                      final int hops) {
1679        SwingUtilities.invokeLater(new Runnable JavaDoc() {
1680            public void run() {
1681                // find
1682
//Node n = TaskNode.find(getRootNode(), target);
1683
Node JavaDoc n = TaskNode.find(getEffectiveRoot(), target);
1684                if (n != null) {
1685                    setExpanded(n, true);
1686                } else if (hops < 50) {
1687                    scheduleNodeExpansion(target, hops + 1);
1688                }
1689            }
1690        });
1691    }
1692
1693    public void requestActive() {
1694        super.requestActive();
1695        if (treeTable != null) {
1696            treeTable.getTable().requestFocus();
1697        }
1698    }
1699
1700    /**
1701     * Returns visible columns
1702     *
1703     * @return visible columns
1704     */

1705    public final ColumnProperty[] getVisibleColumns() {
1706        TableColumnModel JavaDoc tcm = getTable().getColumnModel();
1707        ColumnProperty[] all = getColumns();
1708        List JavaDoc ret = new ArrayList();
1709        ret.add(all[0]);
1710        for (int i = 0; i < tcm.getColumnCount(); i++) {
1711            String JavaDoc title = tcm.getColumn(i).getHeaderValue().toString();
1712            for (int j = 1; j < all.length; j++) {
1713                if (title.equalsIgnoreCase(all[j].getDisplayName())) {
1714                    ret.add(all[j]);
1715                    break;
1716                }
1717            }
1718        }
1719        return (ColumnProperty[]) ret.toArray(new ColumnProperty[ret.size()]);
1720    }
1721    
1722    /** Action to just to previous or next task.
1723     */

1724    private final class PrevNextAction extends javax.swing.AbstractAction JavaDoc {
1725        private boolean prev;
1726        
1727        public PrevNextAction (boolean prev) {
1728            this.prev = prev;
1729        }
1730
1731        public void actionPerformed (java.awt.event.ActionEvent JavaDoc actionEvent) {
1732            if (prev) {
1733                prevTask ();
1734            } else {
1735                nextTask ();
1736            }
1737        }
1738        
1739        
1740    }
1741}
1742
Popular Tags