001 /*
002 * $Id: BasicTaskPaneUI.java 3366 2009-06-17 02:28:19Z kschaefe $
003 *
004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020 */
021 package org.jdesktop.swingx.plaf.basic;
022
023 import java.awt.Color;
024 import java.awt.Component;
025 import java.awt.Cursor;
026 import java.awt.Dimension;
027 import java.awt.Font;
028 import java.awt.Graphics;
029 import java.awt.Insets;
030 import java.awt.Rectangle;
031 import java.awt.event.ActionEvent;
032 import java.awt.event.FocusEvent;
033 import java.awt.event.FocusListener;
034 import java.awt.event.MouseEvent;
035 import java.beans.PropertyChangeEvent;
036 import java.beans.PropertyChangeListener;
037
038 import javax.swing.AbstractAction;
039 import javax.swing.Action;
040 import javax.swing.ActionMap;
041 import javax.swing.BorderFactory;
042 import javax.swing.Icon;
043 import javax.swing.InputMap;
044 import javax.swing.JComponent;
045 import javax.swing.JLabel;
046 import javax.swing.LookAndFeel;
047 import javax.swing.SwingUtilities;
048 import javax.swing.UIManager;
049 import javax.swing.border.Border;
050 import javax.swing.border.CompoundBorder;
051 import javax.swing.event.MouseInputAdapter;
052 import javax.swing.event.MouseInputListener;
053 import javax.swing.plaf.ActionMapUIResource;
054 import javax.swing.plaf.ColorUIResource;
055 import javax.swing.plaf.ComponentUI;
056 import javax.swing.plaf.FontUIResource;
057 import javax.swing.plaf.UIResource;
058 import javax.swing.plaf.basic.BasicGraphicsUtils;
059
060 import org.jdesktop.swingx.JXCollapsiblePane;
061 import org.jdesktop.swingx.JXHyperlink;
062 import org.jdesktop.swingx.JXTaskPane;
063 import org.jdesktop.swingx.SwingXUtilities;
064 import org.jdesktop.swingx.icon.EmptyIcon;
065 import org.jdesktop.swingx.plaf.TaskPaneUI;
066
067 /**
068 * Base implementation of the <code>JXTaskPane</code> UI.
069 *
070 * @author <a href="mailto:fred@L2FProd.com">Frederic Lavigne</a>
071 */
072 public class BasicTaskPaneUI extends TaskPaneUI {
073
074 private static FocusListener focusListener = new RepaintOnFocus();
075
076 public static ComponentUI createUI(JComponent c) {
077 return new BasicTaskPaneUI();
078 }
079
080 protected int titleHeight = 25;
081 protected int roundHeight = 5;
082
083 protected JXTaskPane group;
084
085 protected boolean mouseOver;
086 protected MouseInputListener mouseListener;
087
088 protected PropertyChangeListener propertyListener;
089
090 /**
091 * {@inheritDoc}
092 */
093 @Override
094 public void installUI(JComponent c) {
095 super.installUI(c);
096 group = (JXTaskPane) c;
097
098 installDefaults();
099 installListeners();
100 installKeyboardActions();
101 }
102
103 /**
104 * Installs default properties. Following properties are installed:
105 * <ul>
106 * <li>TaskPane.background</li>
107 * <li>TaskPane.foreground</li>
108 * <li>TaskPane.font</li>
109 * <li>TaskPane.borderColor</li>
110 * <li>TaskPane.titleForeground</li>
111 * <li>TaskPane.titleBackgroundGradientStart</li>
112 * <li>TaskPane.titleBackgroundGradientEnd</li>
113 * <li>TaskPane.titleOver</li>
114 * <li>TaskPane.specialTitleOver</li>
115 * <li>TaskPane.specialTitleForeground</li>
116 * <li>TaskPane.specialTitleBackground</li>
117 * </ul>
118 */
119 protected void installDefaults() {
120 LookAndFeel.installProperty(group, "opaque", true);
121 group.setBorder(createPaneBorder());
122 ((JComponent) group.getContentPane())
123 .setBorder(createContentPaneBorder());
124
125 LookAndFeel.installColorsAndFont(group, "TaskPane.background",
126 "TaskPane.foreground", "TaskPane.font");
127
128 LookAndFeel.installColorsAndFont((JComponent) group.getContentPane(),
129 "TaskPane.background", "TaskPane.foreground", "TaskPane.font");
130 }
131
132 /**
133 * Installs listeners for UI delegate.
134 */
135 protected void installListeners() {
136 mouseListener = createMouseInputListener();
137 group.addMouseMotionListener(mouseListener);
138 group.addMouseListener(mouseListener);
139
140 group.addFocusListener(focusListener);
141 propertyListener = createPropertyListener();
142 group.addPropertyChangeListener(propertyListener);
143 }
144
145 /**
146 * Installs keyboard actions to allow task pane to react on hot keys.
147 */
148 protected void installKeyboardActions() {
149 InputMap inputMap = (InputMap) UIManager.get("TaskPane.focusInputMap");
150 if (inputMap != null) {
151 SwingUtilities.replaceUIInputMap(group, JComponent.WHEN_FOCUSED,
152 inputMap);
153 }
154
155 ActionMap map = getActionMap();
156 if (map != null) {
157 SwingUtilities.replaceUIActionMap(group, map);
158 }
159 }
160
161 ActionMap getActionMap() {
162 ActionMap map = new ActionMapUIResource();
163 map.put("toggleCollapsed", new ToggleCollapsedAction());
164 return map;
165 }
166
167 @Override
168 public void uninstallUI(JComponent c) {
169 uninstallListeners();
170 super.uninstallUI(c);
171 }
172
173 /**
174 * Uninstalls previously installed listeners to free component for garbage collection.
175 */
176 protected void uninstallListeners() {
177 group.removeMouseListener(mouseListener);
178 group.removeMouseMotionListener(mouseListener);
179 group.removeFocusListener(focusListener);
180 group.removePropertyChangeListener(propertyListener);
181 }
182
183 /**
184 * Creates new toggle listener.
185 * @return MouseInputListener reacting on toggle events of task pane.
186 */
187 protected MouseInputListener createMouseInputListener() {
188 return new ToggleListener();
189 }
190
191 /**
192 * Creates property change listener for task pane.
193 * @return Property change listener reacting on changes to the task pane.
194 */
195 protected PropertyChangeListener createPropertyListener() {
196 return new ChangeListener();
197 }
198
199 /**
200 * Evaluates whenever given mouse even have occurred within borders of task pane.
201 * @param event Evaluated event.
202 * @return True if event occurred within task pane area, false otherwise.
203 */
204 protected boolean isInBorder(MouseEvent event) {
205 return event.getY() < getTitleHeight(event.getComponent());
206 }
207
208 /**
209 * Gets current title height. Default value is 25 if not specified otherwise. Method checks
210 * provided component for user set font (!instanceof FontUIResource), if font is set, height
211 * will be calculated from font metrics instead of using internal preset height.
212 * @return Current title height.
213 */
214 protected int getTitleHeight(Component c) {
215 if (c instanceof JXTaskPane) {
216 JXTaskPane taskPane = (JXTaskPane) c;
217 Font font = taskPane.getFont();
218 int height = titleHeight;
219
220 if (font != null && !(font instanceof FontUIResource)) {
221 height = Math.max(height, taskPane.getFontMetrics(font).getHeight());
222 }
223
224 Icon icon = taskPane.getIcon();
225
226 if (icon != null) {
227 height = Math.max(height, icon.getIconHeight() + 4);
228 }
229
230 return height;
231 }
232
233 return titleHeight;
234 }
235
236 /**
237 * Creates new border for task pane.
238 * @return Fresh border on every call.
239 */
240 protected Border createPaneBorder() {
241 return new PaneBorder();
242 }
243
244 @Override
245 public Dimension getPreferredSize(JComponent c) {
246 Component component = group.getComponent(0);
247 if (!(component instanceof JXCollapsiblePane)) {
248 // something wrong in this JXTaskPane
249 return super.getPreferredSize(c);
250 }
251
252 JXCollapsiblePane collapsible = (JXCollapsiblePane) component;
253 Dimension dim = collapsible.getPreferredSize();
254
255 Border groupBorder = group.getBorder();
256 if (groupBorder instanceof PaneBorder) {
257 ((PaneBorder) groupBorder).label.setDisplayedMnemonic(group
258 .getMnemonic());
259 Dimension border = ((PaneBorder) groupBorder)
260 .getPreferredSize(group);
261 dim.width = Math.max(dim.width, border.width);
262 dim.height += border.height;
263 } else {
264 dim.height += getTitleHeight(c);
265 }
266
267 return dim;
268 }
269
270 /**
271 * Creates content pane border.
272 * @return Fresh content pane border initialized with current value of TaskPane.borderColor
273 * on every call.
274 */
275 protected Border createContentPaneBorder() {
276 Color borderColor = UIManager.getColor("TaskPane.borderColor");
277 return new CompoundBorder(new ContentPaneBorder(borderColor),
278 BorderFactory.createEmptyBorder(10, 10, 10, 10));
279 }
280
281 @Override
282 public Component createAction(Action action) {
283 JXHyperlink link = new JXHyperlink(action) {
284 @Override
285 public void updateUI() {
286 super.updateUI();
287 // ensure the ui of this link is correctly update on l&f changes
288 configure(this);
289 }
290 };
291 configure(link);
292 return link;
293 }
294
295 /**
296 * Configures internally used hyperlink on new action creation and on every call to
297 * <code>updateUI()</code>.
298 * @param link Configured hyperlink.
299 */
300 protected void configure(JXHyperlink link) {
301 link.setOpaque(false);
302 link.setBorderPainted(false);
303 link.setFocusPainted(true);
304 link.setForeground(UIManager.getColor("TaskPane.titleForeground"));
305 }
306
307 /**
308 * Ensures expanded group is visible. Issues delayed request for scrolling to visible.
309 */
310 protected void ensureVisible() {
311 SwingUtilities.invokeLater(new Runnable() {
312 public void run() {
313 group.scrollRectToVisible(new Rectangle(group.getWidth(), group
314 .getHeight()));
315 }
316 });
317 }
318
319 /**
320 * Focus listener responsible for repainting of the taskpane on focus change.
321 */
322 static class RepaintOnFocus implements FocusListener {
323 public void focusGained(FocusEvent e) {
324 e.getComponent().repaint();
325 }
326
327 public void focusLost(FocusEvent e) {
328 e.getComponent().repaint();
329 }
330 }
331
332 /**
333 * Change listener responsible for change handling.
334 */
335 class ChangeListener implements PropertyChangeListener {
336 public void propertyChange(PropertyChangeEvent evt) {
337 // if group is expanded but not animated
338 // or if animated has reached expanded state
339 // scroll to visible if scrollOnExpand is enabled
340 if (("collapsed".equals(evt.getPropertyName())
341 && Boolean.TRUE.equals(evt.getNewValue()) && !group
342 .isAnimated())
343 || (JXCollapsiblePane.ANIMATION_STATE_KEY.equals(evt
344 .getPropertyName()) && "expanded".equals(evt
345 .getNewValue()))) {
346 if (group.isScrollOnExpand()) {
347 ensureVisible();
348 }
349 } else if (JXTaskPane.ICON_CHANGED_KEY
350 .equals(evt.getPropertyName())
351 || JXTaskPane.TITLE_CHANGED_KEY.equals(evt
352 .getPropertyName())
353 || JXTaskPane.SPECIAL_CHANGED_KEY.equals(evt
354 .getPropertyName())) {
355 // icon, title, special must lead to a repaint()
356 group.repaint();
357 } else if ("mnemonic".equals(evt.getPropertyName())) {
358 SwingXUtilities.updateMnemonicBinding(group, "toggleCollapsed");
359
360 Border b = group.getBorder();
361
362 if (b instanceof PaneBorder) {
363 int key = (Integer) evt.getNewValue();
364 ((PaneBorder) b).label.setDisplayedMnemonic(key);
365 }
366 }
367 }
368 }
369
370 /**
371 * Mouse listener responsible for handling of toggle events.
372 */
373 class ToggleListener extends MouseInputAdapter {
374 public void mouseEntered(MouseEvent e) {
375 if (isInBorder(e)) {
376 e.getComponent().setCursor(
377 Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
378 } else {
379 mouseOver = false;
380 group.repaint(0, 0, group.getWidth(), getTitleHeight(group));
381 }
382 }
383
384 public void mouseExited(MouseEvent e) {
385 e.getComponent().setCursor(null);
386 mouseOver = false;
387 group.repaint(0, 0, group.getWidth(), getTitleHeight(group));
388 }
389
390 public void mouseMoved(MouseEvent e) {
391 if (isInBorder(e)) {
392 e.getComponent().setCursor(
393 Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
394 mouseOver = true;
395 } else {
396 e.getComponent().setCursor(null);
397 mouseOver = false;
398 }
399
400 group.repaint(0, 0, group.getWidth(), getTitleHeight(group));
401 }
402
403 public void mouseReleased(MouseEvent e) {
404 if (SwingUtilities.isLeftMouseButton(e) && isInBorder(e)) {
405 group.setCollapsed(!group.isCollapsed());
406 }
407 }
408 }
409
410 /**
411 * Toggle expanded action.
412 */
413 class ToggleCollapsedAction extends AbstractAction {
414 /**
415 * Serial version UID.
416 */
417 private static final long serialVersionUID = 5676859881615358815L;
418
419 public ToggleCollapsedAction() {
420 super("toggleCollapsed");
421 }
422
423 public void actionPerformed(ActionEvent e) {
424 group.setCollapsed(!group.isCollapsed());
425 }
426
427 public boolean isEnabled() {
428 return group.isVisible();
429 }
430 }
431
432 /**
433 * Toggle icon.
434 */
435 protected static class ChevronIcon implements Icon {
436 boolean up = true;
437
438 public ChevronIcon(boolean up) {
439 this.up = up;
440 }
441
442 public int getIconHeight() {
443 return 3;
444 }
445
446 public int getIconWidth() {
447 return 6;
448 }
449
450 public void paintIcon(Component c, Graphics g, int x, int y) {
451 if (up) {
452 g.drawLine(x + 3, y, x, y + 3);
453 g.drawLine(x + 3, y, x + 6, y + 3);
454 } else {
455 g.drawLine(x, y, x + 3, y + 3);
456 g.drawLine(x + 3, y + 3, x + 6, y);
457 }
458 }
459 }
460
461 /**
462 * The border around the content pane
463 */
464 protected static class ContentPaneBorder implements Border, UIResource {
465 Color color;
466
467 public ContentPaneBorder(Color color) {
468 this.color = color;
469 }
470
471 public Insets getBorderInsets(Component c) {
472 return new Insets(0, 1, 1, 1);
473 }
474
475 public boolean isBorderOpaque() {
476 return true;
477 }
478
479 public void paintBorder(Component c, Graphics g, int x, int y,
480 int width, int height) {
481 g.setColor(color);
482 g.drawLine(x, y, x, y + height - 1);
483 g.drawLine(x, y + height - 1, x + width - 1, y + height - 1);
484 g.drawLine(x + width - 1, y, x + width - 1, y + height - 1);
485 }
486 }
487
488 /**
489 * The border of the taskpane group paints the "text", the "icon", the
490 * "expanded" status and the "special" type.
491 *
492 */
493 protected class PaneBorder implements Border, UIResource {
494
495 protected Color borderColor;
496 protected Color titleForeground;
497 protected Color specialTitleBackground;
498 protected Color specialTitleForeground;
499 protected Color titleBackgroundGradientStart;
500 protected Color titleBackgroundGradientEnd;
501
502 protected Color titleOver;
503 protected Color specialTitleOver;
504
505 protected JLabel label;
506
507 /**
508 * Creates new instance of individual pane border.
509 */
510 public PaneBorder() {
511 borderColor = UIManager.getColor("TaskPane.borderColor");
512
513 titleForeground = UIManager.getColor("TaskPane.titleForeground");
514
515 specialTitleBackground = UIManager
516 .getColor("TaskPane.specialTitleBackground");
517 specialTitleForeground = UIManager
518 .getColor("TaskPane.specialTitleForeground");
519
520 titleBackgroundGradientStart = UIManager
521 .getColor("TaskPane.titleBackgroundGradientStart");
522 titleBackgroundGradientEnd = UIManager
523 .getColor("TaskPane.titleBackgroundGradientEnd");
524
525 titleOver = UIManager.getColor("TaskPane.titleOver");
526 if (titleOver == null) {
527 titleOver = specialTitleBackground.brighter();
528 }
529 specialTitleOver = UIManager.getColor("TaskPane.specialTitleOver");
530 if (specialTitleOver == null) {
531 specialTitleOver = specialTitleBackground.brighter();
532 }
533
534 label = new JLabel();
535 label.setOpaque(false);
536 label.setIconTextGap(8);
537 }
538
539 public Insets getBorderInsets(Component c) {
540 return new Insets(getTitleHeight(c), 0, 0, 0);
541 }
542
543 /**
544 * Overwritten to always return <code>true</code> to speed up
545 * painting. Don't use transparent borders unless providing UI delegate
546 * that provides proper return value when calling this method.
547 *
548 * @see javax.swing.border.Border#isBorderOpaque()
549 */
550 public boolean isBorderOpaque() {
551 return true;
552 }
553
554 /**
555 * Calculates the preferred border size, its size so all its content
556 * fits.
557 *
558 * @param group
559 * Selected group.
560 */
561 public Dimension getPreferredSize(JXTaskPane group) {
562 // calculate the title width so it is fully visible
563 // it starts with the title width
564 configureLabel(group);
565 Dimension dim = label.getPreferredSize();
566 // add the title left offset
567 dim.width += 3;
568 // add the controls width
569 dim.width += getTitleHeight(group);
570 // and some space between label and controls
571 dim.width += 3;
572
573 dim.height = getTitleHeight(group);
574 return dim;
575 }
576
577 /**
578 * Paints background of the title. This may differ based on properties
579 * of the group.
580 *
581 * @param group
582 * Selected group.
583 * @param g
584 * Target graphics.
585 */
586 protected void paintTitleBackground(JXTaskPane group, Graphics g) {
587 if (group.isSpecial()) {
588 g.setColor(specialTitleBackground);
589 } else {
590 g.setColor(titleBackgroundGradientStart);
591 }
592 g.fillRect(0, 0, group.getWidth(), getTitleHeight(group) - 1);
593 }
594
595 /**
596 * Paints current group title.
597 *
598 * @param group
599 * Selected group.
600 * @param g
601 * Target graphics.
602 * @param textColor
603 * Title color.
604 * @param x
605 * X coordinate of the top left corner.
606 * @param y
607 * Y coordinate of the top left corner.
608 * @param width
609 * Width of the box.
610 * @param height
611 * Height of the box.
612 */
613 protected void paintTitle(JXTaskPane group, Graphics g,
614 Color textColor, int x, int y, int width, int height) {
615 configureLabel(group);
616 label.setForeground(textColor);
617 if (group.getFont() != null && ! (group.getFont() instanceof FontUIResource)) {
618 label.setFont(group.getFont());
619 }
620 g.translate(x, y);
621 label.setBounds(0, 0, width, height);
622 label.paint(g);
623 g.translate(-x, -y);
624 }
625
626 /**
627 * Configures label for the group using its title, font, icon and
628 * orientation.
629 *
630 * @param group
631 * Selected group.
632 */
633 protected void configureLabel(JXTaskPane group) {
634 label.applyComponentOrientation(group.getComponentOrientation());
635 label.setFont(group.getFont());
636 label.setText(group.getTitle());
637 label.setIcon(group.getIcon() == null ? new EmptyIcon() : group
638 .getIcon());
639 }
640
641 /**
642 * Paints expanded controls. Default implementation does nothing.
643 *
644 * @param group
645 * Expanded group.
646 * @param g
647 * Target graphics.
648 * @param x
649 * X coordinate of the top left corner.
650 * @param y
651 * Y coordinate of the top left corner.
652 * @param width
653 * Width of the box.
654 * @param height
655 * Height of the box.
656 */
657 protected void paintExpandedControls(JXTaskPane group, Graphics g,
658 int x, int y, int width, int height) {
659 }
660
661 /**
662 * Gets current paint color.
663 *
664 * @param group
665 * Selected group.
666 * @return Color to be used for painting provided group.
667 */
668 protected Color getPaintColor(JXTaskPane group) {
669 Color paintColor;
670 if (isMouseOverBorder()) {
671 if (mouseOver) {
672 if (group.isSpecial()) {
673 paintColor = specialTitleOver;
674 } else {
675 paintColor = titleOver;
676 }
677 } else {
678 if (group.isSpecial()) {
679 paintColor = specialTitleForeground;
680 } else {
681 paintColor = group.getForeground() == null || group.getForeground() instanceof ColorUIResource ? titleForeground : group.getForeground();
682 }
683 }
684 } else {
685 if (group.isSpecial()) {
686 paintColor = specialTitleForeground;
687 } else {
688 paintColor = group.getForeground() == null || group.getForeground() instanceof ColorUIResource ? titleForeground : group.getForeground();
689 }
690 }
691 return paintColor;
692 }
693
694 /*
695 * @see javax.swing.border.Border#paintBorder(java.awt.Component,
696 * java.awt.Graphics, int, int, int, int)
697 */
698 public void paintBorder(Component c, Graphics g, int x, int y,
699 int width, int height) {
700
701 JXTaskPane group = (JXTaskPane) c;
702
703 // calculate position of title and toggle controls
704 int controlWidth = getTitleHeight(group) - 2 * getRoundHeight();
705 int controlX = group.getWidth() - getTitleHeight(group);
706 int controlY = getRoundHeight() - 1;
707 int titleX = 3;
708 int titleY = 0;
709 int titleWidth = group.getWidth() - getTitleHeight(group) - 3;
710 int titleHeight = getTitleHeight(group);
711
712 if (!group.getComponentOrientation().isLeftToRight()) {
713 controlX = group.getWidth() - controlX - controlWidth;
714 titleX = group.getWidth() - titleX - titleWidth;
715 }
716
717 // paint the title background
718 paintTitleBackground(group, g);
719
720 // paint the the toggles
721 paintExpandedControls(group, g, controlX, controlY, controlWidth,
722 controlWidth);
723
724 // paint the title text and icon
725 Color paintColor = getPaintColor(group);
726
727 // focus painted same color as text
728 if (group.hasFocus()) {
729 paintFocus(g, paintColor, 3, 3, width - 6, getTitleHeight(group) - 6);
730 }
731
732 paintTitle(group, g, paintColor, titleX, titleY, titleWidth,
733 titleHeight);
734 }
735
736 /**
737 * Paints oval 'border' area around the control itself.
738 *
739 * @param group
740 * Expanded group.
741 * @param g
742 * Target graphics.
743 * @param x
744 * X coordinate of the top left corner.
745 * @param y
746 * Y coordinate of the top left corner.
747 * @param width
748 * Width of the box.
749 * @param height
750 * Height of the box.
751 */
752 protected void paintRectAroundControls(JXTaskPane group, Graphics g,
753 int x, int y, int width, int height, Color highColor,
754 Color lowColor) {
755 if (mouseOver) {
756 int x2 = x + width;
757 int y2 = y + height;
758 g.setColor(highColor);
759 g.drawLine(x, y, x2, y);
760 g.drawLine(x, y, x, y2);
761 g.setColor(lowColor);
762 g.drawLine(x2, y, x2, y2);
763 g.drawLine(x, y2, x2, y2);
764 }
765 }
766
767 /**
768 * Paints oval 'border' area around the control itself.
769 *
770 * @param group
771 * Expanded group.
772 * @param g
773 * Target graphics.
774 * @param x
775 * X coordinate of the top left corner.
776 * @param y
777 * Y coordinate of the top left corner.
778 * @param width
779 * Width of the box.
780 * @param height
781 * Height of the box.
782 */
783 protected void paintOvalAroundControls(JXTaskPane group, Graphics g,
784 int x, int y, int width, int height) {
785 if (group.isSpecial()) {
786 g.setColor(specialTitleBackground.brighter());
787 g.drawOval(x, y, width, height);
788 } else {
789 g.setColor(titleBackgroundGradientStart);
790 g.fillOval(x, y, width, height);
791
792 g.setColor(titleBackgroundGradientEnd.darker());
793 g.drawOval(x, y, width, width);
794 }
795 }
796
797 /**
798 * Paints controls for the group.
799 *
800 * @param group
801 * Expanded group.
802 * @param g
803 * Target graphics.
804 * @param x
805 * X coordinate of the top left corner.
806 * @param y
807 * Y coordinate of the top left corner.
808 * @param width
809 * Width of the box.
810 * @param height
811 * Height of the box.
812 */
813 protected void paintChevronControls(JXTaskPane group, Graphics g,
814 int x, int y, int width, int height) {
815 ChevronIcon chevron;
816 if (group.isCollapsed()) {
817 chevron = new ChevronIcon(false);
818 } else {
819 chevron = new ChevronIcon(true);
820 }
821 int chevronX = x + width / 2 - chevron.getIconWidth() / 2;
822 int chevronY = y + (height / 2 - chevron.getIconHeight());
823 chevron.paintIcon(group, g, chevronX, chevronY);
824 chevron.paintIcon(group, g, chevronX, chevronY
825 + chevron.getIconHeight() + 1);
826 }
827
828 /**
829 * Paints focused group.
830 *
831 * @param g
832 * Target graphics.
833 * @param paintColor
834 * Focused group color.
835 * @param x
836 * X coordinate of the top left corner.
837 * @param y
838 * Y coordinate of the top left corner.
839 * @param width
840 * Width of the box.
841 * @param height
842 * Height of the box.
843 */
844 protected void paintFocus(Graphics g, Color paintColor, int x, int y,
845 int width, int height) {
846 g.setColor(paintColor);
847 BasicGraphicsUtils.drawDashedRect(g, x, y, width, height);
848 }
849
850 /**
851 * Default implementation returns false.
852 *
853 * @return true if this border wants to display things differently when
854 * the mouse is over it
855 */
856 protected boolean isMouseOverBorder() {
857 return false;
858 }
859 }
860
861 /**
862 * Gets size of arc used to round corners.
863 *
864 * @return size of arc used to round corners of the panel.
865 */
866 protected int getRoundHeight() {
867 return roundHeight;
868 }
869
870 }