001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.tasklist.ui;
043:
044: import java.awt.BorderLayout;
045: import java.awt.event.MouseEvent;
046: import java.io.IOException;
047: import java.awt.event.ItemEvent;
048: import java.awt.event.ItemListener;
049: import java.awt.event.MouseAdapter;
050: import java.beans.PropertyChangeEvent;
051: import java.beans.PropertyChangeListener;
052: import javax.swing.JToolBar;
053: import org.netbeans.modules.tasklist.impl.ScanningScopeList;
054: import java.io.Serializable;
055: import java.util.List;
056: import java.util.logging.Level;
057: import java.util.logging.Logger;
058: import javax.swing.BorderFactory;
059: import javax.swing.ImageIcon;
060: import javax.swing.JPopupMenu;
061: import javax.swing.JToggleButton;
062: import javax.swing.SwingUtilities;
063: import org.netbeans.api.progress.ProgressHandle;
064: import org.netbeans.api.progress.ProgressHandleFactory;
065: import org.netbeans.modules.tasklist.filter.FilterRepository;
066: import org.netbeans.modules.tasklist.impl.ScannerList;
067: import org.netbeans.modules.tasklist.impl.ScanningScopeList;
068: import org.netbeans.modules.tasklist.impl.TaskManagerImpl;
069: import org.netbeans.spi.tasklist.TaskScanningScope;
070: import org.openide.util.Cancellable;
071: import org.openide.util.NbBundle;
072: import org.openide.windows.TopComponent;
073: import org.openide.windows.WindowManager;
074: import org.openide.util.Utilities;
075:
076: /**
077: * Top component which displays something.
078: */
079: final class TaskListTopComponent extends TopComponent {
080:
081: private static TaskListTopComponent instance;
082: /** path to the icon used by the component and its open action */
083: static final String ICON_PATH = "org/netbeans/modules/tasklist/ui/resources/taskList.png"; //NOI18N
084:
085: private static final String PREFERRED_ID = "TaskListTopComponent"; //NOI18N
086:
087: private TaskManagerImpl taskManager;
088: private PropertyChangeListener scopeListListener;
089: private PropertyChangeListener scannerListListener;
090: private TaskListModel model;
091: private FilterRepository filters;
092: private TaskListTable table;
093: private PropertyChangeListener changeListener;
094:
095: private TaskListTopComponent() {
096: taskManager = TaskManagerImpl.getInstance();
097:
098: initComponents();
099: setName(NbBundle.getMessage(TaskListTopComponent.class,
100: "CTL_TaskListTopComponent")); //NOI18N
101: setToolTipText(NbBundle.getMessage(TaskListTopComponent.class,
102: "HINT_TaskListTopComponent")); //NOI18N
103: setIcon(Utilities.loadImage(ICON_PATH, true));
104:
105: tableScroll.addMouseListener(new MouseAdapter() {
106: @Override
107: public void mousePressed(MouseEvent e) {
108: maybePopup(e);
109: }
110:
111: @Override
112: public void mouseReleased(MouseEvent e) {
113: maybePopup(e);
114: }
115: });
116: }
117:
118: /** This method is called from within the constructor to
119: * initialize the form.
120: * WARNING: Do NOT modify this code. The content of this method is
121: * always regenerated by the Form Editor.
122: */
123: // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
124: private void initComponents() {
125: java.awt.GridBagConstraints gridBagConstraints;
126:
127: toolbar = new javax.swing.JToolBar();
128: tableHolderPanel = new javax.swing.JPanel();
129: tableScroll = new javax.swing.JScrollPane();
130: toolbarSeparator = new javax.swing.JSeparator();
131: statusSeparator = new javax.swing.JSeparator();
132: statusBarPanel = new javax.swing.JPanel();
133:
134: setLayout(new java.awt.GridBagLayout());
135:
136: toolbar.setFloatable(false);
137: toolbar.setOrientation(1);
138: gridBagConstraints = new java.awt.GridBagConstraints();
139: gridBagConstraints.gridx = 0;
140: gridBagConstraints.gridy = 0;
141: gridBagConstraints.gridheight = 3;
142: gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
143: gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
144: gridBagConstraints.weighty = 1.0;
145: add(toolbar, gridBagConstraints);
146:
147: tableHolderPanel.setOpaque(false);
148:
149: tableScroll.setBorder(null);
150:
151: org.jdesktop.layout.GroupLayout tableHolderPanelLayout = new org.jdesktop.layout.GroupLayout(
152: tableHolderPanel);
153: tableHolderPanel.setLayout(tableHolderPanelLayout);
154: tableHolderPanelLayout
155: .setHorizontalGroup(tableHolderPanelLayout
156: .createParallelGroup(
157: org.jdesktop.layout.GroupLayout.LEADING)
158: .add(
159: org.jdesktop.layout.GroupLayout.TRAILING,
160: tableScroll,
161: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
162: 397, Short.MAX_VALUE));
163: tableHolderPanelLayout.setVerticalGroup(tableHolderPanelLayout
164: .createParallelGroup(
165: org.jdesktop.layout.GroupLayout.LEADING).add(
166: tableScroll,
167: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
168: 299, Short.MAX_VALUE));
169:
170: gridBagConstraints = new java.awt.GridBagConstraints();
171: gridBagConstraints.gridx = 2;
172: gridBagConstraints.gridy = 0;
173: gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
174: gridBagConstraints.weightx = 1.0;
175: gridBagConstraints.weighty = 1.0;
176: add(tableHolderPanel, gridBagConstraints);
177:
178: toolbarSeparator
179: .setOrientation(javax.swing.SwingConstants.VERTICAL);
180: gridBagConstraints = new java.awt.GridBagConstraints();
181: gridBagConstraints.gridx = 1;
182: gridBagConstraints.gridy = 0;
183: gridBagConstraints.gridheight = 3;
184: gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
185: gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
186: gridBagConstraints.weighty = 1.0;
187: add(toolbarSeparator, gridBagConstraints);
188: gridBagConstraints = new java.awt.GridBagConstraints();
189: gridBagConstraints.gridx = 2;
190: gridBagConstraints.gridy = 1;
191: gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
192: gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHWEST;
193: gridBagConstraints.weightx = 1.0;
194: add(statusSeparator, gridBagConstraints);
195:
196: statusBarPanel.setLayout(new java.awt.BorderLayout());
197: gridBagConstraints = new java.awt.GridBagConstraints();
198: gridBagConstraints.gridx = 2;
199: gridBagConstraints.gridy = 2;
200: gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
201: gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHEAST;
202: gridBagConstraints.weightx = 1.0;
203: add(statusBarPanel, gridBagConstraints);
204: }// </editor-fold>//GEN-END:initComponents
205:
206: // Variables declaration - do not modify//GEN-BEGIN:variables
207: private javax.swing.JPanel statusBarPanel;
208: private javax.swing.JSeparator statusSeparator;
209: private javax.swing.JPanel tableHolderPanel;
210: private javax.swing.JScrollPane tableScroll;
211: private javax.swing.JToolBar toolbar;
212: private javax.swing.JSeparator toolbarSeparator;
213:
214: // End of variables declaration//GEN-END:variables
215:
216: /**
217: * Gets default instance. Do not use directly: reserved for *.settings files only,
218: * i.e. deserialization routines; otherwise you could get a non-deserialized instance.
219: * To obtain the singleton instance, use {@link findInstance}.
220: */
221: public static synchronized TaskListTopComponent getDefault() {
222: if (instance == null) {
223: instance = new TaskListTopComponent();
224: }
225: return instance;
226: }
227:
228: /**
229: * Obtain the TaskListTopComponent instance. Never call {@link #getDefault} directly!
230: */
231: public static synchronized TaskListTopComponent findInstance() {
232: TopComponent win = WindowManager.getDefault().findTopComponent(
233: PREFERRED_ID);
234: if (win == null) {
235: getLogger()
236: .log(
237: Level.INFO,
238: "Cannot find TaskList component. It will not be located properly in the window system."); //NOI18N
239: return getDefault();
240: }
241: if (win instanceof TaskListTopComponent) {
242: return (TaskListTopComponent) win;
243: }
244: getLogger()
245: .log(
246: Level.INFO,
247: "There seem to be multiple components with the '"
248: + PREFERRED_ID + //NOI18N
249: "' ID. That is a potential source of errors and unexpected behavior."); //NOI18N
250: return getDefault();
251: }
252:
253: @Override
254: public int getPersistenceType() {
255: return TopComponent.PERSISTENCE_ALWAYS;
256: }
257:
258: @Override
259: public void componentOpened() {
260: TaskScanningScope activeScope = Settings.getDefault()
261: .getActiveScanningScope();
262:
263: if (null == activeScope)
264: activeScope = ScanningScopeList.getDefault()
265: .getDefaultScope();
266: if (null == filters) {
267: try {
268: filters = FilterRepository.getDefault();
269: filters.load();
270: } catch (IOException ioE) {
271: getLogger().log(Level.INFO, ioE.getMessage(), ioE);
272: }
273: }
274:
275: if (null == changeListener) {
276: changeListener = createChangeListener();
277: taskManager
278: .addPropertyChangeListener(
279: TaskManagerImpl.PROP_WORKING_STATUS,
280: changeListener);
281: }
282:
283: if (null == model) {
284: table = new TaskListTable();
285: //later on the button in the toolbar will switch to the previously used table model
286: model = new TaskListModel(taskManager.getTasks());
287: table.setModel(model);
288: tableScroll.setViewportView(table);
289: tableScroll.setBorder(BorderFactory.createEmptyBorder());
290: statusBarPanel.add(new StatusBar(taskManager.getTasks()),
291: BorderLayout.CENTER);
292: }
293: ScanningScopeList.getDefault().addPropertyChangeListener(
294: getScopeListListener());
295: ScannerList.getFileScannerList().addPropertyChangeListener(
296: getScannerListListener());
297: ScannerList.getPushScannerList().addPropertyChangeListener(
298: getScannerListListener());
299:
300: rebuildToolbar();
301:
302: final TaskScanningScope scopeToObserve = activeScope;
303: SwingUtilities.invokeLater(new Runnable() {
304: public void run() {
305: taskManager
306: .observe(scopeToObserve, filters.getActive());
307: }
308: });
309: }
310:
311: @Override
312: public void componentClosed() {
313: ScanningScopeList.getDefault().removePropertyChangeListener(
314: getScopeListListener());
315: ScannerList.getFileScannerList().removePropertyChangeListener(
316: getScannerListListener());
317: ScannerList.getPushScannerList().removePropertyChangeListener(
318: getScannerListListener());
319: taskManager.observe(null, null);
320: if (null != changeListener) {
321: taskManager
322: .removePropertyChangeListener(
323: TaskManagerImpl.PROP_WORKING_STATUS,
324: changeListener);
325: changeListener = null;
326: }
327: if (null != progress)
328: progress.finish();
329: progress = null;
330: try {
331: FilterRepository.getDefault().save();
332: } catch (IOException ioE) {
333: getLogger().log(Level.INFO, null, ioE);
334: }
335: }
336:
337: @Override
338: public void requestActive() {
339: super .requestActive();
340: table.requestFocusInWindow();
341: }
342:
343: @Override
344: public Object writeReplace() {
345: return new ResolvableHelper();
346: }
347:
348: @Override
349: protected String preferredID() {
350: return PREFERRED_ID;
351: }
352:
353: private void maybePopup(MouseEvent e) {
354: if (e.isPopupTrigger()) {
355: e.consume();
356: JPopupMenu popup = Util.createPopup(table);
357: popup.show(this , e.getX(), e.getY());
358: }
359: }
360:
361: private PropertyChangeListener getScopeListListener() {
362: if (null == scopeListListener) {
363: scopeListListener = new PropertyChangeListener() {
364: public void propertyChange(PropertyChangeEvent arg0) {
365: ScanningScopeList scopeList = ScanningScopeList
366: .getDefault();
367: List<TaskScanningScope> newScopes = scopeList
368: .getTaskScanningScopes();
369: if (newScopes.isEmpty()) {
370: getLogger().log(Level.INFO,
371: "No task scanning scope found"); //NOI18N
372: }
373: rebuildToolbar();
374: }
375: };
376: }
377: return scopeListListener;
378: }
379:
380: private PropertyChangeListener getScannerListListener() {
381: if (null == scannerListListener) {
382: scannerListListener = new PropertyChangeListener() {
383: public void propertyChange(PropertyChangeEvent e) {
384: SwingUtilities.invokeLater(new Runnable() {
385: public void run() {
386: final TaskScanningScope scopeToObserve = taskManager
387: .getScope();
388: taskManager.observe(null, null);
389: SwingUtilities.invokeLater(new Runnable() {
390: public void run() {
391: taskManager.observe(scopeToObserve,
392: filters.getActive());
393: }
394: });
395: }
396: });
397: }
398: };
399: }
400: return scannerListListener;
401: }
402:
403: private void rebuildToolbar() {
404: toolbar.removeAll();
405: toolbar.setFocusable(false);
406: //scope buttons
407: List<TaskScanningScope> scopes = ScanningScopeList.getDefault()
408: .getTaskScanningScopes();
409: for (TaskScanningScope scope : scopes) {
410: toolbar.add(new ScopeButton(taskManager, scope));
411: }
412: toolbar.add(new JToolBar.Separator());
413: //filter
414: JToggleButton toggleFilter = new FiltersMenuButton(filters
415: .getActive());
416: toolbar.add(toggleFilter);
417: //grouping & other butons
418: toolbar.addSeparator();
419: final JToggleButton toggleGroups = new JToggleButton(
420: new ImageIcon(
421: Utilities
422: .loadImage("org/netbeans/modules/tasklist/ui/resources/groups.png"))); //NOI18N
423: toggleGroups.addItemListener(new ItemListener() {
424: public void itemStateChanged(ItemEvent e) {
425: switchTableModel(e.getStateChange() == ItemEvent.SELECTED);
426: Settings.getDefault().setGroupTasksByCategory(
427: toggleGroups.isSelected());
428: toggleGroups
429: .setToolTipText(toggleGroups.isSelected() ? NbBundle
430: .getMessage(TaskListTopComponent.class,
431: "HINT_TasksAsList") //NOI18N
432: : NbBundle.getMessage(
433: TaskListTopComponent.class,
434: "HINT_GrouppedTasks")); //NOI18N
435: }
436: });
437: toggleGroups.setSelected(Settings.getDefault()
438: .getGroupTasksByCategory());
439: toggleGroups
440: .setToolTipText(toggleGroups.isSelected() ? NbBundle
441: .getMessage(TaskListTopComponent.class,
442: "HINT_TasksAsList") //NOI18N
443: : NbBundle.getMessage(
444: TaskListTopComponent.class,
445: "HINT_GrouppedTasks")); //NOI18N
446: toggleGroups.setFocusable(false);
447: toolbar.add(toggleGroups);
448: }
449:
450: private void switchTableModel(boolean useFoldingModel) {
451: if (useFoldingModel) {
452: model = new FoldingTaskListModel(taskManager.getTasks());
453: table.setModel(model);
454: statusBarPanel.removeAll();
455: } else {
456: model = new TaskListModel(taskManager.getTasks());
457: table.setModel(model);
458: statusBarPanel.add(new StatusBar(taskManager.getTasks()),
459: BorderLayout.CENTER);
460: }
461: statusBarPanel.setVisible(!useFoldingModel);
462: statusSeparator.setVisible(!useFoldingModel);
463: }
464:
465: static private Logger getLogger() {
466: return Logger.getLogger(TaskListTopComponent.class.getName());
467: }
468:
469: private ProgressHandle progress;
470:
471: private PropertyChangeListener createChangeListener() {
472: return new PropertyChangeListener() {
473: public void propertyChange(PropertyChangeEvent e) {
474: synchronized (TaskListTopComponent.this ) {
475: if (((Boolean) e.getNewValue()).booleanValue()) {
476: if (null == progress) {
477: progress = ProgressHandleFactory
478: .createHandle(NbBundle.getMessage(
479: TaskListTopComponent.class,
480: "LBL_ScanProgress"), //NOI18N
481: new Cancellable() { //NOI18N
482: public boolean cancel() {
483: taskManager.abort();
484: return true;
485: }
486: });
487: }
488: progress.start();
489: progress.switchToIndeterminate();
490: } else {
491: if (null != progress)
492: progress.finish();
493: progress = null;
494: }
495: }
496: }
497: };
498: }
499:
500: final static class ResolvableHelper implements Serializable {
501: private static final long serialVersionUID = 1L;
502:
503: public Object readResolve() {
504: return TaskListTopComponent.getDefault();
505: }
506: }
507: }
|