/**
 * L2FProd.com Common Components 7.3 License.
 *
 * Copyright 2005-2007 L2FProd.com
 *
 * Licensed 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 com.l2fprod.common.swing;

import com.l2fprod.common.swing.plaf.JTaskPaneGroupAddon;
import com.l2fprod.common.swing.plaf.LookAndFeelAddons;
import com.l2fprod.common.swing.plaf.TaskPaneGroupUI;

import javax.swing.*;
import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

/**
 * <code>JTaskPaneGroup</code> is a container for tasks and other
 * arbitrary components.
 * <p/>
 * <p/>
 * Several <code>JTaskPaneGroup</code>s are usually grouped together within a
 * {@link JTaskPane}. However it is not mandatory
 * to use a JTaskPane as the parent for JTaskPaneGroup. The JTaskPaneGroup can
 * be added to any other container. See
 * {@link JTaskPane} to understand the benefits of
 * using it as the parent container.
 * <p/>
 * <p/>
 * <code>JTaskPaneGroup</code> provides control to expand and
 * collapse the content area in order to show or hide the task list. It can have an
 * <code>icon</code>, a <code>title</code> and can be marked as
 * <code>special</code>. Marking a <code>JTaskPaneGroup</code> as
 * <code>special</code> is only a hint for the pluggable UI which
 * will usually paint it differently (by example by using another
 * color for the border of the pane).
 * <p/>
 * <p/>
 * When the JTaskPaneGroup is expanded or collapsed, it will be
 * animated with a fade effect. The animated can be disabled on a per
 * component basis through {@link #setAnimated(boolean)}.
 * <p/>
 * To disable the animation for all newly created <code>JTaskPaneGroup</code>,
 * use the UIManager property:
 * <code>UIManager.put("TaskPaneGroup.animate", Boolean.FALSE);</code>.
 * <p/>
 * <p/>
 * Example:
 * <pre>
 * <code>
 * JXFrame frame = new JXFrame();
 *
 * // a container to put all JTaskPaneGroup together
 * JTaskPane taskPaneContainer = new JTaskPane();
 *
 * // create a first taskPane with common actions
 * JTaskPaneGroup actionPane = new JTaskPaneGroup();
 * actionPane.setTitle("Files and Folders");
 * actionPane.setSpecial(true);
 *
 * // actions can be added, an hyperlink will be created
 * Action renameSelectedFile = createRenameFileAction();
 * actionPane.add(renameSelectedFile);
 * actionPane.add(createDeleteFileAction());
 *
 * // add this taskPane to the taskPaneContainer
 * taskPaneContainer.add(actionPane);
 *
 * // create another taskPane, it will show details of the selected file
 * JTaskPaneGroup details = new JTaskPaneGroup();
 * details.setTitle("Details");
 *
 * // add standard components to the details taskPane
 * JLabel searchLabel = new JLabel("Search:");
 * JTextField searchField = new JTextField("");
 * details.add(searchLabel);
 * details.add(searchField);
 *
 * taskPaneContainer.add(details);
 *
 * // put the action list on the left
 * frame.add(taskPaneContainer, BorderLayout.EAST);
 *
 * // and a file browser in the middle
 * frame.add(fileBrowser, BorderLayout.CENTER);
 *
 * frame.pack().
 * frame.setVisible(true);
 * </code>
 * </pre>
 *
 * @author <a href="mailto:fred@L2FProd.com">Frederic Lavigne</a>
 * @javabean.attribute name="isContainer"
 * value="Boolean.TRUE"
 * rtexpr="true"
 * @javabean.attribute name="containerDelegate"
 * value="getContentPane"
 * @javabean.class name="JTaskPaneGroup"
 * shortDescription="JTaskPaneGroup is a container for tasks and other arbitrary components."
 * stopClass="java.awt.Component"
 * @javabean.icons mono16="JTaskPaneGroup16-mono.gif"
 * color16="JTaskPaneGroup16.gif"
 * mono32="JTaskPaneGroup32-mono.gif"
 * color32="JTaskPaneGroup32.gif"
 * @see JTaskPane
 * @see JCollapsiblePane
 */
public class JTaskPaneGroup extends JPanel implements JCollapsiblePane.JCollapsiblePaneContainer {

  public final static String UI_CLASS_ID = "TaskPaneGroupUI";

  // ensure at least the default ui is registered
  static {
    LookAndFeelAddons.contribute(new JTaskPaneGroupAddon());
  }

  /**
   * Used when generating PropertyChangeEvents for the "expanded" property
   */
  public static final String EXPANDED_CHANGED_KEY = "expanded";

  /**
   * Used when generating PropertyChangeEvents for the "collapsable" property
   */
  public static final String COLLAPSABLE_CHANGED_KEY = "collapsable";

  /**
   * Used when generating PropertyChangeEvents for the "scrollOnExpand" property
   */
  public static final String SCROLL_ON_EXPAND_CHANGED_KEY = "scrollOnExpand";

  /**
   * Used when generating PropertyChangeEvents for the "title" property
   */
  public static final String TITLE_CHANGED_KEY = "title";

  /**
   * Used when generating PropertyChangeEvents for the "icon" property
   */
  public static final String ICON_CHANGED_KEY = "icon";

  /**
   * Used when generating PropertyChangeEvents for the "special" property
   */
  public static final String SPECIAL_CHANGED_KEY = "special";

  /**
   * Used when generating PropertyChangeEvents for the "animated" property
   */
  public static final String ANIMATED_CHANGED_KEY = "animated";

  private String title;
  private Icon icon;
  private boolean special;
  private boolean expanded = true;
  private boolean scrollOnExpand;
  private boolean collapsable = true;

  private JCollapsiblePane collapsePane;

  /**
   * Creates a new empty <code>JTaskPaneGroup</code>.
   */
  public JTaskPaneGroup() {
    collapsePane = new JCollapsiblePane();
    super.setLayout(new BorderLayout(0, 0));
    super.addImpl(collapsePane, BorderLayout.CENTER, -1);

    updateUI();
    setFocusable(true);
    setOpaque(false);

    // disable animation if specified in UIManager
    setAnimated(!Boolean.FALSE.equals(UIManager.get("TaskPaneGroup.animate")));

    // listen for animation events and forward them to registered listeners
    collapsePane.addPropertyChangeListener(JCollapsiblePane.ANIMATION_STATE_KEY, new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent evt) {
        JTaskPaneGroup.this.firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
      }
    });
  }

  /**
   * Returns the contentPane object for this JTaskPaneGroup.
   *
   * @return the contentPane property
   */
  public Container getContentPane() {
    return collapsePane.getContentPane();
  }

  /**
   * Notification from the <code>UIManager</code> that the L&F has changed.
   * Replaces the current UI object with the latest version from the <code>UIManager</code>.
   *
   * @see javax.swing.JComponent#updateUI
   */
  public void updateUI() {
    // collapsePane is null when updateUI() is called by the "super()"
    // constructor
    if (collapsePane == null) {
      return;
    }
    setUI((TaskPaneGroupUI) LookAndFeelAddons.getUI(this, TaskPaneGroupUI.class));
  }

  /**
   * Sets the L&F object that renders this component.
   *
   * @param ui the <code>TaskPaneGroupUI</code> L&F object
   * @beaninfo bound: true hidden: true description: The UI object that
   * implements the taskpane group's LookAndFeel.
   * @see javax.swing.UIDefaults#getUI
   */
  public void setUI(TaskPaneGroupUI ui) {
    super.setUI(ui);
  }

  /**
   * Returns the name of the L&F class that renders this component.
   *
   * @return the string {@link #UI_CLASS_ID}
   * @see javax.swing.JComponent#getUIClassID
   * @see javax.swing.UIDefaults#getUI
   */
  public String getUIClassID() {
    return UI_CLASS_ID;
  }

  /**
   * Returns the title currently displayed in the border of this pane.
   *
   * @return the title currently displayed in the border of this pane
   * @since 0.2
   */
  public String getTitle() {
    return title;
  }

  /**
   * Sets the title to be displayed in the border of this pane.
   *
   * @param title the title to be displayed in the border of this pane
   * @javabean.property bound="true"
   * preferred="true"
   * @since 0.2
   */
  public void setTitle(String title) {
    String old = title;
    this.title = title;
    firePropertyChange(TITLE_CHANGED_KEY, old, title);
  }

  /**
   * @param text
   * @see #setTitle(String)
   * @deprecated
   */
  public void setText(String text) {
    setTitle(text);
  }

  /**
   * @see #getTitle()
   * @deprecated
   */
  public String getText() {
    return getTitle();
  }

  /**
   * Returns the icon currently displayed in the border of this pane.
   *
   * @return the icon currently displayed in the border of this pane
   */
  public Icon getIcon() {
    return icon;
  }

  /**
   * Sets the icon to be displayed in the border of this pane. Some pluggable
   * UIs may impose size constraints for the icon. A size of 16x16 pixels is
   * the recommended icon size.
   *
   * @param icon the icon to be displayed in the border of this pane
   * @javabean.property bound="true"
   * preferred="true"
   */
  public void setIcon(Icon icon) {
    Icon old = icon;
    this.icon = icon;
    firePropertyChange(ICON_CHANGED_KEY, old, icon);
  }

  /**
   * Returns true if this pane is "special".
   *
   * @return true if this pane is "special"
   */
  public boolean isSpecial() {
    return special;
  }

  /**
   * Sets this pane to be "special" or not. Marking a <code>JTaskPaneGroup</code>
   * as <code>special</code> is only a hint for the pluggable UI which will
   * usually paint it differently (by example by using another color for the
   * border of the pane).
   * <p/>
   * <p/>
   * Usually the first JTaskPaneGroup in a JTaskPane is marked as special
   * because it contains the default set of actions which can be executed given
   * the current context.
   *
   * @param special true if this pane is "special", false otherwise
   * @javabean.property bound="true"
   * preferred="true"
   */
  public void setSpecial(boolean special) {
    if (this.special != special) {
      this.special = special;
      firePropertyChange(SPECIAL_CHANGED_KEY, !special, special);
    }
  }

  /**
   * Should this group be scrolled to be visible on expand.
   *
   * @param scrollOnExpand true to scroll this group to be
   *                       visible if this group is expanded.
   * @javabean.property bound="true"
   * preferred="true"
   * @see #setExpanded(boolean)
   */
  public void setScrollOnExpand(boolean scrollOnExpand) {
    if (this.scrollOnExpand != scrollOnExpand) {
      this.scrollOnExpand = scrollOnExpand;
      firePropertyChange(SCROLL_ON_EXPAND_CHANGED_KEY, !scrollOnExpand, scrollOnExpand);
    }
  }

  /**
   * Should this group scroll to be visible after
   * this group was expanded.
   *
   * @return true if we should scroll false if nothing
   *         should be done.
   */
  public boolean isScrollOnExpand() {
    return scrollOnExpand;
  }

  /**
   * Expands or collapses this group.
   *
   * @param expanded true to expand the group, false to collapse it
   * @javabean.property bound="true"
   * preferred="true"
   */
  public void setExpanded(boolean expanded) {
    if (this.expanded != expanded) {
      this.expanded = expanded;
      collapsePane.setCollapsed(!expanded);
      firePropertyChange(EXPANDED_CHANGED_KEY, !expanded, expanded);
    }
  }

  /**
   * Returns true if this taskpane is expanded, false if it is collapsed.
   *
   * @return true if this taskpane is expanded, false if it is collapsed.
   */
  public boolean isExpanded() {
    return expanded;
  }

  /**
   * Sets whether or not this group can be collapsed by the user
   *
   * @param collapsable false to prevent the group to be manually collapsed
   * @javabean.property bound="true" preferred="true"
   */
  public void setCollapsable(boolean collapsable) {
    if (this.collapsable != collapsable) {
      this.collapsable = collapsable;
      firePropertyChange(COLLAPSABLE_CHANGED_KEY, !collapsable, collapsable);
    }
  }

  /**
   * @return true if this taskpane can be collapsed by the user.
   */
  public boolean isCollapsable() {
    return collapsable;
  }

  /**
   * Enables or disables animation during expand/collapse transition.
   *
   * @param animated
   * @javabean.property bound="true"
   * preferred="true"
   */
  public void setAnimated(boolean animated) {
    if (isAnimated() != animated) {
      collapsePane.setAnimated(animated);
      firePropertyChange(ANIMATED_CHANGED_KEY, !isAnimated(), isAnimated());
    }
  }

  /**
   * Returns true if this taskpane is animated during expand/collapse
   * transition.
   *
   * @return true if this taskpane is animated during expand/collapse
   *         transition.
   */
  public boolean isAnimated() {
    return collapsePane.isAnimated();
  }

  /**
   * Adds an action to this <code>JTaskPaneGroup</code>. Returns a
   * component built from the action. The returned component has been
   * added to the <code>JTaskPaneGroup</code>.
   *
   * @param action
   * @return a component built from the action
   */
  public Component add(Action action) {
    Component c = ((TaskPaneGroupUI) ui).createAction(action);
    add(c);
    return c;
  }

  public Container getValidatingContainer() {
    return getParent();
  }

  /**
   * Overriden to redirect call to the content pane.
   */
  protected void addImpl(Component comp, Object constraints, int index) {
    getContentPane().add(comp, constraints, index);
  }

  /**
   * Overriden to redirect call to the content pane.
   */
  public void setLayout(LayoutManager mgr) {
    if (collapsePane != null) {
      getContentPane().setLayout(mgr);
    }
  }

  /**
   * Overriden to redirect call to the content pane
   */
  public void remove(Component comp) {
    getContentPane().remove(comp);
  }

  /**
   * Overriden to redirect call to the content pane.
   */
  public void remove(int index) {
    getContentPane().remove(index);
  }

  /**
   * Overriden to redirect call to the content pane.
   */
  public void removeAll() {
    getContentPane().removeAll();
  }

  /**
   * Overriden to prevent focus to group header when group is not collapsable
   */
  public boolean isFocusable() {
    return super.isFocusable() && isCollapsable();
  }

  /**
   * @see JComponent#paramString()
   */
  protected String paramString() {
    return super.paramString() + ",title=" + getTitle() + ",icon=" + getIcon() + ",expanded=" + String.valueOf(isExpanded()) + "," +
        "special=" + String.valueOf(isSpecial()) + ",scrollOnExpand=" + String.valueOf(isScrollOnExpand()) + ",ui=" + getUI();
  }

}
