Package DisplayProject.controls

Source Code of DisplayProject.controls.TabFolder

/*
Copyright (c) 2003-2009 ITerative Consulting Pty Ltd. All Rights Reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:

o Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
 
o Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the distribution.
   
o This jcTOOL Helper Class software, whether in binary or source form may not be used within,
or to derive, any other product without the specific prior written permission of the copyright holder

 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


*/
package DisplayProject.controls;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;

import org.apache.log4j.Logger;

import DisplayProject.Array_Of_Panel;
import DisplayProject.CompoundField;
import DisplayProject.Constants;
import DisplayProject.GridField;
import DisplayProject.LayoutManagerHelper;
import DisplayProject.TabInfo;
import DisplayProject.actions.Caption;
import DisplayProject.actions.FrameWeight;
import DisplayProject.actions.WidgetState;
import DisplayProject.events.ClientEventManager;
import DisplayProject.plaf.TabFolderUI;
import Framework.EventManager;
import Framework.ForteKeyboardFocusManager;
import Framework.ParameterHolder;
import Framework.TextData;
/**
* The TabFolder class defines a tab folder, a compound widget that displays an array of panels as tabbed pages.
* @since 22/3/08
*
*/
@SuppressWarnings("serial")
public class TabFolder extends JTabbedPane implements FocusListener, CompoundField{
  private static Logger _log = Logger.getLogger(TabFolder.class);
  protected int previousTabIndex = -1;
    protected volatile boolean ignoreChange = false;
   
    private static final String uiClassID = "TabFolderUI";
    /**
     * if set to true the tab folder will not change to a new tab either by
     * user action or by programatic change
     */
    protected volatile boolean preventChange = false;

  public TabFolder() {
    super();
    this.setupControl();
  }

  public TabFolder(int tabPlacement, int tabLayoutPolicy) {
    super(tabPlacement, tabLayoutPolicy);
    this.setupControl();
  }

  public TabFolder(int tabPlacement) {
    super(tabPlacement);
    this.setupControl();
  }
 
  private void setupControl() {
    this.putClientProperty("qq_hidden_pages", new Hashtable<Object, Object>());
    this.addFocusListener(this); // CraigM:21/08/2008 - Moved from unnecessary TabbedPaneModel
    // TF:15/12/2009:DET-141:Set this up to allow inheriting of popup menu
    this.setInheritsPopupMenu(true);
  }

  @Override
  public void doLayout() {
   
    // This overload is to ensure that tab folder resize to the minimum
    // size when the size policy is set to natural

    // TF:30/11/07:Added the layout debug stuff here
    LayoutManagerHelper.setDebugBackgroundColor(this);
    LayoutManagerHelper.makeDebugTooltip(this);

    // TF:13/8/07: A tab folder should always be big enough to
    // contain all it's children, irrespective of size policy
    // Now, the tab folder layout manager built into Swing computes
    // the size needed to fully house the Panels on all
    // the tabs, we need only to reset the minimum size to enforce
    // the correct size
    Dimension d = getSize();
    setMinimumSize(null);
    Dimension minSize = getMinimumSize();

    int newWidth = this.getWidth();
    int newHeight = this.getHeight();
    int polW = GridField.getConstraints(this).getWidthPolicy();
    int polH = GridField.getConstraints(this).getHeightPolicy();

    /*
     * get the border sizes
     */
    Insets pad = this.getInsets();

    /*
     * calculate new width as needed
     */
    int desiredWidth = pad.left + minSize.width + pad.right;
    if (desiredWidth > newWidth || polW == Constants.SP_NATURAL) {
      newWidth = desiredWidth;
    }
    // If we've got an explicit size set, maintain that as the lower
    // bound
    // TF: If we have tab folder in forte which has an explicit size
    // set, but this is
    // too small to contain it's children, Forte would expand this
    // up. We currently just
    // always obey the minimum size. Logged as JIRA ITC-5
    if (polW == Constants.SP_EXPLICIT) {
      // if (newWidth < d.width) {
      newWidth = d.width;
      // }
    }

    /*
     * calculate new height as needed
     */
    int desiredHeight = pad.top + minSize.height + pad.bottom;
    if (desiredHeight > newHeight || polH == Constants.SP_NATURAL) {
      newHeight = desiredHeight;
    }
    // If we've got an explicit size set, maintain that as the lower
    // bound
    // TF: If we have tab folder in forte which has an explicit size
    // set, but this is
    // too small to contain it's children, Forte would expand this
    // up. We currently just
    // always obey the minimum size.
    if (polH == Constants.SP_EXPLICIT) {
      // if (newHeight < d.height + pad.top + pad.bottom) {
      // newHeight = d.height + pad.top + pad.bottom;
      // }
      newHeight = d.height;
    }
    Dimension newDim = new Dimension(newWidth, newHeight);
    if (!(newDim.equals(this.getSize()))) {
      this.setMinimumSize(newDim);
      this.setPreferredSize(newDim);
      this.setBounds(this.getX(), this.getY(), newWidth,
          newHeight);
      getParent().invalidate();
      getParent().validate();
    }
    super.doLayout();
  }
//PM:24/4/08 removed to help debugging
//  @Override
//  public String toString() {
//    return "TabFolder [" + this.getName() + "]";
//  }
//PM:25/4/08 not sure this is neaded anymore
//  @Override
//  public void addTab(String title, Component component) {
//    /*******************************************************************
//     * TF:15/8/07:Panels displayed in tabs hide the bottom 2 pixels
//     * (highlighting) of the tab. Hence, to work around this we'll
//     * render the tab object as non-opaque This has a side effect of not
//     * allowing pages of a tab folder to have their own background
//     * colour.
//     ******************************************************************/
//    if (component instanceof JComponent) {
//      ((JComponent) component).setOpaque(false);
//    }
//    super.addTab(title, component);
//  }
//
//  @Override
//  public void addTab(String title, Icon icon, Component component) {
//    if (component instanceof JComponent) {
//      ((JComponent) component).setOpaque(false);
//    }
//    super.addTab(title, icon, component);
//  }
//
//  @Override
//  public void addTab(String title, Icon icon, Component component,
//      String tip) {
//    if (component instanceof JComponent) {
//      ((JComponent) component).setOpaque(false);
//    }
//    super.addTab(title, icon, component, tip);
//  }

  @Override
  public Dimension getMinimumSize() {
    if (!isMinimumSizeSet()) {
      Dimension d = super.getMinimumSize();
      int width = this.getWidth();
      int height = this.getHeight();
      int polW = GridField.getConstraints(this).getWidthPolicy();
      int polH = GridField.getConstraints(this).getHeightPolicy();

      /*
       * get the border sizes
       */
      Insets pad = this.getInsets();

      // If we've got an explicit size set, maintain that as the
      // lower bound
      if (polH == Constants.SP_EXPLICIT
          && pad.top + pad.bottom + height > d.height) {
        d.height = height + pad.top + pad.bottom;
      }

      // If we've got an explicit size set, maintain that as the
      // lower bound
      if (polW == Constants.SP_EXPLICIT
          && pad.left + pad.right + width > d.width) {
        d.width = width + pad.left + pad.right;
      }
      return d;
    } else {
      return super.getMinimumSize();
    }
  }

  @Override
  public void remove(Component component) {
    // PM:29/11/07 remove all tabs
    List<JPanel> pages = getAllPages();
    pages.remove(component);
    super.remove(component);
  }

  private static final String PARENT_TAB_FOLDER_PROPERTY = "qq_Tab_Parent";
  private static final String IS_TAB_VISIBLE_PROPERTY = "qq_Tab_visible";
  private static final String TAB_INFO_PROPERTY = "qq_tabInfo";
  private static final String ALL_PAGES_PROPERTY = "qq_all_pages";
 
 
  @Override
  public void insertTab(String title, Icon icon, Component component,
      String tip, int index) {
   
    ((JPanel)component).putClientProperty(PARENT_TAB_FOLDER_PROPERTY, this)
    // PM:29/11/07 store all tabs
    if (WidgetState.get((JPanel)component) != Constants.FS_INVISIBLE) {//PM:14/01/2009: Only add the page if it is visible
      ((JPanel)component).putClientProperty(IS_TAB_VISIBLE_PROPERTY, true);
      // TF:12/07/2010:(Thanks for VVH): Changed the index to "index" instead of getTabCount()
//      super.insertTab(title, icon, component, tip, getTabCount());
      super.insertTab(title, icon, component, tip, index);
    } else {//PM:14/01/2009:Otherwise add the page to the hidden pages list
            ((JPanel)component).putClientProperty(IS_TAB_VISIBLE_PROPERTY, false);
      TabInfo ti = (TabInfo)((JComponent)component).getClientProperty(TAB_INFO_PROPERTY);
            if (ti == null) {
              ti = new TabInfo(title, null, index, (JPanel)component);
            }
            getHiddenPages().put(component, ti);
    }
   
    List<JPanel> pages = getAllPages();
    if (!pages.contains(component)) //PM:22/07/2008:but only if there are not already stored
      pages.add(index, (JPanel) component);
  }

  @SuppressWarnings("unchecked")
  public List<JPanel> getAllPages() {
    // PM:29/11/07 store all tabs
    List<JPanel> pages = (List<JPanel>) getClientProperty(ALL_PAGES_PROPERTY);
    if (pages == null) {
      pages = new ArrayList<JPanel>();
      putClientProperty(ALL_PAGES_PROPERTY, pages);
    }
    return pages;
  }
 
  @SuppressWarnings("unchecked")
  //PM:22/07/2008:AXA
  public Map<Component, TabInfo> getHiddenPages(){
    return  (Map<Component, TabInfo>)getClientProperty("qq_hidden_pages");
  }
 
 
  /**
   * This flag is used to determine if a tab change was effected from the user clicking the mouse on the tab
   */
  private boolean isSetViaMouse = false;
 
  @Override
  public void setSelectedIndex(final int index) {
    /**
     * PM:22/3/08
     * This is an odd requirement for customers wanting
     * to verify a tab pane before it is changed.
     * So we post and wait
     */
    EventManager.startEventChain();
    Hashtable<String, ParameterHolder> params = new Hashtable<String, ParameterHolder>();
    EventManager.postEventAndWait(this, "BeforeTabChange", params);
//    System.out.println("BeforeTabChange");
        List<JPanel> allPages = this.getAllPages();
        int selectedIndex = getSelectedIndex();
        if (selectedIndex > -1){
          JPanel page = (JPanel)getComponentAt(selectedIndex);
          setPreviousTabIndex(allPages.indexOf(page)+1);
        }
        if (!this.preventChange) {
          super.setSelectedIndex(index);
      // TF:24/3/08:Tab folders default their focus to the first component in the tab if the tab change was caused
      // by a mouse click. We need to defer this event to the end of the EDT queue, even though this method will
      // exist on the EDT queue because the tab component hasn't been set up properly here yet, and may still be invisible
      if (isSetViaMouse) {
        SwingUtilities.invokeLater(new Runnable() {
          public void run() {
            Component c = TabFolder.this.getComponentAt(index);
           
            // CraigM:06/08/2008 - Set the traversal reason to be done by the application
            ForteKeyboardFocusManager.setApplicationTraversal();
           
            if (c.isFocusable()) {
              c.requestFocusInWindow();
            }
            else {
              c.transferFocus();
            }
          }
        });
      }
        }
        this.preventChange = false;
        postTabSelect();
        EventManager.endEventChain();
     
  }
    public void postTabSelect() {
        //PM:26/11/07
        /*
         * Get the actual tab index considering
         * invisible tabs
         */
//      System.out.println("AfterTabSelect");
        int targetIndex = this.getSelectedIndex();
        List<JPanel> allPages = this.getAllPages();
        if (targetIndex > -1){
            JPanel page = (JPanel)this.getComponentAt(targetIndex);
            targetIndex = allPages.indexOf(page)+1;
        }
//        EventManager.startEventChain();
        Hashtable<String, Object> qq_Params = new Hashtable<String, Object>();
        qq_Params.put( "prevIndex", new ParameterHolder(this.getPreviousTabIndex()));
        qq_Params.put( "index", new ParameterHolder(targetIndex) );
        qq_Params.put( "page", new ParameterHolder(this.getSelectedComponent()) );
        ClientEventManager.postEvent(this, "AfterTabSelect", qq_Params);
//        EventManager.endEventChain();
//        this.resetPreviousTabIndex();
    }

    public int getPreviousTabIndex() {
        return this.previousTabIndex;
    }

    public void setPreviousTabIndex(int previousTabIndex) {
      this.previousTabIndex = previousTabIndex;
    }
   
    public void resetPreviousTabIndex() {
        setPreviousTabIndex(getSelectedIndex());
    }

    public void revertTabSelection(int tabIndex) {
        this.ignoreChange = true;
        super.setSelectedIndex(tabIndex);
        this.resetPreviousTabIndex();
        this.ignoreChange = false;
    }

    public boolean shouldIgnoreChange() {
        return this.ignoreChange;
    }

    public void preventChange(){
      this.preventChange = true;
    }
   
  /**
   * Override the processMouseEvent method so we can set a flag to indicate whether we're coming from
   * a mouse click. This is important because Forte set the focus of the tab folder to be the first
   * widget within the tab folder if and only if the tab change was caused by a mouse click.
   */
  @Override
  protected void processMouseEvent(MouseEvent e) {
    this.isSetViaMouse = true;
    super.processMouseEvent(e);
    this.isSetViaMouse = false;
  }
  //PM:21/07/2008:AXA
    public String getUIClassID() {
        return uiClassID;
    }
  //PM:21/07/2008:AXA
    public void updateUI() {
        setUI(TabFolderUI.createUI(this));
        invalidate();
    }
    //PM:22/07/2008:AXA
    @Override
    public void addTab(String title, Component component) {
      insertTab(title, null, component, null, getAllPages().size());
      FrameWeight.set((JComponent)component, Constants.W_NONE);
    }

    public void focusGained(FocusEvent e) {
        EventManager.startEventChain();
        int reason = ForteKeyboardFocusManager.getTraversalReason();
        if (reason != Constants.FC_SUPRESS) {
            Hashtable<String, Object> params = new Hashtable<String, Object>();
            params.put("reason", new ParameterHolder(reason));
            ClientEventManager.postEvent( this, "BeforeFocusLoss", params );
        }
        EventManager.endEventChain();

        // CraigM:21/08/2008 - This is done in setSelectedIndex
//        if (this.getPreviousTabIndex() != this.getSelectedIndex()) {
//          this.postTabSelect();
//        }
    }

    public void focusLost(FocusEvent e) {
        EventManager.startEventChain();
        int reason = ForteKeyboardFocusManager.getTraversalReason();
        if (reason != Constants.FC_SUPRESS) {
            Hashtable<String, Object> params = new Hashtable<String, Object>();
            params.put("reason", new ParameterHolder(reason));
            ClientEventManager.postEvent( this, "BeforeFocusLoss", params );
        }
        EventManager.endEventChain();
    }
   
    @SuppressWarnings("unchecked")
  public void setPages(Array_Of_Panel pages) {
        // add the array of pages
        // TF:13/8/07:Ensure we also remove any existing pages, and reset the size of the panel to 1, 1
        // or there abouts, as Forte did this when setting the tabs
        this.removeAll();
        Map<Component, TabInfo> hiddenPages = this.getHiddenPages();
        List<JPanel> allPages = this.getAllPages();//PM:14/01/2009:get all pages
        allPages.clear();
        if (hiddenPages != null) {
            hiddenPages.clear();
        }

        for (int i = 0; i < pages.size(); i++) {
            JPanel page = (JPanel) pages.get(i);
            TextData caption = Caption.get(page);
            // TF:13/8/07:Panels don't have borders in tab panes
            Border b = page.getBorder();
            if (b instanceof TitledBorder) {
              ((TitledBorder)b).setTitle("");
                ((TitledBorder)b).setBorder(new EmptyBorder(0,0,0,0));
            }
            else {
                page.setBorder(null);
            }
            page.setMinimumSize(null);
            page.setPreferredSize(null);
            page.setSize(1, 1);
           
            addTab(caption.getValue(), page);
           
        }
        this.setSize(1,1);
        this.setMinimumSize(null);
        this.setPreferredSize(null);
    }

    /**
     * Returns the Swing tab index (which doesn't include hidden tabs), based on the index including hidden tabs.
     * CraigM:16/01/2009.
     *
     * @param modelIndex 0 based index including hidden tabs
     * @return 0 based index excluding hidden tabs
     */
    public int getTabIndex(int modelIndex) {
      List<JPanel> allPages = this.getAllPages();
     
      if (allPages != null && modelIndex >= 0 && modelIndex < allPages.size()) {
        JPanel page = allPages.get(modelIndex);
       
        for (int index=0; index<this.getTabCount(); index++) {
          if (this.getComponentAt(index) == page) {
            return index; // Found the page
          }
        }
      }
     
      return -1;
    }
   
    /**
     * Returns the model tab index (which includes hidden tabs), based on the swing index excluding hidden tabs.
     * CraigM:19/01/2009.
     *
     * @param tabIndex 0 based index excluding hidden tabs
     * @return 0 based index including hidden tabs (-1 if not found)
     */
    public int getModelIndex(int tabIndex) {
      try {
        Component page = this.getComponentAt(tabIndex);
          List<JPanel> allPages = this.getAllPages();
          return allPages.indexOf(page);
      }
      catch (IndexOutOfBoundsException e) {
        return -1;
      }
    }

    /**
     * Get list of all the pages in this tab folder. The pages are returned irrespective
     * of their visibility, so that getPages().length != getTabCount() particularly when
     * there are invisible pages. The array returned is a copy of the backing array.
     * @return
     */
    @SuppressWarnings("unchecked")
    public Array_Of_Panel<Panel> getPages() {
      Array_Of_Panel pages = new Array_Of_Panel();
      List<JPanel> allPages = getAllPages();
      for (JPanel page : allPages){
        pages.add(page);
      }
      return pages;
    }

    public void hideTab(JPanel page){
      if (isTabVisible(page)){
        page.setVisible(false);
        page.putClientProperty(IS_TAB_VISIBLE_PROPERTY, false);

        List<JPanel> allPages = getAllPages();
        Map<Component, TabInfo> hiddenPages = getHiddenPages();
        int index = allPages.indexOf(page);
        String caption = getTitleAt(indexOfComponent(page));
        TabInfo ti = (TabInfo)page.getClientProperty(TAB_INFO_PROPERTY);
        if (ti == null) {
          ti = new TabInfo(caption, null, index, page);
        }
        hiddenPages.put(page, ti);
        super.remove(page);
        _log.debug("Setting Tab Invisible: " + caption  );
      }
    }
   
    public void showTab(JPanel page){
      if (!isTabVisible(page)){
       
        page.setVisible(true);//PM:14/01/2009:set to be visible
        page.putClientProperty(IS_TAB_VISIBLE_PROPERTY, true);

        List<JPanel> allPages = getAllPages();
        Map<Component, TabInfo> hiddenPages = getHiddenPages();
        TabInfo ti = (TabInfo) hiddenPages.get(page);
        //PM:22/07/2008: fixed tab order
        int insertIndex = 0;
        JPanel lastVisible = null;
        for (JPanel tab : allPages){
          if (tab == page)
            break;
          if (isTabVisible(tab))
            lastVisible = tab;
        }
        insertIndex = indexOfComponent(lastVisible) + 1;
        super.insertTab(ti.getCaption(), ti.getIcon(), page,
          page.getToolTipText(), insertIndex);
        hiddenPages.remove(page);
        _log.debug("Setting Tab Visible: " + ti.getCaption() );
      }
    }
   
    public boolean isTabVisible(JPanel jp){
      return (Boolean)jp.getClientProperty(IS_TAB_VISIBLE_PROPERTY);
    }

}
TOP

Related Classes of DisplayProject.controls.TabFolder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.