001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.swing.tabcontrol.plaf;
043:
044: import java.awt.Color;
045: import java.awt.Component;
046: import java.awt.Dimension;
047: import java.awt.Font;
048: import java.awt.FontMetrics;
049: import java.awt.Graphics;
050: import java.awt.Graphics2D;
051: import java.awt.Insets;
052: import java.awt.Point;
053: import java.awt.Rectangle;
054: import org.netbeans.swing.tabcontrol.TabDisplayer;
055:
056: import javax.swing.plaf.ComponentUI;
057: import java.awt.event.MouseEvent;
058:
059: import java.util.HashMap;
060: import java.util.Map;
061: import javax.swing.Icon;
062: import javax.swing.JComponent;
063: import javax.swing.UIManager;
064: import org.netbeans.swing.tabcontrol.event.TabActionEvent;
065:
066: import org.openide.awt.HtmlRenderer;
067:
068: /**
069: * Win Vista-like user interface of view type tabs.
070: *
071: * @author S. Aubrecht
072: */
073: public final class WinVistaViewTabDisplayerUI extends
074: AbstractViewTabDisplayerUI {
075:
076: /*********** constants *************/
077:
078: /**
079: * Space between text and left side of the tab
080: */
081: private static final int TXT_X_PAD = 9;
082: private static final int TXT_Y_PAD = 3;
083:
084: private static final int ICON_X_PAD = 4;
085:
086: private static final int BUMP_X_PAD = 3;
087: private static final int BUMP_Y_PAD_UPPER = 6;
088: private static final int BUMP_Y_PAD_BOTTOM = 3;
089:
090: /*********** static fields **********/
091:
092: /**
093: * True when colors were already initialized, false otherwise
094: */
095: private static boolean colorsReady = false;
096:
097: private static Color unselFillBrightUpperC, unselFillDarkUpperC,
098: unselFillBrightLowerC, unselFillDarkLowerC, selFillC,
099: focusFillUpperC, focusFillBrightLowerC,
100: focusFillDarkLowerC, mouseOverFillBrightUpperC,
101: mouseOverFillDarkUpperC, mouseOverFillBrightLowerC,
102: mouseOverFillDarkLowerC, txtC, borderC, selBorderC,
103: borderInnerC;
104:
105: private static Map<Integer, String[]> buttonIconPaths;
106:
107: /**
108: * ******** instance fields ********
109: */
110:
111: private Dimension prefSize;
112:
113: /**
114: * rectangle instance used to speedup recurring computations in painting
115: * methods
116: */
117: private Rectangle tempRect = new Rectangle();
118:
119: /**
120: * Should be constructed only from createUI method.
121: */
122: private WinVistaViewTabDisplayerUI(TabDisplayer displayer) {
123: super (displayer);
124: prefSize = new Dimension(100, 17);
125: }
126:
127: public static ComponentUI createUI(JComponent c) {
128: return new WinVistaViewTabDisplayerUI((TabDisplayer) c);
129: }
130:
131: public void installUI(JComponent c) {
132: super .installUI(c);
133: initColors();
134: c.setOpaque(true);
135: }
136:
137: protected AbstractViewTabDisplayerUI.Controller createController() {
138: return new OwnController();
139: }
140:
141: public Dimension getPreferredSize(JComponent c) {
142: FontMetrics fm = getTxtFontMetrics();
143: int height = fm == null ? 17 : fm.getAscent() + 2
144: * fm.getDescent() + 3;
145: Insets insets = c.getInsets();
146: prefSize.height = height + insets.bottom + insets.top;
147: return prefSize;
148: }
149:
150: protected void paintTabContent(Graphics g, int index, String text,
151: int x, int y, int width, int height) {
152: FontMetrics fm = getTxtFontMetrics();
153: // setting font already here to compute string width correctly
154: g.setFont(getTxtFont());
155: if (0 == index)
156: x++;
157: int txtWidth = width;
158: if (isSelected(index)) {
159: Component buttons = getControlButtons();
160: if (null != buttons) {
161: Dimension buttonsSize = buttons.getPreferredSize();
162: txtWidth = width
163: - (buttonsSize.width + ICON_X_PAD + 2 * TXT_X_PAD);
164: buttons.setLocation(x + txtWidth + 2 * TXT_X_PAD, y
165: + (height - buttonsSize.height) / 2);
166: }
167: } else {
168: txtWidth = width - 2 * TXT_X_PAD;
169: }
170:
171: // draw bump (dragger)
172: ColorUtil.paintVistaTabDragTexture(getDisplayer(), g, x
173: + BUMP_X_PAD, y + BUMP_Y_PAD_UPPER, height
174: - (BUMP_Y_PAD_UPPER + BUMP_Y_PAD_BOTTOM));
175: HtmlRenderer.renderString(text, g, x + TXT_X_PAD, y
176: + fm.getAscent() + TXT_Y_PAD, txtWidth, height,
177: getTxtFont(), txtC, HtmlRenderer.STYLE_TRUNCATE, true);
178: }
179:
180: protected void paintTabBorder(Graphics g, int index, int x, int y,
181: int width, int height) {
182: boolean isFirst = index == 0;
183: boolean isHighlighted = isTabHighlighted(index);
184:
185: g.translate(x, y);
186:
187: Color borderColor = isHighlighted ? selBorderC : borderC;
188: g.setColor(borderColor);
189: int left = 0;
190: //left
191: if (isFirst)
192: g.drawLine(0, 0, 0, height - 2);
193: //top
194: g.drawLine(0, 0, width - 1, 0);
195: //right
196: if (index < getDataModel().size() - 1
197: && isTabHighlighted(index + 1))
198: g.setColor(selBorderC);
199: g.drawLine(width - 1, 0, width - 1, height - 2);
200: //bottom
201: g.setColor(borderC);
202: g.drawLine(0, height - 1, width - 1, height - 1);
203:
204: //inner white border
205: g.setColor(borderInnerC);
206: //left
207: if (isFirst)
208: g.drawLine(1, 1, 1, height - 2);
209: else
210: g.drawLine(0, 1, 0, height - 2);
211: //right
212: g.drawLine(width - 2, 1, width - 2, height - 2);
213: //top
214: g.drawLine(1, 1, width - 2, 1);
215:
216: g.translate(-x, -y);
217: }
218:
219: protected void paintTabBackground(Graphics g, int index, int x,
220: int y, int width, int height) {
221: // shrink rectangle - don't affect border and tab header
222: y += 2;
223: height -= 2;
224: // background body, colored according to state
225: boolean selected = isSelected(index);
226: boolean focused = selected && isActive();
227: boolean attention = isAttention(index);
228: boolean mouseOver = isMouseOver(index);
229: if (focused && !attention) {
230: ColorUtil.vistaFillRectGradient((Graphics2D) g, x, y,
231: width, height, focusFillUpperC,
232: focusFillBrightLowerC, focusFillDarkLowerC);
233: } else if (selected && !attention) {
234: g.setColor(selFillC);
235: g.fillRect(x, y, width, height);
236: } else if (mouseOver && !attention) {
237: ColorUtil.vistaFillRectGradient((Graphics2D) g, x, y,
238: width, height, mouseOverFillBrightUpperC,
239: mouseOverFillDarkUpperC, mouseOverFillBrightLowerC,
240: mouseOverFillDarkLowerC);
241: } else if (attention) {
242: Color a = new Color(255, 255, 128);
243: Color b = new Color(230, 200, 64);
244: ColorUtil.xpFillRectGradient((Graphics2D) g, x, y, width,
245: height, a, b);
246: } else {
247: ColorUtil.vistaFillRectGradient((Graphics2D) g, x, y,
248: width, height, unselFillBrightUpperC,
249: unselFillDarkUpperC, unselFillBrightLowerC,
250: unselFillDarkLowerC);
251: }
252: }
253:
254: /**
255: * Override to bold font
256: */
257: protected Font getTxtFont() {
258: Font font = super .getTxtFont();
259: if (!font.isBold()) {
260: font = font.deriveFont(Font.BOLD);
261: }
262: return font;
263: }
264:
265: /**
266: * @return true if tab with given index should have highlighted border, false otherwise.
267: */
268: private boolean isTabHighlighted(int index) {
269: if (((OwnController) getController()).getMouseIndex() == index) {
270: return true;
271: }
272: return isSelected(index) && isActive();
273: }
274:
275: /**
276: * @return true if tab with given index has mouse cursor above and is not
277: * the selected one, false otherwise.
278: */
279: private boolean isMouseOver(int index) {
280: return ((OwnController) getController()).getMouseIndex() == index
281: && !isSelected(index);
282: }
283:
284: /**
285: * Initialization of colors
286: */
287: private static void initColors() {
288: if (!colorsReady) {
289: txtC = UIManager.getColor("TabbedPane.foreground"); // NOI18N
290:
291: selFillC = UIManager.getColor("tab_sel_fill"); // NOI18N
292:
293: focusFillUpperC = UIManager
294: .getColor("tab_focus_fill_upper"); // NOI18N
295: focusFillBrightLowerC = UIManager
296: .getColor("tab_focus_fill_bright_lower"); // NOI18N
297: focusFillDarkLowerC = UIManager
298: .getColor("tab_focus_fill_dark_lower"); // NOI18N
299:
300: unselFillBrightUpperC = UIManager
301: .getColor("tab_unsel_fill_bright_upper"); // NOI18N
302: unselFillDarkUpperC = UIManager
303: .getColor("tab_unsel_fill_dark_upper"); // NOI18N
304: unselFillBrightLowerC = UIManager
305: .getColor("tab_unsel_fill_bright_lower"); // NOI18N
306: unselFillDarkLowerC = UIManager
307: .getColor("tab_unsel_fill_dark_lower"); // NOI18N
308:
309: mouseOverFillBrightUpperC = UIManager
310: .getColor("tab_mouse_over_fill_bright_upper"); // NOI18N
311: mouseOverFillDarkUpperC = UIManager
312: .getColor("tab_mouse_over_fill_dark_upper"); // NOI18N
313: mouseOverFillBrightLowerC = UIManager
314: .getColor("tab_mouse_over_fill_bright_lower"); // NOI18N
315: mouseOverFillDarkLowerC = UIManager
316: .getColor("tab_mouse_over_fill_dark_lower"); // NOI18N
317:
318: borderC = UIManager.getColor("tab_border"); // NOI18N
319: selBorderC = UIManager.getColor("tab_sel_border"); // NOI18N
320: borderInnerC = UIManager.getColor("tab_border_inner"); // NOI18N
321:
322: colorsReady = true;
323: }
324: }
325:
326: private static void initIcons() {
327: if (null == buttonIconPaths) {
328: buttonIconPaths = new HashMap<Integer, String[]>(7);
329:
330: //close button
331: String[] iconPaths = new String[4];
332: iconPaths[TabControlButton.STATE_DEFAULT] = "org/netbeans/swing/tabcontrol/resources/vista_bigclose_enabled.png"; // NOI18N
333: iconPaths[TabControlButton.STATE_PRESSED] = "org/netbeans/swing/tabcontrol/resources/vista_bigclose_pressed.png"; // NOI18N
334: iconPaths[TabControlButton.STATE_DISABLED] = iconPaths[TabControlButton.STATE_DEFAULT];
335: iconPaths[TabControlButton.STATE_ROLLOVER] = "org/netbeans/swing/tabcontrol/resources/vista_bigclose_rollover.png"; // NOI18N
336: buttonIconPaths.put(TabControlButton.ID_CLOSE_BUTTON,
337: iconPaths);
338:
339: //slide/pin button
340: iconPaths = new String[4];
341: iconPaths[TabControlButton.STATE_DEFAULT] = "org/netbeans/swing/tabcontrol/resources/vista_slideright_enabled.png"; // NOI18N
342: iconPaths[TabControlButton.STATE_PRESSED] = "org/netbeans/swing/tabcontrol/resources/vista_slideright_pressed.png"; // NOI18N
343: iconPaths[TabControlButton.STATE_DISABLED] = iconPaths[TabControlButton.STATE_DEFAULT];
344: iconPaths[TabControlButton.STATE_ROLLOVER] = "org/netbeans/swing/tabcontrol/resources/vista_slideright_rollover.png"; // NOI18N
345: buttonIconPaths.put(TabControlButton.ID_SLIDE_RIGHT_BUTTON,
346: iconPaths);
347:
348: iconPaths = new String[4];
349: iconPaths[TabControlButton.STATE_DEFAULT] = "org/netbeans/swing/tabcontrol/resources/vista_slideleft_enabled.png"; // NOI18N
350: iconPaths[TabControlButton.STATE_PRESSED] = "org/netbeans/swing/tabcontrol/resources/vista_slideleft_pressed.png"; // NOI18N
351: iconPaths[TabControlButton.STATE_DISABLED] = iconPaths[TabControlButton.STATE_DEFAULT];
352: iconPaths[TabControlButton.STATE_ROLLOVER] = "org/netbeans/swing/tabcontrol/resources/vista_slideleft_rollover.png"; // NOI18N
353: buttonIconPaths.put(TabControlButton.ID_SLIDE_LEFT_BUTTON,
354: iconPaths);
355:
356: iconPaths = new String[4];
357: iconPaths[TabControlButton.STATE_DEFAULT] = "org/netbeans/swing/tabcontrol/resources/vista_slidebottom_enabled.png"; // NOI18N
358: iconPaths[TabControlButton.STATE_PRESSED] = "org/netbeans/swing/tabcontrol/resources/vista_slidebottom_pressed.png"; // NOI18N
359: iconPaths[TabControlButton.STATE_DISABLED] = iconPaths[TabControlButton.STATE_DEFAULT];
360: iconPaths[TabControlButton.STATE_ROLLOVER] = "org/netbeans/swing/tabcontrol/resources/vista_slidebottom_rollover.png"; // NOI18N
361: buttonIconPaths.put(TabControlButton.ID_SLIDE_DOWN_BUTTON,
362: iconPaths);
363:
364: iconPaths = new String[4];
365: iconPaths[TabControlButton.STATE_DEFAULT] = "org/netbeans/swing/tabcontrol/resources/vista_pin_enabled.png"; // NOI18N
366: iconPaths[TabControlButton.STATE_PRESSED] = "org/netbeans/swing/tabcontrol/resources/vista_pin_pressed.png"; // NOI18N
367: iconPaths[TabControlButton.STATE_DISABLED] = iconPaths[TabControlButton.STATE_DEFAULT];
368: iconPaths[TabControlButton.STATE_ROLLOVER] = "org/netbeans/swing/tabcontrol/resources/vista_pin_rollover.png"; // NOI18N
369: buttonIconPaths.put(TabControlButton.ID_PIN_BUTTON,
370: iconPaths);
371: }
372: }
373:
374: public Icon getButtonIcon(int buttonId, int buttonState) {
375: Icon res = null;
376: initIcons();
377: String[] paths = buttonIconPaths.get(buttonId);
378: if (null != paths && buttonState >= 0
379: && buttonState < paths.length) {
380: res = TabControlButtonFactory.getIcon(paths[buttonState]);
381: }
382: return res;
383: }
384:
385: public void postTabAction(TabActionEvent e) {
386: super .postTabAction(e);
387: if (TabDisplayer.COMMAND_MAXIMIZE.equals(e.getActionCommand())) {
388: ((OwnController) getController()).updateHighlight(-1);
389: }
390: }
391:
392: /**
393: * Own close icon button controller
394: */
395: private class OwnController extends Controller {
396:
397: /**
398: * holds index of tab in which mouse pointer was lastly located. -1
399: * means mouse pointer is out of component's area
400: */
401: // TBD - should be part of model, not controller
402: private int lastIndex = -1;
403:
404: /**
405: * @return Index of tab in which mouse pointer is currently located.
406: */
407: public int getMouseIndex() {
408: return lastIndex;
409: }
410:
411: /**
412: * Triggers visual tab header change when mouse enters/leaves tab in
413: * advance to superclass functionality.
414: */
415: public void mouseMoved(MouseEvent e) {
416: super .mouseMoved(e);
417: Point pos = e.getPoint();
418: updateHighlight(getLayoutModel().indexOfPoint(pos.x, pos.y));
419: }
420:
421: /**
422: * Resets tab header in advance to superclass functionality
423: */
424: public void mouseExited(MouseEvent e) {
425: super .mouseExited(e);
426: if (!inControlButtonsRect(e.getPoint())) {
427: updateHighlight(-1);
428: }
429: }
430:
431: /**
432: * Invokes repaint of dirty region if needed
433: */
434: private void updateHighlight(int curIndex) {
435: if (curIndex == lastIndex) {
436: return;
437: }
438: // compute region which needs repaint
439: TabLayoutModel tlm = getLayoutModel();
440: int x, y, w, h;
441: Rectangle repaintRect = null;
442: if (curIndex != -1) {
443: x = tlm.getX(curIndex) - 1;
444: y = tlm.getY(curIndex);
445: w = tlm.getW(curIndex) + 2;
446: h = tlm.getH(curIndex);
447: repaintRect = new Rectangle(x, y, w, h);
448: }
449: // due to model changes, lastIndex may become invalid, so check
450: if ((lastIndex != -1)
451: && (lastIndex < getDataModel().size())) {
452: x = tlm.getX(lastIndex) - 1;
453: y = tlm.getY(lastIndex);
454: w = tlm.getW(lastIndex) + 2;
455: h = tlm.getH(lastIndex);
456: if (repaintRect != null) {
457: repaintRect = repaintRect.union(new Rectangle(x, y,
458: w, h));
459: } else {
460: repaintRect = new Rectangle(x, y, w, h);
461: }
462: }
463: // trigger repaint if needed, update index
464: if (repaintRect != null) {
465: getDisplayer().repaint(repaintRect);
466: }
467: lastIndex = curIndex;
468: }
469: } // end of OwnController
470: }
|