All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.swing.tabcontrol.plaf.DefaultTabbedContainerUI Maven / Gradle / Ivy

There is a newer version: RELEASE230
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.netbeans.swing.tabcontrol.plaf;

import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Composite;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.SingleSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListDataEvent;
import javax.swing.plaf.ComponentUI;
import org.netbeans.swing.tabcontrol.TabData;
import org.netbeans.swing.tabcontrol.TabDisplayer;
import org.netbeans.swing.tabcontrol.TabbedContainer;
import org.netbeans.swing.tabcontrol.TabbedContainerUI;
import org.netbeans.swing.tabcontrol.WinsysInfoForTabbedContainer;
import org.netbeans.swing.tabcontrol.event.ArrayDiff;
import org.netbeans.swing.tabcontrol.event.ComplexListDataEvent;
import org.netbeans.swing.tabcontrol.event.ComplexListDataListener;
import org.netbeans.swing.tabcontrol.event.TabActionEvent;
import org.netbeans.swing.tabcontrol.event.VeryComplexListDataEvent;

/**
 * Default UI implementation for tabbed containers.  Manages installing the tab
 * contentDisplayer and content contentDisplayer components, and the
 * relationship between the data models and selection models of the UI.
 * 

* Note that there is typically little reasons to subclass this - to affect the display * or behavior of the tabs, implement {@link org.netbeans.swing.tabcontrol.TabDisplayerUI}, * the UI delegate for the embedded component which displays the tabs. * * @author Tim Boudreau */ public class DefaultTabbedContainerUI extends TabbedContainerUI { /** * Action listener which receives actions from the tab displayer and propagates them through * action listeners on the tab displayer, to allow clients to consume the events. */ private ActionListener actionListener = null; /** * Flag to ensure listeners attached, since ComponentShown notifications are not always * reliable. */ /** UIManager key for the border of the tab displayer in editor ui type. */ public static final String KEY_EDITOR_CONTENT_BORDER = "TabbedContainer.editor.contentBorder"; //NOI18N /** UIManager key for the border of the tab displayer in editor ui type */ public static final String KEY_EDITOR_TABS_BORDER = "TabbedContainer.editor.tabsBorder"; //NOI18N /** UIManager key for the border of the entire tabbed container in editor ui type*/ public static final String KEY_EDITOR_OUTER_BORDER = "TabbedContainer.editor.outerBorder"; //NOI18N /** UIManager key for the border of the tab displayer in view ui type. */ public static final String KEY_VIEW_CONTENT_BORDER = "TabbedContainer.view.contentBorder"; //NOI18N /** UIManager key for the border of the tab displayer in view ui type. */ public static final String KEY_VIEW_TABS_BORDER = "TabbedContainer.view.tabsBorder"; //NOI18N /** UIManager key for the border of the entire tabbed container in view ui type.*/ public static final String KEY_VIEW_OUTER_BORDER = "TabbedContainer.view.outerBorder"; //NOI18N /** UIManager key for the border of the tab displayer in sliding ui type. */ public static final String KEY_SLIDING_CONTENT_BORDER = "TabbedContainer.sliding.contentBorder"; //NOI18N /** UIManager key for the border of the tab displayer in sliding ui type */ public static final String KEY_SLIDING_TABS_BORDER = "TabbedContainer.sliding.tabsBorder"; //NOI18N /** UIManager key for the border of the entire tabbed container in sliding ui type*/ public static final String KEY_SLIDING_OUTER_BORDER = "TabbedContainer.sliding.outerBorder"; //NOI18N /** UIManager key for the border of the tab displayer in toolbar ui type. */ public static final String KEY_TOOLBAR_CONTENT_BORDER = "TabbedContainer.toolbar.contentBorder"; //NOI18N /** UIManager key for the border of the tab displayer in toolbar ui type */ public static final String KEY_TOOLBAR_TABS_BORDER = "TabbedContainer.toolbar.tabsBorder"; //NOI18N /** UIManager key for the border of the entire tabbed container in toolbar ui type*/ public static final String KEY_TOOLBAR_OUTER_BORDER = "TabbedContainer.toolbar.outerBorder"; //NOI18N /** Component listener which listens on the container, and attaches/detaches listeners. * do not alter the value in this field. To provide a different implementation, * override the appropriate creation method. **/ protected ComponentListener componentListener = null; /** Change listener which tracks changes in the selection model and changes the displayed * component to reflect the selected tab * do not alter the value in this field. To provide a different implementation, * override the appropriate creation method. */ protected ChangeListener selectionListener = null; /** Listener on the data model, which handles updating the contained components to keep them * in sync with the contents of the data model. * do not alter the value in this field. To provide a different implementation, * override the appropriate creation method. * @see TabbedContainer#setContentPolicy */ protected ComplexListDataListener modelListener = null; /** * Layout manager which will handle layout of the tabbed container. * do not alter the value in this field. To provide a different implementation, * override the appropriate creation method. */ protected LayoutManager contentDisplayerLayout = null; /** Property change listener which detects changes on the tabbed container, such as its active state, which * should be propagated to the tab displayer. * do not alter the value in this field. To provide a different implementation, * override the appropriate creation method. */ protected PropertyChangeListener propertyChangeListener = null; /** * FxProvider which will provide transition effects when tabs are changed. By default, only used for * TabbedContainer.TYPE_SLIDE tabs. * do not alter the value in this field. To provide a different implementation, * override the appropriate creation method. */ protected FxProvider slideEffectManager = null; /** * The component which displays the selected component in the tabbed container. * do not alter the value in this field. To provide a different implementation, * override the appropriate creation method. */ protected JComponent contentDisplayer = null; /** * The Displayer for the tabs. Normally an instance of TabDisplayer. To get the actual * GUI component that is showing tabs, call its getComponent() method. * do not alter the value in this field. To provide a different implementation, * override the appropriate creation method. */ protected TabDisplayer tabDisplayer = null; private HierarchyListener hierarchyListener = null; /** * Creates a new instance of DefaultTabbedContainerUI */ public DefaultTabbedContainerUI(TabbedContainer c) { super(c); } public static ComponentUI createUI(JComponent c) { return new DefaultTabbedContainerUI((TabbedContainer) c); } /** This method is final. Subclasses which need to provide additional initialization should override * install() * * @param c A JComponent, which must == the displayer field initialized in the constructor */ @Override public final void installUI(JComponent c) { assert c == container; container.setLayout(createLayout()); contentDisplayer = createContentDisplayer(); if( "Aqua".equals(UIManager.getLookAndFeel().getID()) //NOI18N && (container.getType() == TabbedContainer.TYPE_VIEW || container.getType() == TabbedContainer.TYPE_SLIDING) && !Boolean.getBoolean("nb.explorerview.aqua.defaultbackground")) { //NOI18N contentDisplayer.setBackground(UIManager.getColor("NbExplorerView.background")); //NOI18N contentDisplayer.setOpaque(true); } tabDisplayer = createTabDisplayer(); selectionListener = createSelectionListener(); modelListener = createModelListener(); componentListener = createComponentListener(); propertyChangeListener = createPropertyChangeListener(); contentDisplayerLayout = createContentDisplayerLayout(); slideEffectManager = createFxProvider(); actionListener = createDisplayerActionListener(); container.setLayout(createLayout()); hierarchyListener = new ContainerHierarchyListener(); forward = new ForwardingMouseListener(container); installContentDisplayer(); installTabDisplayer(); installBorders(); installListeners(); install(); //#41681, #44278, etc. FOCUS related. the UI needs to be the first listener to be notified // that the selection has changed. Otherwise strange focus effects kick in, eg. when the winsys snapshot gets undated beforehand.. tabDisplayer.getSelectionModel().addChangeListener(selectionListener); } /** Used by unit tests */ TabDisplayer getTabDisplayer() { return tabDisplayer; } private MouseListener forward = null; /** This method is final. Subclasses which need to provide additional initialization should override * uninstall() * * @param c A JComponent, which must == the displayer field initialized in the constructor */ public final void uninstallUI(JComponent c) { assert c == container; tabDisplayer.getSelectionModel().removeChangeListener(selectionListener); uninstall(); uninstallListeners(); uninstallDisplayers(); container = null; contentDisplayer = null; tabDisplayer = null; selectionListener = null; modelListener = null; componentListener = null; propertyChangeListener = null; contentDisplayerLayout = null; actionListener = null; forward = null; } /** Subclasses may override this method to do anything they need to do on installUI(). It will * be called after listeners, the displayer, etc. (all pseudo-final protected fields) have been * initialized. */ protected void install() { } /** Subclasses may override this method to do anything they need to do on uninstallUI(). It will * be called before the protected fields of the instance have been nulled. */ protected void uninstall() { } protected boolean uichange() { installBorders(); return false; } /** * Installs the content displayer component and its layout manager */ protected void installContentDisplayer() { contentDisplayer.setLayout(contentDisplayerLayout); container.add(contentDisplayer, BorderLayout.CENTER); } /** * Installs the tab displayer component into the container. By default, installs it using the * constraint BorderLayout.NORTH. */ protected void installTabDisplayer() { container.add(tabDisplayer, BorderLayout.NORTH); tabDisplayer.registerShortcuts(container); } /** * Installs borders on the container, content displayer and tab displayer */ protected void installBorders() { String tabsKey; String contentKey; String outerKey; switch (container.getType()) { case TabbedContainer.TYPE_EDITOR : tabsKey = KEY_EDITOR_TABS_BORDER; contentKey = KEY_EDITOR_CONTENT_BORDER; outerKey = KEY_EDITOR_OUTER_BORDER; break; case TabbedContainer.TYPE_VIEW : tabsKey = KEY_VIEW_TABS_BORDER; contentKey = KEY_VIEW_CONTENT_BORDER; outerKey = KEY_VIEW_OUTER_BORDER; break; case TabbedContainer.TYPE_SLIDING : tabsKey = KEY_SLIDING_TABS_BORDER; contentKey = KEY_SLIDING_CONTENT_BORDER; outerKey = KEY_SLIDING_OUTER_BORDER; break; case TabbedContainer.TYPE_TOOLBAR : tabsKey = KEY_TOOLBAR_TABS_BORDER; contentKey = KEY_TOOLBAR_CONTENT_BORDER; outerKey = KEY_TOOLBAR_OUTER_BORDER; break; default : throw new IllegalStateException ("Unknown type: " + container.getType()); } try { Border b = (Border) UIManager.get (contentKey); contentDisplayer.setBorder(b); b = (Border) UIManager.get (tabsKey); tabDisplayer.setBorder(b); b = (Border) UIManager.get (outerKey); container.setBorder(b); } catch (ClassCastException cce) { System.err.println ("Expected a border from UIManager for " + tabsKey + "," + contentKey + "," + outerKey); } } /** * Installs a component listener on the component. Listeners on the data * model and selection model are installed when the component is shown, as * detected by the component listener. */ protected void installListeners() { container.addComponentListener(componentListener); container.addHierarchyListener (hierarchyListener); //Allow mouse events to be forwarded as if they came from the //container tabDisplayer.addMouseListener (forward); contentDisplayer.addMouseListener (forward); } /** * Begin listening to the model for changes in the selection, which should * cause us to update the displayed component in the content * contentDisplayer. Listening starts when the component is first shown, and * stops when it is hidden; if you override createComponentListener(), * you will need to call this method when the component is shown. */ protected void attachModelAndSelectionListeners() { container.getModel().addComplexListDataListener(modelListener); container.addPropertyChangeListener(propertyChangeListener); tabDisplayer.setActive (container.isActive()); tabDisplayer.addActionListener (actionListener); } /** * Stop listening to the model for changes in the selection, which should * cause us to update the displayed component in the content * contentDisplayer, and changes in the data model which can affect the * displayed component. Listening starts when the component is first shown, * and stops when it is hidden; if you override createComponentListener(), * you will need to call this method when the component is hidden. */ protected void detachModelAndSelectionListeners() { container.getModel().removeComplexListDataListener(modelListener); container.removePropertyChangeListener(propertyChangeListener); tabDisplayer.removeActionListener (actionListener); } /** * Uninstalls the component listener installed in installListeners() */ protected void uninstallListeners() { container.removeComponentListener(componentListener); container.removeHierarchyListener (hierarchyListener); componentListener = null; propertyChangeListener = null; tabDisplayer.removeMouseListener (forward); contentDisplayer.removeMouseListener (forward); } /** * Uninstalls and nulls references to the content contentDisplayer and tab * contentDisplayer, and removes all components from the content * contentDisplayer. */ protected void uninstallDisplayers() { container.remove(contentDisplayer); container.remove(tabDisplayer); tabDisplayer.unregisterShortcuts(container); contentDisplayer.removeAll(); contentDisplayer = null; tabDisplayer = null; } /** * Create the component which will display the tabs. */ protected TabDisplayer createTabDisplayer() { TabDisplayer result = null; WinsysInfoForTabbedContainer winsysInfo = container.getContainerWinsysInfo(); if (winsysInfo != null) { result = new TabDisplayer( container.getModel(), container.getType(), winsysInfo); } else { result = new TabDisplayer( container.getModel(), container.getType(), container.getLocationInformer()); } result.setName("Tab Displayer"); //NOI18N result.setComponentConverter( container.getComponentConverter() ); return result; } /** * Create the component which will contain the content (the components which * correspond to tabs). The default implementation simply returns a * vanilla, unadorned JPanel. */ protected JPanel createContentDisplayer() { JPanel result = new JPanel(); result.setName ("Content displayer"); //NOI18N return result; } /** * Create an FxProvider instance which will provide transition effects when tabs are selected. * By default creates a no-op instance for all displayer types except TYPE_SLIDING. * * @return An instance of FxProvider */ protected FxProvider createFxProvider() { if (NO_EFFECTS || (tabDisplayer.getType() != TabDisplayer.TYPE_SLIDING && !EFFECTS_EVERYWHERE)) { return new NoOpFxProvider(); } else { if (ADD_TO_GLASSPANE) { return new LiveComponentSlideFxProvider(); } else { return new ImageSlideFxProvider(); } } } /** * Creates the content contentDisplayer's layout manager, responsible for * ensuring that the correct component is on top and is the only one * showing */ protected LayoutManager createContentDisplayerLayout() { return new StackLayout(); } /** * Create the layout manager that will manage the layout of the * TabbedContainer. A TabbedContainer contains two components - the tabs * contentDisplayer, and the component contentDisplayer. *

* The layout manager determines the position of the tabs relative to the * contentDisplayer component which displays the tab contents. *

* The default implementation uses BorderLayout. If you override this, you * should probably override installDisplayer() as well. */ protected LayoutManager createLayout() { if (container.getType() == TabbedContainer.TYPE_SLIDING) { return new SlidingTabsLayout(); } else if (container.getType() == TabbedContainer.TYPE_TOOLBAR) { return new ToolbarTabsLayout(); } else { return new BorderLayout(); } } /** * Create a component listener responsible for initializing the * contentDisplayer component when the tabbed container is shown */ protected ComponentListener createComponentListener() { return new ContainerComponentListener(); } /** * Create a property change listener which will update the tab displayer in * accordance with property changes on the container. Currently the only * property change of interest is calls to TabbedContainer.setActive(), * which simply cause the active state to be set on the displayer. */ protected PropertyChangeListener createPropertyChangeListener() { return new ContainerPropertyChangeListener(); } /** Creates an action listener to catch actions performed on the tab displayer * and forward them to listeners on the container for consumption. * * @return An action listener */ private ActionListener createDisplayerActionListener() { return new DisplayerActionListener(); } /** * Ensures that the component the selection model says is selected is the * one that is showing. */ protected void ensureSelectedComponentIsShowing() { int i = tabDisplayer.getSelectionModel().getSelectedIndex(); if (i != -1) { TabData td = container.getModel().getTab(i); showComponent(toComp(td)); } } /** Convenience method for fetching a component from a TabData object * via the container's ComponentConverter */ protected final Component toComp (TabData data) { return container.getComponentConverter().getComponent(data); } /** * Shows the passed component. This method does not communicate with * the data model in any way shape or form, it just moves the passed * component to the front. It should only be called in response to an event * from the data model or selection model. *

* If you override createContentDisplayerLayoutModel() to * provide your own layout manager to arrange the displayed component, you * need to override this to tell the layout (or do whatever is needed) to * change the component that is shown. * * @param c The component to be shown * @return The previously showing component, or null if no change was made */ protected Component showComponent(Component c) { if (contentDisplayerLayout instanceof StackLayout) { StackLayout stack = ((StackLayout) contentDisplayerLayout); Component last = stack.getVisibleComponent(); stack.showComponent(c, contentDisplayer); if (c != null) { Integer offset = (Integer)((JComponent)c).getClientProperty("MultiViewBorderHack.topOffset"); contentDisplayer.putClientProperty("MultiViewBorderHack.topOffset", offset); } else { contentDisplayer.putClientProperty("MultiViewBorderHack.topOffset", null); } if (last != c) { maybeRemoveLastComponent(last); return last; } } return null; } /** * Shows a component in the control, using the FxProvider created in * createFxProvider() to manage showing it. Equivalent to calling showComponent, * but there may be a delay while the effect is performed. If no FxProvider is installed, * this will simply delegate to showComponent; if not, the FxProvider is expected * to do that when its effect is completed. * * @param c The component to be shown. */ protected final void showComponentWithFxProvider (Component c) { if (slideEffectManager == null || !container.isShowing() || (!(c instanceof JComponent))) { Component last = showComponent (c); maybeRemoveLastComponent (last); } else { slideEffectManager.start((JComponent) c, container.getRootPane(), tabDisplayer.getClientProperty(TabDisplayer.PROP_ORIENTATION)); } } /** * Removes the passed component from the AWT hierarchy if the container's content policy is * CONTENT_POLICY_ADD_ONLY_SELECTED. * * @param c The component that should be removed */ private final void maybeRemoveLastComponent (Component c) { if (c != null && container.getContentPolicy() == TabbedContainer.CONTENT_POLICY_ADD_ONLY_SELECTED) { contentDisplayer.remove (c); } } /** * Fills contentDisplayer container with components retrieved from model. */ protected void initDisplayer() { if (container.getContentPolicy() == TabbedContainer.CONTENT_POLICY_ADD_ALL) { List tabs = container.getModel().getTabs(); Component curC = null; for (Iterator iter = tabs.iterator(); iter.hasNext();) { curC = toComp ((TabData) iter.next()); // string parameter is needed for StackLayout to kick in correctly contentDisplayer.add(curC, ""); } } else { int i = tabDisplayer.getSelectionModel().getSelectedIndex(); if (i != -1) { TabData td = container.getModel().getTab(i); contentDisplayer.add(toComp(td), ""); } } updateActiveState(); } /** * Create a listener for the TabDataModel. This listener is responsible for * keeping the state of the contained components in the displayer in sync * with the contents of the data model. Note that it is not necessary for * this listener to adjust the selection - DefaultTabSelectionModel handles * cases such as removing the selected component appropriately, so if such a * model change happens, a selection change will be immediately forthcoming * to handle it. *

* Note that it is important that this listener be added to the data model * after the DefaultSelectionModel has added its listener. It is * important to create the displayer component before adding this listener. * Some support for privilged listeners may be added to DefaultTabDataModel * in the future to avoid this issue entirely. */ protected ComplexListDataListener createModelListener() { return new ModelListener(); } /** * Create a ChangeListener which will listen to the selection model of the * tab displayer, and update the displayed component in the displayer when * the selection model changes. */ protected ChangeListener createSelectionListener() { return new SelectionListener(); } /** Sets the active state of the displayer to match that of the container */ private void updateActiveState() { //#45630 - more of a hack than a fix. //apparently uninstallUI() was called before the the ContainerPropertyChangeListener instance was removed in //ContainerHierarchyListener's hierarchyChanged method. // for such case the property change should be a noop. TabDisplayer displ = tabDisplayer; TabbedContainer cont = container; if (displ != null && cont != null) { displ.setActive(cont.isActive()); } } public Rectangle getTabRect(int tab, Rectangle r) { if (r == null) { r = new Rectangle(); } tabDisplayer.getTabRect(tab, r); Point p = tabDisplayer.getLocation(); r.x += p.x; r.y += p.y; return r; } protected void requestAttention (int tab) { tabDisplayer.requestAttention (tab); } protected void cancelRequestAttention (int tab) { tabDisplayer.cancelRequestAttention(tab); } @Override protected void setAttentionHighlight (int tab, boolean highlight) { tabDisplayer.setAttentionHighlight(tab, highlight); } public void setShowCloseButton (boolean val) { tabDisplayer.setShowCloseButton(val); } public boolean isShowCloseButton () { return tabDisplayer.isShowCloseButton(); } /** * Scroll pane-like border, good general border around windows. Used if no * border is provided via UIDefaults. */ private static final class DefaultWindowBorder implements Border { private static final Insets insets = new Insets(1, 1, 2, 2); public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { g.translate(x, y); g.setColor(UIManager.getColor("controlShadow")); //NOI18N g.drawRect(0, 0, w - 2, h - 2); g.setColor(UIManager.getColor("controlHighlight")); //NOI18N g.drawLine(w - 1, 1, w - 1, h - 1); g.drawLine(1, h - 1, w - 1, h - 1); g.translate(-x, -y); } public Insets getBorderInsets(Component c) { return insets; } public boolean isBorderOpaque() { return true; } } // end of DefaultWindowBorder protected class ContainerPropertyChangeListener implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent evt) { if (TabbedContainer.PROP_ACTIVE.equals(evt.getPropertyName())) { updateActiveState(); } } } /** Checks the position of the tabbed container relative to its parent * window, and potentially updates its orientation client property. * * @see TabDisplayer#PROP_ORIENTATION */ protected final void updateOrientation() { if (!container.isDisplayable()) { return; } if (Boolean.FALSE.equals(container.getClientProperty (TabbedContainer.PROP_MANAGE_TAB_POSITION))) { //The client has specified that it does not want automatic management //of the displayer orientation return; } Object currOrientation = tabDisplayer.getClientProperty(TabDisplayer.PROP_ORIENTATION); Container window = container.getTopLevelAncestor(); Rectangle containerBounds = container.getBounds(); containerBounds = SwingUtilities.convertRectangle(container, containerBounds, window); boolean longestIsVertical = containerBounds.width < containerBounds.height; int distanceToLeft = containerBounds.x; int distanceToTop = containerBounds.y; int distanceToRight = window.getWidth() - (containerBounds.x + containerBounds.width); int distanceToBottom = window.getHeight() - (containerBounds.y + containerBounds.height); Object orientation; if (!longestIsVertical) { if (distanceToBottom > distanceToTop) { orientation = TabDisplayer.ORIENTATION_NORTH; } else { orientation = TabDisplayer.ORIENTATION_SOUTH; } } else { if (distanceToLeft > distanceToRight) { orientation = TabDisplayer.ORIENTATION_EAST; } else { orientation = TabDisplayer.ORIENTATION_WEST; } } if (currOrientation != orientation) { tabDisplayer.putClientProperty( TabDisplayer.PROP_ORIENTATION, orientation); container.validate(); } } public int tabForCoordinate(Point p) { p = SwingUtilities.convertPoint(container, p, tabDisplayer); return tabDisplayer.tabForCoordinate(p); } public void makeTabVisible(int tab) { tabDisplayer.makeTabVisible (tab); } public SingleSelectionModel getSelectionModel() { return tabDisplayer.getSelectionModel(); } public Image createImageOfTab(int idx) { return tabDisplayer.getUI().createImageOfTab(idx); } public Polygon getExactTabIndication(int idx) { Polygon result = tabDisplayer.getUI().getExactTabIndication(idx); scratchPoint.setLocation(0,0); Point p = SwingUtilities.convertPoint(tabDisplayer, scratchPoint, container); result.translate (-p.x, -p.y); return appendContentBoundsTo(result); } private Point scratchPoint = new Point(); public Polygon getInsertTabIndication(int idx) { Polygon result = tabDisplayer.getUI().getInsertTabIndication(idx); scratchPoint.setLocation(0,0); Point p = SwingUtilities.convertPoint(tabDisplayer, scratchPoint, container); result.translate (-p.x, -p.y); return appendContentBoundsTo(result); } private Polygon appendContentBoundsTo (Polygon p) { int width = contentDisplayer.getWidth(); int height = contentDisplayer.getHeight(); int[] xpoints = new int[p.npoints + 4]; int[] ypoints = new int[xpoints.length]; //XXX not handling this correctly for non-top orientations int pos = 0; Object orientation = tabDisplayer.getClientProperty (TabDisplayer.PROP_ORIENTATION); int tabsHeight = tabDisplayer.getHeight(); if (orientation == null || orientation == TabDisplayer.ORIENTATION_NORTH) { xpoints[pos] = 0; ypoints[pos] = tabsHeight; pos++; xpoints[pos] = p.xpoints[p.npoints-1]; ypoints[pos] = tabsHeight; pos++; for (int i=0; i < p.npoints-2; i++) { xpoints [pos] = p.xpoints[i]; ypoints [pos] = p.ypoints[i]; pos++; } xpoints[pos] = xpoints[pos-1]; ypoints[pos] = tabsHeight; pos++; xpoints[pos] = width - 1; ypoints[pos] = tabsHeight; pos++; xpoints[pos] = width - 1; ypoints[pos] = height -1; pos++; xpoints[pos] = 0; ypoints[pos] = height - 1; } else if (orientation == TabDisplayer.ORIENTATION_SOUTH) { int yxlate = contentDisplayer.getHeight() * 2; xpoints[pos] = 0; ypoints[pos] = 0; pos++; xpoints[pos] = container.getWidth(); ypoints[pos] = 0; pos++; xpoints[pos] = container.getWidth(); ypoints[pos] = container.getHeight() - tabsHeight; pos++; int upperRight = 0; //Search backward for the upper right corner - we only know //the location of the left upper corner int highestFound = Integer.MIN_VALUE; for (int i = p.npoints-2; i >= 0; i--) { if (highestFound < p.ypoints[i]) { upperRight = i; highestFound = p.ypoints[i]; } else if (highestFound == p.ypoints[i]) { break; } } int curr = upperRight-1; for (int i=p.npoints-1; i >= 0; i--) { xpoints[pos] = p.xpoints[curr]; if (ypoints[pos] == highestFound) { ypoints[pos] = Math.min (tabDisplayer.getLocation().y, p.ypoints[curr] + yxlate); } else { ypoints[pos] = p.ypoints[curr] + yxlate; } pos++; curr++; if (curr == p.npoints-1) { curr = 0; } } xpoints[pos] = 0; ypoints[pos] = container.getHeight() - tabsHeight; } else { //Punt on side tabs for now xpoints = p.xpoints; ypoints = p.ypoints; } Polygon result = new EqualPolygon (xpoints, ypoints, xpoints.length); return result; } public Rectangle getContentArea() { return contentDisplayer.getBounds(); } public Rectangle getTabsArea() { return tabDisplayer.getBounds(); } public int dropIndexOfPoint(Point p) { Point p2 = SwingUtilities.convertPoint(container, p, tabDisplayer); return tabDisplayer.getUI().dropIndexOfPoint (p2); } public Rectangle getTabsArea(Rectangle dest) { return tabDisplayer.getBounds(); } /** * A ComponentListener which listens for show/hide to add and remove the * selection and model listeners */ protected class ContainerComponentListener extends ComponentAdapter { public ContainerComponentListener() { } @Override public void componentMoved (ComponentEvent e) { if (container.getType() == TabbedContainer.TYPE_SLIDING) { updateOrientation(); } } @Override public void componentResized (ComponentEvent e) { if (container.getType() == TabbedContainer.TYPE_SLIDING) { updateOrientation(); } } } private boolean bug4924561knownShowing = false; /** * Calls initDisplayer(), then attachModelAndSelectionListeners, * then ensureSelectedComponentIsShowing */ private class ContainerHierarchyListener implements HierarchyListener { public ContainerHierarchyListener() { } public void hierarchyChanged(HierarchyEvent e) { if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) { boolean showing = container.isShowing(); if (showing != bug4924561knownShowing) { if (container.isShowing()) { initDisplayer(); attachModelAndSelectionListeners(); ensureSelectedComponentIsShowing(); if (container.getType() == TabbedContainer.TYPE_SLIDING) { updateOrientation(); } } else { detachModelAndSelectionListeners(); if (container.getType() == TabbedContainer.TYPE_SLIDING) { updateOrientation(); } } } bug4924561knownShowing = showing; } } } private class ToolbarTabsLayout implements LayoutManager { public void layoutContainer(Container container) { Dimension tabSize = tabDisplayer.getPreferredSize(); Insets ins = container.getInsets(); int w = container.getWidth() - (ins.left + ins.right); tabDisplayer.setBounds (ins.left, ins.top, w, tabSize.height); if( tabDisplayer.getModel().size() > 1 ) { //#214427 - check the preferred size again, during the first pass //the tab displayer may not know the available width Dimension newTabSize = tabDisplayer.getPreferredSize(); if( newTabSize.height != tabSize.height ) { tabSize = newTabSize; tabDisplayer.setBounds (ins.left, ins.top, w, tabSize.height); } } contentDisplayer.setBounds(ins.left, ins.top + tabSize.height, w, container.getHeight() - (ins.top + ins.bottom + tabSize.height)); } public Dimension minimumLayoutSize(Container container) { Dimension tabSize = tabDisplayer.getMinimumSize(); Dimension contentSize = contentDisplayer.getMinimumSize(); Insets ins = container.getInsets(); Dimension result = new Dimension(ins.left + ins.top, ins.right + ins.bottom); result.width += Math.max (tabSize.width, contentSize.width); result.height += tabSize.height + contentSize.height; return result; } public Dimension preferredLayoutSize(Container container) { Dimension tabSize = tabDisplayer.getPreferredSize(); Dimension contentSize = contentDisplayer.getPreferredSize(); Insets ins = container.getInsets(); Dimension result = new Dimension(ins.left + ins.top, ins.right + ins.bottom); result.width += Math.max (tabSize.width, contentSize.width); result.height += tabSize.height + contentSize.height; return result; } public void removeLayoutComponent(Component component) { //do nothing } public void addLayoutComponent(String str, Component component) { //do nothing } } /** * A ChangeListener which updates the component displayed in the content * displayer with the selected component in the tab displayer's selection * model */ protected class SelectionListener implements ChangeListener { public SelectionListener() { } public void stateChanged(ChangeEvent e) { if (container.isShowing() //a special case for property sheet dialog window - the selection //change must be processed otherwise the tabbed container may have //undefined preferred size so the property window will be too small || container.getClientProperty("tc") != null) { //NOI18N int idx = tabDisplayer.getSelectionModel().getSelectedIndex(); if (idx != -1) { Component c = toComp(container.getModel().getTab(idx)); c.setBounds(0, 0, contentDisplayer.getWidth(), contentDisplayer.getHeight()); showComponentWithFxProvider(c); } else { showComponent (null); } } } } /** * This class does the heavy lifting of keeping the content of the content * displayer up-to-date with the contents of the data model. */ protected class ModelListener implements ComplexListDataListener { public ModelListener() { } /** * DefaultTabDataModel will always call this method with an instance of * ComplexListDataEvent. */ public void contentsChanged(ListDataEvent e) { //Only need to reread components on setTab (does winsys even use it?) if (e instanceof ComplexListDataEvent) { ComplexListDataEvent clde = (ComplexListDataEvent) e; int index = clde.getIndex0(); if (clde.isUserObjectChanged() && index != -1) { Component comp = contentDisplayer.getComponent( index); Component nue = toComp(tabDisplayer.getModel().getTab(index)); contentDisplayer.remove(comp); boolean add = container.getContentPolicy() == TabbedContainer.CONTENT_POLICY_ADD_ALL || index == container.getSelectionModel().getSelectedIndex(); if (add) { contentDisplayer.add(nue, index); } } if (clde.isTextChanged()) { maybeMakeSelectedTabVisible(clde); } } } /** * This method is called to scroll the selected tab into view if its * title changes (it may be scrolled offscreen). NetBeans' editor uses * this to ensure that the user can see what file they're editing when * the user starts typing (this triggers a * being appended to the tab * title, thus triggering this call). */ private void maybeMakeSelectedTabVisible(ComplexListDataEvent clde) { if (!container.isShowing() || container.getWidth() < 10) { //Java module fires icon changes from badging before the //main window has been validated for the first time return; } if (tabDisplayer.getType() == TabDisplayer.TYPE_EDITOR) { int idx = tabDisplayer.getSelectionModel().getSelectedIndex(); //If more than one tab changed, it's probably not an event we want. //Only do this if there is only one. if ((clde.getIndex0() == clde.getIndex1()) && clde.getIndex0() == idx) { (tabDisplayer).makeTabVisible(idx); } } } public void intervalAdded(ListDataEvent e) { if (container.getContentPolicy() == TabbedContainer.CONTENT_POLICY_ADD_ALL) { Component curC = null; for (int i = e.getIndex0(); i <= e.getIndex1(); i++) { curC = toComp(container.getModel().getTab(i)); contentDisplayer.add(curC, ""); } } } public void intervalRemoved(ListDataEvent e) { // we know that it must be complex data event ComplexListDataEvent clde = (ComplexListDataEvent) e; TabData[] removedTabs = clde.getAffectedItems(); Component curComp; for (int i = 0; i < removedTabs.length; i++) { curComp = toComp(removedTabs[i]); contentDisplayer.remove(curComp); } } public void indicesAdded(ComplexListDataEvent e) { Component curC = null; if (container.getContentPolicy() == TabbedContainer.CONTENT_POLICY_ADD_ALL) { int[] indices = e.getIndices(); for (int i = 0; i < indices.length; i++) { curC = toComp(container.getModel().getTab(indices[i])); contentDisplayer.add(curC, ""); } } } public void indicesRemoved(ComplexListDataEvent e) { int[] indices = e.getIndices(); TabData[] removedTabs = e.getAffectedItems(); Component curComp; for (int i = 0; i < indices.length; i++) { curComp = toComp(removedTabs[i]); // TBD - add assertion curComp.getParent() != contentDisplayer contentDisplayer.remove(curComp); } } public void indicesChanged(ComplexListDataEvent e) { //XXX - if we keep contentPolicies, this could be simplified for //the non ADD_ALL policies if (e instanceof VeryComplexListDataEvent) { ArrayDiff dif = ((VeryComplexListDataEvent) e).getDiff(); //Get the deleted and added indices Set deleted = dif.getDeletedIndices(); Set added = dif.getAddedIndices(); //Get the TabData array from before the change TabData[] old = dif.getOldData(); //Get the TabData array from after the change TabData[] nue = dif.getNewData(); //Now we need to fetch the set of components we should end up //displaying. We need to do this because TabData.equals is only //true if the text *and* the component match. So if the winsys //called setTabs just to change the title of a tab, we would //end up removing and re-adding the component for no reason. Set components = new HashSet(); if (container.getContentPolicy() == TabbedContainer.CONTENT_POLICY_ADD_ALL) { for (int i = 0; i < nue.length; i++) { components.add(toComp(nue[i])); } } boolean changed = false; synchronized (contentDisplayer.getTreeLock()) { //See if we've got anything to delete if (!deleted.isEmpty()) { Iterator i = deleted.iterator(); while (i.hasNext()) { //Get the index into the old array of a deleted tab Integer idx = (Integer) i.next(); //Find the TabData object for it TabData del = old[idx.intValue()]; //Make sure its component is not one we'll be adding if (!components.contains(toComp(del))) { //remove it contentDisplayer.remove(toComp(del)); changed = true; } } } if (container.getContentPolicy() == TabbedContainer.CONTENT_POLICY_ADD_ALL) { //See if we've got anything to add if (!added.isEmpty()) { Iterator i = added.iterator(); while (i.hasNext()) { //Get the index into the new array of the added tab Integer idx = (Integer) i.next(); //Find the TabData object that was added TabData add = nue[idx.intValue()]; //Make sure it's not already showing so we don't do //extra work if (!contentDisplayer.isAncestorOf( toComp(add))) { contentDisplayer.add(toComp(add), ""); changed = true; } } } } } //repaint if (changed) { contentDisplayer.revalidate(); contentDisplayer.repaint(); } } } } /** An action listener which listens on action events from the tab displayer (select, close, etc.) * and propagates them to the tabbed container's action posting mechanism, so listeners on it also * have an opportunity to veto undesired actions, or handle actions themselves. */ private class DisplayerActionListener implements ActionListener { public void actionPerformed (ActionEvent ae) { TabActionEvent tae = (TabActionEvent) ae; if (!shouldPerformAction(tae.getActionCommand(), tae.getTabIndex(), tae.getMouseEvent())) { tae.consume(); } } } private class SlidingTabsLayout implements LayoutManager { public void addLayoutComponent(String name, Component comp) { //do nothing } public void layoutContainer(Container parent) { JComponent c = tabDisplayer; Object orientation = c.getClientProperty ( TabDisplayer.PROP_ORIENTATION); Dimension d = tabDisplayer.getPreferredSize(); Insets ins = container.getInsets(); int width = parent.getWidth() - (ins.left + ins.right); int height = parent.getHeight() - (ins.top + ins.bottom); if (orientation == TabDisplayer.ORIENTATION_NORTH) { c.setBounds (ins.left, ins.top, width, d.height); contentDisplayer.setBounds (ins.left, ins.top + d.height, width, parent.getHeight() - (d.height + ins.top + ins.bottom)); } else if (orientation == TabDisplayer.ORIENTATION_SOUTH) { contentDisplayer.setBounds (ins.top, ins.left, width, parent.getHeight() - (d.height + ins.top + ins.bottom)); c.setBounds (ins.left, parent.getHeight() - (d.height + ins.top + ins.bottom), width, d.height); } else if (orientation == TabDisplayer.ORIENTATION_EAST) { contentDisplayer.setBounds (ins.left, ins.top, width - d.width, height); c.setBounds (parent.getWidth() - (ins.right + d.width), ins.top, d.width, height); } else if (orientation == TabDisplayer.ORIENTATION_WEST) { c.setBounds (ins.left, ins.top, d.width, height); contentDisplayer.setBounds (ins.left + d.width, ins.top, width - d.width, height); } else { throw new IllegalArgumentException ("Unknown orientation: " + orientation); } } public Dimension minimumLayoutSize(Container parent) { JComponent c = tabDisplayer; Object orientation = c.getClientProperty ( TabDisplayer.PROP_ORIENTATION); Dimension tabSize = tabDisplayer.getPreferredSize(); Insets ins = container.getInsets(); Dimension result = new Dimension(); Dimension contentSize = contentDisplayer.getPreferredSize(); if (tabDisplayer.getSelectionModel().getSelectedIndex() == -1) { contentSize.width = 0; contentSize.height = 0; } if (orientation == TabDisplayer.ORIENTATION_NORTH || orientation == TabDisplayer.ORIENTATION_SOUTH) { result.height = ins.top + ins.bottom + contentSize.height + tabSize.height; result.width = ins.left + ins.right + Math.max (contentSize.width, tabSize.width); } else { result.width = ins.left + ins.right + contentSize.width + tabSize.width; result.height = ins.top + ins.bottom + Math.max (contentSize.height, tabSize.height); } return result; } public Dimension preferredLayoutSize(Container parent) { return minimumLayoutSize(parent); } public void removeLayoutComponent(Component comp) { //do nothing } } /** A FxProvider which simply calls finish() from its start * method, providing no effects whatsoever */ private final class NoOpFxProvider extends FxProvider { public void cleanup() { //Do nothing } protected void doFinish() { showComponent (comp); } protected void doStart() { finish(); } } private final class ImageSlideFxProvider extends FxProvider implements ActionListener { private Timer timer = null; private Component prevGlassPane = null; private Dimension d = null; protected void doStart() { if (timer == null) { timer = new Timer (TIMER, this); timer.setRepeats(true); } prevGlassPane = root.getGlassPane(); if (prevGlassPane.isVisible() && prevGlassPane.isShowing()) { //Probably a drag and drop operation - don't interfere doFinish(); return; } initSize(); img = createImageOfComponent(); ImageScalingGlassPane cp = getCustomGlassPane(); root.setGlassPane (cp); cp.setIncrement (0.1f); cp.setBounds (root.getBounds()); cp.setVisible(true); cp.revalidate(); timer.start(); } public void cleanup() { timer.stop(); root.setGlassPane(prevGlassPane); prevGlassPane.setVisible(false); if (img != null) { img.flush(); } img = null; } protected void doFinish() { showComponent (comp); } private void initSize() { d = comp.getPreferredSize(); Dimension d2 = contentDisplayer.getSize(); d.width = Math.max (d2.width, d.width); d.height = Math.max (d2.height, d.height); boolean flip = orientation == TabDisplayer.ORIENTATION_EAST || orientation == TabDisplayer.ORIENTATION_WEST; if (d.width == 0 || d.height == 0) { if (flip) { d.width = root.getWidth(); d.height = tabDisplayer.getHeight(); } else { d.width = tabDisplayer.getWidth(); d.height = root.getHeight(); } } else { if (flip) { d.height = Math.max (d.height, tabDisplayer.getHeight()); } else { d.width = Math.max (d.width, tabDisplayer.getWidth()); } } } private BufferedImage img = null; private BufferedImage createImageOfComponent() { if (USE_SWINGPAINTING) { return null; } if (d.width == 0 || d.height == 0) { //Avoid problems in native graphics engine scaling if we should //end up with crazy values finish(); } BufferedImage img = GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().getDefaultConfiguration(). createCompatibleImage(d.width, d.height); Graphics2D g2d = img.createGraphics(); JComponent c = tabDisplayer; c.setBounds (0, 0, d.width, d.height); comp.paint (g2d); return img; } public void actionPerformed(java.awt.event.ActionEvent e) { float inc = customGlassPane.getIncrement(); if (inc >= 1.0f) { finish(); } else { customGlassPane.setIncrement (inc + INCREMENT); } } private ImageScalingGlassPane customGlassPane = null; private ImageScalingGlassPane getCustomGlassPane() { if (customGlassPane == null) { customGlassPane = new ImageScalingGlassPane(); customGlassPane.setOpaque(false); } return customGlassPane; } private class ImageScalingGlassPane extends JPanel { private float inc = 0f; private Rectangle rect = new Rectangle(); private Rectangle r2 = new Rectangle(); private boolean changed = true; private void setIncrement (float inc) { this.inc = inc; changed = true; if (isShowing()) { Rectangle r = getImageBounds(); if (SYNCHRONOUS_PAINTING) { paintImmediately (r.x, r.y, r.width, r.height); } else { repaint(r.x, r.y, r.width, r.height); } } } private float getIncrement () { return inc; } private Rectangle getImageBounds() { if (!changed) { return rect; } Component c = tabDisplayer; r2.setBounds (0, 0, c.getWidth(), c.getHeight()); Rectangle dispBounds = SwingUtilities.convertRectangle(c, r2, this); if (orientation == TabDisplayer.ORIENTATION_WEST) { rect.x = dispBounds.x + dispBounds.width; rect.y = dispBounds.y; rect.width = Math.round (inc * d.width); rect.height = dispBounds.height; } else if (orientation == TabDisplayer.ORIENTATION_EAST) { rect.width = Math.round (inc * d.width); rect.height = dispBounds.height; rect.x = dispBounds.x - rect.width; rect.y = dispBounds.y; } else if (orientation == TabDisplayer.ORIENTATION_SOUTH) { rect.width = dispBounds.width; rect.height = Math.round(inc * d.height); rect.x = dispBounds.x; rect.y = dispBounds.y - rect.height; } else if (orientation == TabDisplayer.ORIENTATION_NORTH) { rect.x = dispBounds.x; rect.y = dispBounds.y + dispBounds.height; rect.width = dispBounds.width; rect.height = Math.round(inc * d.height); } changed = false; return rect; } @Override public void paint(Graphics g) { try { if (USE_SWINGPAINTING) { SwingUtilities.paintComponent(g, comp, this, getImageBounds()); } else { Graphics2D g2d = (Graphics2D) g; Composite comp = null; if (true) { comp = g2d.getComposite(); g2d.setComposite (AlphaComposite.getInstance(AlphaComposite.SRC_OVER, Math.min(0.99f, inc))); } Rectangle r = getImageBounds(); if (NO_SCALE) { AffineTransform at = AffineTransform.getTranslateInstance(r.x, r.y); g2d.drawRenderedImage(img, at); } else { g2d.drawImage (img, r.x, r.y, r.x + r.width, r.y + r.height, 0, 0, d.width, d.height, getBackground(), null); } if (comp != null) { g2d.setComposite(comp); } } } catch (Exception e) { //Some problem in Apple's graphics scaling engine e.printStackTrace(); finish(); } } } } private final class LiveComponentSlideFxProvider extends FxProvider implements ActionListener { private Timer timer = null; private Component prevGlassPane = null; private Dimension d = null; protected void doStart() { if (timer == null) { timer = new Timer (TIMER, this); timer.setRepeats(true); } prevGlassPane = root.getGlassPane(); if (prevGlassPane.isVisible() && prevGlassPane.isShowing()) { //Probably a drag and drop operation - don't interfere doFinish(); return; } initSize(); LiveComponentResizingGlassPane cp = getCustomGlassPane(); root.setGlassPane (cp); cp.setIncrement (0.1f); cp.setBounds (root.getBounds()); cp.setVisible(true); cp.revalidate(); timer.start(); } private void initSize() { d = comp.getPreferredSize(); Dimension d2 = contentDisplayer.getSize(); d.width = Math.max (d2.width, d.width); d.height = Math.max (d2.height, d.height); boolean flip = orientation == TabDisplayer.ORIENTATION_EAST || orientation == TabDisplayer.ORIENTATION_WEST; if (d.width == 0 || d.height == 0) { if (flip) { d.width = root.getWidth(); d.height = tabDisplayer.getHeight(); } else { d.width = tabDisplayer.getWidth(); d.height = root.getHeight(); } } else { if (flip) { d.height = Math.max (d.height, tabDisplayer.getHeight()); } else { d.width = Math.max (d.width, tabDisplayer.getWidth()); } } } public void cleanup() { timer.stop(); root.setGlassPane(prevGlassPane); prevGlassPane.setVisible(false); customGlassPane.remove(comp); } protected void doFinish() { showComponent (comp); } public void actionPerformed(java.awt.event.ActionEvent e) { float inc = customGlassPane.getIncrement(); if (inc >= 1.0f) { finish(); } else { customGlassPane.setIncrement (inc + INCREMENT); } } private LiveComponentResizingGlassPane customGlassPane = null; private LiveComponentResizingGlassPane getCustomGlassPane() { if (customGlassPane == null) { customGlassPane = new LiveComponentResizingGlassPane(); customGlassPane.setOpaque(false); } return customGlassPane; } private class LiveComponentResizingGlassPane extends JPanel { private float inc = 0f; private Rectangle rect = new Rectangle(); private Rectangle r2 = new Rectangle(); private boolean changed = true; private void setIncrement (float inc) { this.inc = inc; changed = true; if (isShowing()) { if (comp.getParent() != this) { add(comp); comp.setVisible(true); } } doLayout(); } @Override public void doLayout() { Rectangle r = getImageBounds(); comp.setBounds (r.x, r.y, r.width, r.height); } private float getIncrement () { return inc; } private Rectangle getImageBounds() { if (!changed) { return rect; } Component c = tabDisplayer; r2.setBounds (0, 0, c.getWidth(), c.getHeight()); Rectangle dispBounds = SwingUtilities.convertRectangle(c, r2, this); if (orientation == TabDisplayer.ORIENTATION_WEST) { rect.x = dispBounds.x + dispBounds.width; rect.y = dispBounds.y; rect.width = Math.round (inc * d.width); rect.height = dispBounds.height; } else if (orientation == TabDisplayer.ORIENTATION_EAST) { rect.width = Math.round (inc * d.width); rect.height = dispBounds.height; rect.x = dispBounds.x - rect.width; rect.y = dispBounds.y; } else if (orientation == TabDisplayer.ORIENTATION_SOUTH) { rect.width = dispBounds.width; rect.height = Math.round(inc * d.height); rect.x = dispBounds.x; rect.y = dispBounds.y - rect.height; } else if (orientation == TabDisplayer.ORIENTATION_NORTH) { rect.x = dispBounds.x; rect.y = dispBounds.y + dispBounds.height; rect.width = dispBounds.width; rect.height = Math.round(inc * d.height); } changed = false; return rect; } } } //*** A bunch of options for testing /** Sysprop to turn off all sliding effects */ static final boolean NO_EFFECTS = Boolean.getBoolean ("nb.tabcontrol.no.fx"); //NOI18N /** Sysprop to turn off scaling of the slide image */ static final boolean NO_SCALE = Boolean.getBoolean ("nb.tabcontrol.fx.no.scaling"); //NOI18N /** Sysprop to turn use SwingUtilities.paintComponent() instead of an image buffer for sliding effects */ static final boolean USE_SWINGPAINTING = Boolean.getBoolean ("nb.tabcontrol.fx.swingpainting"); //NOI18N /** Sysprop to turn add the component being scaled to the glasspane and alter its size on a * timer to accomplish growing the component */ static final boolean ADD_TO_GLASSPANE = Boolean.getBoolean ("nb.tabcontrol.fx.use.resizing"); //NOI18N /** For those who really love the sliding effect and want to see it on all * tab controls of all types */ static final boolean EFFECTS_EVERYWHERE = Boolean.getBoolean ("nb.tabcontrol.fx.everywhere") || Boolean.getBoolean("nb.tabcontrol.fx.gratuitous"); //NOI18N /** Also have the scaled image be partially transparent as it's drawn */ static final boolean USE_ALPHA = Boolean.getBoolean ("nb.tabcontrol.fx.use.alpha") || Boolean.getBoolean("nb.tabcontrol.fx.gratuitous"); //NOI18N static boolean SYNCHRONOUS_PAINTING = Boolean.getBoolean ("nb.tabcontrol.fx.synchronous"); //NOI18N static float INCREMENT = 0.07f; static int TIMER = 25; static { boolean gratuitous = Boolean.getBoolean("nb.tabcontrol.fx.gratuitous"); //NOI18N String s = System.getProperty ("nb.tabcontrol.fx.increment"); //NOI18N if (s != null) { try { INCREMENT = Float.parseFloat(s); } catch (Exception e) { System.err.println("Bad float value specified: \"" + s +"\""); //NOI18N } } else if (gratuitous) { INCREMENT = 0.02f; } s = System.getProperty ("nb.tabcontrol.fx.timer"); //NOI18N if (s != null) { try { TIMER = Integer.parseInt (s); } catch (Exception e) { System.err.println("Bad integer value specified: \"" + s + "\""); //NOI18N } } else if (gratuitous) { TIMER = 7; } if (gratuitous) { SYNCHRONOUS_PAINTING = true; } } private static final class ForwardingMouseListener implements MouseListener { private final Container c; public ForwardingMouseListener (Container c) { this.c = c; } public void mousePressed (MouseEvent me) { forward (me); } public void mouseReleased (MouseEvent me) { forward (me); } public void mouseClicked (MouseEvent me) { forward (me); } public void mouseEntered (MouseEvent me) { forward (me); } public void mouseExited (MouseEvent me) { forward (me); } private void forward (MouseEvent me) { MouseListener[] ml = c.getMouseListeners(); if (ml.length == 0 || me.isConsumed()) { return; } MouseEvent me2 = SwingUtilities.convertMouseEvent( (Component) me.getSource(), me, c); for (int i=0; i < ml.length; i++) { switch (me2.getID()) { case MouseEvent.MOUSE_ENTERED : ml[i].mouseEntered(me2); break; case MouseEvent.MOUSE_EXITED : ml[i].mouseExited(me2); break; case MouseEvent.MOUSE_PRESSED : ml[i].mousePressed(me2); break; case MouseEvent.MOUSE_RELEASED : ml[i].mouseReleased(me2); break; case MouseEvent.MOUSE_CLICKED : ml[i].mouseClicked(me2); break; default : assert false; } } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy