package org.openswing.swing.client;

import java.beans.*;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

import org.openswing.swing.domains.java.*;
import org.openswing.swing.util.client.*;


/**
 * <p>Title: OpenSwing Framework</p>
 * <p>Description: Combo box input control: its items are retrieved from ClientSettings.getDomain method,
 * that uses domainId property to identify the correct domain to use.</p>
 * <p>Copyright: Copyright (C) 2006 Mauro Carniel</p>
 *
 * <p> This file is part of OpenSwing Framework.
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the (LGPL) Lesser General Public
 * License as published by the Free Software Foundation;
 *
 *                GNU LESSER GENERAL PUBLIC LICENSE
 *                 Version 2.1, February 1999
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *       The author may be contacted at:
 *           maurocarniel@tin.it</p>
 *
 * @author Mauro Carniel
 * @version 1.0
 */
public class ComboBoxControl extends BaseInputControl implements InputControl,SearchControl {

  static {
    UIManager.put("ComboBox.disabledForeground", UIManager.get("ComboBox.foreground"));
    UIManager.put("ComboBox.disabledBackground", UIManager.get("TextField.inactiveBackground"));
    UIManager.put("ComboBox.selectionForeground", Color.black );
    UIManager.put("ComboBox.selectionBackground", ClientSettings.BACKGROUND_SEL_COLOR );
  }

  /** combo box */
  private JComboBox combo = new JComboBox();

  /** domain related to this input control */
  private Domain domain = null;

  /** domain identifier */
  private String domainId = null;

  /** combo box model */
  private DefaultComboBoxModel model = new DefaultComboBoxModel();

  /** background color when the input control is enabled */
  private Color enabledBackColor = combo.getBackground();

  /** define if in insert mode combo box has no item selected; default value: <code>false</code> i.e. the first item is pre-selected */
  private boolean nullAsDefaultValue = false;

  /** define if description in combo items must be translated; default value: <code>true</code> */
  private boolean translateItemDescriptions = true;


  /**
   * Contructor.
   */
  public ComboBoxControl() {
    this.setLayout(new GridBagLayout());
    this.add(combo, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));

//    setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
//    add(combo);
    setOpaque(false);
    combo.addKeyListener(new KeyAdapter() {
      public void keyPressed(KeyEvent e) {
        if (e.getKeyCode()==e.VK_CANCEL || e.getKeyCode()==e.VK_BACK_SPACE || e.getKeyCode()==e.VK_DELETE)
          combo.setSelectedIndex(-1);
      }
    });

    // intercepts key events listened by combo box...1
    combo.setKeySelectionManager(new JComboBox.KeySelectionManager() {
        public int selectionForKey(char aKey, ComboBoxModel aModel) {
            return -1;
        }
    });

    new SearchWindowManager(this);

    if (ClientSettings.TEXT_ORIENTATION!=null)
        setComponentOrientation(ClientSettings.TEXT_ORIENTATION);

    initListeners();
  }



  /**
   * Set the domain identifier, so the control can retrieve its items from the domain.
   * @param domainId domain identifier
   */
  public final void setDomainId(String domainId) {
    this.domainId = domainId;
    if (Beans.isDesignTime())
      return;
    Domain domain = ClientSettings.getInstance().getDomain(domainId);
    setDomain(domain);
  }


  /**
   * Set the specified domain and retrieve the combo items from the domain.
   * @param domain domain to use to retrieve the combo items
   */
  public final void setDomain(Domain domain) {
    this.domain = domain;
    model.removeAllElements();
    if (domain!=null) {
      DomainPair[] pairs = domain.getDomainPairList();
      for(int i=0;i<pairs.length;i++)
        if (translateItemDescriptions)
          model.addElement(ClientSettings.getInstance().getResources().getResource(pairs[i].getDescription()));
        else
          model.addElement(pairs[i].getDescription());
      combo.setModel(model);
      combo.revalidate();
      combo.setSelectedIndex(-1);
    }
  }


  /**
   * @return domain identifier
   */
  public final String getDomainId() {
    return domainId;
  }


  /**
   * Select the combo item related to the specified code.
   * @param code used to retrieve the corresponding item and to select that item in the combo
   */
  public final void setValue(Object code) {
    if (code==null)
      combo.setSelectedIndex(-1);
    if (domain==null)
      return;
    DomainPair pair = domain.getDomainPair(code);
    if (pair!=null) {
      if (translateItemDescriptions)
        combo.setSelectedItem( ClientSettings.getInstance().getResources().getResource(pair.getDescription()) );
      else
        combo.setSelectedItem( pair.getDescription() );
    }
  }


  /**
   * @return code related to the selected combo item; it return null if no item is selected
   */
  public final Object getValue() {
    if (domain==null)
      return null;
    DomainPair[] pairs = domain.getDomainPairList();
    for(int i=0;i<pairs.length;i++)
      if (combo.getSelectedItem()==null)
        return null;
      else {
        if (translateItemDescriptions) {
          if (combo.getSelectedItem().equals( ClientSettings.getInstance().getResources().getResource(pairs[i].getDescription()) ))
            return pairs[i].getCode();
        }
        else {
          if (combo.getSelectedItem().equals( pairs[i].getDescription()) )
            return pairs[i].getCode();
        }
      }
    return null;
  }


  /**
   * @return combo box
   */
  public final JComboBox getComboBox() {
    return combo;
  }


  public void setEnabled(boolean enabled) {
    combo.setEnabled(enabled);
    combo.setFocusable(enabled || ClientSettings.DISABLED_INPUT_CONTROLS_FOCUSABLE);
    if (!enabled)
      combo.setBackground((Color)UIManager.get("TextField.inactiveBackground"));
    else
      combo.setBackground(enabledBackColor);
  }


  /**
   * @return current input control abilitation
   */
  public final boolean isEnabled() {
    try {
      return combo.isEnabled();
    }
    catch (Exception ex) {
      return false;
    }
  }


  /**
   * @return component inside this whose contains the value
   */
  public JComponent getBindingComponent() {
    return combo;
  }


  /**
   * Adds the specified focus listener to receive focus events from
   * this component when this component gains input focus.
   * If listener <code>l</code> is <code>null</code>,
   * no exception is thrown and no action is performed.
   *
   * @param    l   the focus listener
   * @see      java.awt.event.FocusEvent
   * @see      java.awt.event.FocusListener
   * @see      #removeFocusListener
   * @see      #getFocusListeners
   * @since    JDK1.1
   */
  public final void addFocusListener(FocusListener l) {
     try {
        if(combo != null) combo.addFocusListener(l);
     }
     catch (Exception ex) {
     }
  }


  /**
   * Removes the specified focus listener so that it no longer
   * receives focus events from this component. This method performs
   * no function, nor does it throw an exception, if the listener
   * specified by the argument was not previously added to this component.
   * If listener <code>l</code> is <code>null</code>,
   * no exception is thrown and no action is performed.
   *
   * @param    l   the focus listener
   * @see      java.awt.event.FocusEvent
   * @see      java.awt.event.FocusListener
   * @see      #addFocusListener
   * @see      #getFocusListeners
   * @since    JDK1.1
   */
  public final void removeFocusListener(FocusListener l) {
    try {
      combo.removeFocusListener(l);
    }
    catch (Exception ex) {
    }
  }


  /**
   * Adds the specified action listener to receive
   * action events from this textfield.
   *
   * @param l the action listener to be added
   */
  public final void addActionListener(ActionListener l) {
    try {
      combo.addActionListener(l);
    }
    catch (Exception ex) {
    }
  }


  /**
   * Removes the specified action listener so that it no longer
   * receives action events from this textfield.
   *
   * @param l the action listener to be removed
   */
  public final void removeActionListener(ActionListener l) {
    try {
      combo.removeActionListener(l);
    }
    catch (Exception ex) {
    }
  }


  /**
   * Adds an <code>ItemListener</code>.
   * <p>
   * <code>aListener</code> will receive one or two <code>ItemEvent</code>s when
   * the selected item changes.
   *
   * @param aListener the <code>ItemListener</code> that is to be notified
   * @see #setSelectedItem
   */
  public final void addItemListener(ItemListener alistener) {
    try {
      combo.addItemListener(alistener);
    }
    catch (Exception ex) {
    }
  }


  /** Removes an <code>ItemListener</code>.
   *
   * @param aListener  the <code>ItemListener</code> to remove
   */
  public final void removeItemListener(ItemListener alistener) {
    try {
      combo.removeItemListener(alistener);
    }
    catch (Exception ex) {
    }
  }


  /**
   * @return define if in insert mode combo box has no item selected
   */
  public final boolean isNullAsDefaultValue() {
    return nullAsDefaultValue;
  }


  /**
   * Define if in insert mode combo box has no item selected.
   * @param nullAsDefaultValue define if in insert mode combo box has no item selected
   */
  public final void setNullAsDefaultValue(boolean nullAsDefaultValue) {
    this.nullAsDefaultValue = nullAsDefaultValue;
  }


  /**
   * @return domain to use to retrieve the combo items
   */
  public final Domain getDomain() {
    return domain;
  }


  /**
   * @return the selected index in the input control
   */
  public final int getSelectedIndex() {
    return combo.getSelectedIndex();
  }


  /**
   * Set the selected index.
   */
  public final void setSelectedIndex(int index) {
    combo.setSelectedIndex(index);
  }


  /**
   * @return total rows count in the input control
   */
  public final int getRowCount() {
    return combo.getItemCount();
  }


  /**
   * @return the element at the specified index, converted in String format
   */
  public final String getValueAt(int index) {
    return combo.getItemAt(index)==null?"":combo.getItemAt(index).toString();
  }


  /**
   * @return combo control
   */
  public final JComponent getComponent() {
    return combo;
  }


  /**
   * @return <code>true</code> if the input control is in read only mode (so search is enabled), <code>false</code> otherwise
   */
  public final boolean isReadOnly() {
    return isEnabled();
  }


  /**
   * @return <code>true</code> to disable key listening on input control (for instance, in case of nested grids), <code>false</code> to listen for key events
   */
  public final boolean disableListener() {
    return false;
  }


  /**
   * @return define if description in combo items must be translated
   */
  public final boolean isTranslateItemDescriptions() {
    return translateItemDescriptions;
  }


  /**
   * Define if description in combo items must be translated; default value: <code>true</code>.
   * @param translateItemDescriptions flag used to define if description in combo items must be translated
   */
  public final void setTranslateItemDescriptions(boolean translateItemDescriptions) {
    this.translateItemDescriptions = translateItemDescriptions;
  }


  /**
   * Method invoked by SearchWindowManager when the specified "textToSeach" pattern has not matchings in the current content
   * of binded control.
   * This callback can be used to retrieve additional data into control and to search inside new data.
   * @param textToSeach patten of text to search
   * @return -1 if no additional data is available, otherwise the row index of data just added that satify the pattern
   */
  public final int search(String textToSeach) {
    return -1;
  }


  /**
   * Set the component orientation: from left to right or from right to left.
   * @param o component orientation
   */
  public final void setTextOrientation(ComponentOrientation o) {
    combo.setComponentOrientation(o);
  }


  /**
   * @return component orientation
   */
  public final ComponentOrientation getTextOrientation() {
    try {
      return combo.getComponentOrientation();
    }
    catch (Exception ex) {
      return null;
    }
  }

}
