001 /* MetalTabbedPaneUI.java
002 Copyright (C) 2005, 2006 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package javax.swing.plaf.metal;
040
041 import java.awt.Color;
042 import java.awt.Graphics;
043 import java.awt.LayoutManager;
044 import java.awt.Rectangle;
045
046 import javax.swing.JComponent;
047 import javax.swing.JTabbedPane;
048 import javax.swing.UIManager;
049 import javax.swing.plaf.ComponentUI;
050 import javax.swing.plaf.UIResource;
051 import javax.swing.plaf.basic.BasicTabbedPaneUI;
052
053 /**
054 * A UI delegate for the {@link JTabbedPane} component.
055 */
056 public class MetalTabbedPaneUI extends BasicTabbedPaneUI
057 {
058
059 /**
060 * A {@link LayoutManager} responsible for placing all the tabs and the
061 * visible component inside the {@link JTabbedPane}. This class is only used
062 * for {@link JTabbedPane#WRAP_TAB_LAYOUT}.
063 *
064 * @specnote Apparently this class was intended to be protected,
065 * but was made public by a compiler bug and is now
066 * public for compatibility.
067 */
068 public class TabbedPaneLayout
069 extends BasicTabbedPaneUI.TabbedPaneLayout
070 {
071 /**
072 * Creates a new instance of the layout manager.
073 */
074 public TabbedPaneLayout()
075 {
076 // Nothing to do here.
077 }
078
079 /**
080 * Overridden to do nothing, because tab runs are not rotated in the
081 * {@link MetalLookAndFeel}.
082 *
083 * @param tabPlacement the tab placement (one of {@link #TOP},
084 * {@link #BOTTOM}, {@link #LEFT} or {@link #RIGHT}).
085 * @param selectedRun the index of the selected run.
086 */
087 protected void rotateTabRuns(int tabPlacement, int selectedRun)
088 {
089 // do nothing, because tab runs are not rotated in the MetalLookAndFeel
090 }
091
092 /**
093 * Overridden to do nothing, because the selected tab does not have extra
094 * padding in the {@link MetalLookAndFeel}.
095 *
096 * @param tabPlacement the tab placement (one of {@link #TOP},
097 * {@link #BOTTOM}, {@link #LEFT} or {@link #RIGHT}).
098 * @param selectedIndex the index of the selected tab.
099 */
100 protected void padSelectedTab(int tabPlacement, int selectedIndex)
101 {
102 // do nothing, because the selected tab does not have extra padding in
103 // the MetalLookAndFeel
104 }
105
106 /**
107 * Overridden because tab runs are only normalized for TOP and BOTTOM
108 * tab placement in the Metal L&F.
109 */
110 protected void normalizeTabRuns(int tabPlacement, int tabCount, int start,
111 int max)
112 {
113 if (tabPlacement == TOP || tabPlacement == BOTTOM)
114 super.normalizeTabRuns(tabPlacement, tabCount, start, max);
115 }
116 }
117
118 /**
119 * The minimum tab width.
120 */
121 protected int minTabWidth;
122
123 /**
124 * The color for the selected tab.
125 */
126 protected Color selectColor;
127
128 /**
129 * The color for a highlighted selected tab.
130 */
131 protected Color selectHighlight;
132
133 /**
134 * The background color used for the tab area.
135 */
136 protected Color tabAreaBackground;
137
138 /** The graphics to draw the highlight below the tab. */
139 private Graphics hg;
140
141 /**
142 * Indicates if the tabs are having their background filled.
143 */
144 private boolean tabsOpaque;
145
146 /**
147 * Constructs a new instance of MetalTabbedPaneUI.
148 */
149 public MetalTabbedPaneUI()
150 {
151 super();
152 }
153
154 /**
155 * Returns an instance of MetalTabbedPaneUI.
156 *
157 * @param component the component for which we return an UI instance
158 *
159 * @return an instance of MetalTabbedPaneUI
160 */
161 public static ComponentUI createUI(JComponent component)
162 {
163 return new MetalTabbedPaneUI();
164 }
165
166 /**
167 * Creates and returns an instance of {@link TabbedPaneLayout}.
168 *
169 * @return A layout manager used by this UI delegate.
170 */
171 protected LayoutManager createLayoutManager()
172 {
173 return (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
174 ? new MetalTabbedPaneUI.TabbedPaneLayout()
175 : super.createLayoutManager();
176 }
177
178 /**
179 * Paints the border for a single tab.
180 *
181 * @param g the graphics device.
182 * @param tabPlacement the tab placement ({@link #TOP}, {@link #LEFT},
183 * {@link #BOTTOM} or {@link #RIGHT}).
184 * @param tabIndex the index of the tab to draw the border for.
185 * @param x the x-coordinate for the tab's bounding rectangle.
186 * @param y the y-coordinate for the tab's bounding rectangle.
187 * @param w the width for the tab's bounding rectangle.
188 * @param h the height for the tab's bounding rectangle.
189 * @param isSelected indicates whether or not the tab is selected.
190 */
191 protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex,
192 int x, int y, int w, int h, boolean isSelected)
193 {
194 int bottom = y + h - 1;
195 int right = x + w - 1;
196
197 switch (tabPlacement)
198 {
199 case LEFT:
200 paintLeftTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
201 break;
202 case BOTTOM:
203 paintBottomTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
204 break;
205 case RIGHT:
206 paintRightTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
207 break;
208 case TOP:
209 default:
210 paintTopTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
211 }
212 }
213
214 /**
215 * Paints the border for a tab assuming that the tab position is at the top
216 * ({@link #TOP}).
217 *
218 * @param tabIndex the tab index.
219 * @param g the graphics device.
220 * @param x the x-coordinate for the tab's bounding rectangle.
221 * @param y the y-coordinate for the tab's bounding rectangle.
222 * @param w the width for the tab's bounding rectangle.
223 * @param h the height for the tab's bounding rectangle.
224 * @param btm the y coordinate of the bottom border
225 * @param rght the x coordinate of the right border
226 * @param isSelected indicates whether the tab is selected.
227 */
228 protected void paintTopTabBorder(int tabIndex, Graphics g, int x, int y,
229 int w, int h, int btm, int rght, boolean isSelected)
230 {
231 int tabCount = tabPane.getTabCount();
232 int currentRun = getRunForTab(tabCount, tabIndex);
233 int right = w - 1;
234 int bottom = h - 1;
235
236 // Paint gap.
237 if (shouldFillGap(currentRun, tabIndex, x, y))
238 {
239 g.translate(x, y);
240 g.setColor(getColorForGap(currentRun, x, y + 1));
241 g.fillRect(1, 0, 5, 3);
242 g.fillRect(1, 3, 2, 2);
243 g.translate(-x, -y);
244 }
245
246 g.translate(x, y);
247
248 boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
249 Color oceanSelectedBorder =
250 UIManager.getColor("TabbedPane.borderHightlightColor");
251 if (isOcean && isSelected)
252 g.setColor(oceanSelectedBorder);
253 else
254 g.setColor(darkShadow);
255
256 // Slant
257 g.drawLine(1, 5, 6, 0);
258 // Top.
259 g.drawLine(6, 0, right, 0);
260 // Right.
261 int lastIndex = lastTabInRun(tabCount, currentRun);
262 if (tabIndex == lastIndex)
263 g.drawLine(right, 1, right, bottom);
264 // Left.
265 int selectedIndex = tabPane.getSelectedIndex();
266 if (isOcean && tabIndex - 1 == selectedIndex
267 && currentRun == getRunForTab(tabCount, selectedIndex))
268 {
269 g.setColor(oceanSelectedBorder);
270 }
271 if (tabIndex != tabRuns[runCount - 1])
272 {
273 if (isOcean && isSelected)
274 {
275 g.drawLine(0, 6, 0, bottom);
276 g.setColor(darkShadow);
277 g.drawLine(0, 0, 0, 5);
278 }
279 else
280 {
281 g.drawLine(0, 0, 0, bottom);
282 }
283 }
284 else
285 {
286 g.drawLine(0, 6, 0, bottom);
287 }
288
289 // Paint the highlight.
290 g.setColor(isSelected ? selectHighlight : highlight);
291 // Slant.
292 g.drawLine(1, 6, 6, 1);
293 // Top.
294 g.drawLine(6, 1, right, 1);
295 // Left.
296 g.drawLine(1, 6, 1, bottom);
297 int firstIndex = tabRuns[currentRun];
298 if (tabIndex == firstIndex && tabIndex != tabRuns[runCount - 1])
299 {
300 if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1])
301 g.setColor(selectHighlight);
302 else
303 g.setColor(highlight);
304 g.drawLine(1, 0, 1, 4);
305 }
306
307 g.translate(-x, -y);
308 }
309
310 /**
311 * Paints the border for a tab assuming that the tab position is at the left
312 * ({@link #LEFT}).
313 *
314 * @param tabIndex the tab index.
315 * @param g the graphics device.
316 * @param x the x-coordinate for the tab's bounding rectangle.
317 * @param y the y-coordinate for the tab's bounding rectangle.
318 * @param w the width for the tab's bounding rectangle.
319 * @param h the height for the tab's bounding rectangle.
320 * @param btm ???
321 * @param rght ???
322 * @param isSelected indicates whether the tab is selected.
323 */
324 protected void paintLeftTabBorder(int tabIndex, Graphics g, int x, int y,
325 int w, int h, int btm, int rght, boolean isSelected)
326 {
327 g.translate(x, y);
328 int bottom = h - 1;
329 int right = w - 1;
330
331 int tabCount = tabPane.getTabCount();
332 int currentRun = getRunForTab(tabCount, tabIndex);
333 int firstIndex = tabRuns[currentRun];
334
335 // Paint the part of the above tab.
336 if (tabIndex != firstIndex && tabIndex > 0 && tabsOpaque)
337 {
338 Color c;
339 if (tabPane.getSelectedIndex() == tabIndex - 1)
340 c = selectColor;
341 else
342 c = getUnselectedBackground(tabIndex - 1);
343 g.setColor(c);
344 g.fillRect(2, 0, 4, 3);
345 g.drawLine(2, 3, 2, 3);
346 }
347
348 // Paint the highlight.
349 boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
350 if (isOcean)
351 {
352 g.setColor(isSelected ? selectHighlight : MetalLookAndFeel.getWhite());
353 }
354 else
355 {
356 g.setColor(isSelected ? selectHighlight : highlight);
357 }
358 // Slant.
359 g.drawLine(1, 6, 6, 1);
360 // Left.
361 g.drawLine(1, 6, 1, bottom);
362 // Top.
363 g.drawLine(6, 1, right, 1);
364 if (tabIndex != firstIndex)
365 {
366 if (isOcean)
367 {
368 g.setColor(MetalLookAndFeel.getWhite());
369 }
370 g.drawLine(1, 0, 1, 4);
371 }
372
373 // Paint border.
374 Color oceanSelectedBorder =
375 UIManager.getColor("TabbedPane.borderHightlightColor");
376 if (isOcean && isSelected)
377 {
378 g.setColor(oceanSelectedBorder);
379 }
380 else
381 {
382 g.setColor(darkShadow);
383 }
384
385 // Slant.
386 g.drawLine(1, 5, 6, 0);
387 // Top.
388 g.drawLine(6, 0, right, 0);
389 // Bottom.
390 int lastIndex = lastTabInRun(tabCount, currentRun);
391 if (tabIndex == lastIndex)
392 {
393 g.drawLine(0, bottom, right, bottom);
394 }
395 // Left.
396 if (isOcean)
397 {
398 if (tabPane.getSelectedIndex() == tabIndex - 1)
399 {
400 g.drawLine(0, 6, 0, bottom);
401 if (tabIndex != firstIndex)
402 {
403 g.setColor(oceanSelectedBorder);
404 g.drawLine(0, 0, 0, 5);
405 }
406 }
407 else if (isSelected)
408 {
409 g.drawLine(0, 5, 0, bottom);
410 if (tabIndex != firstIndex)
411 {
412 g.setColor(darkShadow);
413 g.drawLine(0, 0, 0, 5);
414 }
415 }
416 else if (tabIndex != firstIndex)
417 {
418 g.drawLine(0, 0, 0, bottom);
419 }
420 else
421 {
422 g.drawLine(0, 6, 0, bottom);
423 }
424 }
425 else
426 {
427 if (tabIndex != firstIndex)
428 {
429 g.drawLine(0, 0, 0, bottom);
430 }
431 else
432 {
433 g.drawLine(0, 6, 0, bottom);
434 }
435 }
436
437 g.translate(-x, -y);
438 }
439
440 /**
441 * Paints the border for a tab assuming that the tab position is at the right
442 * ({@link #RIGHT}).
443 *
444 * @param tabIndex the tab index.
445 * @param g the graphics device.
446 * @param x the x-coordinate for the tab's bounding rectangle.
447 * @param y the y-coordinate for the tab's bounding rectangle.
448 * @param w the width for the tab's bounding rectangle.
449 * @param h the height for the tab's bounding rectangle.
450 * @param btm ???
451 * @param rght ???
452 * @param isSelected indicates whether the tab is selected.
453 */
454 protected void paintRightTabBorder(int tabIndex, Graphics g, int x, int y,
455 int w, int h, int btm, int rght, boolean isSelected)
456 {
457 g.translate(x, y);
458 int bottom = h - 1;
459 int right = w - 1;
460
461 int tabCount = tabPane.getTabCount();
462 int currentRun = getRunForTab(tabCount, tabIndex);
463 int firstIndex = tabRuns[currentRun];
464
465 // Paint part of the above tab.
466 if (tabIndex != firstIndex && tabIndex > 0 && tabsOpaque)
467 {
468 Color c;
469 if (tabPane.getSelectedIndex() == tabIndex - 1)
470 c = selectColor;
471 else
472 c = getUnselectedBackground(tabIndex - 1);
473 g.setColor(c);
474 g.fillRect(right - 5, 0, 5, 3);
475 g.fillRect(right - 2, 3, 2, 2);
476 }
477
478 // Paint highlight.
479 g.setColor(isSelected ? selectHighlight : highlight);
480
481 // Slant.
482 g.drawLine(right - 6, 1, right - 1, 6);
483 // Top.
484 g.drawLine(0, 1, right - 6, 1);
485 // Left.
486 if (! isSelected)
487 {
488 g.drawLine(0, 1, 0, bottom);
489 }
490
491 // Paint border.
492 boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
493 Color oceanSelectedBorder =
494 UIManager.getColor("TabbedPane.borderHightlightColor");
495 if (isOcean && isSelected)
496 {
497 g.setColor(oceanSelectedBorder);
498 }
499 else
500 {
501 g.setColor(darkShadow);
502 }
503
504 // Bottom.
505 int lastIndex = lastTabInRun(tabCount, currentRun);
506 if (tabIndex == lastIndex)
507 {
508 g.drawLine(0, bottom, right, bottom);
509 }
510 // Slant.
511 if (isOcean && tabPane.getSelectedIndex() == tabIndex - 1)
512 {
513 g.setColor(oceanSelectedBorder);
514 }
515 g.drawLine(right - 6, 0, right, 6);
516 // Top.
517 g.drawLine(0, 0, right - 6, 0);
518 // Right.
519 if (isOcean && isSelected)
520 {
521 g.drawLine(right, 6, right, bottom);
522 if (tabIndex != firstIndex)
523 {
524 g.setColor(darkShadow);
525 g.drawLine(right, 0, right, 5);
526 }
527 }
528 else if (isOcean && tabPane.getSelectedIndex() == tabIndex - 1)
529 {
530 if (tabIndex != firstIndex)
531 {
532 g.setColor(oceanSelectedBorder);
533 g.drawLine(right, 0, right, 6);
534 }
535 g.setColor(darkShadow);
536 g.drawLine(right, 7, right, bottom);
537 }
538 else if (tabIndex != firstIndex)
539 {
540 g.drawLine(right, 0, right, bottom);
541 }
542 else
543 {
544 g.drawLine(right, 6, right, bottom);
545 }
546 g.translate(-x, -y);
547 }
548
549 /**
550 * Paints the border for a tab assuming that the tab position is at the bottom
551 * ({@link #BOTTOM}).
552 *
553 * @param tabIndex the tab index.
554 * @param g the graphics device.
555 * @param x the x-coordinate for the tab's bounding rectangle.
556 * @param y the y-coordinate for the tab's bounding rectangle.
557 * @param w the width for the tab's bounding rectangle.
558 * @param h the height for the tab's bounding rectangle.
559 * @param btm ???
560 * @param rght ???
561 * @param isSelected indicates whether the tab is selected.
562 */
563 protected void paintBottomTabBorder(int tabIndex, Graphics g, int x, int y,
564 int w, int h, int btm, int rght, boolean isSelected)
565 {
566 int bottom = h - 1;
567 int right = w - 1;
568
569 int tabCount = tabPane.getTabCount();
570 int currentRun = getRunForTab(tabCount, tabIndex);
571 // Paint gap if necessary.
572 if (shouldFillGap(currentRun, tabIndex, x, y))
573 {
574 g.translate(x, y);
575 g.setColor(getColorForGap(currentRun, x, y));
576 g.fillRect(1, bottom - 4, 3, 5);
577 g.fillRect(4, bottom - 1, 2, 2);
578 g.translate(-x, -y);
579 }
580
581 g.translate(x, y);
582
583 // Paint border.
584 boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
585 Color oceanSelectedBorder =
586 UIManager.getColor("TabbedPane.borderHightlightColor");
587 if (isOcean && isSelected)
588 {
589 g.setColor(oceanSelectedBorder);
590 }
591 else
592 {
593 g.setColor(darkShadow);
594 }
595 // Slant.
596 g.drawLine(1, bottom - 5, 6, bottom);
597 // Bottom.
598 g.drawLine(6, bottom, right, bottom);
599 // Right.
600 int lastIndex = lastTabInRun(tabCount, currentRun);
601 if (tabIndex == lastIndex)
602 {
603 g.drawLine(right, 0, right, bottom);
604 }
605 // Left.
606 if (isOcean && isSelected)
607 {
608 g.drawLine(0, 0, 0, bottom - 5);
609
610 // Paint a connecting line to the tab below for all
611 // but the first tab in the last run.
612 if (tabIndex != tabRuns[runCount-1])
613 {
614 g.setColor(darkShadow);
615 g.drawLine(0, bottom - 5, 0, bottom);
616 }
617 }
618 else
619 {
620 if (isOcean && tabIndex == tabPane.getSelectedIndex() + 1)
621 {
622 g.setColor(oceanSelectedBorder);
623 }
624 if (tabIndex != tabRuns[runCount - 1])
625 {
626 g.drawLine(0, 0, 0, bottom);
627 }
628 else
629 {
630 g.drawLine(0, 0, 0, bottom - 6);
631 }
632 }
633
634 // Paint highlight.
635 g.setColor(isSelected ? selectHighlight : highlight);
636 // Slant.
637 g.drawLine(1, bottom - 6, 6, bottom - 1);
638 // Left.
639 g.drawLine(1, 0, 1, bottom - 6);
640
641 int firstIndex = tabRuns[currentRun];
642 if (tabIndex == firstIndex && tabIndex != tabRuns[runCount - 1])
643 {
644 if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1])
645 {
646 g.setColor(selectHighlight);
647 }
648 else
649 {
650 g.setColor(highlight);
651 }
652 g.drawLine(1, bottom - 4, 1, bottom);
653 }
654
655 g.translate(-x, -y);
656 }
657
658 /**
659 * Paints the background for a tab.
660 *
661 * @param g the graphics device.
662 * @param tabPlacement the tab placement ({@link #TOP}, {@link #LEFT},
663 * {@link #BOTTOM} or {@link #RIGHT}).
664 * @param tabIndex the index of the tab to draw the border for.
665 * @param x the x-coordinate for the tab's bounding rectangle.
666 * @param y the y-coordinate for the tab's bounding rectangle.
667 * @param w the width for the tab's bounding rectangle.
668 * @param h the height for the tab's bounding rectangle.
669 * @param isSelected indicates whether or not the tab is selected.
670 */
671 protected void paintTabBackground(Graphics g, int tabPlacement,
672 int tabIndex, int x, int y, int w, int h, boolean isSelected)
673 {
674 if (isSelected)
675 g.setColor(selectColor);
676 else
677 g.setColor(getUnselectedBackground(tabIndex));
678
679 switch (tabPlacement)
680 {
681 case LEFT:
682 g.fillRect(x + 5, y + 1, w - 5, h - 1);
683 g.fillRect(x + 2, y + 4, 3, h - 4);
684 break;
685 case BOTTOM:
686 g.fillRect(x + 2, y, w - 2, h - 3);
687 g.fillRect(x + 5, y + h - 4, w - 5, 3);
688 break;
689 case RIGHT:
690 g.fillRect(x, y + 1, w - 4, h - 1);
691 g.fillRect(x + w - 4, y + 5, 3, h - 5);
692 break;
693 case TOP:
694 default:
695 g.fillRect(x + 4, y + 2, w - 4, h - 2);
696 g.fillRect(x + 2, y + 5, 2, h - 5);
697 }
698 }
699
700 /**
701 * This method paints the focus rectangle around the selected tab.
702 *
703 * @param g The Graphics object to paint with.
704 * @param tabPlacement The JTabbedPane's tab placement.
705 * @param rects The array of rectangles keeping track of size and position.
706 * @param tabIndex The tab index.
707 * @param iconRect The icon bounds.
708 * @param textRect The text bounds.
709 * @param isSelected Whether this tab is selected.
710 */
711 protected void paintFocusIndicator(Graphics g, int tabPlacement,
712 Rectangle[] rects, int tabIndex,
713 Rectangle iconRect, Rectangle textRect,
714 boolean isSelected)
715 {
716 if (tabPane.hasFocus() && isSelected)
717 {
718 Rectangle rect = rects[tabIndex];
719
720 g.setColor(focus);
721 g.translate(rect.x, rect.y);
722
723 switch (tabPlacement)
724 {
725 case LEFT:
726 // Top line
727 g.drawLine(7, 2, rect.width-2, 2);
728
729 // Right line
730 g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3);
731
732 // Bottom line
733 g.drawLine(rect.width-2, rect.height-2, 3, rect.height-2);
734
735 // Left line
736 g.drawLine(2, rect.height-3, 2, 7);
737
738 // Slant
739 g.drawLine(2, 6, 6, 2);
740 break;
741 case RIGHT:
742 // Top line
743 g.drawLine(1, 2, rect.width-8, 2);
744
745 // Slant
746 g.drawLine(rect.width-7, 2, rect.width-3, 6);
747
748 // Right line
749 g.drawLine(rect.width-3, 7, rect.width-3, rect.height-3);
750
751 // Bottom line
752 g.drawLine(rect.width-3, rect.height-2, 2, rect.height-2);
753
754 // Left line
755 g.drawLine(1, rect.height-2, 1, 2);
756 break;
757 case BOTTOM:
758 // Top line
759 g.drawLine(2, 1, rect.width-2, 1);
760
761 // Right line
762 g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3);
763
764 // Bottom line
765 g.drawLine(7, rect.height-3, rect.width-2, rect.height-3);
766
767 // Slant
768 g.drawLine(6, rect.height-3, 2, rect.height-7);
769
770 // Left line
771 g.drawLine(2, rect.height-8, 2, 2);
772
773 break;
774 case TOP:
775 default:
776 // Top line
777 g.drawLine(6, 2, rect.width-2, 2);
778
779 // Right line
780 g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3);
781
782 // Bottom line
783 g.drawLine(3, rect.height-3, rect.width-2, rect.height-3);
784
785 // Left line
786 g.drawLine(2, rect.height-3, 2, 7);
787
788 // Slant
789 g.drawLine(2, 6, 6, 2);
790
791 }
792
793 g.translate(-rect.x, -rect.y);
794 }
795 }
796
797 /**
798 * Returns <code>true</code> if the tabs in the specified run should be
799 * padded to make the run fill the width/height of the {@link JTabbedPane}.
800 *
801 * @param tabPlacement the tab placement for the {@link JTabbedPane} (one of
802 * {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} and {@link #RIGHT}).
803 * @param run the run index.
804 *
805 * @return A boolean.
806 */
807 protected boolean shouldPadTabRun(int tabPlacement, int run)
808 {
809 // as far as I can tell, all runs should be padded except the last run
810 // (which is drawn at the very top for tabPlacement == TOP)
811 return run < this.runCount - 1;
812 }
813
814 /**
815 * Installs the defaults for this UI. This method calls super.installDefaults
816 * and then loads the Metal specific defaults for TabbedPane.
817 */
818 protected void installDefaults()
819 {
820 super.installDefaults();
821 selectColor = UIManager.getColor("TabbedPane.selected");
822 selectHighlight = UIManager.getColor("TabbedPane.selectHighlight");
823 tabAreaBackground = UIManager.getColor("TabbedPane.tabAreaBackground");
824 tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque");
825 minTabWidth = 0;
826 }
827
828 /**
829 * Returns the color for the gap.
830 *
831 * @param currentRun - The current run to return the color for
832 * @param x - The x position of the current run
833 * @param y - The y position of the current run
834 *
835 * @return the color for the gap in the current run.
836 */
837 protected Color getColorForGap(int currentRun, int x, int y)
838 {
839 int index = tabForCoordinate(tabPane, x, y);
840 int selected = tabPane.getSelectedIndex();
841 if (selected == index)
842 return selectColor;
843 return tabAreaBackground;
844 }
845
846 /**
847 * Returns true if the gap should be filled in.
848 *
849 * @param currentRun - The current run
850 * @param tabIndex - The current tab
851 * @param x - The x position of the tab
852 * @param y - The y position of the tab
853 *
854 * @return true if the gap at the current run should be filled
855 */
856 protected boolean shouldFillGap(int currentRun, int tabIndex, int x, int y)
857 {
858 // As far as I can tell, the gap is never filled in.
859 return false;
860 }
861
862 /**
863 * Paints the highlight below the tab, if there is one.
864 */
865 protected void paintHighlightBelowTab()
866 {
867 int selected = tabPane.getSelectedIndex();
868 int tabPlacement = tabPane.getTabPlacement();
869 Rectangle bounds = getTabBounds(tabPane, selected);
870
871 hg.setColor(selectColor);
872 int x = bounds.x;
873 int y = bounds.y;
874 int w = bounds.width;
875 int h = bounds.height;
876
877 if (tabPlacement == TOP)
878 hg.fillRect(x, y + h - 2, w, 30);
879 else if (tabPlacement == LEFT)
880 hg.fillRect(x + w - 1, y, 20, h);
881 else if (tabPlacement == BOTTOM)
882 hg.fillRect(x, y - h + 2, w, 30);
883 else if (tabPlacement == RIGHT)
884 hg.fillRect(x - 18, y, 20, h);
885 else
886 throw new AssertionError("Unrecognised 'tabPlacement' argument.");
887 hg = null;
888 }
889
890 /**
891 * Returns true if we should rotate the tab runs.
892 *
893 * @param tabPlacement - The current tab placement.
894 * @param selectedRun - The selected run.
895 *
896 * @return true if the tab runs should be rotated.
897 */
898 protected boolean shouldRotateTabRuns(int tabPlacement,
899 int selectedRun)
900 {
901 // false because tab runs are not rotated in the MetalLookAndFeel
902 return false;
903 }
904
905 protected int calculateMaxTabHeight(int tabPlacement)
906 {
907 // FIXME: Why is this overridden?
908 return super.calculateMaxTabHeight(tabPlacement);
909 }
910
911 /**
912 * Returns the amount of overlay among the tabs. In
913 * the Metal L&F the overlay for LEFT and RIGHT placement
914 * is half of the maxTabHeight. For TOP and BOTTOM placement
915 * the tabs do not overlay.
916 *
917 * @param tabPlacement the placement
918 *
919 * @return the amount of overlay among the tabs
920 */
921 protected int getTabRunOverlay(int tabPlacement)
922 {
923 int overlay = 0;
924 if (tabPlacement == LEFT || tabPlacement == RIGHT)
925 {
926 int maxHeight = calculateMaxTabHeight(tabPlacement);
927 overlay = maxTabHeight / 2;
928 }
929 return overlay;
930 }
931
932 /**
933 * Paints the upper edge of the content border.
934 *
935 * @param g the graphics to use for painting
936 * @param tabPlacement the tab placement
937 * @param selectedIndex the index of the selected tab
938 * @param x the upper left coordinate of the content area
939 * @param y the upper left coordinate of the content area
940 * @param w the width of the content area
941 * @param h the height of the content area
942 */
943 protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
944 int selectedIndex, int x, int y,
945 int w, int h)
946 {
947 Color oceanSelectedBorder =
948 UIManager.getColor("TabbedPane.borderHightlightColor");
949 boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
950 if (isOcean)
951 {
952 g.setColor(oceanSelectedBorder);
953 }
954 else
955 {
956 g.setColor(selectHighlight);
957 }
958
959 Rectangle rect = selectedIndex < 0 ? null :
960 getTabBounds(selectedIndex, calcRect);
961
962 // If tabs are not placed on TOP, or if the selected tab is not in the
963 // run directly above the content or the selected tab is not visible,
964 // then we draw an unbroken line.
965 if (tabPlacement != TOP || selectedIndex < 0
966 || rect.y + rect.height + 1 < y || rect.x < x || rect.x > x + w)
967 {
968 g.drawLine(x, y, x + w - 2, y);
969 if (isOcean && tabPlacement == TOP)
970 {
971 g.setColor(MetalLookAndFeel.getWhite());
972 g.drawLine(x, y + 1, x + w - 2, y + 1);
973 }
974 }
975 else
976 {
977 boolean isLast = isLastTabInRun(selectedIndex);
978 if (isLast)
979 {
980 g.drawLine(x, y, rect.x + 1, y);
981 }
982 else
983 {
984 g.drawLine(x, y, rect.x, y);
985 }
986
987 int right = x + w - 1;
988 if (rect.x + rect.width < right - 1)
989 {
990 if (isLast)
991 {
992 g.drawLine(rect.x + rect.width - 1, y, right - 1, y);
993 }
994 else
995 {
996 g.drawLine(rect.x + rect.width, y, right - 1, y);
997 }
998 }
999 else
1000 {
1001 g.setColor(shadow);
1002 g.drawLine(x + w - 2, y, x + w - 2, y);
1003 }
1004
1005 // When in OceanTheme, draw another white line.
1006 if (isOcean)
1007 {
1008 g.setColor(MetalLookAndFeel.getWhite());
1009 if (isLast)
1010 {
1011 g.drawLine(x, y + 1, rect.x + 1, y + 1);
1012 }
1013 else
1014 {
1015 g.drawLine(x, y + 1, rect.x, y + 1);
1016 }
1017
1018 if (rect.x + rect.width < right - 1)
1019 {
1020 if (isLast)
1021 {
1022 g.drawLine(rect.x + rect.width - 1, y + 1, right - 1,
1023 y + 1);
1024 }
1025 else
1026 {
1027 g.drawLine(rect.x + rect.width, y + 1, right - 1, y + 1);
1028 }
1029 }
1030 else
1031 {
1032 g.setColor(shadow);
1033 g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1);
1034 }
1035 }
1036 }
1037 }
1038
1039 /**
1040 * Paints the lower edge of the content border.
1041 *
1042 * @param g the graphics to use for painting
1043 * @param tabPlacement the tab placement
1044 * @param selectedIndex the index of the selected tab
1045 * @param x the upper left coordinate of the content area
1046 * @param y the upper left coordinate of the content area
1047 * @param w the width of the content area
1048 * @param h the height of the content area
1049 */
1050 protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
1051 int selectedIndex, int x, int y,
1052 int w, int h)
1053 {
1054 g.setColor(darkShadow);
1055
1056 // If tabs are not placed on BOTTOM, or if the selected tab is not in the
1057 // run directly below the content or the selected tab is not visible,
1058 // then we draw an unbroken line.
1059 Rectangle rect = selectedIndex < 0 ? null :
1060 getTabBounds(selectedIndex, calcRect);
1061 boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
1062 Color oceanSelectedBorder =
1063 UIManager.getColor("TabbedPane.borderHightlightColor");
1064 if (tabPlacement != BOTTOM || selectedIndex < 0 || rect.y - 1 > h
1065 || rect.x < x || rect.x > x + w)
1066 {
1067 if (isOcean && tabPlacement == BOTTOM)
1068 {
1069 g.setColor(oceanSelectedBorder);
1070 }
1071 g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
1072 }
1073 else
1074 {
1075 boolean isLast = isLastTabInRun(selectedIndex);
1076 if (isOcean)
1077 {
1078 g.setColor(oceanSelectedBorder);
1079 }
1080
1081 int bottom = y + h - 1;
1082 int right = x + w - 1;
1083 if (isLast)
1084 {
1085 g.drawLine(x, bottom, rect.x, bottom);
1086 }
1087 else
1088 {
1089 g.drawLine(x, bottom, rect.x - 1, bottom);
1090 }
1091
1092 if (rect.x + rect.width < x + w - 2)
1093 {
1094 if (isLast)
1095 {
1096 g.drawLine(rect.x + rect.width - 1, bottom, right, bottom);
1097 }
1098 else
1099 {
1100 g.drawLine(rect.x + rect.width, bottom, right, bottom);
1101 }
1102 }
1103 }
1104 }
1105
1106 /**
1107 * Paints the left edge of the content border.
1108 *
1109 * @param g the graphics to use for painting
1110 * @param tabPlacement the tab placement
1111 * @param selectedIndex the index of the selected tab
1112 * @param x the upper left coordinate of the content area
1113 * @param y the upper left coordinate of the content area
1114 * @param w the width of the content area
1115 * @param h the height of the content area
1116 */
1117 protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
1118 int selectedIndex, int x, int y,
1119 int w, int h)
1120 {
1121 boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
1122 Color oceanSelectedBorder =
1123 UIManager.getColor("TabbedPane.borderHightlightColor");
1124 Rectangle rect = selectedIndex < 0 ? null :
1125 getTabBounds(selectedIndex, calcRect);
1126
1127 if (isOcean)
1128 {
1129 g.setColor(oceanSelectedBorder);
1130 }
1131 else
1132 {
1133 g.setColor(selectHighlight);
1134 }
1135
1136 // If tabs are not placed on LEFT, or if the selected tab is not in the
1137 // run directly left to the content or the selected tab is not visible,
1138 // then we draw an unbroken line.
1139 if (tabPlacement != LEFT || selectedIndex < 0
1140 || rect.x + rect.width + 1 < x || rect.y < y || rect.y > y + h)
1141 {
1142 g.drawLine(x, y + 1, x, y + h - 2);
1143 if (isOcean && tabPlacement == LEFT)
1144 {
1145 g.setColor(MetalLookAndFeel.getWhite());
1146 g.drawLine(x, y + 1, x, y + h - 2);
1147 }
1148 }
1149 else
1150 {
1151 g.drawLine(x, y, x, rect.y + 1);
1152 if (rect.y + rect.height < y + h - 2)
1153 {
1154 g.drawLine(x, rect.y + rect.height + 1, x, y + h + 2);
1155 }
1156 if (isOcean)
1157 {
1158 g.setColor(MetalLookAndFeel.getWhite());
1159 g.drawLine(x + 1, y + 1, x + 1, rect.y + 1);
1160 if (rect.y + rect.height < y + h - 2)
1161 {
1162 g.drawLine(x + 1, rect.y + rect.height + 1, x + 1, y + h + 2);
1163 }
1164 }
1165 }
1166
1167 }
1168
1169 /**
1170 * Paints the right edge of the content border.
1171 *
1172 * @param g the graphics to use for painting
1173 * @param tabPlacement the tab placement
1174 * @param selectedIndex the index of the selected tab
1175 * @param x the upper left coordinate of the content area
1176 * @param y the upper left coordinate of the content area
1177 * @param w the width of the content area
1178 * @param h the height of the content area
1179 */
1180 protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
1181 int selectedIndex, int x, int y,
1182 int w, int h)
1183 {
1184 g.setColor(darkShadow);
1185 Rectangle rect = selectedIndex < 0 ? null :
1186 getTabBounds(selectedIndex, calcRect);
1187 boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
1188 Color oceanSelectedBorder =
1189 UIManager.getColor("TabbedPane.borderHightlightColor");
1190
1191 // If tabs are not placed on RIGHT, or if the selected tab is not in the
1192 // run directly right to the content or the selected tab is not visible,
1193 // then we draw an unbroken line.
1194 if (tabPlacement != RIGHT || selectedIndex < 0 || rect.x - 1 > w
1195 || rect.y < y || rect.y > y + h)
1196 {
1197 if (isOcean && tabPlacement == RIGHT)
1198 {
1199 g.setColor(oceanSelectedBorder);
1200 }
1201 g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
1202 }
1203 else
1204 {
1205 if (isOcean)
1206 {
1207 g.setColor(oceanSelectedBorder);
1208 }
1209 g.drawLine(x + w - 1, y, x + w - 1, rect.y);
1210
1211 if (rect.y + rect.height < y + h - 2)
1212 {
1213 g.drawLine(x + w - 1, rect.y + rect.height, x + w - 1, y + h - 2);
1214 }
1215 }
1216 }
1217
1218 /**
1219 * Determines if the specified tab is the last tab in its tab run.
1220 *
1221 * @param tabIndex the index of the tab
1222 *
1223 * @return if the specified tab is the last tab in its tab run
1224 */
1225 private boolean isLastTabInRun(int tabIndex)
1226 {
1227 int count = tabPane.getTabCount();
1228 int run = getRunForTab(count, tabIndex);
1229 int lastIndex = lastTabInRun(count, run);
1230 return tabIndex == lastIndex;
1231 }
1232
1233 /**
1234 * Returns the background for an unselected tab. This first asks the
1235 * JTabbedPane for the background at the specified tab index, if this
1236 * is an UIResource (that means, it is inherited from the JTabbedPane)
1237 * and the TabbedPane.unselectedBackground UI property is not null,
1238 * this returns the value of the TabbedPane.unselectedBackground property,
1239 * otherwise the value returned by the JTabbedPane.
1240 *
1241 * @param tabIndex the index of the tab for which we query the background
1242 *
1243 * @return the background for an unselected tab
1244 */
1245 private Color getUnselectedBackground(int tabIndex)
1246 {
1247 Color bg = tabPane.getBackgroundAt(tabIndex);
1248 Color unselectedBackground =
1249 UIManager.getColor("TabbedPane.unselectedBackground");
1250 if (bg instanceof UIResource && unselectedBackground != null)
1251 bg = unselectedBackground;
1252 return bg;
1253 }
1254
1255 protected int getTabLabelShiftX(int tabPlacement,
1256 int index,
1257 boolean isSelected)
1258 {
1259 return 0;
1260 }
1261
1262 protected int getTabLabelShiftY(int tabPlacement,
1263 int index,
1264 boolean isSelected)
1265 {
1266 return 0;
1267 }
1268
1269 }