1 /* BasicTabbedPaneUI.java -- 2 Copyright (C) 2002, 2004, 2005, 2006 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.ActionEvent; 53 import java.awt.event.FocusAdapter; 54 import java.awt.event.FocusEvent; 55 import java.awt.event.FocusListener; 56 import java.awt.event.MouseAdapter; 57 import java.awt.event.MouseEvent; 58 import java.awt.event.MouseListener; 59 import java.beans.PropertyChangeEvent; 60 import java.beans.PropertyChangeListener; 61 62 import javax.swing.AbstractAction; 63 import javax.swing.ActionMap; 64 import javax.swing.Icon; 65 import javax.swing.InputMap; 66 import javax.swing.JComponent; 67 import javax.swing.JPanel; 68 import javax.swing.JTabbedPane; 69 import javax.swing.JViewport; 70 import javax.swing.KeyStroke; 71 import javax.swing.LookAndFeel; 72 import javax.swing.SwingConstants; 73 import javax.swing.SwingUtilities; 74 import javax.swing.UIManager; 75 import javax.swing.event.ChangeEvent; 76 import javax.swing.event.ChangeListener; 77 import javax.swing.plaf.ActionMapUIResource; 78 import javax.swing.plaf.ComponentUI; 79 import javax.swing.plaf.TabbedPaneUI; 80 import javax.swing.plaf.UIResource; 81 import javax.swing.text.View; 82 83 /** 84 * This is the Basic Look and Feel's UI delegate for JTabbedPane. 85 * 86 * @author Lillian Angel (langel@redhat.com) 87 * @author Kim Ho (kho@redhat.com) 88 * @author Roman Kennke (kennke@aicas.com) 89 * @author Robert Schuster (robertschuster@fsfe.org) 90 */ 91 public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants 92 { 93 94 static class NavigateAction extends AbstractAction 95 { 96 int direction; 97 NavigateAction(String name, int dir)98 NavigateAction(String name, int dir) 99 { 100 super(name); 101 direction = dir; 102 } 103 actionPerformed(ActionEvent event)104 public void actionPerformed(ActionEvent event) 105 { 106 JTabbedPane tp = (JTabbedPane) event.getSource(); 107 BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI(); 108 109 ui.navigateSelectedTab(direction); 110 } 111 112 } 113 114 static class NavigatePageDownAction extends AbstractAction 115 { 116 NavigatePageDownAction()117 public NavigatePageDownAction() 118 { 119 super("navigatePageDown"); 120 } 121 actionPerformed(ActionEvent event)122 public void actionPerformed(ActionEvent event) 123 { 124 JTabbedPane tp = (JTabbedPane) event.getSource(); 125 BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI(); 126 127 int i = tp.getSelectedIndex(); 128 129 if (i < 0) 130 i = 0; 131 132 ui.selectNextTabInRun(i); 133 } 134 135 } 136 137 static class NavigatePageUpAction extends AbstractAction 138 { 139 NavigatePageUpAction()140 public NavigatePageUpAction() 141 { 142 super("navigatePageUp"); 143 } 144 actionPerformed(ActionEvent event)145 public void actionPerformed(ActionEvent event) 146 { 147 JTabbedPane tp = (JTabbedPane) event.getSource(); 148 BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI(); 149 150 int i = tp.getSelectedIndex(); 151 152 if (i < 0) 153 i = 0; 154 155 ui.selectPreviousTabInRun(i); 156 157 } 158 } 159 160 static class RequestFocusAction extends AbstractAction 161 { 162 RequestFocusAction()163 public RequestFocusAction() 164 { 165 super("requestFocus"); 166 } 167 actionPerformed(ActionEvent event)168 public void actionPerformed(ActionEvent event) 169 { 170 ((JTabbedPane) event.getSource()).requestFocus(); 171 } 172 173 } 174 175 static class RequestFocusForVisibleComponentAction extends AbstractAction 176 { 177 RequestFocusForVisibleComponentAction()178 public RequestFocusForVisibleComponentAction() 179 { 180 super("requestFocusForVisibleComponent"); 181 } 182 actionPerformed(ActionEvent event)183 public void actionPerformed(ActionEvent event) 184 { 185 JTabbedPane tp = (JTabbedPane) event.getSource(); 186 187 // FIXME: This should select a suitable component within 188 // the tab content. However I dont know whether we have 189 // to search for this component or wether the called is 190 // supposed to do that. 191 tp.getSelectedComponent().requestFocus(); 192 } 193 194 } 195 196 /** 197 * A helper class that handles focus. 198 * <p>The purpose of this class is to implement a more flexible focus 199 * handling for the tabbed pane, which is used to determine whether the 200 * focus indicator should be painted or not. When in scrolling layout 201 * mode the area containing the tabs is a scrollpane, so simply testing 202 * whether the tabbed pane has the focus does not work.</p> 203 * <p>The <code>FocusHandler</code> is installed on the scrollpane and 204 * the tabbed pane and sets the variable <code>hasFocus</code> to 205 * <code>false</code> only when both components do not hold the focus.</p> 206 * 207 * @specnote Apparently this class was intended to be protected, 208 * but was made public by a compiler bug and is now 209 * public for compatibility. 210 */ 211 public class FocusHandler extends FocusAdapter 212 { 213 /** 214 * This method is called when the component gains focus. 215 * 216 * @param e The FocusEvent. 217 */ focusGained(FocusEvent e)218 public void focusGained(FocusEvent e) 219 { 220 Object source = e.getSource(); 221 if (source == panel ) 222 tabPane.requestFocus(); 223 else if (source == tabPane) 224 tabPane.repaint(); 225 } 226 227 /** 228 * This method is called when the component loses focus. 229 * 230 * @param e The FocusEvent. 231 */ focusLost(FocusEvent e)232 public void focusLost(FocusEvent e) 233 { 234 if (e.getOppositeComponent() == tabPane.getSelectedComponent()) 235 tabPane.requestFocus(); 236 else if (e.getSource() == tabPane) 237 tabPane.repaint(); 238 } 239 } 240 241 /** 242 * A helper class for determining if mouse presses occur inside tabs and 243 * sets the index appropriately. In SCROLL_TAB_MODE, this class also 244 * handles the mouse clicks on the scrolling buttons. 245 * 246 * @specnote Apparently this class was intended to be protected, 247 * but was made public by a compiler bug and is now 248 * public for compatibility. 249 */ 250 public class MouseHandler extends MouseAdapter 251 { mouseReleased(MouseEvent e)252 public void mouseReleased(MouseEvent e) 253 { 254 Object s = e.getSource(); 255 256 // Event may originate from the viewport in 257 // SCROLL_TAB_LAYOUT mode. It is redisptached 258 // through the tabbed pane then. 259 if (tabPane != e.getSource()) 260 { 261 redispatchEvent(e); 262 e.setSource(s); 263 } 264 } 265 266 /** 267 * This method is called when the mouse is pressed. The index cannot 268 * change to a tab that is not enabled. 269 * 270 * @param e The MouseEvent. 271 */ mousePressed(MouseEvent e)272 public void mousePressed(MouseEvent e) 273 { 274 Object s = e.getSource(); 275 276 // Event may originate from the viewport in 277 // SCROLL_TAB_LAYOUT mode. It is redisptached 278 // through the tabbed pane then. 279 if (tabPane != e.getSource()) 280 { 281 redispatchEvent(e); 282 e.setSource(s); 283 } 284 285 int placement = tabPane.getTabPlacement(); 286 287 if (s == incrButton) 288 { 289 if(!incrButton.isEnabled()) 290 return; 291 292 currentScrollLocation++; 293 294 switch (placement) 295 { 296 case JTabbedPane.TOP: 297 case JTabbedPane.BOTTOM: 298 currentScrollOffset = getTabAreaInsets(placement).left; 299 for (int i = 0; i < currentScrollLocation; i++) 300 currentScrollOffset += rects[i].width; 301 break; 302 default: 303 currentScrollOffset = getTabAreaInsets(placement).top; 304 for (int i = 0; i < currentScrollLocation; i++) 305 currentScrollOffset += rects[i].height; 306 break; 307 } 308 309 updateViewPosition(); 310 updateButtons(); 311 312 tabPane.repaint(); 313 } 314 else if (s == decrButton) 315 { 316 if(!decrButton.isEnabled()) 317 return; 318 319 // The scroll location may be zero but the offset 320 // greater than zero because of an adjustement to 321 // make a partially visible tab completely visible. 322 if (currentScrollLocation > 0) 323 currentScrollLocation--; 324 325 // Set the offset back to 0 and recompute it. 326 currentScrollOffset = 0; 327 328 switch (placement) 329 { 330 case JTabbedPane.TOP: 331 case JTabbedPane.BOTTOM: 332 // Take the tab area inset into account. 333 if (currentScrollLocation > 0) 334 currentScrollOffset = getTabAreaInsets(placement).left; 335 // Recompute scroll offset. 336 for (int i = 0; i < currentScrollLocation; i++) 337 currentScrollOffset += rects[i].width; 338 break; 339 default: 340 // Take the tab area inset into account. 341 if (currentScrollLocation > 0) 342 currentScrollOffset = getTabAreaInsets(placement).top; 343 344 for (int i = 0; i < currentScrollLocation; i++) 345 currentScrollOffset += rects[i].height; 346 } 347 348 updateViewPosition(); 349 updateButtons(); 350 351 tabPane.repaint(); 352 } 353 else if (tabPane.isEnabled()) 354 { 355 int index = tabForCoordinate(tabPane, e.getX(), e.getY()); 356 if (!tabPane.isEnabledAt(index)) 357 return; 358 359 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT 360 && s == panel) 361 { 362 scrollTab(index, placement); 363 364 tabPane.setSelectedIndex(index); 365 tabPane.repaint(); 366 } 367 else 368 { 369 tabPane.setSelectedIndex(index); 370 tabPane.revalidate(); 371 tabPane.repaint(); 372 } 373 374 } 375 376 } 377 378 /** 379 * Receives notification when the mouse pointer has entered the tabbed 380 * pane. 381 * 382 * @param e the mouse event 383 */ mouseEntered(MouseEvent e)384 public void mouseEntered(MouseEvent e) 385 { 386 Object s = e.getSource(); 387 388 // Event may originate from the viewport in 389 // SCROLL_TAB_LAYOUT mode. It is redisptached 390 // through the tabbed pane then. 391 if (tabPane != e.getSource()) 392 { 393 redispatchEvent(e); 394 e.setSource(s); 395 } 396 397 int tabIndex = tabForCoordinate(tabPane, e.getX(), e.getY()); 398 setRolloverTab(tabIndex); 399 } 400 401 /** 402 * Receives notification when the mouse pointer has exited the tabbed 403 * pane. 404 * 405 * @param e the mouse event 406 */ mouseExited(MouseEvent e)407 public void mouseExited(MouseEvent e) 408 { 409 Object s = e.getSource(); 410 411 // Event may originate from the viewport in 412 // SCROLL_TAB_LAYOUT mode. It is redisptached 413 // through the tabbed pane then. 414 if (tabPane != e.getSource()) 415 { 416 redispatchEvent(e); 417 e.setSource(s); 418 } 419 420 setRolloverTab(-1); 421 } 422 423 /** 424 * Receives notification when the mouse pointer has moved over the tabbed 425 * pane. 426 * 427 * @param ev the mouse event 428 */ mouseMoved(MouseEvent ev)429 public void mouseMoved(MouseEvent ev) 430 { 431 Object s = ev.getSource(); 432 433 if (tabPane != ev.getSource()) 434 { 435 ev.setSource(tabPane); 436 tabPane.dispatchEvent(ev); 437 438 ev.setSource(s); 439 } 440 441 int tabIndex = tabForCoordinate(tabPane, ev.getX(), ev.getY()); 442 setRolloverTab(tabIndex); 443 } 444 445 /** Modifies the mouse event to originate from 446 * the tabbed pane and redispatches it. 447 * 448 * @param me 449 */ redispatchEvent(MouseEvent me)450 void redispatchEvent(MouseEvent me) 451 { 452 me.setSource(tabPane); 453 Point viewPos = viewport.getViewPosition(); 454 viewPos.x -= viewport.getX(); 455 viewPos.y -= viewport.getY(); 456 me.translatePoint(-viewPos.x, -viewPos.y); 457 tabPane.dispatchEvent(me); 458 459 me.translatePoint(viewPos.x, viewPos.y); 460 } 461 462 } 463 464 /** 465 * This class handles PropertyChangeEvents fired from the JTabbedPane. 466 * 467 * @specnote Apparently this class was intended to be protected, 468 * but was made public by a compiler bug and is now 469 * public for compatibility. 470 */ 471 public class PropertyChangeHandler implements PropertyChangeListener 472 { 473 /** 474 * This method is called whenever one of the properties of the JTabbedPane 475 * changes. 476 * 477 * @param e The PropertyChangeEvent. 478 */ propertyChange(PropertyChangeEvent e)479 public void propertyChange(PropertyChangeEvent e) 480 { 481 out: 482 { 483 if (e.getPropertyName().equals("tabLayoutPolicy")) 484 { 485 currentScrollLocation = currentScrollOffset = 0; 486 487 layoutManager = createLayoutManager(); 488 489 tabPane.setLayout(layoutManager); 490 } 491 else if (e.getPropertyName().equals("tabPlacement") 492 && tabPane.getTabLayoutPolicy() 493 == JTabbedPane.SCROLL_TAB_LAYOUT) 494 { 495 incrButton = createIncreaseButton(); 496 decrButton = createDecreaseButton(); 497 498 // If the tab placement value was changed of a tabbed pane 499 // in SCROLL_TAB_LAYOUT mode we investigate the change to 500 // implement the following behavior which was observed in 501 // the RI: 502 // The scrolling offset will be reset if we change to 503 // a direction which is orthogonal to the current 504 // direction and stays the same if it is parallel. 505 506 int oldPlacement = ((Integer) e.getOldValue()).intValue(); 507 int newPlacement = ((Integer) e.getNewValue()).intValue(); 508 switch (newPlacement) 509 { 510 case JTabbedPane.TOP: 511 case JTabbedPane.BOTTOM: 512 if (oldPlacement == JTabbedPane.TOP 513 || oldPlacement == JTabbedPane.BOTTOM) 514 break out; 515 516 currentScrollOffset = getTabAreaInsets(newPlacement).left; 517 break; 518 default: 519 if (oldPlacement == JTabbedPane.LEFT 520 || oldPlacement == JTabbedPane.RIGHT) 521 break out; 522 523 currentScrollOffset = getTabAreaInsets(newPlacement).top; 524 } 525 526 updateViewPosition(); 527 updateButtons(); 528 } 529 } 530 531 tabPane.revalidate(); 532 tabPane.repaint(); 533 } 534 } 535 536 /** 537 * A LayoutManager responsible for placing all the tabs and the visible 538 * component inside the JTabbedPane. This class is only used for 539 * WRAP_TAB_LAYOUT. 540 * 541 * @specnote Apparently this class was intended to be protected, 542 * but was made public by a compiler bug and is now 543 * public for compatibility. 544 */ 545 public class TabbedPaneLayout implements LayoutManager 546 { 547 /** 548 * This method is called when a component is added to the JTabbedPane. 549 * 550 * @param name The name of the component. 551 * @param comp The component being added. 552 */ addLayoutComponent(String name, Component comp)553 public void addLayoutComponent(String name, Component comp) 554 { 555 // Do nothing. 556 } 557 558 /** 559 * This method is called when the rectangles need to be calculated. It 560 * also fixes the size of the visible component. 561 */ calculateLayoutInfo()562 public void calculateLayoutInfo() 563 { 564 int count = tabPane.getTabCount(); 565 assureRectsCreated(count); 566 calculateTabRects(tabPane.getTabPlacement(), count); 567 tabRunsDirty = false; 568 } 569 570 /** 571 * This method calculates the size of the the JTabbedPane. 572 * 573 * @param minimum Whether the JTabbedPane will try to be as small as it 574 * can. 575 * 576 * @return The desired size of the JTabbedPane. 577 */ calculateSize(boolean minimum)578 protected Dimension calculateSize(boolean minimum) 579 { 580 int tabPlacement = tabPane.getTabPlacement(); 581 582 int width = 0; 583 int height = 0; 584 Component c; 585 Dimension dims; 586 587 // Find out the minimum/preferred size to display the largest child 588 // of the tabbed pane. 589 int count = tabPane.getTabCount(); 590 for (int i = 0; i < count; i++) 591 { 592 c = tabPane.getComponentAt(i); 593 if (c == null) 594 continue; 595 dims = minimum ? c.getMinimumSize() : c.getPreferredSize(); 596 if (dims != null) 597 { 598 height = Math.max(height, dims.height); 599 width = Math.max(width, dims.width); 600 } 601 } 602 603 Insets tabAreaInsets = getTabAreaInsets(tabPlacement); 604 if (tabPlacement == SwingConstants.TOP 605 || tabPlacement == SwingConstants.BOTTOM) 606 { 607 width = Math.max(calculateMaxTabWidth(tabPlacement), width); 608 609 height += preferredTabAreaHeight(tabPlacement, 610 width - tabAreaInsets.left 611 - tabAreaInsets.right); 612 } 613 else 614 { 615 height = Math.max(calculateMaxTabHeight(tabPlacement), height); 616 617 width += preferredTabAreaWidth(tabPlacement, 618 height - tabAreaInsets.top 619 - tabAreaInsets.bottom); 620 } 621 622 Insets tabPaneInsets = tabPane.getInsets(); 623 return new Dimension(width + tabPaneInsets.left + tabPaneInsets.right, 624 height + tabPaneInsets.top + tabPaneInsets.bottom); 625 } 626 627 // if tab placement is LEFT OR RIGHT, they share width. 628 // if tab placement is TOP OR BOTTOM, they share height 629 // PRE STEP: finds the default sizes for the labels as well as their 630 // locations. 631 // AND where they will be placed within the run system. 632 // 1. calls normalizeTab Runs. 633 // 2. calls rotate tab runs. 634 // 3. pads the tab runs. 635 // 4. pads the selected tab. 636 637 /** 638 * This method is called to calculate the tab rectangles. This method 639 * will calculate the size and position of all rectangles (taking into 640 * account which ones should be in which tab run). It will pad them and 641 * normalize them as necessary. 642 * 643 * @param tabPlacement The JTabbedPane's tab placement. 644 * @param tabCount The run the current selection is in. 645 */ calculateTabRects(int tabPlacement, int tabCount)646 protected void calculateTabRects(int tabPlacement, int tabCount) 647 { 648 Insets insets = tabPane.getInsets(); 649 Insets tabAreaInsets = getTabAreaInsets(tabPlacement); 650 Dimension size = tabPane.getSize(); 651 652 // The coordinates of the upper left corner of the tab area. 653 int x; 654 int y; 655 // The location at which the runs must be broken. 656 int breakAt; 657 658 // Calculate the bounds for the tab area. 659 switch (tabPlacement) 660 { 661 case LEFT: 662 maxTabWidth = calculateMaxTabWidth(tabPlacement); 663 x = insets.left + tabAreaInsets.left; 664 y = insets.top + tabAreaInsets.top; 665 breakAt = size.height - (insets.bottom + tabAreaInsets.bottom); 666 break; 667 case RIGHT: 668 maxTabWidth = calculateMaxTabWidth(tabPlacement); 669 x = size.width - (insets.right + tabAreaInsets.right) 670 - maxTabWidth - 1; 671 y = insets.top + tabAreaInsets.top; 672 breakAt = size.height - (insets.bottom + tabAreaInsets.bottom); 673 break; 674 case BOTTOM: 675 maxTabHeight = calculateMaxTabHeight(tabPlacement); 676 x = insets.left + tabAreaInsets.left; 677 y = size.height - (insets.bottom + tabAreaInsets.bottom) 678 - maxTabHeight - 1; 679 breakAt = size.width - (insets.right + tabAreaInsets.right); 680 break; 681 case TOP: 682 default: 683 maxTabHeight = calculateMaxTabHeight(tabPlacement); 684 x = insets.left + tabAreaInsets.left; 685 y = insets.top + tabAreaInsets.top; 686 breakAt = size.width - (insets.right + tabAreaInsets.right); 687 break; 688 } 689 690 if (tabCount == 0) 691 return; 692 693 FontMetrics fm = getFontMetrics(); 694 runCount = 0; 695 selectedRun = -1; 696 int selectedIndex = tabPane.getSelectedIndex(); 697 if (selectedIndex < 0) 698 selectedIndex = 0; 699 700 Rectangle rect; 701 702 // Go through all the tabs and build the tab runs. 703 if (tabPlacement == SwingConstants.TOP 704 || tabPlacement == SwingConstants.BOTTOM) 705 { 706 for (int i = 0; i < tabCount; i++) 707 { 708 rect = rects[i]; 709 if (i > 0) 710 { 711 rect.x = rects[i - 1].x + rects[i - 1].width; 712 } 713 else 714 { 715 tabRuns[0] = 0; 716 runCount = 1; 717 maxTabWidth = 0; 718 rect.x = x; 719 } 720 rect.width = calculateTabWidth(tabPlacement, i, fm); 721 maxTabWidth = Math.max(maxTabWidth, rect.width); 722 723 if (rect.x != 2 + insets.left && rect.x + rect.width > breakAt) 724 { 725 if (runCount > tabRuns.length - 1) 726 expandTabRunsArray(); 727 tabRuns[runCount] = i; 728 runCount++; 729 rect.x = x; 730 } 731 732 rect.y = y; 733 rect.height = maxTabHeight; 734 if (i == selectedIndex) 735 selectedRun = runCount - 1; 736 } 737 } 738 else 739 { 740 for (int i = 0; i < tabCount; i++) 741 { 742 rect = rects[i]; 743 if (i > 0) 744 { 745 rect.y = rects[i - 1].y + rects[i - 1].height; 746 } 747 else 748 { 749 tabRuns[0] = 0; 750 runCount = 1; 751 maxTabHeight = 0; 752 rect.y = y; 753 } 754 rect.height = calculateTabHeight(tabPlacement, i, 755 fm.getHeight()); 756 maxTabHeight = Math.max(maxTabHeight, rect.height); 757 758 if (rect.y != 2 + insets.top && rect.y + rect.height > breakAt) 759 { 760 if (runCount > tabRuns.length - 1) 761 expandTabRunsArray(); 762 tabRuns[runCount] = i; 763 runCount++; 764 rect.y = y; 765 } 766 767 rect.x = x; 768 rect.width = maxTabWidth; 769 770 if (i == selectedIndex) 771 selectedRun = runCount - 1; 772 } 773 } 774 775 if (runCount > 1) 776 { 777 int start; 778 if (tabPlacement == SwingConstants.TOP 779 || tabPlacement == SwingConstants.BOTTOM) 780 start = x; 781 else 782 start = y; 783 normalizeTabRuns(tabPlacement, tabCount, start, breakAt); 784 selectedRun = getRunForTab(tabCount, selectedIndex); 785 if (shouldRotateTabRuns(tabPlacement)) 786 { 787 rotateTabRuns(tabPlacement, selectedRun); 788 } 789 } 790 791 // Suppress padding if we have only one tab run. 792 if (runCount == 1) 793 return; 794 795 // Pad the runs. 796 int tabRunOverlay = getTabRunOverlay(tabPlacement); 797 for (int i = runCount - 1; i >= 0; --i) 798 { 799 int start = tabRuns[i]; 800 int nextIndex; 801 if (i == runCount - 1) 802 nextIndex = 0; 803 else 804 nextIndex = i + 1; 805 int next = tabRuns[nextIndex]; 806 int end = next != 0 ? next - 1 : tabCount - 1; 807 if (tabPlacement == SwingConstants.TOP 808 || tabPlacement == SwingConstants.BOTTOM) 809 { 810 for (int j = start; j <= end; ++j) 811 { 812 rect = rects[j]; 813 rect.y = y; 814 rect.x += getTabRunIndent(tabPlacement, i); 815 } 816 if (shouldPadTabRun(tabPlacement, i)) 817 { 818 padTabRun(tabPlacement, start, end, breakAt); 819 } 820 if (tabPlacement == BOTTOM) 821 y -= maxTabHeight - tabRunOverlay; 822 else 823 y += maxTabHeight - tabRunOverlay; 824 } 825 else 826 { 827 for (int j = start; j <= end; ++j) 828 { 829 rect = rects[j]; 830 rect.x = x; 831 rect.y += getTabRunIndent(tabPlacement, i); 832 } 833 if (shouldPadTabRun(tabPlacement, i)) 834 { 835 padTabRun(tabPlacement, start, end, breakAt); 836 } 837 if (tabPlacement == RIGHT) 838 x -= maxTabWidth - tabRunOverlay; 839 else 840 x += maxTabWidth - tabRunOverlay; 841 842 } 843 } 844 padSelectedTab(tabPlacement, selectedIndex); 845 } 846 847 /** 848 * This method is called when the JTabbedPane is laid out in 849 * WRAP_TAB_LAYOUT. It calls calculateLayoutInfo to find the positions 850 * of all its components. 851 * 852 * @param parent The Container to lay out. 853 */ layoutContainer(Container parent)854 public void layoutContainer(Container parent) 855 { 856 calculateLayoutInfo(); 857 858 int tabPlacement = tabPane.getTabPlacement(); 859 Insets insets = tabPane.getInsets(); 860 861 int selectedIndex = tabPane.getSelectedIndex(); 862 863 Component selectedComponent = null; 864 if (selectedIndex >= 0) 865 selectedComponent = tabPane.getComponentAt(selectedIndex); 866 // The RI doesn't seem to change the component if the new selected 867 // component == null. This is probably so that applications can add 868 // one single component for every tab. 869 if (selectedComponent != null) 870 { 871 setVisibleComponent(selectedComponent); 872 } 873 874 int childCount = tabPane.getComponentCount(); 875 if (childCount > 0) 876 { 877 int compX; 878 int compY; 879 int tabAreaWidth = 0; 880 int tabAreaHeight = 0; 881 switch (tabPlacement) 882 { 883 case LEFT: 884 tabAreaWidth = calculateTabAreaWidth(tabPlacement, runCount, 885 maxTabWidth); 886 compX = tabAreaWidth + insets.left + contentBorderInsets.left; 887 compY = insets.top + contentBorderInsets.top; 888 break; 889 case RIGHT: 890 tabAreaWidth = calculateTabAreaWidth(tabPlacement, runCount, 891 maxTabWidth); 892 compX = insets.left + contentBorderInsets.left; 893 compY = insets.top + contentBorderInsets.top; 894 break; 895 case BOTTOM: 896 tabAreaHeight = calculateTabAreaHeight(tabPlacement, runCount, 897 maxTabHeight); 898 compX = insets.left + contentBorderInsets.left; 899 compY = insets.top + contentBorderInsets.top; 900 break; 901 case TOP: 902 default: 903 tabAreaHeight = calculateTabAreaHeight(tabPlacement, runCount, 904 maxTabHeight); 905 906 compX = insets.left + contentBorderInsets.left; 907 compY = tabAreaHeight + insets.top + contentBorderInsets.top; 908 } 909 Rectangle bounds = tabPane.getBounds(); 910 int compWidth = bounds.width - tabAreaWidth - insets.left 911 - insets.right - contentBorderInsets.left 912 - contentBorderInsets.right; 913 int compHeight = bounds.height - tabAreaHeight - insets.top 914 - insets.bottom - contentBorderInsets.top 915 - contentBorderInsets.bottom; 916 917 918 for (int i = 0; i < childCount; ++i) 919 { 920 Component c = tabPane.getComponent(i); 921 c.setBounds(compX, compY, compWidth, compHeight); 922 } 923 } 924 } 925 926 /** 927 * This method returns the minimum layout size for the given container. 928 * 929 * @param parent The container that is being sized. 930 * 931 * @return The minimum size. 932 */ minimumLayoutSize(Container parent)933 public Dimension minimumLayoutSize(Container parent) 934 { 935 return calculateSize(true); 936 } 937 938 // If there is more free space in an adjacent run AND the tab 939 // in the run can fit in the adjacent run, move it. This method 940 // is not perfect, it is merely an approximation. 941 // If you play around with Sun's JTabbedPane, you'll see that 942 // it does do some pretty strange things with regards to not moving tabs 943 // that should be moved. 944 // start = the x position where the tabs will begin 945 // max = the maximum position of where the tabs can go to 946 // (tabAreaInsets.left + the width of the tab area) 947 948 /** 949 * This method tries to "even out" the number of tabs in each run based on 950 * their widths. 951 * 952 * @param tabPlacement The JTabbedPane's tab placement. 953 * @param tabCount The number of tabs. 954 * @param start The x position where the tabs will begin. 955 * @param max The maximum x position where the tab can run to. 956 */ normalizeTabRuns(int tabPlacement, int tabCount, int start, int max)957 protected void normalizeTabRuns(int tabPlacement, int tabCount, int start, 958 int max) 959 { 960 boolean horizontal = tabPlacement == TOP || tabPlacement == BOTTOM; 961 int currentRun = runCount - 1; 962 double weight = 1.25; 963 for (boolean adjust = true; adjust == true;) 964 { 965 int last = lastTabInRun(tabCount, currentRun); 966 int prevLast = lastTabInRun(tabCount, currentRun - 1); 967 int end; 968 int prevLength; 969 if (horizontal) 970 { 971 end = rects[last].x + rects[last].width; 972 prevLength = (int) (maxTabWidth * weight); 973 } 974 else 975 { 976 end = rects[last].y + rects[last].height; 977 prevLength = (int) (maxTabWidth * weight * 2); 978 } 979 if (max - end > prevLength) 980 { 981 tabRuns[currentRun] = prevLast; 982 if (horizontal) 983 rects[prevLast].x = start; 984 else 985 rects[prevLast].y = start; 986 for (int i = prevLast + 1; i <= last; i++) 987 { 988 if (horizontal) 989 rects[i].x = rects[i - 1].x + rects[i - 1].width; 990 else 991 rects[i].y = rects[i - 1].y + rects[i - 1].height; 992 } 993 } 994 else if (currentRun == runCount - 1) 995 adjust = false; 996 if (currentRun - 1 > 0) 997 currentRun -= 1; 998 else 999 { 1000 // Check again, but with higher ratio to avoid 1001 // clogging up the last run. 1002 currentRun = runCount - 1; 1003 weight += 0.25; 1004 } 1005 } 1006 } 1007 1008 /** 1009 * This method pads the tab at the selected index by the selected tab pad 1010 * insets (so that it looks larger). 1011 * 1012 * @param tabPlacement The placement of the tabs. 1013 * @param selectedIndex The selected index. 1014 */ padSelectedTab(int tabPlacement, int selectedIndex)1015 protected void padSelectedTab(int tabPlacement, int selectedIndex) 1016 { 1017 Insets insets = getSelectedTabPadInsets(tabPlacement); 1018 rects[selectedIndex].x -= insets.left; 1019 rects[selectedIndex].y -= insets.top; 1020 rects[selectedIndex].width += insets.left + insets.right; 1021 rects[selectedIndex].height += insets.top + insets.bottom; 1022 } 1023 1024 // If the tabs on the run don't fill the width of the window, make it 1025 // fit now. 1026 // start = starting index of the run 1027 // end = last index of the run 1028 // max = tabAreaInsets.left + width (or equivalent) 1029 // assert start <= end. 1030 1031 /** 1032 * This method makes each tab in the run larger so that the tabs expand 1033 * to fill the runs width/height (depending on tabPlacement). 1034 * 1035 * @param tabPlacement The placement of the tabs. 1036 * @param start The index of the first tab. 1037 * @param end The last index of the tab 1038 * @param max The amount of space in the run (width for TOP and BOTTOM 1039 * tabPlacement). 1040 */ padTabRun(int tabPlacement, int start, int end, int max)1041 protected void padTabRun(int tabPlacement, int start, int end, int max) 1042 { 1043 if (tabPlacement == SwingConstants.TOP 1044 || tabPlacement == SwingConstants.BOTTOM) 1045 { 1046 int runWidth = rects[end].x + rects[end].width; 1047 int spaceRemaining = max - runWidth; 1048 int numTabs = end - start + 1; 1049 1050 // now divvy up the space. 1051 int spaceAllocated = spaceRemaining / numTabs; 1052 int currX = rects[start].x; 1053 for (int i = start; i <= end; i++) 1054 { 1055 rects[i].x = currX; 1056 rects[i].width += spaceAllocated; 1057 1058 currX += rects[i].width; 1059 // This is used because since the spaceAllocated 1060 // variable is an int, it rounds down. Sometimes, 1061 // we don't fill an entire row, so we make it do 1062 // so now. 1063 1064 if (i == end && rects[i].x + rects[i].width != max) 1065 rects[i].width = max - rects[i].x; 1066 } 1067 } 1068 else 1069 { 1070 int runHeight = rects[end].y + rects[end].height; 1071 int spaceRemaining = max - runHeight; 1072 int numTabs = end - start + 1; 1073 1074 int spaceAllocated = spaceRemaining / numTabs; 1075 int currY = rects[start].y; 1076 for (int i = start; i <= end; i++) 1077 { 1078 rects[i].y = currY; 1079 rects[i].height += spaceAllocated; 1080 currY += rects[i].height; 1081 if (i == end && rects[i].y + rects[i].height != max) 1082 rects[i].height = max - rects[i].y; 1083 } 1084 } 1085 } 1086 1087 /** 1088 * This method returns the preferred layout size for the given container. 1089 * 1090 * @param parent The container to size. 1091 * 1092 * @return The preferred layout size. 1093 */ preferredLayoutSize(Container parent)1094 public Dimension preferredLayoutSize(Container parent) 1095 { 1096 return calculateSize(false); 1097 } 1098 1099 /** 1100 * This method returns the preferred tab height given a tabPlacement and 1101 * width. 1102 * 1103 * @param tabPlacement The JTabbedPane's tab placement. 1104 * @param width The expected width. 1105 * 1106 * @return The preferred tab area height. 1107 */ preferredTabAreaHeight(int tabPlacement, int width)1108 protected int preferredTabAreaHeight(int tabPlacement, int width) 1109 { 1110 if (tabPane.getTabCount() == 0) 1111 return calculateTabAreaHeight(tabPlacement, 0, 0); 1112 1113 int runs = 0; 1114 int runWidth = 0; 1115 int tabWidth = 0; 1116 1117 FontMetrics fm = getFontMetrics(); 1118 1119 Insets tabAreaInsets = getTabAreaInsets(tabPlacement); 1120 Insets insets = tabPane.getInsets(); 1121 1122 // Only interested in width, this is a messed up rectangle now. 1123 width -= tabAreaInsets.left + tabAreaInsets.right + insets.left 1124 + insets.right; 1125 1126 // The reason why we can't use runCount: 1127 // This method is only called to calculate the size request 1128 // for the tabbedPane. However, this size request is dependent on 1129 // our desired width. We need to find out what the height would 1130 // be IF we got our desired width. 1131 for (int i = 0; i < tabPane.getTabCount(); i++) 1132 { 1133 tabWidth = calculateTabWidth(tabPlacement, i, fm); 1134 if (runWidth + tabWidth > width) 1135 { 1136 runWidth = tabWidth; 1137 runs++; 1138 } 1139 else 1140 runWidth += tabWidth; 1141 } 1142 runs++; 1143 1144 int maxTabHeight = calculateMaxTabHeight(tabPlacement); 1145 int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs, 1146 maxTabHeight); 1147 return tabAreaHeight; 1148 } 1149 1150 /** 1151 * This method calculates the preferred tab area width given a tab 1152 * placement and height. 1153 * 1154 * @param tabPlacement The JTabbedPane's tab placement. 1155 * @param height The expected height. 1156 * 1157 * @return The preferred tab area width. 1158 */ preferredTabAreaWidth(int tabPlacement, int height)1159 protected int preferredTabAreaWidth(int tabPlacement, int height) 1160 { 1161 if (tabPane.getTabCount() == 0) 1162 return calculateTabAreaHeight(tabPlacement, 0, 0); 1163 1164 int runs = 0; 1165 int runHeight = 0; 1166 int tabHeight = 0; 1167 1168 FontMetrics fm = getFontMetrics(); 1169 1170 Insets tabAreaInsets = getTabAreaInsets(tabPlacement); 1171 Insets insets = tabPane.getInsets(); 1172 1173 height -= tabAreaInsets.top + tabAreaInsets.bottom + insets.top 1174 + insets.bottom; 1175 int fontHeight = fm.getHeight(); 1176 1177 for (int i = 0; i < tabPane.getTabCount(); i++) 1178 { 1179 tabHeight = calculateTabHeight(tabPlacement, i, fontHeight); 1180 if (runHeight + tabHeight > height) 1181 { 1182 runHeight = tabHeight; 1183 runs++; 1184 } 1185 else 1186 runHeight += tabHeight; 1187 } 1188 runs++; 1189 1190 int maxTabWidth = calculateMaxTabWidth(tabPlacement); 1191 int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, 1192 maxTabWidth); 1193 return tabAreaWidth; 1194 } 1195 1196 /** 1197 * This method rotates the places each run in the correct place the 1198 * tabRuns array. See the comment for tabRuns for how the runs are placed 1199 * in the array. 1200 * 1201 * @param tabPlacement The JTabbedPane's tab placement. 1202 * @param selectedRun The run the current selection is in. 1203 */ rotateTabRuns(int tabPlacement, int selectedRun)1204 protected void rotateTabRuns(int tabPlacement, int selectedRun) 1205 { 1206 if (runCount == 1 || selectedRun == 0 || selectedRun == -1) 1207 return; 1208 int[] newTabRuns = new int[tabRuns.length]; 1209 int currentRun = selectedRun; 1210 int i = 0; 1211 do 1212 { 1213 newTabRuns[i] = tabRuns[currentRun]; 1214 currentRun = getNextTabRun(currentRun); 1215 i++; 1216 } 1217 while (i < runCount); 1218 1219 tabRuns = newTabRuns; 1220 BasicTabbedPaneUI.this.selectedRun = 1; 1221 } 1222 1223 /** 1224 * This method is called when a component is removed from the 1225 * JTabbedPane. 1226 * 1227 * @param comp The component removed. 1228 */ removeLayoutComponent(Component comp)1229 public void removeLayoutComponent(Component comp) 1230 { 1231 // Do nothing. 1232 } 1233 } 1234 1235 /** 1236 * This class acts as the LayoutManager for the JTabbedPane in 1237 * SCROLL_TAB_MODE. 1238 */ 1239 private class TabbedPaneScrollLayout extends TabbedPaneLayout 1240 { 1241 /** 1242 * This method returns the preferred layout size for the given container. 1243 * 1244 * @param parent The container to calculate a size for. 1245 * 1246 * @return The preferred layout size. 1247 */ preferredLayoutSize(Container parent)1248 public Dimension preferredLayoutSize(Container parent) 1249 { 1250 return super.calculateSize(false); 1251 } 1252 1253 /** 1254 * This method returns the minimum layout size for the given container. 1255 * 1256 * @param parent The container to calculate a size for. 1257 * 1258 * @return The minimum layout size. 1259 */ minimumLayoutSize(Container parent)1260 public Dimension minimumLayoutSize(Container parent) 1261 { 1262 return super.calculateSize(true); 1263 } 1264 1265 /** 1266 * This method calculates the tab area height given a desired width. 1267 * 1268 * @param tabPlacement The JTabbedPane's tab placement. 1269 * @param width The expected width. 1270 * 1271 * @return The tab area height given the width. 1272 */ preferredTabAreaHeight(int tabPlacement, int width)1273 protected int preferredTabAreaHeight(int tabPlacement, int width) 1274 { 1275 if (tabPane.getTabCount() == 0) 1276 return calculateTabAreaHeight(tabPlacement, 0, 0); 1277 1278 int runs = 1; 1279 1280 int maxTabHeight = calculateMaxTabHeight(tabPlacement); 1281 int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs, 1282 maxTabHeight); 1283 return tabAreaHeight; 1284 } 1285 1286 /** 1287 * This method calculates the tab area width given a desired height. 1288 * 1289 * @param tabPlacement The JTabbedPane's tab placement. 1290 * @param height The expected height. 1291 * 1292 * @return The tab area width given the height. 1293 */ preferredTabAreaWidth(int tabPlacement, int height)1294 protected int preferredTabAreaWidth(int tabPlacement, int height) 1295 { 1296 if (tabPane.getTabCount() == 0) 1297 return calculateTabAreaHeight(tabPlacement, 0, 0); 1298 1299 int runs = 1; 1300 1301 int maxTabWidth = calculateMaxTabWidth(tabPlacement); 1302 int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth); 1303 return tabAreaWidth; 1304 } 1305 1306 /** 1307 * This method is called to calculate the tab rectangles. This method 1308 * will calculate the size and position of all rectangles (taking into 1309 * account which ones should be in which tab run). It will pad them and 1310 * normalize them as necessary. 1311 * 1312 * @param tabPlacement The JTabbedPane's tab placement. 1313 * @param tabCount The number of tabs. 1314 */ calculateTabRects(int tabPlacement, int tabCount)1315 protected void calculateTabRects(int tabPlacement, int tabCount) 1316 { 1317 if (tabCount == 0) 1318 return; 1319 1320 FontMetrics fm = getFontMetrics(); 1321 SwingUtilities.calculateInnerArea(tabPane, calcRect); 1322 Insets tabAreaInsets = getTabAreaInsets(tabPlacement); 1323 Insets insets = tabPane.getInsets(); 1324 if (tabPlacement == SwingConstants.TOP 1325 || tabPlacement == SwingConstants.BOTTOM) 1326 { 1327 int maxHeight = calculateMaxTabHeight(tabPlacement); 1328 calcRect.width -= tabAreaInsets.left + tabAreaInsets.right; 1329 int width = 0; 1330 int runWidth = tabAreaInsets.left + insets.left; 1331 int top = insets.top + tabAreaInsets.top; 1332 for (int i = 0; i < tabCount; i++) 1333 { 1334 width = calculateTabWidth(tabPlacement, i, fm); 1335 1336 // The proper instances should exists because 1337 // assureRectsCreated() was being run already. 1338 rects[i].setBounds(runWidth, top, width, maxHeight); 1339 1340 runWidth += width; 1341 } 1342 tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right; 1343 tabAreaRect.height = maxTabHeight + tabAreaInsets.top 1344 + tabAreaInsets.bottom; 1345 contentRect.width = tabAreaRect.width; 1346 contentRect.height = tabPane.getHeight() - insets.top 1347 - insets.bottom - tabAreaRect.height; 1348 contentRect.x = insets.left; 1349 tabAreaRect.x = insets.left; 1350 if (tabPlacement == SwingConstants.BOTTOM) 1351 { 1352 contentRect.y = insets.top; 1353 tabAreaRect.y = contentRect.y + contentRect.height; 1354 } 1355 else 1356 { 1357 tabAreaRect.y = insets.top; 1358 contentRect.y = tabAreaRect.y + tabAreaRect.height; 1359 } 1360 } 1361 else 1362 { 1363 int maxWidth = calculateMaxTabWidth(tabPlacement); 1364 1365 calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom; 1366 int height = 0; 1367 int runHeight = tabAreaInsets.top + insets.top; 1368 int fontHeight = fm.getHeight(); 1369 int left = insets.left + tabAreaInsets.left; 1370 for (int i = 0; i < tabCount; i++) 1371 { 1372 height = calculateTabHeight(tabPlacement, i, fontHeight); 1373 1374 // The proper instances should exists because 1375 // assureRectsCreated() was being run already. 1376 rects[i].setBounds(left, runHeight, maxWidth, height); 1377 runHeight += height; 1378 } 1379 tabAreaRect.width = maxTabWidth + tabAreaInsets.left 1380 + tabAreaInsets.right; 1381 tabAreaRect.height = tabPane.getHeight() - insets.top 1382 - insets.bottom; 1383 tabAreaRect.y = insets.top; 1384 contentRect.width = tabPane.getWidth() - insets.left - insets.right 1385 - tabAreaRect.width; 1386 contentRect.height = tabAreaRect.height; 1387 contentRect.y = insets.top; 1388 if (tabPlacement == SwingConstants.LEFT) 1389 { 1390 tabAreaRect.x = insets.left; 1391 contentRect.x = tabAreaRect.x + tabAreaRect.width; 1392 } 1393 else 1394 { 1395 contentRect.x = insets.left; 1396 tabAreaRect.x = contentRect.x + contentRect.width; 1397 } 1398 } 1399 1400 // Unlike the behavior in the WRAP_TAB_LAYOUT the selected 1401 // tab is not padded specially. 1402 } 1403 1404 /** 1405 * This method is called when the JTabbedPane is laid out in 1406 * SCROLL_TAB_LAYOUT. It finds the position for all components in the 1407 * JTabbedPane. 1408 * 1409 * @param pane The JTabbedPane to be laid out. 1410 */ layoutContainer(Container pane)1411 public void layoutContainer(Container pane) 1412 { 1413 super.layoutContainer(pane); 1414 int tabCount = tabPane.getTabCount(); 1415 if (tabCount == 0) 1416 return; 1417 int tabPlacement = tabPane.getTabPlacement(); 1418 1419 if (tabPlacement == SwingConstants.TOP 1420 || tabPlacement == SwingConstants.BOTTOM) 1421 { 1422 if (tabAreaRect.x + tabAreaRect.width < rects[tabCount - 1].x 1423 + rects[tabCount - 1].width) 1424 { 1425 Dimension incrDims = incrButton.getPreferredSize(); 1426 Dimension decrDims = decrButton.getPreferredSize(); 1427 1428 if (tabPlacement == SwingConstants.BOTTOM) 1429 { 1430 // Align scroll buttons with the bottom border of the tabbed 1431 // pane's content area. 1432 decrButton.setBounds(tabAreaRect.x + tabAreaRect.width 1433 - incrDims.width - decrDims.width, 1434 tabAreaRect.y, decrDims.width, 1435 decrDims.height); 1436 incrButton.setBounds(tabAreaRect.x + tabAreaRect.width 1437 - incrDims.width, tabAreaRect.y, 1438 incrDims.width, incrDims.height); 1439 } 1440 else 1441 { 1442 // Align scroll buttons with the top border of the tabbed 1443 // pane's content area. 1444 decrButton.setBounds(tabAreaRect.x + tabAreaRect.width 1445 - incrDims.width - decrDims.width, 1446 tabAreaRect.y + tabAreaRect.height 1447 - decrDims.height, decrDims.width, 1448 decrDims.height); 1449 incrButton.setBounds(tabAreaRect.x + tabAreaRect.width 1450 - incrDims.width, 1451 tabAreaRect.y + tabAreaRect.height 1452 - incrDims.height, 1453 incrDims.width, incrDims.height); 1454 } 1455 1456 tabAreaRect.width -= decrDims.width + incrDims.width; 1457 1458 updateButtons(); 1459 1460 incrButton.setVisible(true); 1461 decrButton.setVisible(true); 1462 } 1463 else 1464 { 1465 incrButton.setVisible(false); 1466 decrButton.setVisible(false); 1467 1468 currentScrollOffset = 0; 1469 currentScrollLocation = 0; 1470 } 1471 } 1472 1473 if (tabPlacement == SwingConstants.LEFT 1474 || tabPlacement == SwingConstants.RIGHT) 1475 { 1476 if (tabAreaRect.y + tabAreaRect.height < rects[tabCount - 1].y 1477 + rects[tabCount - 1].height) 1478 { 1479 Dimension incrDims = incrButton.getPreferredSize(); 1480 Dimension decrDims = decrButton.getPreferredSize(); 1481 1482 if (tabPlacement == SwingConstants.RIGHT) 1483 { 1484 // Align scroll buttons with the right border of the tabbed 1485 // pane's content area. 1486 decrButton.setBounds(tabAreaRect.x, 1487 tabAreaRect.y + tabAreaRect.height 1488 - incrDims.height - decrDims.height, 1489 decrDims.width, decrDims.height); 1490 incrButton.setBounds(tabAreaRect.x, 1491 tabAreaRect.y + tabAreaRect.height 1492 - incrDims.height, incrDims.width, 1493 incrDims.height); 1494 } 1495 else 1496 { 1497 // Align scroll buttons with the left border of the tabbed 1498 // pane's content area. 1499 decrButton.setBounds(tabAreaRect.x + tabAreaRect.width 1500 - decrDims.width, 1501 tabAreaRect.y + tabAreaRect.height 1502 - incrDims.height - decrDims.height, 1503 decrDims.width, decrDims.height); 1504 incrButton.setBounds(tabAreaRect.x + tabAreaRect.width 1505 - incrDims.width, 1506 tabAreaRect.y + tabAreaRect.height 1507 - incrDims.height, incrDims.width, 1508 incrDims.height); 1509 } 1510 1511 tabAreaRect.height -= decrDims.height + incrDims.height; 1512 1513 incrButton.setVisible(true); 1514 decrButton.setVisible(true); 1515 } 1516 else 1517 { 1518 incrButton.setVisible(false); 1519 decrButton.setVisible(false); 1520 1521 currentScrollOffset = 0; 1522 currentScrollLocation = 0; 1523 } 1524 } 1525 viewport.setBounds(tabAreaRect.x, tabAreaRect.y, tabAreaRect.width, 1526 tabAreaRect.height); 1527 1528 updateViewPosition(); 1529 1530 viewport.repaint(); 1531 } 1532 } 1533 1534 /** 1535 * This class handles ChangeEvents from the JTabbedPane. 1536 * 1537 * @specnote Apparently this class was intended to be protected, 1538 * but was made public by a compiler bug and is now 1539 * public for compatibility. 1540 */ 1541 public class TabSelectionHandler implements ChangeListener 1542 { 1543 /** 1544 * This method is called whenever a ChangeEvent is fired from the 1545 * JTabbedPane. 1546 * 1547 * @param e The ChangeEvent fired. 1548 */ stateChanged(ChangeEvent e)1549 public void stateChanged(ChangeEvent e) 1550 { 1551 selectedRun = getRunForTab(tabPane.getTabCount(), 1552 tabPane.getSelectedIndex()); 1553 1554 if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) 1555 tabPane.revalidate(); 1556 tabPane.repaint(); 1557 } 1558 } 1559 1560 /** 1561 * This helper class is a JPanel that fits inside the ScrollViewport. This 1562 * panel's sole job is to paint the tab rectangles inside the viewport so 1563 * that it's clipped correctly. 1564 */ 1565 private class ScrollingPanel extends JPanel 1566 { 1567 /** 1568 * This is a private UI class for our panel. 1569 */ 1570 private class ScrollingPanelUI extends BasicPanelUI 1571 { 1572 /** 1573 * This method overrides the default paint method. It paints the tab 1574 * rectangles for the JTabbedPane in the panel. 1575 * 1576 * @param g The Graphics object to paint with. 1577 * @param c The JComponent to paint. 1578 */ paint(Graphics g, JComponent c)1579 public void paint(Graphics g, JComponent c) 1580 { 1581 int placement = tabPane.getTabPlacement(); 1582 g.setColor(highlight); 1583 if (placement == SwingUtilities.TOP 1584 || placement == SwingUtilities.BOTTOM) 1585 g.fillRect(currentScrollOffset, 0, 1586 tabAreaRect.width, tabAreaRect.height); 1587 else 1588 g.fillRect(0, currentScrollOffset, 1589 tabAreaRect.width, tabAreaRect.height); 1590 1591 paintTabArea(g, placement, tabPane.getSelectedIndex()); 1592 } 1593 } 1594 1595 /** 1596 * This method overrides the updateUI method. It makes the default UI for 1597 * this ScrollingPanel to be a ScrollingPanelUI. 1598 */ updateUI()1599 public void updateUI() 1600 { 1601 setUI(new ScrollingPanelUI()); 1602 } 1603 } 1604 1605 /** 1606 * This is a helper class that paints the panel that paints tabs. This 1607 * custom JViewport is used so that the tabs painted in the panel will be 1608 * clipped. This class implements UIResource so tabs are not added when 1609 * this objects of this class are added to the JTabbedPane. 1610 */ 1611 private class ScrollingViewport extends JViewport implements UIResource 1612 { 1613 // TODO: Maybe remove this inner class. 1614 } 1615 1616 /** 1617 * This is a helper class that implements UIResource so it is not added as a 1618 * tab when an object of this class is added to the JTabbedPane. 1619 */ 1620 private class ScrollingButton extends BasicArrowButton implements UIResource 1621 { 1622 /** 1623 * Creates a ScrollingButton given the direction. 1624 * 1625 * @param dir The direction to point in. 1626 */ ScrollingButton(int dir)1627 public ScrollingButton(int dir) 1628 { 1629 super(dir); 1630 } 1631 } 1632 1633 /** The button that increments the current scroll location. 1634 * This is package-private to avoid an accessor method. */ 1635 transient ScrollingButton incrButton; 1636 1637 /** The button that decrements the current scroll location. 1638 * This is package-private to avoid an accessor method. */ 1639 transient ScrollingButton decrButton; 1640 1641 /** The viewport used to display the tabs. 1642 * This is package-private to avoid an accessor method. */ 1643 transient ScrollingViewport viewport; 1644 1645 /** The panel inside the viewport that paints the tabs. 1646 * This is package-private to avoid an accessor method. */ 1647 transient ScrollingPanel panel; 1648 1649 /** The starting visible tab in the run in SCROLL_TAB_MODE. 1650 * This is package-private to avoid an accessor method. */ 1651 transient int currentScrollLocation; 1652 1653 transient int currentScrollOffset; 1654 1655 /** A reusable rectangle. */ 1656 protected Rectangle calcRect; 1657 1658 /** An array of Rectangles keeping track of the tabs' area and position. */ 1659 protected Rectangle[] rects; 1660 1661 /** The insets around the content area. */ 1662 protected Insets contentBorderInsets; 1663 1664 /** The extra insets around the selected tab. */ 1665 protected Insets selectedTabPadInsets; 1666 1667 /** The insets around the tab area. */ 1668 protected Insets tabAreaInsets; 1669 1670 /** The insets around each and every tab. */ 1671 protected Insets tabInsets; 1672 1673 /** 1674 * The outer bottom and right edge color for both the tab and content 1675 * border. 1676 */ 1677 protected Color darkShadow; 1678 1679 /** The color of the focus outline on the selected tab. */ 1680 protected Color focus; 1681 1682 /** FIXME: find a use for this. */ 1683 protected Color highlight; 1684 1685 /** The top and left edge color for both the tab and content border. */ 1686 protected Color lightHighlight; 1687 1688 /** The inner bottom and right edge color for the tab and content border. */ 1689 protected Color shadow; 1690 1691 /** The maximum tab height. */ 1692 protected int maxTabHeight; 1693 1694 /** The maximum tab width. */ 1695 protected int maxTabWidth; 1696 1697 /** The number of runs in the JTabbedPane. */ 1698 protected int runCount; 1699 1700 /** The index of the run that the selected index is in. */ 1701 protected int selectedRun; 1702 1703 /** The amount of space each run overlaps the previous by. */ 1704 protected int tabRunOverlay; 1705 1706 /** The gap between text and label */ 1707 protected int textIconGap; 1708 1709 /** This array keeps track of which tabs are in which run. 1710 * <p>The value at index i denotes the index of the first tab in run i.</p> 1711 * <p>If the value for any index (i > 0) is 0 then (i - 1) is the last 1712 * run.</p> 1713 */ 1714 protected int[] tabRuns; 1715 1716 /** 1717 * Indicates if the layout of the tab runs is ok or not. This is package 1718 * private to avoid a synthetic accessor method. 1719 */ 1720 boolean tabRunsDirty; 1721 1722 /** 1723 * This is the keystroke for moving down. 1724 * 1725 * @deprecated 1.3 1726 */ 1727 protected KeyStroke downKey; 1728 1729 /** 1730 * This is the keystroke for moving left. 1731 * 1732 * @deprecated 1.3 1733 */ 1734 protected KeyStroke leftKey; 1735 1736 /** 1737 * This is the keystroke for moving right. 1738 * 1739 * @deprecated 1.3 1740 */ 1741 protected KeyStroke rightKey; 1742 1743 /** 1744 * This is the keystroke for moving up. 1745 * 1746 * @deprecated 1.3 1747 */ 1748 protected KeyStroke upKey; 1749 1750 /** The listener that listens for focus events. */ 1751 protected FocusListener focusListener; 1752 1753 /** The listener that listens for mouse events. */ 1754 protected MouseListener mouseListener; 1755 1756 /** The listener that listens for property change events. */ 1757 protected PropertyChangeListener propertyChangeListener; 1758 1759 /** The listener that listens for change events. */ 1760 protected ChangeListener tabChangeListener; 1761 1762 /** The tab pane that this UI paints. */ 1763 protected JTabbedPane tabPane; 1764 1765 /** The current layout manager for the tabPane. 1766 * This is package-private to avoid an accessor method. */ 1767 transient LayoutManager layoutManager; 1768 1769 /** The rectangle that describes the tab area's position and size. 1770 * This is package-private to avoid an accessor method. */ 1771 transient Rectangle tabAreaRect; 1772 1773 /** The rectangle that describes the content area's position and 1774 * size. This is package-private to avoid an accessor method. */ 1775 transient Rectangle contentRect; 1776 1777 /** 1778 * The index over which the mouse is currently moving. 1779 */ 1780 private int rolloverTab; 1781 1782 /** 1783 * Determines if tabs are painted opaque or not. This can be adjusted using 1784 * the UIManager property 'TabbedPane.tabsOpaque'. 1785 */ 1786 private boolean tabsOpaque; 1787 1788 /** 1789 * The currently visible component. 1790 */ 1791 private Component visibleComponent; 1792 1793 private Color selectedColor; 1794 1795 private Rectangle tempTextRect = new Rectangle(); 1796 1797 private Rectangle tempIconRect = new Rectangle(); 1798 1799 /** 1800 * Creates a new BasicTabbedPaneUI object. 1801 */ BasicTabbedPaneUI()1802 public BasicTabbedPaneUI() 1803 { 1804 super(); 1805 rects = new Rectangle[0]; 1806 tabRuns = new int[10]; 1807 } 1808 1809 /** 1810 * This method creates a ScrollingButton that points in the appropriate 1811 * direction for an increasing button. 1812 * This is package-private to avoid an accessor method. 1813 * 1814 * @return The increase ScrollingButton. 1815 */ createIncreaseButton()1816 ScrollingButton createIncreaseButton() 1817 { 1818 if (incrButton == null) 1819 incrButton = new ScrollingButton(SwingConstants.NORTH); 1820 if (tabPane.getTabPlacement() == SwingConstants.TOP 1821 || tabPane.getTabPlacement() == SwingConstants.BOTTOM) 1822 incrButton.setDirection(SwingConstants.EAST); 1823 else 1824 incrButton.setDirection(SwingConstants.SOUTH); 1825 return incrButton; 1826 } 1827 1828 /** 1829 * This method creates a ScrollingButton that points in the appropriate 1830 * direction for a decreasing button. 1831 * This is package-private to avoid an accessor method. 1832 * 1833 * @return The decrease ScrollingButton. 1834 */ createDecreaseButton()1835 ScrollingButton createDecreaseButton() 1836 { 1837 if (decrButton == null) 1838 decrButton = new ScrollingButton(SwingConstants.SOUTH); 1839 if (tabPane.getTabPlacement() == SwingConstants.TOP 1840 || tabPane.getTabPlacement() == SwingConstants.BOTTOM) 1841 decrButton.setDirection(SwingConstants.WEST); 1842 else 1843 decrButton.setDirection(SwingConstants.NORTH); 1844 return decrButton; 1845 } 1846 1847 /** 1848 * This method finds the point to set the view position at given the index 1849 * of a tab. The tab will be the first visible tab in the run. 1850 * This is package-private to avoid an accessor method. 1851 * 1852 * @param index The index of the first visible tab. 1853 * 1854 * @return The position of the first visible tab. 1855 */ findPointForIndex(int index)1856 Point findPointForIndex(int index) 1857 { 1858 int tabPlacement = tabPane.getTabPlacement(); 1859 int selectedIndex = tabPane.getSelectedIndex(); 1860 Insets insets = getSelectedTabPadInsets(tabPlacement); 1861 int w = 0; 1862 int h = 0; 1863 1864 if (tabPlacement == TOP || tabPlacement == BOTTOM) 1865 { 1866 if (index > 0) 1867 { 1868 w += rects[index - 1].x + rects[index - 1].width; 1869 if (index > selectedIndex) 1870 w -= insets.left + insets.right; 1871 } 1872 } 1873 1874 else 1875 { 1876 if (index > 0) 1877 { 1878 h += rects[index - 1].y + rects[index - 1].height; 1879 if (index > selectedIndex) 1880 h -= insets.top + insets.bottom; 1881 } 1882 } 1883 1884 Point p = new Point(w, h); 1885 return p; 1886 } 1887 1888 /** TabbedPanes in scrolling mode should use this method to 1889 * scroll properly to the tab given by the index argument. 1890 * 1891 * @param index The tab to scroll to. 1892 * @param placement The tab's placement. 1893 */ scrollTab(int index, int placement)1894 final void scrollTab(int index, int placement) 1895 { 1896 int diff; 1897 if (index >= 0 && tabPane.isEnabledAt(index)) 1898 { 1899 // If the user clicked on the last tab and that one was 1900 // only partially visible shift the scroll offset to make 1901 // it completely visible. 1902 switch (placement) 1903 { 1904 case JTabbedPane.TOP: 1905 case JTabbedPane.BOTTOM: 1906 if ((diff = rects[index].x 1907 + rects[index].width 1908 - decrButton.getX() - currentScrollOffset) > 0) 1909 currentScrollOffset += diff; 1910 else if ((diff = rects[index].x - currentScrollOffset) < 0) 1911 { 1912 if (index == 0) 1913 currentScrollOffset = 0; 1914 else 1915 currentScrollOffset += diff; 1916 } 1917 1918 currentScrollLocation = tabForCoordinate(tabPane, 1919 currentScrollOffset, 1920 rects[index].y); 1921 break; 1922 default: 1923 if ((diff = rects[index].y + rects[index].height 1924 - decrButton.getY() - currentScrollOffset) > 0) 1925 currentScrollOffset += diff; 1926 else if ((diff = rects[index].y - currentScrollOffset) < 0) 1927 { 1928 if (index == 0) 1929 currentScrollOffset = 0; 1930 else 1931 currentScrollOffset += diff; 1932 } 1933 1934 currentScrollLocation = tabForCoordinate(tabPane, 1935 rects[index].x, 1936 currentScrollOffset); 1937 } 1938 1939 updateViewPosition(); 1940 updateButtons(); 1941 } 1942 } 1943 1944 /** Sets the enabled state of the increase and decrease button 1945 * according to the current scrolling offset and tab pane width 1946 * (or height in TOP/BOTTOM placement). 1947 */ updateButtons()1948 final void updateButtons() 1949 { 1950 int tc = tabPane.getTabCount(); 1951 1952 // The increase button should be enabled as long as the 1953 // right/bottom border of the last tab is under the left/top 1954 // border of the decrease button. 1955 switch (tabPane.getTabPlacement()) 1956 { 1957 case JTabbedPane.BOTTOM: 1958 case JTabbedPane.TOP: 1959 incrButton.setEnabled(currentScrollLocation + 1 < tc 1960 && rects[tc-1].x + rects[tc-1].width 1961 - currentScrollOffset > decrButton.getX()); 1962 break; 1963 default: 1964 incrButton.setEnabled(currentScrollLocation + 1 < tc 1965 && rects[tc-1].y + rects[tc-1].height 1966 - currentScrollOffset > decrButton.getY()); 1967 } 1968 1969 // The decrease button is enabled when the tab pane is scrolled in any way. 1970 decrButton.setEnabled(currentScrollOffset > 0); 1971 1972 } 1973 1974 /** 1975 * Updates the position of the scrolling viewport's view 1976 * according to the current scroll offset. 1977 */ updateViewPosition()1978 final void updateViewPosition() 1979 { 1980 Point p = viewport.getViewPosition(); 1981 1982 // The unneeded coordinate must be set to zero 1983 // in order to correctly handle placement changes. 1984 switch (tabPane.getTabPlacement()) 1985 { 1986 case JTabbedPane.LEFT: 1987 case JTabbedPane.RIGHT: 1988 p.x = 0; 1989 p.y = currentScrollOffset; 1990 break; 1991 default: 1992 p.x = currentScrollOffset; 1993 p.y = 0; 1994 } 1995 1996 viewport.setViewPosition(p); 1997 } 1998 1999 /** 2000 * This method creates a new BasicTabbedPaneUI. 2001 * 2002 * @param c The JComponent to create a UI for. 2003 * 2004 * @return A new BasicTabbedPaneUI. 2005 */ createUI(JComponent c)2006 public static ComponentUI createUI(JComponent c) 2007 { 2008 return new BasicTabbedPaneUI(); 2009 } 2010 2011 /** 2012 * This method installs the UI for the given JComponent. 2013 * 2014 * @param c The JComponent to install the UI for. 2015 */ installUI(JComponent c)2016 public void installUI(JComponent c) 2017 { 2018 super.installUI(c); 2019 if (c instanceof JTabbedPane) 2020 { 2021 tabPane = (JTabbedPane) c; 2022 2023 installComponents(); 2024 installDefaults(); 2025 installListeners(); 2026 installKeyboardActions(); 2027 2028 layoutManager = createLayoutManager(); 2029 tabPane.setLayout(layoutManager); 2030 } 2031 } 2032 2033 /** 2034 * This method uninstalls the UI for the given JComponent. 2035 * 2036 * @param c The JComponent to uninstall the UI for. 2037 */ uninstallUI(JComponent c)2038 public void uninstallUI(JComponent c) 2039 { 2040 layoutManager = null; 2041 2042 uninstallKeyboardActions(); 2043 uninstallListeners(); 2044 uninstallDefaults(); 2045 uninstallComponents(); 2046 2047 tabPane = null; 2048 } 2049 2050 /** 2051 * This method creates the appropriate layout manager for the JTabbedPane's 2052 * current tab layout policy. If the tab layout policy is 2053 * SCROLL_TAB_LAYOUT, then all the associated components that need to be 2054 * created will be done so now. 2055 * 2056 * @return A layout manager given the tab layout policy. 2057 */ createLayoutManager()2058 protected LayoutManager createLayoutManager() 2059 { 2060 if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) 2061 return new TabbedPaneLayout(); 2062 else 2063 { 2064 runCount = 1; 2065 tabRuns[0] = 0; 2066 2067 incrButton = createIncreaseButton(); 2068 incrButton.addMouseListener(mouseListener); 2069 2070 decrButton = createDecreaseButton(); 2071 decrButton.addMouseListener(mouseListener); 2072 decrButton.setEnabled(false); 2073 2074 panel = new ScrollingPanel(); 2075 panel.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE); 2076 panel.addMouseListener(mouseListener); 2077 panel.addFocusListener(focusListener); 2078 2079 viewport = new ScrollingViewport(); 2080 viewport.setBackground(Color.LIGHT_GRAY); 2081 viewport.setView(panel); 2082 viewport.setLayout(null); 2083 2084 tabPane.add(incrButton); 2085 tabPane.add(decrButton); 2086 tabPane.add(viewport); 2087 2088 return new TabbedPaneScrollLayout(); 2089 } 2090 } 2091 2092 /** 2093 * This method installs components for this JTabbedPane. 2094 */ installComponents()2095 protected void installComponents() 2096 { 2097 // Nothing to be done. 2098 } 2099 2100 /** 2101 * This method uninstalls components for this JTabbedPane. 2102 */ uninstallComponents()2103 protected void uninstallComponents() 2104 { 2105 if (incrButton != null) 2106 tabPane.remove(incrButton); 2107 2108 if (decrButton != null) 2109 tabPane.remove(decrButton); 2110 2111 if (viewport != null) 2112 tabPane.remove(viewport); 2113 } 2114 2115 /** 2116 * This method installs defaults for the Look and Feel. 2117 */ installDefaults()2118 protected void installDefaults() 2119 { 2120 LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background", 2121 "TabbedPane.foreground", 2122 "TabbedPane.font"); 2123 tabPane.setOpaque(false); 2124 2125 lightHighlight = UIManager.getColor("TabbedPane.highlight"); 2126 highlight = UIManager.getColor("TabbedPane.light"); 2127 2128 shadow = UIManager.getColor("TabbedPane.shadow"); 2129 darkShadow = UIManager.getColor("TabbedPane.darkShadow"); 2130 2131 focus = UIManager.getColor("TabbedPane.focus"); 2132 2133 textIconGap = UIManager.getInt("TabbedPane.textIconGap"); 2134 tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay"); 2135 2136 tabInsets = UIManager.getInsets("TabbedPane.tabInsets"); 2137 selectedTabPadInsets 2138 = UIManager.getInsets("TabbedPane.selectedTabPadInsets"); 2139 tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets"); 2140 contentBorderInsets 2141 = UIManager.getInsets("TabbedPane.contentBorderInsets"); 2142 tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque"); 2143 2144 // Although 'TabbedPane.contentAreaColor' is not defined in the defaults 2145 // of BasicLookAndFeel it is used by this class. 2146 selectedColor = UIManager.getColor("TabbedPane.contentAreaColor"); 2147 if (selectedColor == null) 2148 selectedColor = UIManager.getColor("control"); 2149 2150 calcRect = new Rectangle(); 2151 tabRuns = new int[10]; 2152 tabAreaRect = new Rectangle(); 2153 contentRect = new Rectangle(); 2154 } 2155 2156 /** 2157 * This method uninstalls defaults for the Look and Feel. 2158 */ uninstallDefaults()2159 protected void uninstallDefaults() 2160 { 2161 calcRect = null; 2162 tabAreaRect = null; 2163 contentRect = null; 2164 tabRuns = null; 2165 2166 tempIconRect = null; 2167 tempTextRect = null; 2168 2169 contentBorderInsets = null; 2170 tabAreaInsets = null; 2171 selectedTabPadInsets = null; 2172 tabInsets = null; 2173 2174 focus = null; 2175 darkShadow = null; 2176 shadow = null; 2177 lightHighlight = null; 2178 highlight = null; 2179 2180 selectedColor = null; 2181 } 2182 2183 /** 2184 * This method creates and installs the listeners for this UI. 2185 */ installListeners()2186 protected void installListeners() 2187 { 2188 mouseListener = createMouseListener(); 2189 tabChangeListener = createChangeListener(); 2190 propertyChangeListener = createPropertyChangeListener(); 2191 focusListener = createFocusListener(); 2192 2193 tabPane.addMouseListener(mouseListener); 2194 tabPane.addChangeListener(tabChangeListener); 2195 tabPane.addPropertyChangeListener(propertyChangeListener); 2196 tabPane.addFocusListener(focusListener); 2197 } 2198 2199 /** 2200 * This method removes and nulls the listeners for this UI. 2201 */ uninstallListeners()2202 protected void uninstallListeners() 2203 { 2204 tabPane.removeFocusListener(focusListener); 2205 tabPane.removePropertyChangeListener(propertyChangeListener); 2206 tabPane.removeChangeListener(tabChangeListener); 2207 tabPane.removeMouseListener(mouseListener); 2208 2209 if (incrButton != null) 2210 incrButton.removeMouseListener(mouseListener); 2211 2212 if (decrButton != null) 2213 decrButton.removeMouseListener(mouseListener); 2214 2215 if (panel != null) 2216 { 2217 panel.removeMouseListener(mouseListener); 2218 panel.removeFocusListener(focusListener); 2219 } 2220 2221 focusListener = null; 2222 propertyChangeListener = null; 2223 tabChangeListener = null; 2224 mouseListener = null; 2225 } 2226 2227 /** 2228 * This method creates a new MouseListener. 2229 * 2230 * @return A new MouseListener. 2231 */ createMouseListener()2232 protected MouseListener createMouseListener() 2233 { 2234 return new MouseHandler(); 2235 } 2236 2237 /** 2238 * This method creates a new FocusListener. 2239 * 2240 * @return A new FocusListener. 2241 */ createFocusListener()2242 protected FocusListener createFocusListener() 2243 { 2244 return new FocusHandler(); 2245 } 2246 2247 /** 2248 * This method creates a new ChangeListener. 2249 * 2250 * @return A new ChangeListener. 2251 */ createChangeListener()2252 protected ChangeListener createChangeListener() 2253 { 2254 return new TabSelectionHandler(); 2255 } 2256 2257 /** 2258 * This method creates a new PropertyChangeListener. 2259 * 2260 * @return A new PropertyChangeListener. 2261 */ createPropertyChangeListener()2262 protected PropertyChangeListener createPropertyChangeListener() 2263 { 2264 return new PropertyChangeHandler(); 2265 } 2266 2267 /** 2268 * This method installs keyboard actions for the JTabbedPane. 2269 */ installKeyboardActions()2270 protected void installKeyboardActions() 2271 { 2272 InputMap keyMap = (InputMap) UIManager.get("TabbedPane.focusInputMap"); 2273 SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, keyMap); 2274 2275 keyMap = (InputMap) UIManager.get("TabbedPane.ancestorInputMap"); 2276 SwingUtilities 2277 .replaceUIInputMap(tabPane, 2278 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, 2279 keyMap); 2280 2281 ActionMap map = getActionMap(); 2282 SwingUtilities.replaceUIActionMap(tabPane, map); 2283 } 2284 2285 /** 2286 * This method uninstalls keyboard actions for the JTabbedPane. 2287 */ uninstallKeyboardActions()2288 protected void uninstallKeyboardActions() 2289 { 2290 SwingUtilities.replaceUIActionMap(tabPane, null); 2291 SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, null); 2292 SwingUtilities 2293 .replaceUIInputMap(tabPane, 2294 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, 2295 null); 2296 } 2297 2298 /** 2299 * This method returns the minimum size of the JTabbedPane. 2300 * 2301 * @param c The JComponent to find a size for. 2302 * 2303 * @return The minimum size. 2304 */ getMinimumSize(JComponent c)2305 public Dimension getMinimumSize(JComponent c) 2306 { 2307 return layoutManager.minimumLayoutSize(tabPane); 2308 } 2309 2310 /** 2311 * This method returns the maximum size of the JTabbedPane. 2312 * 2313 * @param c The JComponent to find a size for. 2314 * 2315 * @return The maximum size. 2316 */ getMaximumSize(JComponent c)2317 public Dimension getMaximumSize(JComponent c) 2318 { 2319 return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE); 2320 } 2321 2322 /** 2323 * This method paints the JTabbedPane. 2324 * 2325 * @param g The Graphics object to paint with. 2326 * @param c The JComponent to paint. 2327 */ paint(Graphics g, JComponent c)2328 public void paint(Graphics g, JComponent c) 2329 { 2330 if (!tabPane.isValid()) 2331 tabPane.validate(); 2332 2333 if (tabPane.getTabCount() == 0) 2334 return; 2335 2336 int index = tabPane.getSelectedIndex(); 2337 if (index < 0) 2338 index = 0; 2339 2340 int tabPlacement = tabPane.getTabPlacement(); 2341 2342 // Paint the tab area only in WRAP_TAB_LAYOUT Mode from this method 2343 // because it is done through the ScrollingViewport.paint() method 2344 // for the SCROLL_TAB_LAYOUT mode. 2345 if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) 2346 { 2347 g.setColor(highlight); 2348 g.fillRect(tabAreaRect.x, tabAreaRect.y, 2349 tabAreaRect.width, tabAreaRect.height); 2350 paintTabArea(g, tabPlacement, index); 2351 } 2352 2353 paintContentBorder(g, tabPlacement, index); 2354 } 2355 2356 /** 2357 * This method paints the tab area. This includes painting the rectangles 2358 * that make up the tabs. 2359 * 2360 * @param g The Graphics object to paint with. 2361 * @param tabPlacement The JTabbedPane's tab placement. 2362 * @param selectedIndex The selected index. 2363 */ paintTabArea(Graphics g, int tabPlacement, int selectedIndex)2364 protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) 2365 { 2366 // Please note: the ordering of the painting is important. 2367 // we WANT to paint the outermost run first and then work our way in. 2368 2369 // The following drawing code works for both tab layouts. 2370 int tabCount = tabPane.getTabCount(); 2371 2372 for (int i = runCount - 1; i >= 0; --i) 2373 { 2374 int start = tabRuns[i]; 2375 int next; 2376 if (i == runCount - 1) 2377 next = tabRuns[0]; 2378 else 2379 next = tabRuns[i + 1]; 2380 int end = next != 0 ? next - 1 : tabCount - 1; 2381 for (int j = start; j <= end; ++j) 2382 { 2383 if (j != selectedIndex) 2384 { 2385 paintTab(g, tabPlacement, rects, j, 2386 tempIconRect, tempTextRect); 2387 } 2388 } 2389 } 2390 2391 // Paint selected tab in front of every other tab. 2392 if (selectedIndex >= 0) 2393 paintTab(g, tabPlacement, rects, selectedIndex, 2394 tempIconRect, tempTextRect); 2395 } 2396 2397 /** 2398 * This method paints an individual tab. 2399 * 2400 * @param g The Graphics object to paint with. 2401 * @param tabPlacement The JTabbedPane's tab placement. 2402 * @param rects The array of rectangles that keep the size and position of 2403 * the tabs. 2404 * @param tabIndex The tab index to paint. 2405 * @param iconRect The rectangle to use for the icon. 2406 * @param textRect The rectangle to use for the text. 2407 */ paintTab(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect)2408 protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects, 2409 int tabIndex, Rectangle iconRect, Rectangle textRect) 2410 { 2411 Rectangle rect = rects[tabIndex]; 2412 boolean isSelected = tabIndex == tabPane.getSelectedIndex(); 2413 // Paint background if necessary. 2414 if (tabsOpaque || tabPane.isOpaque()) 2415 { 2416 paintTabBackground(g, tabPlacement, tabIndex, rect.x, rect.y, 2417 rect.width, rect.height, isSelected); 2418 } 2419 2420 // Paint border. 2421 paintTabBorder(g, tabPlacement, tabIndex, rect.x, rect.y, rect.width, 2422 rect.height, isSelected); 2423 2424 // Layout label. 2425 FontMetrics fm = getFontMetrics(); 2426 Icon icon = getIconForTab(tabIndex); 2427 String title = tabPane.getTitleAt(tabIndex); 2428 layoutLabel(tabPlacement, fm, tabIndex, title, icon, rect, iconRect, 2429 textRect, isSelected); 2430 // Paint the text. 2431 paintText(g, tabPlacement, tabPane.getFont(), fm, tabIndex, title, 2432 textRect, isSelected); 2433 2434 // Paint icon if necessary. 2435 paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected); 2436 2437 // Paint focus indicator. 2438 paintFocusIndicator(g, tabPlacement, rects, tabIndex, iconRect, textRect, 2439 isSelected); 2440 } 2441 2442 /** 2443 * This method lays out the tab and finds the location to paint the icon 2444 * and text. 2445 * 2446 * @param tabPlacement The JTabbedPane's tab placement. 2447 * @param metrics The font metrics for the font to paint with. 2448 * @param tabIndex The tab index to paint. 2449 * @param title The string painted. 2450 * @param icon The icon painted. 2451 * @param tabRect The tab bounds. 2452 * @param iconRect The calculated icon bounds. 2453 * @param textRect The calculated text bounds. 2454 * @param isSelected Whether this tab is selected. 2455 */ layoutLabel(int tabPlacement, FontMetrics metrics, int tabIndex, String title, Icon icon, Rectangle tabRect, Rectangle iconRect, Rectangle textRect, boolean isSelected)2456 protected void layoutLabel(int tabPlacement, FontMetrics metrics, 2457 int tabIndex, String title, Icon icon, 2458 Rectangle tabRect, Rectangle iconRect, 2459 Rectangle textRect, boolean isSelected) 2460 { 2461 // Reset the icon and text rectangles, as the result is not specified 2462 // when the locations are not (0,0). 2463 textRect.x = 0; 2464 textRect.y = 0; 2465 textRect.width = 0; 2466 textRect.height = 0; 2467 iconRect.x = 0; 2468 iconRect.y = 0; 2469 iconRect.width = 0; 2470 iconRect.height = 0; 2471 SwingUtilities.layoutCompoundLabel(tabPane, metrics, title, icon, 2472 SwingConstants.CENTER, 2473 SwingConstants.CENTER, 2474 SwingConstants.CENTER, 2475 SwingConstants.RIGHT, tabRect, 2476 iconRect, textRect, textIconGap); 2477 2478 int shiftX = getTabLabelShiftX(tabPlacement, tabIndex, isSelected); 2479 int shiftY = getTabLabelShiftY(tabPlacement, tabIndex, isSelected); 2480 2481 iconRect.x += shiftX; 2482 iconRect.y += shiftY; 2483 2484 textRect.x += shiftX; 2485 textRect.y += shiftY; 2486 } 2487 2488 /** 2489 * This method paints the icon. 2490 * 2491 * @param g The Graphics object to paint. 2492 * @param tabPlacement The JTabbedPane's tab placement. 2493 * @param tabIndex The tab index to paint. 2494 * @param icon The icon to paint. 2495 * @param iconRect The bounds of the icon. 2496 * @param isSelected Whether this tab is selected. 2497 */ paintIcon(Graphics g, int tabPlacement, int tabIndex, Icon icon, Rectangle iconRect, boolean isSelected)2498 protected void paintIcon(Graphics g, int tabPlacement, int tabIndex, 2499 Icon icon, Rectangle iconRect, boolean isSelected) 2500 { 2501 if (icon != null) 2502 icon.paintIcon(tabPane, g, iconRect.x, iconRect.y); 2503 } 2504 2505 /** 2506 * This method paints the text for the given tab. 2507 * 2508 * @param g The Graphics object to paint with. 2509 * @param tabPlacement The JTabbedPane's tab placement. 2510 * @param font The font to paint with. 2511 * @param metrics The fontmetrics of the given font. 2512 * @param tabIndex The tab index. 2513 * @param title The string to paint. 2514 * @param textRect The bounds of the string. 2515 * @param isSelected Whether this tab is selected. 2516 */ paintText(Graphics g, int tabPlacement, Font font, FontMetrics metrics, int tabIndex, String title, Rectangle textRect, boolean isSelected)2517 protected void paintText(Graphics g, int tabPlacement, Font font, 2518 FontMetrics metrics, int tabIndex, String title, 2519 Rectangle textRect, boolean isSelected) 2520 { 2521 g.setFont(font); 2522 View textView = getTextViewForTab(tabIndex); 2523 if (textView != null) 2524 { 2525 textView.paint(g, textRect); 2526 return; 2527 } 2528 2529 int ascent = metrics.getAscent(); 2530 2531 int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex); 2532 if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) 2533 { 2534 Color fg = tabPane.getForegroundAt(tabIndex); 2535 if (isSelected && (fg instanceof UIResource)) 2536 { 2537 Color selectionForeground = 2538 UIManager.getColor("TabbedPane.selectionForeground"); 2539 if (selectionForeground != null) 2540 fg = selectionForeground; 2541 } 2542 g.setColor(fg); 2543 2544 if (mnemIndex != -1) 2545 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, 2546 textRect.x, 2547 textRect.y + ascent); 2548 else 2549 g.drawString(title, textRect.x, textRect.y + ascent); 2550 } 2551 else 2552 { 2553 Color bg = tabPane.getBackgroundAt(tabIndex); 2554 g.setColor(bg.brighter()); 2555 if (mnemIndex != -1) 2556 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, 2557 textRect.x, textRect.y 2558 + ascent); 2559 else 2560 g.drawString(title, textRect.x, textRect.y + ascent); 2561 2562 g.setColor(bg.darker()); 2563 if (mnemIndex != -1) 2564 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, 2565 textRect.x + 1, 2566 textRect.y + 1 2567 + ascent); 2568 else 2569 g.drawString(title, textRect.x + 1, textRect.y + 1 + ascent); 2570 } 2571 } 2572 2573 /** 2574 * This method returns how much the label for the tab should shift in the X 2575 * direction. 2576 * 2577 * @param tabPlacement The JTabbedPane's tab placement. 2578 * @param tabIndex The tab index being painted. 2579 * @param isSelected Whether this tab is selected. 2580 * 2581 * @return The amount the label should shift by in the X direction. 2582 */ getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected)2583 protected int getTabLabelShiftX(int tabPlacement, int tabIndex, 2584 boolean isSelected) 2585 { 2586 switch (tabPlacement) 2587 { 2588 default: 2589 case SwingUtilities.TOP: 2590 case SwingUtilities.BOTTOM: 2591 return 1; 2592 case SwingUtilities.LEFT: 2593 return (isSelected) ? -1 : 1; 2594 case SwingUtilities.RIGHT: 2595 return (isSelected) ? 1 : -1; 2596 } 2597 } 2598 2599 /** 2600 * This method returns how much the label for the tab should shift in the Y 2601 * direction. 2602 * 2603 * @param tabPlacement The JTabbedPane's tab placement. 2604 * @param tabIndex The tab index being painted. 2605 * @param isSelected Whether this tab is selected. 2606 * 2607 * @return The amount the label should shift by in the Y direction. 2608 */ getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected)2609 protected int getTabLabelShiftY(int tabPlacement, int tabIndex, 2610 boolean isSelected) 2611 { 2612 switch (tabPlacement) 2613 { 2614 default: 2615 case SwingUtilities.TOP: 2616 return (isSelected) ? -1 : 1; 2617 case SwingUtilities.BOTTOM: 2618 return (isSelected) ? 1 : -1; 2619 case SwingUtilities.LEFT: 2620 case SwingUtilities.RIGHT: 2621 return 0; 2622 } 2623 } 2624 2625 /** 2626 * This method paints the focus rectangle around the selected tab. 2627 * 2628 * @param g The Graphics object to paint with. 2629 * @param tabPlacement The JTabbedPane's tab placement. 2630 * @param rects The array of rectangles keeping track of size and position. 2631 * @param tabIndex The tab index. 2632 * @param iconRect The icon bounds. 2633 * @param textRect The text bounds. 2634 * @param isSelected Whether this tab is selected. 2635 */ paintFocusIndicator(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect, boolean isSelected)2636 protected void paintFocusIndicator(Graphics g, int tabPlacement, 2637 Rectangle[] rects, int tabIndex, 2638 Rectangle iconRect, Rectangle textRect, 2639 boolean isSelected) 2640 { 2641 if (tabPane.hasFocus() && isSelected) 2642 { 2643 Rectangle rect = rects[tabIndex]; 2644 // The focus rectangle. 2645 int x; 2646 int y; 2647 int w; 2648 int h; 2649 2650 g.setColor(focus); 2651 switch (tabPlacement) 2652 { 2653 case LEFT: 2654 x = rect.x + 3; 2655 y = rect.y + 3; 2656 w = rect.width - 5; 2657 h = rect.height - 6; 2658 break; 2659 case RIGHT: 2660 x = rect.x + 2; 2661 y = rect.y + 3; 2662 w = rect.width - 6; 2663 h = rect.height - 5; 2664 break; 2665 case BOTTOM: 2666 x = rect.x + 3; 2667 y = rect.y + 2; 2668 w = rect.width - 6; 2669 h = rect.height - 5; 2670 break; 2671 case TOP: 2672 default: 2673 x = rect.x + 3; 2674 y = rect.y + 3; 2675 w = rect.width - 6; 2676 h = rect.height - 5; 2677 } 2678 2679 BasicGraphicsUtils.drawDashedRect(g, x, y, w, h); 2680 } 2681 } 2682 2683 /** 2684 * This method paints the border for an individual tab. 2685 * 2686 * @param g The Graphics object to paint with. 2687 * @param tabPlacement The JTabbedPane's tab placement. 2688 * @param tabIndex The tab index. 2689 * @param x The x position of the tab. 2690 * @param y The y position of the tab. 2691 * @param w The width of the tab. 2692 * @param h The height of the tab. 2693 * @param isSelected Whether the tab is selected. 2694 */ paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected)2695 protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, 2696 int x, int y, int w, int h, boolean isSelected) 2697 { 2698 Color saved = g.getColor(); 2699 2700 switch (tabPlacement) 2701 { 2702 case SwingConstants.TOP: 2703 g.setColor(shadow); 2704 // Inner right line. 2705 g.drawLine(x + w - 2, y + 2, x + w - 2, y + h); 2706 2707 g.setColor(darkShadow); 2708 // Outer right line. 2709 g.drawLine(x + w - 1, y + 2, x + w - 1, y + h); 2710 2711 // Upper right corner. 2712 g.drawLine(x + w - 2, y + 1, x + w - 1, y + 2); 2713 2714 g.setColor(lightHighlight); 2715 2716 // Left line. 2717 g.drawLine(x, y + 3, x, y + h); 2718 2719 // Upper line. 2720 g.drawLine(x + 3, y, x + w - 3, y); 2721 2722 // Upper left corner. 2723 g.drawLine(x, y + 2, x + 2, y); 2724 2725 break; 2726 case SwingConstants.LEFT: 2727 g.setColor(lightHighlight); 2728 // Top line. 2729 g.drawLine(x + 3, y, x + w - 1, y); 2730 2731 // Top left border. 2732 g.drawLine(x + 2, y, x, y + 2); 2733 2734 // Left line. 2735 g.drawLine(x, y + 3, x, y + h - 4); 2736 2737 // Bottom left corner. 2738 g.drawLine(x, y + h - 3, x + 1, y + h - 2); 2739 2740 g.setColor(darkShadow); 2741 // Outer bottom line. 2742 g.drawLine(x + 2, y + h - 1, x + w - 1, y + h - 1); 2743 2744 g.setColor(shadow); 2745 // Inner bottom line. 2746 g.drawLine(x + 2, y + h - 2, x + w - 1, y + h - 2); 2747 2748 break; 2749 case SwingConstants.BOTTOM: 2750 g.setColor(shadow); 2751 // Inner right line. 2752 g.drawLine(x + w - 2, y, x + w - 2, y + h - 2); 2753 2754 // Inner bottom line. 2755 g.drawLine(x + 2, y + h - 1, x + w - 3, y + h - 1); 2756 2757 g.setColor(darkShadow); 2758 // Outer right line. 2759 g.drawLine(x + w - 1, y, x + w - 1, y + h - 3); 2760 2761 // Bottom right corner. 2762 g.drawLine(x + w - 1, y + h - 2, x + w - 3, y + h); 2763 2764 // Bottom line. 2765 g.drawLine(x + 2, y + h, x + w - 4, y + h); 2766 2767 g.setColor(lightHighlight); 2768 // Left line. 2769 g.drawLine(x, y, x, y + h - 3); 2770 2771 // Bottom left corner. 2772 g.drawLine(x, y + h - 2, x + 1, y + h - 1); 2773 break; 2774 case SwingConstants.RIGHT: 2775 g.setColor(lightHighlight); 2776 // Top line. 2777 g.drawLine(x, y, x + w - 3, y); 2778 2779 g.setColor(darkShadow); 2780 // Top right corner. 2781 g.drawLine(x + w - 2, y + 1, x + w - 1, y + 2); 2782 2783 // Outer right line. 2784 g.drawLine(x + w - 1, y + 3, x + w - 1, y + h - 3); 2785 2786 // Bottom right corner. 2787 g.drawLine(x + w - 2, y + h - 2, x + w - 3, y + h - 1); 2788 2789 // Bottom line. 2790 g.drawLine(x, y + h - 1, x + w - 4, y + h - 1); 2791 2792 g.setColor(shadow); 2793 2794 // Inner right line. 2795 g.drawLine(x + w - 2, y + 2, x + w - 2, y + h - 3); 2796 2797 // Inner bottom line. 2798 g.drawLine(x, y + h - 2, x + w - 3, y + h - 2); 2799 2800 break; 2801 } 2802 2803 g.setColor(saved); 2804 } 2805 2806 /** 2807 * This method paints the background for an individual tab. 2808 * 2809 * @param g The Graphics object to paint with. 2810 * @param tabPlacement The JTabbedPane's tab placement. 2811 * @param tabIndex The tab index. 2812 * @param x The x position of the tab. 2813 * @param y The y position of the tab. 2814 * @param w The width of the tab. 2815 * @param h The height of the tab. 2816 * @param isSelected Whether the tab is selected. 2817 */ paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected)2818 protected void paintTabBackground(Graphics g, int tabPlacement, 2819 int tabIndex, int x, int y, int w, int h, 2820 boolean isSelected) 2821 { 2822 Color saved = g.getColor(); 2823 2824 if (isSelected) 2825 g.setColor(selectedColor); 2826 else 2827 { 2828 Color bg = tabPane.getBackgroundAt(tabIndex); 2829 if (bg == null) 2830 bg = Color.LIGHT_GRAY; 2831 g.setColor(bg); 2832 } 2833 2834 switch (tabPlacement) 2835 { 2836 case SwingConstants.TOP: 2837 g.fillRect(x + 1, y + 1, w - 1, h - 1); 2838 break; 2839 case SwingConstants.BOTTOM: 2840 g.fillRect(x, y, w - 1, h - 1); 2841 break; 2842 case SwingConstants.LEFT: 2843 g.fillRect(x + 1, y + 1, w - 1, h - 2); 2844 break; 2845 case SwingConstants.RIGHT: 2846 g.fillRect(x, y + 1, w - 1, h - 2); 2847 break; 2848 } 2849 2850 g.setColor(saved); 2851 } 2852 2853 /** 2854 * This method paints the border around the content area. 2855 * 2856 * @param g The Graphics object to paint with. 2857 * @param tabPlacement The JTabbedPane's tab placement. 2858 * @param selectedIndex The index of the selected tab. 2859 */ paintContentBorder(Graphics g, int tabPlacement, int selectedIndex)2860 protected void paintContentBorder(Graphics g, int tabPlacement, 2861 int selectedIndex) 2862 { 2863 int width = tabPane.getWidth(); 2864 int height = tabPane.getHeight(); 2865 Insets insets = tabPane.getInsets(); 2866 2867 // Calculate coordinates of content area. 2868 int x = insets.left; 2869 int y = insets.top; 2870 int w = width - insets.left - insets.right; 2871 int h = height - insets.top - insets.bottom; 2872 2873 switch (tabPlacement) 2874 { 2875 case LEFT: 2876 x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); 2877 w -= x - insets.left; 2878 break; 2879 case RIGHT: 2880 w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); 2881 break; 2882 case BOTTOM: 2883 h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); 2884 break; 2885 case TOP: 2886 default: 2887 y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); 2888 h -= y - insets.top; 2889 } 2890 2891 // Fill background if necessary. 2892 if (tabPane.isOpaque()) 2893 { 2894 Color bg = UIManager.getColor("TabbedPane.contentAreaColor"); 2895 g.setColor(bg); 2896 g.fillRect(x, y, w, h); 2897 } 2898 2899 // Paint border. 2900 paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h); 2901 paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h); 2902 paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h); 2903 paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h); 2904 } 2905 2906 /** 2907 * This method paints the top edge of the content border. 2908 * 2909 * @param g The Graphics object to paint with. 2910 * @param tabPlacement The JTabbedPane's tab placement. 2911 * @param selectedIndex The selected tab index. 2912 * @param x The x coordinate for the content area. 2913 * @param y The y coordinate for the content area. 2914 * @param w The width of the content area. 2915 * @param h The height of the content area. 2916 */ paintContentBorderTopEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h)2917 protected void paintContentBorderTopEdge(Graphics g, int tabPlacement, 2918 int selectedIndex, int x, int y, 2919 int w, int h) 2920 { 2921 Color saved = g.getColor(); 2922 g.setColor(lightHighlight); 2923 2924 int startgap = rects[selectedIndex].x - currentScrollOffset; 2925 int endgap = rects[selectedIndex].x + rects[selectedIndex].width 2926 - currentScrollOffset; 2927 2928 // Paint the highlight line with a gap if the tabs are at the top 2929 // and the selected tab is inside the visible area. 2930 if (tabPlacement == SwingConstants.TOP && startgap >= 0) 2931 { 2932 g.drawLine(x, y, startgap, y); 2933 g.drawLine(endgap, y, x + w - 1, y); 2934 2935 g.setColor(selectedColor); 2936 g.drawLine(startgap, y, endgap - 1, y); 2937 } 2938 else 2939 g.drawLine(x, y, x + w, y); 2940 2941 g.setColor(selectedColor); 2942 g.drawLine(x, y + 1, x + w - 1, y + 1); 2943 g.drawLine(x, y + 2, x + w - 1, y + 2); 2944 2945 g.setColor(saved); 2946 } 2947 2948 /** 2949 * This method paints the left edge of the content border. 2950 * 2951 * @param g The Graphics object to paint with. 2952 * @param tabPlacement The JTabbedPane's tab placement. 2953 * @param selectedIndex The selected tab index. 2954 * @param x The x coordinate for the content area. 2955 * @param y The y coordinate for the content area. 2956 * @param w The width of the content area. 2957 * @param h The height of the content area. 2958 */ paintContentBorderLeftEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h)2959 protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement, 2960 int selectedIndex, int x, int y, 2961 int w, int h) 2962 { 2963 Color saved = g.getColor(); 2964 g.setColor(lightHighlight); 2965 2966 int startgap = rects[selectedIndex].y - currentScrollOffset; 2967 int endgap = rects[selectedIndex].y + rects[selectedIndex].height 2968 - currentScrollOffset; 2969 2970 if (tabPlacement == SwingConstants.LEFT && startgap >= 0) 2971 { 2972 g.drawLine(x, y, x, startgap); 2973 g.drawLine(x, endgap, x, y + h - 1); 2974 2975 g.setColor(selectedColor); 2976 g.drawLine(x, startgap, x, endgap - 1); 2977 } 2978 else 2979 g.drawLine(x, y, x, y + h - 1); 2980 2981 g.setColor(selectedColor); 2982 g.drawLine(x + 1, y + 1, x + 1, y + h - 4); 2983 2984 g.setColor(saved); 2985 } 2986 2987 /** 2988 * This method paints the bottom edge of the content border. 2989 * 2990 * @param g The Graphics object to paint with. 2991 * @param tabPlacement The JTabbedPane's tab placement. 2992 * @param selectedIndex The selected tab index. 2993 * @param x The x coordinate for the content area. 2994 * @param y The y coordinate for the content area. 2995 * @param w The width of the content area. 2996 * @param h The height of the content area. 2997 */ paintContentBorderBottomEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h)2998 protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement, 2999 int selectedIndex, int x, int y, 3000 int w, int h) 3001 { 3002 Color saved = g.getColor(); 3003 3004 int startgap = rects[selectedIndex].x - currentScrollOffset; 3005 int endgap = rects[selectedIndex].x + rects[selectedIndex].width 3006 - currentScrollOffset; 3007 3008 if (tabPlacement == SwingConstants.BOTTOM && startgap >= 0) 3009 { 3010 g.setColor(shadow); 3011 g.drawLine(x + 1, y + h - 2, startgap, y + h - 2); 3012 g.drawLine(endgap, y + h - 2, x + w - 2, y + h - 2); 3013 3014 g.setColor(darkShadow); 3015 g.drawLine(x, y + h - 1, startgap , y + h - 1); 3016 g.drawLine(endgap, y + h - 1, x + w - 1, y + h - 1); 3017 3018 g.setColor(selectedColor); 3019 g.drawLine(startgap, y + h - 1, endgap - 1, y + h - 1); 3020 g.drawLine(startgap, y + h - 2, endgap - 1, y + h - 2); 3021 } 3022 else 3023 { 3024 g.setColor(shadow); 3025 g.drawLine(x + 1, y + h - 2, x + w - 1, y + h - 2); 3026 g.setColor(darkShadow); 3027 g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); 3028 } 3029 3030 g.setColor(selectedColor); 3031 g.drawLine(x + 1, y + h - 3, x + w - 2, y + h - 3); 3032 3033 g.setColor(saved); 3034 } 3035 3036 /** 3037 * This method paints the right edge of the content border. 3038 * 3039 * @param g The Graphics object to paint with. 3040 * @param tabPlacement The JTabbedPane's tab placement. 3041 * @param selectedIndex The selected tab index. 3042 * @param x The x coordinate for the content area. 3043 * @param y The y coordinate for the content area. 3044 * @param w The width of the content area. 3045 * @param h The height of the content area. 3046 */ paintContentBorderRightEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h)3047 protected void paintContentBorderRightEdge(Graphics g, int tabPlacement, 3048 int selectedIndex, int x, int y, 3049 int w, int h) 3050 { 3051 Color saved = g.getColor(); 3052 int startgap = rects[selectedIndex].y - currentScrollOffset; 3053 int endgap = rects[selectedIndex].y + rects[selectedIndex].height 3054 - currentScrollOffset; 3055 3056 if (tabPlacement == SwingConstants.RIGHT && startgap >= 0) 3057 { 3058 g.setColor(shadow); 3059 g.drawLine(x + w - 2, y + 1, x + w - 2, startgap); 3060 g.drawLine(x + w - 2, endgap, x + w - 2, y + h - 2); 3061 3062 g.setColor(darkShadow); 3063 g.drawLine(x + w - 1, y, x + w - 1, startgap); 3064 g.drawLine(x + w - 1, endgap, x + w - 1, y + h - 2); 3065 3066 g.setColor(selectedColor); 3067 g.drawLine(x + w - 2, startgap, x + w - 2, endgap - 1); 3068 g.drawLine(x + w - 1, startgap, x + w - 1, endgap - 1); 3069 } 3070 else 3071 { 3072 g.setColor(shadow); 3073 g.drawLine(x + w - 2, y + 1, x + w - 2, y + h - 2); 3074 g.setColor(darkShadow); 3075 g.drawLine(x + w - 1, y, x + w - 1, y + h - 2); 3076 } 3077 3078 g.setColor(selectedColor); 3079 g.drawLine(x + w - 3, y + 1, x + w - 3, y + h - 4); 3080 3081 g.setColor(saved); 3082 } 3083 3084 /** 3085 * <p>This method returns the bounds of a tab for the given index 3086 * and shifts it by the current scrolling offset if the tabbed 3087 * pane is in scrolling tab layout mode.</p> 3088 * 3089 * <p>Subclassses should retrievs a tab's bounds by this method 3090 * if they want to find out whether the tab is currently visible.</p> 3091 * 3092 * @param pane The JTabbedPane. 3093 * @param i The index to look for. 3094 * 3095 * @return The bounds of the tab with the given index. 3096 */ getTabBounds(JTabbedPane pane, int i)3097 public Rectangle getTabBounds(JTabbedPane pane, int i) 3098 { 3099 // Need to re-layout container if tab does not exist. 3100 if (i >= rects.length) 3101 layoutManager.layoutContainer(pane); 3102 3103 // Properly shift coordinates if scrolling has taken 3104 // place. 3105 if (pane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) 3106 { 3107 Rectangle r = new Rectangle(rects[i]); 3108 3109 switch(pane.getTabPlacement()) 3110 { 3111 case SwingConstants.TOP: 3112 case SwingConstants.BOTTOM: 3113 r.x -= currentScrollOffset; 3114 break; 3115 default: 3116 r.y -= currentScrollOffset; 3117 } 3118 3119 return r; 3120 } 3121 3122 return rects[i]; 3123 } 3124 3125 /** 3126 * This method returns the number of runs. 3127 * 3128 * @param pane The JTabbedPane. 3129 * 3130 * @return The number of runs. 3131 */ getTabRunCount(JTabbedPane pane)3132 public int getTabRunCount(JTabbedPane pane) 3133 { 3134 return runCount; 3135 } 3136 3137 /** 3138 * This method returns the tab index given a coordinate. 3139 * 3140 * @param pane The JTabbedPane. 3141 * @param x The x coordinate. 3142 * @param y The y coordinate. 3143 * 3144 * @return The tab index that the coordinate lands in. 3145 */ tabForCoordinate(JTabbedPane pane, int x, int y)3146 public int tabForCoordinate(JTabbedPane pane, int x, int y) 3147 { 3148 // Note: This code is tab layout mode agnostic. 3149 if (! tabPane.isValid()) 3150 tabPane.validate(); 3151 3152 int tabCount = tabPane.getTabCount(); 3153 3154 // If the user clicked outside of any tab rect the 3155 // selection should not change. 3156 int index = tabPane.getSelectedIndex(); 3157 for (int i = 0; i < tabCount; ++i) 3158 { 3159 if (rects[i].contains(x, y)) 3160 { 3161 index = i; 3162 break; 3163 } 3164 } 3165 3166 return index; 3167 } 3168 3169 /** 3170 * <p>This method returns the tab bounds in the given rectangle.</p> 3171 * 3172 * <p>The returned rectangle will be shifted by the current scroll 3173 * offset if the tabbed pane is in scrolling tab layout mode.</p>. 3174 * 3175 * @param tabIndex The index to get bounds for. 3176 * @param dest The rectangle to store bounds in. 3177 * 3178 * @return The rectangle passed in. 3179 */ getTabBounds(int tabIndex, Rectangle dest)3180 protected Rectangle getTabBounds(int tabIndex, Rectangle dest) 3181 { 3182 dest.setBounds(getTabBounds(tabPane, tabIndex)); 3183 return dest; 3184 } 3185 3186 /** 3187 * This method returns the component that is shown in the content area. 3188 * 3189 * @return The component that is shown in the content area. 3190 */ getVisibleComponent()3191 protected Component getVisibleComponent() 3192 { 3193 return visibleComponent; 3194 } 3195 3196 /** 3197 * This method sets the visible component. 3198 * 3199 * @param component The component to be set visible. 3200 */ setVisibleComponent(Component component)3201 protected void setVisibleComponent(Component component) 3202 { 3203 // Make old component invisible. 3204 if (visibleComponent != null && visibleComponent != component 3205 && visibleComponent.getParent() == tabPane) 3206 { 3207 visibleComponent.setVisible(false); 3208 } 3209 3210 // Make new component visible. 3211 if (component != null && ! component.isVisible()) 3212 { 3213 component.setVisible(true); 3214 } 3215 visibleComponent = component; 3216 } 3217 3218 /** 3219 * This method assures that enough rectangles are created given the 3220 * tabCount. The old array is copied to the new one. 3221 * 3222 * @param tabCount The number of tabs. 3223 */ assureRectsCreated(int tabCount)3224 protected void assureRectsCreated(int tabCount) 3225 { 3226 if (rects.length < tabCount) 3227 { 3228 Rectangle[] old = rects; 3229 rects = new Rectangle[tabCount]; 3230 System.arraycopy(old, 0, rects, 0, old.length); 3231 for (int i = old.length; i < rects.length; i++) 3232 rects[i] = new Rectangle(); 3233 } 3234 } 3235 3236 /** 3237 * This method expands the tabRuns array to give it more room. The old array 3238 * is copied to the new one. 3239 */ expandTabRunsArray()3240 protected void expandTabRunsArray() 3241 { 3242 // This method adds another 10 index positions to the tabRuns array. 3243 if (tabRuns == null) 3244 tabRuns = new int[10]; 3245 else 3246 { 3247 int[] newRuns = new int[tabRuns.length + 10]; 3248 System.arraycopy(tabRuns, 0, newRuns, 0, tabRuns.length); 3249 tabRuns = newRuns; 3250 } 3251 } 3252 3253 /** 3254 * This method returns which run a particular tab belongs to. 3255 * 3256 * @param tabCount The number of tabs. 3257 * @param tabIndex The tab to find. 3258 * 3259 * @return The tabRuns index that it belongs to. 3260 */ getRunForTab(int tabCount, int tabIndex)3261 protected int getRunForTab(int tabCount, int tabIndex) 3262 { 3263 if (runCount == 1 && tabIndex < tabCount && tabIndex >= 0) 3264 return 0; 3265 for (int i = 0; i < runCount; i++) 3266 { 3267 int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1; 3268 if (first == tabCount) 3269 first = 0; 3270 int last = lastTabInRun(tabCount, i); 3271 if (last >= tabIndex && first <= tabIndex) 3272 return i; 3273 } 3274 return -1; 3275 } 3276 3277 /** 3278 * This method returns the index of the last tab in a run. 3279 * 3280 * @param tabCount The number of tabs. 3281 * @param run The run to check. 3282 * 3283 * @return The last tab in the given run. 3284 */ lastTabInRun(int tabCount, int run)3285 protected int lastTabInRun(int tabCount, int run) 3286 { 3287 int lastTab; 3288 if (runCount == 1) 3289 lastTab = tabCount - 1; 3290 else 3291 { 3292 int nextRun; 3293 if (run == runCount - 1) 3294 nextRun = 0; 3295 else 3296 nextRun = run + 1; 3297 3298 if (tabRuns[nextRun] == 0) 3299 lastTab = tabCount - 1; 3300 else 3301 lastTab = tabRuns[nextRun] - 1; 3302 } 3303 return lastTab; 3304 } 3305 3306 /** 3307 * This method returns the tab run overlay. 3308 * 3309 * @param tabPlacement The JTabbedPane's tab placement. 3310 * 3311 * @return The tab run overlay. 3312 */ getTabRunOverlay(int tabPlacement)3313 protected int getTabRunOverlay(int tabPlacement) 3314 { 3315 return tabRunOverlay; 3316 } 3317 3318 /** 3319 * This method returns the tab run indent. It is used in WRAP_TAB_LAYOUT and 3320 * makes each tab run start indented by a certain amount. 3321 * 3322 * @param tabPlacement The JTabbedPane's tab placement. 3323 * @param run The run to get indent for. 3324 * 3325 * @return The amount a run should be indented. 3326 */ getTabRunIndent(int tabPlacement, int run)3327 protected int getTabRunIndent(int tabPlacement, int run) 3328 { 3329 return 0; 3330 } 3331 3332 /** 3333 * This method returns whether a tab run should be padded. 3334 * 3335 * @param tabPlacement The JTabbedPane's tab placement. 3336 * @param run The run to check. 3337 * 3338 * @return Whether the given run should be padded. 3339 */ shouldPadTabRun(int tabPlacement, int run)3340 protected boolean shouldPadTabRun(int tabPlacement, int run) 3341 { 3342 return true; 3343 } 3344 3345 /** 3346 * This method returns whether the tab runs should be rotated. 3347 * 3348 * @param tabPlacement The JTabbedPane's tab placement. 3349 * 3350 * @return Whether runs should be rotated. 3351 */ shouldRotateTabRuns(int tabPlacement)3352 protected boolean shouldRotateTabRuns(int tabPlacement) 3353 { 3354 return true; 3355 } 3356 3357 /** 3358 * This method returns an icon for the tab. If the tab is disabled, it 3359 * should return the disabledIcon. If it is enabled, then it should return 3360 * the default icon. 3361 * 3362 * @param tabIndex The tab index to get an icon for. 3363 * 3364 * @return The icon for the tab index. 3365 */ getIconForTab(int tabIndex)3366 protected Icon getIconForTab(int tabIndex) 3367 { 3368 if (tabPane.isEnabledAt(tabIndex)) 3369 return tabPane.getIconAt(tabIndex); 3370 else 3371 return tabPane.getDisabledIconAt(tabIndex); 3372 } 3373 3374 /** 3375 * This method returns a view that can paint the text for the label. 3376 * 3377 * @param tabIndex The tab index to get a view for. 3378 * 3379 * @return The view for the tab index. 3380 */ getTextViewForTab(int tabIndex)3381 protected View getTextViewForTab(int tabIndex) 3382 { 3383 // FIXME: When the label contains HTML this should return something 3384 // non-null. 3385 return null; 3386 } 3387 3388 /** 3389 * This method returns the tab height, including insets, for the given index 3390 * and fontheight. 3391 * 3392 * @param tabPlacement The JTabbedPane's tab placement. 3393 * @param tabIndex The index of the tab to calculate. 3394 * @param fontHeight The font height. 3395 * 3396 * @return This tab's height. 3397 */ calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight)3398 protected int calculateTabHeight(int tabPlacement, int tabIndex, 3399 int fontHeight) 3400 { 3401 // FIXME: Handle HTML by using the view (see getTextViewForTab). 3402 3403 int height = fontHeight; 3404 Icon icon = getIconForTab(tabIndex); 3405 Insets tabInsets = getTabInsets(tabPlacement, tabIndex); 3406 if (icon != null) 3407 height = Math.max(height, icon.getIconHeight()); 3408 height += tabInsets.top + tabInsets.bottom + 2; 3409 return height; 3410 } 3411 3412 /** 3413 * This method returns the max tab height. 3414 * 3415 * @param tabPlacement The JTabbedPane's tab placement. 3416 * 3417 * @return The maximum tab height. 3418 */ calculateMaxTabHeight(int tabPlacement)3419 protected int calculateMaxTabHeight(int tabPlacement) 3420 { 3421 maxTabHeight = 0; 3422 3423 FontMetrics fm = getFontMetrics(); 3424 int fontHeight = fm.getHeight(); 3425 3426 for (int i = 0; i < tabPane.getTabCount(); i++) 3427 maxTabHeight = Math.max(calculateTabHeight(tabPlacement, i, fontHeight), 3428 maxTabHeight); 3429 3430 return maxTabHeight; 3431 } 3432 3433 /** 3434 * This method calculates the tab width, including insets, for the given tab 3435 * index and font metrics. 3436 * 3437 * @param tabPlacement The JTabbedPane's tab placement. 3438 * @param tabIndex The tab index to calculate for. 3439 * @param metrics The font's metrics. 3440 * 3441 * @return The tab width for the given index. 3442 */ calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics)3443 protected int calculateTabWidth(int tabPlacement, int tabIndex, 3444 FontMetrics metrics) 3445 { 3446 Icon icon = getIconForTab(tabIndex); 3447 Insets insets = getTabInsets(tabPlacement, tabIndex); 3448 3449 int width = insets.bottom + insets.right + 3; 3450 if (icon != null) 3451 { 3452 width += icon.getIconWidth() + textIconGap; 3453 } 3454 3455 View v = getTextViewForTab(tabIndex); 3456 if (v != null) 3457 width += v.getPreferredSpan(View.X_AXIS); 3458 else 3459 { 3460 String label = tabPane.getTitleAt(tabIndex); 3461 width += metrics.stringWidth(label); 3462 } 3463 return width; 3464 } 3465 3466 /** 3467 * This method calculates the max tab width. 3468 * 3469 * @param tabPlacement The JTabbedPane's tab placement. 3470 * 3471 * @return The maximum tab width. 3472 */ calculateMaxTabWidth(int tabPlacement)3473 protected int calculateMaxTabWidth(int tabPlacement) 3474 { 3475 maxTabWidth = 0; 3476 3477 FontMetrics fm = getFontMetrics(); 3478 3479 for (int i = 0; i < tabPane.getTabCount(); i++) 3480 maxTabWidth = Math.max(calculateTabWidth(tabPlacement, i, fm), 3481 maxTabWidth); 3482 3483 return maxTabWidth; 3484 } 3485 3486 /** 3487 * This method calculates the tab area height, including insets, for the 3488 * given amount of runs and tab height. 3489 * 3490 * @param tabPlacement The JTabbedPane's tab placement. 3491 * @param horizRunCount The number of runs. 3492 * @param maxTabHeight The max tab height. 3493 * 3494 * @return The tab area height. 3495 */ calculateTabAreaHeight(int tabPlacement, int horizRunCount, int maxTabHeight)3496 protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount, 3497 int maxTabHeight) 3498 { 3499 Insets insets = getTabAreaInsets(tabPlacement); 3500 int tabAreaHeight = horizRunCount * maxTabHeight 3501 - (horizRunCount - 1) 3502 * getTabRunOverlay(tabPlacement); 3503 3504 tabAreaHeight += insets.top + insets.bottom; 3505 3506 return tabAreaHeight; 3507 } 3508 3509 /** 3510 * This method calculates the tab area width, including insets, for the 3511 * given amount of runs and tab width. 3512 * 3513 * @param tabPlacement The JTabbedPane's tab placement. 3514 * @param vertRunCount The number of runs. 3515 * @param maxTabWidth The max tab width. 3516 * 3517 * @return The tab area width. 3518 */ calculateTabAreaWidth(int tabPlacement, int vertRunCount, int maxTabWidth)3519 protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount, 3520 int maxTabWidth) 3521 { 3522 Insets insets = getTabAreaInsets(tabPlacement); 3523 int tabAreaWidth = vertRunCount * maxTabWidth 3524 - (vertRunCount - 1) 3525 * getTabRunOverlay(tabPlacement); 3526 3527 tabAreaWidth += insets.left + insets.right; 3528 3529 return tabAreaWidth; 3530 } 3531 3532 /** 3533 * This method returns the tab insets appropriately rotated. 3534 * 3535 * @param tabPlacement The JTabbedPane's tab placement. 3536 * @param tabIndex The tab index. 3537 * 3538 * @return The tab insets for the given index. 3539 */ getTabInsets(int tabPlacement, int tabIndex)3540 protected Insets getTabInsets(int tabPlacement, int tabIndex) 3541 { 3542 return tabInsets; 3543 } 3544 3545 /** 3546 * This method returns the selected tab pad insets appropriately rotated. 3547 * 3548 * @param tabPlacement The JTabbedPane's tab placement. 3549 * 3550 * @return The selected tab pad insets. 3551 */ getSelectedTabPadInsets(int tabPlacement)3552 protected Insets getSelectedTabPadInsets(int tabPlacement) 3553 { 3554 Insets target = new Insets(0, 0, 0, 0); 3555 rotateInsets(selectedTabPadInsets, target, tabPlacement); 3556 return target; 3557 } 3558 3559 /** 3560 * This method returns the tab area insets appropriately rotated. 3561 * 3562 * @param tabPlacement The JTabbedPane's tab placement. 3563 * 3564 * @return The tab area insets. 3565 */ getTabAreaInsets(int tabPlacement)3566 protected Insets getTabAreaInsets(int tabPlacement) 3567 { 3568 Insets target = new Insets(0, 0, 0, 0); 3569 rotateInsets(tabAreaInsets, target, tabPlacement); 3570 return target; 3571 } 3572 3573 /** 3574 * This method returns the content border insets appropriately rotated. 3575 * 3576 * @param tabPlacement The JTabbedPane's tab placement. 3577 * 3578 * @return The content border insets. 3579 */ getContentBorderInsets(int tabPlacement)3580 protected Insets getContentBorderInsets(int tabPlacement) 3581 { 3582 Insets target = new Insets(0, 0, 0, 0); 3583 rotateInsets(contentBorderInsets, target, tabPlacement); 3584 return target; 3585 } 3586 3587 /** 3588 * This method returns the fontmetrics for the font of the JTabbedPane. 3589 * 3590 * @return The font metrics for the JTabbedPane. 3591 */ getFontMetrics()3592 protected FontMetrics getFontMetrics() 3593 { 3594 FontMetrics fm = tabPane.getFontMetrics(tabPane.getFont()); 3595 return fm; 3596 } 3597 3598 /** 3599 * This method navigates from the selected tab into the given direction. As 3600 * a result, a new tab will be selected (if possible). 3601 * 3602 * @param direction The direction to navigate in. 3603 */ navigateSelectedTab(int direction)3604 protected void navigateSelectedTab(int direction) 3605 { 3606 int tabPlacement = tabPane.getTabPlacement(); 3607 if (tabPlacement == SwingConstants.TOP 3608 || tabPlacement == SwingConstants.BOTTOM) 3609 { 3610 if (direction == SwingConstants.WEST) 3611 selectPreviousTabInRun(tabPane.getSelectedIndex()); 3612 else if (direction == SwingConstants.EAST) 3613 selectNextTabInRun(tabPane.getSelectedIndex()); 3614 3615 else 3616 { 3617 int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(), 3618 tabPane.getSelectedIndex(), 3619 (tabPlacement == SwingConstants.TOP) 3620 ? direction == SwingConstants.NORTH 3621 : direction == SwingConstants.SOUTH); 3622 selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(), 3623 offset); 3624 } 3625 } 3626 if (tabPlacement == SwingConstants.LEFT 3627 || tabPlacement == SwingConstants.RIGHT) 3628 { 3629 if (direction == SwingConstants.NORTH) 3630 selectPreviousTabInRun(tabPane.getSelectedIndex()); 3631 else if (direction == SwingConstants.SOUTH) 3632 selectNextTabInRun(tabPane.getSelectedIndex()); 3633 else 3634 { 3635 int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(), 3636 tabPane.getSelectedIndex(), 3637 (tabPlacement == SwingConstants.LEFT) 3638 ? direction == SwingConstants.WEST 3639 : direction == SwingConstants.EAST); 3640 selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(), 3641 offset); 3642 } 3643 } 3644 } 3645 3646 /** 3647 * This method selects the next tab in the run. 3648 * 3649 * @param current The current selected index. 3650 */ selectNextTabInRun(int current)3651 protected void selectNextTabInRun(int current) 3652 { 3653 current = getNextTabIndexInRun(tabPane.getTabCount(), 3654 current); 3655 3656 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) 3657 scrollTab(current, tabPane.getTabPlacement()); 3658 3659 tabPane.setSelectedIndex(current); 3660 } 3661 3662 /** 3663 * This method selects the previous tab in the run. 3664 * 3665 * @param current The current selected index. 3666 */ selectPreviousTabInRun(int current)3667 protected void selectPreviousTabInRun(int current) 3668 { 3669 current = getPreviousTabIndexInRun(tabPane.getTabCount(), 3670 current); 3671 3672 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) 3673 scrollTab(current, tabPane.getTabPlacement()); 3674 3675 tabPane.setSelectedIndex(current); 3676 } 3677 3678 /** 3679 * This method selects the next tab (regardless of runs). 3680 * 3681 * @param current The current selected index. 3682 */ selectNextTab(int current)3683 protected void selectNextTab(int current) 3684 { 3685 current = getNextTabIndex(current); 3686 3687 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) 3688 scrollTab(current, tabPane.getTabPlacement()); 3689 3690 tabPane.setSelectedIndex(current); 3691 } 3692 3693 /** 3694 * This method selects the previous tab (regardless of runs). 3695 * 3696 * @param current The current selected index. 3697 */ selectPreviousTab(int current)3698 protected void selectPreviousTab(int current) 3699 { 3700 current = getPreviousTabIndex(current); 3701 3702 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) 3703 scrollTab(current, tabPane.getTabPlacement()); 3704 3705 tabPane.setSelectedIndex(current); 3706 } 3707 3708 /** 3709 * This method selects the correct tab given an offset from the current tab 3710 * index. If the tab placement is TOP or BOTTOM, the offset will be in the 3711 * y direction, otherwise, it will be in the x direction. A new coordinate 3712 * will be found by adding the offset to the current location of the tab. 3713 * The tab that the new location will be selected. 3714 * 3715 * @param tabPlacement The JTabbedPane's tab placement. 3716 * @param tabIndex The tab to start from. 3717 * @param offset The coordinate offset. 3718 */ selectAdjacentRunTab(int tabPlacement, int tabIndex, int offset)3719 protected void selectAdjacentRunTab(int tabPlacement, int tabIndex, 3720 int offset) 3721 { 3722 int x = rects[tabIndex].x + rects[tabIndex].width / 2; 3723 int y = rects[tabIndex].y + rects[tabIndex].height / 2; 3724 3725 switch (tabPlacement) 3726 { 3727 case SwingConstants.TOP: 3728 case SwingConstants.BOTTOM: 3729 y += offset; 3730 break; 3731 case SwingConstants.RIGHT: 3732 case SwingConstants.LEFT: 3733 x += offset; 3734 break; 3735 } 3736 3737 int index = tabForCoordinate(tabPane, x, y); 3738 if (index != -1) 3739 { 3740 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) 3741 scrollTab(index, tabPlacement); 3742 tabPane.setSelectedIndex(index); 3743 } 3744 } 3745 3746 // This method is called when you press up/down to cycle through tab runs. 3747 // it returns the distance (between the two runs' x/y position. 3748 // where one run is the current selected run and the other run is the run in the 3749 // direction of the scroll (dictated by the forward flag) 3750 // the offset is an absolute value of the difference 3751 3752 /** 3753 * This method calculates the offset distance for use in 3754 * selectAdjacentRunTab. The offset returned will be a difference in the y 3755 * coordinate between the run in the desired direction and the current run 3756 * (for tabPlacement in TOP or BOTTOM). Use x coordinate for LEFT and 3757 * RIGHT. 3758 * 3759 * @param tabPlacement The JTabbedPane's tab placement. 3760 * @param tabCount The number of tabs. 3761 * @param tabIndex The starting index. 3762 * @param forward If forward, the run in the desired direction will be the 3763 * next run. 3764 * 3765 * @return The offset between the two runs. 3766 */ getTabRunOffset(int tabPlacement, int tabCount, int tabIndex, boolean forward)3767 protected int getTabRunOffset(int tabPlacement, int tabCount, int tabIndex, 3768 boolean forward) 3769 { 3770 int currRun = getRunForTab(tabCount, tabIndex); 3771 int offset; 3772 int nextRun = forward ? getNextTabRun(currRun) : getPreviousTabRun(currRun); 3773 if (tabPlacement == SwingConstants.TOP 3774 || tabPlacement == SwingConstants.BOTTOM) 3775 offset = rects[lastTabInRun(tabCount, nextRun)].y 3776 - rects[lastTabInRun(tabCount, currRun)].y; 3777 else 3778 offset = rects[lastTabInRun(tabCount, nextRun)].x 3779 - rects[lastTabInRun(tabCount, currRun)].x; 3780 3781 return offset; 3782 } 3783 3784 /** 3785 * This method returns the previous tab index. 3786 * 3787 * @param base The index to start from. 3788 * 3789 * @return The previous tab index. 3790 */ getPreviousTabIndex(int base)3791 protected int getPreviousTabIndex(int base) 3792 { 3793 base--; 3794 if (base < 0) 3795 return tabPane.getTabCount() - 1; 3796 return base; 3797 } 3798 3799 /** 3800 * This method returns the next tab index. 3801 * 3802 * @param base The index to start from. 3803 * 3804 * @return The next tab index. 3805 */ getNextTabIndex(int base)3806 protected int getNextTabIndex(int base) 3807 { 3808 base++; 3809 if (base == tabPane.getTabCount()) 3810 return 0; 3811 return base; 3812 } 3813 3814 /** 3815 * This method returns the next tab index in the run. If the next index is 3816 * out of this run, it will return the starting tab index for the run. 3817 * 3818 * @param tabCount The number of tabs. 3819 * @param base The index to start from. 3820 * 3821 * @return The next tab index in the run. 3822 */ getNextTabIndexInRun(int tabCount, int base)3823 protected int getNextTabIndexInRun(int tabCount, int base) 3824 { 3825 int index = getNextTabIndex(base); 3826 int run = getRunForTab(tabCount, base); 3827 if (base == lastTabInRun(tabCount, run)) 3828 index = (run > 0) 3829 ? lastTabInRun(tabCount, getPreviousTabRun(run)) + 1 3830 : 0; 3831 3832 return index; 3833 } 3834 3835 /** 3836 * This method returns the previous tab index in the run. If the previous 3837 * index is out of this run, it will return the last index for the run. 3838 * 3839 * @param tabCount The number of tabs. 3840 * @param base The index to start from. 3841 * 3842 * @return The previous tab index in the run. 3843 */ getPreviousTabIndexInRun(int tabCount, int base)3844 protected int getPreviousTabIndexInRun(int tabCount, int base) 3845 { 3846 int index = getPreviousTabIndex(base); 3847 int run = getRunForTab(tabCount, base); 3848 if (index == lastTabInRun(tabCount, getPreviousTabRun(run))) 3849 index = lastTabInRun(tabCount, run); 3850 3851 return index; 3852 } 3853 3854 /** 3855 * This method returns the index of the previous run. 3856 * 3857 * @param baseRun The run to start from. 3858 * 3859 * @return The index of the previous run. 3860 */ getPreviousTabRun(int baseRun)3861 protected int getPreviousTabRun(int baseRun) 3862 { 3863 if (getTabRunCount(tabPane) == 1) 3864 return 1; 3865 3866 int prevRun = --baseRun; 3867 if (prevRun < 0) 3868 prevRun = getTabRunCount(tabPane) - 1; 3869 return prevRun; 3870 } 3871 3872 /** 3873 * This method returns the index of the next run. 3874 * 3875 * @param baseRun The run to start from. 3876 * 3877 * @return The index of the next run. 3878 */ getNextTabRun(int baseRun)3879 protected int getNextTabRun(int baseRun) 3880 { 3881 if (getTabRunCount(tabPane) == 1) 3882 return 1; 3883 3884 int nextRun = ++baseRun; 3885 if (nextRun == getTabRunCount(tabPane)) 3886 nextRun = 0; 3887 return nextRun; 3888 } 3889 3890 /** 3891 * This method rotates the insets given a direction to rotate them in. 3892 * Target placement should be one of TOP, LEFT, BOTTOM, RIGHT. The rotated 3893 * insets will be stored in targetInsets. Passing in TOP as the direction 3894 * does nothing. Passing in LEFT switches top and left, right and bottom. 3895 * Passing in BOTTOM switches top and bottom. Passing in RIGHT switches top 3896 * for left, left for bottom, bottom for right, and right for top. 3897 * 3898 * @param topInsets The reference insets. 3899 * @param targetInsets An Insets object to store the new insets. 3900 * @param targetPlacement The rotation direction. 3901 */ rotateInsets(Insets topInsets, Insets targetInsets, int targetPlacement)3902 protected static void rotateInsets(Insets topInsets, Insets targetInsets, 3903 int targetPlacement) 3904 { 3905 // Sun's version will happily throw an NPE if params are null, 3906 // so I won't check it either. 3907 switch (targetPlacement) 3908 { 3909 default: 3910 case SwingConstants.TOP: 3911 targetInsets.top = topInsets.top; 3912 targetInsets.left = topInsets.left; 3913 targetInsets.right = topInsets.right; 3914 targetInsets.bottom = topInsets.bottom; 3915 break; 3916 case SwingConstants.LEFT: 3917 targetInsets.left = topInsets.top; 3918 targetInsets.top = topInsets.left; 3919 targetInsets.right = topInsets.bottom; 3920 targetInsets.bottom = topInsets.right; 3921 break; 3922 case SwingConstants.BOTTOM: 3923 targetInsets.top = topInsets.bottom; 3924 targetInsets.bottom = topInsets.top; 3925 targetInsets.left = topInsets.left; 3926 targetInsets.right = topInsets.right; 3927 break; 3928 case SwingConstants.RIGHT: 3929 targetInsets.top = topInsets.left; 3930 targetInsets.left = topInsets.bottom; 3931 targetInsets.bottom = topInsets.right; 3932 targetInsets.right = topInsets.top; 3933 break; 3934 } 3935 } 3936 getActionMap()3937 ActionMap getActionMap() 3938 { 3939 ActionMap map = (ActionMap) UIManager.get("TabbedPane.actionMap"); 3940 3941 if (map == null) // first time here 3942 { 3943 map = createActionMap(); 3944 if (map != null) 3945 UIManager.put("TabbedPane.actionMap", map); 3946 } 3947 return map; 3948 } 3949 createActionMap()3950 ActionMap createActionMap() 3951 { 3952 ActionMap map = new ActionMapUIResource(); 3953 3954 map.put("navigatePageDown", new NavigatePageDownAction()); 3955 map.put("navigatePageUp", new NavigatePageUpAction()); 3956 map.put("navigateDown", 3957 new NavigateAction("navigateDown", SwingConstants.SOUTH)); 3958 3959 map.put("navigateUp", 3960 new NavigateAction("navigateUp", SwingConstants.NORTH)); 3961 3962 map.put("navigateLeft", 3963 new NavigateAction("navigateLeft", SwingConstants.WEST)); 3964 3965 map.put("navigateRight", 3966 new NavigateAction("navigateRight", SwingConstants.EAST)); 3967 3968 map.put("requestFocusForVisibleComponent", 3969 new RequestFocusForVisibleComponentAction()); 3970 map.put("requestFocus", new RequestFocusAction()); 3971 3972 return map; 3973 } 3974 3975 /** 3976 * Sets the tab which should be highlighted when in rollover mode. And 3977 * <code>index</code> of <code>-1</code> means that the rollover tab 3978 * is deselected (i.e. the mouse is outside of the tabarea). 3979 * 3980 * @param index the index of the tab that is under the mouse, <code>-1</code> 3981 * for no tab 3982 * 3983 * @since 1.5 3984 */ setRolloverTab(int index)3985 protected void setRolloverTab(int index) 3986 { 3987 rolloverTab = index; 3988 } 3989 3990 /** 3991 * Retunrs the index of the tab over which the mouse is currently moving, 3992 * or <code>-1</code> for no tab. 3993 * 3994 * @return the index of the tab over which the mouse is currently moving, 3995 * or <code>-1</code> for no tab 3996 * 3997 * @since 1.5 3998 */ getRolloverTab()3999 protected int getRolloverTab() 4000 { 4001 return rolloverTab; 4002 } 4003 } 4004