blob: 445c455367abb40485623af6a308d7bbb7c92c76 [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.palette;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import javax.swing.Action;
import javax.swing.SwingUtilities;
import org.netbeans.spi.palette.DragAndDropHandler;
import org.netbeans.spi.palette.PaletteController;
import org.openide.nodes.*;
import org.openide.util.*;
import org.netbeans.modules.palette.ui.Customizer;
import org.netbeans.spi.palette.PaletteActions;
/**
* Default implementation of PaletteModel interface based on Nodes.
*
* @author S. Aubrecht
*/
public class DefaultModel implements Model, NodeListener {
/**
* Palette's root node. Its subnodes are palette categories.
*/
private RootNode rootNode;
/**
* Item currently selected in the palette or null.
*/
private Item selectedItem;
/**
* Category that owns the selected item or null.
*/
private Category selectedCategory;
private PropertyChangeSupport propertySupport;
private ArrayList<ModelListener> modelListeners = new ArrayList<ModelListener>( 3 );
private boolean categoriesNeedRefresh = true;
/**
* Cached categories
*/
private Category[] categories;
/**
* Creates a new instance of DefaultPaletteModel
*
* @param rootNode Palette's root node.
*/
public DefaultModel( RootNode rootNode ) {
this.rootNode = rootNode;
propertySupport = new PropertyChangeSupport( this );
this.rootNode.addNodeListener( this );
}
public void setSelectedItem( Lookup category, Lookup item ) {
Category cat = null;
Item it = null;
if( null != category ) {
Node catNode = category.lookup( Node.class );
if( null != catNode ) {
cat = findCategory( catNode );
}
}
if( null != item && null != cat ) {
Node itNode = item.lookup( Node.class );
if( null != itNode ) {
it = findItem( cat, itNode );//new DefaultItem( itNode );
}
}
Item oldValue = selectedItem;
this.selectedItem = it;
this.selectedCategory = cat;
propertySupport.firePropertyChange( Model.PROP_SELECTED_ITEM, oldValue, selectedItem );
}
public void clearSelection() {
setSelectedItem( null, null );
}
public Action[] getActions() {
return rootNode.getActions( false );
}
public Item getSelectedItem() {
return selectedItem;
}
public Category getSelectedCategory() {
return selectedCategory;
}
public void addModelListener( ModelListener listener ) {
synchronized( modelListeners ) {
modelListeners.add( listener );
propertySupport.addPropertyChangeListener( listener );
}
}
public void removeModelListener( ModelListener listener ) {
synchronized( modelListeners ) {
modelListeners.remove( listener );
propertySupport.removePropertyChangeListener( listener );
}
}
public synchronized Category[] getCategories() {
if( null == categories || categoriesNeedRefresh ) {
Node[] nodes = rootNode.getChildren().getNodes( canBlock() );
categories = nodes2categories( nodes );
categoriesNeedRefresh = false;
}
return categories;
}
public static boolean canBlock() {
return !Children.MUTEX.isReadAccess() && !Children.MUTEX.isWriteAccess ();
}
/** Fired when a set of new children is added.
* @param ev event describing the action
*/
public void childrenAdded(NodeMemberEvent ev) {
categoriesNeedRefresh = true;
if( isRefreshingChildren )
return;
final Node[] nodes = ev.getDelta();
SwingUtilities.invokeLater( new Runnable() {
public void run() {
Category[] addedCategories = findCategories( nodes );
fireCategoriesChanged( addedCategories, true );
}
});
}
/** Fired when a set of children is removed.
* @param ev event describing the action
*/
public void childrenRemoved(NodeMemberEvent ev) {
categoriesNeedRefresh = true;
final Node[] nodes = ev.getDelta();
SwingUtilities.invokeLater( new Runnable() {
public void run() {
Category[] removedCategories = findCategories( nodes );
fireCategoriesChanged( removedCategories, false );
}
});
}
/** Fired when the order of children is changed.
* @param ev event describing the change
*/
public void childrenReordered(NodeReorderEvent ev) {
categoriesNeedRefresh = true;
fireCategoriesChanged( null, false );
}
/** Fired when the node is deleted.
* @param ev event describing the node
*/
public synchronized void nodeDestroyed(NodeEvent ev) {
this.rootNode.removeNodeListener( this );
}
public void propertyChange(PropertyChangeEvent evt) {
}
private void fireCategoriesChanged( Category[] changedCategories, boolean added ) {
ModelListener[] listeners;
synchronized( modelListeners ) {
listeners = new ModelListener[modelListeners.size()];
listeners = modelListeners.toArray( listeners );
}
for( int i=0; i<listeners.length; i++ ) {
if( null != changedCategories ) {
if( added ) {
listeners[i].categoriesAdded( changedCategories );
} else {
listeners[i].categoriesRemoved( changedCategories );
}
} else {
listeners[i].categoriesReordered();
}
}
}
/**
* Wrap the given Nodes in PaletteCategory instances.
*/
private Category[] nodes2categories( Node[] nodes ) {
Category[] res = new Category[ nodes.length ];
for( int i=0; i<res.length; i++ ) {
res[i] = new DefaultCategory( nodes[i] );
}
return res;
}
private Category[] findCategories( Node[] nodes ) {
Category[] res = new Category[ nodes.length ];
Category[] current = getCategories();
for( int i=0; i<res.length; i++ ) {
boolean found = false;
for( int j=0; !found && null != current && j<current.length; j++ ) {
Node catNode = current[j].getLookup().lookup( Node.class );
if( nodes[i].equals( catNode ) ) {
res[i] = current[j];
found = true;
}
}
if( !found ) {
res[i] = new DefaultCategory( nodes[i] );
}
}
return res;
}
private boolean isRefreshingChildren = false;
public void refresh() {
synchronized( rootNode ) {
PaletteActions customActions = rootNode.getLookup().lookup( PaletteActions.class );
Action customRefreshAction = customActions.getRefreshAction();
if( null != customRefreshAction ) {
customRefreshAction.actionPerformed( new ActionEvent( getRoot(), 0, "refresh" ) ); //NOI18N
}
clearSelection();
categoriesNeedRefresh = true;
isRefreshingChildren = true;
try {
rootNode.refreshChildren();
} finally {
isRefreshingChildren = false;
}
fireCategoriesChanged( null, false );
}
}
public void showCustomizer( PaletteController controller, Settings settings ) {
Customizer.show( rootNode, controller, settings );
}
public Lookup getRoot() {
return rootNode.getLookup();
}
public boolean moveCategory( Category source, Category target, boolean moveBefore ) {
int targetIndex = categoryToIndex( target );
if( !moveBefore ) {
targetIndex++;
}
DragAndDropHandler handler = getDragAndDropHandler();
return handler.moveCategory( source.getLookup(), targetIndex );
}
private int categoryToIndex( Category category ) {
Node node = category.getLookup().lookup( Node.class );
if( null != node ) {
Index order = rootNode.getCookie( Index.class );
if( null != order ) {
return order.indexOf( node );
}
}
return -1;
}
public String getName() {
return rootNode.getName();
}
private Category findCategory( Node node ) {
Category[] cats = getCategories();
for( int i=0; i<cats.length; i++ ) {
Node catNode = cats[i].getLookup().lookup( Node.class );
if( null != catNode && catNode.equals( node ) )
return cats[i];
}
return null;
}
private Item findItem( Category category, Node node ) {
Item[] items = category.getItems();
for( int i=0; i<items.length; i++ ) {
Node itNode = items[i].getLookup().lookup( Node.class );
if( null != itNode && itNode.equals( node ) )
return items[i];
}
return null;
}
public boolean canReorderCategories() {
return getDragAndDropHandler().canReorderCategories( rootNode.getLookup() );
}
private DragAndDropHandler getDragAndDropHandler() {
return rootNode.getLookup().lookup( DragAndDropHandler.class );
}
}