1 /* BasicTabbedPaneUI.java -- 2 Copyright (C) 2002, 2004, 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.basic; 40 41 import java.awt.Color; 42 import java.awt.Component; 43 import java.awt.Container; 44 import java.awt.Dimension; 45 import java.awt.Font; 46 import java.awt.FontMetrics; 47 import java.awt.Graphics; 48 import java.awt.Insets; 49 import java.awt.LayoutManager; 50 import java.awt.Point; 51 import java.awt.Rectangle; 52 import java.awt.event.FocusAdapter; 53 import java.awt.event.FocusEvent; 54 import java.awt.event.FocusListener; 55 import java.awt.event.MouseAdapter; 56 import java.awt.event.MouseEvent; 57 import java.awt.event.MouseListener; 58 import java.beans.PropertyChangeEvent; 59 import java.beans.PropertyChangeListener; 60 61 import javax.swing.Icon; 62 import javax.swing.JComponent; 63 import javax.swing.JPanel; 64 import javax.swing.JTabbedPane; 65 import javax.swing.JViewport; 66 import javax.swing.KeyStroke; 67 import javax.swing.LookAndFeel; 68 import javax.swing.SwingConstants; 69 import javax.swing.SwingUtilities; 70 import javax.swing.UIManager; 71 import javax.swing.event.ChangeEvent; 72 import javax.swing.event.ChangeListener; 73 import javax.swing.plaf.ComponentUI; 74 import javax.swing.plaf.PanelUI; 75 import javax.swing.plaf.TabbedPaneUI; 76 import javax.swing.plaf.UIResource; 77 import javax.swing.text.View; 78 79 /** 80 * This is the Basic Look and Feel's UI delegate for JTabbedPane. 81 */ 82 public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants 83 { 84 /** 85 * A helper class that handles focus. 86 * 87 * @specnote Apparently this class was intended to be protected, 88 * but was made public by a compiler bug and is now 89 * public for compatibility. 90 */ 91 public class FocusHandler extends FocusAdapter 92 { 93 /** 94 * This method is called when the component gains focus. 95 * 96 * @param e The FocusEvent. 97 */ focusGained(FocusEvent e)98 public void focusGained(FocusEvent e) 99 { 100 // FIXME: Implement. 101 } 102 103 /** 104 * This method is called when the component loses focus. 105 * 106 * @param e The FocusEvent. 107 */ focusLost(FocusEvent e)108 public void focusLost(FocusEvent e) 109 { 110 // FIXME: Implement. 111 } 112 } 113 114 /** 115 * A helper class for determining if mouse presses occur inside tabs and 116 * sets the index appropriately. In SCROLL_TAB_MODE, this class also 117 * handles the mouse clicks on the scrolling buttons. 118 * 119 * @specnote Apparently this class was intended to be protected, 120 * but was made public by a compiler bug and is now 121 * public for compatibility. 122 */ 123 public class MouseHandler extends MouseAdapter 124 { 125 /** 126 * This method is called when the mouse is pressed. The index cannot 127 * change to a tab that is not enabled. 128 * 129 * @param e The MouseEvent. 130 */ mousePressed(MouseEvent e)131 public void mousePressed(MouseEvent e) 132 { 133 int x = e.getX(); 134 int y = e.getY(); 135 int tabCount = tabPane.getTabCount(); 136 137 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) 138 { 139 if (e.getSource() == incrButton) 140 { 141 if (++currentScrollLocation >= tabCount) 142 currentScrollLocation = tabCount - 1; 143 144 int width = 0; 145 for (int i = currentScrollLocation - 1; i < tabCount; i++) 146 width += rects[i].width; 147 if (width < viewport.getWidth()) 148 // FIXME: Still getting mouse events after the button is disabled. 149 // incrButton.setEnabled(false); 150 currentScrollLocation--; 151 else if (! decrButton.isEnabled()) 152 decrButton.setEnabled(true); 153 tabPane.revalidate(); 154 tabPane.repaint(); 155 return; 156 } 157 else if (e.getSource() == decrButton) 158 { 159 if (--currentScrollLocation < 0) 160 currentScrollLocation = 0; 161 if (currentScrollLocation == 0) 162 decrButton.setEnabled(false); 163 else if (! incrButton.isEnabled()) 164 incrButton.setEnabled(true); 165 tabPane.revalidate(); 166 tabPane.repaint(); 167 return; 168 } 169 } 170 171 int index = tabForCoordinate(tabPane, x, y); 172 173 // We need to check since there are areas where tabs cannot be 174 // e.g. in the inset area. 175 if (index != -1 && tabPane.isEnabledAt(index)) 176 tabPane.setSelectedIndex(index); 177 tabPane.revalidate(); 178 tabPane.repaint(); 179 } 180 } 181 182 /** 183 * This class handles PropertyChangeEvents fired from the JTabbedPane. 184 * 185 * @specnote Apparently this class was intended to be protected, 186 * but was made public by a compiler bug and is now 187 * public for compatibility. 188 */ 189 public class PropertyChangeHandler implements PropertyChangeListener 190 { 191 /** 192 * This method is called whenever one of the properties of the JTabbedPane 193 * changes. 194 * 195 * @param e The PropertyChangeEvent. 196 */ propertyChange(PropertyChangeEvent e)197 public void propertyChange(PropertyChangeEvent e) 198 { 199 if (e.getPropertyName().equals("tabLayoutPolicy")) 200 { 201 layoutManager = createLayoutManager(); 202 203 tabPane.setLayout(layoutManager); 204 } 205 else if (e.getPropertyName().equals("tabPlacement") 206 && tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) 207 { 208 incrButton = createIncreaseButton(); 209 decrButton = createDecreaseButton(); 210 } 211 tabPane.layout(); 212 tabPane.repaint(); 213 } 214 } 215 216 /** 217 * A LayoutManager responsible for placing all the tabs and the visible 218 * component inside the JTabbedPane. This class is only used for 219 * WRAP_TAB_LAYOUT. 220 * 221 * @specnote Apparently this class was intended to be protected, 222 * but was made public by a compiler bug and is now 223 * public for compatibility. 224 */ 225 public class TabbedPaneLayout implements LayoutManager 226 { 227 /** 228 * This method is called when a component is added to the JTabbedPane. 229 * 230 * @param name The name of the component. 231 * @param comp The component being added. 232 */ addLayoutComponent(String name, Component comp)233 public void addLayoutComponent(String name, Component comp) 234 { 235 // Do nothing. 236 } 237 238 /** 239 * This method is called when the rectangles need to be calculated. It 240 * also fixes the size of the visible component. 241 */ calculateLayoutInfo()242 public void calculateLayoutInfo() 243 { 244 calculateTabRects(tabPane.getTabPlacement(), tabPane.getTabCount()); 245 246 if (tabPane.getSelectedIndex() != -1) 247 { 248 Component visible = getVisibleComponent(); 249 Insets insets = getContentBorderInsets(tabPane.getTabPlacement()); 250 if (visible != null) 251 visible.setBounds(contentRect.x + insets.left, 252 contentRect.y + insets.top, 253 contentRect.width - insets.left - insets.right, 254 contentRect.height - insets.top - insets.bottom); 255 } 256 } 257 258 /** 259 * This method calculates the size of the the JTabbedPane. 260 * 261 * @param minimum Whether the JTabbedPane will try to be as small as it 262 * can. 263 * 264 * @return The desired size of the JTabbedPane. 265 */ calculateSize(boolean minimum)266 protected Dimension calculateSize(boolean minimum) 267 { 268 int tabPlacement = tabPane.getTabPlacement(); 269 int width = 0; 270 int height = 0; 271 272 int componentHeight = 0; 273 int componentWidth = 0; 274 Component c; 275 Dimension dims; 276 for (int i = 0; i < tabPane.getTabCount(); i++) 277 { 278 c = tabPane.getComponentAt(i); 279 if (c == null) 280 continue; 281 calcRect = c.getBounds(); 282 dims = c.getPreferredSize(); 283 if (dims != null) 284 { 285 componentHeight = Math.max(componentHeight, dims.height); 286 componentWidth = Math.max(componentWidth, dims.width); 287 } 288 } 289 Insets insets = tabPane.getInsets(); 290 291 if (tabPlacement == SwingConstants.TOP 292 || tabPlacement == SwingConstants.BOTTOM) 293 { 294 int min = calculateMaxTabWidth(tabPlacement); 295 width = Math.max(min, componentWidth); 296 297 int tabAreaHeight = preferredTabAreaHeight(tabPlacement, width); 298 height = tabAreaHeight + componentHeight; 299 } 300 else 301 { 302 int min = calculateMaxTabHeight(tabPlacement); 303 height = Math.max(min, componentHeight); 304 305 int tabAreaWidth = preferredTabAreaWidth(tabPlacement, height); 306 width = tabAreaWidth + componentWidth; 307 } 308 309 return new Dimension(width, height); 310 } 311 312 // if tab placement is LEFT OR RIGHT, they share width. 313 // if tab placement is TOP OR BOTTOM, they share height 314 // PRE STEP: finds the default sizes for the labels as well as their locations. 315 // AND where they will be placed within the run system. 316 // 1. calls normalizeTab Runs. 317 // 2. calls rotate tab runs. 318 // 3. pads the tab runs. 319 // 4. pads the selected tab. 320 321 /** 322 * This method is called to calculate the tab rectangles. This method 323 * will calculate the size and position of all rectangles (taking into 324 * account which ones should be in which tab run). It will pad them and 325 * normalize them as necessary. 326 * 327 * @param tabPlacement The JTabbedPane's tab placement. 328 * @param tabCount The run the current selection is in. 329 */ calculateTabRects(int tabPlacement, int tabCount)330 protected void calculateTabRects(int tabPlacement, int tabCount) 331 { 332 if (tabCount == 0) 333 return; 334 assureRectsCreated(tabCount); 335 336 FontMetrics fm = getFontMetrics(); 337 SwingUtilities.calculateInnerArea(tabPane, calcRect); 338 Insets tabAreaInsets = getTabAreaInsets(tabPlacement); 339 Insets insets = tabPane.getInsets(); 340 int max = 0; 341 int runs = 0; 342 int start = getTabRunIndent(tabPlacement, 1); 343 if (tabPlacement == SwingConstants.TOP 344 || tabPlacement == SwingConstants.BOTTOM) 345 { 346 int maxHeight = calculateMaxTabHeight(tabPlacement); 347 348 calcRect.width -= tabAreaInsets.left + tabAreaInsets.right; 349 max = calcRect.width + tabAreaInsets.left + insets.left; 350 start += tabAreaInsets.left + insets.left; 351 int width = 0; 352 int runWidth = start; 353 354 for (int i = 0; i < tabCount; i++) 355 { 356 width = calculateTabWidth(tabPlacement, i, fm); 357 if (runWidth + width > max) 358 { 359 runWidth = tabAreaInsets.left + insets.left 360 + getTabRunIndent(tabPlacement, ++runs); 361 rects[i] = new Rectangle(runWidth, 362 insets.top + tabAreaInsets.top, 363 width, maxHeight); 364 runWidth += width; 365 if (runs > tabRuns.length - 1) 366 expandTabRunsArray(); 367 tabRuns[runs] = i; 368 } 369 else 370 { 371 rects[i] = new Rectangle(runWidth, 372 insets.top + tabAreaInsets.top, 373 width, maxHeight); 374 runWidth += width; 375 } 376 } 377 runs++; 378 tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right; 379 tabAreaRect.height = runs * maxTabHeight 380 - (runs - 1) * tabRunOverlay 381 + tabAreaInsets.top + tabAreaInsets.bottom; 382 contentRect.width = tabAreaRect.width; 383 contentRect.height = tabPane.getHeight() - insets.top 384 - insets.bottom - tabAreaRect.height; 385 contentRect.x = insets.left; 386 tabAreaRect.x = insets.left; 387 if (tabPlacement == SwingConstants.BOTTOM) 388 { 389 contentRect.y = insets.top; 390 tabAreaRect.y = contentRect.y + contentRect.height; 391 } 392 else 393 { 394 tabAreaRect.y = insets.top; 395 contentRect.y = tabAreaRect.y + tabAreaRect.height; 396 } 397 } 398 else 399 { 400 int maxWidth = calculateMaxTabWidth(tabPlacement); 401 calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom; 402 max = calcRect.height + tabAreaInsets.top + insets.top; 403 404 int height = 0; 405 start += tabAreaInsets.top + insets.top; 406 int runHeight = start; 407 408 int fontHeight = fm.getHeight(); 409 410 for (int i = 0; i < tabCount; i++) 411 { 412 height = calculateTabHeight(tabPlacement, i, fontHeight); 413 if (runHeight + height > max) 414 { 415 runHeight = tabAreaInsets.top + insets.top 416 + getTabRunIndent(tabPlacement, ++runs); 417 rects[i] = new Rectangle(insets.left + tabAreaInsets.left, 418 runHeight, maxWidth, height); 419 runHeight += height; 420 if (runs > tabRuns.length - 1) 421 expandTabRunsArray(); 422 tabRuns[runs] = i; 423 } 424 else 425 { 426 rects[i] = new Rectangle(insets.left + tabAreaInsets.left, 427 runHeight, maxWidth, height); 428 runHeight += height; 429 } 430 } 431 runs++; 432 433 tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay 434 + tabAreaInsets.left + tabAreaInsets.right; 435 tabAreaRect.height = tabPane.getHeight() - insets.top 436 - insets.bottom; 437 tabAreaRect.y = insets.top; 438 contentRect.width = tabPane.getWidth() - insets.left - insets.right 439 - tabAreaRect.width; 440 contentRect.height = tabAreaRect.height; 441 contentRect.y = insets.top; 442 if (tabPlacement == SwingConstants.LEFT) 443 { 444 tabAreaRect.x = insets.left; 445 contentRect.x = tabAreaRect.x + tabAreaRect.width; 446 } 447 else 448 { 449 contentRect.x = insets.left; 450 tabAreaRect.x = contentRect.x + contentRect.width; 451 } 452 } 453 runCount = runs; 454 455 tabRuns[0] = 0; 456 normalizeTabRuns(tabPlacement, tabCount, start, max); 457 selectedRun = getRunForTab(tabCount, tabPane.getSelectedIndex()); 458 if (shouldRotateTabRuns(tabPlacement)) 459 rotateTabRuns(tabPlacement, selectedRun); 460 461 // Need to pad the runs and move them to the correct location. 462 for (int i = 0; i < runCount; i++) 463 { 464 int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1; 465 if (first == tabCount) 466 first = 0; 467 int last = lastTabInRun(tabCount, i); 468 if (shouldPadTabRun(tabPlacement, i)) 469 padTabRun(tabPlacement, first, last, max); 470 471 // Done padding, now need to move it. 472 if (tabPlacement == SwingConstants.TOP && i > 0) 473 { 474 for (int j = first; j <= last; j++) 475 rects[j].y += (runCount - i) * maxTabHeight 476 - (runCount - i) * tabRunOverlay; 477 } 478 479 if (tabPlacement == SwingConstants.BOTTOM) 480 { 481 int height = tabPane.getBounds().height - insets.bottom 482 - tabAreaInsets.bottom; 483 int adjustment; 484 if (i == 0) 485 adjustment = height - maxTabHeight; 486 else 487 adjustment = height - (runCount - i + 1) * maxTabHeight 488 - (runCount - i) * tabRunOverlay; 489 490 for (int j = first; j <= last; j++) 491 rects[j].y = adjustment; 492 } 493 494 if (tabPlacement == SwingConstants.LEFT && i > 0) 495 { 496 for (int j = first; j <= last; j++) 497 rects[j].x += (runCount - i) * maxTabWidth 498 - (runCount - i) * tabRunOverlay; 499 } 500 501 if (tabPlacement == SwingConstants.RIGHT) 502 { 503 int width = tabPane.getBounds().width - insets.right 504 - tabAreaInsets.right; 505 int adjustment; 506 if (i == 0) 507 adjustment = width - maxTabWidth; 508 else 509 adjustment = width - (runCount - i + 1) * maxTabWidth 510 + (runCount - i) * tabRunOverlay; 511 512 for (int j = first; j <= last; j++) 513 rects[j].x = adjustment; 514 } 515 } 516 padSelectedTab(tabPlacement, tabPane.getSelectedIndex()); 517 } 518 519 /** 520 * This method is called when the JTabbedPane is laid out in 521 * WRAP_TAB_LAYOUT. It calls calculateLayoutInfo to find the positions 522 * of all its components. 523 * 524 * @param parent The Container to lay out. 525 */ layoutContainer(Container parent)526 public void layoutContainer(Container parent) 527 { 528 calculateLayoutInfo(); 529 } 530 531 /** 532 * This method returns the minimum layout size for the given container. 533 * 534 * @param parent The container that is being sized. 535 * 536 * @return The minimum size. 537 */ minimumLayoutSize(Container parent)538 public Dimension minimumLayoutSize(Container parent) 539 { 540 return calculateSize(false); 541 } 542 543 // If there is more free space in an adjacent run AND the tab in the run can fit in the 544 // adjacent run, move it. This method is not perfect, it is merely an approximation. 545 // If you play around with Sun's JTabbedPane, you'll see that 546 // it does do some pretty strange things with regards to not moving tabs 547 // that should be moved. 548 // start = the x position where the tabs will begin 549 // max = the maximum position of where the tabs can go to (tabAreaInsets.left + the width of the tab area) 550 551 /** 552 * This method tries to "even out" the number of tabs in each run based on 553 * their widths. 554 * 555 * @param tabPlacement The JTabbedPane's tab placement. 556 * @param tabCount The number of tabs. 557 * @param start The x position where the tabs will begin. 558 * @param max The maximum x position where the tab can run to. 559 */ normalizeTabRuns(int tabPlacement, int tabCount, int start, int max)560 protected void normalizeTabRuns(int tabPlacement, int tabCount, int start, 561 int max) 562 { 563 Insets tabAreaInsets = getTabAreaInsets(tabPlacement); 564 if (tabPlacement == SwingUtilities.TOP 565 || tabPlacement == SwingUtilities.BOTTOM) 566 { 567 // We should only do this for runCount - 1, cause we can only shift that many times between 568 // runs. 569 for (int i = 1; i < runCount; i++) 570 { 571 Rectangle currRun = rects[lastTabInRun(tabCount, i)]; 572 Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))]; 573 int spaceInCurr = currRun.x + currRun.width; 574 int spaceInNext = nextRun.x + nextRun.width; 575 576 int diffNow = spaceInCurr - spaceInNext; 577 int diffLater = (spaceInCurr - currRun.width) 578 - (spaceInNext + currRun.width); 579 while (Math.abs(diffLater) < Math.abs(diffNow) 580 && spaceInNext + currRun.width < max) 581 { 582 tabRuns[i]--; 583 spaceInNext += currRun.width; 584 spaceInCurr -= currRun.width; 585 currRun = rects[lastTabInRun(tabCount, i)]; 586 diffNow = spaceInCurr - spaceInNext; 587 diffLater = (spaceInCurr - currRun.width) 588 - (spaceInNext + currRun.width); 589 } 590 591 // Fix the bounds. 592 int first = lastTabInRun(tabCount, i) + 1; 593 int last = lastTabInRun(tabCount, getNextTabRun(i)); 594 int currX = tabAreaInsets.left; 595 for (int j = first; j <= last; j++) 596 { 597 rects[j].x = currX; 598 currX += rects[j].width; 599 } 600 } 601 } 602 else 603 { 604 for (int i = 1; i < runCount; i++) 605 { 606 Rectangle currRun = rects[lastTabInRun(tabCount, i)]; 607 Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))]; 608 int spaceInCurr = currRun.y + currRun.height; 609 int spaceInNext = nextRun.y + nextRun.height; 610 611 int diffNow = spaceInCurr - spaceInNext; 612 int diffLater = (spaceInCurr - currRun.height) 613 - (spaceInNext + currRun.height); 614 while (Math.abs(diffLater) < Math.abs(diffNow) 615 && spaceInNext + currRun.height < max) 616 { 617 tabRuns[i]--; 618 spaceInNext += currRun.height; 619 spaceInCurr -= currRun.height; 620 currRun = rects[lastTabInRun(tabCount, i)]; 621 diffNow = spaceInCurr - spaceInNext; 622 diffLater = (spaceInCurr - currRun.height) 623 - (spaceInNext + currRun.height); 624 } 625 626 int first = lastTabInRun(tabCount, i) + 1; 627 int last = lastTabInRun(tabCount, getNextTabRun(i)); 628 int currY = tabAreaInsets.top; 629 for (int j = first; j <= last; j++) 630 { 631 rects[j].y = currY; 632 currY += rects[j].height; 633 } 634 } 635 } 636 } 637 638 /** 639 * This method pads the tab at the selected index by the selected tab pad 640 * insets (so that it looks larger). 641 * 642 * @param tabPlacement The placement of the tabs. 643 * @param selectedIndex The selected index. 644 */ padSelectedTab(int tabPlacement, int selectedIndex)645 protected void padSelectedTab(int tabPlacement, int selectedIndex) 646 { 647 Insets insets = getSelectedTabPadInsets(tabPlacement); 648 rects[selectedIndex].x -= insets.left; 649 rects[selectedIndex].y -= insets.top; 650 rects[selectedIndex].width += insets.left + insets.right; 651 rects[selectedIndex].height += insets.top + insets.bottom; 652 } 653 654 // If the tabs on the run don't fill the width of the window, make it fit now. 655 // start = starting index of the run 656 // end = last index of the run 657 // max = tabAreaInsets.left + width (or equivalent) 658 // assert start <= end. 659 660 /** 661 * This method makes each tab in the run larger so that the tabs expand 662 * to fill the runs width/height (depending on tabPlacement). 663 * 664 * @param tabPlacement The placement of the tabs. 665 * @param start The index of the first tab. 666 * @param end The last index of the tab 667 * @param max The amount of space in the run (width for TOP and BOTTOM 668 * tabPlacement). 669 */ padTabRun(int tabPlacement, int start, int end, int max)670 protected void padTabRun(int tabPlacement, int start, int end, int max) 671 { 672 if (tabPlacement == SwingConstants.TOP 673 || tabPlacement == SwingConstants.BOTTOM) 674 { 675 int runWidth = rects[end].x + rects[end].width; 676 int spaceRemaining = max - runWidth; 677 int numTabs = end - start + 1; 678 679 // now divvy up the space. 680 int spaceAllocated = spaceRemaining / numTabs; 681 int currX = rects[start].x; 682 for (int i = start; i <= end; i++) 683 { 684 rects[i].x = currX; 685 rects[i].width += spaceAllocated; 686 currX += rects[i].width; 687 // This is used because since the spaceAllocated 688 // variable is an int, it rounds down. Sometimes, 689 // we don't fill an entire row, so we make it do 690 // so now. 691 if (i == end && rects[i].x + rects[i].width != max) 692 rects[i].width = max - rects[i].x; 693 } 694 } 695 else 696 { 697 int runHeight = rects[end].y + rects[end].height; 698 int spaceRemaining = max - runHeight; 699 int numTabs = end - start + 1; 700 701 int spaceAllocated = spaceRemaining / numTabs; 702 int currY = rects[start].y; 703 for (int i = start; i <= end; i++) 704 { 705 rects[i].y = currY; 706 rects[i].height += spaceAllocated; 707 currY += rects[i].height; 708 if (i == end && rects[i].y + rects[i].height != max) 709 rects[i].height = max - rects[i].y; 710 } 711 } 712 } 713 714 /** 715 * This method returns the preferred layout size for the given container. 716 * 717 * @param parent The container to size. 718 * 719 * @return The preferred layout size. 720 */ preferredLayoutSize(Container parent)721 public Dimension preferredLayoutSize(Container parent) 722 { 723 return calculateSize(false); 724 } 725 726 /** 727 * This method returns the preferred tab height given a tabPlacement and 728 * width. 729 * 730 * @param tabPlacement The JTabbedPane's tab placement. 731 * @param width The expected width. 732 * 733 * @return The preferred tab area height. 734 */ preferredTabAreaHeight(int tabPlacement, int width)735 protected int preferredTabAreaHeight(int tabPlacement, int width) 736 { 737 if (tabPane.getTabCount() == 0) 738 return calculateTabAreaHeight(tabPlacement, 0, 0); 739 740 int runs = 0; 741 int runWidth = 0; 742 int tabWidth = 0; 743 744 FontMetrics fm = getFontMetrics(); 745 746 Insets tabAreaInsets = getTabAreaInsets(tabPlacement); 747 Insets insets = tabPane.getInsets(); 748 749 // Only interested in width, this is a messed up rectangle now. 750 width -= tabAreaInsets.left + tabAreaInsets.right + insets.left 751 + insets.right; 752 753 // The reason why we can't use runCount: 754 // This method is only called to calculate the size request 755 // for the tabbedPane. However, this size request is dependent on 756 // our desired width. We need to find out what the height would 757 // be IF we got our desired width. 758 for (int i = 0; i < tabPane.getTabCount(); i++) 759 { 760 tabWidth = calculateTabWidth(tabPlacement, i, fm); 761 if (runWidth + tabWidth > width) 762 { 763 runWidth = tabWidth; 764 runs++; 765 } 766 else 767 runWidth += tabWidth; 768 } 769 runs++; 770 771 int maxTabHeight = calculateMaxTabHeight(tabPlacement); 772 int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs, 773 maxTabHeight); 774 return tabAreaHeight; 775 } 776 777 /** 778 * This method calculates the preferred tab area width given a tab 779 * placement and height. 780 * 781 * @param tabPlacement The JTabbedPane's tab placement. 782 * @param height The expected height. 783 * 784 * @return The preferred tab area width. 785 */ preferredTabAreaWidth(int tabPlacement, int height)786 protected int preferredTabAreaWidth(int tabPlacement, int height) 787 { 788 if (tabPane.getTabCount() == 0) 789 return calculateTabAreaHeight(tabPlacement, 0, 0); 790 791 int runs = 0; 792 int runHeight = 0; 793 int tabHeight = 0; 794 795 FontMetrics fm = getFontMetrics(); 796 797 Insets tabAreaInsets = getTabAreaInsets(tabPlacement); 798 Insets insets = tabPane.getInsets(); 799 800 height -= tabAreaInsets.top + tabAreaInsets.bottom + insets.top 801 + insets.bottom; 802 int fontHeight = fm.getHeight(); 803 804 for (int i = 0; i < tabPane.getTabCount(); i++) 805 { 806 tabHeight = calculateTabHeight(tabPlacement, i, fontHeight); 807 if (runHeight + tabHeight > height) 808 { 809 runHeight = tabHeight; 810 runs++; 811 } 812 else 813 runHeight += tabHeight; 814 } 815 runs++; 816 817 int maxTabWidth = calculateMaxTabWidth(tabPlacement); 818 int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth); 819 return tabAreaWidth; 820 } 821 822 /** 823 * This method rotates the places each run in the correct place the 824 * tabRuns array. See the comment for tabRuns for how the runs are placed 825 * in the array. 826 * 827 * @param tabPlacement The JTabbedPane's tab placement. 828 * @param selectedRun The run the current selection is in. 829 */ rotateTabRuns(int tabPlacement, int selectedRun)830 protected void rotateTabRuns(int tabPlacement, int selectedRun) 831 { 832 if (runCount == 1 || selectedRun == 1 || selectedRun == -1) 833 return; 834 int[] newTabRuns = new int[tabRuns.length]; 835 int currentRun = selectedRun; 836 int i = 1; 837 do 838 { 839 newTabRuns[i] = tabRuns[currentRun]; 840 currentRun = getNextTabRun(currentRun); 841 i++; 842 } 843 while (i < runCount); 844 if (runCount > 1) 845 newTabRuns[0] = tabRuns[currentRun]; 846 847 tabRuns = newTabRuns; 848 BasicTabbedPaneUI.this.selectedRun = 1; 849 } 850 851 /** 852 * This method is called when a component is removed from the 853 * JTabbedPane. 854 * 855 * @param comp The component removed. 856 */ removeLayoutComponent(Component comp)857 public void removeLayoutComponent(Component comp) 858 { 859 // Do nothing. 860 } 861 } 862 863 /** 864 * This class acts as the LayoutManager for the JTabbedPane in 865 * SCROLL_TAB_MODE. 866 */ 867 private class TabbedPaneScrollLayout extends TabbedPaneLayout 868 { 869 /** 870 * This method returns the preferred layout size for the given container. 871 * 872 * @param parent The container to calculate a size for. 873 * 874 * @return The preferred layout size. 875 */ preferredLayoutSize(Container parent)876 public Dimension preferredLayoutSize(Container parent) 877 { 878 return super.calculateSize(true); 879 } 880 881 /** 882 * This method returns the minimum layout size for the given container. 883 * 884 * @param parent The container to calculate a size for. 885 * 886 * @return The minimum layout size. 887 */ minimumLayoutSize(Container parent)888 public Dimension minimumLayoutSize(Container parent) 889 { 890 return super.calculateSize(true); 891 } 892 893 /** 894 * This method calculates the tab area height given a desired width. 895 * 896 * @param tabPlacement The JTabbedPane's tab placement. 897 * @param width The expected width. 898 * 899 * @return The tab area height given the width. 900 */ preferredTabAreaHeight(int tabPlacement, int width)901 protected int preferredTabAreaHeight(int tabPlacement, int width) 902 { 903 if (tabPane.getTabCount() == 0) 904 return calculateTabAreaHeight(tabPlacement, 0, 0); 905 906 int runs = 1; 907 908 int maxTabHeight = calculateMaxTabHeight(tabPlacement); 909 int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs, 910 maxTabHeight); 911 return tabAreaHeight; 912 } 913 914 /** 915 * This method calculates the tab area width given a desired height. 916 * 917 * @param tabPlacement The JTabbedPane's tab placement. 918 * @param height The expected height. 919 * 920 * @return The tab area width given the height. 921 */ preferredTabAreaWidth(int tabPlacement, int height)922 protected int preferredTabAreaWidth(int tabPlacement, int height) 923 { 924 if (tabPane.getTabCount() == 0) 925 return calculateTabAreaHeight(tabPlacement, 0, 0); 926 927 int runs = 1; 928 929 int maxTabWidth = calculateMaxTabWidth(tabPlacement); 930 int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth); 931 return tabAreaWidth; 932 } 933 934 /** 935 * This method is called to calculate the tab rectangles. This method 936 * will calculate the size and position of all rectangles (taking into 937 * account which ones should be in which tab run). It will pad them and 938 * normalize them as necessary. 939 * 940 * @param tabPlacement The JTabbedPane's tab placement. 941 * @param tabCount The number of tabs. 942 */ calculateTabRects(int tabPlacement, int tabCount)943 protected void calculateTabRects(int tabPlacement, int tabCount) 944 { 945 if (tabCount == 0) 946 return; 947 assureRectsCreated(tabCount); 948 949 FontMetrics fm = getFontMetrics(); 950 SwingUtilities.calculateInnerArea(tabPane, calcRect); 951 Insets tabAreaInsets = getTabAreaInsets(tabPlacement); 952 Insets insets = tabPane.getInsets(); 953 int max = 0; 954 int runs = 1; 955 int start = 0; 956 int top = 0; 957 if (tabPlacement == SwingConstants.TOP 958 || tabPlacement == SwingConstants.BOTTOM) 959 { 960 int maxHeight = calculateMaxTabHeight(tabPlacement); 961 calcRect.width -= tabAreaInsets.left + tabAreaInsets.right; 962 max = calcRect.width + tabAreaInsets.left + insets.left; 963 start = tabAreaInsets.left + insets.left; 964 int width = 0; 965 int runWidth = start; 966 top = insets.top + tabAreaInsets.top; 967 for (int i = 0; i < tabCount; i++) 968 { 969 width = calculateTabWidth(tabPlacement, i, fm); 970 971 rects[i] = new Rectangle(runWidth, top, width, maxHeight); 972 runWidth += width; 973 } 974 tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right; 975 tabAreaRect.height = runs * maxTabHeight 976 - (runs - 1) * tabRunOverlay 977 + tabAreaInsets.top + tabAreaInsets.bottom; 978 contentRect.width = tabAreaRect.width; 979 contentRect.height = tabPane.getHeight() - insets.top 980 - insets.bottom - tabAreaRect.height; 981 contentRect.x = insets.left; 982 tabAreaRect.x = insets.left; 983 if (tabPlacement == SwingConstants.BOTTOM) 984 { 985 contentRect.y = insets.top; 986 tabAreaRect.y = contentRect.y + contentRect.height; 987 } 988 else 989 { 990 tabAreaRect.y = insets.top; 991 contentRect.y = tabAreaRect.y + tabAreaRect.height; 992 } 993 } 994 else 995 { 996 int maxWidth = calculateMaxTabWidth(tabPlacement); 997 998 calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom; 999 max = calcRect.height + tabAreaInsets.top; 1000 int height = 0; 1001 start = tabAreaInsets.top + insets.top; 1002 int runHeight = start; 1003 int fontHeight = fm.getHeight(); 1004 top = insets.left + tabAreaInsets.left; 1005 for (int i = 0; i < tabCount; i++) 1006 { 1007 height = calculateTabHeight(tabPlacement, i, fontHeight); 1008 rects[i] = new Rectangle(top, runHeight, maxWidth, height); 1009 runHeight += height; 1010 } 1011 tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay 1012 + tabAreaInsets.left + tabAreaInsets.right; 1013 tabAreaRect.height = tabPane.getHeight() - insets.top 1014 - insets.bottom; 1015 tabAreaRect.y = insets.top; 1016 contentRect.width = tabPane.getWidth() - insets.left - insets.right 1017 - tabAreaRect.width; 1018 contentRect.height = tabAreaRect.height; 1019 contentRect.y = insets.top; 1020 if (tabPlacement == SwingConstants.LEFT) 1021 { 1022 tabAreaRect.x = insets.left; 1023 contentRect.x = tabAreaRect.x + tabAreaRect.width; 1024 } 1025 else 1026 { 1027 contentRect.x = insets.left; 1028 tabAreaRect.x = contentRect.x + contentRect.width; 1029 } 1030 } 1031 runCount = runs; 1032 1033 padSelectedTab(tabPlacement, tabPane.getSelectedIndex()); 1034 } 1035 1036 /** 1037 * This method is called when the JTabbedPane is laid out in 1038 * SCROLL_TAB_LAYOUT. It finds the position for all components in the 1039 * JTabbedPane. 1040 * 1041 * @param pane The JTabbedPane to be laid out. 1042 */ layoutContainer(Container pane)1043 public void layoutContainer(Container pane) 1044 { 1045 super.layoutContainer(pane); 1046 int tabCount = tabPane.getTabCount(); 1047 Point p = null; 1048 if (tabCount == 0) 1049 return; 1050 int tabPlacement = tabPane.getTabPlacement(); 1051 incrButton.hide(); 1052 decrButton.hide(); 1053 if (tabPlacement == SwingConstants.TOP 1054 || tabPlacement == SwingConstants.BOTTOM) 1055 { 1056 if (tabAreaRect.x + tabAreaRect.width < rects[tabCount - 1].x 1057 + rects[tabCount - 1].width) 1058 { 1059 Dimension incrDims = incrButton.getPreferredSize(); 1060 Dimension decrDims = decrButton.getPreferredSize(); 1061 1062 decrButton.setBounds(tabAreaRect.x + tabAreaRect.width 1063 - incrDims.width - decrDims.width, 1064 tabAreaRect.y, decrDims.width, 1065 tabAreaRect.height); 1066 incrButton.setBounds(tabAreaRect.x + tabAreaRect.width 1067 - incrDims.width, tabAreaRect.y, 1068 decrDims.width, tabAreaRect.height); 1069 1070 tabAreaRect.width -= decrDims.width + incrDims.width; 1071 incrButton.show(); 1072 decrButton.show(); 1073 } 1074 } 1075 1076 if (tabPlacement == SwingConstants.LEFT 1077 || tabPlacement == SwingConstants.RIGHT) 1078 { 1079 if (tabAreaRect.y + tabAreaRect.height < rects[tabCount - 1].y 1080 + rects[tabCount - 1].height) 1081 { 1082 Dimension incrDims = incrButton.getPreferredSize(); 1083 Dimension decrDims = decrButton.getPreferredSize(); 1084 1085 decrButton.setBounds(tabAreaRect.x, 1086 tabAreaRect.y + tabAreaRect.height 1087 - incrDims.height - decrDims.height, 1088 tabAreaRect.width, decrDims.height); 1089 incrButton.setBounds(tabAreaRect.x, 1090 tabAreaRect.y + tabAreaRect.height 1091 - incrDims.height, tabAreaRect.width, 1092 incrDims.height); 1093 1094 tabAreaRect.height -= decrDims.height + incrDims.height; 1095 incrButton.show(); 1096 decrButton.show(); 1097 } 1098 } 1099 viewport.setBounds(tabAreaRect.x, tabAreaRect.y, tabAreaRect.width, 1100 tabAreaRect.height); 1101 int tabC = tabPane.getTabCount() - 1; 1102 if (tabCount > 0) 1103 { 1104 int w = Math.max(rects[tabC].width + rects[tabC].x, tabAreaRect.width); 1105 int h = Math.max(rects[tabC].height, tabAreaRect.height); 1106 p = findPointForIndex(currentScrollLocation); 1107 1108 // we want to cover that entire space so that borders that run under 1109 // the tab area don't show up when we move the viewport around. 1110 panel.setSize(w + p.x, h + p.y); 1111 } 1112 viewport.setViewPosition(p); 1113 viewport.repaint(); 1114 } 1115 } 1116 1117 /** 1118 * This class handles ChangeEvents from the JTabbedPane. 1119 * 1120 * @specnote Apparently this class was intended to be protected, 1121 * but was made public by a compiler bug and is now 1122 * public for compatibility. 1123 */ 1124 public class TabSelectionHandler implements ChangeListener 1125 { 1126 /** 1127 * This method is called whenever a ChangeEvent is fired from the 1128 * JTabbedPane. 1129 * 1130 * @param e The ChangeEvent fired. 1131 */ stateChanged(ChangeEvent e)1132 public void stateChanged(ChangeEvent e) 1133 { 1134 selectedRun = getRunForTab(tabPane.getTabCount(), 1135 tabPane.getSelectedIndex()); 1136 tabPane.revalidate(); 1137 tabPane.repaint(); 1138 } 1139 } 1140 1141 /** 1142 * This helper class is a JPanel that fits inside the ScrollViewport. This 1143 * panel's sole job is to paint the tab rectangles inside the viewport so 1144 * that it's clipped correctly. 1145 */ 1146 private class ScrollingPanel extends JPanel 1147 { 1148 /** 1149 * This is a private UI class for our panel. 1150 */ 1151 private class ScrollingPanelUI extends BasicPanelUI 1152 { 1153 /** 1154 * This method overrides the default paint method. It paints the tab 1155 * rectangles for the JTabbedPane in the panel. 1156 * 1157 * @param g The Graphics object to paint with. 1158 * @param c The JComponent to paint. 1159 */ paint(Graphics g, JComponent c)1160 public void paint(Graphics g, JComponent c) 1161 { 1162 paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex()); 1163 } 1164 } 1165 1166 /** 1167 * This method overrides the updateUI method. It makes the default UI for 1168 * this ScrollingPanel to be a ScrollingPanelUI. 1169 */ updateUI()1170 public void updateUI() 1171 { 1172 setUI((PanelUI) new ScrollingPanelUI()); 1173 } 1174 } 1175 1176 /** 1177 * This is a helper class that paints the panel that paints tabs. This 1178 * custom JViewport is used so that the tabs painted in the panel will be 1179 * clipped. This class implements UIResource so tabs are not added when 1180 * this objects of this class are added to the JTabbedPane. 1181 */ 1182 private class ScrollingViewport extends JViewport implements UIResource 1183 { 1184 // TODO: Maybe remove this inner class. 1185 } 1186 1187 /** 1188 * This is a helper class that implements UIResource so it is not added as a 1189 * tab when an object of this class is added to the JTabbedPane. 1190 */ 1191 private class ScrollingButton extends BasicArrowButton implements UIResource 1192 { 1193 /** 1194 * Creates a ScrollingButton given the direction. 1195 * 1196 * @param dir The direction to point in. 1197 */ ScrollingButton(int dir)1198 public ScrollingButton(int dir) 1199 { 1200 super(dir); 1201 } 1202 } 1203 1204 /** The button that increments the current scroll location. 1205 * This is package-private to avoid an accessor method. */ 1206 transient ScrollingButton incrButton; 1207 1208 /** The button that decrements the current scroll location. 1209 * This is package-private to avoid an accessor method. */ 1210 transient ScrollingButton decrButton; 1211 1212 /** The viewport used to display the tabs. 1213 * This is package-private to avoid an accessor method. */ 1214 transient ScrollingViewport viewport; 1215 1216 /** The panel inside the viewport that paints the tabs. 1217 * This is package-private to avoid an accessor method. */ 1218 transient ScrollingPanel panel; 1219 1220 /** The starting visible tab in the run in SCROLL_TAB_MODE. 1221 * This is package-private to avoid an accessor method. */ 1222 transient int currentScrollLocation; 1223 1224 /** A reusable rectangle. */ 1225 protected Rectangle calcRect; 1226 1227 /** An array of Rectangles keeping track of the tabs' area and position. */ 1228 protected Rectangle[] rects; 1229 1230 /** The insets around the content area. */ 1231 protected Insets contentBorderInsets; 1232 1233 /** The extra insets around the selected tab. */ 1234 protected Insets selectedTabPadInsets; 1235 1236 /** The insets around the tab area. */ 1237 protected Insets tabAreaInsets; 1238 1239 /** The insets around each and every tab. */ 1240 protected Insets tabInsets; 1241 1242 /** 1243 * The outer bottom and right edge color for both the tab and content 1244 * border. 1245 */ 1246 protected Color darkShadow; 1247 1248 /** The color of the focus outline on the selected tab. */ 1249 protected Color focus; 1250 1251 /** FIXME: find a use for this. */ 1252 protected Color highlight; 1253 1254 /** The top and left edge color for both the tab and content border. */ 1255 protected Color lightHighlight; 1256 1257 /** The inner bottom and right edge color for the tab and content border. */ 1258 protected Color shadow; 1259 1260 /** The maximum tab height. */ 1261 protected int maxTabHeight; 1262 1263 /** The maximum tab width. */ 1264 protected int maxTabWidth; 1265 1266 /** The number of runs in the JTabbedPane. */ 1267 protected int runCount; 1268 1269 /** The index of the run that the selected index is in. */ 1270 protected int selectedRun; 1271 1272 /** The amount of space each run overlaps the previous by. */ 1273 protected int tabRunOverlay; 1274 1275 /** The gap between text and label */ 1276 protected int textIconGap; 1277 1278 // Keeps track of tab runs. 1279 // The organization of this array is as follows (lots of experimentation to 1280 // figure this out) 1281 // index 0 = furthest away from the component area (aka outer run) 1282 // index 1 = closest to component area (aka selected run) 1283 // index > 1 = listed in order leading from selected run to outer run. 1284 // each int in the array is the tab index + 1 (counting starts at 1) 1285 // for the last tab in the run. (same as the rects array) 1286 1287 /** This array keeps track of which tabs are in which run. See above. */ 1288 protected int[] tabRuns; 1289 1290 /** 1291 * This is the keystroke for moving down. 1292 * 1293 * @deprecated 1.3 1294 */ 1295 protected KeyStroke downKey; 1296 1297 /** 1298 * This is the keystroke for moving left. 1299 * 1300 * @deprecated 1.3 1301 */ 1302 protected KeyStroke leftKey; 1303 1304 /** 1305 * This is the keystroke for moving right. 1306 * 1307 * @deprecated 1.3 1308 */ 1309 protected KeyStroke rightKey; 1310 1311 /** 1312 * This is the keystroke for moving up. 1313 * 1314 * @deprecated 1.3 1315 */ 1316 protected KeyStroke upKey; 1317 1318 /** The listener that listens for focus events. */ 1319 protected FocusListener focusListener; 1320 1321 /** The listener that listens for mouse events. */ 1322 protected MouseListener mouseListener; 1323 1324 /** The listener that listens for property change events. */ 1325 protected PropertyChangeListener propertyChangeListener; 1326 1327 /** The listener that listens for change events. */ 1328 protected ChangeListener tabChangeListener; 1329 1330 /** The tab pane that this UI paints. */ 1331 protected JTabbedPane tabPane; 1332 1333 /** The current layout manager for the tabPane. 1334 * This is package-private to avoid an accessor method. */ 1335 transient LayoutManager layoutManager; 1336 1337 /** The rectangle that describes the tab area's position and size. 1338 * This is package-private to avoid an accessor method. */ 1339 transient Rectangle tabAreaRect; 1340 1341 /** The rectangle that describes the content area's position and 1342 * size. This is package-private to avoid an accessor method. */ 1343 transient Rectangle contentRect; 1344 1345 /** 1346 * Creates a new BasicTabbedPaneUI object. 1347 */ BasicTabbedPaneUI()1348 public BasicTabbedPaneUI() 1349 { 1350 super(); 1351 } 1352 1353 /** 1354 * This method creates a ScrollingButton that points in the appropriate 1355 * direction for an increasing button. 1356 * This is package-private to avoid an accessor method. 1357 * 1358 * @return The increase ScrollingButton. 1359 */ createIncreaseButton()1360 ScrollingButton createIncreaseButton() 1361 { 1362 if (incrButton == null) 1363 incrButton = new ScrollingButton(SwingConstants.NORTH); 1364 if (tabPane.getTabPlacement() == SwingConstants.TOP 1365 || tabPane.getTabPlacement() == SwingConstants.BOTTOM) 1366 incrButton.setDirection(SwingConstants.EAST); 1367 else 1368 incrButton.setDirection(SwingConstants.SOUTH); 1369 return incrButton; 1370 } 1371 1372 /** 1373 * This method creates a ScrollingButton that points in the appropriate 1374 * direction for a decreasing button. 1375 * This is package-private to avoid an accessor method. 1376 * 1377 * @return The decrease ScrollingButton. 1378 */ createDecreaseButton()1379 ScrollingButton createDecreaseButton() 1380 { 1381 if (decrButton == null) 1382 decrButton = new ScrollingButton(SwingConstants.SOUTH); 1383 if (tabPane.getTabPlacement() == SwingConstants.TOP 1384 || tabPane.getTabPlacement() == SwingConstants.BOTTOM) 1385 decrButton.setDirection(SwingConstants.WEST); 1386 else 1387 decrButton.setDirection(SwingConstants.NORTH); 1388 return decrButton; 1389 } 1390 1391 /** 1392 * This method finds the point to set the view position at given the index 1393 * of a tab. The tab will be the first visible tab in the run. 1394 * This is package-private to avoid an accessor method. 1395 * 1396 * @param index The index of the first visible tab. 1397 * 1398 * @return The position of the first visible tab. 1399 */ findPointForIndex(int index)1400 Point findPointForIndex(int index) 1401 { 1402 int tabPlacement = tabPane.getTabPlacement(); 1403 int selectedIndex = tabPane.getSelectedIndex(); 1404 Insets insets = getSelectedTabPadInsets(tabPlacement); 1405 int w = 0; 1406 int h = 0; 1407 1408 if (tabPlacement == TOP || tabPlacement == BOTTOM) 1409 { 1410 if (index > 0) 1411 { 1412 w += rects[index - 1].x + rects[index - 1].width; 1413 if (index > selectedIndex) 1414 w -= insets.left + insets.right; 1415 } 1416 } 1417 1418 else 1419 { 1420 if (index > 0) 1421 { 1422 h += rects[index - 1].y + rects[index - 1].height; 1423 if (index > selectedIndex) 1424 h -= insets.top + insets.bottom; 1425 } 1426 } 1427 1428 Point p = new Point(w, h); 1429 return p; 1430 } 1431 1432 /** 1433 * This method creates a new BasicTabbedPaneUI. 1434 * 1435 * @param c The JComponent to create a UI for. 1436 * 1437 * @return A new BasicTabbedPaneUI. 1438 */ createUI(JComponent c)1439 public static ComponentUI createUI(JComponent c) 1440 { 1441 return new BasicTabbedPaneUI(); 1442 } 1443 1444 /** 1445 * This method installs the UI for the given JComponent. 1446 * 1447 * @param c The JComponent to install the UI for. 1448 */ installUI(JComponent c)1449 public void installUI(JComponent c) 1450 { 1451 super.installUI(c); 1452 if (c instanceof JTabbedPane) 1453 { 1454 tabPane = (JTabbedPane) c; 1455 1456 installComponents(); 1457 installDefaults(); 1458 installListeners(); 1459 installKeyboardActions(); 1460 1461 layoutManager = createLayoutManager(); 1462 tabPane.setLayout(layoutManager); 1463 tabPane.layout(); 1464 } 1465 } 1466 1467 /** 1468 * This method uninstalls the UI for the given JComponent. 1469 * 1470 * @param c The JComponent to uninstall the UI for. 1471 */ uninstallUI(JComponent c)1472 public void uninstallUI(JComponent c) 1473 { 1474 layoutManager = null; 1475 1476 uninstallKeyboardActions(); 1477 uninstallListeners(); 1478 uninstallDefaults(); 1479 uninstallComponents(); 1480 1481 tabPane = null; 1482 } 1483 1484 /** 1485 * This method creates the appropriate layout manager for the JTabbedPane's 1486 * current tab layout policy. If the tab layout policy is 1487 * SCROLL_TAB_LAYOUT, then all the associated components that need to be 1488 * created will be done so now. 1489 * 1490 * @return A layout manager given the tab layout policy. 1491 */ createLayoutManager()1492 protected LayoutManager createLayoutManager() 1493 { 1494 if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) 1495 return new TabbedPaneLayout(); 1496 else 1497 { 1498 incrButton = createIncreaseButton(); 1499 decrButton = createDecreaseButton(); 1500 viewport = new ScrollingViewport(); 1501 viewport.setLayout(null); 1502 panel = new ScrollingPanel(); 1503 viewport.setView(panel); 1504 tabPane.add(incrButton); 1505 tabPane.add(decrButton); 1506 tabPane.add(viewport); 1507 currentScrollLocation = 0; 1508 decrButton.setEnabled(false); 1509 panel.addMouseListener(mouseListener); 1510 incrButton.addMouseListener(mouseListener); 1511 decrButton.addMouseListener(mouseListener); 1512 viewport.setBackground(Color.LIGHT_GRAY); 1513 1514 return new TabbedPaneScrollLayout(); 1515 } 1516 } 1517 1518 /** 1519 * This method installs components for this JTabbedPane. 1520 */ installComponents()1521 protected void installComponents() 1522 { 1523 // Nothing to be done. 1524 } 1525 1526 /** 1527 * This method uninstalls components for this JTabbedPane. 1528 */ uninstallComponents()1529 protected void uninstallComponents() 1530 { 1531 // Nothing to be done. 1532 } 1533 1534 /** 1535 * This method installs defaults for the Look and Feel. 1536 */ installDefaults()1537 protected void installDefaults() 1538 { 1539 LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background", 1540 "TabbedPane.foreground", 1541 "TabbedPane.font"); 1542 tabPane.setOpaque(false); 1543 1544 highlight = UIManager.getColor("TabbedPane.highlight"); 1545 lightHighlight = UIManager.getColor("TabbedPane.lightHighlight"); 1546 1547 shadow = UIManager.getColor("TabbedPane.shadow"); 1548 darkShadow = UIManager.getColor("TabbedPane.darkShadow"); 1549 1550 focus = UIManager.getColor("TabbedPane.focus"); 1551 1552 textIconGap = UIManager.getInt("TabbedPane.textIconGap"); 1553 tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay"); 1554 1555 tabInsets = UIManager.getInsets("TabbedPane.tabbedPaneTabInsets"); 1556 selectedTabPadInsets = UIManager.getInsets("TabbedPane.tabbedPaneTabPadInsets"); 1557 tabAreaInsets = UIManager.getInsets("TabbedPane.tabbedPaneTabAreaInsets"); 1558 contentBorderInsets = UIManager.getInsets("TabbedPane.tabbedPaneContentBorderInsets"); 1559 1560 calcRect = new Rectangle(); 1561 tabRuns = new int[10]; 1562 tabAreaRect = new Rectangle(); 1563 contentRect = new Rectangle(); 1564 } 1565 1566 /** 1567 * This method uninstalls defaults for the Look and Feel. 1568 */ uninstallDefaults()1569 protected void uninstallDefaults() 1570 { 1571 calcRect = null; 1572 tabAreaRect = null; 1573 contentRect = null; 1574 tabRuns = null; 1575 1576 contentBorderInsets = null; 1577 tabAreaInsets = null; 1578 selectedTabPadInsets = null; 1579 tabInsets = null; 1580 1581 focus = null; 1582 darkShadow = null; 1583 shadow = null; 1584 lightHighlight = null; 1585 highlight = null; 1586 1587 tabPane.setBackground(null); 1588 tabPane.setForeground(null); 1589 tabPane.setFont(null); 1590 } 1591 1592 /** 1593 * This method creates and installs the listeners for this UI. 1594 */ installListeners()1595 protected void installListeners() 1596 { 1597 mouseListener = createMouseListener(); 1598 tabChangeListener = createChangeListener(); 1599 propertyChangeListener = createPropertyChangeListener(); 1600 focusListener = createFocusListener(); 1601 1602 tabPane.addMouseListener(mouseListener); 1603 tabPane.addChangeListener(tabChangeListener); 1604 tabPane.addPropertyChangeListener(propertyChangeListener); 1605 tabPane.addFocusListener(focusListener); 1606 } 1607 1608 /** 1609 * This method removes and nulls the listeners for this UI. 1610 */ uninstallListeners()1611 protected void uninstallListeners() 1612 { 1613 tabPane.removeFocusListener(focusListener); 1614 tabPane.removePropertyChangeListener(propertyChangeListener); 1615 tabPane.removeChangeListener(tabChangeListener); 1616 tabPane.removeMouseListener(mouseListener); 1617 1618 focusListener = null; 1619 propertyChangeListener = null; 1620 tabChangeListener = null; 1621 mouseListener = null; 1622 } 1623 1624 /** 1625 * This method creates a new MouseListener. 1626 * 1627 * @return A new MouseListener. 1628 */ createMouseListener()1629 protected MouseListener createMouseListener() 1630 { 1631 return new MouseHandler(); 1632 } 1633 1634 /** 1635 * This method creates a new FocusListener. 1636 * 1637 * @return A new FocusListener. 1638 */ createFocusListener()1639 protected FocusListener createFocusListener() 1640 { 1641 return new FocusHandler(); 1642 } 1643 1644 /** 1645 * This method creates a new ChangeListener. 1646 * 1647 * @return A new ChangeListener. 1648 */ createChangeListener()1649 protected ChangeListener createChangeListener() 1650 { 1651 return new TabSelectionHandler(); 1652 } 1653 1654 /** 1655 * This method creates a new PropertyChangeListener. 1656 * 1657 * @return A new PropertyChangeListener. 1658 */ createPropertyChangeListener()1659 protected PropertyChangeListener createPropertyChangeListener() 1660 { 1661 return new PropertyChangeHandler(); 1662 } 1663 1664 /** 1665 * This method installs keyboard actions for the JTabbedPane. 1666 */ installKeyboardActions()1667 protected void installKeyboardActions() 1668 { 1669 // FIXME: Implement. 1670 } 1671 1672 /** 1673 * This method uninstalls keyboard actions for the JTabbedPane. 1674 */ uninstallKeyboardActions()1675 protected void uninstallKeyboardActions() 1676 { 1677 // FIXME: Implement. 1678 } 1679 1680 /** 1681 * This method returns the minimum size of the JTabbedPane. 1682 * 1683 * @param c The JComponent to find a size for. 1684 * 1685 * @return The minimum size. 1686 */ getMinimumSize(JComponent c)1687 public Dimension getMinimumSize(JComponent c) 1688 { 1689 return layoutManager.minimumLayoutSize(tabPane); 1690 } 1691 1692 /** 1693 * This method returns the maximum size of the JTabbedPane. 1694 * 1695 * @param c The JComponent to find a size for. 1696 * 1697 * @return The maximum size. 1698 */ getMaximumSize(JComponent c)1699 public Dimension getMaximumSize(JComponent c) 1700 { 1701 return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE); 1702 } 1703 1704 /** 1705 * This method paints the JTabbedPane. 1706 * 1707 * @param g The Graphics object to paint with. 1708 * @param c The JComponent to paint. 1709 */ paint(Graphics g, JComponent c)1710 public void paint(Graphics g, JComponent c) 1711 { 1712 if (tabPane.getTabCount() == 0) 1713 return; 1714 if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) 1715 paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex()); 1716 paintContentBorder(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex()); 1717 } 1718 1719 /** 1720 * This method paints the tab area. This includes painting the rectangles 1721 * that make up the tabs. 1722 * 1723 * @param g The Graphics object to paint with. 1724 * @param tabPlacement The JTabbedPane's tab placement. 1725 * @param selectedIndex The selected index. 1726 */ paintTabArea(Graphics g, int tabPlacement, int selectedIndex)1727 protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) 1728 { 1729 Rectangle ir = new Rectangle(); 1730 Rectangle tr = new Rectangle(); 1731 1732 boolean isScroll = tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT; 1733 1734 // Please note: the ordering of the painting is important. 1735 // we WANT to paint the outermost run first and then work our way in. 1736 int tabCount = tabPane.getTabCount(); 1737 int currRun = 1; 1738 1739 if (tabCount > runCount) 1740 runCount = tabCount; 1741 1742 if (tabCount < 1) 1743 return; 1744 1745 if (runCount > 1) 1746 currRun = 0; 1747 for (int i = 0; i < runCount; i++) 1748 { 1749 int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1; 1750 if (isScroll) 1751 first = currentScrollLocation; 1752 else if (first == tabCount) 1753 first = 0; 1754 int last = lastTabInRun(tabCount, currRun); 1755 if (isScroll) 1756 { 1757 for (int k = first; k < tabCount; k++) 1758 { 1759 if (rects[k].x + rects[k].width - rects[first].x > viewport 1760 .getWidth()) 1761 { 1762 last = k; 1763 break; 1764 } 1765 } 1766 } 1767 1768 for (int j = first; j <= last; j++) 1769 { 1770 if (j != selectedIndex || isScroll) 1771 paintTab(g, tabPlacement, rects, j, ir, tr); 1772 } 1773 currRun = getPreviousTabRun(currRun); 1774 } 1775 if (! isScroll) 1776 paintTab(g, tabPlacement, rects, selectedIndex, ir, tr); 1777 } 1778 1779 /** 1780 * This method paints an individual tab. 1781 * 1782 * @param g The Graphics object to paint with. 1783 * @param tabPlacement The JTabbedPane's tab placement. 1784 * @param rects The array of rectangles that keep the size and position of 1785 * the tabs. 1786 * @param tabIndex The tab index to paint. 1787 * @param iconRect The rectangle to use for the icon. 1788 * @param textRect The rectangle to use for the text. 1789 */ paintTab(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect)1790 protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects, 1791 int tabIndex, Rectangle iconRect, Rectangle textRect) 1792 { 1793 FontMetrics fm = getFontMetrics(); 1794 Icon icon = getIconForTab(tabIndex); 1795 String title = tabPane.getTitleAt(tabIndex); 1796 boolean isSelected = tabIndex == tabPane.getSelectedIndex(); 1797 calcRect = getTabBounds(tabPane, tabIndex); 1798 1799 int x = calcRect.x; 1800 int y = calcRect.y; 1801 int w = calcRect.width; 1802 int h = calcRect.height; 1803 if (getRunForTab(tabPane.getTabCount(), tabIndex) == 1) 1804 { 1805 Insets insets = getTabAreaInsets(tabPlacement); 1806 switch (tabPlacement) 1807 { 1808 case TOP: 1809 h += insets.bottom; 1810 break; 1811 case LEFT: 1812 w += insets.right; 1813 break; 1814 case BOTTOM: 1815 y -= insets.top; 1816 h += insets.top; 1817 break; 1818 case RIGHT: 1819 x -= insets.left; 1820 w += insets.left; 1821 break; 1822 } 1823 } 1824 1825 layoutLabel(tabPlacement, fm, tabIndex, title, icon, calcRect, iconRect, 1826 textRect, isSelected); 1827 paintTabBackground(g, tabPlacement, tabIndex, x, y, w, h, isSelected); 1828 paintTabBorder(g, tabPlacement, tabIndex, x, y, w, h, isSelected); 1829 1830 // FIXME: Paint little folding corner and jagged edge clipped tab. 1831 if (icon != null) 1832 paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected); 1833 if (title != null && ! title.equals("")) 1834 paintText(g, tabPlacement, tabPane.getFont(), fm, tabIndex, title, 1835 textRect, isSelected); 1836 } 1837 1838 /** 1839 * This method lays out the tab and finds the location to paint the icon 1840 * and text. 1841 * 1842 * @param tabPlacement The JTabbedPane's tab placement. 1843 * @param metrics The font metrics for the font to paint with. 1844 * @param tabIndex The tab index to paint. 1845 * @param title The string painted. 1846 * @param icon The icon painted. 1847 * @param tabRect The tab bounds. 1848 * @param iconRect The calculated icon bounds. 1849 * @param textRect The calculated text bounds. 1850 * @param isSelected Whether this tab is selected. 1851 */ layoutLabel(int tabPlacement, FontMetrics metrics, int tabIndex, String title, Icon icon, Rectangle tabRect, Rectangle iconRect, Rectangle textRect, boolean isSelected)1852 protected void layoutLabel(int tabPlacement, FontMetrics metrics, 1853 int tabIndex, String title, Icon icon, 1854 Rectangle tabRect, Rectangle iconRect, 1855 Rectangle textRect, boolean isSelected) 1856 { 1857 SwingUtilities.layoutCompoundLabel(metrics, title, icon, 1858 SwingConstants.CENTER, 1859 SwingConstants.CENTER, 1860 SwingConstants.CENTER, 1861 SwingConstants.RIGHT, tabRect, 1862 iconRect, textRect, textIconGap); 1863 1864 int shiftX = getTabLabelShiftX(tabPlacement, tabIndex, isSelected); 1865 int shiftY = getTabLabelShiftY(tabPlacement, tabIndex, isSelected); 1866 1867 iconRect.x += shiftX; 1868 iconRect.y += shiftY; 1869 1870 textRect.x += shiftX; 1871 textRect.y += shiftY; 1872 } 1873 1874 /** 1875 * This method paints the icon. 1876 * 1877 * @param g The Graphics object to paint. 1878 * @param tabPlacement The JTabbedPane's tab placement. 1879 * @param tabIndex The tab index to paint. 1880 * @param icon The icon to paint. 1881 * @param iconRect The bounds of the icon. 1882 * @param isSelected Whether this tab is selected. 1883 */ paintIcon(Graphics g, int tabPlacement, int tabIndex, Icon icon, Rectangle iconRect, boolean isSelected)1884 protected void paintIcon(Graphics g, int tabPlacement, int tabIndex, 1885 Icon icon, Rectangle iconRect, boolean isSelected) 1886 { 1887 icon.paintIcon(tabPane, g, iconRect.x, iconRect.y); 1888 } 1889 1890 /** 1891 * This method paints the text for the given tab. 1892 * 1893 * @param g The Graphics object to paint with. 1894 * @param tabPlacement The JTabbedPane's tab placement. 1895 * @param font The font to paint with. 1896 * @param metrics The fontmetrics of the given font. 1897 * @param tabIndex The tab index. 1898 * @param title The string to paint. 1899 * @param textRect The bounds of the string. 1900 * @param isSelected Whether this tab is selected. 1901 */ paintText(Graphics g, int tabPlacement, Font font, FontMetrics metrics, int tabIndex, String title, Rectangle textRect, boolean isSelected)1902 protected void paintText(Graphics g, int tabPlacement, Font font, 1903 FontMetrics metrics, int tabIndex, String title, 1904 Rectangle textRect, boolean isSelected) 1905 { 1906 View textView = getTextViewForTab(tabIndex); 1907 if (textView != null) 1908 { 1909 textView.paint(g, textRect); 1910 return; 1911 } 1912 1913 Color fg = tabPane.getForegroundAt(tabIndex); 1914 if (fg == null) 1915 fg = tabPane.getForeground(); 1916 Color bg = tabPane.getBackgroundAt(tabIndex); 1917 if (bg == null) 1918 bg = tabPane.getBackground(); 1919 1920 Color saved_color = g.getColor(); 1921 Font f = g.getFont(); 1922 g.setFont(font); 1923 1924 if (tabPane.isEnabledAt(tabIndex)) 1925 { 1926 g.setColor(fg); 1927 1928 int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex); 1929 1930 if (mnemIndex != -1) 1931 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, 1932 textRect.x, 1933 textRect.y 1934 + metrics.getAscent()); 1935 else 1936 g.drawString(title, textRect.x, textRect.y + metrics.getAscent()); 1937 } 1938 else 1939 { 1940 g.setColor(bg.brighter()); 1941 1942 int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex); 1943 1944 if (mnemIndex != -1) 1945 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, 1946 textRect.x, textRect.y); 1947 else 1948 g.drawString(title, textRect.x, textRect.y); 1949 1950 g.setColor(bg.darker()); 1951 if (mnemIndex != -1) 1952 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, 1953 textRect.x + 1, 1954 textRect.y + 1); 1955 else 1956 g.drawString(title, textRect.x + 1, textRect.y + 1); 1957 } 1958 1959 g.setColor(saved_color); 1960 g.setFont(f); 1961 } 1962 1963 /** 1964 * This method returns how much the label for the tab should shift in the X 1965 * direction. 1966 * 1967 * @param tabPlacement The JTabbedPane's tab placement. 1968 * @param tabIndex The tab index being painted. 1969 * @param isSelected Whether this tab is selected. 1970 * 1971 * @return The amount the label should shift by in the X direction. 1972 */ getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected)1973 protected int getTabLabelShiftX(int tabPlacement, int tabIndex, 1974 boolean isSelected) 1975 { 1976 // No reason to shift. 1977 return 0; 1978 } 1979 1980 /** 1981 * This method returns how much the label for the tab should shift in the Y 1982 * direction. 1983 * 1984 * @param tabPlacement The JTabbedPane's tab placement. 1985 * @param tabIndex The tab index being painted. 1986 * @param isSelected Whether this tab is selected. 1987 * 1988 * @return The amount the label should shift by in the Y direction. 1989 */ getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected)1990 protected int getTabLabelShiftY(int tabPlacement, int tabIndex, 1991 boolean isSelected) 1992 { 1993 // No reason to shift. 1994 return 0; 1995 } 1996 1997 /** 1998 * This method paints the focus rectangle around the selected tab. 1999 * 2000 * @param g The Graphics object to paint with. 2001 * @param tabPlacement The JTabbedPane's tab placement. 2002 * @param rects The array of rectangles keeping track of size and position. 2003 * @param tabIndex The tab index. 2004 * @param iconRect The icon bounds. 2005 * @param textRect The text bounds. 2006 * @param isSelected Whether this tab is selected. 2007 */ paintFocusIndicator(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect, boolean isSelected)2008 protected void paintFocusIndicator(Graphics g, int tabPlacement, 2009 Rectangle[] rects, int tabIndex, 2010 Rectangle iconRect, Rectangle textRect, 2011 boolean isSelected) 2012 { 2013 Color saved = g.getColor(); 2014 calcRect = iconRect.union(textRect); 2015 2016 g.setColor(focus); 2017 2018 g.drawRect(calcRect.x, calcRect.y, calcRect.width, calcRect.height); 2019 2020 g.setColor(saved); 2021 } 2022 2023 /** 2024 * This method paints the border for an individual tab. 2025 * 2026 * @param g The Graphics object to paint with. 2027 * @param tabPlacement The JTabbedPane's tab placement. 2028 * @param tabIndex The tab index. 2029 * @param x The x position of the tab. 2030 * @param y The y position of the tab. 2031 * @param w The width of the tab. 2032 * @param h The height of the tab. 2033 * @param isSelected Whether the tab is selected. 2034 */ paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected)2035 protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, 2036 int x, int y, int w, int h, boolean isSelected) 2037 { 2038 Color saved = g.getColor(); 2039 2040 if (! isSelected || tabPlacement != SwingConstants.TOP) 2041 { 2042 g.setColor(shadow); 2043 g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); 2044 g.setColor(darkShadow); 2045 g.drawLine(x, y + h, x + w, y + h); 2046 } 2047 2048 if (! isSelected || tabPlacement != SwingConstants.LEFT) 2049 { 2050 g.setColor(darkShadow); 2051 g.drawLine(x + w, y, x + w, y + h); 2052 g.setColor(shadow); 2053 g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); 2054 } 2055 2056 if (! isSelected || tabPlacement != SwingConstants.RIGHT) 2057 { 2058 g.setColor(lightHighlight); 2059 g.drawLine(x, y, x, y + h); 2060 } 2061 2062 if (! isSelected || tabPlacement != SwingConstants.BOTTOM) 2063 { 2064 g.setColor(lightHighlight); 2065 g.drawLine(x, y, x + w, y); 2066 } 2067 2068 g.setColor(saved); 2069 } 2070 2071 /** 2072 * This method paints the background for an individual tab. 2073 * 2074 * @param g The Graphics object to paint with. 2075 * @param tabPlacement The JTabbedPane's tab placement. 2076 * @param tabIndex The tab index. 2077 * @param x The x position of the tab. 2078 * @param y The y position of the tab. 2079 * @param w The width of the tab. 2080 * @param h The height of the tab. 2081 * @param isSelected Whether the tab is selected. 2082 */ paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected)2083 protected void paintTabBackground(Graphics g, int tabPlacement, 2084 int tabIndex, int x, int y, int w, int h, 2085 boolean isSelected) 2086 { 2087 Color saved = g.getColor(); 2088 if (isSelected) 2089 g.setColor(Color.LIGHT_GRAY); 2090 else 2091 { 2092 Color bg = tabPane.getBackgroundAt(tabIndex); 2093 if (bg == null) 2094 bg = Color.GRAY; 2095 g.setColor(bg); 2096 } 2097 2098 g.fillRect(x, y, w, h); 2099 2100 g.setColor(saved); 2101 } 2102 2103 /** 2104 * This method paints the border around the content area. 2105 * 2106 * @param g The Graphics object to paint with. 2107 * @param tabPlacement The JTabbedPane's tab placement. 2108 * @param selectedIndex The index of the selected tab. 2109 */ paintContentBorder(Graphics g, int tabPlacement, int selectedIndex)2110 protected void paintContentBorder(Graphics g, int tabPlacement, 2111 int selectedIndex) 2112 { 2113 Insets insets = getContentBorderInsets(tabPlacement); 2114 int x = contentRect.x; 2115 int y = contentRect.y; 2116 int w = contentRect.width; 2117 int h = contentRect.height; 2118 paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h); 2119 paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h); 2120 paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h); 2121 paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h); 2122 } 2123 2124 /** 2125 * This method paints the top edge of the content border. 2126 * 2127 * @param g The Graphics object to paint with. 2128 * @param tabPlacement The JTabbedPane's tab placement. 2129 * @param selectedIndex The selected tab index. 2130 * @param x The x coordinate for the content area. 2131 * @param y The y coordinate for the content area. 2132 * @param w The width of the content area. 2133 * @param h The height of the content area. 2134 */ paintContentBorderTopEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h)2135 protected void paintContentBorderTopEdge(Graphics g, int tabPlacement, 2136 int selectedIndex, int x, int y, 2137 int w, int h) 2138 { 2139 Color saved = g.getColor(); 2140 g.setColor(lightHighlight); 2141 2142 int startgap = rects[selectedIndex].x; 2143 int endgap = rects[selectedIndex].x + rects[selectedIndex].width; 2144 2145 int diff = 0; 2146 2147 if (tabPlacement == SwingConstants.TOP) 2148 { 2149 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) 2150 { 2151 Point p = findPointForIndex(currentScrollLocation); 2152 diff = p.x; 2153 } 2154 2155 g.drawLine(x, y, startgap - diff, y); 2156 g.drawLine(endgap - diff, y, x + w, y); 2157 } 2158 else 2159 g.drawLine(x, y, x + w, y); 2160 2161 g.setColor(saved); 2162 } 2163 2164 /** 2165 * This method paints the left edge of the content border. 2166 * 2167 * @param g The Graphics object to paint with. 2168 * @param tabPlacement The JTabbedPane's tab placement. 2169 * @param selectedIndex The selected tab index. 2170 * @param x The x coordinate for the content area. 2171 * @param y The y coordinate for the content area. 2172 * @param w The width of the content area. 2173 * @param h The height of the content area. 2174 */ paintContentBorderLeftEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h)2175 protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement, 2176 int selectedIndex, int x, int y, 2177 int w, int h) 2178 { 2179 Color saved = g.getColor(); 2180 g.setColor(lightHighlight); 2181 2182 int startgap = rects[selectedIndex].y; 2183 int endgap = rects[selectedIndex].y + rects[selectedIndex].height; 2184 2185 int diff = 0; 2186 2187 if (tabPlacement == SwingConstants.LEFT) 2188 { 2189 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) 2190 { 2191 Point p = findPointForIndex(currentScrollLocation); 2192 diff = p.y; 2193 } 2194 2195 g.drawLine(x, y, x, startgap - diff); 2196 g.drawLine(x, endgap - diff, x, y + h); 2197 } 2198 else 2199 g.drawLine(x, y, x, y + h); 2200 2201 g.setColor(saved); 2202 } 2203 2204 /** 2205 * This method paints the bottom edge of the content border. 2206 * 2207 * @param g The Graphics object to paint with. 2208 * @param tabPlacement The JTabbedPane's tab placement. 2209 * @param selectedIndex The selected tab index. 2210 * @param x The x coordinate for the content area. 2211 * @param y The y coordinate for the content area. 2212 * @param w The width of the content area. 2213 * @param h The height of the content area. 2214 */ paintContentBorderBottomEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h)2215 protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement, 2216 int selectedIndex, int x, int y, 2217 int w, int h) 2218 { 2219 Color saved = g.getColor(); 2220 2221 int startgap = rects[selectedIndex].x; 2222 int endgap = rects[selectedIndex].x + rects[selectedIndex].width; 2223 2224 int diff = 0; 2225 2226 if (tabPlacement == SwingConstants.BOTTOM) 2227 { 2228 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) 2229 { 2230 Point p = findPointForIndex(currentScrollLocation); 2231 diff = p.x; 2232 } 2233 2234 g.setColor(shadow); 2235 g.drawLine(x + 1, y + h - 1, startgap - diff, y + h - 1); 2236 g.drawLine(endgap - diff, y + h - 1, x + w - 1, y + h - 1); 2237 2238 g.setColor(darkShadow); 2239 g.drawLine(x, y + h, startgap - diff, y + h); 2240 g.drawLine(endgap - diff, y + h, x + w, y + h); 2241 } 2242 else 2243 { 2244 g.setColor(shadow); 2245 g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); 2246 g.setColor(darkShadow); 2247 g.drawLine(x, y + h, x + w, y + h); 2248 } 2249 2250 g.setColor(saved); 2251 } 2252 2253 /** 2254 * This method paints the right edge of the content border. 2255 * 2256 * @param g The Graphics object to paint with. 2257 * @param tabPlacement The JTabbedPane's tab placement. 2258 * @param selectedIndex The selected tab index. 2259 * @param x The x coordinate for the content area. 2260 * @param y The y coordinate for the content area. 2261 * @param w The width of the content area. 2262 * @param h The height of the content area. 2263 */ paintContentBorderRightEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h)2264 protected void paintContentBorderRightEdge(Graphics g, int tabPlacement, 2265 int selectedIndex, int x, int y, 2266 int w, int h) 2267 { 2268 Color saved = g.getColor(); 2269 int startgap = rects[selectedIndex].y; 2270 int endgap = rects[selectedIndex].y + rects[selectedIndex].height; 2271 2272 int diff = 0; 2273 2274 if (tabPlacement == SwingConstants.RIGHT) 2275 { 2276 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) 2277 { 2278 Point p = findPointForIndex(currentScrollLocation); 2279 diff = p.y; 2280 } 2281 2282 g.setColor(shadow); 2283 g.drawLine(x + w - 1, y + 1, x + w - 1, startgap - diff); 2284 g.drawLine(x + w - 1, endgap - diff, x + w - 1, y + h - 1); 2285 2286 g.setColor(darkShadow); 2287 g.drawLine(x + w, y, x + w, startgap - diff); 2288 g.drawLine(x + w, endgap - diff, x + w, y + h); 2289 } 2290 else 2291 { 2292 g.setColor(shadow); 2293 g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); 2294 g.setColor(darkShadow); 2295 g.drawLine(x + w, y, x + w, y + h); 2296 } 2297 2298 g.setColor(saved); 2299 } 2300 2301 /** 2302 * This method returns the tab bounds for the given index. 2303 * 2304 * @param pane The JTabbedPane. 2305 * @param i The index to look for. 2306 * 2307 * @return The bounds of the tab with the given index. 2308 */ getTabBounds(JTabbedPane pane, int i)2309 public Rectangle getTabBounds(JTabbedPane pane, int i) 2310 { 2311 return rects[i]; 2312 } 2313 2314 /** 2315 * This method returns the number of runs. 2316 * 2317 * @param pane The JTabbedPane. 2318 * 2319 * @return The number of runs. 2320 */ getTabRunCount(JTabbedPane pane)2321 public int getTabRunCount(JTabbedPane pane) 2322 { 2323 return runCount; 2324 } 2325 2326 /** 2327 * This method returns the tab index given a coordinate. 2328 * 2329 * @param pane The JTabbedPane. 2330 * @param x The x coordinate. 2331 * @param y The y coordinate. 2332 * 2333 * @return The tab index that the coordinate lands in. 2334 */ tabForCoordinate(JTabbedPane pane, int x, int y)2335 public int tabForCoordinate(JTabbedPane pane, int x, int y) 2336 { 2337 Point p = new Point(x, y); 2338 int tabCount = tabPane.getTabCount(); 2339 int currRun = 1; 2340 for (int i = 0; i < runCount; i++) 2341 { 2342 int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1; 2343 if (first == tabCount) 2344 first = 0; 2345 int last = lastTabInRun(tabCount, currRun); 2346 for (int j = first; j <= last; j++) 2347 { 2348 if (getTabBounds(pane, j).contains(p)) 2349 return j; 2350 } 2351 currRun = getNextTabRun(currRun); 2352 } 2353 return -1; 2354 } 2355 2356 /** 2357 * This method returns the tab bounds in the given rectangle. 2358 * 2359 * @param tabIndex The index to get bounds for. 2360 * @param dest The rectangle to store bounds in. 2361 * 2362 * @return The rectangle passed in. 2363 */ getTabBounds(int tabIndex, Rectangle dest)2364 protected Rectangle getTabBounds(int tabIndex, Rectangle dest) 2365 { 2366 dest.setBounds(getTabBounds(tabPane, tabIndex)); 2367 return dest; 2368 } 2369 2370 /** 2371 * This method returns the component that is shown in the content area. 2372 * 2373 * @return The component that is shown in the content area. 2374 */ getVisibleComponent()2375 protected Component getVisibleComponent() 2376 { 2377 return tabPane.getComponentAt(tabPane.getSelectedIndex()); 2378 } 2379 2380 /** 2381 * This method sets the visible component. 2382 * 2383 * @param component The component to be set visible. 2384 */ setVisibleComponent(Component component)2385 protected void setVisibleComponent(Component component) 2386 { 2387 component.setVisible(true); 2388 tabPane.setSelectedComponent(component); 2389 } 2390 2391 /** 2392 * This method assures that enough rectangles are created given the 2393 * tabCount. The old array is copied to the new one. 2394 * 2395 * @param tabCount The number of tabs. 2396 */ assureRectsCreated(int tabCount)2397 protected void assureRectsCreated(int tabCount) 2398 { 2399 if (rects == null) 2400 rects = new Rectangle[tabCount]; 2401 if (tabCount == rects.length) 2402 return; 2403 else 2404 { 2405 int numToCopy = Math.min(tabCount, rects.length); 2406 Rectangle[] tmp = new Rectangle[tabCount]; 2407 System.arraycopy(rects, 0, tmp, 0, numToCopy); 2408 rects = tmp; 2409 } 2410 } 2411 2412 /** 2413 * This method expands the tabRuns array to give it more room. The old array 2414 * is copied to the new one. 2415 */ expandTabRunsArray()2416 protected void expandTabRunsArray() 2417 { 2418 // This method adds another 10 index positions to the tabRuns array. 2419 if (tabRuns == null) 2420 tabRuns = new int[10]; 2421 else 2422 { 2423 int[] newRuns = new int[tabRuns.length + 10]; 2424 System.arraycopy(tabRuns, 0, newRuns, 0, tabRuns.length); 2425 tabRuns = newRuns; 2426 } 2427 } 2428 2429 /** 2430 * This method returns which run a particular tab belongs to. 2431 * 2432 * @param tabCount The number of tabs. 2433 * @param tabIndex The tab to find. 2434 * 2435 * @return The tabRuns index that it belongs to. 2436 */ getRunForTab(int tabCount, int tabIndex)2437 protected int getRunForTab(int tabCount, int tabIndex) 2438 { 2439 if (runCount == 1 && tabIndex < tabCount && tabIndex >= 0) 2440 return 1; 2441 for (int i = 0; i < runCount; i++) 2442 { 2443 int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1; 2444 if (first == tabCount) 2445 first = 0; 2446 int last = lastTabInRun(tabCount, i); 2447 if (last >= tabIndex && first <= tabIndex) 2448 return i; 2449 } 2450 return -1; 2451 } 2452 2453 /** 2454 * This method returns the index of the last tab in a run. 2455 * 2456 * @param tabCount The number of tabs. 2457 * @param run The run to check. 2458 * 2459 * @return The last tab in the given run. 2460 */ lastTabInRun(int tabCount, int run)2461 protected int lastTabInRun(int tabCount, int run) 2462 { 2463 if (tabRuns[run] == 0) 2464 return tabCount - 1; 2465 else 2466 return tabRuns[run] - 1; 2467 } 2468 2469 /** 2470 * This method returns the tab run overlay. 2471 * 2472 * @param tabPlacement The JTabbedPane's tab placement. 2473 * 2474 * @return The tab run overlay. 2475 */ getTabRunOverlay(int tabPlacement)2476 protected int getTabRunOverlay(int tabPlacement) 2477 { 2478 return tabRunOverlay; 2479 } 2480 2481 /** 2482 * This method returns the tab run indent. It is used in WRAP_TAB_LAYOUT and 2483 * makes each tab run start indented by a certain amount. 2484 * 2485 * @param tabPlacement The JTabbedPane's tab placement. 2486 * @param run The run to get indent for. 2487 * 2488 * @return The amount a run should be indented. 2489 */ getTabRunIndent(int tabPlacement, int run)2490 protected int getTabRunIndent(int tabPlacement, int run) 2491 { 2492 return 0; 2493 } 2494 2495 /** 2496 * This method returns whether a tab run should be padded. 2497 * 2498 * @param tabPlacement The JTabbedPane's tab placement. 2499 * @param run The run to check. 2500 * 2501 * @return Whether the given run should be padded. 2502 */ shouldPadTabRun(int tabPlacement, int run)2503 protected boolean shouldPadTabRun(int tabPlacement, int run) 2504 { 2505 return true; 2506 } 2507 2508 /** 2509 * This method returns whether the tab runs should be rotated. 2510 * 2511 * @param tabPlacement The JTabbedPane's tab placement. 2512 * 2513 * @return Whether runs should be rotated. 2514 */ shouldRotateTabRuns(int tabPlacement)2515 protected boolean shouldRotateTabRuns(int tabPlacement) 2516 { 2517 return true; 2518 } 2519 2520 /** 2521 * This method returns an icon for the tab. If the tab is disabled, it 2522 * should return the disabledIcon. If it is enabled, then it should return 2523 * the default icon. 2524 * 2525 * @param tabIndex The tab index to get an icon for. 2526 * 2527 * @return The icon for the tab index. 2528 */ getIconForTab(int tabIndex)2529 protected Icon getIconForTab(int tabIndex) 2530 { 2531 if (tabPane.isEnabledAt(tabIndex)) 2532 return tabPane.getIconAt(tabIndex); 2533 else 2534 return tabPane.getDisabledIconAt(tabIndex); 2535 } 2536 2537 /** 2538 * This method returns a view that can paint the text for the label. 2539 * 2540 * @param tabIndex The tab index to get a view for. 2541 * 2542 * @return The view for the tab index. 2543 */ getTextViewForTab(int tabIndex)2544 protected View getTextViewForTab(int tabIndex) 2545 { 2546 return null; 2547 } 2548 2549 /** 2550 * This method returns the tab height, including insets, for the given index 2551 * and fontheight. 2552 * 2553 * @param tabPlacement The JTabbedPane's tab placement. 2554 * @param tabIndex The index of the tab to calculate. 2555 * @param fontHeight The font height. 2556 * 2557 * @return This tab's height. 2558 */ calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight)2559 protected int calculateTabHeight(int tabPlacement, int tabIndex, 2560 int fontHeight) 2561 { 2562 Icon icon = getIconForTab(tabIndex); 2563 Insets insets = getTabInsets(tabPlacement, tabIndex); 2564 2565 int height = 0; 2566 if (icon != null) 2567 { 2568 Rectangle vr = new Rectangle(); 2569 Rectangle ir = new Rectangle(); 2570 Rectangle tr = new Rectangle(); 2571 layoutLabel(tabPlacement, getFontMetrics(), tabIndex, 2572 tabPane.getTitleAt(tabIndex), icon, vr, ir, tr, 2573 tabIndex == tabPane.getSelectedIndex()); 2574 height = tr.union(ir).height; 2575 } 2576 else 2577 height = fontHeight; 2578 2579 height += insets.top + insets.bottom; 2580 return height; 2581 } 2582 2583 /** 2584 * This method returns the max tab height. 2585 * 2586 * @param tabPlacement The JTabbedPane's tab placement. 2587 * 2588 * @return The maximum tab height. 2589 */ calculateMaxTabHeight(int tabPlacement)2590 protected int calculateMaxTabHeight(int tabPlacement) 2591 { 2592 maxTabHeight = 0; 2593 2594 FontMetrics fm = getFontMetrics(); 2595 int fontHeight = fm.getHeight(); 2596 2597 for (int i = 0; i < tabPane.getTabCount(); i++) 2598 maxTabHeight = Math.max(calculateTabHeight(tabPlacement, i, fontHeight), 2599 maxTabHeight); 2600 2601 return maxTabHeight; 2602 } 2603 2604 /** 2605 * This method calculates the tab width, including insets, for the given tab 2606 * index and font metrics. 2607 * 2608 * @param tabPlacement The JTabbedPane's tab placement. 2609 * @param tabIndex The tab index to calculate for. 2610 * @param metrics The font's metrics. 2611 * 2612 * @return The tab width for the given index. 2613 */ calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics)2614 protected int calculateTabWidth(int tabPlacement, int tabIndex, 2615 FontMetrics metrics) 2616 { 2617 Icon icon = getIconForTab(tabIndex); 2618 Insets insets = getTabInsets(tabPlacement, tabIndex); 2619 2620 int width = 0; 2621 if (icon != null) 2622 { 2623 Rectangle vr = new Rectangle(); 2624 Rectangle ir = new Rectangle(); 2625 Rectangle tr = new Rectangle(); 2626 layoutLabel(tabPlacement, getFontMetrics(), tabIndex, 2627 tabPane.getTitleAt(tabIndex), icon, vr, ir, tr, 2628 tabIndex == tabPane.getSelectedIndex()); 2629 width = tr.union(ir).width; 2630 } 2631 else 2632 width = metrics.stringWidth(tabPane.getTitleAt(tabIndex)); 2633 2634 width += insets.left + insets.right; 2635 return width; 2636 } 2637 2638 /** 2639 * This method calculates the max tab width. 2640 * 2641 * @param tabPlacement The JTabbedPane's tab placement. 2642 * 2643 * @return The maximum tab width. 2644 */ calculateMaxTabWidth(int tabPlacement)2645 protected int calculateMaxTabWidth(int tabPlacement) 2646 { 2647 maxTabWidth = 0; 2648 2649 FontMetrics fm = getFontMetrics(); 2650 2651 for (int i = 0; i < tabPane.getTabCount(); i++) 2652 maxTabWidth = Math.max(calculateTabWidth(tabPlacement, i, fm), 2653 maxTabWidth); 2654 2655 return maxTabWidth; 2656 } 2657 2658 /** 2659 * This method calculates the tab area height, including insets, for the 2660 * given amount of runs and tab height. 2661 * 2662 * @param tabPlacement The JTabbedPane's tab placement. 2663 * @param horizRunCount The number of runs. 2664 * @param maxTabHeight The max tab height. 2665 * 2666 * @return The tab area height. 2667 */ calculateTabAreaHeight(int tabPlacement, int horizRunCount, int maxTabHeight)2668 protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount, 2669 int maxTabHeight) 2670 { 2671 Insets insets = getTabAreaInsets(tabPlacement); 2672 int tabAreaHeight = horizRunCount * maxTabHeight 2673 - (horizRunCount - 1) * tabRunOverlay; 2674 2675 tabAreaHeight += insets.top + insets.bottom; 2676 2677 return tabAreaHeight; 2678 } 2679 2680 /** 2681 * This method calculates the tab area width, including insets, for the 2682 * given amount of runs and tab width. 2683 * 2684 * @param tabPlacement The JTabbedPane's tab placement. 2685 * @param vertRunCount The number of runs. 2686 * @param maxTabWidth The max tab width. 2687 * 2688 * @return The tab area width. 2689 */ calculateTabAreaWidth(int tabPlacement, int vertRunCount, int maxTabWidth)2690 protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount, 2691 int maxTabWidth) 2692 { 2693 Insets insets = getTabAreaInsets(tabPlacement); 2694 int tabAreaWidth = vertRunCount * maxTabWidth 2695 - (vertRunCount - 1) * tabRunOverlay; 2696 2697 tabAreaWidth += insets.left + insets.right; 2698 2699 return tabAreaWidth; 2700 } 2701 2702 /** 2703 * This method returns the tab insets appropriately rotated. 2704 * 2705 * @param tabPlacement The JTabbedPane's tab placement. 2706 * @param tabIndex The tab index. 2707 * 2708 * @return The tab insets for the given index. 2709 */ getTabInsets(int tabPlacement, int tabIndex)2710 protected Insets getTabInsets(int tabPlacement, int tabIndex) 2711 { 2712 Insets target = new Insets(0, 0, 0, 0); 2713 rotateInsets(tabInsets, target, tabPlacement); 2714 return target; 2715 } 2716 2717 /** 2718 * This method returns the selected tab pad insets appropriately rotated. 2719 * 2720 * @param tabPlacement The JTabbedPane's tab placement. 2721 * 2722 * @return The selected tab pad insets. 2723 */ getSelectedTabPadInsets(int tabPlacement)2724 protected Insets getSelectedTabPadInsets(int tabPlacement) 2725 { 2726 Insets target = new Insets(0, 0, 0, 0); 2727 rotateInsets(selectedTabPadInsets, target, tabPlacement); 2728 return target; 2729 } 2730 2731 /** 2732 * This method returns the tab area insets appropriately rotated. 2733 * 2734 * @param tabPlacement The JTabbedPane's tab placement. 2735 * 2736 * @return The tab area insets. 2737 */ getTabAreaInsets(int tabPlacement)2738 protected Insets getTabAreaInsets(int tabPlacement) 2739 { 2740 Insets target = new Insets(0, 0, 0, 0); 2741 rotateInsets(tabAreaInsets, target, tabPlacement); 2742 return target; 2743 } 2744 2745 /** 2746 * This method returns the content border insets appropriately rotated. 2747 * 2748 * @param tabPlacement The JTabbedPane's tab placement. 2749 * 2750 * @return The content border insets. 2751 */ getContentBorderInsets(int tabPlacement)2752 protected Insets getContentBorderInsets(int tabPlacement) 2753 { 2754 Insets target = new Insets(0, 0, 0, 0); 2755 rotateInsets(contentBorderInsets, target, tabPlacement); 2756 return target; 2757 } 2758 2759 /** 2760 * This method returns the fontmetrics for the font of the JTabbedPane. 2761 * 2762 * @return The font metrics for the JTabbedPane. 2763 */ getFontMetrics()2764 protected FontMetrics getFontMetrics() 2765 { 2766 FontMetrics fm = tabPane.getToolkit().getFontMetrics(tabPane.getFont()); 2767 return fm; 2768 } 2769 2770 /** 2771 * This method navigates from the selected tab into the given direction. As 2772 * a result, a new tab will be selected (if possible). 2773 * 2774 * @param direction The direction to navigate in. 2775 */ navigateSelectedTab(int direction)2776 protected void navigateSelectedTab(int direction) 2777 { 2778 int tabPlacement = tabPane.getTabPlacement(); 2779 if (tabPlacement == SwingConstants.TOP 2780 || tabPlacement == SwingConstants.BOTTOM) 2781 { 2782 if (direction == SwingConstants.WEST) 2783 selectPreviousTabInRun(tabPane.getSelectedIndex()); 2784 else if (direction == SwingConstants.EAST) 2785 selectNextTabInRun(tabPane.getSelectedIndex()); 2786 2787 else 2788 { 2789 int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(), 2790 tabPane.getSelectedIndex(), 2791 (tabPlacement == SwingConstants.RIGHT) 2792 ? true : false); 2793 selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(), 2794 offset); 2795 } 2796 } 2797 if (tabPlacement == SwingConstants.LEFT 2798 || tabPlacement == SwingConstants.RIGHT) 2799 { 2800 if (direction == SwingConstants.NORTH) 2801 selectPreviousTabInRun(tabPane.getSelectedIndex()); 2802 else if (direction == SwingConstants.SOUTH) 2803 selectNextTabInRun(tabPane.getSelectedIndex()); 2804 else 2805 { 2806 int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(), 2807 tabPane.getSelectedIndex(), 2808 (tabPlacement == SwingConstants.RIGHT) 2809 ? true : false); 2810 selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(), 2811 offset); 2812 } 2813 } 2814 } 2815 2816 /** 2817 * This method selects the next tab in the run. 2818 * 2819 * @param current The current selected index. 2820 */ selectNextTabInRun(int current)2821 protected void selectNextTabInRun(int current) 2822 { 2823 tabPane.setSelectedIndex(getNextTabIndexInRun(tabPane.getTabCount(), 2824 current)); 2825 } 2826 2827 /** 2828 * This method selects the previous tab in the run. 2829 * 2830 * @param current The current selected index. 2831 */ selectPreviousTabInRun(int current)2832 protected void selectPreviousTabInRun(int current) 2833 { 2834 tabPane.setSelectedIndex(getPreviousTabIndexInRun(tabPane.getTabCount(), 2835 current)); 2836 } 2837 2838 /** 2839 * This method selects the next tab (regardless of runs). 2840 * 2841 * @param current The current selected index. 2842 */ selectNextTab(int current)2843 protected void selectNextTab(int current) 2844 { 2845 tabPane.setSelectedIndex(getNextTabIndex(current)); 2846 } 2847 2848 /** 2849 * This method selects the previous tab (regardless of runs). 2850 * 2851 * @param current The current selected index. 2852 */ selectPreviousTab(int current)2853 protected void selectPreviousTab(int current) 2854 { 2855 tabPane.setSelectedIndex(getPreviousTabIndex(current)); 2856 } 2857 2858 /** 2859 * This method selects the correct tab given an offset from the current tab 2860 * index. If the tab placement is TOP or BOTTOM, the offset will be in the 2861 * y direction, otherwise, it will be in the x direction. A new coordinate 2862 * will be found by adding the offset to the current location of the tab. 2863 * The tab that the new location will be selected. 2864 * 2865 * @param tabPlacement The JTabbedPane's tab placement. 2866 * @param tabIndex The tab to start from. 2867 * @param offset The coordinate offset. 2868 */ selectAdjacentRunTab(int tabPlacement, int tabIndex, int offset)2869 protected void selectAdjacentRunTab(int tabPlacement, int tabIndex, 2870 int offset) 2871 { 2872 int x = rects[tabIndex].x + rects[tabIndex].width / 2; 2873 int y = rects[tabIndex].y + rects[tabIndex].height / 2; 2874 2875 switch (tabPlacement) 2876 { 2877 case SwingConstants.TOP: 2878 case SwingConstants.BOTTOM: 2879 y += offset; 2880 break; 2881 case SwingConstants.RIGHT: 2882 case SwingConstants.LEFT: 2883 x += offset; 2884 break; 2885 } 2886 2887 int index = tabForCoordinate(tabPane, x, y); 2888 if (index != -1) 2889 tabPane.setSelectedIndex(index); 2890 } 2891 2892 // This method is called when you press up/down to cycle through tab runs. 2893 // it returns the distance (between the two runs' x/y position. 2894 // where one run is the current selected run and the other run is the run in the 2895 // direction of the scroll (dictated by the forward flag) 2896 // the offset is an absolute value of the difference 2897 2898 /** 2899 * This method calculates the offset distance for use in 2900 * selectAdjacentRunTab. The offset returned will be a difference in the y 2901 * coordinate between the run in the desired direction and the current run 2902 * (for tabPlacement in TOP or BOTTOM). Use x coordinate for LEFT and 2903 * RIGHT. 2904 * 2905 * @param tabPlacement The JTabbedPane's tab placement. 2906 * @param tabCount The number of tabs. 2907 * @param tabIndex The starting index. 2908 * @param forward If forward, the run in the desired direction will be the 2909 * next run. 2910 * 2911 * @return The offset between the two runs. 2912 */ getTabRunOffset(int tabPlacement, int tabCount, int tabIndex, boolean forward)2913 protected int getTabRunOffset(int tabPlacement, int tabCount, int tabIndex, 2914 boolean forward) 2915 { 2916 int currRun = getRunForTab(tabCount, tabIndex); 2917 int offset; 2918 int nextRun = (forward) ? getNextTabRun(currRun) : getPreviousTabRun(currRun); 2919 if (tabPlacement == SwingConstants.TOP 2920 || tabPlacement == SwingConstants.BOTTOM) 2921 offset = rects[lastTabInRun(tabCount, nextRun)].y 2922 - rects[lastTabInRun(tabCount, currRun)].y; 2923 else 2924 offset = rects[lastTabInRun(tabCount, nextRun)].x 2925 - rects[lastTabInRun(tabCount, currRun)].x; 2926 return offset; 2927 } 2928 2929 /** 2930 * This method returns the previous tab index. 2931 * 2932 * @param base The index to start from. 2933 * 2934 * @return The previous tab index. 2935 */ getPreviousTabIndex(int base)2936 protected int getPreviousTabIndex(int base) 2937 { 2938 base--; 2939 if (base < 0) 2940 return tabPane.getTabCount() - 1; 2941 return base; 2942 } 2943 2944 /** 2945 * This method returns the next tab index. 2946 * 2947 * @param base The index to start from. 2948 * 2949 * @return The next tab index. 2950 */ getNextTabIndex(int base)2951 protected int getNextTabIndex(int base) 2952 { 2953 base++; 2954 if (base == tabPane.getTabCount()) 2955 return 0; 2956 return base; 2957 } 2958 2959 /** 2960 * This method returns the next tab index in the run. If the next index is 2961 * out of this run, it will return the starting tab index for the run. 2962 * 2963 * @param tabCount The number of tabs. 2964 * @param base The index to start from. 2965 * 2966 * @return The next tab index in the run. 2967 */ getNextTabIndexInRun(int tabCount, int base)2968 protected int getNextTabIndexInRun(int tabCount, int base) 2969 { 2970 int index = getNextTabIndex(base); 2971 int run = getRunForTab(tabCount, base); 2972 if (index == lastTabInRun(tabCount, run) + 1) 2973 index = lastTabInRun(tabCount, getPreviousTabRun(run)) + 1; 2974 return getNextTabIndex(base); 2975 } 2976 2977 /** 2978 * This method returns the previous tab index in the run. If the previous 2979 * index is out of this run, it will return the last index for the run. 2980 * 2981 * @param tabCount The number of tabs. 2982 * @param base The index to start from. 2983 * 2984 * @return The previous tab index in the run. 2985 */ getPreviousTabIndexInRun(int tabCount, int base)2986 protected int getPreviousTabIndexInRun(int tabCount, int base) 2987 { 2988 int index = getPreviousTabIndex(base); 2989 int run = getRunForTab(tabCount, base); 2990 if (index == lastTabInRun(tabCount, getPreviousTabRun(run))) 2991 index = lastTabInRun(tabCount, run); 2992 return getPreviousTabIndex(base); 2993 } 2994 2995 /** 2996 * This method returns the index of the previous run. 2997 * 2998 * @param baseRun The run to start from. 2999 * 3000 * @return The index of the previous run. 3001 */ getPreviousTabRun(int baseRun)3002 protected int getPreviousTabRun(int baseRun) 3003 { 3004 if (getTabRunCount(tabPane) == 1) 3005 return 1; 3006 3007 int prevRun = --baseRun; 3008 if (prevRun < 0) 3009 prevRun = getTabRunCount(tabPane) - 1; 3010 return prevRun; 3011 } 3012 3013 /** 3014 * This method returns the index of the next run. 3015 * 3016 * @param baseRun The run to start from. 3017 * 3018 * @return The index of the next run. 3019 */ getNextTabRun(int baseRun)3020 protected int getNextTabRun(int baseRun) 3021 { 3022 if (getTabRunCount(tabPane) == 1) 3023 return 1; 3024 3025 int nextRun = ++baseRun; 3026 if (nextRun == getTabRunCount(tabPane)) 3027 nextRun = 0; 3028 return nextRun; 3029 } 3030 3031 /** 3032 * This method rotates the insets given a direction to rotate them in. 3033 * Target placement should be one of TOP, LEFT, BOTTOM, RIGHT. The rotated 3034 * insets will be stored in targetInsets. Passing in TOP as the direction 3035 * does nothing. Passing in LEFT switches top and left, right and bottom. 3036 * Passing in BOTTOM switches top and bottom. Passing in RIGHT switches top 3037 * for left, left for bottom, bottom for right, and right for top. 3038 * 3039 * @param topInsets The reference insets. 3040 * @param targetInsets An Insets object to store the new insets. 3041 * @param targetPlacement The rotation direction. 3042 */ rotateInsets(Insets topInsets, Insets targetInsets, int targetPlacement)3043 protected static void rotateInsets(Insets topInsets, Insets targetInsets, 3044 int targetPlacement) 3045 { 3046 // Sun's version will happily throw an NPE if params are null, 3047 // so I won't check it either. 3048 switch (targetPlacement) 3049 { 3050 case SwingConstants.TOP: 3051 targetInsets.top = topInsets.top; 3052 targetInsets.left = topInsets.left; 3053 targetInsets.right = topInsets.right; 3054 targetInsets.bottom = topInsets.bottom; 3055 break; 3056 case SwingConstants.LEFT: 3057 targetInsets.left = topInsets.top; 3058 targetInsets.top = topInsets.left; 3059 targetInsets.right = topInsets.bottom; 3060 targetInsets.bottom = topInsets.right; 3061 break; 3062 case SwingConstants.BOTTOM: 3063 targetInsets.top = topInsets.bottom; 3064 targetInsets.bottom = topInsets.top; 3065 targetInsets.left = topInsets.left; 3066 targetInsets.right = topInsets.right; 3067 break; 3068 case SwingConstants.RIGHT: 3069 targetInsets.top = topInsets.left; 3070 targetInsets.left = topInsets.bottom; 3071 targetInsets.bottom = topInsets.right; 3072 targetInsets.right = topInsets.top; 3073 break; 3074 } 3075 } 3076 } 3077