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 org.netbeans.swing.tabcontrol.TabDisplayer;
045:
046: import javax.swing.*;
047: import javax.swing.plaf.ComponentUI;
048: import java.awt.*;
049: import java.awt.event.MouseEvent;
050:
051: import java.util.HashMap;
052: import java.util.Map;
053: import org.netbeans.swing.tabcontrol.event.TabActionEvent;
054:
055: import org.openide.awt.HtmlRenderer;
056:
057: /**
058: * Win XP-like user interface of view type tabs.
059: *
060: * @author Dafe Simonek
061: */
062: public final class WinXPViewTabDisplayerUI extends
063: AbstractViewTabDisplayerUI {
064:
065: /*********** constants *************/
066:
067: /**
068: * Space between text and left side of the tab
069: */
070: private static final int TXT_X_PAD = 8;
071: private static final int TXT_Y_PAD = 3;
072:
073: private static final int ICON_X_PAD = 2;
074:
075: private static final int BUMP_X_PAD = 3;
076: private static final int BUMP_Y_PAD_UPPER = 5;
077: private static final int BUMP_Y_PAD_BOTTOM = 3;
078:
079: private static final int HIGHLIGHTED_RAISE = 1;
080:
081: /*********** static fields **********/
082:
083: /**
084: * True when colors were already initialized, false otherwise
085: */
086: private static boolean colorsReady = false;
087:
088: private static Color unselFillBrightC, unselFillDarkC, selFillC,
089: focusFillBrightC, focusFillDarkC, txtC, borderC,
090: bottomBorderC, selBorderC, bgFillC;
091:
092: private static Map<Integer, String[]> buttonIconPaths;
093:
094: /**
095: * ******** instance fields ********
096: */
097:
098: private Dimension prefSize;
099:
100: /**
101: * rectangle instance used to speedup recurring computations in painting
102: * methods
103: */
104: private Rectangle tempRect = new Rectangle();
105:
106: /**
107: * Should be constructed only from createUI method.
108: */
109: private WinXPViewTabDisplayerUI(TabDisplayer displayer) {
110: super (displayer);
111: prefSize = new Dimension(100, 17);
112: }
113:
114: public static ComponentUI createUI(JComponent c) {
115: return new WinXPViewTabDisplayerUI((TabDisplayer) c);
116: }
117:
118: @Override
119: public void installUI(JComponent c) {
120: super .installUI(c);
121: initColors();
122: initIcons();
123: c.setOpaque(true);
124: }
125:
126: @Override
127: protected AbstractViewTabDisplayerUI.Controller createController() {
128: return new OwnController();
129: }
130:
131: @Override
132: public Dimension getPreferredSize(JComponent c) {
133: FontMetrics fm = getTxtFontMetrics();
134: int height = fm == null ? 17 : fm.getAscent() + 2
135: * fm.getDescent() + 3;
136: Insets insets = c.getInsets();
137: prefSize.height = height + insets.bottom + insets.top;
138: return prefSize;
139: }
140:
141: protected void paintTabContent(Graphics g, int index, String text,
142: int x, int y, int width, int height) {
143: FontMetrics fm = getTxtFontMetrics();
144: // setting font already here to compute string width correctly
145: g.setFont(getTxtFont());
146: // highlighted one is higher then others
147: if (!isTabInFront(index) && isMoreThanOne()) {
148: y += HIGHLIGHTED_RAISE;
149: height -= HIGHLIGHTED_RAISE;
150: }
151: int txtWidth = width;
152: if (isSelected(index)) {
153: Component buttons = getControlButtons();
154: if (null != buttons) {
155: Dimension buttonsSize = buttons.getPreferredSize();
156: txtWidth = width
157: - (buttonsSize.width + ICON_X_PAD + 2 * TXT_X_PAD);
158: buttons.setLocation(x + txtWidth + 2 * TXT_X_PAD, y
159: + (height - buttonsSize.height) / 2);
160: }
161: } else {
162: txtWidth = width - 2 * TXT_X_PAD;
163: }
164:
165: int highlightedRaiseCompensation = (!isTabInFront(index) && isMoreThanOne()) ? HIGHLIGHTED_RAISE
166: : 0;
167: // draw bump (dragger)
168: ColorUtil.paintXpTabDragTexture(getDisplayer(), g, x
169: + BUMP_X_PAD, y + BUMP_Y_PAD_UPPER, height
170: - (BUMP_Y_PAD_UPPER + BUMP_Y_PAD_BOTTOM)
171: + highlightedRaiseCompensation);
172: HtmlRenderer.renderString(text, g, x + TXT_X_PAD, y
173: + fm.getAscent() + TXT_Y_PAD, txtWidth, height,
174: getTxtFont(), txtC, HtmlRenderer.STYLE_TRUNCATE, true);
175: }
176:
177: protected void paintTabBorder(Graphics g, int index, int x, int y,
178: int width, int height) {
179: boolean isFirst = index == 0;
180: boolean isHighlighted = isTabHighlighted(index);
181:
182: g.translate(x, y);
183:
184: ColorUtil.paintXpTabHeader(
185: isHighlighted ? ColorUtil.XP_HIGHLIGHTED_TAB
186: : ColorUtil.XP_REGULAR_TAB, g, 0, 0, width);
187: Color borderColor = isHighlighted ? selBorderC : borderC;
188: g.setColor(borderColor);
189: if (isFirst) {
190: g.drawLine(0, 3, 0, height - 2);
191: }
192: g.drawLine(width - 1, 3, width - 1, height - 2);
193: g.setColor(bottomBorderC);
194: g.drawLine(0, height - 1, width - 1, height - 1);
195:
196: g.translate(-x, -y);
197: }
198:
199: protected void paintTabBackground(Graphics g, int index, int x,
200: int y, int width, int height) {
201: // shrink rectangle - don't affect border and tab header
202: y += 3;
203: width -= 1;
204: height -= 4;
205: // background body, colored according to state
206: boolean selected = isSelected(index);
207: boolean focused = selected && isActive();
208: boolean attention = isAttention(index);
209: if (focused && !attention) {
210: ColorUtil.xpFillRectGradient((Graphics2D) g, x, y, width,
211: height, focusFillBrightC, focusFillDarkC);
212: } else if (selected && isMoreThanOne() && !attention) {
213: g.setColor(selFillC);
214: g.fillRect(x, y, width, height);
215: } else if (attention) {
216: Color a = new Color(255, 255, 128);
217: Color b = new Color(230, 200, 64);
218: ColorUtil.xpFillRectGradient((Graphics2D) g, x, y, width,
219: height, a, b);
220: } else {
221: ColorUtil.xpFillRectGradient((Graphics2D) g, x, y, width,
222: height, unselFillBrightC, unselFillDarkC);
223: }
224: }
225:
226: /**
227: * Override to bold font
228: */
229: @Override
230: protected Font getTxtFont() {
231: Font font = UIManager.getFont("Label.font");
232: if (null == font)
233: font = super .getTxtFont();
234: if (!font.isBold()) {
235: font = font.deriveFont(Font.BOLD);
236: }
237: return font;
238: }
239:
240: /**
241: * @return true if tab with given index should be highlighted with XP
242: * highlight header, false otherwise.
243: */
244: private boolean isTabHighlighted(int index) {
245: if (((OwnController) getController()).getMouseIndex() == index) {
246: return true;
247: }
248: return isTabInFront(index) && isMoreThanOne();
249: }
250:
251: /**
252: * @return true if tab is selected in other tabs or selected and also
253: * active
254: */
255: private boolean isTabInFront(int index) {
256: return isSelected(index) && (isActive() || isMoreThanOne());
257: }
258:
259: /**
260: * @return true if there is more then one tab, false otherwise
261: */
262: private boolean isMoreThanOne() {
263: return getDataModel().size() > 1;
264: }
265:
266: /**
267: * Initialization of colors
268: */
269: private static void initColors() {
270: if (!colorsReady) {
271: txtC = UIManager.getColor("TabbedPane.foreground"); // NOI18N
272: selFillC = UIManager.getColor("TabbedPane.highlight"); // NOI18N
273: focusFillBrightC = UIManager
274: .getColor("tab_focus_fill_bright"); // NOI18N
275: focusFillDarkC = UIManager.getColor("tab_focus_fill_dark"); // NOI18N
276: unselFillBrightC = UIManager
277: .getColor("tab_unsel_fill_bright"); // NOI18N
278: unselFillDarkC = UIManager.getColor("tab_unsel_fill_dark"); // NOI18N
279: borderC = UIManager.getColor("tab_border"); // NOI18N
280: bottomBorderC = UIManager.getColor("tab_bottom_border"); // NOI18N
281: selBorderC = UIManager.getColor("tab_sel_border"); // NOI18N
282: bgFillC = UIManager.getColor("workplace_fill"); // NOI18N
283: colorsReady = true;
284: }
285: }
286:
287: private static void initIcons() {
288: if (null == buttonIconPaths) {
289: buttonIconPaths = new HashMap<Integer, String[]>(7);
290:
291: //close button
292: String[] iconPaths = new String[4];
293: iconPaths[TabControlButton.STATE_DEFAULT] = "org/netbeans/swing/tabcontrol/resources/xp_bigclose_enabled.png"; // NOI18N
294: iconPaths[TabControlButton.STATE_PRESSED] = "org/netbeans/swing/tabcontrol/resources/xp_bigclose_pressed.png"; // NOI18N
295: iconPaths[TabControlButton.STATE_DISABLED] = iconPaths[TabControlButton.STATE_DEFAULT];
296: iconPaths[TabControlButton.STATE_ROLLOVER] = "org/netbeans/swing/tabcontrol/resources/xp_bigclose_rollover.png"; // NOI18N
297: buttonIconPaths.put(TabControlButton.ID_CLOSE_BUTTON,
298: iconPaths);
299:
300: //slide/pin button
301: iconPaths = new String[4];
302: iconPaths[TabControlButton.STATE_DEFAULT] = "org/netbeans/swing/tabcontrol/resources/xp_slideright_enabled.png"; // NOI18N
303: iconPaths[TabControlButton.STATE_PRESSED] = "org/netbeans/swing/tabcontrol/resources/xp_slideright_pressed.png"; // NOI18N
304: iconPaths[TabControlButton.STATE_DISABLED] = iconPaths[TabControlButton.STATE_DEFAULT];
305: iconPaths[TabControlButton.STATE_ROLLOVER] = "org/netbeans/swing/tabcontrol/resources/xp_slideright_rollover.png"; // NOI18N
306: buttonIconPaths.put(TabControlButton.ID_SLIDE_RIGHT_BUTTON,
307: iconPaths);
308:
309: iconPaths = new String[4];
310: iconPaths[TabControlButton.STATE_DEFAULT] = "org/netbeans/swing/tabcontrol/resources/xp_slideleft_enabled.png"; // NOI18N
311: iconPaths[TabControlButton.STATE_PRESSED] = "org/netbeans/swing/tabcontrol/resources/xp_slideleft_pressed.png"; // NOI18N
312: iconPaths[TabControlButton.STATE_DISABLED] = iconPaths[TabControlButton.STATE_DEFAULT];
313: iconPaths[TabControlButton.STATE_ROLLOVER] = "org/netbeans/swing/tabcontrol/resources/xp_slideleft_rollover.png"; // NOI18N
314: buttonIconPaths.put(TabControlButton.ID_SLIDE_LEFT_BUTTON,
315: iconPaths);
316:
317: iconPaths = new String[4];
318: iconPaths[TabControlButton.STATE_DEFAULT] = "org/netbeans/swing/tabcontrol/resources/xp_slidebottom_enabled.png"; // NOI18N
319: iconPaths[TabControlButton.STATE_PRESSED] = "org/netbeans/swing/tabcontrol/resources/xp_slidebottom_pressed.png"; // NOI18N
320: iconPaths[TabControlButton.STATE_DISABLED] = iconPaths[TabControlButton.STATE_DEFAULT];
321: iconPaths[TabControlButton.STATE_ROLLOVER] = "org/netbeans/swing/tabcontrol/resources/xp_slidebottom_rollover.png"; // NOI18N
322: buttonIconPaths.put(TabControlButton.ID_SLIDE_DOWN_BUTTON,
323: iconPaths);
324:
325: iconPaths = new String[4];
326: iconPaths[TabControlButton.STATE_DEFAULT] = "org/netbeans/swing/tabcontrol/resources/xp_pin_enabled.png"; // NOI18N
327: iconPaths[TabControlButton.STATE_PRESSED] = "org/netbeans/swing/tabcontrol/resources/xp_pin_pressed.png"; // NOI18N
328: iconPaths[TabControlButton.STATE_DISABLED] = iconPaths[TabControlButton.STATE_DEFAULT];
329: iconPaths[TabControlButton.STATE_ROLLOVER] = "org/netbeans/swing/tabcontrol/resources/xp_pin_rollover.png"; // NOI18N
330: buttonIconPaths.put(TabControlButton.ID_PIN_BUTTON,
331: iconPaths);
332: }
333: }
334:
335: @Override
336: public Icon getButtonIcon(int buttonId, int buttonState) {
337: Icon res = null;
338: initIcons();
339: String[] paths = buttonIconPaths.get(buttonId);
340: if (null != paths && buttonState >= 0
341: && buttonState < paths.length) {
342: res = TabControlButtonFactory.getIcon(paths[buttonState]);
343: }
344: return res;
345: }
346:
347: @Override
348: public void postTabAction(TabActionEvent e) {
349: super .postTabAction(e);
350: if (TabDisplayer.COMMAND_MAXIMIZE.equals(e.getActionCommand())) {
351: ((OwnController) getController()).updateHighlight(-1);
352: }
353: }
354:
355: /**
356: * Own close icon button controller
357: */
358: private class OwnController extends Controller {
359:
360: /**
361: * holds index of tab in which mouse pointer was lastly located. -1
362: * means mouse pointer is out of component's area
363: */
364: // TBD - should be part of model, not controller
365: private int lastIndex = -1;
366:
367: /**
368: * @return Index of tab in which mouse pointer is currently located.
369: */
370: public int getMouseIndex() {
371: return lastIndex;
372: }
373:
374: /**
375: * Triggers visual tab header change when mouse enters/leaves tab in
376: * advance to superclass functionality.
377: */
378: @Override
379: public void mouseMoved(MouseEvent e) {
380: super .mouseMoved(e);
381: Point pos = e.getPoint();
382: if (!e.getSource().equals(displayer)) {
383: pos = SwingUtilities.convertPoint((Component) e
384: .getSource(), pos, displayer);
385: }
386: updateHighlight(getLayoutModel().indexOfPoint(pos.x, pos.y));
387: }
388:
389: /**
390: * Resets tab header in advance to superclass functionality
391: */
392: @Override
393: public void mouseExited(MouseEvent e) {
394: super .mouseExited(e);
395: if (!inControlButtonsRect(e.getPoint())) {
396: updateHighlight(-1);
397: }
398: }
399:
400: /**
401: * Invokes repaint of dirty region if needed
402: */
403: private void updateHighlight(int curIndex) {
404: if (curIndex == lastIndex) {
405: return;
406: }
407: // compute region which needs repaint
408: TabLayoutModel tlm = getLayoutModel();
409: int x, y, w, h;
410: Rectangle repaintRect = null;
411: if (curIndex != -1) {
412: x = tlm.getX(curIndex);
413: y = tlm.getY(curIndex);
414: w = tlm.getW(curIndex);
415: h = tlm.getH(curIndex);
416: repaintRect = new Rectangle(x, y, w, h);
417: }
418: // due to model changes, lastIndex may become invalid, so check
419: if ((lastIndex != -1)
420: && (lastIndex < getDataModel().size())) {
421: x = tlm.getX(lastIndex);
422: y = tlm.getY(lastIndex);
423: w = tlm.getW(lastIndex);
424: h = tlm.getH(lastIndex);
425: if (repaintRect != null) {
426: repaintRect = repaintRect.union(new Rectangle(x, y,
427: w, h));
428: } else {
429: repaintRect = new Rectangle(x, y, w, h);
430: }
431: }
432: // trigger repaint if needed, update index
433: if (repaintRect != null) {
434: getDisplayer().repaint(repaintRect);
435: }
436: lastIndex = curIndex;
437: }
438:
439: @Override
440: public void mouseEntered(MouseEvent e) {
441: super .mouseEntered(e);
442: mouseMoved(e);
443: }
444:
445: } // end of OwnController
446: }
|