0001: /*
0002: * Copyright (C) 2004 NNL Technology AB
0003: * Visit www.infonode.net for information about InfoNode(R)
0004: * products and how to contact NNL Technology AB.
0005: *
0006: * This program is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU General Public License
0008: * as published by the Free Software Foundation; either version 2
0009: * of the License, or (at your option) any later version.
0010: *
0011: * This program is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0014: * GNU General Public License for more details.
0015: *
0016: * You should have received a copy of the GNU General Public License
0017: * along with this program; if not, write to the Free Software
0018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
0019: * MA 02111-1307, USA.
0020: */
0021:
0022: // $Id: TitledTab.java,v 1.88 2007/01/28 21:25:49 jesper Exp $
0023: package net.infonode.tabbedpanel.titledtab;
0024:
0025: import java.awt.BorderLayout;
0026: import java.awt.Color;
0027: import java.awt.Component;
0028: import java.awt.Container;
0029: import java.awt.Dimension;
0030: import java.awt.Font;
0031: import java.awt.Insets;
0032: import java.awt.KeyboardFocusManager;
0033: import java.awt.LayoutManager;
0034: import java.awt.Point;
0035: import java.awt.Shape;
0036: import java.awt.event.FocusEvent;
0037: import java.awt.event.FocusListener;
0038: import java.awt.event.MouseAdapter;
0039: import java.awt.event.MouseEvent;
0040: import java.awt.event.MouseListener;
0041: import java.awt.event.MouseMotionListener;
0042: import java.util.ArrayList;
0043: import java.util.Map;
0044: import java.util.Set;
0045:
0046: import javax.swing.Icon;
0047: import javax.swing.JComponent;
0048: import javax.swing.JLabel;
0049: import javax.swing.SwingUtilities;
0050: import javax.swing.border.Border;
0051: import javax.swing.border.CompoundBorder;
0052: import javax.swing.border.EmptyBorder;
0053: import javax.swing.plaf.PanelUI;
0054:
0055: import net.infonode.gui.DimensionProvider;
0056: import net.infonode.gui.InsetsUtil;
0057: import net.infonode.gui.RotatableLabel;
0058: import net.infonode.gui.TranslatingShape;
0059: import net.infonode.gui.border.FocusBorder;
0060: import net.infonode.gui.hover.HoverEvent;
0061: import net.infonode.gui.hover.HoverListener;
0062: import net.infonode.gui.hover.hoverable.HoverManager;
0063: import net.infonode.gui.hover.hoverable.Hoverable;
0064: import net.infonode.gui.icon.IconProvider;
0065: import net.infonode.gui.layout.StackableLayout;
0066: import net.infonode.gui.panel.SimplePanel;
0067: import net.infonode.gui.shaped.panel.ShapedPanel;
0068: import net.infonode.properties.base.Property;
0069: import net.infonode.properties.gui.InternalPropertiesUtil;
0070: import net.infonode.properties.gui.util.ComponentProperties;
0071: import net.infonode.properties.gui.util.ShapedPanelProperties;
0072: import net.infonode.properties.propertymap.PropertyMapTreeListener;
0073: import net.infonode.properties.propertymap.PropertyMapWeakListenerManager;
0074: import net.infonode.properties.util.PropertyChangeListener;
0075: import net.infonode.tabbedpanel.Tab;
0076: import net.infonode.tabbedpanel.TabAdapter;
0077: import net.infonode.tabbedpanel.TabEvent;
0078: import net.infonode.tabbedpanel.TabRemovedEvent;
0079: import net.infonode.tabbedpanel.TabSelectTrigger;
0080: import net.infonode.tabbedpanel.TabbedPanel;
0081: import net.infonode.tabbedpanel.TabbedPanelProperties;
0082: import net.infonode.tabbedpanel.TabbedUtils;
0083: import net.infonode.util.Alignment;
0084: import net.infonode.util.Direction;
0085: import net.infonode.util.ValueChange;
0086:
0087: /**
0088: * <p>A TitledTab is a tab that has support for text, icon and a custom Swing component
0089: * (called title component). Titled tab supports several properties that makes it possible
0090: * to change the look (borders, colors, insets), layout (up, down, left, right).</p>
0091: *
0092: * <p>Titled tab has a line based layout, i.e. the text, icon and title component are
0093: * laid out in a line. The layout of the tab can be rotated, i.e. the text and the icon will
0094: * be rotated 90, 180 or 270 degrees. The title component will not be rotated but moved so
0095: * that the line layout will persist.</p>
0096: *
0097: * <p>A titled tab has 3 rendering states:
0098: * <ul>
0099: * <li>Normal - The tab is selectable but not yet selected
0100: * <li>Highlighted - The tab is either highlighted or selected
0101: * <li>Disabled - The tab is disabled and cannot be selected or highlighted
0102: * </ul>Most of the properties for the tab can be configured for each of the tab rendering
0103: * states.</p>
0104: *
0105: * <p><strong>Note:</strong> If only the normal state properties have been configured, the
0106: * highlighted and disabled state will automatically use the same properties as for the normal
0107: * state, see {@link TitledTabProperties} and {@link TitledTabStateProperties}.</p>
0108: *
0109: * <p>TitledTab implements the {@link net.infonode.gui.icon.IconProvider} interface and
0110: * overloads toString() so that both text and icon for the normal state is shown in the
0111: * tab drop down list in a tabbed panel.</p>
0112: *
0113: * <p>TitledTab supports mouse hovering. A {@link HoverListener} can be set in the
0114: * {@link TitledTabProperties}. The hover listener receives a {@link HoverEvent} when the mouse
0115: * enters or exits the tab. The hover event's source will be the affected titled tab.</p>
0116: *
0117: * @author $Author: jesper $
0118: * @version $Revision: 1.88 $
0119: * @see TitledTabProperties
0120: * @see TitledTabStateProperties
0121: */
0122: public class TitledTab extends Tab implements IconProvider {
0123: private static PanelUI UI = new PanelUI() {
0124: };
0125:
0126: private class StatePanel extends SimplePanel {
0127: private ShapedPanel panel = new ShapedPanel();
0128: private SimplePanel titleComponentPanel = new SimplePanel();
0129: private RotatableLabel label = new RotatableLabel(null, null) {
0130: public Dimension getPreferredSize() {
0131: Dimension d = super .getPreferredSize();
0132: String text = this .getText();
0133: Icon tmpIcon = this .getIcon();
0134:
0135: if (text == null || tmpIcon == null) {
0136: this .setText(" ");
0137: this .setIcon(icon);
0138: if (getDirection().isHorizontal())
0139: d = new Dimension(d.width, super
0140: .getPreferredSize().height);
0141: else
0142: d = new Dimension(
0143: super .getPreferredSize().width,
0144: d.height);
0145:
0146: this .setText(text);
0147: this .setIcon(tmpIcon);
0148: }
0149:
0150: return d;
0151: }
0152: };
0153: private JComponent titleComponent;
0154: private Direction currentLayoutDirection;
0155: private int currentLayoutGap = -1;
0156: private Alignment currentLayoutAlignment;
0157: private String toolTipText;
0158: private Icon icon;
0159:
0160: public StatePanel(Border focusBorder) {
0161: super (new BorderLayout());
0162:
0163: label.setBorder(focusBorder);
0164: label.setMinimumSize(new Dimension(0, 0));
0165:
0166: panel.add(label, BorderLayout.CENTER);
0167: add(panel, BorderLayout.CENTER);
0168: }
0169:
0170: public String getToolTipText() {
0171: return toolTipText;
0172: }
0173:
0174: public JComponent getTitleComponent() {
0175: return titleComponent;
0176: }
0177:
0178: public Shape getShape() {
0179: return panel.getShape();
0180: }
0181:
0182: public JLabel getLabel() {
0183: return label;
0184: }
0185:
0186: public void setTitleComponent(JComponent titleComponent,
0187: TitledTabStateProperties stateProps) {
0188: JComponent oldTitleComponent = this .titleComponent;
0189: this .titleComponent = null;
0190: if (oldTitleComponent != null
0191: && oldTitleComponent.getParent() == titleComponentPanel)
0192: titleComponentPanel.remove(oldTitleComponent);
0193: this .titleComponent = titleComponent;
0194: updateLayout(stateProps, true);
0195: }
0196:
0197: public void activateTitleComponent() {
0198: if (titleComponent != null) {
0199: if (titleComponent.getParent() != titleComponentPanel) {
0200: if (titleComponent.getParent() != null)
0201: titleComponent.getParent().remove(
0202: titleComponent);
0203: titleComponentPanel.add(titleComponent,
0204: BorderLayout.CENTER);
0205: }
0206: } else {
0207: titleComponentPanel.removeAll();
0208: }
0209: }
0210:
0211: public void activate() {
0212: remove(panel);
0213: eventPanel.add(panel, BorderLayout.CENTER);
0214: add(eventPanel, BorderLayout.CENTER);
0215: }
0216:
0217: public void deactivate() {
0218: remove(eventPanel);
0219: eventPanel.remove(panel);
0220: add(panel, BorderLayout.CENTER);
0221: }
0222:
0223: public Dimension getPreferredSize() {
0224: activateTitleComponent();
0225:
0226: return getAdjustedSize(super .getPreferredSize());
0227: }
0228:
0229: public Dimension getMinimumSize() {
0230: activateTitleComponent();
0231:
0232: return getAdjustedSize(super .getMinimumSize());
0233: }
0234:
0235: public Dimension getMaximumSize() {
0236: activateTitleComponent();
0237: return super .getMaximumSize();
0238: }
0239:
0240: private Dimension getAdjustedSize(Dimension d) {
0241: DimensionProvider prov = properties
0242: .getMinimumSizeProvider();
0243: if (prov == null)
0244: return d;
0245:
0246: Dimension min = properties.getMinimumSizeProvider()
0247: .getDimension(this );
0248:
0249: if (min == null)
0250: return d;
0251:
0252: return new Dimension(Math.max(min.width, d.width), Math
0253: .max(min.height, d.height));
0254: }
0255:
0256: public JComponent getFocusableComponent() {
0257: return label;
0258: }
0259:
0260: private void updateLayout(
0261: TitledTabStateProperties stateProperties,
0262: boolean titleComponentChanged) {
0263: if (titleComponent != null
0264: && stateProperties.getTitleComponentVisible()) {
0265: Direction d = stateProperties.getDirection();
0266: int gap;
0267: if (stateProperties.getIconVisible()
0268: || stateProperties.getTextVisible())
0269: gap = stateProperties.getTextTitleComponentGap();
0270: else
0271: gap = 0;
0272: Alignment alignment = stateProperties
0273: .getTitleComponentTextRelativeAlignment();
0274: if (titleComponentPanel.getComponentCount() == 0
0275: || (titleComponentPanel.getComponentCount() > 0 && titleComponentPanel
0276: .getComponent(0) != titleComponent)
0277: || titleComponentChanged
0278: || gap != currentLayoutGap
0279: || alignment != currentLayoutAlignment
0280: || d != currentLayoutDirection) {
0281: titleComponentChanged = false;
0282: currentLayoutDirection = d;
0283: currentLayoutGap = gap;
0284: currentLayoutAlignment = alignment;
0285:
0286: panel.remove(titleComponentPanel);
0287: if (d == Direction.UP) {
0288: panel
0289: .add(
0290: titleComponentPanel,
0291: alignment == Alignment.LEFT ? BorderLayout.SOUTH
0292: : BorderLayout.NORTH);
0293: titleComponentPanel.setBorder(new EmptyBorder(
0294: alignment == Alignment.LEFT ? gap : 0,
0295: 0, alignment == Alignment.LEFT ? 0
0296: : gap, 0));
0297: } else if (d == Direction.LEFT) {
0298: panel
0299: .add(
0300: titleComponentPanel,
0301: alignment == Alignment.LEFT ? BorderLayout.EAST
0302: : BorderLayout.WEST);
0303: titleComponentPanel.setBorder(new EmptyBorder(
0304: 0, alignment == Alignment.LEFT ? gap
0305: : 0, 0,
0306: alignment == Alignment.LEFT ? 0 : gap));
0307: } else if (d == Direction.DOWN) {
0308: panel
0309: .add(
0310: titleComponentPanel,
0311: alignment == Alignment.LEFT ? BorderLayout.NORTH
0312: : BorderLayout.SOUTH);
0313: titleComponentPanel.setBorder(new EmptyBorder(
0314: alignment == Alignment.LEFT ? 0 : gap,
0315: 0, alignment == Alignment.LEFT ? gap
0316: : 0, 0));
0317: } else {
0318: panel
0319: .add(
0320: titleComponentPanel,
0321: alignment == Alignment.LEFT ? BorderLayout.WEST
0322: : BorderLayout.EAST);
0323: titleComponentPanel.setBorder(new EmptyBorder(
0324: 0, alignment == Alignment.LEFT ? 0
0325: : gap, 0,
0326: alignment == Alignment.LEFT ? gap : 0));
0327: }
0328:
0329: panel.revalidate();
0330: }
0331: } else {
0332: panel.remove(titleComponentPanel);
0333: titleComponentPanel.removeAll();
0334:
0335: panel.revalidate();
0336: }
0337: }
0338:
0339: public void updateShapedPanel(
0340: TitledTabStateProperties stateProperties) {
0341: Direction tabAreaOrientation = getTabAreaOrientation();
0342: ShapedPanelProperties shapedPanelProperties = stateProperties
0343: .getShapedPanelProperties();
0344: InternalPropertiesUtil.applyTo(shapedPanelProperties,
0345: panel, tabAreaOrientation.getNextCW());
0346: panel
0347: .setHorizontalFlip(tabAreaOrientation == Direction.DOWN
0348: || tabAreaOrientation == Direction.LEFT ? !shapedPanelProperties
0349: .getHorizontalFlip()
0350: : shapedPanelProperties.getHorizontalFlip());
0351: }
0352:
0353: public void setBorders(Border outerBorder, Border innerBorder) {
0354: setBorder(outerBorder);
0355: panel.setBorder(innerBorder);
0356: }
0357:
0358: public boolean updateState(Map changes,
0359: TitledTabStateProperties stateProperties) {
0360: boolean updateBorders = false;
0361:
0362: if (changes == null) {
0363: label
0364: .setText(stateProperties.getTextVisible() ? stateProperties
0365: .getText()
0366: : null);
0367:
0368: icon = stateProperties.getIcon();
0369: label
0370: .setIcon(stateProperties.getIconVisible() ? stateProperties
0371: .getIcon()
0372: : null);
0373:
0374: label.setIconTextGap(stateProperties.getIconTextGap());
0375:
0376: label.setDirection(stateProperties.getDirection());
0377:
0378: Alignment alignment = stateProperties
0379: .getIconTextRelativeAlignment();
0380: label
0381: .setHorizontalTextPosition(alignment == Alignment.LEFT ? JLabel.RIGHT
0382: : JLabel.LEFT);
0383:
0384: alignment = stateProperties.getHorizontalAlignment();
0385: label
0386: .setHorizontalAlignment(alignment == Alignment.LEFT ? JLabel.LEFT
0387: : alignment == Alignment.CENTER ? JLabel.CENTER
0388: : JLabel.RIGHT);
0389:
0390: alignment = stateProperties.getVerticalAlignment();
0391: label
0392: .setVerticalAlignment(alignment == Alignment.TOP ? JLabel.TOP
0393: : alignment == Alignment.CENTER ? JLabel.CENTER
0394: : JLabel.BOTTOM);
0395:
0396: toolTipText = stateProperties.getToolTipEnabled() ? stateProperties
0397: .getToolTipText()
0398: : null;
0399: if (toolTipText != null && toolTipText.length() == 0)
0400: toolTipText = null;
0401: if (currentStatePanel == this )
0402: eventPanel.setToolTipText(toolTipText);
0403:
0404: updateLayout(stateProperties, true);
0405:
0406: ComponentProperties componentProperties = stateProperties
0407: .getComponentProperties();
0408: label.setFont(componentProperties.getFont());
0409:
0410: Color c = componentProperties.getForegroundColor();
0411: label.setForeground(c);
0412: setForeground(c);
0413:
0414: updateShapedPanel(stateProperties);
0415:
0416: updateBorders = true;
0417: } else {
0418: Map m = (Map) changes.get(stateProperties.getMap());
0419: if (m != null) {
0420: Set keySet = m.keySet();
0421:
0422: if (keySet.contains(TitledTabStateProperties.TEXT)
0423: || keySet
0424: .contains(TitledTabStateProperties.TEXT_VISIBLE)) {
0425: label
0426: .setText(stateProperties
0427: .getTextVisible() ? stateProperties
0428: .getText()
0429: : null);
0430: }
0431:
0432: if (keySet.contains(TitledTabStateProperties.ICON)
0433: || keySet
0434: .contains(TitledTabStateProperties.ICON_VISIBLE)) {
0435: icon = stateProperties.getIcon();
0436: label
0437: .setIcon(stateProperties
0438: .getIconVisible() ? stateProperties
0439: .getIcon()
0440: : null);
0441: }
0442:
0443: if (keySet
0444: .contains(TitledTabStateProperties.ICON_TEXT_GAP)) {
0445: label
0446: .setIconTextGap(((Integer) ((ValueChange) m
0447: .get(TitledTabStateProperties.ICON_TEXT_GAP))
0448: .getNewValue()).intValue());
0449: }
0450:
0451: if (keySet
0452: .contains(TitledTabStateProperties.ICON_TEXT_RELATIVE_ALIGNMENT)) {
0453: Alignment alignment = (Alignment) ((ValueChange) m
0454: .get(TitledTabStateProperties.ICON_TEXT_RELATIVE_ALIGNMENT))
0455: .getNewValue();
0456: label
0457: .setHorizontalTextPosition(alignment == Alignment.LEFT ? JLabel.RIGHT
0458: : JLabel.LEFT);
0459: }
0460:
0461: if (keySet
0462: .contains(TitledTabStateProperties.HORIZONTAL_ALIGNMENT)) {
0463: Alignment alignment = (Alignment) ((ValueChange) m
0464: .get(TitledTabStateProperties.HORIZONTAL_ALIGNMENT))
0465: .getNewValue();
0466: label
0467: .setHorizontalAlignment(alignment == Alignment.LEFT ? JLabel.LEFT
0468: : alignment == Alignment.CENTER ? JLabel.CENTER
0469: : JLabel.RIGHT);
0470: }
0471:
0472: if (keySet
0473: .contains(TitledTabStateProperties.VERTICAL_ALIGNMENT)) {
0474: Alignment alignment = (Alignment) ((ValueChange) m
0475: .get(TitledTabStateProperties.VERTICAL_ALIGNMENT))
0476: .getNewValue();
0477: label
0478: .setVerticalAlignment(alignment == Alignment.TOP ? JLabel.TOP
0479: : alignment == Alignment.CENTER ? JLabel.CENTER
0480: : JLabel.BOTTOM);
0481: }
0482:
0483: if (keySet
0484: .contains(TitledTabStateProperties.TOOL_TIP_TEXT)
0485: || keySet
0486: .contains(TitledTabStateProperties.TOOL_TIP_ENABLED)) {
0487: toolTipText = stateProperties
0488: .getToolTipEnabled() ? stateProperties
0489: .getToolTipText() : null;
0490: if (toolTipText != null
0491: && toolTipText.length() == 0)
0492: toolTipText = null;
0493:
0494: if (currentStatePanel == this )
0495: eventPanel.setToolTipText(toolTipText);
0496: }
0497:
0498: if (keySet
0499: .contains(TitledTabStateProperties.DIRECTION)
0500: || keySet
0501: .contains(TitledTabStateProperties.TITLE_COMPONENT_TEXT_RELATIVE_ALIGNMENT)
0502: || keySet
0503: .contains(TitledTabStateProperties.TITLE_COMPONENT_VISIBLE)
0504: || keySet
0505: .contains(TitledTabStateProperties.TEXT_TITLE_COMPONENT_GAP)
0506: || keySet
0507: .contains(TitledTabStateProperties.ICON_VISIBLE)
0508: || keySet
0509: .contains(TitledTabStateProperties.TEXT_VISIBLE)) {
0510: label.setDirection(stateProperties
0511: .getDirection());
0512:
0513: updateLayout(
0514: stateProperties,
0515: keySet
0516: .contains(TitledTabStateProperties.TITLE_COMPONENT_VISIBLE));
0517: }
0518:
0519: if (keySet
0520: .contains(TitledTabStateProperties.DIRECTION)) {
0521: updateBorders = true;
0522: }
0523: }
0524:
0525: m = (Map) changes.get(stateProperties
0526: .getComponentProperties().getMap());
0527: if (m != null) {
0528: Set keySet = m.keySet();
0529:
0530: if (keySet.contains(ComponentProperties.FONT)) {
0531: label.setFont((Font) ((ValueChange) m
0532: .get(ComponentProperties.FONT))
0533: .getNewValue());
0534: }
0535:
0536: if (keySet
0537: .contains(ComponentProperties.FOREGROUND_COLOR)) {
0538: Color c = (Color) ((ValueChange) m
0539: .get(ComponentProperties.FOREGROUND_COLOR))
0540: .getNewValue();
0541: label.setForeground(c);
0542: setForeground(c);
0543: }
0544:
0545: if (keySet
0546: .contains(ComponentProperties.BACKGROUND_COLOR)) {
0547: Color c = (Color) ((ValueChange) m
0548: .get(ComponentProperties.BACKGROUND_COLOR))
0549: .getNewValue();
0550: panel.setBackground(c);
0551: }
0552:
0553: if (keySet.contains(ComponentProperties.INSETS)
0554: || keySet
0555: .contains(ComponentProperties.BORDER)) {
0556: updateBorders = true;
0557: }
0558: }
0559:
0560: m = (Map) changes.get(stateProperties
0561: .getShapedPanelProperties().getMap());
0562: if (m != null) {
0563: updateShapedPanel(stateProperties);
0564: }
0565: }
0566:
0567: return updateBorders;
0568: }
0569: }
0570:
0571: private TitledTabProperties properties = TitledTabProperties
0572: .getDefaultProperties();
0573:
0574: private HoverListener hoverListener = properties.getHoverListener();
0575:
0576: private class HoverablePanel extends SimplePanel implements
0577: Hoverable {
0578: public HoverablePanel(LayoutManager l) {
0579: super (l);
0580: }
0581:
0582: public void hoverEnter() {
0583: if (hoverListener != null && getTabbedPanel() != null)
0584: hoverListener.mouseEntered(new HoverEvent(
0585: TitledTab.this ));
0586: }
0587:
0588: public void hoverExit() {
0589: if (hoverListener != null)
0590: hoverListener
0591: .mouseExited(new HoverEvent(TitledTab.this ));
0592: }
0593:
0594: public boolean acceptHover(ArrayList enterableHoverables) {
0595: return true;
0596: }
0597: }
0598:
0599: private HoverablePanel eventPanel = new HoverablePanel(
0600: new BorderLayout()) {
0601:
0602: public boolean contains(int x, int y) {
0603: return getComponentCount() > 0
0604: && getComponent(0).contains(x, y);
0605: }
0606:
0607: public boolean inside(int x, int y) {
0608: return getComponentCount() > 0
0609: && getComponent(0).inside(x, y);
0610: }
0611:
0612: };
0613:
0614: public boolean contains(int x, int y) {
0615: Point p = SwingUtilities.convertPoint(this , new Point(x, y),
0616: eventPanel);
0617: return eventPanel.contains(p.x, p.y);
0618: }
0619:
0620: public boolean inside(int x, int y) {
0621: Point p = SwingUtilities.convertPoint(this , new Point(x, y),
0622: eventPanel);
0623: return eventPanel.inside(p.x, p.y);
0624: }
0625:
0626: private StatePanel normalStatePanel;
0627: private StatePanel highlightedStatePanel;
0628: private StatePanel disabledStatePanel;
0629:
0630: private ArrayList mouseListeners;
0631: private ArrayList mouseMotionListeners;
0632: private StackableLayout layout;
0633: private StatePanel currentStatePanel;
0634: private FocusBorder focusBorder;
0635:
0636: private Direction lastTabAreaOrientation = Direction.UP;
0637:
0638: private PropertyMapTreeListener propertiesListener = new PropertyMapTreeListener() {
0639: public void propertyValuesChanged(Map changes) {
0640: doUpdateTab(changes);
0641: }
0642: };
0643:
0644: private PropertyChangeListener tabbedPanelPropertiesListener = new PropertyChangeListener() {
0645: public void propertyChanged(Property property,
0646: Object valueContainer, Object oldValue, Object newValue) {
0647: updateTabAreaOrientation((Direction) newValue);
0648: }
0649: };
0650:
0651: private FocusListener focusListener = new FocusListener() {
0652: public void focusGained(FocusEvent e) {
0653: if (properties.getFocusable())
0654: repaint();
0655: }
0656:
0657: public void focusLost(FocusEvent e) {
0658: if (properties.getFocusable())
0659: repaint();
0660: }
0661: };
0662:
0663: /**
0664: * Constructs a TitledTab with a text, icon, content component and title component.
0665: *
0666: * @param text text or null for no text. The text will be applied to the
0667: * normal state properties
0668: * @param icon icon or null for no icon. The icon will be applied to the
0669: * normal state properties
0670: * @param contentComponent content component or null for no content component
0671: * @param titleComponent title component or null for no title component. The title
0672: * component will be applied to all the states
0673: * @see net.infonode.tabbedpanel.TabFactory
0674: */
0675: public TitledTab(String text, Icon icon,
0676: JComponent contentComponent, JComponent titleComponent) {
0677: super (contentComponent);
0678: super .setOpaque(false);
0679:
0680: addFocusListener(new FocusListener() {
0681: public void focusGained(FocusEvent e) {
0682: repaint();
0683: }
0684:
0685: public void focusLost(FocusEvent e) {
0686: repaint();
0687: }
0688: });
0689:
0690: focusBorder = new FocusBorder(this );
0691: normalStatePanel = new StatePanel(focusBorder);
0692: highlightedStatePanel = new StatePanel(focusBorder);
0693: disabledStatePanel = new StatePanel(focusBorder);
0694:
0695: layout = new StackableLayout(this ) {
0696: public void layoutContainer(Container parent) {
0697: super .layoutContainer(parent);
0698: StatePanel visibleStatePanel = (StatePanel) getVisibleComponent();
0699: visibleStatePanel.activateTitleComponent();
0700: }
0701: };
0702:
0703: setLayout(layout);
0704:
0705: add(normalStatePanel);
0706: add(highlightedStatePanel);
0707: add(disabledStatePanel);
0708:
0709: setText(text);
0710: setIcon(icon);
0711: setTitleComponent(titleComponent);
0712:
0713: eventPanel.addMouseListener(new MouseAdapter() {
0714: public void mousePressed(MouseEvent e) {
0715: updateFocus(TabSelectTrigger.MOUSE_PRESS);
0716: }
0717:
0718: public void mouseReleased(MouseEvent e) {
0719: updateFocus(TabSelectTrigger.MOUSE_RELEASE);
0720: }
0721:
0722: private void updateFocus(TabSelectTrigger trigger) {
0723: if (isEnabled()
0724: && properties.getFocusable()
0725: && getTabbedPanel() != null
0726: && getTabbedPanel().getProperties()
0727: .getTabSelectTrigger() == trigger) {
0728: Component focusedComponent = KeyboardFocusManager
0729: .getCurrentKeyboardFocusManager()
0730: .getFocusOwner();
0731:
0732: if (focusedComponent instanceof TitledTab
0733: && ((TitledTab) focusedComponent)
0734: .getTabbedPanel() == getTabbedPanel())
0735: requestFocusInWindow();
0736: else if (isSelected()
0737: || TabbedUtils
0738: .getParentTabbedPanel(focusedComponent) != getTabbedPanel())
0739: requestFocusInWindow();
0740: }
0741: }
0742: });
0743:
0744: setEventComponent(eventPanel);
0745:
0746: MouseListener mouseListener = new MouseListener() {
0747: public void mouseClicked(MouseEvent e) {
0748: if (mouseListeners != null) {
0749: MouseEvent event = convertMouseEvent(e);
0750: Object[] l = mouseListeners.toArray();
0751: for (int i = 0; i < l.length; i++)
0752: ((MouseListener) l[i]).mouseClicked(event);
0753: }
0754: }
0755:
0756: public void mousePressed(MouseEvent e) {
0757: if (mouseListeners != null) {
0758: MouseEvent event = convertMouseEvent(e);
0759: Object[] l = mouseListeners.toArray();
0760: for (int i = 0; i < l.length; i++)
0761: ((MouseListener) l[i]).mousePressed(event);
0762: }
0763: }
0764:
0765: public void mouseReleased(MouseEvent e) {
0766: if (mouseListeners != null) {
0767: MouseEvent event = convertMouseEvent(e);
0768: Object[] l = mouseListeners.toArray();
0769: for (int i = 0; i < l.length; i++)
0770: ((MouseListener) l[i]).mouseReleased(event);
0771: }
0772: }
0773:
0774: public void mouseEntered(MouseEvent e) {
0775: if (mouseListeners != null) {
0776: MouseEvent event = convertMouseEvent(e);
0777: Object[] l = mouseListeners.toArray();
0778: for (int i = 0; i < l.length; i++)
0779: ((MouseListener) l[i]).mouseEntered(event);
0780: }
0781: }
0782:
0783: public void mouseExited(MouseEvent e) {
0784: if (mouseListeners != null) {
0785: MouseEvent event = convertMouseEvent(e);
0786: Object[] l = mouseListeners.toArray();
0787: for (int i = 0; i < l.length; i++)
0788: ((MouseListener) l[i]).mouseExited(event);
0789: }
0790: }
0791: };
0792:
0793: MouseMotionListener mouseMotionListener = new MouseMotionListener() {
0794: public void mouseDragged(MouseEvent e) {
0795: if (mouseMotionListeners != null) {
0796: MouseEvent event = convertMouseEvent(e);
0797: Object[] l = mouseMotionListeners.toArray();
0798: for (int i = 0; i < l.length; i++)
0799: ((MouseMotionListener) l[i])
0800: .mouseDragged(event);
0801: }
0802: }
0803:
0804: public void mouseMoved(MouseEvent e) {
0805: if (mouseMotionListeners != null) {
0806: MouseEvent event = convertMouseEvent(e);
0807: Object[] l = mouseMotionListeners.toArray();
0808: for (int i = 0; i < l.length; i++)
0809: ((MouseMotionListener) l[i]).mouseMoved(event);
0810: }
0811: }
0812: };
0813:
0814: eventPanel.addMouseListener(mouseListener);
0815: eventPanel.addMouseMotionListener(mouseMotionListener);
0816:
0817: PropertyMapWeakListenerManager.addWeakTreeListener(properties
0818: .getMap(), propertiesListener);
0819:
0820: addTabListener(new TabAdapter() {
0821: public void tabAdded(TabEvent event) {
0822: PropertyMapWeakListenerManager
0823: .addWeakPropertyChangeListener(
0824: getTabbedPanel().getProperties()
0825: .getMap(),
0826: TabbedPanelProperties.TAB_AREA_ORIENTATION,
0827: tabbedPanelPropertiesListener);
0828: updateTabAreaOrientation(getTabbedPanel()
0829: .getProperties().getTabAreaOrientation());
0830: }
0831:
0832: public void tabRemoved(TabRemovedEvent event) {
0833: PropertyMapWeakListenerManager
0834: .removeWeakPropertyChangeListener(
0835: event.getTabbedPanel().getProperties()
0836: .getMap(),
0837: TabbedPanelProperties.TAB_AREA_ORIENTATION,
0838: tabbedPanelPropertiesListener);
0839: }
0840: });
0841:
0842: doUpdateTab(null);
0843: updateCurrentStatePanel();
0844: }
0845:
0846: /**
0847: * Gets the title component for the normal state
0848: *
0849: * @return title component or null if no title component
0850: */
0851: public JComponent getNormalStateTitleComponent() {
0852: return normalStatePanel.getTitleComponent();
0853: }
0854:
0855: /**
0856: * Gets the title component for the highlighted state
0857: *
0858: * @return title component or null if no title component
0859: */
0860: public JComponent getHighlightedStateTitleComponent() {
0861: return highlightedStatePanel.getTitleComponent();
0862: }
0863:
0864: /**
0865: * Gets the title component for the disabled state
0866: *
0867: * @return title component or null if no title component
0868: */
0869: public JComponent getDisabledStateTitleComponent() {
0870: return disabledStatePanel.getTitleComponent();
0871: }
0872:
0873: /**
0874: * <p>Sets the title component.</p>
0875: *
0876: * <p>This method is a convenience method for setting the same title component for
0877: * all states.</p>
0878: *
0879: * @param titleComponent the title component or null for no title component
0880: */
0881: public void setTitleComponent(JComponent titleComponent) {
0882: normalStatePanel.setTitleComponent(titleComponent, properties
0883: .getNormalProperties());
0884: highlightedStatePanel.setTitleComponent(titleComponent,
0885: properties.getHighlightedProperties());
0886: disabledStatePanel.setTitleComponent(titleComponent, properties
0887: .getDisabledProperties());
0888: }
0889:
0890: /**
0891: * Sets the normal state title component
0892: *
0893: * @param titleComponent the title component or null for no title component
0894: */
0895: public void setNormalStateTitleComponent(JComponent titleComponent) {
0896: normalStatePanel.setTitleComponent(titleComponent, properties
0897: .getNormalProperties());
0898: }
0899:
0900: /**
0901: * Sets the highlighted state title component
0902: *
0903: * @param titleComponent the title component or null for no title component
0904: */
0905: public void setHighlightedStateTitleComponent(
0906: JComponent titleComponent) {
0907: highlightedStatePanel.setTitleComponent(titleComponent,
0908: properties.getHighlightedProperties());
0909: }
0910:
0911: /**
0912: * Sets the disabled state title component
0913: *
0914: * @param titleComponent the title component or null for no title component
0915: */
0916: public void setDisabledStateTitleComponent(JComponent titleComponent) {
0917: disabledStatePanel.setTitleComponent(titleComponent, properties
0918: .getDisabledProperties());
0919: }
0920:
0921: /**
0922: * <p>Sets if this TitledTab should be highlighted or not.</p>
0923: *
0924: * <p><strong>Note:</strong> This will only have effect if this TitledTab
0925: * is enabled and a member of a tabbed panel.</p>
0926: *
0927: * @param highlighted true for highlight, otherwise false
0928: */
0929: public void setHighlighted(boolean highlighted) {
0930: super .setHighlighted(highlighted);
0931: updateCurrentStatePanel();
0932: }
0933:
0934: /**
0935: * <p>
0936: * Sets if this TitledTab should be enabled or disabled
0937: * </p>
0938: *
0939: * <p>
0940: * <strong>Note:</strong> since ITP 1.5.0 this method will change the enabled property
0941: * in the {@link TitledTabProperties} for this tab. Enabled/disabled can be controlled by
0942: * modifying the property or this method.
0943: * </p>
0944: *
0945: * @param enabled true for enabled, otherwise false
0946: */
0947: public void setEnabled(boolean enabled) {
0948: super .setEnabled(enabled);
0949: updateCurrentStatePanel();
0950: }
0951:
0952: /**
0953: * Gets the text for the normal state
0954: *
0955: * @return the text or null if no text
0956: */
0957: public String getText() {
0958: return properties.getNormalProperties().getText();
0959: }
0960:
0961: /**
0962: * Sets the text for the normal state
0963: *
0964: * @param text the text or null for no text
0965: */
0966: public void setText(String text) {
0967: properties.getNormalProperties().setText(text);
0968: }
0969:
0970: /**
0971: * Gets the icon for the normal state
0972: *
0973: * @return the icon or null if none
0974: */
0975: public Icon getIcon() {
0976: return properties.getNormalProperties().getIcon();
0977: }
0978:
0979: /**
0980: * Sets the icon for the normal state
0981: *
0982: * @param icon the icon or null for no icon
0983: */
0984: public void setIcon(Icon icon) {
0985: properties.getNormalProperties().setIcon(icon);
0986: }
0987:
0988: /**
0989: * Gets the TitledTabProperties
0990: *
0991: * @return the TitledTabProperties for this TitledTab
0992: */
0993: public TitledTabProperties getProperties() {
0994: return properties;
0995: }
0996:
0997: /**
0998: * Gets the text for the normal state.
0999: *
1000: * Same as getText().
1001: *
1002: * @return the text or null if no text
1003: * @see #getText
1004: * @since ITP 1.1.0
1005: */
1006: public String toString() {
1007: return getText();
1008: }
1009:
1010: /**
1011: * Adds a MouseListener to receive mouse events from this TitledTab.
1012: *
1013: * @param l the MouseListener
1014: */
1015: public synchronized void addMouseListener(MouseListener l) {
1016: if (mouseListeners == null)
1017: mouseListeners = new ArrayList(2);
1018: mouseListeners.add(l);
1019: }
1020:
1021: /**
1022: * Removes a MouseListener
1023: *
1024: * @param l the MouseListener to remove
1025: */
1026: public synchronized void removeMouseListener(MouseListener l) {
1027: if (mouseListeners != null) {
1028: mouseListeners.remove(l);
1029:
1030: if (mouseListeners.size() == 0)
1031: mouseListeners = null;
1032: }
1033: }
1034:
1035: /**
1036: * Gets the mouse listeners
1037: *
1038: * @return the mouse listeners
1039: */
1040: public synchronized MouseListener[] getMouseListeners() {
1041: MouseListener[] listeners = new MouseListener[0];
1042:
1043: if (mouseListeners != null) {
1044: Object[] l = mouseListeners.toArray();
1045: listeners = new MouseListener[l.length];
1046: for (int i = 0; i < l.length; i++)
1047: listeners[i] = (MouseListener) l[i];
1048: }
1049:
1050: return listeners;
1051: }
1052:
1053: /**
1054: * Adds a MouseMotionListener to receive mouse events from this TitledTab.
1055: *
1056: * @param l the MouseMotionListener
1057: */
1058: public synchronized void addMouseMotionListener(
1059: MouseMotionListener l) {
1060: if (mouseMotionListeners == null)
1061: mouseMotionListeners = new ArrayList(2);
1062:
1063: mouseMotionListeners.add(l);
1064: }
1065:
1066: /**
1067: * Removes a MouseMotionListener
1068: *
1069: * @param l the MouseMotionListener to remove
1070: */
1071: public synchronized void removeMouseMotionListener(
1072: MouseMotionListener l) {
1073: if (mouseMotionListeners != null) {
1074: mouseMotionListeners.remove(l);
1075:
1076: if (mouseMotionListeners.size() == 0)
1077: mouseMotionListeners = null;
1078: }
1079: }
1080:
1081: /**
1082: * Gets the mouse motion listeners
1083: *
1084: * @return the mouse motion listeners
1085: */
1086: public synchronized MouseMotionListener[] getMouseMotionListeners() {
1087: MouseMotionListener[] listeners = new MouseMotionListener[0];
1088:
1089: if (mouseMotionListeners != null) {
1090: Object[] l = mouseMotionListeners.toArray();
1091: listeners = new MouseMotionListener[l.length];
1092: for (int i = 0; i < l.length; i++)
1093: listeners[i] = (MouseMotionListener) l[i];
1094: }
1095:
1096: return listeners;
1097: }
1098:
1099: /**
1100: * Gets the Shape for the current active rendering state.
1101: *
1102: * @return the Shape for the active rendering state, null if no special shape
1103: * @since ITP 1.2.0
1104: */
1105: public Shape getShape() {
1106: Shape shape = currentStatePanel.getShape();
1107:
1108: if (shape == null)
1109: return null;
1110:
1111: Point p = SwingUtilities.convertPoint(currentStatePanel, 0, 0,
1112: this );
1113: return new TranslatingShape(shape, p.x, p.y);
1114: }
1115:
1116: protected void setTabbedPanel(TabbedPanel tabbedPanel) {
1117: if (tabbedPanel == null)
1118: HoverManager.getInstance().removeHoverable(eventPanel);
1119:
1120: super .setTabbedPanel(tabbedPanel);
1121:
1122: if (tabbedPanel != null)
1123: HoverManager.getInstance().addHoverable(eventPanel);
1124: }
1125:
1126: private Insets getBorderInsets(Border border) {
1127: return border == null ? InsetsUtil.EMPTY_INSETS : border
1128: .getBorderInsets(this );
1129: }
1130:
1131: private void updateBorders() {
1132: Direction tabAreaOrientation = getTabAreaOrientation();
1133: int raised = properties.getHighlightedRaised();
1134: Insets notRaised = InsetsUtil.setInset(InsetsUtil.EMPTY_INSETS,
1135: tabAreaOrientation, raised);
1136: Border normalBorder = new EmptyBorder(notRaised);
1137:
1138: Insets maxInsets = properties.getBorderSizePolicy() == TitledTabBorderSizePolicy.INDIVIDUAL_SIZE ? null
1139: : InsetsUtil
1140: .max(
1141: getBorderInsets(properties
1142: .getNormalProperties()
1143: .getComponentProperties()
1144: .getBorder()),
1145: InsetsUtil
1146: .max(
1147: getBorderInsets(properties
1148: .getHighlightedProperties()
1149: .getComponentProperties()
1150: .getBorder()),
1151: getBorderInsets(properties
1152: .getDisabledProperties()
1153: .getComponentProperties()
1154: .getBorder())));
1155:
1156: Insets normalInsets = InsetsUtil.rotate(properties
1157: .getNormalProperties().getDirection(), properties
1158: .getNormalProperties().getComponentProperties()
1159: .getInsets());
1160:
1161: Insets disabledInsets = InsetsUtil.rotate(properties
1162: .getDisabledProperties().getDirection(), properties
1163: .getDisabledProperties().getComponentProperties()
1164: .getInsets());
1165:
1166: int edgeInset = Math.min(InsetsUtil.getInset(normalInsets,
1167: tabAreaOrientation.getOpposite()), InsetsUtil.getInset(
1168: disabledInsets, tabAreaOrientation.getOpposite()));
1169:
1170: int normalLowered = Math.min(edgeInset, raised);
1171:
1172: Border innerNormalBorder = getInnerBorder(properties
1173: .getNormalProperties(), tabAreaOrientation,
1174: -normalLowered, maxInsets);
1175: Border innerHighlightBorder = getInnerBorder(properties
1176: .getHighlightedProperties(), tabAreaOrientation, raised
1177: - normalLowered, maxInsets);
1178: Border innerDisabledBorder = getInnerBorder(properties
1179: .getDisabledProperties(), tabAreaOrientation,
1180: -normalLowered, maxInsets);
1181:
1182: normalStatePanel.setBorders(normalBorder, innerNormalBorder);
1183: highlightedStatePanel.setBorders(null, innerHighlightBorder);
1184: disabledStatePanel
1185: .setBorders(normalBorder, innerDisabledBorder);
1186: }
1187:
1188: private void doUpdateTab(Map changes) {
1189: boolean updateBorders = false;
1190:
1191: if (changes == null) {
1192: // Init all
1193: updateBorders = true;
1194:
1195: setFocusableComponent(properties.getFocusable() ? this
1196: : null);
1197: focusBorder.setEnabled(properties.getFocusMarkerEnabled());
1198:
1199: updateHoverListener(properties.getHoverListener());
1200: layout
1201: .setUseSelectedComponentSize(properties
1202: .getSizePolicy() == TitledTabSizePolicy.INDIVIDUAL_SIZE);
1203: } else {
1204: Map m = (Map) changes.get(properties.getMap());
1205: if (m != null) {
1206: Set keySet = m.keySet();
1207:
1208: if (keySet.contains(TitledTabProperties.FOCUSABLE)) {
1209: setFocusableComponent(properties.getFocusable() ? this
1210: : null);
1211: }
1212:
1213: if (keySet
1214: .contains(TitledTabProperties.FOCUS_MARKER_ENABLED)) {
1215: focusBorder.setEnabled(properties
1216: .getFocusMarkerEnabled());
1217: currentStatePanel.getLabel().repaint();
1218: }
1219:
1220: if (keySet.contains(TitledTabProperties.HOVER_LISTENER)) {
1221: updateHoverListener((HoverListener) ((ValueChange) m
1222: .get(TitledTabProperties.HOVER_LISTENER))
1223: .getNewValue());
1224: }
1225:
1226: if (keySet.contains(TitledTabProperties.SIZE_POLICY)) {
1227: layout
1228: .setUseSelectedComponentSize(((TitledTabSizePolicy) ((ValueChange) m
1229: .get(TitledTabProperties.SIZE_POLICY))
1230: .getNewValue()) == TitledTabSizePolicy.INDIVIDUAL_SIZE);
1231: }
1232:
1233: if (keySet
1234: .contains(TitledTabProperties.HIGHLIGHTED_RAISED_AMOUNT)
1235: || keySet
1236: .contains(TitledTabProperties.BORDER_SIZE_POLICY)) {
1237: updateBorders = true;
1238: }
1239:
1240: if (keySet.contains(TitledTabProperties.ENABLED)) {
1241: doSetEnabled(properties.getEnabled());
1242: }
1243: }
1244: }
1245:
1246: updateBorders = normalStatePanel.updateState(changes,
1247: properties.getNormalProperties())
1248: || updateBorders;
1249: updateBorders = highlightedStatePanel.updateState(changes,
1250: properties.getHighlightedProperties())
1251: || updateBorders;
1252: updateBorders = disabledStatePanel.updateState(changes,
1253: properties.getDisabledProperties())
1254: || updateBorders;
1255:
1256: if (updateBorders)
1257: updateBorders();
1258: }
1259:
1260: private void updateHoverListener(HoverListener newHoverListener) {
1261: HoverListener oldHoverListener = hoverListener;
1262: hoverListener = newHoverListener;
1263: if (HoverManager.getInstance().isHovered(eventPanel)) {
1264: if (oldHoverListener != null)
1265: oldHoverListener.mouseExited(new HoverEvent(
1266: TitledTab.this ));
1267: if (hoverListener != null)
1268: hoverListener.mouseEntered(new HoverEvent(
1269: TitledTab.this ));
1270: }
1271: }
1272:
1273: private Border getInnerBorder(TitledTabStateProperties properties,
1274: Direction tabOrientation, int raised, Insets maxInsets) {
1275: Direction tabDir = properties.getDirection();
1276: Insets insets = InsetsUtil.rotate(tabDir, properties
1277: .getComponentProperties().getInsets());
1278:
1279: if (maxInsets != null)
1280: insets = InsetsUtil.add(insets, InsetsUtil.sub(maxInsets,
1281: getBorderInsets(properties.getComponentProperties()
1282: .getBorder())));
1283:
1284: Border border = properties.getComponentProperties().getBorder();
1285: Border innerBorder = new EmptyBorder(InsetsUtil.add(insets,
1286: InsetsUtil.setInset(InsetsUtil.EMPTY_INSETS,
1287: tabOrientation.getOpposite(), raised)));
1288: return border == null ? innerBorder : new CompoundBorder(
1289: border, innerBorder);
1290: }
1291:
1292: private Direction getTabAreaOrientation() {
1293: return getTabbedPanel() == null ? lastTabAreaOrientation
1294: : getTabbedPanel().getProperties()
1295: .getTabAreaOrientation();
1296: }
1297:
1298: private void updateTabAreaOrientation(Direction newDirection) {
1299: if (lastTabAreaOrientation != newDirection) {
1300: lastTabAreaOrientation = newDirection;
1301: updateBorders();
1302:
1303: normalStatePanel.updateShapedPanel(properties
1304: .getNormalProperties());
1305: highlightedStatePanel.updateShapedPanel(properties
1306: .getHighlightedProperties());
1307: disabledStatePanel.updateShapedPanel(properties
1308: .getDisabledProperties());
1309: }
1310: }
1311:
1312: private void updateCurrentStatePanel() {
1313: StatePanel newStatePanel = normalStatePanel;
1314: if (!isEnabled())
1315: newStatePanel = disabledStatePanel;
1316: else if (isHighlighted())
1317: newStatePanel = highlightedStatePanel;
1318:
1319: eventPanel.setToolTipText(newStatePanel.getToolTipText());
1320:
1321: if (currentStatePanel != newStatePanel) {
1322: if (currentStatePanel != null)
1323: currentStatePanel.deactivate();
1324: currentStatePanel = newStatePanel;
1325: currentStatePanel.activate();
1326: }
1327: layout.showComponent(currentStatePanel);
1328: }
1329:
1330: private MouseEvent convertMouseEvent(MouseEvent e) {
1331: Point p = SwingUtilities.convertPoint((JComponent) e
1332: .getSource(), e.getPoint(), TitledTab.this );
1333: return new MouseEvent(TitledTab.this , e.getID(), e.getWhen(), e
1334: .getModifiers(), (int) p.getX(), (int) p.getY(), e
1335: .getClickCount(),
1336: !e.isConsumed() && e.isPopupTrigger(), e.getButton());
1337: }
1338:
1339: private void doSetEnabled(boolean enabled) {
1340: super .setEnabled(enabled);
1341: updateCurrentStatePanel();
1342: }
1343:
1344: public void setUI(PanelUI ui) {
1345: if (getUI() != UI)
1346: super .setUI(UI);
1347: }
1348:
1349: public void updateUI() {
1350: setUI(UI);
1351: }
1352:
1353: public void setOpaque(boolean opaque) {
1354: // Ignore
1355: }
1356:
1357: }
|