SynthTabbedPaneUI.java 34.5 KB
Newer Older
D
duke 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
/*
 * Copyright 2002-2007 Sun Microsystems, Inc.  All Rights Reserved.
 * 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
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * 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.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

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 118 119 120 121
    public static ComponentUI createUI(JComponent c) {
        return new SynthTabbedPaneUI();
    }

    private boolean scrollableTabLayoutEnabled() {
        return (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT);
    }

P
peterz 已提交
122 123 124 125
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
126 127 128 129 130 131 132 133 134 135 136 137 138
    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 已提交
139 140 141
            tabOverlap = style.getInt(context, "TabbedPane.tabOverlap", 0);
            extendTabsToBase = style.getBoolean(context,
                    "TabbedPane.extendTabsToBase", false);
D
duke 已提交
142 143 144 145 146 147
            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 已提交
148 149 150 151
            tabAreaStatesMatchSelectedTab = style.getBoolean(context,
                    "TabbedPane.tabAreaStatesMatchSelectedTab", false);
            nudgeSelectedLabel = style.getBoolean(context,
                    "TabbedPane.nudgeSelectedLabel", true);
D
duke 已提交
152 153 154 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
            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 已提交
185 186 187 188
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
189 190 191 192 193
    protected void installListeners() {
        super.installListeners();
        tabPane.addPropertyChangeListener(this);
    }

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

P
peterz 已提交
203 204 205 206
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
    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 已提交
229 230 231 232
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
233
    public SynthContext getContext(JComponent c) {
P
peterz 已提交
234
        return getContext(c, SynthLookAndFeel.getComponentState(c));
D
duke 已提交
235 236
    }

P
peterz 已提交
237
    private SynthContext getContext(JComponent c, int state) {
D
duke 已提交
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
        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 已提交
258 259 260 261
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
262
    protected JButton createScrollButton(int direction) {
P
peterz 已提交
263 264 265 266 267 268 269 270
        // 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 已提交
271 272 273
        return new SynthScrollableTabButton(direction);
    }

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

P
peterz 已提交
284 285 286 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
    /**
     * @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 已提交
335 336 337
    /**
     * @inheritDoc
     */
P
peterz 已提交
338 339 340 341 342 343 344 345 346
    @Override
    protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) {
        if (nudgeSelectedLabel) {
            return super.getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
        } else {
            return 0;
        }
    }

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

P
peterz 已提交
359
    /**
360 361 362 363 364 365 366 367 368 369
     * 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 已提交
370 371
     */
    @Override
D
duke 已提交
372 373 374 375 376 377 378 379 380 381
    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 已提交
382 383 384 385
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
    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,
401
                SwingUtilities.CENTER, calcRect,
D
duke 已提交
402 403 404 405
                iconRect, textRect, textIconGap);
        return textRect.y + metrics.getAscent() + getBaselineOffset();
    }

P
peterz 已提交
406 407 408 409
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
410 411 412 413 414
    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 已提交
415
    /**
416 417 418 419 420 421 422
     * 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 已提交
423 424
     */
    @Override
D
duke 已提交
425 426 427 428 429 430 431
    public void paint(Graphics g, JComponent c) {
        SynthContext context = getContext(c);

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

P
peterz 已提交
432 433 434 435
    /**
     * Paints the specified component.
     *
     * @param context context for the component being painted
436 437
     * @param g the {@code Graphics} object used for painting
     * @see #update(Graphics,JComponent)
P
peterz 已提交
438
     */
D
duke 已提交
439 440 441 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
    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 已提交
492 493 494 495
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
496 497 498 499 500 501 502 503 504 505 506 507 508
    protected void paintTabArea(Graphics g, int tabPlacement,
                                int selectedIndex) {
        // This can be invoked from ScrollabeTabPanel
        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;

        paintTabArea(tabAreaContext, g, tabPlacement, selectedIndex,
                     new Rectangle(x, y, width, height));
    }

P
peterz 已提交
509
    private void paintTabArea(SynthContext ss, Graphics g,
D
duke 已提交
510 511 512 513
                                int tabPlacement, int selectedIndex,
                                Rectangle tabAreaBounds) {
        Rectangle clipRect = g.getClipBounds();

P
peterz 已提交
514 515 516 517 518 519 520 521 522 523 524 525 526 527
        //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 已提交
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562
        // 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 已提交
563 564 565 566
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
567 568 569 570 571 572
    protected void setRolloverTab(int index) {
        int oldRolloverTab = getRolloverTab();
        super.setRolloverTab(index);

        Rectangle r = null;

P
peterz 已提交
573 574 575 576 577 578 579 580 581
        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 已提交
582 583
            }

P
peterz 已提交
584 585 586 587 588
            if (index >= 0) {
                r = getTabBounds(tabPane, index);
                if (r != null) {
                    tabPane.repaint(r);
                }
D
duke 已提交
589 590 591 592
            }
        }
    }

P
peterz 已提交
593
    private void paintTab(SynthContext ss, Graphics g,
D
duke 已提交
594 595 596 597 598
                            int tabPlacement, Rectangle[] rects, int tabIndex,
                            Rectangle iconRect, Rectangle textRect) {
        Rectangle tabRect = rects[tabIndex];
        int selectedIndex = tabPane.getSelectedIndex();
        boolean isSelected = selectedIndex == tabIndex;
P
peterz 已提交
599 600 601
        updateTabContext(tabIndex, isSelected, isSelected && selectedTabIsPressed,
                            (getRolloverTab() == tabIndex),
                            (getFocusIndex() == tabIndex));
D
duke 已提交
602 603

        SynthLookAndFeel.updateSubregion(ss, g, tabRect);
P
peterz 已提交
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641
        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 已提交
642
        tabContext.getPainter().paintTabbedPaneTabBorder(tabContext, g,
P
peterz 已提交
643
                x, y, width, height, tabIndex, placement);
D
duke 已提交
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660

        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 已提交
661
    private void layoutLabel(SynthContext ss, int tabPlacement,
D
duke 已提交
662 663 664 665 666 667 668 669 670 671 672 673 674
                               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,
675
                         SwingUtilities.LEADING, SwingUtilities.CENTER,
D
duke 已提交
676 677 678 679 680 681 682 683 684 685 686 687
                         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 已提交
688
    private void paintText(SynthContext ss,
D
duke 已提交
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
                             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 已提交
710
    private void paintContentBorder(SynthContext ss, Graphics g,
D
duke 已提交
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 743 744 745 746 747 748 749 750 751 752 753 754 755 756
                                      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 已提交
757 758 759 760
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
761 762 763 764 765 766 767 768 769 770 771 772
    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 已提交
773 774 775 776
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803
    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 已提交
804 805 806 807
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
808 809 810 811 812 813 814 815 816 817 818 819
    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 已提交
820 821 822 823
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
824
    protected Insets getTabInsets(int tabPlacement, int tabIndex) {
P
peterz 已提交
825
        updateTabContext(tabIndex, false, false, false,
D
duke 已提交
826 827 828 829
                          (getFocusIndex() == tabIndex));
        return tabInsets;
    }

P
peterz 已提交
830 831 832 833
    /**
     * @inheritDoc
     */
    @Override
D
duke 已提交
834 835 836 837
    protected FontMetrics getFontMetrics() {
        return getFontMetrics(tabContext.getStyle().getFont(tabContext));
    }

P
peterz 已提交
838
    private FontMetrics getFontMetrics(Font font) {
D
duke 已提交
839 840 841 842
        return tabPane.getFontMetrics(font);
    }

    private void updateTabContext(int index, boolean selected,
P
peterz 已提交
843
                                  boolean isMouseDown, boolean isMouseOver, boolean hasFocus) {
D
duke 已提交
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866
        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 已提交
867 868 869 870
        if (isMouseDown) {
            state |= SynthConstants.PRESSED;
        }

D
duke 已提交
871 872 873
        tabContext.setComponentState(state);
    }

P
peterz 已提交
874 875 876 877 878 879
    /**
     * @inheritDoc
     *
     * Overridden to create a TabbedPaneLayout subclass which takes into
     * account tabOverlap.
     */
P
peterz 已提交
880 881
    @Override
    protected LayoutManager createLayoutManager() {
P
peterz 已提交
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 916 917 918 919 920 921 922 923 924 925 926 927 928 929
        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 已提交
930 931 932 933 934 935 936
    private class SynthScrollableTabButton extends SynthArrowButton implements
            UIResource {
        public SynthScrollableTabButton(int direction) {
            super(direction);
        }
    }
}