001: /**
002: * L2FProd.com Common Components 7.3 License.
003: *
004: * Copyright 2005-2007 L2FProd.com
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */package com.l2fprod.common.swing.plaf.basic;
018:
019: import com.l2fprod.common.swing.JCollapsiblePane;
020: import com.l2fprod.common.swing.JLinkButton;
021: import com.l2fprod.common.swing.JTaskPaneGroup;
022: import com.l2fprod.common.swing.icons.EmptyIcon;
023: import com.l2fprod.common.swing.plaf.TaskPaneGroupUI;
024:
025: import java.awt.Color;
026: import java.awt.Component;
027: import java.awt.Cursor;
028: import java.awt.Dimension;
029: import java.awt.Graphics;
030: import java.awt.Insets;
031: import java.awt.Rectangle;
032: import java.awt.event.ActionEvent;
033: import java.awt.event.FocusEvent;
034: import java.awt.event.FocusListener;
035: import java.awt.event.MouseEvent;
036: import java.beans.PropertyChangeEvent;
037: import java.beans.PropertyChangeListener;
038:
039: import javax.swing.AbstractAction;
040: import javax.swing.Action;
041: import javax.swing.ActionMap;
042: import javax.swing.BorderFactory;
043: import javax.swing.Icon;
044: import javax.swing.InputMap;
045: import javax.swing.JComponent;
046: import javax.swing.JLabel;
047: import javax.swing.LookAndFeel;
048: import javax.swing.SwingUtilities;
049: import javax.swing.UIManager;
050: import javax.swing.border.Border;
051: import javax.swing.border.CompoundBorder;
052: import javax.swing.event.MouseInputAdapter;
053: import javax.swing.event.MouseInputListener;
054: import javax.swing.plaf.ActionMapUIResource;
055: import javax.swing.plaf.ComponentUI;
056: import javax.swing.plaf.basic.BasicGraphicsUtils;
057:
058: /**
059: * Base implementation of the <code>JTaskPaneGroup</code> UI.
060: */
061: public class BasicTaskPaneGroupUI extends TaskPaneGroupUI {
062:
063: private static FocusListener focusListener = new RepaintOnFocus();
064:
065: public static ComponentUI createUI(JComponent c) {
066: return new BasicTaskPaneGroupUI();
067: }
068:
069: protected static int TITLE_HEIGHT = 25;
070: protected static int ROUND_HEIGHT = 5;
071:
072: protected JTaskPaneGroup group;
073:
074: protected boolean mouseOver;
075: protected MouseInputListener mouseListener;
076:
077: protected PropertyChangeListener propertyListener;
078:
079: public void installUI(JComponent c) {
080: super .installUI(c);
081: group = (JTaskPaneGroup) c;
082:
083: installDefaults();
084: installListeners();
085: installKeyboardActions();
086: }
087:
088: protected void installDefaults() {
089: group.setOpaque(true);
090: group.setBorder(createPaneBorder());
091: ((JComponent) group.getContentPane())
092: .setBorder(createContentPaneBorder());
093:
094: LookAndFeel.installColorsAndFont(group,
095: "TaskPaneGroup.background", "TaskPaneGroup.foreground",
096: "TaskPaneGroup.font");
097:
098: LookAndFeel.installColorsAndFont((JComponent) group
099: .getContentPane(), "TaskPaneGroup.background",
100: "TaskPaneGroup.foreground", "TaskPaneGroup.font");
101: }
102:
103: protected void installListeners() {
104: mouseListener = createMouseInputListener();
105: group.addMouseMotionListener(mouseListener);
106: group.addMouseListener(mouseListener);
107:
108: group.addFocusListener(focusListener);
109: propertyListener = createPropertyListener();
110: group.addPropertyChangeListener(propertyListener);
111: }
112:
113: protected void installKeyboardActions() {
114: InputMap inputMap = (InputMap) UIManager
115: .get("TaskPaneGroup.focusInputMap");
116: if (inputMap != null) {
117: SwingUtilities.replaceUIInputMap(group,
118: JComponent.WHEN_FOCUSED, inputMap);
119: }
120:
121: ActionMap map = getActionMap();
122: if (map != null) {
123: SwingUtilities.replaceUIActionMap(group, map);
124: }
125: }
126:
127: ActionMap getActionMap() {
128: ActionMap map = new ActionMapUIResource();
129: map.put("toggleExpanded", new ToggleExpandedAction());
130: return map;
131: }
132:
133: public void uninstallUI(JComponent c) {
134: uninstallListeners();
135: super .uninstallUI(c);
136: }
137:
138: protected void uninstallListeners() {
139: group.removeMouseListener(mouseListener);
140: group.removeMouseMotionListener(mouseListener);
141: group.removeFocusListener(focusListener);
142: group.removePropertyChangeListener(propertyListener);
143: }
144:
145: protected MouseInputListener createMouseInputListener() {
146: return new ToggleListener();
147: }
148:
149: protected PropertyChangeListener createPropertyListener() {
150: return new ChangeListener();
151: }
152:
153: protected boolean isInBorder(MouseEvent event) {
154: return event.getY() < getTitleHeight();
155: }
156:
157: protected final int getTitleHeight() {
158: return TITLE_HEIGHT;
159: }
160:
161: protected Border createPaneBorder() {
162: return new PaneBorder();
163: }
164:
165: public Dimension getPreferredSize(JComponent c) {
166: Component component = group.getComponent(0);
167: if (!(component instanceof JCollapsiblePane)) {
168: // something wrong in this JTaskPaneGroup
169: return super .getPreferredSize(c);
170: }
171:
172: JCollapsiblePane collapsible = (JCollapsiblePane) component;
173: Dimension dim = collapsible.getPreferredSize();
174:
175: Border groupBorder = group.getBorder();
176: if (groupBorder instanceof PaneBorder) {
177: Dimension border = ((PaneBorder) groupBorder)
178: .getPreferredSize(group);
179: dim.width = Math.max(dim.width, border.width);
180: dim.height += border.height;
181: } else {
182: dim.height += getTitleHeight();
183: }
184:
185: return dim;
186: }
187:
188: protected Border createContentPaneBorder() {
189: Color borderColor = UIManager
190: .getColor("TaskPaneGroup.borderColor");
191: return new CompoundBorder(new ContentPaneBorder(borderColor),
192: BorderFactory.createEmptyBorder(10, 10, 10, 10));
193: }
194:
195: public Component createAction(Action action) {
196: JLinkButton link = new JLinkButton(action) {
197: public void updateUI() {
198: super .updateUI();
199: // ensure the ui of this link is correctly update on l&f changes
200: if (BasicTaskPaneGroupUI.this != null)
201: configure(this );
202: }
203:
204: };
205: configure(link);
206: return link;
207: }
208:
209: protected void configure(JLinkButton link) {
210: link.setOpaque(false);
211: link.setBorder(null);
212: link.setBorderPainted(false);
213: link.setFocusPainted(true);
214: link.setForeground(UIManager
215: .getColor("TaskPaneGroup.titleForeground"));
216: }
217:
218: protected void ensureVisible() {
219: SwingUtilities.invokeLater(new Runnable() {
220: public void run() {
221: group.scrollRectToVisible(new Rectangle(group
222: .getWidth(), group.getHeight()));
223: }
224: });
225: }
226:
227: static class RepaintOnFocus implements FocusListener {
228: public void focusGained(FocusEvent e) {
229: e.getComponent().repaint();
230: }
231:
232: public void focusLost(FocusEvent e) {
233: e.getComponent().repaint();
234: }
235: }
236:
237: class ChangeListener implements PropertyChangeListener {
238: public void propertyChange(PropertyChangeEvent evt) {
239: // if group is expanded but not animated
240: // or if animated has reached expanded state
241: // scroll to visible if scrollOnExpand is enabled
242: if ((JTaskPaneGroup.EXPANDED_CHANGED_KEY.equals(evt
243: .getPropertyName())
244: && Boolean.TRUE.equals(evt.getNewValue()) && !group
245: .isAnimated())
246: || (JCollapsiblePane.ANIMATION_STATE_KEY.equals(evt
247: .getPropertyName()) && "expanded"
248: .equals(evt.getNewValue()))) {
249: if (group.isScrollOnExpand()) {
250: ensureVisible();
251: }
252: } else if (JTaskPaneGroup.ICON_CHANGED_KEY.equals(evt
253: .getPropertyName())
254: || JTaskPaneGroup.TITLE_CHANGED_KEY.equals(evt
255: .getPropertyName())
256: || JTaskPaneGroup.SPECIAL_CHANGED_KEY.equals(evt
257: .getPropertyName())
258: || JTaskPaneGroup.COLLAPSABLE_CHANGED_KEY
259: .equals(evt.getPropertyName())) {
260: // icon, title, special must lead to a repaint()
261: group.repaint();
262: }
263: }
264: }
265:
266: class ToggleListener extends MouseInputAdapter {
267: public void mouseEntered(MouseEvent e) {
268: if (isInBorder(e)) {
269: e.getComponent().setCursor(
270: Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
271: } else {
272: mouseOver = false;
273: group.repaint();
274: }
275: }
276:
277: public void mouseExited(MouseEvent e) {
278: e.getComponent().setCursor(null);
279: mouseOver = false;
280: group.repaint();
281: }
282:
283: public void mouseMoved(MouseEvent e) {
284: if (isInBorder(e) && group.isCollapsable()) {
285: e.getComponent().setCursor(
286: Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
287: mouseOver = true;
288: group.repaint();
289: } else {
290: e.getComponent().setCursor(null);
291: mouseOver = false;
292: group.repaint();
293: }
294: }
295:
296: public void mouseReleased(MouseEvent e) {
297: if (isInBorder(e) && group.isCollapsable()) {
298: group.setExpanded(!group.isExpanded());
299: }
300: }
301: }
302:
303: class ToggleExpandedAction extends AbstractAction {
304: public ToggleExpandedAction() {
305: super ("toggleExpanded");
306: }
307:
308: public void actionPerformed(ActionEvent e) {
309: group.setExpanded(!group.isExpanded());
310: }
311:
312: public boolean isEnabled() {
313: return group.isVisible() && group.isCollapsable();
314: }
315: }
316:
317: protected static class ChevronIcon implements Icon {
318: boolean up = true;
319:
320: public ChevronIcon(boolean up) {
321: this .up = up;
322: }
323:
324: public int getIconHeight() {
325: return 3;
326: }
327:
328: public int getIconWidth() {
329: return 6;
330: }
331:
332: public void paintIcon(Component c, Graphics g, int x, int y) {
333: if (up) {
334: g.drawLine(x + 3, y, x, y + 3);
335: g.drawLine(x + 3, y, x + 6, y + 3);
336: } else {
337: g.drawLine(x, y, x + 3, y + 3);
338: g.drawLine(x + 3, y + 3, x + 6, y);
339: }
340: }
341: }
342:
343: /**
344: * The border around the content pane
345: */
346: protected static class ContentPaneBorder implements Border {
347: Color color;
348:
349: public ContentPaneBorder(Color color) {
350: this .color = color;
351: }
352:
353: public Insets getBorderInsets(Component c) {
354: return new Insets(0, 1, 1, 1);
355: }
356:
357: public boolean isBorderOpaque() {
358: return true;
359: }
360:
361: public void paintBorder(Component c, Graphics g, int x, int y,
362: int width, int height) {
363: g.setColor(color);
364: g.drawLine(x, y, x, y + height - 1);
365: g
366: .drawLine(x, y + height - 1, x + width - 1, y
367: + height - 1);
368: g.drawLine(x + width - 1, y, x + width - 1, y + height - 1);
369: }
370: }
371:
372: /**
373: * The border of the taskpane group paints the "text", the "icon", the
374: * "expanded" status and the "special" type.
375: *
376: */
377: protected class PaneBorder implements Border {
378:
379: protected Color borderColor;
380: protected Color titleForeground;
381: protected Color specialTitleBackground;
382: protected Color specialTitleForeground;
383: protected Color titleBackgroundGradientStart;
384: protected Color titleBackgroundGradientEnd;
385:
386: protected Color titleOver;
387: protected Color specialTitleOver;
388:
389: protected JLabel label;
390:
391: public PaneBorder() {
392: borderColor = UIManager
393: .getColor("TaskPaneGroup.borderColor");
394:
395: titleForeground = UIManager
396: .getColor("TaskPaneGroup.titleForeground");
397:
398: specialTitleBackground = UIManager
399: .getColor("TaskPaneGroup.specialTitleBackground");
400: specialTitleForeground = UIManager
401: .getColor("TaskPaneGroup.specialTitleForeground");
402:
403: titleBackgroundGradientStart = UIManager
404: .getColor("TaskPaneGroup.titleBackgroundGradientStart");
405: titleBackgroundGradientEnd = UIManager
406: .getColor("TaskPaneGroup.titleBackgroundGradientEnd");
407:
408: titleOver = UIManager.getColor("TaskPaneGroup.titleOver");
409: if (titleOver == null) {
410: titleOver = specialTitleBackground.brighter();
411: }
412: specialTitleOver = UIManager
413: .getColor("TaskPaneGroup.specialTitleOver");
414: if (specialTitleOver == null) {
415: specialTitleOver = specialTitleBackground.brighter();
416: }
417:
418: label = new JLabel();
419: label.setOpaque(false);
420: label.setIconTextGap(8);
421: }
422:
423: public Insets getBorderInsets(Component c) {
424: return new Insets(getTitleHeight(), 0, 0, 0);
425: }
426:
427: public boolean isBorderOpaque() {
428: return true;
429: }
430:
431: /**
432: * Calculates the preferred border size, its size so all its content fits.
433: */
434: public Dimension getPreferredSize(JTaskPaneGroup group) {
435: // calculate the title width so it is fully visible
436: // it starts with the title width
437: configureLabel(group);
438: Dimension dim = label.getPreferredSize();
439: // add the title left offset
440: dim.width += 3;
441: // add the controls width
442: dim.width += TITLE_HEIGHT;
443: // and some space between label and controls
444: dim.width += 3;
445:
446: dim.height = getTitleHeight();
447: return dim;
448: }
449:
450: protected void paintTitleBackground(JTaskPaneGroup group,
451: Graphics g) {
452: if (group.isSpecial()) {
453: g.setColor(specialTitleBackground);
454: } else {
455: g.setColor(titleBackgroundGradientStart);
456: }
457: g.fillRect(0, 0, group.getWidth(), getTitleHeight() - 1);
458: }
459:
460: protected void paintTitle(JTaskPaneGroup group, Graphics g,
461: Color textColor, int x, int y, int width, int height) {
462: configureLabel(group);
463: label.setForeground(textColor);
464: g.translate(x, y);
465: label.setBounds(0, 0, width, height);
466: label.paint(g);
467: g.translate(-x, -y);
468: }
469:
470: protected void configureLabel(JTaskPaneGroup group) {
471: label.applyComponentOrientation(group
472: .getComponentOrientation());
473: label.setFont(group.getFont());
474: label.setText(group.getTitle());
475: label.setIcon(group.getIcon() == null ? new EmptyIcon()
476: : group.getIcon());
477: }
478:
479: protected void paintExpandedControls(JTaskPaneGroup group,
480: Graphics g, int x, int y, int width, int height) {
481: }
482:
483: protected Color getPaintColor(JTaskPaneGroup group) {
484: Color paintColor;
485: if (isMouseOverBorder()) {
486: if (mouseOver) {
487: if (group.isSpecial()) {
488: paintColor = specialTitleOver;
489: } else {
490: paintColor = titleOver;
491: }
492: } else {
493: if (group.isSpecial()) {
494: paintColor = specialTitleForeground;
495: } else {
496: paintColor = titleForeground;
497: }
498: }
499: } else {
500: if (group.isSpecial()) {
501: paintColor = specialTitleForeground;
502: } else {
503: paintColor = titleForeground;
504: }
505: }
506: return paintColor;
507: }
508:
509: public void paintBorder(Component c, Graphics g, int x, int y,
510: int width, int height) {
511:
512: JTaskPaneGroup group = (JTaskPaneGroup) c;
513:
514: // calculate position of title and toggle controls
515: int controlWidth = TITLE_HEIGHT - 2 * ROUND_HEIGHT;
516: // oval looks more like a circle with odd width
517: if (controlWidth % 2 != 0) {
518: controlWidth++;
519: }
520:
521: int controlX = group.getWidth() - TITLE_HEIGHT;
522: int controlY = ROUND_HEIGHT - 1;
523: int titleX = 3;
524: int titleY = 0;
525: int titleWidth = group.getWidth() - getTitleHeight() - 3;
526: int titleHeight = getTitleHeight();
527:
528: if (!group.getComponentOrientation().isLeftToRight()) {
529: controlX = group.getWidth() - controlX - controlWidth;
530: titleX = group.getWidth() - titleX - titleWidth;
531: }
532:
533: // paint the title background
534: paintTitleBackground(group, g);
535:
536: // paint the the toggles
537: if (group.isCollapsable()) {
538: paintExpandedControls(group, g, controlX, controlY,
539: controlWidth, controlWidth);
540: }
541:
542: // paint the title text and icon
543: Color paintColor = getPaintColor(group);
544:
545: // focus painted same color as text
546: if (group.hasFocus()) {
547: paintFocus(g, paintColor, 3, 3, width - 6,
548: getTitleHeight() - 6);
549: }
550:
551: paintTitle(group, g, paintColor, titleX, titleY,
552: titleWidth, titleHeight);
553: }
554:
555: protected void paintRectAroundControls(JTaskPaneGroup group,
556: Graphics g, int x, int y, int width, int height,
557: Color highColor, Color lowColor) {
558: if (mouseOver) {
559: int x2 = x + width;
560: int y2 = y + height;
561: g.setColor(highColor);
562: g.drawLine(x, y, x2, y);
563: g.drawLine(x, y, x, y2);
564: g.setColor(lowColor);
565: g.drawLine(x2, y, x2, y2);
566: g.drawLine(x, y2, x2, y2);
567: }
568: }
569:
570: protected void paintOvalAroundControls(JTaskPaneGroup group,
571: Graphics g, int x, int y, int width, int height) {
572: if (group.isSpecial()) {
573: g.setColor(specialTitleBackground.brighter());
574: g.drawOval(x, y, width, height);
575: } else {
576: g.setColor(titleBackgroundGradientStart);
577: g.fillOval(x, y, width, width);
578:
579: g.setColor(titleBackgroundGradientEnd.darker());
580: g.drawOval(x, y, width, width);
581: }
582: }
583:
584: protected void paintChevronControls(JTaskPaneGroup group,
585: Graphics g, int x, int y, int width, int height) {
586: ChevronIcon chevron;
587: if (group.isExpanded()) {
588: chevron = new ChevronIcon(true);
589: } else {
590: chevron = new ChevronIcon(false);
591: }
592: int chevronX = x + width / 2 - chevron.getIconWidth() / 2;
593: int chevronY = y + (height / 2 - chevron.getIconHeight())
594: - (group.isExpanded() ? 1 : 0);
595: chevron.paintIcon(group, g, chevronX, chevronY);
596: chevron.paintIcon(group, g, chevronX, chevronY
597: + chevron.getIconHeight() + 1);
598: }
599:
600: protected void paintFocus(Graphics g, Color paintColor, int x,
601: int y, int width, int height) {
602: g.setColor(paintColor);
603: BasicGraphicsUtils.drawDashedRect(g, x, y, width, height);
604: }
605:
606: /**
607: * Default implementation returns false.
608: *
609: * @return true if this border wants to display things differently when the
610: * mouse is over it
611: */
612: protected boolean isMouseOverBorder() {
613: return false;
614: }
615: }
616:
617: }
|