View Javadoc

1   /*
2    * Copyright (c) 2003-2008 by Cosylab d. d.
3    *
4    * This file is part of CosyBeans-Common.
5    *
6    * CosyBeans-Common 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 3 of the License, or
9    * (at your option) any later version.
10   *
11   * CosyBeans-Common is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with CosyBeans-Common.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package com.cosylab.gui.components.util;
21  
22  import java.awt.BasicStroke;
23  import java.awt.Color;
24  import java.awt.Component;
25  import java.awt.Container;
26  import java.awt.Dimension;
27  import java.awt.Graphics;
28  import java.awt.Graphics2D;
29  import java.awt.GridLayout;
30  import java.awt.Insets;
31  import java.awt.LayoutManager;
32  import java.awt.LayoutManager2;
33  import java.awt.Rectangle;
34  
35  import javax.swing.JButton;
36  import javax.swing.border.Border;
37  import javax.swing.plaf.metal.MetalBorders;
38  import javax.swing.plaf.metal.MetalTabbedPaneUI;
39  
40  import com.cosylab.gui.components.SimpleButton;
41  
42  
43  /**
44   * A class containing user interface and border inner classes,
45   * used in rendering some of the Swing components
46   *
47   * @author <a href="mailto:miha.kadunc@cosylab.com">Miha Kadunc</a>
48   * @version $id$
49   */
50  public class CosyUIElements {
51      public static final int BOTTOM = 1;
52      public static final int LEFT = 2;
53      public static final int RIGHT = 3;
54      public static final int TOP = 0;
55      private static Border panelBorder = null;
56      private static Border containerBorder = null;
57      private static Border flushBorder = null;
58      //private static Border[] lineBorders = new Border[4];
59  
60      public static Border getPlainBorder(boolean raised) {
61          if (raised) {
62              if (panelBorder == null) {
63                  panelBorder = new PanelFlushBorder(true, true);
64              }
65  
66              return panelBorder;
67          } else {
68              if (containerBorder == null) {
69                  containerBorder = new PanelFlushBorder(false, true);
70              }
71  
72              return containerBorder;
73          }
74      }
75  
76      public static Border getSingleLineBorder(int location, Color color) {
77          return new SingleLineBorder(location, color);
78      }
79  
80      public static Border getFlushBorder() {
81          if (flushBorder == null) {
82              flushBorder = new MetalBorders.Flush3DBorder();
83          }
84  
85          return flushBorder;
86      }
87  
88      public static class ButtonOnRightLayout implements LayoutManager2 {
89          private static String BUTTON = "Button";
90  		// notused private static String OTHER = "Other";
91          private Dimension minSize = new Dimension(60, 20);
92          private Dimension prefSize = new Dimension(2, 20);
93          private Component button = null;
94          private Component other = null;
95  
96          /**
97           * @see java.awt.LayoutManager#addLayoutComponent(String, Component)
98           */
99          public void addLayoutComponent(String name, Component comp) {
100             if (comp instanceof SimpleButton || comp instanceof JButton) {
101                 button = comp;
102             } else {
103                 other = comp;
104             }
105 
106             prefSize.width = prefSize.width + comp.getPreferredSize().width;
107         }
108 
109         /**
110          * @see java.awt.LayoutManager#layoutContainer(Container)
111          */
112         public void layoutContainer(Container parent) {
113             int x = parent.getInsets().left;
114             int y = parent.getInsets().top;
115             int w = parent.getWidth() - x - parent.getInsets().right;
116             int h = parent.getHeight() - y - parent.getInsets().bottom;
117 
118             if (button != null) {
119                 button.setBounds((x + w) - h, y, h, h);
120                 w = w - h;
121             }
122 
123             if (other != null) {
124                 other.setBounds(x, y, w, h);
125             }
126         }
127 
128         /**
129          * @see java.awt.LayoutManager#minimumLayoutSize(Container)
130          */
131         public Dimension minimumLayoutSize(Container parent) {
132             minSize.width = 2;
133             minSize.height = 20;
134 
135             if (button != null) {
136                 minSize.width = minSize.width + button.getMinimumSize().width;
137             }
138 
139             if (other != null) {
140                 minSize.width = minSize.width + other.getMinimumSize().width;
141                 minSize.height = other.getMinimumSize().height;
142             }
143 
144             return minSize;
145         }
146 
147         /**
148          * @see java.awt.LayoutManager#preferredLayoutSize(Container)
149          */
150         public Dimension preferredLayoutSize(Container parent) {
151             prefSize.width = 0;
152             prefSize.height = 20;
153 
154             if (button != null) {
155                 prefSize.width = prefSize.width +
156                     button.getPreferredSize().width;
157             }
158 
159             if (other != null) {
160                 prefSize.width = prefSize.width +
161                     other.getPreferredSize().width;
162                 prefSize.height = other.getPreferredSize().height;
163             }
164 
165             return prefSize;
166         }
167 
168         /**
169          * @see java.awt.LayoutManager#removeLayoutComponent(Component)
170          */
171         public void removeLayoutComponent(Component comp) {
172             if (comp.equals(button)) {
173                 button = null;
174             } else if (comp.equals(other)) {
175                 other = null;
176             }
177         }
178 
179         /**
180          * @see java.awt.LayoutManager2#addLayoutComponent(java.awt.Component, java.lang.Object)
181          */
182         public void addLayoutComponent(Component comp, Object constraints) {
183             if (constraints == BUTTON) {
184                 button = comp;
185             } else {
186                 other = comp;
187             }
188 
189             prefSize.width = prefSize.width + comp.getPreferredSize().width;
190         }
191 
192         /**
193          * @see java.awt.LayoutManager2#getLayoutAlignmentX(java.awt.Container)
194          */
195         public float getLayoutAlignmentX(Container target) {
196             return 0;
197         }
198 
199         /**
200          * @see java.awt.LayoutManager2#getLayoutAlignmentY(java.awt.Container)
201          */
202         public float getLayoutAlignmentY(Container target) {
203             return 0;
204         }
205 
206         /**
207          * @see java.awt.LayoutManager2#invalidateLayout(java.awt.Container)
208          */
209         public void invalidateLayout(Container target) {
210         }
211 
212         /**
213          * @see java.awt.LayoutManager2#maximumLayoutSize(java.awt.Container)
214          */
215         public Dimension maximumLayoutSize(Container target) {
216             return prefSize;
217         }
218     }
219 
220     /**
221      *
222      *
223      * @version @@VERSION@@
224      * @author Miha Kadunc (miha.kadunc@cosylab.com)
225      */
226     public static class TwinOptimalLayout implements LayoutManager {
227         private int horBorder;
228         private int vertBorder;
229         private int spaceBetween;
230         private Component[] comps;
231 
232         public TwinOptimalLayout(int horBorder, int vertBorder, int spaceBetween) {
233             this.horBorder = horBorder;
234             this.vertBorder = vertBorder;
235             this.spaceBetween = spaceBetween;
236             comps = new Component[2];
237         }
238 
239         /**
240          * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String, java.awt.Component)
241          */
242         public void addLayoutComponent(String name, Component comp) {
243             if (comps[0] == null) {
244                 comps[0] = comp;
245             } else {
246                 comps[1] = comp;
247             }
248         }
249 
250         /**
251          * @see java.awt.LayoutManager#layoutContainer(java.awt.Container)
252          */
253         public void layoutContainer(Container parent) {
254             int w = parent.getWidth();
255             int h = parent.getHeight();
256             double ratio = (double) w / (double) h;
257 
258             if (ratio > 1) {
259                 int cw = (w - (2 * horBorder) - spaceBetween) / 2;
260 
261                 if (comps[0] != null) {
262                     comps[0].setBounds(horBorder, vertBorder, cw,
263                         h - (2 * vertBorder));
264                 }
265 
266                 if (comps[1] != null) {
267                     comps[1].setBounds(w - cw - horBorder, vertBorder, cw,
268                         h - (2 * vertBorder));
269                 }
270             } else {
271                 int ch = (h - (2 * vertBorder) - spaceBetween) / 2;
272 
273                 if (comps[0] != null) {
274                     comps[0].setBounds(horBorder, vertBorder,
275                         w - (2 * horBorder), ch);
276                 }
277 
278                 if (comps[1] != null) {
279                     comps[1].setBounds(horBorder, h - ch - vertBorder,
280                         w - (2 * horBorder), ch);
281                 }
282             }
283         }
284 
285         /**
286          * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container)
287          */
288         public Dimension minimumLayoutSize(Container parent) {
289             return new Dimension(10, 12);
290         }
291 
292         /**
293          * @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container)
294          */
295         public Dimension preferredLayoutSize(Container parent) {
296             return new Dimension(30, 20);
297         }
298 
299         /**
300          * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component)
301          */
302         public void removeLayoutComponent(Component comp) {
303             if (comps[0] == comp) {
304                 comps[0] = null;
305             }
306 
307             if (comps[1] == comp) {
308                 comps[1] = null;
309             }
310         }
311     }
312 
313     /**
314      * A class implementing the user interface of a <code>JTabbedPane</code>.
315      * It assumes that the tabs are situated at the top of the pane,
316      * and that the components contained inside the tabbed pane have a
317      * <code>PanelFlushBorder</code>.
318      */
319     public static class FlushTabbedPaneUI extends MetalTabbedPaneUI {
320         private static final Insets ZERO_INSETS = new Insets(0, 0, 0, 0);
321 
322         /**
323          * @see javax.swing.plaf.basic.BasicTabbedPaneUI#getContentBorderInsets(int)
324          */
325         protected Insets getContentBorderInsets(int tabPlacement) {
326             return ZERO_INSETS;
327         }
328 
329         /**
330          * @see javax.swing.plaf.basic.BasicTabbedPaneUI#getTabAreaInsets(int)
331          */
332         protected Insets getTabAreaInsets(int tabPlacement) {
333             return ZERO_INSETS;
334         }
335 
336         /**
337          * @see javax.swing.plaf.basic.BasicTabbedPaneUI#paintFocusIndicator(Graphics, int, Rectangle[], int, Rectangle, Rectangle, boolean)
338          */
339         protected void paintFocusIndicator(Graphics g, int tabPlacement,
340             Rectangle[] rects, int tabIndex, Rectangle iconRect,
341             Rectangle textRect, boolean isSelected) {
342             if (tabPane.hasFocus() && isSelected) {
343                 Rectangle tabRect = rects[tabIndex];
344                 boolean lastInRun = (tabIndex == (tabPane.getTabCount() - 1));
345                 g.setColor(focus);
346                 g.translate(tabRect.x, tabRect.y);
347 
348                 int right = tabRect.width - 2;
349 
350                 if (lastInRun) {
351                     right = right - 1;
352                 }
353 
354                 int bottom = tabRect.height - 1;
355                 boolean leftToRight = true;
356 
357                 switch (tabPlacement) {
358                 case RIGHT:
359                     g.drawLine(right - 6, 2, right - 2, 6); // slant
360                     g.drawLine(1, 2, right - 6, 2); // top
361                     g.drawLine(right - 2, 6, right - 2, bottom); // right
362                     g.drawLine(1, 2, 1, bottom); // left
363                     g.drawLine(1, bottom, right - 2, bottom); // bottom
364 
365                     break;
366 
367                 case BOTTOM:
368 
369                     if (leftToRight) {
370                         g.drawLine(2, bottom - 6, 6, bottom - 2); // slant
371                         g.drawLine(6, bottom - 2, right, bottom - 2); // bottom
372                         g.drawLine(2, 0, 2, bottom - 6); // left
373                         g.drawLine(2, 0, right, 0); // top
374                         g.drawLine(right, 0, right, bottom - 2); // right
375                     } else {
376                         g.drawLine(right - 2, bottom - 6, right - 6, bottom -
377                             2); // slant
378                         g.drawLine(right - 2, 0, right - 2, bottom - 6); // right
379 
380                         if (lastInRun) {
381                             // last tab in run
382                             g.drawLine(2, bottom - 2, right - 6, bottom - 2); // bottom
383                             g.drawLine(2, 0, right - 2, 0); // top
384                             g.drawLine(2, 0, 2, bottom - 2); // left
385                         } else {
386                             g.drawLine(1, bottom - 2, right - 6, bottom - 2); // bottom
387                             g.drawLine(1, 0, right - 2, 0); // top
388                             g.drawLine(1, 0, 1, bottom - 2); // left
389                         }
390                     }
391 
392                     break;
393 
394                 case LEFT:
395                     g.drawLine(2, 2, 2, bottom - 1); // left
396                     g.drawLine(2, 2, right, 2); // top
397                     g.drawLine(right, 2, right, bottom - 1); // right
398                     g.drawLine(2, bottom - 1, right, bottom - 1); // bottom
399 
400                     break;
401 
402                 case TOP:default:
403 
404                     if (leftToRight) {
405                         g.drawLine(2, 5, 5, 2); //slant
406                         g.drawLine(2, 5, 2, bottom - 2); // left
407                         g.drawLine(5, 2, right - 4, 2); // top
408                         g.drawLine(right - 4, 2, right - 1, 5); //right slant
409                         g.drawLine(right - 1, 5, right - 1, bottom - 2); // right
410                         g.drawLine(2, bottom - 2, right - 1, bottom - 2); // bottom
411                     } else {
412                         g.drawLine(right - 2, 6, right - 6, 2); // slant
413                         g.drawLine(right - 2, 6, right - 2, bottom - 1); // right
414 
415                         if (lastInRun) {
416                             // last tab in run
417                             g.drawLine(right - 6, 2, 2, 2); // top
418                             g.drawLine(2, 2, 2, bottom - 1); // left
419                             g.drawLine(right - 2, bottom - 1, 2, bottom - 1); // bottom
420                         } else {
421                             g.drawLine(right - 6, 2, 1, 2); // top
422                             g.drawLine(1, 2, 1, bottom - 1); // left
423                             g.drawLine(right - 2, bottom - 1, 1, bottom - 1); // bottom
424                         }
425                     }
426                 }
427 
428                 g.translate(-tabRect.x, -tabRect.y);
429             }
430         }
431 
432         /**
433          * @see javax.swing.plaf.metal.MetalTabbedPaneUI#paintTopTabBorder(int, Graphics, int, int, int, int, int, int, boolean)
434          */
435         protected void paintTopTabBorder(int tabIndex, Graphics g, int x,
436             int y, int w, int h, int btm, int rght, boolean isSelected) {
437             int currentRun = getRunForTab(tabPane.getTabCount(), tabIndex);
438             // notused int lastIndex = lastTabInRun(tabPane.getTabCount(), currentRun);
439             int firstIndex = tabRuns[currentRun];
440             boolean leftToRight = true;
441             int bottom = h - 1;
442             int right = w - 1;
443 
444             g.translate(x, y);
445 
446             //
447             // Paint Border
448             //
449             g.setColor(shadow);
450 
451             if (leftToRight) {
452                 //right slant
453                 g.drawLine(right - 4, 0, right, 4);
454             }
455 
456             g.setColor(darkShadow);
457 
458             if (leftToRight) {
459                 /*      // Paint top
460                           g.drawLine( 0, 0, right, 0);
461 
462                 */
463 
464                 // Paint right
465                 g.drawLine(right, 4, right, bottom);
466 
467                 //Paint bottom
468                 g.drawLine(1, bottom, right - 1, bottom);
469 
470                 /*      // Paint left
471                           g.drawLine( 0, 1, 0, bottom);
472                   */
473             }
474 
475             //
476             // Paint Highlight
477             //
478             g.setColor(isSelected ? selectHighlight : highlight);
479 
480             if (leftToRight) {
481                 //Paint slant
482                 g.drawLine(0, 4, 4, 0);
483 
484                 //Paint top     
485                 g.drawLine(4, 0, right - 4, 0);
486 
487                 // Paint left
488                 g.drawLine(0, 5, 0, bottom);
489 
490                 // paint highlight in the gap on tab behind this one
491                 // on the left end (where they all line up)
492                 if ((tabIndex == firstIndex) &&
493                         (tabIndex != tabRuns[runCount - 1])) {
494                     //  first tab in run but not first tab in last run
495                     if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1]) {
496                         // tab in front of selected tab
497                         g.setColor(selectHighlight);
498                     } else {
499                         // tab in front of normal tab
500                         g.setColor(highlight);
501                     }
502 
503                     g.drawLine(1, 0, 1, 4);
504                 }
505             }
506 
507             g.translate(-x, -y);
508         }
509 
510         /**
511          * @see javax.swing.plaf.basic.BasicTabbedPaneUI#paintTabBackground(Graphics, int, int, int, int, int, int, boolean)
512          */
513         protected void paintTabBackground(Graphics g, int tabPlacement,
514             int tabIndex, int x, int y, int w, int h, boolean isSelected) {
515 				// notusedint slantWidth = h / 2;
516 
517             if (isSelected) {
518                 g.setColor(selectColor);
519             } else {
520                 g.setColor(tabPane.getBackgroundAt(tabIndex));
521             }
522 
523             switch (tabPlacement) {
524             case LEFT:
525                 g.fillRect(x + 2, y + 1, w - 2, h - 1);
526 
527                 break;
528 
529             case BOTTOM:
530                 g.fillRect(x + 2, y, w - 2, h - 4);
531                 g.fillRect(x + 5, (y + (h - 1)) - 3, w - 5, 3);
532 
533                 break;
534 
535             case RIGHT:
536                 g.fillRect(x + 1, y + 1, w - 5, h - 1);
537                 g.fillRect((x + (w - 1)) - 3, y + 5, 3, h - 5);
538 
539                 break;
540 
541             case TOP:default:
542                 g.fillRect(x + 1, y + 3, 2, h - 3); //left
543                 g.fillRect(x + 3, y + 1, w - 6, h - 1); //center
544                 g.fillRect(x + (w - 3), y + 3, 2, h - 3); //right
545             }
546         }
547     }
548 
549     public static final class FillingGridLayout extends GridLayout {
550 
551 		private static final long serialVersionUID = 1L;
552 
553 		/**
554          * Creates a grid layout with a default of one column per component,
555          * in a single row.
556          *
557          * @since JDK1.1
558          */
559         public FillingGridLayout() {
560             this(1, 0, 0, 0);
561         }
562 
563         /**
564          * Creates a grid layout with the specified number of rows and
565          * columns. All components in the layout are given equal size.
566          * <p>
567          * One, but not both, of <code>rows</code> and <code>cols</code> can
568          * be zero, which means that any number of objects can be placed in a
569          * row or in a column.
570          *
571          * @param     rows   the rows, with the value zero meaning
572          *                   any number of rows.
573          * @param     cols   the columns, with the value zero meaning
574          *                   any number of columns.
575          */
576         public FillingGridLayout(int rows, int cols) {
577             this(rows, cols, 0, 0);
578         }
579 
580         /**
581          * Creates a grid layout with the specified number of rows and
582          * columns. All components in the layout are given equal size.
583          * <p>
584          * In addition, the horizontal and vertical gaps are set to the
585          * specified values. Horizontal gaps are placed at the left and
586          * right edges, and between each of the columns. Vertical gaps are
587          * placed at the top and bottom edges, and between each of the rows.
588          * <p>
589          * One, but not both, of <code>rows</code> and <code>cols</code> can
590          * be zero, which means that any number of objects can be placed in a
591          * row or in a column.
592          * <p>
593          * All <code>GridLayout</code> constructors defer to this one.
594          *
595          * @param     rows   the rows, with the value zero meaning
596          *                   any number of rows
597          * @param     cols   the columns, with the value zero meaning
598          *                   any number of columns
599          * @param     hgap   the horizontal gap
600          * @param     vgap   the vertical gap
601          * @exception   IllegalArgumentException  if the value of both
602          *      <code>rows</code> and <code>cols</code> is
603          *      set to zero
604          */
605         public FillingGridLayout(int rows, int cols, int hgap, int vgap) {
606             super(rows, cols, hgap, vgap);
607         }
608 
609         /**
610          * Lays out the specified container using this layout.
611          * <p>
612          * This method reshapes the components in the specified target
613          * container in order to satisfy the constraints of the
614          * <code>GridLayout</code> object.
615          * <p>
616          * The grid layout manager determines the size of individual
617          * components by dividing the free space in the container into
618          * equal-sized portions according to the number of rows and columns
619          * in the layout. The container's free space equals the container's
620          * size minus any insets and any specified horizontal or vertical
621          * gap. All components in a grid layout are given the same size.
622          *
623          * @param      target   the container in which to do the layout
624          * @see        java.awt.Container
625          * @see        java.awt.Container#doLayout
626          */
627         public void layoutContainer(Container parent) {
628             synchronized (parent.getTreeLock()) {
629                 Insets insets = parent.getInsets();
630                 int ncomponents = parent.getComponentCount();
631                 int nrows = getRows();
632                 int ncols = getColumns();
633                 boolean ltr = parent.getComponentOrientation().isLeftToRight();
634 
635                 if (ncomponents == 0) {
636                     return;
637                 }
638 
639                 if (nrows > 0) {
640                     ncols = ((ncomponents + nrows) - 1) / nrows;
641                 } else {
642                     nrows = ((ncomponents + ncols) - 1) / ncols;
643                 }
644 
645                 double w = parent.getWidth() - (insets.left + insets.right);
646                 double h = parent.getHeight() - (insets.top + insets.bottom);
647                 w = (w - (((double) ncols - 1) * getHgap())) / ncols;
648                 h = (h - (((double) nrows - 1) * getVgap())) / nrows;
649 
650                 if (ltr) {
651                     int oldx = insets.left;
652                     double x = oldx;
653                     int oldy;
654                     double y;
655 
656                     for (int c = 0; c < ncols; c++) {
657                         x = x + w + getVgap();
658                         oldy = insets.top;
659                         y = oldy;
660 
661                         for (int r = 0; r < nrows; r++) {
662                             int i = (r * ncols) + c;
663                             y = y + h + getVgap();
664 
665                             if (i < ncomponents) {
666                                 parent.getComponent(i).setBounds(oldx,
667                                     oldy, (int) (x - oldx),
668                                     (int) (y - oldy));
669                             }
670 
671                             oldy = oldy + (int) (y - oldy);
672                         }
673 
674                         oldx = oldx + (int) (x - oldx);
675                     }
676                 } else {
677                     double x = parent.getWidth() - insets.right - w;
678                     double y;
679 
680                     for (int c = 0; c < ncols; c++, x -= (w + getHgap())) {
681                         y = insets.top;
682 
683                         for (int r = 0; r < nrows; r++, y += (h + getVgap())) {
684                             int i = (r * ncols) + c;
685 
686                             if (i < ncomponents) {
687                                 parent.getComponent(i).setBounds((int) x,
688                                     (int) y, (int) w, (int) h);
689                             }
690                         }
691                     }
692                 }
693             }
694         }
695     }
696 
697     /**
698      * A class implementing the user interface of a <code>JTabbedPane</code>.
699      * It assumes that the tabs are situated at the top of the pane,
700      * and that the components contained inside the tabbed pane have no
701      * border.
702      */
703     public static final class PlainTabbedPaneUI extends FlushTabbedPaneUI {
704         private static final Insets CONTENT_INSETS = new Insets(1, 1, 1, 1);
705 
706         /**
707          * @see javax.swing.plaf.basic.BasicTabbedPaneUI#getContentBorderInsets(int)
708          */
709         protected Insets getContentBorderInsets(int tabPlacement) {
710             return CONTENT_INSETS;
711         }
712 
713         /**
714          * @see javax.swing.plaf.metal.MetalTabbedPaneUI#paintTopTabBorder(int, Graphics, int, int, int, int, int, int, boolean)
715          */
716         protected void paintTopTabBorder(int tabIndex, Graphics g, int x,
717             int y, int w, int h, int btm, int rght, boolean isSelected) {
718             super.paintTopTabBorder(tabIndex, g, x, y, w, h, btm, rght,
719                 isSelected);
720 
721             int bottom = h - 1;
722             int right = w - 1;
723 
724             g.translate(x, y);
725 
726             if (isSelected) {
727                 g.setColor(selectColor);
728             } else {
729                 g.setColor(tabPane.getBackgroundAt(tabIndex));
730             }
731 
732             //Paint bottom
733             g.drawLine(1, bottom, right - 1, bottom);
734             g.translate(-x, -y);
735         }
736 
737         /**
738          * @see javax.swing.plaf.basic.BasicTabbedPaneUI#paintContentBorder(Graphics, int, int)
739          */
740         protected void paintContentBorder(Graphics g, int tabPlacement,
741             int selectedIndex) {
742             super.paintContentBorder(g, tabPlacement, selectedIndex);
743 
744             Rectangle tab = new Rectangle();
745             getTabBounds(selectedIndex, tab);
746             PanelFlushBorder.paintBorder(g, 0, tab.y + tab.height,
747                 g.getClipBounds().width,
748                 g.getClipBounds().height - tab.height - tab.y);
749             g.setColor(selectColor);
750             g.drawLine(tab.x + 1, tab.y + tab.height, (tab.x + tab.width) - 2,
751                 tab.y + tab.height);
752             g.setColor(shadow);
753             g.drawLine((tab.x + tab.width) - 1, tab.y + tab.height,
754                 (tab.x + tab.width) - 1, tab.y + tab.height);
755         }
756     }
757 
758     /**
759      * A clean border that visually raises the component above its surroundings.
760      * Should be used in combination with ContainerFlushBorder.
761      *
762      * @author  Miha Kadunc
763      * @version @@VERSION@@
764      */
765     public static class PanelFlushBorder implements Border {
766         private final Insets insets = new Insets(1, 1, 1, 1);
767         private final Insets cloneInsets = new Insets(1, 1, 1, 1);
768         private boolean lockInsets = false;
769         private boolean raised = true;
770 
771         public PanelFlushBorder() {
772             this(true);
773         }
774 
775         public PanelFlushBorder(boolean isRaised) {
776             this(isRaised, false);
777         }
778 
779         private PanelFlushBorder(boolean isRaised, boolean lockInsets) {
780             this.lockInsets = lockInsets;
781             raised = isRaised;
782         }
783 
784         /**
785          * @see javax.swing.border.Border#paintBorder(Component, Graphics, int, int, int, int)
786          */
787         public void paintBorder(Component c, Graphics g, int x, int y,
788             int width, int height) {
789             if (raised) {
790                 paintBorder(g, x, y, width, height);
791             } else {
792                 paintBorder(g, (x + width) - 1, (y + height) - 1, 2 - width,
793                     2 - height);
794             }
795         }
796 
797         public static void paintBorder(Graphics g, int x, int y, int width,
798             int height) {
799         	((Graphics2D)g).setStroke(new BasicStroke());
800             g.setColor(ColorHelper.getControlShadow());
801             g.drawLine((x + width) - 1, y, (x + width) - 1, (y + height) - 1);
802             g.drawLine(x, (y + height) - 1, (x + width) - 1, (y + height) - 1);
803 
804             g.setColor(ColorHelper.getControlLightHighlight());
805             g.drawLine(x, y, x, (y + height) - 1);
806             g.drawLine(x, y, (x + width) - 1, y);
807 
808             g.setColor(ColorHelper.getControl());
809             g.drawLine((x + width) - 1, y, (x + width) - 1, y);
810             g.drawLine(x, (y + height) - 1, x, (y + height) - 1);
811         }
812 
813         /**
814          * @see javax.swing.border.Border#getBorderInsets(Component)
815          */
816         public Insets getBorderInsets(Component c) {
817             if (!lockInsets) {
818                 return insets;
819             } else {
820                 cloneInsets.top = insets.top;
821                 cloneInsets.bottom = insets.bottom;
822                 cloneInsets.left = insets.left;
823                 cloneInsets.right = insets.right;
824 
825                 return cloneInsets;
826             }
827         }
828 
829         /**
830          * @see javax.swing.border.Border#isBorderOpaque()
831          */
832         public boolean isBorderOpaque() {
833             return true;
834         }
835     }
836 
837     /**
838      * @author  Jernej Kamenik
839      */
840     private static final class SingleLineBorder implements Border {
841         private final Insets insets = new Insets(0, 0, 0, 0);
842         private Color color;
843         private int orientation;
844 
845         public SingleLineBorder(int orientation, Color color) {
846             this.orientation = orientation;
847             this.color = color;
848 
849             switch (orientation) {
850             case TOP:
851                 insets.top = 1;
852 
853                 break;
854 
855             case BOTTOM:
856                 insets.bottom = 1;
857 
858                 break;
859 
860             case LEFT:
861                 insets.left = 1;
862 
863                 break;
864 
865             case RIGHT:
866                 insets.right = 1;
867 
868                 break;
869             }
870         }
871 
872         /**
873          * @see javax.swing.border.Border#paintBorder(Component, Graphics, int, int, int, int)
874          */
875         public void paintBorder(Component c, Graphics g, int x, int y,
876             int width, int height) {
877             g.setColor(color);
878 
879             switch (orientation) {
880             case TOP:
881                 g.drawLine(x, y, (x + width) - 1, y);
882 
883                 break;
884 
885             case BOTTOM:
886                 g.drawLine(x, (y + height) - 1, (x + width) - 1,
887                     (y + height) - 1);
888 
889                 break;
890 
891             case LEFT:
892                 g.drawLine(x, y, x, (y + height) - 1);
893 
894                 break;
895 
896             case RIGHT:
897                 g.drawLine((x + width) - 1, y, (x + width) - 1, (y + height) -
898                     1);
899 
900                 break;
901             }
902         }
903 
904         /**
905          * @see javax.swing.border.Border#getBorderInsets(Component)
906          */
907         public Insets getBorderInsets(Component c) {
908             return insets;
909         }
910 
911         /**
912          * @see javax.swing.border.Border#isBorderOpaque()
913          */
914         public boolean isBorderOpaque() {
915             return true;
916         }
917     }
918 }