1 /*
   2  * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing.plaf.metal;
  27 
  28 import javax.swing.*;
  29 import javax.swing.event.*;
  30 import java.awt.*;
  31 import java.awt.event.*;
  32 import javax.swing.plaf.*;
  33 import java.io.Serializable;
  34 import javax.swing.plaf.basic.BasicTabbedPaneUI;
  35 
  36 /**
  37  * The Metal subclass of BasicTabbedPaneUI.
  38  * <p>
  39  * <strong>Warning:</strong>
  40  * Serialized objects of this class will not be compatible with
  41  * future Swing releases. The current serialization support is
  42  * appropriate for short term storage or RMI between applications running
  43  * the same version of Swing.  As of 1.4, support for long term storage
  44  * of all JavaBeans
  45  * has been added to the <code>java.beans</code> package.
  46  * Please see {@link java.beans.XMLEncoder}.
  47  *
  48  * @author Tom Santos
  49  */
  50 @SuppressWarnings("serial") // Same-version serialization only
  51 public class MetalTabbedPaneUI extends BasicTabbedPaneUI {
  52 
  53     /**
  54      * The minimum width of a pane.
  55      */
  56     protected int minTabWidth = 40;
  57     // Background color for unselected tabs that don't have an explicitly
  58     // set color.
  59     private Color unselectedBackground;
  60 
  61     /**
  62      * The color of tab's background.
  63      */
  64     protected Color tabAreaBackground;
  65 
  66     /**
  67      * The color of the selected pane.
  68      */
  69     protected Color selectColor;
  70 
  71     /**
  72      * The color of the highlight.
  73      */
  74     protected Color selectHighlight;
  75     private boolean tabsOpaque = true;
  76 
  77     // Whether or not we're using ocean. This is cached as it is used
  78     // extensively during painting.
  79     private boolean ocean;
  80     // Selected border color for ocean.
  81     private Color oceanSelectedBorderColor;
  82 
  83     /**
  84      * Constructs {@code MetalTabbedPaneUI}.
  85      *
  86      * @param x a component
  87      * @return an instance of {@code MetalTabbedPaneUI}
  88      */
  89     public static ComponentUI createUI( JComponent x ) {
  90         return new MetalTabbedPaneUI();
  91     }
  92 
  93     protected LayoutManager createLayoutManager() {
  94         if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
  95             return super.createLayoutManager();
  96         }
  97         return new TabbedPaneLayout();
  98     }
  99 
 100     protected void installDefaults() {
 101         super.installDefaults();
 102 
 103         tabAreaBackground = UIManager.getColor("TabbedPane.tabAreaBackground");
 104         selectColor = UIManager.getColor("TabbedPane.selected");
 105         selectHighlight = UIManager.getColor("TabbedPane.selectHighlight");
 106         tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque");
 107         unselectedBackground = UIManager.getColor(
 108                                          "TabbedPane.unselectedBackground");
 109         ocean = MetalLookAndFeel.usingOcean();
 110         if (ocean) {
 111             oceanSelectedBorderColor = UIManager.getColor(
 112                          "TabbedPane.borderHightlightColor");
 113         }
 114     }
 115 
 116 
 117     protected void paintTabBorder( Graphics g, int tabPlacement,
 118                                    int tabIndex, int x, int y, int w, int h,
 119                                    boolean isSelected) {
 120         int bottom = y + (h-1);
 121         int right = x + (w-1);
 122 
 123         switch ( tabPlacement ) {
 124         case LEFT:
 125             paintLeftTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
 126             break;
 127         case BOTTOM:
 128             paintBottomTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
 129             break;
 130         case RIGHT:
 131             paintRightTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
 132             break;
 133         case TOP:
 134         default:
 135             paintTopTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
 136         }
 137     }
 138 
 139 
 140     /**
 141      * Paints the top tab border.
 142      *
 143      * @param tabIndex a tab index
 144      * @param g an instance of {@code Graphics}
 145      * @param x an X coordinate
 146      * @param y an Y coordinate
 147      * @param w a width
 148      * @param h a height
 149      * @param btm bottom
 150      * @param rght right
 151      * @param isSelected a selection
 152      */
 153     protected void paintTopTabBorder( int tabIndex, Graphics g,
 154                                       int x, int y, int w, int h,
 155                                       int btm, int rght,
 156                                       boolean isSelected ) {
 157         int currentRun = getRunForTab( tabPane.getTabCount(), tabIndex );
 158         int lastIndex = lastTabInRun( tabPane.getTabCount(), currentRun );
 159         int firstIndex = tabRuns[ currentRun ];
 160         boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
 161         int selectedIndex = tabPane.getSelectedIndex();
 162         int bottom = h - 1;
 163         int right = w - 1;
 164 
 165         //
 166         // Paint Gap
 167         //
 168 
 169         if (shouldFillGap( currentRun, tabIndex, x, y ) ) {
 170             g.translate( x, y );
 171 
 172             if ( leftToRight ) {
 173                 g.setColor( getColorForGap( currentRun, x, y + 1 ) );
 174                 g.fillRect( 1, 0, 5, 3 );
 175                 g.fillRect( 1, 3, 2, 2 );
 176             } else {
 177                 g.setColor( getColorForGap( currentRun, x + w - 1, y + 1 ) );
 178                 g.fillRect( right - 5, 0, 5, 3 );
 179                 g.fillRect( right - 2, 3, 2, 2 );
 180             }
 181 
 182             g.translate( -x, -y );
 183         }
 184 
 185         g.translate( x, y );
 186 
 187         //
 188         // Paint Border
 189         //
 190 
 191         if (ocean && isSelected) {
 192             g.setColor(oceanSelectedBorderColor);
 193         }
 194         else {
 195             g.setColor( darkShadow );
 196         }
 197 
 198         if ( leftToRight ) {
 199 
 200             // Paint slant
 201             g.drawLine( 1, 5, 6, 0 );
 202 
 203             // Paint top
 204             g.drawLine( 6, 0, right, 0 );
 205 
 206             // Paint right
 207             if ( tabIndex==lastIndex ) {
 208                 // last tab in run
 209                 g.drawLine( right, 1, right, bottom );
 210             }
 211 
 212             if (ocean && tabIndex - 1 == selectedIndex &&
 213                                 currentRun == getRunForTab(
 214                                 tabPane.getTabCount(), selectedIndex)) {
 215                 g.setColor(oceanSelectedBorderColor);
 216             }
 217 
 218             // Paint left
 219             if ( tabIndex != tabRuns[ runCount - 1 ] ) {
 220                 // not the first tab in the last run
 221                 if (ocean && isSelected) {
 222                     g.drawLine(0, 6, 0, bottom);
 223                     g.setColor(darkShadow);
 224                     g.drawLine(0, 0, 0, 5);
 225                 }
 226                 else {
 227                     g.drawLine( 0, 0, 0, bottom );
 228                 }
 229             } else {
 230                 // the first tab in the last run
 231                 g.drawLine( 0, 6, 0, bottom );
 232             }
 233         } else {
 234 
 235             // Paint slant
 236             g.drawLine( right - 1, 5, right - 6, 0 );
 237 
 238             // Paint top
 239             g.drawLine( right - 6, 0, 0, 0 );
 240 
 241             // Paint left
 242             if ( tabIndex==lastIndex ) {
 243                 // last tab in run
 244                 g.drawLine( 0, 1, 0, bottom );
 245             }
 246 
 247             // Paint right
 248             if (ocean && tabIndex - 1 == selectedIndex &&
 249                                 currentRun == getRunForTab(
 250                                 tabPane.getTabCount(), selectedIndex)) {
 251                 g.setColor(oceanSelectedBorderColor);
 252                 g.drawLine(right, 0, right, bottom);
 253             }
 254             else if (ocean && isSelected) {
 255                 g.drawLine(right, 6, right, bottom);
 256                 if (tabIndex != 0) {
 257                     g.setColor(darkShadow);
 258                     g.drawLine(right, 0, right, 5);
 259                 }
 260             }
 261             else {
 262                 if ( tabIndex != tabRuns[ runCount - 1 ] ) {
 263                     // not the first tab in the last run
 264                     g.drawLine( right, 0, right, bottom );
 265                 } else {
 266                     // the first tab in the last run
 267                     g.drawLine( right, 6, right, bottom );
 268                 }
 269             }
 270         }
 271 
 272         //
 273         // Paint Highlight
 274         //
 275 
 276         g.setColor( isSelected ? selectHighlight : highlight );
 277 
 278         if ( leftToRight ) {
 279 
 280             // Paint slant
 281             g.drawLine( 1, 6, 6, 1 );
 282 
 283             // Paint top
 284             g.drawLine( 6, 1, (tabIndex == lastIndex) ? right - 1 : right, 1 );
 285 
 286             // Paint left
 287             g.drawLine( 1, 6, 1, bottom );
 288 
 289             // paint highlight in the gap on tab behind this one
 290             // on the left end (where they all line up)
 291             if ( tabIndex==firstIndex && tabIndex!=tabRuns[runCount - 1] ) {
 292                 //  first tab in run but not first tab in last run
 293                 if (tabPane.getSelectedIndex()==tabRuns[currentRun+1]) {
 294                     // tab in front of selected tab
 295                     g.setColor( selectHighlight );
 296                 }
 297                 else {
 298                     // tab in front of normal tab
 299                     g.setColor( highlight );
 300                 }
 301                 g.drawLine( 1, 0, 1, 4 );
 302             }
 303         } else {
 304 
 305             // Paint slant
 306             g.drawLine( right - 1, 6, right - 6, 1 );
 307 
 308             // Paint top
 309             g.drawLine( right - 6, 1, 1, 1 );
 310 
 311             // Paint left
 312             if ( tabIndex==lastIndex ) {
 313                 // last tab in run
 314                 g.drawLine( 1, 1, 1, bottom );
 315             } else {
 316                 g.drawLine( 0, 1, 0, bottom );
 317             }
 318         }
 319 
 320         g.translate( -x, -y );
 321     }
 322 
 323     /**
 324      * Returns {@code true} if the gap should be filled.
 325      *
 326      * @param currentRun the current run
 327      * @param tabIndex the tab index
 328      * @param x an X coordinate
 329      * @param y an Y coordinate
 330      * @return {@code true} if the gap should be filled
 331      */
 332     protected boolean shouldFillGap( int currentRun, int tabIndex, int x, int y ) {
 333         boolean result = false;
 334 
 335         if (!tabsOpaque) {
 336             return false;
 337         }
 338 
 339         if ( currentRun == runCount - 2 ) {  // If it's the second to last row.
 340             Rectangle lastTabBounds = getTabBounds( tabPane, tabPane.getTabCount() - 1 );
 341             Rectangle tabBounds = getTabBounds( tabPane, tabIndex );
 342             if (MetalUtils.isLeftToRight(tabPane)) {
 343                 int lastTabRight = lastTabBounds.x + lastTabBounds.width - 1;
 344 
 345                 // is the right edge of the last tab to the right
 346                 // of the left edge of the current tab?
 347                 if ( lastTabRight > tabBounds.x + 2 ) {
 348                     return true;
 349                 }
 350             } else {
 351                 int lastTabLeft = lastTabBounds.x;
 352                 int currentTabRight = tabBounds.x + tabBounds.width - 1;
 353 
 354                 // is the left edge of the last tab to the left
 355                 // of the right edge of the current tab?
 356                 if ( lastTabLeft < currentTabRight - 2 ) {
 357                     return true;
 358                 }
 359             }
 360         } else {
 361             // fill in gap for all other rows except last row
 362             result = currentRun != runCount - 1;
 363         }
 364 
 365         return result;
 366     }
 367 
 368     /**
 369      * Returns the color of the gap.
 370      *
 371      * @param currentRun the current run
 372      * @param x an X coordinate
 373      * @param y an Y coordinate
 374      * @return the color of the gap
 375      */
 376     protected Color getColorForGap( int currentRun, int x, int y ) {
 377         final int shadowWidth = 4;
 378         int selectedIndex = tabPane.getSelectedIndex();
 379         int startIndex = tabRuns[ currentRun + 1 ];
 380         int endIndex = lastTabInRun( tabPane.getTabCount(), currentRun + 1 );
 381         int tabOverGap = -1;
 382         // Check each tab in the row that is 'on top' of this row
 383         for ( int i = startIndex; i <= endIndex; ++i ) {
 384             Rectangle tabBounds = getTabBounds( tabPane, i );
 385             int tabLeft = tabBounds.x;
 386             int tabRight = (tabBounds.x + tabBounds.width) - 1;
 387             // Check to see if this tab is over the gap
 388             if ( MetalUtils.isLeftToRight(tabPane) ) {
 389                 if ( tabLeft <= x && tabRight - shadowWidth > x ) {
 390                     return selectedIndex == i ? selectColor : getUnselectedBackgroundAt( i );
 391                 }
 392             }
 393             else {
 394                 if ( tabLeft + shadowWidth < x && tabRight >= x ) {
 395                     return selectedIndex == i ? selectColor : getUnselectedBackgroundAt( i );
 396                 }
 397             }
 398         }
 399 
 400         return tabPane.getBackground();
 401     }
 402 
 403     /**
 404      * Paints the left tab border.
 405      *
 406      * @param tabIndex a tab index
 407      * @param g an instance of {@code Graphics}
 408      * @param x an X coordinate
 409      * @param y an Y coordinate
 410      * @param w a width
 411      * @param h a height
 412      * @param btm bottom
 413      * @param rght right
 414      * @param isSelected a selection
 415      */
 416     protected void paintLeftTabBorder( int tabIndex, Graphics g,
 417                                        int x, int y, int w, int h,
 418                                        int btm, int rght,
 419                                        boolean isSelected ) {
 420         int tabCount = tabPane.getTabCount();
 421         int currentRun = getRunForTab( tabCount, tabIndex );
 422         int lastIndex = lastTabInRun( tabCount, currentRun );
 423         int firstIndex = tabRuns[ currentRun ];
 424 
 425         g.translate( x, y );
 426 
 427         int bottom = h - 1;
 428         int right = w - 1;
 429 
 430         //
 431         // Paint part of the tab above
 432         //
 433 
 434         if ( tabIndex != firstIndex && tabsOpaque ) {
 435             g.setColor( tabPane.getSelectedIndex() == tabIndex - 1 ?
 436                         selectColor :
 437                         getUnselectedBackgroundAt( tabIndex - 1 ) );
 438             g.fillRect( 2, 0, 4, 3 );
 439             g.drawLine( 2, 3, 2, 3 );
 440         }
 441 
 442 
 443         //
 444         // Paint Highlight
 445         //
 446 
 447         if (ocean) {
 448             g.setColor(isSelected ? selectHighlight :
 449                        MetalLookAndFeel.getWhite());
 450         }
 451         else {
 452             g.setColor( isSelected ? selectHighlight : highlight );
 453         }
 454 
 455         // Paint slant
 456         g.drawLine( 1, 6, 6, 1 );
 457 
 458         // Paint left
 459         g.drawLine( 1, 6, 1, bottom );
 460 
 461         // Paint top
 462         g.drawLine( 6, 1, right, 1 );
 463 
 464         if ( tabIndex != firstIndex ) {
 465             if (tabPane.getSelectedIndex() == tabIndex - 1) {
 466                 g.setColor(selectHighlight);
 467             } else {
 468                 g.setColor(ocean ? MetalLookAndFeel.getWhite() : highlight);
 469             }
 470 
 471             g.drawLine( 1, 0, 1, 4 );
 472         }
 473 
 474         //
 475         // Paint Border
 476         //
 477 
 478         if (ocean) {
 479             if (isSelected) {
 480                 g.setColor(oceanSelectedBorderColor);
 481             }
 482             else {
 483                 g.setColor( darkShadow );
 484             }
 485         }
 486         else {
 487             g.setColor( darkShadow );
 488         }
 489 
 490         // Paint slant
 491         g.drawLine( 1, 5, 6, 0 );
 492 
 493         // Paint top
 494         g.drawLine( 6, 0, right, 0 );
 495 
 496         // Paint bottom
 497         if ( tabIndex == lastIndex ) {
 498             g.drawLine( 0, bottom, right, bottom );
 499         }
 500 
 501         // Paint left
 502         if (ocean) {
 503             if (tabPane.getSelectedIndex() == tabIndex - 1) {
 504                 g.drawLine(0, 5, 0, bottom);
 505                 g.setColor(oceanSelectedBorderColor);
 506                 g.drawLine(0, 0, 0, 5);
 507             }
 508             else if (isSelected) {
 509                 g.drawLine( 0, 6, 0, bottom );
 510                 if (tabIndex != 0) {
 511                     g.setColor(darkShadow);
 512                     g.drawLine(0, 0, 0, 5);
 513                 }
 514             }
 515             else if ( tabIndex != firstIndex ) {
 516                 g.drawLine( 0, 0, 0, bottom );
 517             } else {
 518                 g.drawLine( 0, 6, 0, bottom );
 519             }
 520         }
 521         else { // metal
 522             if ( tabIndex != firstIndex ) {
 523                 g.drawLine( 0, 0, 0, bottom );
 524             } else {
 525                 g.drawLine( 0, 6, 0, bottom );
 526             }
 527         }
 528 
 529         g.translate( -x, -y );
 530     }
 531 
 532 
 533     /**
 534      * Paints the bottom tab border.
 535      *
 536      * @param tabIndex a tab index
 537      * @param g an instance of {@code Graphics}
 538      * @param x an X coordinate
 539      * @param y an Y coordinate
 540      * @param w a width
 541      * @param h a height
 542      * @param btm bottom
 543      * @param rght right
 544      * @param isSelected a selection
 545      */
 546     protected void paintBottomTabBorder( int tabIndex, Graphics g,
 547                                          int x, int y, int w, int h,
 548                                          int btm, int rght,
 549                                          boolean isSelected ) {
 550         int tabCount = tabPane.getTabCount();
 551         int currentRun = getRunForTab( tabCount, tabIndex );
 552         int lastIndex = lastTabInRun( tabCount, currentRun );
 553         int firstIndex = tabRuns[ currentRun ];
 554         boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
 555 
 556         int bottom = h - 1;
 557         int right = w - 1;
 558 
 559         //
 560         // Paint Gap
 561         //
 562 
 563         if ( shouldFillGap( currentRun, tabIndex, x, y ) ) {
 564             g.translate( x, y );
 565 
 566             if ( leftToRight ) {
 567                 g.setColor( getColorForGap( currentRun, x, y ) );
 568                 g.fillRect( 1, bottom - 4, 3, 5 );
 569                 g.fillRect( 4, bottom - 1, 2, 2 );
 570             } else {
 571                 g.setColor( getColorForGap( currentRun, x + w - 1, y ) );
 572                 g.fillRect( right - 3, bottom - 3, 3, 4 );
 573                 g.fillRect( right - 5, bottom - 1, 2, 2 );
 574                 g.drawLine( right - 1, bottom - 4, right - 1, bottom - 4 );
 575             }
 576 
 577             g.translate( -x, -y );
 578         }
 579 
 580         g.translate( x, y );
 581 
 582 
 583         //
 584         // Paint Border
 585         //
 586 
 587         if (ocean && isSelected) {
 588             g.setColor(oceanSelectedBorderColor);
 589         }
 590         else {
 591             g.setColor( darkShadow );
 592         }
 593 
 594         if ( leftToRight ) {
 595 
 596             // Paint slant
 597             g.drawLine( 1, bottom - 5, 6, bottom );
 598 
 599             // Paint bottom
 600             g.drawLine( 6, bottom, right, bottom );
 601 
 602             // Paint right
 603             if ( tabIndex == lastIndex ) {
 604                 g.drawLine( right, 0, right, bottom );
 605             }
 606 
 607             // Paint left
 608             if (ocean && isSelected) {
 609                 g.drawLine(0, 0, 0, bottom - 6);
 610                 if ((currentRun == 0 && tabIndex != 0) ||
 611                     (currentRun > 0 && tabIndex != tabRuns[currentRun - 1])) {
 612                     g.setColor(darkShadow);
 613                     g.drawLine(0, bottom - 5, 0, bottom);
 614                 }
 615             }
 616             else {
 617                 if (ocean && tabIndex == tabPane.getSelectedIndex() + 1) {
 618                     g.setColor(oceanSelectedBorderColor);
 619                 }
 620                 if ( tabIndex != tabRuns[ runCount - 1 ] ) {
 621                     g.drawLine( 0, 0, 0, bottom );
 622                 } else {
 623                     g.drawLine( 0, 0, 0, bottom - 6 );
 624                 }
 625             }
 626         } else {
 627 
 628             // Paint slant
 629             g.drawLine( right - 1, bottom - 5, right - 6, bottom );
 630 
 631             // Paint bottom
 632             g.drawLine( right - 6, bottom, 0, bottom );
 633 
 634             // Paint left
 635             if ( tabIndex==lastIndex ) {
 636                 // last tab in run
 637                 g.drawLine( 0, 0, 0, bottom );
 638             }
 639 
 640             // Paint right
 641             if (ocean && tabIndex == tabPane.getSelectedIndex() + 1) {
 642                 g.setColor(oceanSelectedBorderColor);
 643                 g.drawLine(right, 0, right, bottom);
 644             }
 645             else if (ocean && isSelected) {
 646                 g.drawLine(right, 0, right, bottom - 6);
 647                 if (tabIndex != firstIndex) {
 648                     g.setColor(darkShadow);
 649                     g.drawLine(right, bottom - 5, right, bottom);
 650                 }
 651             }
 652             else if ( tabIndex != tabRuns[ runCount - 1 ] ) {
 653                 // not the first tab in the last run
 654                 g.drawLine( right, 0, right, bottom );
 655             } else {
 656                 // the first tab in the last run
 657                 g.drawLine( right, 0, right, bottom - 6 );
 658             }
 659         }
 660 
 661         //
 662         // Paint Highlight
 663         //
 664 
 665         g.setColor( isSelected ? selectHighlight : highlight );
 666 
 667         if ( leftToRight ) {
 668 
 669             // Paint slant
 670             g.drawLine( 1, bottom - 6, 6, bottom - 1 );
 671 
 672             // Paint left
 673             g.drawLine( 1, 0, 1, bottom - 6 );
 674 
 675             // paint highlight in the gap on tab behind this one
 676             // on the left end (where they all line up)
 677             if ( tabIndex==firstIndex && tabIndex!=tabRuns[runCount - 1] ) {
 678                 //  first tab in run but not first tab in last run
 679                 if (tabPane.getSelectedIndex()==tabRuns[currentRun+1]) {
 680                     // tab in front of selected tab
 681                     g.setColor( selectHighlight );
 682                 }
 683                 else {
 684                     // tab in front of normal tab
 685                     g.setColor( highlight );
 686                 }
 687                 g.drawLine( 1, bottom - 4, 1, bottom );
 688             }
 689         } else {
 690 
 691             // Paint left
 692             if ( tabIndex==lastIndex ) {
 693                 // last tab in run
 694                 g.drawLine( 1, 0, 1, bottom - 1 );
 695             } else {
 696                 g.drawLine( 0, 0, 0, bottom - 1 );
 697             }
 698         }
 699 
 700         g.translate( -x, -y );
 701     }
 702 
 703     /**
 704      * Paints the right tab border.
 705      *
 706      * @param tabIndex a tab index
 707      * @param g an instance of {@code Graphics}
 708      * @param x an X coordinate
 709      * @param y an Y coordinate
 710      * @param w a width
 711      * @param h a height
 712      * @param btm bottom
 713      * @param rght right
 714      * @param isSelected a selection
 715      */
 716     protected void paintRightTabBorder( int tabIndex, Graphics g,
 717                                         int x, int y, int w, int h,
 718                                         int btm, int rght,
 719                                         boolean isSelected ) {
 720         int tabCount = tabPane.getTabCount();
 721         int currentRun = getRunForTab( tabCount, tabIndex );
 722         int lastIndex = lastTabInRun( tabCount, currentRun );
 723         int firstIndex = tabRuns[ currentRun ];
 724 
 725         g.translate( x, y );
 726 
 727         int bottom = h - 1;
 728         int right = w - 1;
 729 
 730         //
 731         // Paint part of the tab above
 732         //
 733 
 734         if ( tabIndex != firstIndex && tabsOpaque ) {
 735             g.setColor( tabPane.getSelectedIndex() == tabIndex - 1 ?
 736                         selectColor :
 737                         getUnselectedBackgroundAt( tabIndex - 1 ) );
 738             g.fillRect( right - 5, 0, 5, 3 );
 739             g.fillRect( right - 2, 3, 2, 2 );
 740         }
 741 
 742 
 743         //
 744         // Paint Highlight
 745         //
 746 
 747         g.setColor( isSelected ? selectHighlight : highlight );
 748 
 749         // Paint slant
 750         g.drawLine( right - 6, 1, right - 1, 6 );
 751 
 752         // Paint top
 753         g.drawLine( 0, 1, right - 6, 1 );
 754 
 755         // Paint left
 756         if ( !isSelected ) {
 757             g.drawLine( 0, 1, 0, bottom );
 758         }
 759 
 760 
 761         //
 762         // Paint Border
 763         //
 764 
 765         if (ocean && isSelected) {
 766             g.setColor(oceanSelectedBorderColor);
 767         }
 768         else {
 769             g.setColor( darkShadow );
 770         }
 771 
 772         // Paint bottom
 773         if ( tabIndex == lastIndex ) {
 774             g.drawLine( 0, bottom, right, bottom );
 775         }
 776 
 777         // Paint slant
 778         if (ocean && tabPane.getSelectedIndex() == tabIndex - 1) {
 779             g.setColor(oceanSelectedBorderColor);
 780         }
 781         g.drawLine( right - 6, 0, right, 6 );
 782 
 783         // Paint top
 784         g.drawLine( 0, 0, right - 6, 0 );
 785 
 786         // Paint right
 787         if (ocean && isSelected) {
 788             g.drawLine(right, 6, right, bottom);
 789             if (tabIndex != firstIndex) {
 790                 g.setColor(darkShadow);
 791                 g.drawLine(right, 0, right, 5);
 792             }
 793         }
 794         else if (ocean && tabPane.getSelectedIndex() == tabIndex - 1) {
 795             g.setColor(oceanSelectedBorderColor);
 796             g.drawLine(right, 0, right, 6);
 797             g.setColor(darkShadow);
 798             g.drawLine(right, 6, right, bottom);
 799         }
 800         else if ( tabIndex != firstIndex ) {
 801             g.drawLine( right, 0, right, bottom );
 802         } else {
 803             g.drawLine( right, 6, right, bottom );
 804         }
 805 
 806         g.translate( -x, -y );
 807     }
 808 
 809     public void update( Graphics g, JComponent c ) {
 810         if ( c.isOpaque() ) {
 811             g.setColor( tabAreaBackground );
 812             g.fillRect( 0, 0, c.getWidth(),c.getHeight() );
 813         }
 814         paint( g, c );
 815     }
 816 
 817     protected void paintTabBackground( Graphics g, int tabPlacement,
 818                                        int tabIndex, int x, int y, int w, int h, boolean isSelected ) {
 819         int slantWidth = h / 2;
 820         if ( isSelected ) {
 821             g.setColor( selectColor );
 822         } else {
 823             g.setColor( getUnselectedBackgroundAt( tabIndex ) );
 824         }
 825 
 826         if (MetalUtils.isLeftToRight(tabPane)) {
 827             switch ( tabPlacement ) {
 828                 case LEFT:
 829                     g.fillRect( x + 5, y + 1, w - 5, h - 1);
 830                     g.fillRect( x + 2, y + 4, 3, h - 4 );
 831                     break;
 832                 case BOTTOM:
 833                     g.fillRect( x + 2, y, w - 2, h - 4 );
 834                     g.fillRect( x + 5, y + (h - 1) - 3, w - 5, 3 );
 835                     break;
 836                 case RIGHT:
 837                     g.fillRect( x, y + 2, w - 4, h - 2);
 838                     g.fillRect( x + (w - 1) - 3, y + 5, 3, h - 5 );
 839                     break;
 840                 case TOP:
 841                 default:
 842                     g.fillRect( x + 4, y + 2, (w - 1) - 3, (h - 1) - 1 );
 843                     g.fillRect( x + 2, y + 5, 2, h - 5 );
 844             }
 845         } else {
 846             switch ( tabPlacement ) {
 847                 case LEFT:
 848                     g.fillRect( x + 5, y + 1, w - 5, h - 1);
 849                     g.fillRect( x + 2, y + 4, 3, h - 4 );
 850                     break;
 851                 case BOTTOM:
 852                     g.fillRect( x, y, w - 5, h - 1 );
 853                     g.fillRect( x + (w - 1) - 4, y, 4, h - 5);
 854                     g.fillRect( x + (w - 1) - 4, y + (h - 1) - 4, 2, 2);
 855                     break;
 856                 case RIGHT:
 857                     g.fillRect( x + 1, y + 1, w - 5, h - 1);
 858                     g.fillRect( x + (w - 1) - 3, y + 5, 3, h - 5 );
 859                     break;
 860                 case TOP:
 861                 default:
 862                     g.fillRect( x, y + 2, (w - 1) - 3, (h - 1) - 1 );
 863                     g.fillRect( x + (w - 1) - 3, y + 5, 3, h - 3 );
 864             }
 865         }
 866     }
 867 
 868     /**
 869      * Overridden to do nothing for the Java L&amp;F.
 870      */
 871     protected int getTabLabelShiftX( int tabPlacement, int tabIndex, boolean isSelected ) {
 872         return 0;
 873     }
 874 
 875 
 876     /**
 877      * Overridden to do nothing for the Java L&amp;F.
 878      */
 879     protected int getTabLabelShiftY( int tabPlacement, int tabIndex, boolean isSelected ) {
 880         return 0;
 881     }
 882 
 883     /**
 884      * {@inheritDoc}
 885      *
 886      * @since 1.6
 887      */
 888     protected int getBaselineOffset() {
 889         return 0;
 890     }
 891 
 892     public void paint( Graphics g, JComponent c ) {
 893         int tabPlacement = tabPane.getTabPlacement();
 894 
 895         Insets insets = c.getInsets(); Dimension size = c.getSize();
 896 
 897         // Paint the background for the tab area
 898         if ( tabPane.isOpaque() ) {
 899             Color background = c.getBackground();
 900             if (background instanceof UIResource && tabAreaBackground != null) {
 901                 g.setColor(tabAreaBackground);
 902             }
 903             else {
 904                 g.setColor(background);
 905             }
 906             switch ( tabPlacement ) {
 907             case LEFT:
 908                 g.fillRect( insets.left, insets.top,
 909                             calculateTabAreaWidth( tabPlacement, runCount, maxTabWidth ),
 910                             size.height - insets.bottom - insets.top );
 911                 break;
 912             case BOTTOM:
 913                 int totalTabHeight = calculateTabAreaHeight( tabPlacement, runCount, maxTabHeight );
 914                 g.fillRect( insets.left, size.height - insets.bottom - totalTabHeight,
 915                             size.width - insets.left - insets.right,
 916                             totalTabHeight );
 917                 break;
 918             case RIGHT:
 919                 int totalTabWidth = calculateTabAreaWidth( tabPlacement, runCount, maxTabWidth );
 920                 g.fillRect( size.width - insets.right - totalTabWidth,
 921                             insets.top, totalTabWidth,
 922                             size.height - insets.top - insets.bottom );
 923                 break;
 924             case TOP:
 925             default:
 926                 g.fillRect( insets.left, insets.top,
 927                             size.width - insets.right - insets.left,
 928                             calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight) );
 929                 paintHighlightBelowTab();
 930             }
 931         }
 932 
 933         super.paint( g, c );
 934     }
 935 
 936     /**
 937      * Paints highlights below tab.
 938      */
 939     protected void paintHighlightBelowTab( ) {
 940 
 941     }
 942 
 943 
 944     protected void paintFocusIndicator(Graphics g, int tabPlacement,
 945                                        Rectangle[] rects, int tabIndex,
 946                                        Rectangle iconRect, Rectangle textRect,
 947                                        boolean isSelected) {
 948         if ( tabPane.hasFocus() && isSelected ) {
 949             Rectangle tabRect = rects[tabIndex];
 950             boolean lastInRun = isLastInRun( tabIndex );
 951             g.setColor( focus );
 952             g.translate( tabRect.x, tabRect.y );
 953             int right = tabRect.width - 1;
 954             int bottom = tabRect.height - 1;
 955             boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
 956             switch ( tabPlacement ) {
 957             case RIGHT:
 958                 g.drawLine( right - 6,2 , right - 2,6 );         // slant
 959                 g.drawLine( 1,2 , right - 6,2 );                 // top
 960                 g.drawLine( right - 2,6 , right - 2,bottom );    // right
 961                 g.drawLine( 1,2 , 1,bottom );                    // left
 962                 g.drawLine( 1,bottom , right - 2,bottom );       // bottom
 963                 break;
 964             case BOTTOM:
 965                 if ( leftToRight ) {
 966                     g.drawLine( 2, bottom - 6, 6, bottom - 2 );   // slant
 967                     g.drawLine( 6, bottom - 2,
 968                                 right, bottom - 2 );              // bottom
 969                     g.drawLine( 2, 0, 2, bottom - 6 );            // left
 970                     g.drawLine( 2, 0, right, 0 );                 // top
 971                     g.drawLine( right, 0, right, bottom - 2 );    // right
 972                 } else {
 973                     g.drawLine( right - 2, bottom - 6,
 974                                 right - 6, bottom - 2 );          // slant
 975                     g.drawLine( right - 2, 0,
 976                                 right - 2, bottom - 6 );          // right
 977                     if ( lastInRun ) {
 978                         // last tab in run
 979                         g.drawLine( 2, bottom - 2,
 980                                     right - 6, bottom - 2 );      // bottom
 981                         g.drawLine( 2, 0, right - 2, 0 );         // top
 982                         g.drawLine( 2, 0, 2, bottom - 2 );        // left
 983                     } else {
 984                         g.drawLine( 1, bottom - 2,
 985                                     right - 6, bottom - 2 );      // bottom
 986                         g.drawLine( 1, 0, right - 2, 0 );         // top
 987                         g.drawLine( 1, 0, 1, bottom - 2 );        // left
 988                     }
 989                 }
 990                 break;
 991             case LEFT:
 992                 g.drawLine( 2, 6, 6, 2 );                         // slant
 993                 g.drawLine( 2, 6, 2, bottom - 1);                 // left
 994                 g.drawLine( 6, 2, right, 2 );                     // top
 995                 g.drawLine( right, 2, right, bottom - 1 );        // right
 996                 g.drawLine( 2, bottom - 1,
 997                             right, bottom - 1 );                  // bottom
 998                 break;
 999             case TOP:
1000              default:
1001                     if ( leftToRight ) {
1002                         g.drawLine( 2, 6, 6, 2 );                     // slant
1003                         g.drawLine( 2, 6, 2, bottom - 1);             // left
1004                         g.drawLine( 6, 2, right, 2 );                 // top
1005                         g.drawLine( right, 2, right, bottom - 1 );    // right
1006                         g.drawLine( 2, bottom - 1,
1007                                     right, bottom - 1 );              // bottom
1008                     }
1009                     else {
1010                         g.drawLine( right - 2, 6, right - 6, 2 );     // slant
1011                         g.drawLine( right - 2, 6,
1012                                     right - 2, bottom - 1);           // right
1013                         if ( lastInRun ) {
1014                             // last tab in run
1015                             g.drawLine( right - 6, 2, 2, 2 );         // top
1016                             g.drawLine( 2, 2, 2, bottom - 1 );        // left
1017                             g.drawLine( right - 2, bottom - 1,
1018                                         2, bottom - 1 );              // bottom
1019                         }
1020                         else {
1021                             g.drawLine( right - 6, 2, 1, 2 );         // top
1022                             g.drawLine( 1, 2, 1, bottom - 1 );        // left
1023                             g.drawLine( right - 2, bottom - 1,
1024                                         1, bottom - 1 );              // bottom
1025                         }
1026                     }
1027             }
1028             g.translate( -tabRect.x, -tabRect.y );
1029         }
1030     }
1031 
1032     protected void paintContentBorderTopEdge( Graphics g, int tabPlacement,
1033                                               int selectedIndex,
1034                                               int x, int y, int w, int h ) {
1035         boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
1036         int right = x + w - 1;
1037         Rectangle selRect = selectedIndex < 0? null :
1038                                getTabBounds(selectedIndex, calcRect);
1039         if (ocean) {
1040             g.setColor(oceanSelectedBorderColor);
1041         }
1042         else {
1043             g.setColor(selectHighlight);
1044         }
1045 
1046         // Draw unbroken line if tabs are not on TOP, OR
1047         // selected tab is not in run adjacent to content, OR
1048         // selected tab is not visible (SCROLL_TAB_LAYOUT)
1049         //
1050          if (tabPlacement != TOP || selectedIndex < 0 ||
1051             (selRect.y + selRect.height + 1 < y) ||
1052             (selRect.x < x || selRect.x > x + w)) {
1053             g.drawLine(x, y, x+w-2, y);
1054             if (ocean && tabPlacement == TOP) {
1055                 g.setColor(MetalLookAndFeel.getWhite());
1056                 g.drawLine(x, y + 1, x+w-2, y + 1);
1057             }
1058         } else {
1059             // Break line to show visual connection to selected tab
1060             boolean lastInRun = isLastInRun(selectedIndex);
1061 
1062             if ( leftToRight || lastInRun ) {
1063                 g.drawLine(x, y, selRect.x + 1, y);
1064             } else {
1065                 g.drawLine(x, y, selRect.x, y);
1066             }
1067 
1068             if (selRect.x + selRect.width < right - 1) {
1069                 if ( leftToRight && !lastInRun ) {
1070                     g.drawLine(selRect.x + selRect.width, y, right - 1, y);
1071                 } else {
1072                     g.drawLine(selRect.x + selRect.width - 1, y, right - 1, y);
1073                 }
1074             } else {
1075                 g.setColor(shadow);
1076                 g.drawLine(x+w-2, y, x+w-2, y);
1077             }
1078 
1079             if (ocean) {
1080                 g.setColor(MetalLookAndFeel.getWhite());
1081 
1082                 if ( leftToRight || lastInRun ) {
1083                     g.drawLine(x, y + 1, selRect.x + 1, y + 1);
1084                 } else {
1085                     g.drawLine(x, y + 1, selRect.x, y + 1);
1086                 }
1087 
1088                 if (selRect.x + selRect.width < right - 1) {
1089                     if ( leftToRight && !lastInRun ) {
1090                         g.drawLine(selRect.x + selRect.width, y + 1,
1091                                    right - 1, y + 1);
1092                     } else {
1093                         g.drawLine(selRect.x + selRect.width - 1, y + 1,
1094                                    right - 1, y + 1);
1095                     }
1096                 } else {
1097                     g.setColor(shadow);
1098                     g.drawLine(x+w-2, y + 1, x+w-2, y + 1);
1099                 }
1100             }
1101         }
1102     }
1103 
1104     protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
1105                                                 int selectedIndex,
1106                                                 int x, int y, int w, int h) {
1107         boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
1108         int bottom = y + h - 1;
1109         int right = x + w - 1;
1110         Rectangle selRect = selectedIndex < 0? null :
1111                                getTabBounds(selectedIndex, calcRect);
1112 
1113         g.setColor(darkShadow);
1114 
1115         // Draw unbroken line if tabs are not on BOTTOM, OR
1116         // selected tab is not in run adjacent to content, OR
1117         // selected tab is not visible (SCROLL_TAB_LAYOUT)
1118         //
1119         if (tabPlacement != BOTTOM || selectedIndex < 0 ||
1120              (selRect.y - 1 > h) ||
1121              (selRect.x < x || selRect.x > x + w)) {
1122             if (ocean && tabPlacement == BOTTOM) {
1123                 g.setColor(oceanSelectedBorderColor);
1124             }
1125             g.drawLine(x, y+h-1, x+w-1, y+h-1);
1126         } else {
1127             // Break line to show visual connection to selected tab
1128             boolean lastInRun = isLastInRun(selectedIndex);
1129 
1130             if (ocean) {
1131                 g.setColor(oceanSelectedBorderColor);
1132             }
1133 
1134             if ( leftToRight || lastInRun ) {
1135                 g.drawLine(x, bottom, selRect.x, bottom);
1136             } else {
1137                 g.drawLine(x, bottom, selRect.x - 1, bottom);
1138             }
1139 
1140             if (selRect.x + selRect.width < x + w - 2) {
1141                 if ( leftToRight && !lastInRun ) {
1142                     g.drawLine(selRect.x + selRect.width, bottom,
1143                                                    right, bottom);
1144                 } else {
1145                     g.drawLine(selRect.x + selRect.width - 1, bottom,
1146                                                        right, bottom);
1147                 }
1148             }
1149         }
1150     }
1151 
1152     protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
1153                                               int selectedIndex,
1154                                               int x, int y, int w, int h) {
1155         Rectangle selRect = selectedIndex < 0? null :
1156                                getTabBounds(selectedIndex, calcRect);
1157         if (ocean) {
1158             g.setColor(oceanSelectedBorderColor);
1159         }
1160         else {
1161             g.setColor(selectHighlight);
1162         }
1163 
1164         // Draw unbroken line if tabs are not on LEFT, OR
1165         // selected tab is not in run adjacent to content, OR
1166         // selected tab is not visible (SCROLL_TAB_LAYOUT)
1167         //
1168         if (tabPlacement != LEFT || selectedIndex < 0 ||
1169             (selRect.x + selRect.width + 1 < x) ||
1170             (selRect.y < y || selRect.y > y + h)) {
1171             g.drawLine(x, y + 1, x, y+h-2);
1172             if (ocean && tabPlacement == LEFT) {
1173                 g.setColor(MetalLookAndFeel.getWhite());
1174                 g.drawLine(x + 1, y, x + 1, y + h - 2);
1175             }
1176         } else {
1177             // Break line to show visual connection to selected tab
1178             g.drawLine(x, y, x, selRect.y + 1);
1179             if (selRect.y + selRect.height < y + h - 2) {
1180               g.drawLine(x, selRect.y + selRect.height + 1,
1181                          x, y+h+2);
1182             }
1183             if (ocean) {
1184                 g.setColor(MetalLookAndFeel.getWhite());
1185                 g.drawLine(x + 1, y + 1, x + 1, selRect.y + 1);
1186                 if (selRect.y + selRect.height < y + h - 2) {
1187                     g.drawLine(x + 1, selRect.y + selRect.height + 1,
1188                                x + 1, y+h+2);
1189                 }
1190             }
1191         }
1192     }
1193 
1194     protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
1195                                                int selectedIndex,
1196                                                int x, int y, int w, int h) {
1197         Rectangle selRect = selectedIndex < 0? null :
1198                                getTabBounds(selectedIndex, calcRect);
1199 
1200         g.setColor(darkShadow);
1201         // Draw unbroken line if tabs are not on RIGHT, OR
1202         // selected tab is not in run adjacent to content, OR
1203         // selected tab is not visible (SCROLL_TAB_LAYOUT)
1204         //
1205         if (tabPlacement != RIGHT || selectedIndex < 0 ||
1206              (selRect.x - 1 > w) ||
1207              (selRect.y < y || selRect.y > y + h)) {
1208             if (ocean && tabPlacement == RIGHT) {
1209                 g.setColor(oceanSelectedBorderColor);
1210             }
1211             g.drawLine(x+w-1, y, x+w-1, y+h-1);
1212         } else {
1213             // Break line to show visual connection to selected tab
1214             if (ocean) {
1215                 g.setColor(oceanSelectedBorderColor);
1216             }
1217             g.drawLine(x+w-1, y, x+w-1, selRect.y);
1218 
1219             if (selRect.y + selRect.height < y + h - 2) {
1220                 g.drawLine(x+w-1, selRect.y + selRect.height,
1221                            x+w-1, y+h-2);
1222             }
1223         }
1224     }
1225 
1226     protected int calculateMaxTabHeight( int tabPlacement ) {
1227         FontMetrics metrics = getFontMetrics();
1228         int height = metrics.getHeight();
1229         boolean tallerIcons = false;
1230 
1231         for ( int i = 0; i < tabPane.getTabCount(); ++i ) {
1232             Icon icon = tabPane.getIconAt( i );
1233             if ( icon != null ) {
1234                 if ( icon.getIconHeight() > height ) {
1235                     tallerIcons = true;
1236                     break;
1237                 }
1238             }
1239         }
1240         return super.calculateMaxTabHeight( tabPlacement ) -
1241                   (tallerIcons ? (tabInsets.top + tabInsets.bottom) : 0);
1242     }
1243 
1244 
1245     protected int getTabRunOverlay( int tabPlacement ) {
1246         // Tab runs laid out vertically should overlap
1247         // at least as much as the largest slant
1248         if ( tabPlacement == LEFT || tabPlacement == RIGHT ) {
1249             int maxTabHeight = calculateMaxTabHeight(tabPlacement);
1250             return maxTabHeight / 2;
1251         }
1252         return 0;
1253     }
1254 
1255     /**
1256      * Returns {@code true} if tab runs should be rotated.
1257      *
1258      * @param tabPlacement a tab placement
1259      * @param selectedRun a selected run
1260      * @return {@code true} if tab runs should be rotated.
1261      */
1262     protected boolean shouldRotateTabRuns( int tabPlacement, int selectedRun ) {
1263         return false;
1264     }
1265 
1266     // Don't pad last run
1267     protected boolean shouldPadTabRun( int tabPlacement, int run ) {
1268         return runCount > 1 && run < runCount - 1;
1269     }
1270 
1271     private boolean isLastInRun( int tabIndex ) {
1272         int run = getRunForTab( tabPane.getTabCount(), tabIndex );
1273         int lastIndex = lastTabInRun( tabPane.getTabCount(), run );
1274         return tabIndex == lastIndex;
1275     }
1276 
1277     /**
1278      * Returns the color to use for the specified tab.
1279      */
1280     private Color getUnselectedBackgroundAt(int index) {
1281         Color color = tabPane.getBackgroundAt(index);
1282         if (color instanceof UIResource) {
1283             if (unselectedBackground != null) {
1284                 return unselectedBackground;
1285             }
1286         }
1287         return color;
1288     }
1289 
1290     /**
1291      * Returns the tab index of JTabbedPane the mouse is currently over
1292      */
1293     int getRolloverTabIndex() {
1294         return getRolloverTab();
1295     }
1296 
1297     /**
1298      * This class should be treated as a &quot;protected&quot; inner class.
1299      * Instantiate it only within subclasses of {@code MetalTabbedPaneUI}.
1300      */
1301     public class TabbedPaneLayout extends BasicTabbedPaneUI.TabbedPaneLayout {
1302 
1303         /**
1304          * Constructs {@code TabbedPaneLayout}.
1305          */
1306         public TabbedPaneLayout() {
1307             MetalTabbedPaneUI.this.super();
1308         }
1309 
1310         protected void normalizeTabRuns( int tabPlacement, int tabCount,
1311                                      int start, int max ) {
1312             // Only normalize the runs for top & bottom;  normalizing
1313             // doesn't look right for Metal's vertical tabs
1314             // because the last run isn't padded and it looks odd to have
1315             // fat tabs in the first vertical runs, but slimmer ones in the
1316             // last (this effect isn't noticeable for horizontal tabs).
1317             if ( tabPlacement == TOP || tabPlacement == BOTTOM ) {
1318                 super.normalizeTabRuns( tabPlacement, tabCount, start, max );
1319             }
1320         }
1321 
1322         // Don't rotate runs!
1323         protected void rotateTabRuns( int tabPlacement, int selectedRun ) {
1324         }
1325 
1326         // Don't pad selected tab
1327         protected void padSelectedTab( int tabPlacement, int selectedIndex ) {
1328         }
1329     }
1330 
1331 }