SynthTabbedPaneUI.java 34.0 KB
Newer Older
D
duke 已提交
1
/*
2
 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
D
duke 已提交
3 4 5 6
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
7
 * published by the Free Software Foundation.  Oracle designates this
D
duke 已提交
8
 * particular file as subject to the "Classpath" exception as provided
9
 * by Oracle in the LICENSE file that accompanied this code.
D
duke 已提交
10 11 12 13 14 15 16 17 18 19 20
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
21 22 23
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
D
duke 已提交
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
 */

package javax.swing.plaf.synth;

import javax.swing.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import javax.swing.text.View;

import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import sun.swing.SwingUtilities2;

/**
P
peterz 已提交
40 41 42 43 44
 * Provides the Synth L&F UI delegate for
 * {@link javax.swing.JTabbedPane}.
 *
 * <p>Looks up the {@code selectedTabPadInsets} property from the Style,
 * which represents additional insets for the selected tab.
D
duke 已提交
45 46
 *
 * @author Scott Violet
P
peterz 已提交
47
 * @since 1.7
D
duke 已提交
48
 */
P
peterz 已提交
49 50 51
public class SynthTabbedPaneUI extends BasicTabbedPaneUI
                               implements PropertyChangeListener, SynthUI {

P
peterz 已提交
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
    /**
     * <p>If non-zero, tabOverlap indicates the amount that the tab bounds
     * should be altered such that they would overlap with a tab on either the
     * leading or trailing end of a run (ie: in TOP, this would be on the left
     * or right).</p>

     * <p>A positive overlap indicates that tabs should overlap right/down,
     * while a negative overlap indicates tha tabs should overlap left/up.</p>
     *
     * <p>When tabOverlap is specified, it both changes the x position and width
     * of the tab if in TOP or BOTTOM placement, and changes the y position and
     * height if in LEFT or RIGHT placement.</p>
     *
     * <p>This is done for the following reason. Consider a run of 10 tabs.
     * There are 9 gaps between these tabs. If you specified a tabOverlap of
     * "-1", then each of the tabs "x" values will be shifted left. This leaves
     * 9 pixels of space to the right of the right-most tab unpainted. So, each
     * tab's width is also extended by 1 pixel to make up the difference.</p>
     *
     * <p>This property respects the RTL component orientation.</p>
     */
    private int tabOverlap = 0;

    /**
     * When a tabbed pane has multiple rows of tabs, this indicates whether
     * the tabs in the upper row(s) should extend to the base of the tab area,
     * or whether they should remain at their normal tab height. This does not
     * affect the bounds of the tabs, only the bounds of area painted by the
     * tabs. The text position does not change. The result is that the bottom
     * border of the upper row of tabs becomes fully obscured by the lower tabs,
     * resulting in a cleaner look.
     */
    private boolean extendTabsToBase = false;

D
duke 已提交
86 87 88 89 90 91 92 93 94
    private SynthContext tabAreaContext;
    private SynthContext tabContext;
    private SynthContext tabContentContext;

    private SynthStyle style;
    private SynthStyle tabStyle;
    private SynthStyle tabAreaStyle;
    private SynthStyle tabContentStyle;

95 96
    private Rectangle textRect = new Rectangle();
    private Rectangle iconRect = new Rectangle();
D
duke 已提交
97 98 99

    private Rectangle tabAreaBounds = new Rectangle();

P
peterz 已提交
100 101 102 103 104 105 106 107
    //added for the Nimbus look and feel, where the tab area is painted differently depending on the
    //state for the selected tab
    private boolean tabAreaStatesMatchSelectedTab = false;
    //added for the Nimbus LAF to ensure that the labels don't move whether the tab is selected or not
    private boolean nudgeSelectedLabel = true;

    private boolean selectedTabIsPressed = false;

P
peterz 已提交
108 109 110 111 112 113
    /**
     * Creates a new UI object for the given component.
     *
     * @param c component to create UI object for
     * @return the UI object
     */
D
duke 已提交
114 115 116 117
    public static ComponentUI createUI(JComponent c) {
        return new SynthTabbedPaneUI();
    }

118 119 120
    private SynthTabbedPaneUI() {
    }

D
duke 已提交
121 122 123 124
    private boolean scrollableTabLayoutEnabled() {
        return (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT);
    }

P
peterz 已提交
125 126 127 128
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
129 130 131 132 133 134 135 136 137 138 139 140 141
    protected void installDefaults() {
        updateStyle(tabPane);
    }

    private void updateStyle(JTabbedPane c) {
        SynthContext context = getContext(c, ENABLED);
        SynthStyle oldStyle = style;
        style = SynthLookAndFeel.updateStyle(context, this);
        // Add properties other than JComponent colors, Borders and
        // opacity settings here:
        if (style != oldStyle) {
            tabRunOverlay =
                style.getInt(context, "TabbedPane.tabRunOverlay", 0);
P
peterz 已提交
142 143 144
            tabOverlap = style.getInt(context, "TabbedPane.tabOverlap", 0);
            extendTabsToBase = style.getBoolean(context,
                    "TabbedPane.extendTabsToBase", false);
D
duke 已提交
145 146 147 148 149 150
            textIconGap = style.getInt(context, "TabbedPane.textIconGap", 0);
            selectedTabPadInsets = (Insets)style.get(context,
                "TabbedPane.selectedTabPadInsets");
            if (selectedTabPadInsets == null) {
                selectedTabPadInsets = new Insets(0, 0, 0, 0);
            }
P
peterz 已提交
151 152 153 154
            tabAreaStatesMatchSelectedTab = style.getBoolean(context,
                    "TabbedPane.tabAreaStatesMatchSelectedTab", false);
            nudgeSelectedLabel = style.getBoolean(context,
                    "TabbedPane.nudgeSelectedLabel", true);
D
duke 已提交
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
            if (oldStyle != null) {
                uninstallKeyboardActions();
                installKeyboardActions();
            }
        }
        context.dispose();

        if (tabContext != null) {
            tabContext.dispose();
        }
        tabContext = getContext(c, Region.TABBED_PANE_TAB, ENABLED);
        this.tabStyle = SynthLookAndFeel.updateStyle(tabContext, this);
        tabInsets = tabStyle.getInsets(tabContext, null);


        if (tabAreaContext != null) {
            tabAreaContext.dispose();
        }
        tabAreaContext = getContext(c, Region.TABBED_PANE_TAB_AREA, ENABLED);
        this.tabAreaStyle = SynthLookAndFeel.updateStyle(tabAreaContext, this);
        tabAreaInsets = tabAreaStyle.getInsets(tabAreaContext, null);


        if (tabContentContext != null) {
            tabContentContext.dispose();
        }
        tabContentContext = getContext(c, Region.TABBED_PANE_CONTENT, ENABLED);
        this.tabContentStyle = SynthLookAndFeel.updateStyle(tabContentContext,
                                                            this);
        contentBorderInsets =
            tabContentStyle.getInsets(tabContentContext, null);
    }

P
peterz 已提交
188 189 190 191
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
192 193 194 195 196
    protected void installListeners() {
        super.installListeners();
        tabPane.addPropertyChangeListener(this);
    }

P
peterz 已提交
197 198 199 200
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
201 202 203 204 205
    protected void uninstallListeners() {
        super.uninstallListeners();
        tabPane.removePropertyChangeListener(this);
    }

P
peterz 已提交
206 207 208 209
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
    protected void uninstallDefaults() {
        SynthContext context = getContext(tabPane, ENABLED);
        style.uninstallDefaults(context);
        context.dispose();
        style = null;

        tabStyle.uninstallDefaults(tabContext);
        tabContext.dispose();
        tabContext = null;
        tabStyle = null;

        tabAreaStyle.uninstallDefaults(tabAreaContext);
        tabAreaContext.dispose();
        tabAreaContext = null;
        tabAreaStyle = null;

        tabContentStyle.uninstallDefaults(tabContentContext);
        tabContentContext.dispose();
        tabContentContext = null;
        tabContentStyle = null;
    }

P
peterz 已提交
232 233 234 235
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
236
    public SynthContext getContext(JComponent c) {
P
peterz 已提交
237
        return getContext(c, SynthLookAndFeel.getComponentState(c));
D
duke 已提交
238 239
    }

P
peterz 已提交
240
    private SynthContext getContext(JComponent c, int state) {
D
duke 已提交
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
        return SynthContext.getContext(SynthContext.class, c,
                    SynthLookAndFeel.getRegion(c),style, state);
    }

    private SynthContext getContext(JComponent c, Region subregion, int state){
        SynthStyle style = null;
        Class klass = SynthContext.class;

        if (subregion == Region.TABBED_PANE_TAB) {
            style = tabStyle;
        }
        else if (subregion == Region.TABBED_PANE_TAB_AREA) {
            style = tabAreaStyle;
        }
        else if (subregion == Region.TABBED_PANE_CONTENT) {
            style = tabContentStyle;
        }
        return SynthContext.getContext(klass, c, subregion, style, state);
    }

P
peterz 已提交
261 262 263 264
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
265
    protected JButton createScrollButton(int direction) {
P
peterz 已提交
266 267 268 269 270 271 272 273
        // added for Nimbus LAF so that it can use the basic arrow buttons
        // UIManager is queried directly here because this is called before
        // updateStyle is called so the style can not be queried directly
        if (UIManager.getBoolean("TabbedPane.useBasicArrows")) {
            JButton btn = super.createScrollButton(direction);
            btn.setBorder(BorderFactory.createEmptyBorder());
            return btn;
        }
D
duke 已提交
274 275 276
        return new SynthScrollableTabButton(direction);
    }

P
peterz 已提交
277 278 279 280
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
281 282 283 284 285 286
    public void propertyChange(PropertyChangeEvent e) {
        if (SynthLookAndFeel.shouldUpdateStyle(e)) {
            updateStyle(tabPane);
        }
    }

P
peterz 已提交
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
    /**
     * @inheritDoc
     *
     * Overridden to keep track of whether the selected tab is also pressed.
     */
    @Override
    protected MouseListener createMouseListener() {
        final MouseListener delegate = super.createMouseListener();
        final MouseMotionListener delegate2 = (MouseMotionListener)delegate;
        return new MouseListener() {
            public void mouseClicked(MouseEvent e) { delegate.mouseClicked(e); }
            public void mouseEntered(MouseEvent e) { delegate.mouseEntered(e); }
            public void mouseExited(MouseEvent e) { delegate.mouseExited(e); }

            public void mousePressed(MouseEvent e) {
                if (!tabPane.isEnabled()) {
                    return;
                }

                int tabIndex = tabForCoordinate(tabPane, e.getX(), e.getY());
                if (tabIndex >= 0 && tabPane.isEnabledAt(tabIndex)) {
                    if (tabIndex == tabPane.getSelectedIndex()) {
                        // Clicking on selected tab
                        selectedTabIsPressed = true;
                        //TODO need to just repaint the tab area!
                        tabPane.repaint();
                    }
                }

                //forward the event (this will set the selected index, or none at all
                delegate.mousePressed(e);
            }

            public void mouseReleased(MouseEvent e) {
                if (selectedTabIsPressed) {
                    selectedTabIsPressed = false;
                    //TODO need to just repaint the tab area!
                    tabPane.repaint();
                }
                //forward the event
                delegate.mouseReleased(e);

                //hack: The super method *should* be setting the mouse-over property correctly
                //here, but it doesn't. That is, when the mouse is released, whatever tab is below the
                //released mouse should be in rollover state. But, if you select a tab and don't
                //move the mouse, this doesn't happen. Hence, forwarding the event.
                delegate2.mouseMoved(e);
            }
        };
    }

P
peterz 已提交
338 339 340
    /**
     * @inheritDoc
     */
P
peterz 已提交
341 342 343 344 345 346 347 348 349
    @Override
    protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) {
        if (nudgeSelectedLabel) {
            return super.getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
        } else {
            return 0;
        }
    }

P
peterz 已提交
350 351 352
    /**
     * @inheritDoc
     */
P
peterz 已提交
353 354 355 356 357 358 359 360 361
    @Override
    protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
        if (nudgeSelectedLabel) {
            return super.getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
        } else {
            return 0;
        }
    }

P
peterz 已提交
362
    /**
363 364 365 366 367 368 369 370 371 372
     * Notifies this UI delegate to repaint the specified component.
     * This method paints the component background, then calls
     * the {@link #paint(SynthContext,Graphics)} method.
     *
     * <p>In general, this method does not need to be overridden by subclasses.
     * All Look and Feel rendering code should reside in the {@code paint} method.
     *
     * @param g the {@code Graphics} object used for painting
     * @param c the component being painted
     * @see #paint(SynthContext,Graphics)
P
peterz 已提交
373 374
     */
    @Override
D
duke 已提交
375 376 377 378 379 380 381 382 383 384
    public void update(Graphics g, JComponent c) {
        SynthContext context = getContext(c);

        SynthLookAndFeel.update(context, g);
        context.getPainter().paintTabbedPaneBackground(context,
                          g, 0, 0, c.getWidth(), c.getHeight());
        paint(context, g);
        context.dispose();
    }

P
peterz 已提交
385 386 387 388
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
    protected int getBaseline(int tab) {
        if (tabPane.getTabComponentAt(tab) != null ||
                getTextViewForTab(tab) != null) {
            return super.getBaseline(tab);
        }
        String title = tabPane.getTitleAt(tab);
        Font font = tabContext.getStyle().getFont(tabContext);
        FontMetrics metrics = getFontMetrics(font);
        Icon icon = getIconForTab(tab);
        textRect.setBounds(0, 0, 0, 0);
        iconRect.setBounds(0, 0, 0, 0);
        calcRect.setBounds(0, 0, Short.MAX_VALUE, maxTabHeight);
        tabContext.getStyle().getGraphicsUtils(tabContext).layoutText(
                tabContext, metrics, title, icon, SwingUtilities.CENTER,
                SwingUtilities.CENTER, SwingUtilities.LEADING,
404
                SwingUtilities.CENTER, calcRect,
D
duke 已提交
405 406 407 408
                iconRect, textRect, textIconGap);
        return textRect.y + metrics.getAscent() + getBaselineOffset();
    }

P
peterz 已提交
409 410 411 412
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
413 414 415 416 417
    public void paintBorder(SynthContext context, Graphics g, int x,
                            int y, int w, int h) {
        context.getPainter().paintTabbedPaneBorder(context, g, x, y, w, h);
    }

P
peterz 已提交
418
    /**
419 420 421 422 423 424 425
     * Paints the specified component according to the Look and Feel.
     * <p>This method is not used by Synth Look and Feel.
     * Painting is handled by the {@link #paint(SynthContext,Graphics)} method.
     *
     * @param g the {@code Graphics} object used for painting
     * @param c the component being painted
     * @see #paint(SynthContext,Graphics)
P
peterz 已提交
426 427
     */
    @Override
D
duke 已提交
428 429 430 431 432 433 434
    public void paint(Graphics g, JComponent c) {
        SynthContext context = getContext(c);

        paint(context, g);
        context.dispose();
    }

P
peterz 已提交
435 436 437 438
    /**
     * Paints the specified component.
     *
     * @param context context for the component being painted
439 440
     * @param g the {@code Graphics} object used for painting
     * @see #update(Graphics,JComponent)
P
peterz 已提交
441
     */
D
duke 已提交
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
    protected void paint(SynthContext context, Graphics g) {
        int selectedIndex = tabPane.getSelectedIndex();
        int tabPlacement = tabPane.getTabPlacement();

        ensureCurrentLayout();

        // Paint tab area
        // If scrollable tabs are enabled, the tab area will be
        // painted by the scrollable tab panel instead.
        //
        if (!scrollableTabLayoutEnabled()) { // WRAP_TAB_LAYOUT
            Insets insets = tabPane.getInsets();
            int x = insets.left;
            int y = insets.top;
            int width = tabPane.getWidth() - insets.left - insets.right;
            int height = tabPane.getHeight() - insets.top - insets.bottom;
            int size;
            switch(tabPlacement) {
            case LEFT:
                width = calculateTabAreaWidth(tabPlacement, runCount,
                                              maxTabWidth);
                break;
            case RIGHT:
                size = calculateTabAreaWidth(tabPlacement, runCount,
                                             maxTabWidth);
                x = x + width - size;
                width = size;
                break;
            case BOTTOM:
                size = calculateTabAreaHeight(tabPlacement, runCount,
                                              maxTabHeight);
                y = y + height - size;
                height = size;
                break;
            case TOP:
            default:
                height = calculateTabAreaHeight(tabPlacement, runCount,
                                                maxTabHeight);
            }

            tabAreaBounds.setBounds(x, y, width, height);

            if (g.getClipBounds().intersects(tabAreaBounds)) {
                paintTabArea(tabAreaContext, g, tabPlacement,
                         selectedIndex, tabAreaBounds);
            }
        }

        // Paint content border
        paintContentBorder(tabContentContext, g, tabPlacement, selectedIndex);
    }


P
peterz 已提交
495
    private void paintTabArea(SynthContext ss, Graphics g,
D
duke 已提交
496 497 498 499
                                int tabPlacement, int selectedIndex,
                                Rectangle tabAreaBounds) {
        Rectangle clipRect = g.getClipBounds();

P
peterz 已提交
500 501 502 503 504 505 506 507 508 509 510 511 512 513
        //if the tab area's states should match that of the selected tab, then
        //first update the selected tab's states, then set the state
        //for the tab area to match
        //otherwise, restore the tab area's state to ENABLED (which is the
        //only supported state otherwise).
        if (tabAreaStatesMatchSelectedTab && selectedIndex >= 0) {
            updateTabContext(selectedIndex, true, selectedTabIsPressed,
                              (getRolloverTab() == selectedIndex),
                              (getFocusIndex() == selectedIndex));
            ss.setComponentState(tabContext.getComponentState());
        } else {
            ss.setComponentState(SynthConstants.ENABLED);
        }

D
duke 已提交
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
        // Paint the tab area.
        SynthLookAndFeel.updateSubregion(ss, g, tabAreaBounds);
        ss.getPainter().paintTabbedPaneTabAreaBackground(ss, g,
             tabAreaBounds.x, tabAreaBounds.y, tabAreaBounds.width,
             tabAreaBounds.height, tabPlacement);
        ss.getPainter().paintTabbedPaneTabAreaBorder(ss, g, tabAreaBounds.x,
             tabAreaBounds.y, tabAreaBounds.width, tabAreaBounds.height,
             tabPlacement);

        int tabCount = tabPane.getTabCount();

        iconRect.setBounds(0, 0, 0, 0);
        textRect.setBounds(0, 0, 0, 0);

        // Paint tabRuns of tabs from back to front
        for (int i = runCount - 1; i >= 0; i--) {
            int start = tabRuns[i];
            int next = tabRuns[(i == runCount - 1)? 0 : i + 1];
            int end = (next != 0? next - 1: tabCount - 1);
            for (int j = start; j <= end; j++) {
                if (rects[j].intersects(clipRect) && selectedIndex != j) {
                    paintTab(tabContext, g, tabPlacement, rects, j, iconRect,
                             textRect);
                }
            }
        }

        if (selectedIndex >= 0) {
            if (rects[selectedIndex].intersects(clipRect)) {
                paintTab(tabContext, g, tabPlacement, rects, selectedIndex,
                         iconRect, textRect);
            }
        }
    }

P
peterz 已提交
549 550 551 552
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
553 554 555 556 557 558
    protected void setRolloverTab(int index) {
        int oldRolloverTab = getRolloverTab();
        super.setRolloverTab(index);

        Rectangle r = null;

P
peterz 已提交
559 560 561 562 563 564 565 566 567
        if (oldRolloverTab != index && tabAreaStatesMatchSelectedTab) {
            //TODO need to just repaint the tab area!
            tabPane.repaint();
        } else {
            if ((oldRolloverTab >= 0) && (oldRolloverTab < tabPane.getTabCount())) {
                r = getTabBounds(tabPane, oldRolloverTab);
                if (r != null) {
                    tabPane.repaint(r);
                }
D
duke 已提交
568 569
            }

P
peterz 已提交
570 571 572 573 574
            if (index >= 0) {
                r = getTabBounds(tabPane, index);
                if (r != null) {
                    tabPane.repaint(r);
                }
D
duke 已提交
575 576 577 578
            }
        }
    }

P
peterz 已提交
579
    private void paintTab(SynthContext ss, Graphics g,
D
duke 已提交
580 581 582 583 584
                            int tabPlacement, Rectangle[] rects, int tabIndex,
                            Rectangle iconRect, Rectangle textRect) {
        Rectangle tabRect = rects[tabIndex];
        int selectedIndex = tabPane.getSelectedIndex();
        boolean isSelected = selectedIndex == tabIndex;
P
peterz 已提交
585 586 587
        updateTabContext(tabIndex, isSelected, isSelected && selectedTabIsPressed,
                            (getRolloverTab() == tabIndex),
                            (getFocusIndex() == tabIndex));
D
duke 已提交
588 589

        SynthLookAndFeel.updateSubregion(ss, g, tabRect);
P
peterz 已提交
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
        int x = tabRect.x;
        int y = tabRect.y;
        int height = tabRect.height;
        int width = tabRect.width;
        int placement = tabPane.getTabPlacement();
        if (extendTabsToBase && runCount > 1) {
            //paint this tab such that its edge closest to the base is equal to
            //edge of the selected tab closest to the base. In terms of the TOP
            //tab placement, this will cause the bottom of each tab to be
            //painted even with the bottom of the selected tab. This is because
            //in each tab placement (TOP, LEFT, BOTTOM, RIGHT) the selected tab
            //is closest to the base.
            if (selectedIndex >= 0) {
                Rectangle r = rects[selectedIndex];
                switch (placement) {
                    case TOP:
                        int bottomY = r.y + r.height;
                        height = bottomY - tabRect.y;
                        break;
                    case LEFT:
                        int rightX = r.x + r.width;
                        width = rightX - tabRect.x;
                        break;
                    case BOTTOM:
                        int topY = r.y;
                        height = (tabRect.y + tabRect.height) - topY;
                        y = topY;
                        break;
                    case RIGHT:
                        int leftX = r.x;
                        width = (tabRect.x + tabRect.width) - leftX;
                        x = leftX;
                        break;
                }
            }
        }
        tabContext.getPainter().paintTabbedPaneTabBackground(tabContext, g,
                x, y, width, height, tabIndex, placement);
D
duke 已提交
628
        tabContext.getPainter().paintTabbedPaneTabBorder(tabContext, g,
P
peterz 已提交
629
                x, y, width, height, tabIndex, placement);
D
duke 已提交
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646

        if (tabPane.getTabComponentAt(tabIndex) == null) {
            String title = tabPane.getTitleAt(tabIndex);
            Font font = ss.getStyle().getFont(ss);
            FontMetrics metrics = SwingUtilities2.getFontMetrics(tabPane, g, font);
            Icon icon = getIconForTab(tabIndex);

            layoutLabel(ss, tabPlacement, metrics, tabIndex, title, icon,
                    tabRect, iconRect, textRect, isSelected);

            paintText(ss, g, tabPlacement, font, metrics,
                    tabIndex, title, textRect, isSelected);

            paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
        }
    }

P
peterz 已提交
647
    private void layoutLabel(SynthContext ss, int tabPlacement,
D
duke 已提交
648 649 650 651 652 653 654 655 656 657 658 659 660
                               FontMetrics metrics, int tabIndex,
                               String title, Icon icon,
                               Rectangle tabRect, Rectangle iconRect,
                               Rectangle textRect, boolean isSelected ) {
        View v = getTextViewForTab(tabIndex);
        if (v != null) {
            tabPane.putClientProperty("html", v);
        }

        textRect.x = textRect.y = iconRect.x = iconRect.y = 0;

        ss.getStyle().getGraphicsUtils(ss).layoutText(ss, metrics, title,
                         icon, SwingUtilities.CENTER, SwingUtilities.CENTER,
661
                         SwingUtilities.LEADING, SwingUtilities.CENTER,
D
duke 已提交
662 663 664 665 666 667 668 669 670 671 672 673
                         tabRect, iconRect, textRect, textIconGap);

        tabPane.putClientProperty("html", null);

        int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
        int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
        iconRect.x += xNudge;
        iconRect.y += yNudge;
        textRect.x += xNudge;
        textRect.y += yNudge;
    }

P
peterz 已提交
674
    private void paintText(SynthContext ss,
D
duke 已提交
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
                             Graphics g, int tabPlacement,
                             Font font, FontMetrics metrics, int tabIndex,
                             String title, Rectangle textRect,
                             boolean isSelected) {
        g.setFont(font);

        View v = getTextViewForTab(tabIndex);
        if (v != null) {
            // html
            v.paint(g, textRect);
        } else {
            // plain text
            int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);

            g.setColor(ss.getStyle().getColor(ss, ColorType.TEXT_FOREGROUND));
            ss.getStyle().getGraphicsUtils(ss).paintText(ss, g, title,
                                  textRect, mnemIndex);
        }
    }


P
peterz 已提交
696
    private void paintContentBorder(SynthContext ss, Graphics g,
D
duke 已提交
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742
                                      int tabPlacement, int selectedIndex) {
        int width = tabPane.getWidth();
        int height = tabPane.getHeight();
        Insets insets = tabPane.getInsets();

        int x = insets.left;
        int y = insets.top;
        int w = width - insets.right - insets.left;
        int h = height - insets.top - insets.bottom;

        switch(tabPlacement) {
          case LEFT:
              x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
              w -= (x - insets.left);
              break;
          case RIGHT:
              w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
              break;
          case BOTTOM:
              h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
              break;
          case TOP:
          default:
              y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
              h -= (y - insets.top);
        }
        SynthLookAndFeel.updateSubregion(ss, g, new Rectangle(x, y, w, h));
        ss.getPainter().paintTabbedPaneContentBackground(ss, g, x, y,
                                                           w, h);
        ss.getPainter().paintTabbedPaneContentBorder(ss, g, x, y, w, h);
    }

    private void ensureCurrentLayout() {
        if (!tabPane.isValid()) {
            tabPane.validate();
        }
        /* If tabPane doesn't have a peer yet, the validate() call will
         * silently fail.  We handle that by forcing a layout if tabPane
         * is still invalid.  See bug 4237677.
         */
        if (!tabPane.isValid()) {
            TabbedPaneLayout layout = (TabbedPaneLayout)tabPane.getLayout();
            layout.calculateLayoutInfo();
        }
    }

P
peterz 已提交
743 744 745 746
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
747 748 749 750 751 752 753 754 755 756 757 758
    protected int calculateMaxTabHeight(int tabPlacement) {
        FontMetrics metrics = getFontMetrics(tabContext.getStyle().getFont(
                                             tabContext));
        int tabCount = tabPane.getTabCount();
        int result = 0;
        int fontHeight = metrics.getHeight();
        for(int i = 0; i < tabCount; i++) {
            result = Math.max(calculateTabHeight(tabPlacement, i, fontHeight), result);
        }
        return result;
    }

P
peterz 已提交
759 760 761 762
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
    protected int calculateTabWidth(int tabPlacement, int tabIndex,
                                    FontMetrics metrics) {
        Icon icon = getIconForTab(tabIndex);
        Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
        int width = tabInsets.left + tabInsets.right;
        Component tabComponent = tabPane.getTabComponentAt(tabIndex);
        if (tabComponent != null) {
            width += tabComponent.getPreferredSize().width;
        } else {
            if (icon != null) {
                width += icon.getIconWidth() + textIconGap;
            }
            View v = getTextViewForTab(tabIndex);
            if (v != null) {
                // html
                width += (int) v.getPreferredSpan(View.X_AXIS);
            } else {
                // plain text
                String title = tabPane.getTitleAt(tabIndex);
                width += tabContext.getStyle().getGraphicsUtils(tabContext).
                        computeStringWidth(tabContext, metrics.getFont(),
                                metrics, title);
            }
        }
        return width;
    }

P
peterz 已提交
790 791 792 793
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
794 795 796 797 798 799 800 801 802 803 804 805
    protected int calculateMaxTabWidth(int tabPlacement) {
        FontMetrics metrics = getFontMetrics(tabContext.getStyle().getFont(
                                     tabContext));
        int tabCount = tabPane.getTabCount();
        int result = 0;
        for(int i = 0; i < tabCount; i++) {
            result = Math.max(calculateTabWidth(tabPlacement, i, metrics),
                              result);
        }
        return result;
    }

P
peterz 已提交
806 807 808 809
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
810
    protected Insets getTabInsets(int tabPlacement, int tabIndex) {
P
peterz 已提交
811
        updateTabContext(tabIndex, false, false, false,
D
duke 已提交
812 813 814 815
                          (getFocusIndex() == tabIndex));
        return tabInsets;
    }

P
peterz 已提交
816 817 818 819
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
820 821 822 823
    protected FontMetrics getFontMetrics() {
        return getFontMetrics(tabContext.getStyle().getFont(tabContext));
    }

P
peterz 已提交
824
    private FontMetrics getFontMetrics(Font font) {
D
duke 已提交
825 826 827 828
        return tabPane.getFontMetrics(font);
    }

    private void updateTabContext(int index, boolean selected,
P
peterz 已提交
829
                                  boolean isMouseDown, boolean isMouseOver, boolean hasFocus) {
D
duke 已提交
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
        int state = 0;
        if (!tabPane.isEnabled() || !tabPane.isEnabledAt(index)) {
            state |= SynthConstants.DISABLED;
            if (selected) {
                state |= SynthConstants.SELECTED;
            }
        }
        else if (selected) {
            state |= (SynthConstants.ENABLED | SynthConstants.SELECTED);
            if (isMouseOver && UIManager.getBoolean("TabbedPane.isTabRollover")) {
                state |= SynthConstants.MOUSE_OVER;
            }
        }
        else if (isMouseOver) {
            state |= (SynthConstants.ENABLED | SynthConstants.MOUSE_OVER);
        }
        else {
            state = SynthLookAndFeel.getComponentState(tabPane);
            state &= ~SynthConstants.FOCUSED; // don't use tabbedpane focus state
        }
        if (hasFocus && tabPane.hasFocus()) {
            state |= SynthConstants.FOCUSED; // individual tab has focus
        }
P
peterz 已提交
853 854 855 856
        if (isMouseDown) {
            state |= SynthConstants.PRESSED;
        }

D
duke 已提交
857 858 859
        tabContext.setComponentState(state);
    }

P
peterz 已提交
860 861 862 863 864 865
    /**
     * @inheritDoc
     *
     * Overridden to create a TabbedPaneLayout subclass which takes into
     * account tabOverlap.
     */
P
peterz 已提交
866 867
    @Override
    protected LayoutManager createLayoutManager() {
P
peterz 已提交
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915
        if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
            return super.createLayoutManager();
        } else { /* WRAP_TAB_LAYOUT */
            return new TabbedPaneLayout() {
                @Override
                public void calculateLayoutInfo() {
                    super.calculateLayoutInfo();
                    //shift all the tabs, if necessary
                    if (tabOverlap != 0) {
                        int tabCount = tabPane.getTabCount();
                        //left-to-right/right-to-left only affects layout
                        //when placement is TOP or BOTTOM
                        boolean ltr = tabPane.getComponentOrientation().isLeftToRight();
                        for (int i = runCount - 1; i >= 0; i--) {
                            int start = tabRuns[i];
                            int next = tabRuns[(i == runCount - 1)? 0 : i + 1];
                            int end = (next != 0? next - 1: tabCount - 1);
                            for (int j = start+1; j <= end; j++) {
                                // xshift and yshift represent the amount &
                                // direction to shift the tab in their
                                // respective axis.
                                int xshift = 0;
                                int yshift = 0;
                                // configure xshift and y shift based on tab
                                // position and ltr/rtl
                                switch (tabPane.getTabPlacement()) {
                                    case JTabbedPane.TOP:
                                    case JTabbedPane.BOTTOM:
                                        xshift = ltr ? tabOverlap : -tabOverlap;
                                        break;
                                    case JTabbedPane.LEFT:
                                    case JTabbedPane.RIGHT:
                                        yshift = tabOverlap;
                                        break;
                                    default: //do nothing
                                }
                                rects[j].x += xshift;
                                rects[j].y += yshift;
                                rects[j].width += Math.abs(xshift);
                                rects[j].height += Math.abs(yshift);
                            }
                        }
                    }
                }
            };
        }
    }

D
duke 已提交
916 917 918 919
    private class SynthScrollableTabButton extends SynthArrowButton implements
            UIResource {
        public SynthScrollableTabButton(int direction) {
            super(direction);
920
            setName("TabbedPane.button");
D
duke 已提交
921 922 923
        }
    }
}