/*
    VLDocking Framework 2.1
    Copyright VLSOLUTIONS, 2004-2006

    email : info@vlsolutions.com
------------------------------------------------------------------------
This software is distributed under the CeCILL license, a GNU GPL-compatible
license adapted to french law.
French and English license headers are provided at the begining of
the source files of this software application.
------------------------------------------------------------------------
LICENCE CeCILL (FRENCH VERSION).
------------------------------------------------------------------------
Ce logiciel est un programme informatique servant à améliorer les interfaces
homme-machine d'applications Java basées sur Swing, en leur apportant un
ensemble de fonctions relatives au dockage des composants.

Ce logiciel est régi par la licence CeCILL soumise au droit français et
respectant les principes de diffusion des logiciels libres. Vous pouvez
utiliser, modifier et/ou redistribuer ce programme sous les conditions
de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
sur le site "http://www.cecill.info".

En contrepartie de l'accessibilité au code source et des droits de copie,
de modification et de redistribution accordés par cette licence, il n'est
offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,
seule une responsabilité restreinte pèse sur l'auteur du programme,  le
titulaire des droits patrimoniaux et les concédants successifs.

A cet égard  l'attention de l'utilisateur est attirée sur les risques
associés au chargement,  à l'utilisation,  à la modification et/ou au
développement et à la reproduction du logiciel par l'utilisateur étant
donné sa spécificité de logiciel libre, qui peut le rendre complexe à
manipuler et qui le réserve donc à des développeurs et des professionnels
avertis possédant  des  connaissances  informatiques approfondies.  Les
utilisateurs sont donc invités à charger  et  tester  l'adéquation  du
logiciel à leurs besoins dans des conditions permettant d'assurer la
sécurité de leurs systèmes et ou de leurs données et, plus généralement,
à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.

Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
pris connaissance de la licence CeCILL, et que vous en avez accepté les
termes.

------------------------------------------------------------------------
CeCILL License (ENGLISH VERSION)
------------------------------------------------------------------------

This software is a computer program whose purpose is to enhance Human-Computer
Interfaces written in Java with the Swing framework, providing them a set of
functions related to component docking.

This software is governed by the CeCILL  license under French law and
abiding by the rules of distribution of free software.  You can  use,
modify and/ or redistribute the software under the terms of the CeCILL
license as circulated by CEA, CNRS and INRIA at the following URL
"http://www.cecill.info".

As a counterpart to the access to the source code and  rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty  and the software's author,  the holder of the
economic rights,  and the successive licensors  have only  limited
liability.

In this respect, the user's attention is drawn to the risks associated
with loading,  using,  modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean  that it is complicated to manipulate,  and  that  also
therefore means  that it is reserved for developers  and  experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or
data to be ensured and,  more generally, to use and operate it in the
same conditions as regards security.

The fact that you are presently reading this means that you have had
knowledge of the CeCILL license and that you accept its terms.

*/


package com.vlsolutions.swing.docking;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.beans.PropertyChangeEvent;
import java.awt.event.MouseEvent;
import java.awt.Component;
import java.awt.Point;
import java.util.*;

/** A title bar, associated to a DockView (container of a single user component).
 * <p>
 * Here is an example of a title bar : <img src="doc-files/titlebar.gif">.
 * <p>
 * DockViewTitleBar is able to display the following properties of a DockKey :
 * <ul>
 * <li> Icon
 * <li> Name
 * <li> Tooltip
 * <li> Notification (blinking background).
 * <li> And some buttons if their corresponding Action contains the
 * </ul>
 * <p>
 * This title bar supports buttons used for docking features :
 * <table border="1"><tr><th> function </th><th>Version 1.1</th><th>Version 2.0</th></tr>
 *<tr><td>maximize</td><td><img src="doc-files/maximize16.gif"></td><td><img src="doc-files/maximize16v2rollover.png"></td></tr>
 *<tr><td>restore</td><td><img src="doc-files/restore16.gif"></td><td><img src="doc-files/restore16v2rollover.png"></td></tr>
 *<tr><td>hide</td><td><img src="doc-files/hide16.gif"></td><td><img src="doc-files/hide16v2rollover.png"></td></tr>
 *<tr><td>dock</td><td><img src="doc-files/dock16.gif"></td><td><img src="doc-files/dock16v2rollover.png"></td></tr>
 *<tr><td>close</td><td><img src="doc-files/close16.gif"></td><td><img src="doc-files/close16v2rollover.png"></td></tr>
 *<tr><td>float (detach)</td><td>n/a </td><td><img src="doc-files/float16v2rollover.png"></td></tr>
 *<tr><td>attach</td><td>n/a </td><td><img src="doc-files/attach16v2rollover.png"></td></tr>
 * </table>
 *
 * <p>
 * The buttons managed have no effect on the state of the dockable : they just fire
 * property change events, and it is the responsibility of the DockableContainer to
 * listen to those events and to relay the operation to the docking desktop.
 * <p>
 * Note : the UI Delegate of the DockViewTitleBar is the {@link com.vlsolutions.swing.docking.ui.DockViewTitleBarUI}
 *
 * @author Lilian Chamontin, vlsolutions.
 * @version 1.0
 */
public class DockViewTitleBar extends JPanel implements DockableDragSource {
  
  /** Property name designating the autohide button selection */
  public static final String PROPERTY_AUTOHIDE = "DockTitle.AUTOHIDE";
  
  /** Property name designating the close button selection */
  public static final String PROPERTY_CLOSED = "DockTitle.CLOSED";
  
  /** Property name designating a drag gesture beginning */
  public static final String PROPERTY_DRAGGED = "DockTitle.DRAGGED";
  
  /** Property name designating the maximized button selection */
  public static final String PROPERTY_MAXIMIZED = "DockTitle.MAXIMIZED";
  
  /** Property name designating the float button selection */
  public static final String PROPERTY_FLOAT = "DockTitle.FLOAT";
  
  
  private static final String uiClassID = "DockViewTitleBarUI";
  
  private static final String CLOSE_TEXT = UIManager.getString("DockViewTitleBar.closeButtonText");
  private static final String ICONIFY_TEXT = UIManager.getString("DockViewTitleBar.minimizeButtonText");
  private static final String RESTORE_TEXT = UIManager.getString("DockViewTitleBar.restoreButtonText");
  private static final String MAXIMIZE_TEXT = UIManager.getString("DockViewTitleBar.maximizeButtonText");
  private static final String FLOAT_TEXT = UIManager.getString("DockViewTitleBar.floatButtonText");
  private static final String ATTACH_TEXT = UIManager.getString("DockViewTitleBar.attachButtonText");
  
  private static Icon closeIcon = UIManager.getIcon("DockViewTitleBar.menu.close");
  private static Icon maximizeIcon = UIManager.getIcon("DockViewTitleBar.menu.maximize");
  private static Icon restoreIcon = UIManager.getIcon("DockViewTitleBar.menu.restore");
  private static Icon hideIcon = UIManager.getIcon("DockViewTitleBar.menu.hide");
  private static Icon dockIcon = UIManager.getIcon("DockViewTitleBar.menu.dock");
  private static Icon floatIcon = UIManager.getIcon("DockViewTitleBar.menu.float");
  private static Icon attachIcon = UIManager.getIcon("DockViewTitleBar.menu.attach");
  
  // ------------ member fields --------------
  
  // this creation tricks to manage the UI class requiring access to the components
  // before they are created
  private JLabel titleLabel = getTitleLabel();
  
  private JButton closeButton = getCloseButton();
  private JButton dockButton = getHideOrDockButton();
  private JButton maximizeButton = getMaximizeOrRestoreButton();
  private JButton floatButton = getFloatButton();
  private boolean active;
  
  private ActionListener actionListener = new ActionListener(){
    public void actionPerformed(ActionEvent e){
      if (e.getActionCommand().equals("dock")){
        dockAction();
      } else if (e.getActionCommand().equals("close")){
        closeAction();
      } else if (e.getActionCommand().equals("maximize")){
        maximizeAction();
      } else if (e.getActionCommand().equals("float")){
        floatAction();
      }
    }
  };
  
  
  private Dockable target; // the component this title is for
  
  /** used to have a state of blinking (notification) */
  private boolean isNotification = false;
  
  private JPopupMenu currentPopUp = null;
  
  /** current blinking count (the limit to the notification timer) */
  private int blinkCount = 0;
  
  private int MAX_BLINKS = UIManager.getInt("DockingDesktop.notificationBlinkCount");
  
  /** reacts to single and double click on title bar */
  private MouseListener titleMouseListener = new MouseAdapter() {
    public void mouseClicked(MouseEvent e) {
      if (e.getClickCount() == 2) {
        maximizeAction();
      } else {
        //requestFocus();
        // replaced by a better focus behaviour for 1.2
        target.getComponent().requestFocus();
      }
    }
    public void mousePressed(MouseEvent e){
      if (e.isPopupTrigger()){
        checkForPopUp(e);
      }
    }
    public void mouseReleased(MouseEvent e){
      if (e.isPopupTrigger()){
        checkForPopUp(e);
      }
    }
    
    
  };
  
  
  /** listen to the key changes */
  private PropertyChangeListener dockKeyListener = new PropertyChangeListener() {
    public void propertyChange(PropertyChangeEvent e) {
      String pName = e.getPropertyName();
      if (pName.equals(DockKey.PROPERTY_ICON)) {
        titleLabel.setIcon( (Icon) e.getNewValue());
      } else if (pName.equals(DockKey.PROPERTY_NAME)) {
        titleLabel.setText( (String) e.getNewValue());
        revalidate();
      } else if (pName.equals(DockKey.PROPERTY_TOOLTIP)) {
        setToolTipText( (String) e.getNewValue());
      } else if (pName.equals(DockKey.PROPERTY_NOTIFICATION)) {
        // attract user attention
        boolean isOn = ( (Boolean) e.getNewValue()).booleanValue();
        if (isOn && !isActive()) {
          if (notificationTimer == null) {
            notificationTimer = new javax.swing.Timer(1000,
                new ActionListener() {
              public void actionPerformed(ActionEvent actionEvent) {
                setNotification(!isNotification);
                if (!isNotification){
                  blinkCount ++;
                  if (blinkCount >= MAX_BLINKS){
                    blinkCount = 0;
                    notificationTimer.stop(); // enough blinking
                  }
                }
              }
            });
          }
          notificationTimer.restart();
        } else {
          if (notificationTimer != null) {
            notificationTimer.stop();
            blinkCount = 0;
          }
          setNotification(false);
        }
      }
      /*else if (pName.equals(DockKey.PROPERTY_DOCKABLE_STATE)){
       *
        int newState = ((Integer)e.getNewValue()).intValue();
        int oldState = ((Integer)e.getOldValue()).intValue();
        switch (newState){
          case DockableState.STATE_HIDDEN :
            setAutoHide(true);
            break;
          case DockableState.STATE_MAXIMIZED :
            setMaximized(true);
            break;
          case DockableState.STATE_DOCKED :
            if (oldState == DockableState.STATE_HIDDEN){
              setAutoHide(false);
            }
            break;
          default:
            //@todo : managed the floatable state
            // nothing to track here
        }
      }*/
    }
  };
  
  /** Timer used to trigger repaint event for notification (blinking title bar)  */
  private javax.swing.Timer notificationTimer;
  
  /** singleton for keyboard management */
  private static FocusHighlighter focusHighlighter = new FocusHighlighter(); //2005/11/10

  private DockingDesktop desktop;
  
  
  /** Constructs an empty title bar (no dockable yet associated).
   */
  public DockViewTitleBar() {
    this(null);
  }
  
  /** Constructs a title bar for the specified dockable.
   *<p>
   * Warning : a DockViewTitleBar can be used with multiple dockables (this is the case 
   * for example in autohide borders, where a single titlebar is shared by all hidden dockables
   * (shown only when one is expanding).
   */
  public DockViewTitleBar(Dockable dockable) {
    setDockable(dockable);
    
    setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
    setOpaque(true);
    
    
    closeButton.setText("");
    dockButton.setText("");
    maximizeButton.setText("");
    closeButton.setActionCommand("close");
    dockButton.setActionCommand("dock");
    maximizeButton.setActionCommand("maximize");
    floatButton.setActionCommand("float");
    
    closeButton.addActionListener(actionListener);
    dockButton.addActionListener(actionListener);
    maximizeButton.addActionListener(actionListener);
    floatButton.addActionListener(actionListener);
    
    this.addMouseListener(titleMouseListener);
    
    //maximizeButton.addMouseListener(new ButtonRolloverEffect());
    
    
    
    
  }
  
  /** Returns the desktop associated to this title bar, if one has been registered 
   * with #installDocking(DockingDesktop), or null.
   */
  public DockingDesktop getDesktop(){
    return desktop;
  }
  
  
  /** Overriden as a means to unregister internal listeners, do not call directly */
  public void removeNotify(){
    super.removeNotify();
/*    KeyboardFocusManager.getCurrentKeyboardFocusManager().removePropertyChangeListener(
        focusHighlighter);  2005/11/10: now it's shared between title bars*/
    if (target != null){
      target.getDockKey().removePropertyChangeListener(dockKeyListener);
    }
    
  }
  
  /** called to build the title bar */
  protected void layoutTitleBar(){
    removeAll();
    
    add(Box.createHorizontalStrut(4)); // keep the label not to close to the border
    add(titleLabel);
    add(Box.createHorizontalGlue());
    
    int height = UIManager.getInt("DockViewTitleBar.height");
    add(Box.createRigidArea(new Dimension(4,height))); // fixed height
    add(dockButton);
    add(floatButton);
    add(maximizeButton);
    add(closeButton);
  }
  
  /** Returns the label used to display the dockkey name.
   *<p> Shouldn't be used to update the title : the best way is to update the
   * DockKey (property listener ensure the labels and buttons stay in sync).
   *
   *@since 2.0
   */
  public JLabel getTitleLabel(){
    if (titleLabel == null){
      titleLabel = new JLabel();
    }
    return titleLabel;
  }
  
  
  public JButton getCloseButton(){
    if (closeButton == null){
      closeButton = new JButton();
    }
    return closeButton;
  }
  
  /** returns the button used for hiding or docking the view.
   * <p>
   * As hiding and docking are mutually exclusive, the same button is used for both purposes
   */
  public JButton getHideOrDockButton(){
    if (dockButton == null){
      dockButton = new JButton();
    }
    return dockButton;
  }
  
  /** returns the button used for maximizing or restoring the view.
   * <p>
   * As those operations are mutually exclusive, the same button is used for both purposes
   */
  public JButton getMaximizeOrRestoreButton(){
    if (maximizeButton == null){
      maximizeButton = new JButton();
    }
    return maximizeButton;
  }
  
  /** returns the button used for floating (detach) the view.
   */
  public JButton getFloatButton(){
    if (floatButton == null){
      floatButton = new JButton();
    }
    return floatButton;
  }
  
  
  private void setNotification(boolean notification) {
    boolean old = this.isNotification;
    this.isNotification = notification;
    firePropertyChange("titlebar.notification", old, notification);
  }
  
  private boolean isAutoHide(){
    return target.getDockKey().getDockableState() == DockableState.STATE_HIDDEN;
  }
  private boolean isMaximized(){
    return target.getDockKey().getDockableState() == DockableState.STATE_MAXIMIZED;
  }
  
  private void dockAction() {
    boolean old = isAutoHide();
    firePropertyChange(PROPERTY_AUTOHIDE, old, !old);
  }
  
  private void maximizeAction() {
    // we use the visible property of the button to check if maximization
    // is enabled or not
    if (maximizeButton.isVisible()){
      boolean old = isMaximized();
      // we don't really change the maximized property,
      // which will be updated if the desktop accepts this maximization
      /* We do not use fireVetoableChange, to keep maximization processing outside
       * the title bar (everything is driven par the dockingdesktop and the
       * SingleDockableContainer
       */
      firePropertyChange(PROPERTY_MAXIMIZED, old, !old);
    }
  }
  
  private void closeAction() {
    firePropertyChange(PROPERTY_CLOSED, false, true);
  }
  
  private void floatAction() {
    if (target.getDockKey().getDockableState() == DockableState.STATE_FLOATING){
      firePropertyChange(PROPERTY_FLOAT, true, false);
    } else {
      firePropertyChange(PROPERTY_FLOAT, false, true);
    }
  }
  
  
  /** This method is invoked to hide the pop-up that could still be visible
   * (To avoid a visible pop-up for an invisible component)
   */
  public void closePopUp(){
    if (currentPopUp != null){
      currentPopUp.setVisible(false);
      // @todo uncertain about correct gc of the pop-up in that case
      currentPopUp = null;
    }
  }
  
  
  /** {@inheritDoc}
   * @since 2.0
   */
  public String getUIClassID() {
    return uiClassID;
  }
  
  
  /** Returns true if the dockable is the currently active one.
   * <p>
   * There is at most one active dockable for a dekstop, and it there is one,
   * it is the one which contains the keybord focused component.
   * */
  public boolean isActive() {
    return active;
  }
  
  /** Updates the active property.
   * A title bar is active when the dockable it is for is ancestor of
   * the keybord focused component.
   *   */
  public void setActive(boolean active) {
    boolean old = this.active;
    this.active = active;
    firePropertyChange("active", old, active);
  }
  
  /** Changes the dockable this title bar is for */
  public void setDockable(Dockable dockable) {
    Dockable old = target;
    if (target != null && target != dockable) {
      target.getDockKey().removePropertyChangeListener(dockKeyListener);
    }
    if (dockable != null) {
      this.target = dockable;
      
      layoutTitleBar();
            
      DockKey key = dockable.getDockKey();
      titleLabel.setText(key.getName());
      titleLabel.setIcon(key.getIcon());
      setToolTipText(key.getTooltip());
      revalidate();
      
      /*dockButton.setVisible(key.isAutoHideEnabled());
       
      this.autoHide = key.getDockableState() == DockableState.STATE_HIDDEN;
       
      setDockButtonAsAutoHide(autoHide);
       
      closeButton.setVisible(key.isCloseEnabled());
      maximizeButton.setVisible(key.isMaximizeEnabled() && !autoHide);*/
      
      key.addPropertyChangeListener(dockKeyListener);
      
    }
    firePropertyChange("dockable", old, dockable);
  }
  
  
  
  
  /** {@inheritDoc} */
  public Dockable getDockable() {
    return target;
  }
  
  /** {@inheritDoc} */
  public boolean startDragComponent(Point p) {
    // disable DnD for some cases : 
    // - child of a compound dockable, in hidden state
    // - child of a maximized compund dockable
    // - maximized dockable
    int targetState = target.getDockKey().getDockableState();
    if (targetState == DockableState.STATE_HIDDEN){
      if (DockingUtilities.isChildOfCompoundDockable(target)){
        // nested hidden dockables cannot be drag and dropped
        return false;
      }
    } else if (targetState == DockableState.STATE_DOCKED){
      boolean isChildOfMaximizedContainer = false;      
      if (desktop != null){
          Dockable max = desktop.getMaximizedDockable();
          if (max != null && max.getComponent().getParent().isAncestorOf(this)){
            isChildOfMaximizedContainer = true;
          }
      }
      if (isChildOfMaximizedContainer){
        return false;
      }
    } else if (targetState == DockableState.STATE_MAXIMIZED){
      return false;
    }
    
    // notify our listeners that drag has begun
    firePropertyChange(PROPERTY_DRAGGED, false, true);
    return true;
  }
  
  /** Returns a readeable String representing this title bar */
  public String toString() {
    if (target != null) {
      return "DockViewTitleBar of [" + target.getDockKey() + "]";
    } else {
      return "DockViewTitleBar";
    }
  }
  private JMenuItem createPopUpItem(String text, Icon icon, String tooltip, String actionCommand, KeyStroke accelerator){
    JMenuItem menuItem = new JMenuItem(text, icon);
    menuItem.setActionCommand(actionCommand);
    menuItem.addActionListener(actionListener);
    if (accelerator != null){
      menuItem.setAccelerator(accelerator);
    }
    return menuItem;
  }
  
  private void checkForPopUp(MouseEvent e){
    JPopupMenu popup = new JPopupMenu(target.getDockKey().getName());
    // first add the standard menu
    DockKey key = target.getDockKey();
    switch (key.getDockableState()){
      case DockableState.STATE_DOCKED:
        initDockedPopUp(popup);
        break;
      case DockableState.STATE_HIDDEN:
        initAutoHidePopUp(popup);
        break;
      case DockableState.STATE_MAXIMIZED:
        initMaximizedPopUp(popup);
        break;
      case DockableState.STATE_FLOATING:
        initFloatingPopUp(popup);
        break;
      default:
        // nothing to do
    }
    
    DockableActionCustomizer customizer = target.getDockKey().getActionCustomizer();
    if (customizer != null && customizer.isSingleDockableTitleBarPopUpCustomizer()){
      if (popup.getComponentCount() > 0){
        popup.addSeparator();
      }
      customizer.visitSingleDockableTitleBarPopUp(popup, target);
    }
    if (popup.getComponentCount() > 0){
      popup.show(DockViewTitleBar.this, e.getX(), e.getY());
      this.currentPopUp = popup;
    }
  }
  
  /** Init the popup displayed as the title bar contextual menu */
  protected void initMaximizedPopUp(JPopupMenu popup){
    
    popup.add(createPopUpItem(RESTORE_TEXT, restoreIcon, RESTORE_TEXT,
        "maximize", (KeyStroke)UIManager.get("DockingDesktop.maximizeActionAccelerator") ));
  }
  
  protected void initAutoHidePopUp(JPopupMenu popup){
    if (DockingUtilities.isChildOfCompoundDockable(target)){
      // restore option not allowed for children of a compound dockable
    } else {      
      popup.add(createPopUpItem(RESTORE_TEXT, dockIcon, RESTORE_TEXT,
          "dock", (KeyStroke)UIManager.get("DockingDesktop.dockActionAccelerator")));
    }
    if (target.getDockKey().isCloseEnabled()){
      popup.add(createPopUpItem(CLOSE_TEXT, closeIcon, CLOSE_TEXT,
          "close", (KeyStroke)UIManager.get("DockingDesktop.closeActionAccelerator")));
    }
  }
  
  protected void initDockedPopUp(JPopupMenu popup){
    DockKey key = target.getDockKey();
    if (key.isAutoHideEnabled()){
      popup.add(createPopUpItem(ICONIFY_TEXT, hideIcon, ICONIFY_TEXT,
          "dock", (KeyStroke)UIManager.get("DockingDesktop.dockActionAccelerator")));
    }
    if (key.isFloatEnabled()){
      popup.add(createPopUpItem(FLOAT_TEXT, floatIcon, FLOAT_TEXT,
          "float", (KeyStroke)UIManager.get("DockingDesktop.floatActionAccelerator")));
    }
    if (key.isMaximizeEnabled()){
      popup.add(createPopUpItem(MAXIMIZE_TEXT, maximizeIcon, MAXIMIZE_TEXT,
          "maximize", (KeyStroke)UIManager.get("DockingDesktop.maximizeActionAccelerator")));
    }
    if (key.isCloseEnabled()){
      popup.add(createPopUpItem(CLOSE_TEXT, closeIcon, CLOSE_TEXT,
          "close", (KeyStroke)UIManager.get("DockingDesktop.closeActionAccelerator")));
    }
  }
  
  /** Init the popup displayed as the title bar contextual menu */
  protected void initFloatingPopUp(JPopupMenu popup){
    if (DockingUtilities.isChildOfCompoundDockable(target)){
      // attach option not allowed for children of a compound dockable
    } else {
      popup.add(createPopUpItem(ATTACH_TEXT, attachIcon, ATTACH_TEXT,
          "float", (KeyStroke)UIManager.get("DockingDesktop.floatActionAccelerator")));
    }
  }
  
  /** Returns the container of the dockable's component */
  public Container getDockableContainer() {
    // easy
    return getParent();
  }

  public void installDocking(DockingDesktop desktop) {
    this.desktop = desktop;
  }
  
  public void uninstallDocking(DockingDesktop desktop){
    this.desktop = null;
  }

  /** notifies the source when the drag operation has ended (by a drop or cancelled) 
   * @since 2.1.3
   */ 
  public void endDragComponent(boolean dropped){
    // nothing more to do
  }
  
  
  /* used to highlight the title bar when its parent is ancestor of the
   *   focused component
   */
  private static class FocusHighlighter implements PropertyChangeListener {
    private DockViewTitleBar activeTitleBar;
    
    FocusHighlighter(){
      // this is a singleton so we register here for keyboard focus events properties
      KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener(
          "focusOwner", this);
    }
    
    
    // focusOwner
    public void propertyChange(PropertyChangeEvent e) {
      Component c = (Component) e.getNewValue();
      Component ancestor = c;
      // is the focus contained in a single dockable container
      while(ancestor != null && !(ancestor instanceof SingleDockableContainer) ){
        ancestor = ancestor.getParent();
      }
      if (ancestor != null){
        if (ancestor instanceof DockView){
          DockView view = (DockView) ancestor;
          DockViewTitleBar tb = view.getTitleBar();
          if (tb == activeTitleBar){
            // no view change in focus
          } else if (tb != null){
            if (activeTitleBar != null){
              activeTitleBar.setActive(false);
            }
            tb.setActive(true);
            // reset notification (blinking)
            Dockable target = tb.target;
            if (target != null && target.getDockKey() != null){ //2007/02/27 fixed NPE
              target.getDockKey().setNotification(false);
            }
          } else { // tb == null && tb != activeTitleBar
            activeTitleBar.setActive(false);
          }
          activeTitleBar = tb;
        } else if (ancestor instanceof AutoHideExpandPanel){
          DockViewTitleBar tb = ((AutoHideExpandPanel)ancestor).getTitleBar();
          if (tb == activeTitleBar){
            // no view change in focus
          } else if (tb != null){
            if (activeTitleBar != null){
              activeTitleBar.setActive(false);
            }
            tb.setActive(true);
            // reset notification (blinking)
            tb.target.getDockKey().setNotification(false);
          } else { // tb == null && tb != activeTitleBar
            activeTitleBar.setActive(false);
          }
          activeTitleBar = tb;
        } else {
          if (activeTitleBar != null){
            activeTitleBar.setActive(false);
          }
          activeTitleBar = null;
        }
      } else {
        if (activeTitleBar != null){
          activeTitleBar.setActive(false);
        }
        activeTitleBar = null;
      }
      
      
/*      // try to find if the component taking the focus is the ancestor
      if (activeTitleBar != null){
      Container parent = activeTitleBar.getParent();
      if (parent != null && c != null && parent.isAncestorOf(c)) {
        if (!active) {
          setActive(true);
        }
        // reset notification (blinking)
        target.getDockKey().setNotification(false);
      } else if (active) {
        setActive(false);
      }
 */
    }
  }
  
  
  
}
