blob: b8a7f10e5cc3d718081806ab7a84ef66353fa218 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.tasklist.ui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JToolBar;
import java.io.Serializable;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.netbeans.modules.tasklist.filter.FilterRepository;
import org.netbeans.modules.tasklist.impl.ScannerList;
import org.netbeans.modules.tasklist.impl.ScanningScopeList;
import org.netbeans.modules.tasklist.impl.TaskList;
import org.netbeans.modules.tasklist.impl.TaskManagerImpl;
import org.netbeans.spi.tasklist.Task;
import org.netbeans.spi.tasklist.TaskScanningScope;
import org.openide.util.Cancellable;
import org.openide.util.HelpCtx;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
/**
* Top component which displays something.
*/
final class TaskListTopComponent extends TopComponent {
private static TaskListTopComponent instance;
/** path to the icon used by the component and its open action */
static final String ICON_PATH = "org/netbeans/modules/tasklist/ui/resources/taskList.png"; //NOI18N
private static final String PREFERRED_ID = "TaskListTopComponent"; //NOI18N
private TaskManagerImpl taskManager;
private PropertyChangeListener scopeListListener;
private PropertyChangeListener scannerListListener;
private TaskListModel model;
private FilterRepository filters;
private TaskListTable table;
private PropertyChangeListener changeListener;
private TaskList.Listener tasksListener;
private static final boolean isMacLaf = "Aqua".equals(UIManager.getLookAndFeel().getID());
private static final Color macBackground = UIManager.getColor("NbExplorerView.background");
private TaskListTopComponent() {
taskManager = TaskManagerImpl.getInstance();
initComponents();
// statusBarPanel.setMinimumSize(new JLabel("dummy").getPreferredSize());
setName(NbBundle.getMessage(TaskListTopComponent.class, "CTL_TaskListTopComponent")); //NOI18N
setToolTipText(NbBundle.getMessage(TaskListTopComponent.class, "HINT_TaskListTopComponent")); //NOI18N
setIcon(ImageUtilities.loadImage(ICON_PATH, true));
tableScroll.addMouseListener( new MouseAdapter() {
@Override
public void mousePressed( MouseEvent e ) {
maybePopup( e );
}
@Override
public void mouseReleased( MouseEvent e ) {
maybePopup( e );
}
});
if( isMacLaf ) {
tableScroll.setBackground(macBackground);
tableScroll.getViewport().setBackground(macBackground);
toolbar.setBackground(macBackground);
statusBarPanel.setBackground(macBackground);
}
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
toolbar = new javax.swing.JToolBar();
tableHolderPanel = new javax.swing.JPanel();
tableScroll = new javax.swing.JScrollPane();
toolbarSeparator = new javax.swing.JSeparator();
statusSeparator = new javax.swing.JSeparator();
statusBarPanel = new javax.swing.JPanel();
setLayout(new java.awt.GridBagLayout());
toolbar.setFloatable(false);
toolbar.setOrientation(javax.swing.SwingConstants.VERTICAL);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridheight = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weighty = 1.0;
add(toolbar, gridBagConstraints);
tableHolderPanel.setOpaque(false);
tableScroll.setBorder(null);
javax.swing.GroupLayout tableHolderPanelLayout = new javax.swing.GroupLayout(tableHolderPanel);
tableHolderPanel.setLayout(tableHolderPanelLayout);
tableHolderPanelLayout.setHorizontalGroup(
tableHolderPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(tableScroll, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 397, Short.MAX_VALUE)
);
tableHolderPanelLayout.setVerticalGroup(
tableHolderPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(tableScroll, javax.swing.GroupLayout.DEFAULT_SIZE, 299, Short.MAX_VALUE)
);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
add(tableHolderPanel, gridBagConstraints);
toolbarSeparator.setOrientation(javax.swing.SwingConstants.VERTICAL);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridheight = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weighty = 1.0;
add(toolbarSeparator, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHWEST;
gridBagConstraints.weightx = 1.0;
add(statusSeparator, gridBagConstraints);
statusBarPanel.setLayout(new java.awt.BorderLayout());
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHEAST;
gridBagConstraints.weightx = 1.0;
add(statusBarPanel, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JPanel statusBarPanel;
private javax.swing.JSeparator statusSeparator;
private javax.swing.JPanel tableHolderPanel;
private javax.swing.JScrollPane tableScroll;
private javax.swing.JToolBar toolbar;
private javax.swing.JSeparator toolbarSeparator;
// End of variables declaration//GEN-END:variables
/**
* Gets default instance. Do not use directly: reserved for *.settings files only,
* i.e. deserialization routines; otherwise you could get a non-deserialized instance.
* To obtain the singleton instance, use {@link findInstance}.
*/
public static synchronized TaskListTopComponent getDefault() {
if (instance == null) {
instance = new TaskListTopComponent();
}
return instance;
}
/**
* Obtain the TaskListTopComponent instance. Never call {@link #getDefault} directly!
*/
public static synchronized TaskListTopComponent findInstance() {
TopComponent win = WindowManager.getDefault().findTopComponent(PREFERRED_ID);
if (win == null) {
getLogger().log( Level.INFO, "Cannot find TaskList component. It will not be located properly in the window system." ); //NOI18N
return getDefault();
}
if (win instanceof TaskListTopComponent) {
return (TaskListTopComponent)win;
}
getLogger().log( Level.INFO,
"There seem to be multiple components with the '" + PREFERRED_ID + //NOI18N
"' ID. That is a potential source of errors and unexpected behavior."); //NOI18N
return getDefault();
}
@Override
public int getPersistenceType() {
return TopComponent.PERSISTENCE_ALWAYS;
}
@Override
public void componentOpened() {
if( null == model ) {
toolbarSeparator.setVisible(false);
statusSeparator.setVisible(false);
tableScroll.setViewportView( createNoTasksMessage() );
}
WindowManager.getDefault().invokeWhenUIReady(new Runnable() {
@Override
public void run() {
TaskManagerImpl.RP.post(new Runnable() {
@Override
public void run() {
init();
}
});
}
});
}
private void init() {
TaskScanningScope activeScope = getActiveScope();
if( null == filters ) {
try {
filters = FilterRepository.getDefault();
filters.load();
} catch( IOException ioE ) {
getLogger().log( Level.INFO, ioE.getMessage(), ioE );
}
}
if( null == changeListener ) {
changeListener = createChangeListener();
taskManager.addPropertyChangeListener( TaskManagerImpl.PROP_WORKING_STATUS, changeListener );
}
if( null == model ) {
table = new TaskListTable();
if( isMacLaf )
table.setBackground(macBackground);
//later on the button in the toolbar will switch to the previously used table model
model = new TaskListModel( taskManager.getTasks() );
table.setModel( model );
tasksListener = new TaskList.Listener() {
@Override
public void tasksAdded( List<? extends Task> tasks ) {
Runnable runnable = new Runnable() {
@Override
public void run() {
tableScroll.setViewportView( table );
Color background = UIManager.getColor("TextArea.background"); //NOI18N
if( null != background )
tableScroll.getViewport().setBackground( background );
tableScroll.setBorder( BorderFactory.createEmptyBorder() );
if( isMacLaf ) {
table.setBackground(macBackground);
tableScroll.getViewport().setBackground(macBackground);
}
}
};
taskManager.getTasks().removeListener(tasksListener);
if( SwingUtilities.isEventDispatchThread() ) {
runnable.run();
} else {
SwingUtilities.invokeLater(runnable);
}
tasksListener = null;
}
@Override
public void tasksRemoved(List<? extends Task> tasks) {
}
@Override
public void cleared() {
}
};
taskManager.getTasks().addListener(tasksListener);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
tableScroll.setViewportView( createNoTasksMessage() );
tableScroll.setBorder( BorderFactory.createEmptyBorder() );
statusBarPanel.add( new CountStatusBar(taskManager.getTasks()), BorderLayout.WEST );
statusBarPanel.add( new ScopeStatusBar(taskManager), BorderLayout.CENTER );
toolbarSeparator.setVisible(true);
statusSeparator.setVisible(true);
rebuildToolbar();
}
});
}
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
ScanningScopeList.getDefault().addPropertyChangeListener( getScopeListListener() );
ScannerList.getFileScannerList().addPropertyChangeListener( getScannerListListener() );
ScannerList.getPushScannerList().addPropertyChangeListener( getScannerListListener() );
}
});
final TaskScanningScope scopeToObserve = activeScope;
taskManager.observe( scopeToObserve, filters.getActive() );
}
private Component createNoTasksMessage() {
JPanel panel = new JPanel( new BorderLayout() );
if( isMacLaf ) {
panel.setBackground(macBackground);
} else {
Color background = UIManager.getColor("TextArea.background"); //NOI18N
if( null != background )
panel.setBackground( background );
}
JLabel msg = new JLabel( NbBundle.getMessage(TaskListTopComponent.class, "LBL_NoTasks" ) ); //NOI18N
msg.setHorizontalAlignment(JLabel.CENTER);
msg.setEnabled(false);
msg.setOpaque(false);
panel.add( msg, BorderLayout.CENTER );
return panel;
}
@Override
public void componentClosed() {
ScanningScopeList.getDefault().removePropertyChangeListener( getScopeListListener() );
ScannerList.getFileScannerList().removePropertyChangeListener( getScannerListListener() );
ScannerList.getPushScannerList().removePropertyChangeListener( getScannerListListener() );
taskManager.observe( null, null );
if( null != changeListener ) {
taskManager.removePropertyChangeListener( TaskManagerImpl.PROP_WORKING_STATUS, changeListener );
changeListener = null;
}
synchronized( this ) {
if( null != progress )
progress.finish();
progress = null;
}
try {
FilterRepository.getDefault().save();
} catch( IOException ioE ) {
getLogger().log( Level.INFO, null, ioE );
}
}
@Override
public void requestActive() {
super.requestActive();
if( null != table )
table.requestFocusInWindow();
}
@Override
public Object writeReplace() {
return new ResolvableHelper();
}
@Override
protected String preferredID() {
return PREFERRED_ID;
}
private void maybePopup( MouseEvent e ) {
if( e.isPopupTrigger() && null != table ) {
e.consume();
JPopupMenu popup = Util.createPopup( table );
popup.show( this, e.getX(), e.getY() );
}
}
private PropertyChangeListener getScopeListListener() {
if( null == scopeListListener ) {
scopeListListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent arg0) {
ScanningScopeList scopeList = ScanningScopeList.getDefault();
List<TaskScanningScope> newScopes = scopeList.getTaskScanningScopes();
if( newScopes.isEmpty() ) {
getLogger().log( Level.INFO, "No task scanning scope found" ); //NOI18N
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
rebuildToolbar();
}
});
}
};
}
return scopeListListener;
}
private PropertyChangeListener getScannerListListener() {
if( null == scannerListListener ) {
scannerListListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent e) {
final TaskScanningScope scopeToObserve = taskManager.getScope();
taskManager.observe( null, null );
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
taskManager.observe( scopeToObserve, filters.getActive() );
}
});
}
};
}
return scannerListListener;
}
private void rebuildToolbar() {
toolbar.removeAll();
toolbar.setFocusable( false );
//scope buttons
List<TaskScanningScope> scopes = ScanningScopeList.getDefault().getTaskScanningScopes();
for( TaskScanningScope scope : scopes ) {
final ScopeButton scopeButton = new ScopeButton( taskManager, scope );
scopeButton.setSelected(scope.equals(Settings.getDefault().getActiveScanningScope()));
toolbar.add(scopeButton);
}
toolbar.add( new JToolBar.Separator() );
//filter
JToggleButton toggleFilter = new FiltersMenuButton( filters.getActive() );
toolbar.add( toggleFilter );
//grouping & other butons
toolbar.addSeparator();
final JToggleButton toggleGroups = new JToggleButton( ImageUtilities.loadImageIcon("org/netbeans/modules/tasklist/ui/resources/groups.png", false)); //NOI18N
toggleGroups.addItemListener( new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
switchTableModel( e.getStateChange() == ItemEvent.SELECTED );
Settings.getDefault().setGroupTasksByCategory( toggleGroups.isSelected() );
toggleGroups.setToolTipText( toggleGroups.isSelected()
? NbBundle.getMessage( TaskListTopComponent.class, "HINT_TasksAsList" ) //NOI18N
: NbBundle.getMessage( TaskListTopComponent.class, "HINT_GrouppedTasks" ) ); //NOI18N
}
});
toggleGroups.setSelected( Settings.getDefault().getGroupTasksByCategory() );
toggleGroups.setToolTipText( toggleGroups.isSelected()
? NbBundle.getMessage( TaskListTopComponent.class, "HINT_TasksAsList" ) //NOI18N
: NbBundle.getMessage( TaskListTopComponent.class, "HINT_GrouppedTasks" ) ); //NOI18N
toggleGroups.setFocusable( false );
toolbar.add( toggleGroups );
}
private void switchTableModel( boolean useFoldingModel ) {
if( useFoldingModel ) {
model = new FoldingTaskListModel( taskManager.getTasks() );
table.setModel( model );
statusBarPanel.removeAll();
} else {
model = new TaskListModel( taskManager.getTasks() );
table.setModel( model );
statusBarPanel.add( new CountStatusBar(taskManager.getTasks()), BorderLayout.WEST );
statusBarPanel.add( new ScopeStatusBar(taskManager), BorderLayout.CENTER );
}
statusBarPanel.setVisible( !useFoldingModel );
statusSeparator.setVisible( !useFoldingModel );
}
static private Logger getLogger() {
return Logger.getLogger( TaskListTopComponent.class.getName() );
}
private ProgressHandle progress;
private PropertyChangeListener createChangeListener() {
return new PropertyChangeListener() {
@Override
public void propertyChange( PropertyChangeEvent e ) {
synchronized( TaskListTopComponent.this ) {
if( ((Boolean)e.getNewValue()).booleanValue() ) {
if( null == progress ) {
progress = ProgressHandleFactory.createHandle(
NbBundle.getMessage( TaskListTopComponent.class, "LBL_ScanProgress" ), //NOI18N
new Cancellable() { //NOI18N
@Override
public boolean cancel() {
taskManager.abort();
return true;
}
});
progress.start();
progress.switchToIndeterminate();
}
} else {
if( null != progress )
progress.finish();
progress = null;
}
}
}
};
}
final static class ResolvableHelper implements Serializable {
private static final long serialVersionUID = 1L;
public Object readResolve() {
return TaskListTopComponent.getDefault();
}
}
/** Defines task list Help ID */
@Override
public HelpCtx getHelpCtx () {
return new HelpCtx(TaskListTopComponent.class);
}
private TaskScanningScope getActiveScope() {
TaskScanningScope activeScope = Settings.getDefault().getActiveScanningScope();
if( null == activeScope )
activeScope = ScanningScopeList.getDefault().getDefaultScope();
return activeScope;
}
}