OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [javax/] [swing/] [plaf/] [basic/] [BasicTabbedPaneUI.java] - Blame information for rev 772

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 772 jeremybenn
/* 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
 
98
    NavigateAction(String name, int dir)
99
    {
100
      super(name);
101
      direction = dir;
102
    }
103
 
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
 
117
    public NavigatePageDownAction()
118
    {
119
      super("navigatePageDown");
120
    }
121
 
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
 
140
    public NavigatePageUpAction()
141
    {
142
      super("navigatePageUp");
143
    }
144
 
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
 
163
    public RequestFocusAction()
164
    {
165
      super("requestFocus");
166
    }
167
 
168
    public void actionPerformed(ActionEvent event)
169
    {
170
      ((JTabbedPane) event.getSource()).requestFocus();
171
    }
172
 
173
  }
174
 
175
  static class RequestFocusForVisibleComponentAction extends AbstractAction
176
  {
177
 
178
    public RequestFocusForVisibleComponentAction()
179
    {
180
      super("requestFocusForVisibleComponent");
181
    }
182
 
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
     */
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
     */
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
  {
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
     */
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
       */
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
     */
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
     */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
2095
  protected void installComponents()
2096
  {
2097
    // Nothing to be done.
2098
  }
2099
 
2100
  /**
2101
   * This method uninstalls components for this JTabbedPane.
2102
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
2262
  protected PropertyChangeListener createPropertyChangeListener()
2263
  {
2264
    return new PropertyChangeHandler();
2265
  }
2266
 
2267
  /**
2268
   * This method installs keyboard actions for the JTabbedPane.
2269
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
   */
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
 
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
 
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
   */
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
   */
3999
  protected int getRolloverTab()
4000
  {
4001
    return rolloverTab;
4002
  }
4003
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.