218 rolloverTabIndex = -1;
219 focusIndex = -1;
220 c.setLayout(createLayoutManager());
221 installComponents();
222 installDefaults();
223 installListeners();
224 installKeyboardActions();
225 }
226
227 public void uninstallUI(final JComponent c) {
228 uninstallKeyboardActions();
229 uninstallListeners();
230 uninstallDefaults();
231 uninstallComponents();
232 c.setLayout(null);
233
234 this.tabPane = null;
235 }
236
237 /**
238 * Invoked by <code>installUI</code> to create
239 * a layout manager object to manage
240 * the <code>JTabbedPane</code>.
241 *
242 * @return a layout manager object
243 *
244 * @see TabbedPaneLayout
245 * @see javax.swing.JTabbedPane#getTabLayoutPolicy
246 */
247 protected LayoutManager createLayoutManager() {
248 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
249 return new TabbedPaneScrollLayout();
250 } else { /* WRAP_TAB_LAYOUT */
251 return new TabbedPaneLayout();
252 }
253 }
254
255 /* In an attempt to preserve backward compatibility for programs
256 * which have extended BasicTabbedPaneUI to do their own layout, the
257 * UI uses the installed layoutManager (and not tabLayoutPolicy) to
258 * determine if scrollTabLayout is enabled.
259 */
260 boolean scrollableTabLayoutEnabled() {
519 if (mnemonicToIndexMap == null) {
520 initMnemonics();
521 }
522 // [2165820] Mac OS X change: mnemonics need to be triggered with ctrl-option, not just option.
523 mnemonicInputMap.put(KeyStroke.getKeyStroke(mnemonic, Event.ALT_MASK | Event.CTRL_MASK), "setSelectedIndex");
524 mnemonicToIndexMap.put(new Integer(mnemonic), new Integer(index));
525 }
526
527 /**
528 * Installs the state needed for mnemonics.
529 */
530 private void initMnemonics() {
531 mnemonicToIndexMap = new Hashtable<Integer, Integer>();
532 mnemonicInputMap = new ComponentInputMapUIResource(tabPane);
533 mnemonicInputMap.setParent(SwingUtilities.getUIInputMap(tabPane, JComponent.WHEN_IN_FOCUSED_WINDOW));
534 SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_IN_FOCUSED_WINDOW, mnemonicInputMap);
535 }
536
537 /**
538 * Sets the tab the mouse is over by location. This is a cover method
539 * for <code>setRolloverTab(tabForCoordinate(x, y, false))</code>.
540 */
541 private void setRolloverTab(final int x, final int y) {
542 // NOTE:
543 // This calls in with false otherwise it could trigger a validate,
544 // which should NOT happen if the user is only dragging the
545 // mouse around.
546 setRolloverTab(tabForCoordinate(tabPane, x, y, false));
547 }
548
549 /**
550 * Sets the tab the mouse is currently over to <code>index</code>.
551 * <code>index</code> will be -1 if the mouse is no longer over any
552 * tab. No checking is done to ensure the passed in index identifies a
553 * valid tab.
554 *
555 * @param index Index of the tab the mouse is over.
556 * @since 1.5
557 */
558 protected void setRolloverTab(final int index) {
559 rolloverTabIndex = index;
560 }
561
562 /**
563 * Returns the tab the mouse is currently over, or {@code -1} if the mouse is no
564 * longer over any tab.
565 *
566 * @return the tab the mouse is currently over, or {@code -1} if the mouse is no
567 * longer over any tab
568 * @since 1.5
569 */
570 protected int getRolloverTab() {
571 return rolloverTabIndex;
659 return c.getBaseline(pref.width, pref.height) + (cellHeight - pref.height) / 2 + tabInsets.top;
660 } else {
661 final View view = getTextViewForTab(tab);
662 if (view != null) {
663 final int viewHeight = (int)view.getPreferredSpan(View.Y_AXIS);
664 final int baseline = BasicHTML.getHTMLBaseline(view, (int)view.getPreferredSpan(View.X_AXIS), viewHeight);
665 if (baseline >= 0) {
666 return maxTabHeight / 2 - viewHeight / 2 + baseline + getBaselineOffset();
667 }
668 return -1;
669 }
670 }
671 final FontMetrics metrics = getFontMetrics();
672 final int fontHeight = metrics.getHeight();
673 final int fontBaseline = metrics.getAscent();
674 return maxTabHeight / 2 - fontHeight / 2 + fontBaseline + getBaselineOffset();
675 }
676
677 /**
678 * Returns the amount the baseline is offset by. This is typically
679 * the same as <code>getTabLabelShiftY</code>.
680 *
681 * @return amount to offset the baseline by
682 * @since 1.6
683 */
684 protected int getBaselineOffset() {
685 switch (tabPane.getTabPlacement()) {
686 case SwingConstants.TOP:
687 if (tabPane.getTabCount() > 1) {
688 return 1;
689 } else {
690 return -1;
691 }
692 case SwingConstants.BOTTOM:
693 if (tabPane.getTabCount() > 1) {
694 return -1;
695 } else {
696 return 1;
697 }
698 default: // RIGHT|LEFT
699 return (maxTabHeight % 2);
748 ensureCurrentLayout();
749
750 // Paint content border and tab area
751 if (tabsOverlapBorder) {
752 paintContentBorder(g, tabPlacement, selectedIndex);
753 }
754 // If scrollable tabs are enabled, the tab area will be
755 // painted by the scrollable tab panel instead.
756 //
757 if (!scrollableTabLayoutEnabled()) { // WRAP_TAB_LAYOUT
758 paintTabArea(g, tabPlacement, selectedIndex);
759 }
760 if (!tabsOverlapBorder) {
761 paintContentBorder(g, tabPlacement, selectedIndex);
762 }
763 }
764
765 /**
766 * Paints the tabs in the tab area.
767 * Invoked by paint().
768 * The graphics parameter must be a valid <code>Graphics</code>
769 * object. Tab placement may be either:
770 * <code>JTabbedPane.TOP</code>, <code>JTabbedPane.BOTTOM</code>,
771 * <code>JTabbedPane.LEFT</code>, or <code>JTabbedPane.RIGHT</code>.
772 * The selected index must be a valid tabbed pane tab index (0 to
773 * tab count - 1, inclusive) or -1 if no tab is currently selected.
774 * The handling of invalid parameters is unspecified.
775 *
776 * @param g the graphics object to use for rendering
777 * @param tabPlacement the placement for the tabs within the JTabbedPane
778 * @param selectedIndex the tab index of the selected component
779 *
780 * @since 1.4
781 */
782 protected void paintTabArea(final Graphics g, final int tabPlacement, final int selectedIndex) {
783 final int tabCount = tabPane.getTabCount();
784
785 final Rectangle iconRect = new Rectangle(), textRect = new Rectangle();
786 final Rectangle clipRect = g.getClipBounds();
787
788 // Paint tabRuns of tabs from back to front
789 for (int i = runCount - 1; i >= 0; i--) {
790 final int start = tabRuns[i];
791 final int next = tabRuns[(i == runCount - 1) ? 0 : i + 1];
1389 final int tabCount = tabPane.getTabCount();
1390 for (int i = 0; i < tabCount; i++) {
1391 if (rects[i].contains(p.x, p.y)) {
1392 return i;
1393 }
1394 }
1395 return -1;
1396 }
1397
1398 /**
1399 * Returns the bounds of the specified tab in the coordinate space
1400 * of the JTabbedPane component. This is required because the tab rects
1401 * are by default defined in the coordinate space of the component where
1402 * they are rendered, which could be the JTabbedPane
1403 * (for WRAP_TAB_LAYOUT) or a ScrollableTabPanel (SCROLL_TAB_LAYOUT).
1404 * This method should be used whenever the tab rectangle must be relative
1405 * to the JTabbedPane itself and the result should be placed in a
1406 * designated Rectangle object (rather than instantiating and returning
1407 * a new Rectangle each time). The tab index parameter must be a valid
1408 * tabbed pane tab index (0 to tab count - 1, inclusive). The destination
1409 * rectangle parameter must be a valid <code>Rectangle</code> instance.
1410 * The handling of invalid parameters is unspecified.
1411 *
1412 * @param tabIndex the index of the tab
1413 * @param dest the rectangle where the result should be placed
1414 * @return the resulting rectangle
1415 *
1416 * @since 1.4
1417 */
1418 protected Rectangle getTabBounds(final int tabIndex, final Rectangle dest) {
1419 dest.width = rects[tabIndex].width;
1420 dest.height = rects[tabIndex].height;
1421
1422 if (scrollableTabLayoutEnabled()) { // SCROLL_TAB_LAYOUT
1423 // Need to translate coordinates based on viewport location &
1424 // view position
1425 final Point vpp = tabScroller.viewport.getLocation();
1426 final Point viewp = tabScroller.viewport.getViewPosition();
1427 dest.x = rects[tabIndex].x + vpp.x - viewp.x;
1428 dest.y = rects[tabIndex].y + vpp.y - viewp.y;
1429
3700 }
3701 return UIManager.getColor("control");
3702 }
3703
3704 protected void paintComponent(final Graphics g) {
3705 super.paintComponent(g);
3706 if (isParamsSet() && g instanceof Graphics2D) {
3707 final Graphics2D g2 = (Graphics2D)g;
3708 g2.clipRect(0, 0, getWidth(), getHeight());
3709 g2.setColor(getBgColor());
3710 g2.translate(cropx, cropy);
3711 g2.fill(shape);
3712 paintCroppedTabEdge(g);
3713 g2.translate(-cropx, -cropy);
3714 }
3715 }
3716 }
3717
3718 /**
3719 * An ActionMap that populates its contents as necessary. The
3720 * contents are populated by invoking the <code>loadActionMap</code>
3721 * method on the passed in Object.
3722 *
3723 * @version 1.6, 11/17/05
3724 * @author Scott Violet
3725 */
3726 @SuppressWarnings("serial") // Superclass is not serializable across versions
3727 static class LazyActionMap extends ActionMapUIResource {
3728 /**
3729 * Object to invoke <code>loadActionMap</code> on. This may be
3730 * a Class object.
3731 */
3732 private transient Object _loader;
3733
3734 /**
3735 * Installs an ActionMap that will be populated by invoking the
3736 * <code>loadActionMap</code> method on the specified Class
3737 * when necessary.
3738 * <p>
3739 * This should be used if the ActionMap can be shared.
3740 *
3741 * @param c JComponent to install the ActionMap on.
3742 * @param loaderClass Class object that gets loadActionMap invoked
3743 * on.
3744 * @param defaultsKey Key to use to defaults table to check for
3745 * existing map and what resulting Map will be registered on.
3746 */
3747 static void installLazyActionMap(final JComponent c, final Class<AquaTabbedPaneCopyFromBasicUI> loaderClass, final String defaultsKey) {
3748 ActionMap map = (ActionMap)UIManager.get(defaultsKey);
3749 if (map == null) {
3750 map = new LazyActionMap(loaderClass);
3751 UIManager.getLookAndFeelDefaults().put(defaultsKey, map);
3752 }
3753 SwingUtilities.replaceUIActionMap(c, map);
3754 }
3755
3756 /**
3757 * Returns an ActionMap that will be populated by invoking the
3758 * <code>loadActionMap</code> method on the specified Class
3759 * when necessary.
3760 * <p>
3761 * This should be used if the ActionMap can be shared.
3762 *
3763 * @param c JComponent to install the ActionMap on.
3764 * @param loaderClass Class object that gets loadActionMap invoked
3765 * on.
3766 * @param defaultsKey Key to use to defaults table to check for
3767 * existing map and what resulting Map will be registered on.
3768 */
3769 static ActionMap getActionMap(final Class<AquaTabbedPaneCopyFromBasicUI> loaderClass, final String defaultsKey) {
3770 ActionMap map = (ActionMap)UIManager.get(defaultsKey);
3771 if (map == null) {
3772 map = new LazyActionMap(loaderClass);
3773 UIManager.getLookAndFeelDefaults().put(defaultsKey, map);
3774 }
3775 return map;
3776 }
3777
3778 private LazyActionMap(final Class<AquaTabbedPaneCopyFromBasicUI> loader) {
|
218 rolloverTabIndex = -1;
219 focusIndex = -1;
220 c.setLayout(createLayoutManager());
221 installComponents();
222 installDefaults();
223 installListeners();
224 installKeyboardActions();
225 }
226
227 public void uninstallUI(final JComponent c) {
228 uninstallKeyboardActions();
229 uninstallListeners();
230 uninstallDefaults();
231 uninstallComponents();
232 c.setLayout(null);
233
234 this.tabPane = null;
235 }
236
237 /**
238 * Invoked by {@code installUI} to create
239 * a layout manager object to manage
240 * the {@code JTabbedPane}.
241 *
242 * @return a layout manager object
243 *
244 * @see TabbedPaneLayout
245 * @see javax.swing.JTabbedPane#getTabLayoutPolicy
246 */
247 protected LayoutManager createLayoutManager() {
248 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
249 return new TabbedPaneScrollLayout();
250 } else { /* WRAP_TAB_LAYOUT */
251 return new TabbedPaneLayout();
252 }
253 }
254
255 /* In an attempt to preserve backward compatibility for programs
256 * which have extended BasicTabbedPaneUI to do their own layout, the
257 * UI uses the installed layoutManager (and not tabLayoutPolicy) to
258 * determine if scrollTabLayout is enabled.
259 */
260 boolean scrollableTabLayoutEnabled() {
519 if (mnemonicToIndexMap == null) {
520 initMnemonics();
521 }
522 // [2165820] Mac OS X change: mnemonics need to be triggered with ctrl-option, not just option.
523 mnemonicInputMap.put(KeyStroke.getKeyStroke(mnemonic, Event.ALT_MASK | Event.CTRL_MASK), "setSelectedIndex");
524 mnemonicToIndexMap.put(new Integer(mnemonic), new Integer(index));
525 }
526
527 /**
528 * Installs the state needed for mnemonics.
529 */
530 private void initMnemonics() {
531 mnemonicToIndexMap = new Hashtable<Integer, Integer>();
532 mnemonicInputMap = new ComponentInputMapUIResource(tabPane);
533 mnemonicInputMap.setParent(SwingUtilities.getUIInputMap(tabPane, JComponent.WHEN_IN_FOCUSED_WINDOW));
534 SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_IN_FOCUSED_WINDOW, mnemonicInputMap);
535 }
536
537 /**
538 * Sets the tab the mouse is over by location. This is a cover method
539 * for {@code setRolloverTab(tabForCoordinate(x, y, false))}.
540 */
541 private void setRolloverTab(final int x, final int y) {
542 // NOTE:
543 // This calls in with false otherwise it could trigger a validate,
544 // which should NOT happen if the user is only dragging the
545 // mouse around.
546 setRolloverTab(tabForCoordinate(tabPane, x, y, false));
547 }
548
549 /**
550 * Sets the tab the mouse is currently over to {@code index}.
551 * {@code index} will be -1 if the mouse is no longer over any
552 * tab. No checking is done to ensure the passed in index identifies a
553 * valid tab.
554 *
555 * @param index Index of the tab the mouse is over.
556 * @since 1.5
557 */
558 protected void setRolloverTab(final int index) {
559 rolloverTabIndex = index;
560 }
561
562 /**
563 * Returns the tab the mouse is currently over, or {@code -1} if the mouse is no
564 * longer over any tab.
565 *
566 * @return the tab the mouse is currently over, or {@code -1} if the mouse is no
567 * longer over any tab
568 * @since 1.5
569 */
570 protected int getRolloverTab() {
571 return rolloverTabIndex;
659 return c.getBaseline(pref.width, pref.height) + (cellHeight - pref.height) / 2 + tabInsets.top;
660 } else {
661 final View view = getTextViewForTab(tab);
662 if (view != null) {
663 final int viewHeight = (int)view.getPreferredSpan(View.Y_AXIS);
664 final int baseline = BasicHTML.getHTMLBaseline(view, (int)view.getPreferredSpan(View.X_AXIS), viewHeight);
665 if (baseline >= 0) {
666 return maxTabHeight / 2 - viewHeight / 2 + baseline + getBaselineOffset();
667 }
668 return -1;
669 }
670 }
671 final FontMetrics metrics = getFontMetrics();
672 final int fontHeight = metrics.getHeight();
673 final int fontBaseline = metrics.getAscent();
674 return maxTabHeight / 2 - fontHeight / 2 + fontBaseline + getBaselineOffset();
675 }
676
677 /**
678 * Returns the amount the baseline is offset by. This is typically
679 * the same as {@code getTabLabelShiftY}.
680 *
681 * @return amount to offset the baseline by
682 * @since 1.6
683 */
684 protected int getBaselineOffset() {
685 switch (tabPane.getTabPlacement()) {
686 case SwingConstants.TOP:
687 if (tabPane.getTabCount() > 1) {
688 return 1;
689 } else {
690 return -1;
691 }
692 case SwingConstants.BOTTOM:
693 if (tabPane.getTabCount() > 1) {
694 return -1;
695 } else {
696 return 1;
697 }
698 default: // RIGHT|LEFT
699 return (maxTabHeight % 2);
748 ensureCurrentLayout();
749
750 // Paint content border and tab area
751 if (tabsOverlapBorder) {
752 paintContentBorder(g, tabPlacement, selectedIndex);
753 }
754 // If scrollable tabs are enabled, the tab area will be
755 // painted by the scrollable tab panel instead.
756 //
757 if (!scrollableTabLayoutEnabled()) { // WRAP_TAB_LAYOUT
758 paintTabArea(g, tabPlacement, selectedIndex);
759 }
760 if (!tabsOverlapBorder) {
761 paintContentBorder(g, tabPlacement, selectedIndex);
762 }
763 }
764
765 /**
766 * Paints the tabs in the tab area.
767 * Invoked by paint().
768 * The graphics parameter must be a valid {@code Graphics}
769 * object. Tab placement may be either:
770 * {@code JTabbedPane.TOP}, {@code JTabbedPane.BOTTOM},
771 * {@code JTabbedPane.LEFT}, or {@code JTabbedPane.RIGHT}.
772 * The selected index must be a valid tabbed pane tab index (0 to
773 * tab count - 1, inclusive) or -1 if no tab is currently selected.
774 * The handling of invalid parameters is unspecified.
775 *
776 * @param g the graphics object to use for rendering
777 * @param tabPlacement the placement for the tabs within the JTabbedPane
778 * @param selectedIndex the tab index of the selected component
779 *
780 * @since 1.4
781 */
782 protected void paintTabArea(final Graphics g, final int tabPlacement, final int selectedIndex) {
783 final int tabCount = tabPane.getTabCount();
784
785 final Rectangle iconRect = new Rectangle(), textRect = new Rectangle();
786 final Rectangle clipRect = g.getClipBounds();
787
788 // Paint tabRuns of tabs from back to front
789 for (int i = runCount - 1; i >= 0; i--) {
790 final int start = tabRuns[i];
791 final int next = tabRuns[(i == runCount - 1) ? 0 : i + 1];
1389 final int tabCount = tabPane.getTabCount();
1390 for (int i = 0; i < tabCount; i++) {
1391 if (rects[i].contains(p.x, p.y)) {
1392 return i;
1393 }
1394 }
1395 return -1;
1396 }
1397
1398 /**
1399 * Returns the bounds of the specified tab in the coordinate space
1400 * of the JTabbedPane component. This is required because the tab rects
1401 * are by default defined in the coordinate space of the component where
1402 * they are rendered, which could be the JTabbedPane
1403 * (for WRAP_TAB_LAYOUT) or a ScrollableTabPanel (SCROLL_TAB_LAYOUT).
1404 * This method should be used whenever the tab rectangle must be relative
1405 * to the JTabbedPane itself and the result should be placed in a
1406 * designated Rectangle object (rather than instantiating and returning
1407 * a new Rectangle each time). The tab index parameter must be a valid
1408 * tabbed pane tab index (0 to tab count - 1, inclusive). The destination
1409 * rectangle parameter must be a valid {@code Rectangle} instance.
1410 * The handling of invalid parameters is unspecified.
1411 *
1412 * @param tabIndex the index of the tab
1413 * @param dest the rectangle where the result should be placed
1414 * @return the resulting rectangle
1415 *
1416 * @since 1.4
1417 */
1418 protected Rectangle getTabBounds(final int tabIndex, final Rectangle dest) {
1419 dest.width = rects[tabIndex].width;
1420 dest.height = rects[tabIndex].height;
1421
1422 if (scrollableTabLayoutEnabled()) { // SCROLL_TAB_LAYOUT
1423 // Need to translate coordinates based on viewport location &
1424 // view position
1425 final Point vpp = tabScroller.viewport.getLocation();
1426 final Point viewp = tabScroller.viewport.getViewPosition();
1427 dest.x = rects[tabIndex].x + vpp.x - viewp.x;
1428 dest.y = rects[tabIndex].y + vpp.y - viewp.y;
1429
3700 }
3701 return UIManager.getColor("control");
3702 }
3703
3704 protected void paintComponent(final Graphics g) {
3705 super.paintComponent(g);
3706 if (isParamsSet() && g instanceof Graphics2D) {
3707 final Graphics2D g2 = (Graphics2D)g;
3708 g2.clipRect(0, 0, getWidth(), getHeight());
3709 g2.setColor(getBgColor());
3710 g2.translate(cropx, cropy);
3711 g2.fill(shape);
3712 paintCroppedTabEdge(g);
3713 g2.translate(-cropx, -cropy);
3714 }
3715 }
3716 }
3717
3718 /**
3719 * An ActionMap that populates its contents as necessary. The
3720 * contents are populated by invoking the {@code loadActionMap}
3721 * method on the passed in Object.
3722 *
3723 * @version 1.6, 11/17/05
3724 * @author Scott Violet
3725 */
3726 @SuppressWarnings("serial") // Superclass is not serializable across versions
3727 static class LazyActionMap extends ActionMapUIResource {
3728 /**
3729 * Object to invoke {@code loadActionMap} on. This may be
3730 * a Class object.
3731 */
3732 private transient Object _loader;
3733
3734 /**
3735 * Installs an ActionMap that will be populated by invoking the
3736 * {@code loadActionMap} method on the specified Class
3737 * when necessary.
3738 * <p>
3739 * This should be used if the ActionMap can be shared.
3740 *
3741 * @param c JComponent to install the ActionMap on.
3742 * @param loaderClass Class object that gets loadActionMap invoked
3743 * on.
3744 * @param defaultsKey Key to use to defaults table to check for
3745 * existing map and what resulting Map will be registered on.
3746 */
3747 static void installLazyActionMap(final JComponent c, final Class<AquaTabbedPaneCopyFromBasicUI> loaderClass, final String defaultsKey) {
3748 ActionMap map = (ActionMap)UIManager.get(defaultsKey);
3749 if (map == null) {
3750 map = new LazyActionMap(loaderClass);
3751 UIManager.getLookAndFeelDefaults().put(defaultsKey, map);
3752 }
3753 SwingUtilities.replaceUIActionMap(c, map);
3754 }
3755
3756 /**
3757 * Returns an ActionMap that will be populated by invoking the
3758 * {@code loadActionMap} method on the specified Class
3759 * when necessary.
3760 * <p>
3761 * This should be used if the ActionMap can be shared.
3762 *
3763 * @param c JComponent to install the ActionMap on.
3764 * @param loaderClass Class object that gets loadActionMap invoked
3765 * on.
3766 * @param defaultsKey Key to use to defaults table to check for
3767 * existing map and what resulting Map will be registered on.
3768 */
3769 static ActionMap getActionMap(final Class<AquaTabbedPaneCopyFromBasicUI> loaderClass, final String defaultsKey) {
3770 ActionMap map = (ActionMap)UIManager.get(defaultsKey);
3771 if (map == null) {
3772 map = new LazyActionMap(loaderClass);
3773 UIManager.getLookAndFeelDefaults().put(defaultsKey, map);
3774 }
3775 return map;
3776 }
3777
3778 private LazyActionMap(final Class<AquaTabbedPaneCopyFromBasicUI> loader) {
|