Skip navigation links
org.netbeans.spi.palette/1 1.70

Common Palette
Stable

See: Description

Common Palette 
Package Description
org.netbeans.spi.palette  

The API provides access to the Common Component Palette. The palette clients can use this API to define content to be displayed in the common palette TopComponent when their editors are active. The module will autoload. Palette

The API includes support for the clients writing palette content insertable into the text editor.
This support covers the DTD definition of the palette item definition file format and the content of the Lookup holding object(s) representing the selected item. editor-palette-item-1_0.dtd

What is New (see all changes)?

Use Cases

Palette Content

The Common Palette content is a two-level hierarchy. The top-most level are Categories, the Category children are Items. It's possible to select (highlight) items in the palette panel using a mouse or keyboard and then inserted/dropped into an editor that supports the palette.

The palette content can come from two different sources:

  • Folders and files hierarchy defined in XML layer: folders under palette's root folder represent categories, files in category folders are palette item. This way of creating of palette content is more convenient for module developers as very little extra coding is required to setup a palette.
  • An arbitraty hierarchy of generic Nodes: the children of palette's root Node are categories and the children of a category Node are palette items. This approach is more flexible when the palette content must change dynamically (e.g. according to cursor position in editor window) however more coding may be needed to setup the Node hierarchy. Please see Nodes API for more details on Node management.
Basic usage

The following steps must be taken if a module wants to define its own palette content as a hierarchy of folders and files in its XML layer:

  • Define palette's root folder in module's layer and also define subfolders for categories and file objects for palette items.
    Example:
          <filesystem>
              <folder name="MyModulePalette">
                  <folder name="Category1">
                      <file name="PaletteItem_1.myitem" url="palette/PaletteItem_1.xml" />
                      <file name="PaletteItem_2.myitem" url="palette/PaletteItem_2.xml" />
                      <file name="PaletteItem_3.myitem" url="palette/PaletteItem_3.xml" />
                  </folder>
                  <folder name="Category2">
                      <file name="PaletteItem_4.myitem" url="palette/PaletteItem_4.xml" />
                      <file name="PaletteItem_5.myitem" url="palette/PaletteItem_5.xml" />
                      <file name="PaletteItem_6.myitem" url="palette/PaletteItem_6.xml" />
                  </folder>
              </folder>
          </filesystem>
                  

    Note: it's necessary to define some way of loading of the palette item from e.g. XML files. There are several possible ways to achieve that, please refer to DataLoaders API for more details.
  • Extend PaletteActions class that provides custom Actions for palette's popup menus.
    Example:
            class MyPaletteActions extends PaletteActions {
    
                public Action getPreferredAction(Lookup lookup) {
                    Node itemNode = (Node)lookup.lookup( Node.class );
                    if( null != itemNode ) {
                        return new InsertItemAtDefaultLocationAction( itemNode );
                    }
                    return null;
                }
    
                public Action[] getCustomItemActions(Lookup lookup) {
                    Node itemNode = (Node)lookup.lookup( Node.class );
                    if( null != itemNode ) {
                        return new Action[] { new CustomizeItemAction( itemNode ) };
                    }
                    return null;
                }
    
                public Action[] getCustomCategoryActions(Lookup lookup) {
                    Node categoryNode = (Node)lookup.lookup( Node.class );
                    if( null != categoryNode ) {
                        return new Action[] { new CustomizeCategoryAction( categoryNode ) };
                    }
                    return null;
                }
    
                public Action[] getImportActions() {
                    return new Action[] { new ImportItemsFromWebAction() };
                }
    
                public Action[] getCustomPaletteActions() {
                    return null; //no custom actions for palette's root
                }
            }
                    
  • Use PaletteFactory to create an instance of PaletteController. The editor module keeps a reference to this object and registers a PropertyChangeListner to it to be notified of palette's selection changes.
    Example:
            class MyClass {
                PaletteController controller;
    
                PaletteController initializePalette() {
                    try {
                        controller = PaletteFactory.createPalette( "MyPalette", new MyPaletteActions() );
                    } catch (IOException ex) {
                        ex.printStackTrace();
                        return;
                    }
    
                    controller.addPropertyChangeListener( new PropertyChangeListener() {
                        public void propertyChange(PropertyChangeEvent evt) {
                            if( PaletteController.PROP_SELECTED_ITEM.equals( evt.getPropertyName() ) ) {
                                Lookup selItem = controller.getSelectedItem();
                                if( null == selItem ) {
                                    //nothing is selected in palette
                                } else {
                                    Node selNode = (Node)selItem.lookup( Node.class );
                                    if( null != selNode ) {
                                        //change mouse cursor for editor window to indicate 
                                        //the type of palette item that can be dropped
                                        changeCursorInEditorWindow( selNode );
                                    }
                                }
                            }
                        }
                    });
                    return controller;
                }
            }
                  
  • Add the instance of PaletteController to the lookup of editor's TopComponent.
            class MyEditorTopComponent extends TopComponent {
    
                private MyEditorTopComponent() {
                    this( new InstanceContent() );
                }
    
                private MyEditorTopComponent( InstanceContent content ) {
                    super( new AbstractLookup( content ) );
                    content.add( initializePalette() );
    
                    initEditorComponents();
                }
    
                PaletteController controller;
                private PaletteController initializePalette() {
                    if( null == controller ) {
                        controller = PaletteFactory.createPalette( "MyPalette", new MyPaletteActions() );
                    }
                    return controller;
                }
            }
                  

When an item is selected in the palette and user clicks into the editor window then the module can ask for selected item by calling PaletteController.getSelectedItem(). This method returns a Lookup that holds object(s) representing the selected item. After the item is inserted into the editor window the module may clear palette's selection (PaletteController.clearSelection()) or leave the item selected to implement 'multi drop' insertion scenario.

Filtering

It is possible to filter palette content and hide some categories and/or items from the user by extending PaletteFilter class.

       class MyPaletteFilter extends PaletteFilter {

            public boolean isValidItem(Lookup lookup) {
                Node itemNode = (Node)lookup.lookup( Node.class );
                return isItemVisibleInCurrentEditorContext( itemNode );
            }

            public boolean isValidCategory(Lookup lookup) {
                Node categoryNode = (Node)lookup.lookup( Node.class );
                return isCategoryVisibleInCurrentEditorContext( categoryNode );
            }

            private boolean isItemVisibleInCurrentEditorContext( Node item ) {
                boolean res = true;
                //check current cursor positions and/or item type and decide whether
                //the item is valid, i.e. can be selected and dropped into editor
                return res;
            }

            private boolean isCategoryVisibleInCurrentEditorContext( Node item ) {
                boolean res = true;
                //check current cursor positions and/or category type and decide whether
                //the category is valid, i.e. its items can be selected and dropped into editor
                return res;
            }
          

Then initialize the palette using the following method:

                MyPaletteFilter filter = new MyPaletteFilter();
                PaletteController controller = PaletteFactory.createPalette( "MyPalette", new MyPaletteActions(), filter, null );
          

It is necessary to call PaletteController.refresh() to refresh and repaint the palette window whenever the filtering condition has changed:

              myPaletteFilter.setShowSomeSpecialCategories( false );
              paletteController.refresh();
          

Default Settings

The initial state of the palette can be overridden by setting appropriate attributes to palette model. The list of supported attributes is defined in PaletteController class. If the palette model is create from Nodes then the attributes are extracted by calling Node.getValue() method on the root Node and category and item nodes. If the palette model is defined as folders and files in the layer then the attributes are extracted by calling FileObject.getAttribute().

In the example below the palette will not show item names initially (only icons are visible), the user can change this in palette's context menu. Category1 is read-only therefore the user cannot remove it. Category2 is not initially visible, the user can change this in palette's customizer.

      <filesystem>
          <folder name="MyModulePalette">
              <attr name="showItemNames" stringvalue="false"/>

              <folder name="Category1">
                  <attr name="isReadonly" stringvalue="true"/>

                  <file name="PaletteItem_1.myitem" url="palette/PaletteItem_1.myitem" />
                  <file name="PaletteItem_2.myitem" url="palette/PaletteItem_2.myitem" />
                  <file name="PaletteItem_3.myitem" url="palette/PaletteItem_3.myitem" />
              </folder>

              <folder name="Category2">
                  <attr name="isVisible" stringvalue="false"/>

                  <file name="PaletteItem_4.myitem" url="palette/PaletteItem_4.myitem" />
                  <file name="PaletteItem_5.myitem" url="palette/PaletteItem_5.myitem" />
                  <file name="PaletteItem_6.myitem" url="palette/PaletteItem_6.myitem" />
              </folder>
          </folder>
      </filesystem>
            

Adding categories and items at runtime

It is possible to add new palette categories and/or palette item at runtime when the palette window is already visible.

Adding a new category is very straight-forward, it basically means creating a new folder under palette's root folder in XML layer:

        FileObject paletteRoot = FileUtil.getConfigFile( "MyModulePalette" );
        paletteRoot.createFolder( "NewCategory" );
        

Adding a new item is a similar task:

        FileObject paletteRoot = FileUtil.getConfigFile( "MyPalette" );
        FileObject targetCategoryFO = paletteRoot.getFileObject( "CategoryName" );
        DataFolder targetCategoryDF = DataFolder.findFolder( targetCategoryFO );
        DataObject dobj = (DataObject)itemNode.getLookup().lookup( DataObject.class );
        dobj.copy( targetCategoryFolder );
        

Please refer to Nodes API in case the palette content is defined as a hierarchy of arbitrary Nodes.

Palette content for text-based editors

The following steps must be taken when writing the item using the support provided by this module:

  1. Create XML file with item definition according to the editor-palette-item-1_0.dtd.
  2. Register it in the editor's layer file (see Basic usage).
  3. Provide custom item implementation of the ActiveEditorDrop interface if needed. I must be referenced from the definition file.

Exported Interfaces

This table lists all of the module exported APIs with defined stability classifications. It is generated based on answers to questions about the architecture of the module. Read them all...
Group of java interfaces
Interface NameIn/OutStabilitySpecified in What Document?
PaletteExportedStableoverview-summary.html

Group of dtd interfaces
Interface NameIn/OutStabilitySpecified in What Document?
editor-palette-item-1_0.dtdExportedStable .../dtds/editor-palette-item-1_0.dtd

Group of java.io.File interfaces
Interface NameIn/OutStabilitySpecified in What Document?
org-netbeans-modules-palette.jarExportedStable

Group of layer interfaces
Interface NameIn/OutStabilitySpecified in What Document?
user_settingsExportedPrivate

There's a private XML file for user settings for each palette model.

Group of lookup interfaces
Interface NameIn/OutStabilitySpecified in What Document?
activated_nodeExportedStable

Palette listens to system activated node changes. The palette TopComponent opens when an editor TopComponent with a PaletteController instance in its Lookup is opened or activated. Palette window closes when the editor window is closed or deactivated and no other visible editor window supports the palette.
The palette window always shows the content from the last active editor window regardless where the input focus is. The palette content is updated when user activates a different editor window that supports the palette.

node_representionExportedStable

The palette item implementor can either directly provide the item body or her own item class implementing org.openide.text.ActiveEditorDrop interface.
Lookup that holds object(s) representing the selected item then associates custom item class instance with the org.openide.text.ActiveEditorDrop.class key and the body with java.lang.String key.
Editor side implementor can use the Lookup content whenever the Lookup is given, namely in the editor-provided implementations of the PaletteActions, DragAndDropHandler and PropertyChangeListener (registered on the PaletteController) interfaces.

Implementation Details

Where are the sources for the module?

The sources for the module are in the Apache Git repositories or in the GitHub repositories.

What do other modules need to do to declare a dependency on this one, in addition to or instead of a plain module dependency?

Nothing.

Read more about the implementation in the answers to architecture questions.

Skip navigation links
org.netbeans.spi.palette/1 1.70