Source for javax.swing.plaf.metal.MetalTabbedPaneUI

   1: /* MetalTabbedPaneUI.java
   2:    Copyright (C) 2005 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing.plaf.metal;
  40: 
  41: import java.awt.Color;
  42: import java.awt.Graphics;
  43: import java.awt.LayoutManager;
  44: import java.awt.Rectangle;
  45: 
  46: import javax.swing.JComponent;
  47: import javax.swing.JTabbedPane;
  48: import javax.swing.UIManager;
  49: import javax.swing.plaf.ComponentUI;
  50: import javax.swing.plaf.basic.BasicTabbedPaneUI;
  51: 
  52: /**
  53:  * A UI delegate for the {@link JTabbedPane} component.
  54:  */
  55: public class MetalTabbedPaneUI extends BasicTabbedPaneUI
  56: {
  57: 
  58:   /**
  59:    * A {@link LayoutManager} responsible for placing all the tabs and the 
  60:    * visible component inside the {@link JTabbedPane}. This class is only used 
  61:    * for {@link JTabbedPane#WRAP_TAB_LAYOUT}.
  62:    *
  63:    * @specnote Apparently this class was intended to be protected,
  64:    *           but was made public by a compiler bug and is now
  65:    *           public for compatibility.
  66:    */
  67:   public class TabbedPaneLayout 
  68:     extends BasicTabbedPaneUI.TabbedPaneLayout
  69:   {
  70:     /**
  71:      * Creates a new instance of the layout manager.
  72:      */
  73:     public TabbedPaneLayout()
  74:     {
  75:       // Nothing to do here.
  76:     }
  77:     
  78:     /**
  79:      * Overridden to do nothing, because tab runs are not rotated in the 
  80:      * {@link MetalLookAndFeel}.
  81:      * 
  82:      * @param tabPlacement  the tab placement (one of {@link #TOP}, 
  83:      *        {@link #BOTTOM}, {@link #LEFT} or {@link #RIGHT}).
  84:      * @param selectedRun  the index of the selected run.
  85:      */
  86:     protected void rotateTabRuns(int tabPlacement, int selectedRun)
  87:     {
  88:       // do nothing, because tab runs are not rotated in the MetalLookAndFeel
  89:     }
  90:     
  91:     /**
  92:      * Overridden to do nothing, because the selected tab does not have extra
  93:      * padding in the {@link MetalLookAndFeel}.
  94:      * 
  95:      * @param tabPlacement  the tab placement (one of {@link #TOP}, 
  96:      *        {@link #BOTTOM}, {@link #LEFT} or {@link #RIGHT}).
  97:      * @param selectedIndex  the index of the selected tab.
  98:      */
  99:     protected void padSelectedTab(int tabPlacement, int selectedIndex)
 100:     {
 101:       // do nothing, because the selected tab does not have extra padding in 
 102:       // the MetalLookAndFeel
 103:     }
 104:   }
 105: 
 106:   /**
 107:    * The minimum tab width.
 108:    */
 109:   protected int minTabWidth;
 110: 
 111:   /**
 112:    * The color for the selected tab.
 113:    */
 114:   protected Color selectColor;
 115: 
 116:   /**
 117:    * The color for a highlighted selected tab.
 118:    */
 119:   protected Color selectHighlight;
 120: 
 121:   /**
 122:    * The background color used for the tab area.
 123:    */
 124:   protected Color tabAreaBackground;
 125: 
 126:   /** The graphics to draw the highlight below the tab. */
 127:   private Graphics hg;
 128:   
 129:   /**
 130:    * Constructs a new instance of MetalTabbedPaneUI.
 131:    */
 132:   public MetalTabbedPaneUI()
 133:   {
 134:     super();
 135:   }
 136: 
 137:   /**
 138:    * Returns an instance of MetalTabbedPaneUI.
 139:    *
 140:    * @param component the component for which we return an UI instance
 141:    *
 142:    * @return an instance of MetalTabbedPaneUI
 143:    */
 144:   public static ComponentUI createUI(JComponent component)
 145:   {
 146:     return new MetalTabbedPaneUI();
 147:   }
 148:   
 149:   /**
 150:    * Creates and returns an instance of {@link TabbedPaneLayout}.
 151:    * 
 152:    * @return A layout manager used by this UI delegate.
 153:    */
 154:   protected LayoutManager createLayoutManager()
 155:   {
 156:     return super.createLayoutManager();
 157:   }
 158:   
 159:   /**
 160:    * Paints the border for a single tab.
 161:    * 
 162:    * @param g  the graphics device.
 163:    * @param tabPlacement  the tab placement ({@link #TOP}, {@link #LEFT}, 
 164:    *        {@link #BOTTOM} or {@link #RIGHT}).
 165:    * @param tabIndex  the index of the tab to draw the border for.
 166:    * @param x  the x-coordinate for the tab's bounding rectangle.
 167:    * @param y  the y-coordinate for the tab's bounding rectangle.
 168:    * @param w  the width for the tab's bounding rectangle.
 169:    * @param h  the height for the tab's bounding rectangle.
 170:    * @param isSelected  indicates whether or not the tab is selected.
 171:    */
 172:   protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, 
 173:           int x, int y, int w, int h, boolean isSelected) 
 174:   {
 175:     if (tabPlacement == TOP)
 176:       paintTopTabBorder(tabIndex, g, x, y, w, h, 0, 0, isSelected);
 177:     else if (tabPlacement == LEFT) 
 178:       paintLeftTabBorder(tabIndex, g, x, y, w, h, 0, 0, isSelected);
 179:     else if (tabPlacement == BOTTOM)
 180:       paintBottomTabBorder(tabIndex, g, x, y, w, h, 0, 0, isSelected);
 181:     else if (tabPlacement == RIGHT)
 182:       paintRightTabBorder(tabIndex, g, x, y, w, h, 0, 0, isSelected);
 183:     else 
 184:       throw new AssertionError("Unrecognised 'tabPlacement' argument.");
 185:   }
 186: 
 187:   /**
 188:    * Paints the border for a tab assuming that the tab position is at the top
 189:    * ({@link #TOP}).
 190:    * 
 191:    * @param tabIndex  the tab index.
 192:    * @param g  the graphics device.
 193:    * @param x  the x-coordinate for the tab's bounding rectangle.
 194:    * @param y  the y-coordinate for the tab's bounding rectangle.
 195:    * @param w  the width for the tab's bounding rectangle.
 196:    * @param h  the height for the tab's bounding rectangle.
 197:    * @param btm  ???
 198:    * @param rght  ???
 199:    * @param isSelected  indicates whether the tab is selected.
 200:    */
 201:   protected void paintTopTabBorder(int tabIndex, Graphics g, int x, int y,
 202:       int w, int h, int btm, int rght, boolean isSelected)
 203:   {
 204:     int currentRun = getRunForTab(tabPane.getTabCount(), tabIndex);
 205:     if (shouldFillGap(currentRun, tabIndex, x, y))
 206:       {
 207:         g.translate(x, y);
 208:         g.setColor(getColorForGap(currentRun, x, y));
 209:         g.fillRect(1, 0, 5, 3);
 210:         g.fillRect(1, 3, 2, 2);
 211:         g.translate(-x, -y);
 212:       }
 213:     
 214:     if (isSelected)
 215:     {
 216:       g.setColor(MetalLookAndFeel.getControlHighlight());
 217:       g.drawLine(x + 1, y + h, x + 1, y + 6);      
 218:       g.drawLine(x + 1, y + 6, x + 6, y + 1);
 219:       g.drawLine(x + 6, y + 1, x + w - 1, y + 1);
 220:     }
 221:     g.setColor(MetalLookAndFeel.getControlDarkShadow());
 222:     g.drawLine(x, y + h - 1, x, y + 6);
 223:     g.drawLine(x, y + 6, x + 6, y);
 224:     g.drawLine(x + 6, y, x + w, y);
 225:     g.drawLine(x + w, y, x + w, y + h - 1);
 226:   }
 227:   
 228:   /**
 229:    * Paints the border for a tab assuming that the tab position is at the left
 230:    * ({@link #LEFT}).
 231:    * 
 232:    * @param tabIndex  the tab index.
 233:    * @param g  the graphics device.
 234:    * @param x  the x-coordinate for the tab's bounding rectangle.
 235:    * @param y  the y-coordinate for the tab's bounding rectangle.
 236:    * @param w  the width for the tab's bounding rectangle.
 237:    * @param h  the height for the tab's bounding rectangle.
 238:    * @param btm  ???
 239:    * @param rght  ???
 240:    * @param isSelected  indicates whether the tab is selected.
 241:    */
 242:   protected void paintLeftTabBorder(int tabIndex, Graphics g, int x, int y,
 243:       int w, int h, int btm, int rght, boolean isSelected)
 244:   {
 245:     if (isSelected)
 246:     {
 247:       g.setColor(MetalLookAndFeel.getControlHighlight());
 248:       g.drawLine(x + 1, y + h, x + 1, y + 6);      
 249:       g.drawLine(x + 1, y + 6, x + 6, y + 1);
 250:       g.drawLine(x + 6, y + 1, x + w - 1, y + 1);
 251:     }
 252:     g.setColor(MetalLookAndFeel.getControlDarkShadow());
 253:     g.drawLine(x, y + h, x, y + 6);
 254:     g.drawLine(x, y + 6, x + 6, y);
 255:     g.drawLine(x + 6, y, x + w - 1, y);
 256:     g.drawLine(x, y + h, x + w - 1, y + h);
 257:   }
 258:   
 259:   /**
 260:    * Paints the border for a tab assuming that the tab position is at the right
 261:    * ({@link #RIGHT}).
 262:    * 
 263:    * @param tabIndex  the tab index.
 264:    * @param g  the graphics device.
 265:    * @param x  the x-coordinate for the tab's bounding rectangle.
 266:    * @param y  the y-coordinate for the tab's bounding rectangle.
 267:    * @param w  the width for the tab's bounding rectangle.
 268:    * @param h  the height for the tab's bounding rectangle.
 269:    * @param btm  ???
 270:    * @param rght  ???
 271:    * @param isSelected  indicates whether the tab is selected.
 272:    */
 273:   protected void paintRightTabBorder(int tabIndex, Graphics g, int x, int y,
 274:       int w, int h, int btm, int rght, boolean isSelected)
 275:   {
 276:     if (isSelected)
 277:     {
 278:       g.setColor(MetalLookAndFeel.getControlHighlight());
 279:       g.drawLine(x, y + 1, x + w - 7, y + 1);      
 280:       g.drawLine(x + w - 7, y + 1, x + w - 1, y + 7);
 281:     }
 282:     g.setColor(MetalLookAndFeel.getControlDarkShadow());
 283:     g.drawLine(x, y, x + w - 7, y);
 284:     g.drawLine(x + w - 7, y, x + w - 1, y + 6);
 285:     g.drawLine(x + w - 1, y + 6, x + w - 1, y + h - 1);
 286:     g.drawLine(x + w - 1, y + h, x, y + h);
 287:   }
 288:   
 289:   /**
 290:    * Paints the border for a tab assuming that the tab position is at the bottom
 291:    * ({@link #BOTTOM}).
 292:    * 
 293:    * @param tabIndex  the tab index.
 294:    * @param g  the graphics device.
 295:    * @param x  the x-coordinate for the tab's bounding rectangle.
 296:    * @param y  the y-coordinate for the tab's bounding rectangle.
 297:    * @param w  the width for the tab's bounding rectangle.
 298:    * @param h  the height for the tab's bounding rectangle.
 299:    * @param btm  ???
 300:    * @param rght  ???
 301:    * @param isSelected  indicates whether the tab is selected.
 302:    */
 303:   protected void paintBottomTabBorder(int tabIndex, Graphics g, int x, int y,
 304:       int w, int h, int btm, int rght, boolean isSelected)
 305:   {
 306:     int currentRun = getRunForTab(tabPane.getTabCount(), tabIndex);
 307:     if (shouldFillGap(currentRun, tabIndex, x, y))
 308:       {
 309:         g.translate(x, y);
 310:         g.setColor(getColorForGap(currentRun, x, y));
 311:         g.fillRect(1, h - 5, 3, 5);
 312:         g.fillRect(4, h - 2, 2, 2);
 313:         g.translate(-x, -y);
 314:       }
 315:     
 316:     if (isSelected)
 317:     {
 318:       g.setColor(MetalLookAndFeel.getControlHighlight());
 319:       g.drawLine(x + 1, y, x + 1, y + h - 7);      
 320:       g.drawLine(x + 1, y + h - 7, x + 7, y + h - 1);
 321:     }
 322:     g.setColor(MetalLookAndFeel.getControlDarkShadow());
 323:     g.drawLine(x, y, x, y + h - 7);
 324:     g.drawLine(x, y + h - 7, x + 6, y + h - 1);
 325:     g.drawLine(x + 6, y + h - 1, x + w, y + h - 1);
 326:     g.drawLine(x + w, y + h - 1, x + w, y);
 327:   }
 328: 
 329:   /**
 330:    * Paints the background for a tab.
 331:    * 
 332:    * @param g  the graphics device.
 333:    * @param tabPlacement  the tab placement ({@link #TOP}, {@link #LEFT}, 
 334:    *        {@link #BOTTOM} or {@link #RIGHT}).
 335:    * @param tabIndex  the index of the tab to draw the border for.
 336:    * @param x  the x-coordinate for the tab's bounding rectangle.
 337:    * @param y  the y-coordinate for the tab's bounding rectangle.
 338:    * @param w  the width for the tab's bounding rectangle.
 339:    * @param h  the height for the tab's bounding rectangle.
 340:    * @param isSelected  indicates whether or not the tab is selected.
 341:    */
 342:   protected void paintTabBackground(Graphics g, int tabPlacement,
 343:       int tabIndex, int x, int y, int w, int h, boolean isSelected)
 344:   {
 345:     if (isSelected)
 346:       g.setColor(UIManager.getColor("TabbedPane.selected"));
 347:     else
 348:       {
 349:         // This is only present in the OceanTheme, so we must check if it
 350:         // is actually there
 351:         Color background = UIManager.getColor("TabbedPane.unselectedBackground");
 352:         if (background == null)
 353:           background = UIManager.getColor("TabbedPane.background");
 354:         g.setColor(background);
 355:       }
 356:     int[] px, py;
 357:     if (tabPlacement == TOP) 
 358:       {
 359:         px = new int[] {x + 6, x + w - 1, x + w -1, x + 2, x + 2};
 360:         py = new int[] {y + 2, y + 2, y + h - 1, y + h -1, y + 6};
 361:       }
 362:     else if (tabPlacement == LEFT)
 363:       {
 364:         px = new int[] {x + 6, x + w - 1, x + w -1, x + 2, x + 2};
 365:         py = new int[] {y + 2, y + 2, y + h - 1, y + h -1, y + 6};
 366:       }
 367:     else if (tabPlacement == BOTTOM)
 368:       {
 369:         px = new int[] {x + 2, x + w - 1, x + w -1, x + 8, x + 2};
 370:         py = new int[] {y, y, y + h - 1, y + h -1, y + h - 7};
 371:       }
 372:     else if (tabPlacement == RIGHT)
 373:       {
 374:         px = new int[] {x + 2, x + w - 7, x + w - 1, x + w - 1, x + 2};
 375:         py = new int[] {y + 2, y + 2, y + 7, y + h -1, y + h - 1};
 376:       }
 377:     else 
 378:       throw new AssertionError("Unrecognised 'tabPlacement' argument.");
 379:     g.fillPolygon(px, py, 5);
 380:     hg = g;
 381:     paintHighlightBelowTab();
 382:   }
 383:   
 384:   /**
 385:    * Returns <code>true</code> if the tabs in the specified run should be 
 386:    * padded to make the run fill the width/height of the {@link JTabbedPane}.
 387:    * 
 388:    * @param tabPlacement  the tab placement for the {@link JTabbedPane} (one of
 389:    *        {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} and {@link #RIGHT}).
 390:    * @param run  the run index.
 391:    * 
 392:    * @return A boolean.
 393:    */
 394:   protected boolean shouldPadTabRun(int tabPlacement, int run)
 395:   {
 396:     // as far as I can tell, all runs should be padded except the last run
 397:     // (which is drawn at the very top for tabPlacement == TOP)
 398:     return run < this.runCount - 1;
 399:   }
 400: 
 401:   /**
 402:    * Installs the defaults for this UI. This method calls super.installDefaults
 403:    * and then loads the Metal specific defaults for TabbedPane.
 404:    */
 405:   protected void installDefaults()
 406:   {
 407:     super.installDefaults();
 408:     selectColor = UIManager.getColor("TabbedPane.selected");
 409:     selectHighlight = UIManager.getColor("TabbedPane.selectHighlight");
 410:     tabAreaBackground = UIManager.getColor("TabbedPane.tabAreaBackground");
 411:     minTabWidth = 0;
 412:   }
 413:   
 414:   /**
 415:    * Returns the color for the gap.
 416:    * 
 417:    * @param currentRun - The current run to return the color for
 418:    * @param x - The x position of the current run
 419:    * @param y - The y position of the current run
 420:    * 
 421:    * @return the color for the gap in the current run.
 422:    */
 423:   protected Color getColorForGap(int currentRun, int x, int y)
 424:   {
 425:     int index = tabForCoordinate(tabPane, x, y);
 426:     int selected = tabPane.getSelectedIndex();
 427:     if (selected == index)
 428:       return selectColor;
 429:     return tabAreaBackground;
 430:   }
 431:   
 432:   /**
 433:    * Returns true if the gap should be filled in.
 434:    * 
 435:    * @param currentRun - The current run
 436:    * @param tabIndex - The current tab
 437:    * @param x - The x position of the tab
 438:    * @param y - The y position of the tab
 439:    * 
 440:    * @return true if the gap at the current run should be filled 
 441:    */
 442:   protected boolean shouldFillGap(int currentRun, int tabIndex, int x, int y)
 443:   {
 444:     // As far as I can tell, the gap is never filled in.
 445:     return false;
 446:   }
 447:   
 448:   /**
 449:    * Paints the highlight below the tab, if there is one.
 450:    */
 451:   protected void paintHighlightBelowTab()
 452:   {
 453:     int selected = tabPane.getSelectedIndex();
 454:     int tabPlacement = tabPane.getTabPlacement();
 455:     Rectangle bounds = getTabBounds(tabPane, selected);
 456:     
 457:     hg.setColor(selectColor);
 458:     int x = bounds.x;
 459:     int y = bounds.y;
 460:     int w = bounds.width;
 461:     int h = bounds.height;
 462: 
 463:     if (tabPlacement == TOP) 
 464:         hg.fillRect(x, y + h - 2, w, 30);
 465:     else if (tabPlacement == LEFT)
 466:         hg.fillRect(x + w - 1, y, 20, h);
 467:     else if (tabPlacement == BOTTOM)
 468:         hg.fillRect(x, y - h + 2, w, 30);
 469:     else if (tabPlacement == RIGHT)
 470:         hg.fillRect(x - 18, y, 20, h);
 471:     else 
 472:       throw new AssertionError("Unrecognised 'tabPlacement' argument.");
 473:     hg = null;
 474:   }
 475:   
 476:   /**
 477:    * Returns true if we should rotate the tab runs. 
 478:    * 
 479:    * @param tabPlacement - The current tab placement.
 480:    * @param selectedRun - The selected run.
 481:    * 
 482:    * @return true if the tab runs should be rotated.
 483:    */
 484:   protected boolean shouldRotateTabRuns(int tabPlacement,
 485:                                         int selectedRun)
 486:   {
 487:     // false because tab runs are not rotated in the MetalLookAndFeel
 488:     return false;
 489:   }
 490: }