/*
 * Licensed Materials - Property of Rogue Wave Software, Inc. 
 * © Copyright Rogue Wave Software, Inc. 2014, 2015 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */
package utils.sdm;

import ilog.views.IlvApplyObject;
import ilog.views.IlvGraphic;
import ilog.views.IlvPoint;
import ilog.views.IlvRect;
import ilog.views.applications.util.beans.editor.StrokeEditor;
import ilog.views.beans.editor.IlvPointEditor;
import ilog.views.beans.editor.IlvRectEditor;
import ilog.views.io.IlvInputStream;
import ilog.views.io.IlvOutputStream;
import ilog.views.io.IlvPersistentObject;
import ilog.views.io.IlvReadFileException;
import ilog.views.maps.IlvMapUtil;
import ilog.views.maps.beans.editor.IlvSymbolLocationEditor;
import ilog.views.maps.format.IlvMapDataPathManager;
import ilog.views.maps.symbology.swing.IlvAbstractGroupTreeViewActions;
import ilog.views.maps.symbology.swing.IlvSymbologyTreeView;
import ilog.views.sdm.IlvSDMEngine;
import ilog.views.sdm.IlvSDMModel;
import ilog.views.sdm.model.IlvDefaultSDMNode;
import ilog.views.sdm.model.IlvSDMNode;
import ilog.views.sdm.renderer.maps.IlvMapRenderer;
import ilog.views.symbology.palettes.IlvPalette;
import ilog.views.symbology.palettes.IlvPaletteManager;
import ilog.views.symbology.palettes.IlvPaletteObject;
import ilog.views.symbology.palettes.IlvPaletteSymbol;
import ilog.views.symbology.palettes.IlvPaletteSymbolParameter;
import ilog.views.symbology.palettes.event.PaletteViewerEvent;
import ilog.views.symbology.palettes.event.PaletteViewerListener;
import ilog.views.symbology.palettes.swing.IlvPaletteDragAndDropData;
import ilog.views.symbology.palettes.swing.IlvPaletteManagerComboboxViewer;
import ilog.views.symbology.palettes.swing.action.IlvPaletteLoadAction;
import ilog.views.util.IlvImageUtil;
import ilog.views.util.beans.IlvPropertyEditorManager;
import ilog.views.util.beans.editor.IlvDoubleArrayPropertyEditor;
import ilog.views.util.beans.editor.IlvFloatArrayPropertyEditor;
import ilog.views.util.beans.editor.IlvIntegerArrayPropertyEditor;
import ilog.views.util.beans.editor.IlvLongArrayPropertyEditor;
import ilog.views.util.beans.editor.IlvStringArrayPropertyEditor;
import ilog.views.util.convert.IlvConvert;
import ilog.views.util.internal.IlvReflection;
import ilog.views.util.psheet.IlvPropertyDescriptor;
import ilog.views.util.psheet.IlvPropertyDescriptorWithEditor;
import ilog.views.util.psheet.IlvPropertySheet;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.Image;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyEditor;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.table.JTableHeader;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellRenderer;

/**
 * The symbol actions in the symbology tree panel to deal with symbols in a palette.
 */
public class PaletteSymbologyTreeViewActions extends IlvAbstractGroupTreeViewActions {
  static {
    // Register property editor for some of the symbol properties.
    IlvPropertyEditorManager.registerEditor(IlvRect.class, IlvRectEditor.class);
    IlvPropertyEditorManager.registerEditor(IlvPoint.class, IlvPointEditor.class);
    IlvPropertyEditorManager.registerEditor(IlvReflection.arrayType(String.class), IlvStringArrayPropertyEditor.class);
    IlvPropertyEditorManager.registerEditor(IlvReflection.arrayType(Double.class), IlvDoubleArrayPropertyEditor.class);
    IlvPropertyEditorManager.registerEditor(IlvReflection.arrayType(Float.class), IlvFloatArrayPropertyEditor.class);
    IlvPropertyEditorManager.registerEditor(IlvReflection.arrayType(Long.class), IlvLongArrayPropertyEditor.class);
    IlvPropertyEditorManager.registerEditor(IlvReflection.arrayType(Integer.class), IlvIntegerArrayPropertyEditor.class);
    IlvPropertyEditorManager.registerEditor(java.awt.Stroke.class, StrokeEditor.class);
    // add additional converters for arrays (useful for chart symbols)
    // JV-4318
    IlvConvert.addConverter(new IlvDoubleArrayPropertyEditor(),IlvReflection.arrayType(double.class));
    IlvConvert.addConverter(new IlvIntegerArrayPropertyEditor(),IlvReflection.arrayType(int.class));
    IlvConvert.addConverter(new IlvFloatArrayPropertyEditor(),IlvReflection.arrayType(float.class));
    IlvConvert.addConverter(new IlvStringArrayPropertyEditor(),IlvReflection.arrayType(String.class));
  }
  /**
   * Class used to store and retrieve the persistent context .
   * This class saves and reloads the palette urls that are currently accessible in the palette manager.
   */
  static public class PaletteContext implements IlvPersistentObject {
    private final String[] paletteURLs;

    private PaletteContext(String[] paletteURLs) {
      this.paletteURLs = paletteURLs;
    }

    /**
     * Reads a list of palette urls from a stream to create a <code>PaletteContext</code>.
     * @param stream stream to read from.
     * @throws IlvReadFileException
     */
    public PaletteContext(IlvInputStream stream) throws IlvReadFileException {
      paletteURLs = stream.readStringArray("URLs");//$NON-NLS-1$
    }

    /**
     * Write the list of palettes.
     * {@inheritDoc}
     * @see ilog.views.io.IlvPersistentObject#write(ilog.views.io.IlvOutputStream)
     */
    public void write(IlvOutputStream stream) throws IOException {
      stream.write("URLs", paletteURLs); //$NON-NLS-1$
    }
  }
  // some specific SDM property names
  final static String TAG_PROPERTY_NAME = "tag"; //$NON-NLS-1$
  final static String SYMBOLINFO_PROPERTY_NAME = "symbolInfo"; //$NON-NLS-1$
  final static String ID_PROPERTY_NAME = "id";//$NON-NLS-1$
  final static String VISIBLE_PROPERTY_NAME = "visible";//$NON-NLS-1$
  final static String LATITUDE_PROPERTY_NAME = IlvMapRenderer.LATITUDE_PROPERTY;
  final static String LONGITUDE_PROPERTY_NAME = IlvMapRenderer.LONGITUDE_PROPERTY;
  /**
   * Specific tree cell renderer that displays the symbol icon.
   */
  private static final class PaletteCellRenderer implements TreeCellRenderer {
    // delegate renderer. Note: we expect it to be a DefaultTreeCellRenderer
    private final TreeCellRenderer delegate;

    private PaletteCellRenderer(TreeCellRenderer or) {
      this.delegate = or;
    }

    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
        boolean leaf, int row, boolean hasFocus) {
      Component c = delegate.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
      if (leaf && value instanceof IlvSDMNode) {
        IlvSDMNode node = ((IlvSDMNode) value);
        // retrieve the symbol definition
        Object v = node.getProperty(SYMBOLINFO_PROPERTY_NAME);
        if (v instanceof IlvPaletteSymbol) {
          // retrieve the symbol icon
          IlvPaletteSymbol symb = (IlvPaletteSymbol) v;
          ImageIcon ic = symb.getSmallIcon();
          if (c instanceof DefaultTreeCellRenderer) {
            // set the icon on the renderer
            ((DefaultTreeCellRenderer) c).setIcon(ic);
          }
          // change the text to only display the id (a bit terser than default)
          String n = node.getID();
          if (n == null) {
            n = node.toString();
          }
          ((DefaultTreeCellRenderer) c).setText(n);
        }
      }
      return c;
    }
  }
  /**
   * Property descriptor used to edit the symbol properties
   */
  private static class SymbolPropertyDescriptor implements IlvPropertyDescriptorWithEditor {
    private final IlvSDMModel model;
    private final Object target;
    private final String name;
    // symbol parameter associated with the property.
    private IlvPaletteSymbolParameter _param;

    /**
     * Creates a new <code>SymbolPropertyDescriptor</code>.
     * @param model model used to retrieve properties
     * @param target the target symbol
     * @param name the name of the property
     */
    public SymbolPropertyDescriptor(IlvSDMModel model, Object target, String name) {
      this.model = model;
      this.target = target;
      this.name = name;
    }

    /**
     * Returns <code>true</code> if the property is public.
     */
    public boolean isPublic() {
      return true;
    }

    /**
     * Returns the name of the property.
     */
    public String getName() {
      return name;
    }

    /**
     * Returns the name that will be displayed in the property sheet.
     */
    public String getDisplayName() {
      return name;
    }

    /**
     * Returns the description shown in the tool-tip.
     */
    public String getShortDescription() {
      IlvPaletteSymbolParameter param = getParam();
      if (param != null) {
        return param.getShortDescription();
      }
      return null;
    }

    private IlvPaletteSymbolParameter getParam() {
      if (_param == null) {
        // retrieve additional information.
        Object v = model.getObjectProperty(target, SYMBOLINFO_PROPERTY_NAME);
        if (v instanceof IlvPaletteSymbol) {
          _param = ((IlvPaletteSymbol) v).getParameter(name);
        }
      }
      return _param;
    }

    /**
     * Returns the class of the property value.
     */
    public Class getPropertyType() {
      try {
        Object v = get(target);
        if (v != null) {
          return v.getClass();
        }
      } catch (Exception ex) {
        ex.printStackTrace();
      }
      return String.class;
    }

    /**
     * Returns the the editor of the property.
     */
    public PropertyEditor getPropertyEditor() {
      IlvPaletteSymbolParameter param = getParam();
      if (param != null) {
        return param.getPropertyEditor();
      }
      return null;
    }

    /**
     * Returns the value of the property for the target object.
     */
    public Object get(Object target1) throws Exception {
      if (name == TAG_PROPERTY_NAME) {
        return model.getTag(target1);
      } else if (name == ID_PROPERTY_NAME) {
        return model.getID(target1);
      } else {
        Object v = model.getObjectProperty(target1, name);
        return v;
      }
    }

    /**
     * Sets the value of the property for the target object.
     */
    public void set(Object target, Object value) throws Exception {
      if (name == ID_PROPERTY_NAME && value != null)
        model.setID(target, value.toString());
      else if (name != TAG_PROPERTY_NAME) {
        model.setObjectProperty(target, name, value);
      }
    }

    /**
     * Returns a <code>String</code> representing a URL where
     * the HTML documentation of the <code>get</code> method of the property is.
     */
    public String getGetDocumentation() {
      return null;
    }

    /**
     * Returns a <code>String</code> representing a URL where
     * the HTML documentation of the <code>set</code> method of the property is.
     */
    public String getSetDocumentation() {
      return null;
    }

    public Class getPropertyEditorClass() {
      return null;
    }

    public void setPropertyEditorClass(Class editorClass) {
      throw new IllegalArgumentException("Cannot set editor for this class"); //$NON-NLS-1$
    }
  }
  /**
   * Property sheet to edit symbol parameters
   */
  static class PropertySheet extends IlvPropertySheet implements PropertyChangeListener {
    private IlvSDMModel model;
    private IlvSymbolLocationEditor locator;

    /**
     * Creates a new <code>PropertySheet</code>.
     * @param locator GUI used to pick coordinates on the map.
     */
    PropertySheet(IlvSymbolLocationEditor locator) {
      super(null);
      this.locator = locator;
      JTable table = getTable();
      // Re-set a default table header of the table, this was set to
      // null by the super class constructor.
      JTableHeader header = new JTableHeader(table.getColumnModel());
      table.setTableHeader(header);
      // Do not allow the column rearrangement.
      header.setReorderingAllowed(false);
    }

    /**
     * Sets the SDM model targeted.
     * @param model the SDM model
     */
    void setModel(IlvSDMModel model) {
      this.model = model;
    }

    /**
     * Initializes the <code>IlvPropertySheet</code> to edit the specified target.
     */
    public void setTarget(Object newTarget) {
      super.setTarget(newTarget);
      if (locator != null && model != null) {
        if (target instanceof IlvSDMNode) {
          locator.removePropertyChangeListener(this);
          Double latr = (Double) model.getObjectProperty(target, LATITUDE_PROPERTY_NAME);
          Double lonr = (Double) model.getObjectProperty(target, LONGITUDE_PROPERTY_NAME);
          locator.setLatLon(latr.doubleValue(), lonr.doubleValue());
          locator.addPropertyChangeListener(this);
        }
        add(locator.getComponent(), BorderLayout.SOUTH);
      }
    }

    /**
     * This method gets called when a the location is changed through the locator.
     */
    public void propertyChange(PropertyChangeEvent e) {
      if (locator != null && model != null) {
        model.setObjectProperty(target, LONGITUDE_PROPERTY_NAME, new Double(locator.getLon()));
        model.setObjectProperty(target, LATITUDE_PROPERTY_NAME, new Double(locator.getLat()));
      }
    }

    /**
     * This method is overridden to create property descriptors for the symbol
     * @param target1 The selected symbol.
     */
    public IlvPropertyDescriptor[] getPropertyDescriptors(Object target1) {
      if (target1 instanceof IlvSDMNode) {
        ArrayList list = new ArrayList();
        //      list.add(new SDMPropertyDescriptor(model, target1, TAG_PROPERTY_NAME));
        //      list.add(new SDMPropertyDescriptor(model, target1, VISIBLE_PROPERTY_NAME));
        list.add(new SymbolPropertyDescriptor(model, target1, ID_PROPERTY_NAME));
        String[] properties = model.getObjectPropertyNames(target1);
        for (int i = 0; i < properties.length; i++) {
          String property = properties[i];
          if (!property.equals(ID_PROPERTY_NAME) && !property.equals(TAG_PROPERTY_NAME)
              && !property.equals(SYMBOLINFO_PROPERTY_NAME) && !property.equals(VISIBLE_PROPERTY_NAME)
              && !property.equals(LONGITUDE_PROPERTY_NAME) && !property.equals(LATITUDE_PROPERTY_NAME)) {
            IlvPropertyDescriptor desc = new SymbolPropertyDescriptor(model, target1, property);
            list.add(desc);
          }
        }
        return (IlvPropertyDescriptor[]) list.toArray(new SymbolPropertyDescriptor[0]);
      }
      return super.getPropertyDescriptors(target1);
    }
  }
  /**
   * GUI to create and edit symbols from a set of palettes.
   */
  static class PaletteSymbologyEditPanel {
    // symbol property sheet for edition
    private final PropertySheet editSheet;
    // SDM Engine
    private final IlvSDMEngine engine;
    // current symbol beeing edited/created
    private Object currentNode;
    // main panel
    private JPanel _mainPanel;
    // palette manager that loads & manages palettes
    private final IlvPaletteManager paletteManager;
    // GUI to select symbol definitions in the various palettes
    private IlvPaletteManagerComboboxViewer _paletteViewer;
    // factory for the palette selection dialog 
    private JOptionPane _paletteDialogPane;
    // factory for the symbol property sheet dialog 
    private JOptionPane _editSheetDialogPane;
    // the palette selection dialog 
    private JDialog _paletteDialog;
    // the symbol property sheet dialog 
    private JDialog _editSheetDialog;

    /**
     * Creates a new <code>PaletteSymbologyEditPanel</code>.
     * @param engine SDM Engine
     * @param paletteManager palette Manager
     * @param locator lat/lon picker
     */
    PaletteSymbologyEditPanel(IlvSDMEngine engine, IlvPaletteManager paletteManager, IlvSymbolLocationEditor locator) {
      this.editSheet = new PropertySheet(locator);
      this.engine = engine;
      this.paletteManager = paletteManager;
    }

    /**
     * Create the various GUIs
     */
    private void createGUI() {
      _paletteViewer = new IlvPaletteManagerComboboxViewer(paletteManager);
      _paletteViewer.setSearchToolVisisble(false);
      _mainPanel = new JPanel(new BorderLayout());
      _mainPanel.add(_paletteViewer.getComponent(), BorderLayout.CENTER);
      _paletteDialogPane = new JOptionPane(_mainPanel, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
      _editSheetDialogPane = new JOptionPane(editSheet, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION);
    }

    /**
     * Sets the target node to be created/edited
     * @param symbol
     */
    void setSymbol(Object node) {
      this.currentNode = node;
    }

    /**
     * @return The main palette selection panel
     */
    JPanel getMainPanel() {
      if (_mainPanel == null) {
        createGUI();
      }
      return _mainPanel;
    }

    /**
     * @return the palette selection pane
     */
    private JOptionPane getSelectionOptionPane() {
      if (_paletteDialogPane == null) {
        createGUI();
      }
      return _paletteDialogPane;
    }

    /**
     * @return the property edition pane
     */
    private JOptionPane getPropertyOptionPane() {
      if (_editSheetDialogPane == null) {
        createGUI();
      }
      return _editSheetDialogPane;
    }

    /**
     * @return the palette viewer
     */
    IlvPaletteManagerComboboxViewer getSelectionPaletteViewer() {
      if (_paletteViewer == null) {
        createGUI();
      }
      return _paletteViewer;
    }

    /**
     * Shows a palette selection dialog
     * @param view
     * @param applier
     */
    void showDialog(final IlvSymbologyTreeView view, final IlvApplyObject applier) {
      if (currentNode == null) {// creation
        if (_paletteDialog == null) {
          _paletteDialog = getSelectionOptionPane().createDialog(
              view,
              IlvMapUtil.getString(PaletteSymbologyTreeViewActions.class,
                  "PaletteSymbologyTreeViewActions.selectionTitle")); //$NON-NLS-1$
          _paletteDialog.setModal(false);
          _paletteDialog.addComponentListener(new ComponentAdapter() {
            public void componentHidden(ComponentEvent e) {
              Object ans = getSelectionOptionPane().getValue();
              if (new Integer(JOptionPane.OK_OPTION).equals(ans)) {
                createSelectedSymbol(view, applier);
              }
            }
          });
        }
        _paletteDialog.setVisible(true);
      } else {
        // edition
        editSheet.setModel(engine.getModel());
        editSheet.setTarget(null);
        editSheet.setTarget(currentNode);
        if (_editSheetDialog == null) {
          _editSheetDialog = getPropertyOptionPane().createDialog(
              view,
              IlvMapUtil.getString(PaletteSymbologyTreeViewActions.class,
                  "PaletteSymbologyTreeViewActions.editionTitle")); //$NON-NLS-1$
          _editSheetDialog.setModal(false);
          // JV-3575
          _editSheetDialog.addComponentListener(new ComponentAdapter() {
            public void componentHidden(ComponentEvent e) {
                applier.apply(null,currentNode);
            }
          });
        }
        _editSheetDialog.setVisible(true);
      }
    }

    /**
     * Creates a new symbol and initializes its properties
     * @param view
     * @param applier
     */
    private void createSelectedSymbol(final IlvSymbologyTreeView view, final IlvApplyObject applier) {
      IlvPaletteSymbol paletteSymbol = getSelectionPaletteViewer().getSelectedSymbol();
      if (paletteSymbol != null) {
        engine.setAdjusting(true);
        currentNode = view.getSymbologyTreeViewActions().newSymbol(paletteSymbol.getFullID());
        if (applier != null) {
          applier.apply(null, currentNode);
        }
        SDMPaletteSymbolCreator
            .setupSymbol(currentNode, engine.getModel(), paletteSymbol, new Double(0), new Double(0));
        engine.setAdjusting(false);
      }
    }
  }
  /**
   * Indicates whether the palette selection should be embedded in the tree view or shown as a new dialog
   */
  static public boolean useDialog = false;
  /**
   * The object in charge of symbol creation and edition
   */
  private PaletteSymbologyEditPanel customAddPanel;
  /**
   * The object managing the list of current loaded palettes
   */
  private IlvPaletteManager _paletteManager;
  private IlvPaletteManager getPaletteManager() {
      if (_paletteManager==null){
          _paletteManager = new IlvPaletteManager();
          if(getView()!=null) {
              _paletteManager.attach(getView().getSymbologyEngine());
              try {
                _paletteManager.load("ilog/views/palettes/shared/"); //$NON-NLS-1$
              } catch (IOException e) {
                e.printStackTrace();
              }
          }

      }
      return _paletteManager;
  }
  private boolean hasPaletteManager() {
      return _paletteManager!=null;
  }
  /**
   * lat/lon picker
   */
  private IlvSymbolLocationEditor locator;

  /**
   * {@inheritDoc}
   */
  public Object newSymbol(String tag) {
    IlvSDMEngine eng = getView().getSymbologyEngine();
    IlvPaletteManager pm = getPaletteManager();
    for (int i = 0; i < pm.getPaletteCount(); i++) {
      IlvPalette p = pm.getPalette(i);
      // finds the palette which contains the right tag.
      IlvPaletteSymbol paletteSymbol = p.getSymbol(tag);
      if (paletteSymbol != null) {
        // creates the symbol
        SDMPaletteSymbolCreator creator = new SDMPaletteSymbolCreator(eng, paletteSymbol);
        return creator.createSymbol();
      }
    }
    return new IlvDefaultSDMNode(tag);
  }

  /**
   * Creates actions to manage  symbol tree.
   */
  public PaletteSymbologyTreeViewActions() {
    // nothing to do
  }

  /**
   * Not implemented
   * 
   * @see ilog.views.maps.symbology.swing.IlvSymbologyTreeViewActions#editSymbology(IlvSDMModel,
   *      ilog.views.IlvApplyObject)
   */
  public void editSymbology(IlvSDMModel model, IlvApplyObject applier) {
    // not implemented
  }

  /**
   * Returns false
   * 
   * @see ilog.views.maps.symbology.swing.IlvSymbologyTreeViewActions#isEditSymbologyEnabled(IlvSDMModel)
   */
  public boolean isEditSymbologyEnabled(IlvSDMModel model) {
    return false;
  }

  /**
   * The  Symbology is not supposed to change on the fly. This method does
   * nothing.
   * 
   * @see ilog.views.maps.symbology.swing.IlvSymbologyTreeViewActions#actOnSymbologyChanged()
   */
  public void actOnSymbologyChanged() {
    // this should not happen
  }
  
 /**
  * The view changed, we need to attach the palette manager
  * {@inheritDoc}
  * @see ilog.views.maps.symbology.swing.IlvAbstractGroupTreeViewActions#setView(ilog.views.maps.symbology.swing.IlvSymbologyTreeView)
  */
  public void setView(IlvSymbologyTreeView treeView) {
    super.setView(treeView);
    treeView.getTree().setCellRenderer(new PaletteCellRenderer(treeView.getTree().getCellRenderer()));
    if (hasPaletteManager()){
        getPaletteManager().attach(treeView.getSymbologyEngine());
        try {
          if (getPaletteManager().getPaletteCount() == 0) {
              getPaletteManager().load("ilog/views/palettes/shared/"); //$NON-NLS-1$
          }
        } catch (IOException e) {
          e.printStackTrace();
        }
    }
  }

  /**
   * Displays a symbol creation dialog. Adds the symbol in the tree if validated
   * by the user.
   * 
   * @see ilog.views.maps.symbology.swing.IlvSymbologyTreeViewActions#createSymbol(IlvSDMModel,
   *      java.lang.Object, ilog.views.IlvApplyObject)
   */
  public void createSymbol(final IlvSDMModel model, final Object group, final IlvApplyObject applier) {
    // add the object in the model once the add dialog has finished.
    final IlvApplyObject applier2 = new IlvApplyObject() {
      public void apply(IlvGraphic obj, Object node) {
        model.addObject(node, group, null);
        model.getID(node);
        applier.apply(obj, node);
      }
    };
    if (customAddPanel == null) {
      // create the component to manage symbol edition/creation GUI
      customAddPanel = new PaletteSymbologyEditPanel(getView().getSymbologyEngine(), getPaletteManager(), getLatLonPicker());
      JComponent palettePanel = customAddPanel.getMainPanel();
      if (!useDialog) {
        // create a palette selection panel
        JToolBar buttonPanel = new JToolBar();
        // close panel button
        JButton close = new JButton(IlvMapUtil.getString(PaletteSymbologyTreeViewActions.class,
            "PaletteSymbologyTreeViewActions.closeLabel")); //$NON-NLS-1$
        close.setToolTipText(IlvMapUtil.getString(PaletteSymbologyTreeViewActions.class,
            "PaletteSymbologyTreeViewActions.closeTip")); //$NON-NLS-1$
        try {
          Image image = IlvImageUtil.getImageFromFile(PaletteSymbologyTreeViewActions.class, "/icons/close_icon.gif"); //$NON-NLS-1$
          close.setIcon(new ImageIcon(image));
        } catch (IOException e1) {
          // ignore
        }
        close.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            customAddPanel.getMainPanel().setVisible(false);
          }
        });
        // add symbol button
        final JButton add = new JButton(IlvMapUtil.getString(PaletteSymbologyTreeViewActions.class,
            "PaletteSymbologyTreeViewActions.addLabel")); //$NON-NLS-1$
        add.setToolTipText(IlvMapUtil.getString(PaletteSymbologyTreeViewActions.class,
            "PaletteSymbologyTreeViewActions.addTip")); //$NON-NLS-1$
        try {
          Image image = IlvImageUtil.getImageFromFile(PaletteSymbologyTreeViewActions.class,
              "/ilog/views/maps/symbology/swing/images/add.gif"); //$NON-NLS-1$
          add.setIcon(new ImageIcon(image));
        } catch (IOException e1) {
          // ignore
        }
        add.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            customAddPanel.createSelectedSymbol(getView(), applier2);
          }
        });
        add.setEnabled(false);
        
        // Palette unload button
        /* Uncomment this if you want to provide an unload palette button.
        final JButton pbb = new JButton(IlvMapUtil.getString(PaletteSymbologyTreeViewActions.class,
            "PaletteSymbologyTreeViewActions.removePalette")); //$NON-NLS-1$
        pbb.setToolTipText(IlvMapUtil.getString(PaletteSymbologyTreeViewActions.class,
            "PaletteSymbologyTreeViewActions.removePaletteTip")); //$NON-NLS-1$
        try {
          Image image = IlvImageUtil.getImageFromFile(PaletteSymbologyTreeViewActions.class,"/icons/removepalette.gif"); //$NON-NLS-1$
          pbb.setIcon(new ImageIcon(image));
        } catch (IOException e1) {
          // ignore
        }
        pbb.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            IlvPalette palette=customAddPanel.getSelectionPaletteViewer().getSelectedPalette();
            if(palette!=null) {
            // TODO we should test here that the palette is no longer used or this may cause problem at reload time
              paletteManager.remove(palette);
            }
          }
        });
     
        */
        // manage palette selection to enable/disable add button
        customAddPanel.getSelectionPaletteViewer().addPropertyChangeListener("current_palette",new PropertyChangeListener() { //$NON-NLS-1$
          public void propertyChange(PropertyChangeEvent evt) {
            // pbb.setEnabled(evt.getNewValue()!=null);
            add.setEnabled(false);
          }          
        });
        // manage symbol/group selection to enable/disable add button
        customAddPanel.getSelectionPaletteViewer().addPaletteViewerListener(new PaletteViewerListener() {
          public void categoryDeselected(PaletteViewerEvent evt) {
            add.setEnabled(false);
          }

          public void categorySelected(PaletteViewerEvent evt) {
            add.setEnabled(false);
          }

          public void symbolDeselected(PaletteViewerEvent evt) {
            add.setEnabled(false);
          }

          public void symbolSelected(PaletteViewerEvent evt) {
            add.setEnabled(true);
          }
        });
        // Palette load button
        JButton pba = new JButton(new IlvPaletteLoadAction(palettePanel, getPaletteManager()));
        Insets insets = new Insets(0, 0, 0, 0);
        close.setMargin(insets);
        add.setMargin(insets);
        pba.setMargin(insets);
        // pbb.setMargin(insets);
        buttonPanel.add(add);
        buttonPanel.add(pba);
        // buttonPanel.add(pbb);
        buttonPanel.add(new JComponent() {/* empty separator*/
        });
        buttonPanel.add(close);
        buttonPanel.setFloatable(false);
        palettePanel.add(buttonPanel, BorderLayout.SOUTH);
        palettePanel.setBorder(BorderFactory.createLoweredBevelBorder());
        // add the panel in the tree view.
        getView().add(
                palettePanel,
                new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, insets,
                    0, 0));
        getView().updateUI();
      } else {
        // add the load palette button to the dialog
        JButton pb = new JButton(new IlvPaletteLoadAction(palettePanel, getPaletteManager()));
        palettePanel.add(pb, BorderLayout.SOUTH);
      }
    }
    // we are creating a new symbol
    customAddPanel.setSymbol(null);
    if (useDialog) {
      // show the dialog
      customAddPanel.showDialog(getView(), applier2);
    } else {
      // make the panel visible
      customAddPanel.getMainPanel().setVisible(true);
    }
  }

  /**
   * Returns true
   * 
   * @see ilog.views.maps.symbology.swing.IlvSymbologyTreeViewActions#isCreateSymbolEnabled(IlvSDMModel)
   */
  public boolean isCreateSymbolEnabled(IlvSDMModel model) {
    return true;
  }

  /**
   * Shows an  symbol edition dialog
   * 
   * @see ilog.views.maps.symbology.swing.IlvSymbologyTreeViewActions#editSymbol(IlvSDMModel,
   *      java.lang.Object, ilog.views.IlvApplyObject)
   */
  public void editSymbol(IlvSDMModel model, Object node, IlvApplyObject applier) {
    if (customAddPanel == null) {
      customAddPanel = new PaletteSymbologyEditPanel(getView().getSymbologyEngine(), getPaletteManager(), getLatLonPicker());
    }
    customAddPanel.setSymbol(node);
    customAddPanel.showDialog(getView(), applier);
  }

  /**
   * Returns true
   * 
   * @see ilog.views.maps.symbology.swing.IlvSymbologyTreeViewActions#isEditSymbolEnabled(IlvSDMModel)
   */
  public boolean isEditSymbolEnabled(IlvSDMModel model) {
    return true;
  }

  /**
   * Duplicates the symbol
   * 
   * @see ilog.views.maps.symbology.swing.IlvSymbologyTreeViewActions#duplicateSymbol(IlvSDMModel,
   *      java.lang.Object)
   */
  public Object duplicateSymbol(IlvSDMModel model, Object node) {
    IlvSDMNode src = (IlvSDMNode) node;
    // create a new symbol with the same tag
    IlvSDMNode rtn = (IlvSDMNode) model.createNode(src.getTag());
    String[] names = src.getPropertyNames();
    // add the object in the model
    model.addObject(rtn, model.getParent(src), null);
    // copy all the properties
    for (int i = 0; i < names.length; i++) {
      model.setObjectProperty(rtn, names[i], src.getProperty(names[i]));
    }
    model.getID(rtn);
    return rtn;
  }

  /**
   * {@inheritDoc}
   * This method expects dropped data that is an instance of {link IlvPaletteDragAndDropData}.
   */
  public Object dropSymbolAt(IlvSDMModel model, Object data, Double lonRad, Double latRad) {
    if (data instanceof IlvPaletteDragAndDropData) {
      IlvPaletteDragAndDropData pdd = (IlvPaletteDragAndDropData) data;
      // get the symbol definition
      IlvPaletteObject po = pdd.getPaletteObject();
      if (po instanceof IlvPaletteSymbol) {
        IlvPaletteSymbol paletteSymbol = (IlvPaletteSymbol) po;
        model.setAdjusting(true);
        // create a new symbol
        Object currentNode = newSymbol(paletteSymbol.getFullID());
        // add it into the model
        model.addObject(currentNode, null, null);
        model.getID(currentNode);
        // set its default properties
        SDMPaletteSymbolCreator.setupSymbol(currentNode, model, paletteSymbol, lonRad, latRad);
        model.setAdjusting(false);
        if (customAddPanel != null && customAddPanel._paletteDialog != null) {
          customAddPanel._paletteDialog.setVisible(false);
        }
        return currentNode;
      }
    }
    return null;
  }

  /**
   * Returns true
   * 
   * @see ilog.views.maps.symbology.swing.IlvSymbologyTreeViewActions#isDuplicateSymbolEnabled(IlvSDMModel)
   */
  public boolean isDuplicateSymbolEnabled(IlvSDMModel model) {
    return true;
  }

  /**
   * Sets the locator to use to pick a point interactively.
   * @param locator
   *          locator to use.
   */
  public void setLatLonPicker(IlvSymbolLocationEditor locator) {
    this.locator = locator;
  }

  /** Returns  the locator used.
    * @return  the locator used.
   */
  public IlvSymbolLocationEditor getLatLonPicker() {
    return locator;
  }
  /**
   * The string displayed in the menu 
   * @see CombinedTreeViewActions
   * {@inheritDoc}
   * @see java.lang.Object#toString()
   */
  public String toString() {
    return IlvMapUtil.getString(PaletteSymbologyTreeViewActions.class, "PaletteSymbologyTreeViewActions.symbolType"); //$NON-NLS-1$
  }
/**
 * Retrieve the persistent context attached to these actions
 * {@inheritDoc}
 * @see ilog.views.maps.symbology.swing.IlvPersistentSDMNodeFactory#getPersistentContext()
 */
  public IlvPersistentObject getPersistentContext() {
    if (! hasPaletteManager() || getPaletteManager().getPaletteCount() == 0) {
      return null;
    }
    String urls[] = new String[getPaletteManager().getPaletteCount() - 1];
    if (urls.length == 0)
      return null;
    for (int i = 0; i < urls.length; i++) {
      IlvPalette palette = getPaletteManager().getPalette(i + 1);
      urls[i] = palette.getJarURL().toExternalForm();
    }
    return new PaletteContext(urls);
  }
/**
 * Sets the persistent context for these actions: this means loading the saved palettes.
 * {@inheritDoc}
 * @see ilog.views.maps.symbology.swing.IlvPersistentSDMNodeFactory#setPersistentContext(ilog.views.io.IlvPersistentObject)
 */
  public void setPersistentContext(IlvPersistentObject context) {
    if (context instanceof PaletteContext) {
      PaletteContext paletteContext = (PaletteContext) context;
      for (int i = 0; i < paletteContext.paletteURLs.length; i++) {
        try {
          URL url=IlvMapDataPathManager.ResolveURL(paletteContext.paletteURLs[i]);
          getPaletteManager().load(url);
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }
  /**
   * At read time, we do not want the saved property information (a string) to replace the symbol definition set in {@link #newSymbol(String)}
   * {@inheritDoc}
   * @see ilog.views.maps.symbology.swing.IlvPersistentSDMNodeFactory#isPropertyIgnored(ilog.views.sdm.IlvSDMModel, java.lang.Object, java.lang.String)
   */
    public boolean isPropertyIgnored(IlvSDMModel model, Object node, String propertyName) {
    if (SYMBOLINFO_PROPERTY_NAME.equals(propertyName))
      return true;
    return false;
  }
}