/*
 * Licensed Materials - Property of Perforce Software, Inc. 
 * © Copyright Perforce Software, Inc. 2014, 2021 
 * © 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 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 java.util.List;

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;

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;

/**
 * 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)
     */
    Override
    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;
    }

    Override
    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.
     */
    Override
    public boolean isPublic() {
      return true;
    }

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

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

    /**
     * Returns the description shown in the tool-tip.
     */
    Override
    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.
     */
    Override
    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.
     */
    Override
    public PropertyEditor getPropertyEditor() {
      IlvPaletteSymbolParameter param = getParam();
      if (param != null) {
        return param.getPropertyEditor();
      }
      return null;
    }

    /**
     * Returns the value of the property for the target object.
     */
    Override
    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.
     */
    Override
    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.
     */
    Override
    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.
     */
    Override
    public String getSetDocumentation() {
      return null;
    }

    Override
    public Class<?> getPropertyEditorClass() {
      return null;
    }

    Override
    SuppressWarnings("rawtypes")
    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.
     */
    Override
    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.
     */
    Override
    public void propertyChange(PropertyChangeEvent e) {
      if (locator != null && model != null) {
        model.setObjectProperty(target, LONGITUDE_PROPERTY_NAME, Double.valueOf(locator.getLon()));
        model.setObjectProperty(target, LATITUDE_PROPERTY_NAME, Double.valueOf(locator.getLat()));
      }
    }

    /**
     * This method is overridden to create property descriptors for the symbol
     * 
     * @param target1
     *          The selected symbol.
     */
    Override
    public IlvPropertyDescriptor[] getPropertyDescriptors(Object target1) {
      if (target1 instanceof IlvSDMNode) {
        List<IlvPropertyDescriptor> list = new ArrayList<IlvPropertyDescriptor>();
        // 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 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() {
            Override
            public void componentHidden(ComponentEvent e) {
              Object ans = getSelectionOptionPane().getValue();
              if (Integer.valueOf(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() {
            Override
            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, Double.valueOf(0),
            Double.valueOf(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}
   */
  Override
  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)
   */
  Override
  public void editSymbology(IlvSDMModel model, IlvApplyObject applier) {
    // not implemented
  }

  /**
   * Returns false
   * 
   * @see ilog.views.maps.symbology.swing.IlvSymbologyTreeViewActions#isEditSymbologyEnabled(IlvSDMModel)
   */
  Override
  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()
   */
  Override
  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)
   */
  Override
  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)
   */
  Override
  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() {
      Override
      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() {
          Override
          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() {
          Override
          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", //$NON-NLS-1$
            new PropertyChangeListener() {
              Override
              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() {
          Override
          public void categoryDeselected(PaletteViewerEvent evt) {
            add.setEnabled(false);
          }

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

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

          Override
          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)
   */
  Override
  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)
   */
  Override
  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)
   */
  Override
  public boolean isEditSymbolEnabled(IlvSDMModel model) {
    return true;
  }

  /**
   * Duplicates the symbol
   * 
   * @see ilog.views.maps.symbology.swing.IlvSymbologyTreeViewActions#duplicateSymbol(IlvSDMModel,
   *      java.lang.Object)
   */
  Override
  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}.
   */
  Override
  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)
   */
  Override
  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()
   */
  Override
  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()
   */
  Override
  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)
   */
  Override
  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)
   */
  Override
  public boolean isPropertyIgnored(IlvSDMModel model, Object node, String propertyName) {
    if (SYMBOLINFO_PROPERTY_NAME.equals(propertyName))
      return true;
    return false;
  }
}