001: package org.jvnet.substance.netbeans;
002:
003: import java.awt.Color;
004: import java.awt.Component;
005: import java.awt.Dimension;
006: import java.awt.Graphics;
007: import java.awt.Graphics2D;
008: import java.awt.Insets;
009: import java.awt.Polygon;
010: import java.awt.Rectangle;
011: import java.awt.RenderingHints;
012: import java.awt.Shape;
013: import java.awt.geom.GeneralPath;
014: import java.awt.image.BufferedImage;
015: import java.util.HashMap;
016: import java.util.HashSet;
017: import java.util.Map;
018: import java.util.Set;
019: import javax.swing.Icon;
020: import javax.swing.JComponent;
021: import org.jvnet.substance.SubstanceImageCreator;
022: import org.jvnet.substance.SubstanceLookAndFeel;
023: import org.jvnet.substance.border.SubstanceBorderPainter;
024: import org.jvnet.substance.button.BaseButtonShaper;
025: import org.jvnet.substance.button.ClassicButtonShaper;
026: import org.jvnet.substance.color.ColorScheme;
027: import org.jvnet.substance.painter.SubstanceGradientPainter;
028: import org.jvnet.substance.utils.MemoryAnalyzer;
029: import org.jvnet.substance.utils.SubstanceConstants.Side;
030: import org.jvnet.substance.utils.SubstanceCoreUtilities;
031: import org.jvnet.substance.utils.SubstanceSizeUtils;
032: import org.netbeans.swing.tabcontrol.plaf.AbstractTabCellRenderer;
033: import org.netbeans.swing.tabcontrol.plaf.TabPainter;
034:
035: /**
036: * Cell renderer for a single tab in
037: * {@link org.netbeans.swing.tabcontrol.TabbedContainer}. This class is based
038: * on code from
039: * <code>org.netbeans.swing.tabcontrol.plaf.MetalEditorTabCellRenderer</code>
040: * from NetBeans source base.
041: *
042: * @author Kirill Grouchnikov
043: */
044: public class SubstanceTabCellRenderer extends AbstractTabCellRenderer {
045:
046: private static final SubstanceTabPainter metalborder = new SubstanceTabPainter();
047:
048: private static final SubstanceRightClippedTabPainter rightBorder = new SubstanceRightClippedTabPainter();
049:
050: private static final SubstanceLeftClippedTabPainter leftBorder = new SubstanceLeftClippedTabPainter();
051:
052: protected static Map<String, BufferedImage> backgroundCache = new HashMap<String, BufferedImage>();
053:
054: /**
055: * Creates a new instance of MetalEditorTabCellRenderer
056: */
057: public SubstanceTabCellRenderer() {
058: super (leftBorder, metalborder, rightBorder, new Dimension(34,
059: 29));
060: setBorder(metalborder);
061: }
062:
063: private static synchronized BufferedImage getBackgroundImage(
064: Component c, SubstanceGradientPainter gradientPainter,
065: SubstanceBorderPainter borderPainter, int width,
066: int height, int radius, ColorScheme scheme, float cyclePos) {
067:
068: String key = gradientPainter.getDisplayName() + ":"
069: + borderPainter.getDisplayName() + ":" + width + ":"
070: + height + ":"
071: + SubstanceCoreUtilities.getSchemeId(scheme) + ":"
072: + cyclePos;
073: MemoryAnalyzer.enqueueUsage("Checking " + key);
074: BufferedImage result = backgroundCache.get(key);
075: if (result == null) {
076: Set<Side> bottom = new HashSet<Side>();
077: bottom.add(Side.BOTTOM);
078:
079: Shape contour = BaseButtonShaper.getBaseOutline(width,
080: height, radius, bottom);
081:
082: result = gradientPainter.getContourBackground(width,
083: height, contour, false, scheme, scheme, cyclePos,
084: true, false);
085: borderPainter.paintBorder(result.getGraphics(), c, width,
086: height, contour, null, scheme, scheme, cyclePos,
087: false);
088: MemoryAnalyzer
089: .enqueueUsage("Computed background image for "
090: + key);
091: backgroundCache.put(key, result);
092: }
093: return result;
094: }
095:
096: /*
097: * (non-Javadoc)
098: *
099: * @see org.netbeans.swing.tabcontrol.plaf.AbstractTabCellRenderer#getCaptionYAdjustment()
100: */
101: protected int getCaptionYAdjustment() {
102: return 0;
103: }
104:
105: // public Dimension getPadding() {
106: // Dimension d = super.getPadding();
107: // d.width = isShowCloseButton() && !Boolean.getBoolean("nb.tabs.suppressCloseButton") ? 32 : 16;
108: // return d;
109: // }
110:
111: /**
112: * Painter for regular tab.
113: *
114: * @author Kirill Grouchnikov
115: */
116: private static class SubstanceTabPainter implements TabPainter {
117:
118: public Insets getBorderInsets(Component c) {
119: SubstanceTabCellRenderer mtr = (SubstanceTabCellRenderer) c;
120: return new Insets(mtr.isSelected() ? 1 : 3, mtr
121: .isSelected() ? 10 : 9, 0, 0); // XXX
122: }
123:
124: public boolean supportsCloseButton(JComponent renderer) {
125: return ((AbstractTabCellRenderer) renderer)
126: .isShowCloseButton();
127: }
128:
129: public boolean isBorderOpaque() {
130: return true;
131: }
132:
133: public void paintBorder(Component c, Graphics g, int x, int y,
134: int width, int height) {
135: }
136:
137: public Polygon getInteriorPolygon(Component c) {
138: Polygon p = new Polygon();
139:
140: int width = c.getWidth() - 2;
141: int height = c.getHeight() + 1;
142:
143: p.addPoint(0, 0);
144: p.addPoint(width, 0);
145: p.addPoint(width, height);
146: p.addPoint(0, height);
147: return p;
148: }
149:
150: public void paintInterior(Graphics g, Component c) {
151: SubstanceTabCellRenderer mtr = (SubstanceTabCellRenderer) c;
152:
153: Graphics2D graphics = (Graphics2D) g.create();
154: int width = c.getWidth() - 2;
155: int height = c.getHeight() + 1;
156:
157: ColorScheme colorScheme = (mtr.isSelected() | mtr
158: .inCloseButton()) ? SubstanceLookAndFeel
159: .getActiveColorScheme() : SubstanceLookAndFeel
160: .getDefaultColorScheme();
161: int cyclePos = mtr.isPressed() ? 5 : 0;
162:
163: SubstanceGradientPainter painter = SubstanceLookAndFeel
164: .getCurrentGradientPainter();
165: SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
166: .getBorderPainter(c, painter);
167: int radius = (SubstanceLookAndFeel.getCurrentButtonShaper() instanceof ClassicButtonShaper) ? 2
168: : height / 3;
169: BufferedImage bi = getBackgroundImage(c, painter,
170: borderPainter, width - 2, height + 2, radius,
171: colorScheme, cyclePos);
172:
173: graphics.drawImage(bi, 2, 0, null);
174:
175: Rectangle r = new Rectangle();
176: getCloseButtonRectangle(mtr, r, new Rectangle(0, 0, mtr
177: .getWidth(), mtr.getHeight()));
178: r.x += 2;
179: r.y += 2;
180: r.width -= 4;
181: r.height -= 4;
182:
183: if ((r.width > 0) && (r.height > 0)) {
184: Icon closeIcon = SubstanceImageCreator.getCloseIcon(
185: r.width, SubstanceLookAndFeel.getTheme());
186: closeIcon.paintIcon(c, g, r.x, r.y);
187: //
188: // // close icon
189: // graphics.setColor(mtr.getForeground());
190: // graphics.drawLine(r.x, r.y, r.x + r.width - 1, r.y + r.height - 1);
191: // graphics.drawLine(r.x, r.y + r.height - 1, r.x + r.width - 1, r.y);
192: if (mtr.inCloseButton()) {
193: SubstanceImageCreator.paintBorder(null, graphics,
194: r.x - 2, r.y - 2, r.width + 4,
195: r.height + 4, 0.0f, SubstanceLookAndFeel
196: .getTheme());
197: }
198: }
199: }
200:
201: public void getCloseButtonRectangle(JComponent jc,
202: final Rectangle rect, Rectangle bounds) {
203: if (!((AbstractTabCellRenderer) jc).isShowCloseButton()) {
204: rect.x = -100;
205: rect.y = -100;
206: rect.width = 0;
207: rect.height = 0;
208: return;
209: }
210: Insets ins = getBorderInsets(jc);
211:
212: rect.y = bounds.y + ins.top;
213:
214: rect.height = bounds.height - rect.y;
215: rect.x = bounds.x + bounds.width - 18;
216:
217: int closeButtonSize = SubstanceSizeUtils
218: .getTabCloseIconSize(SubstanceSizeUtils
219: .getComponentFontSize(jc));
220: rect.width = closeButtonSize + 4;
221:
222: rect.height = closeButtonSize + 4;
223: rect.y += (rect.height / 2) - 4;
224:
225: // Issue nnn
226: rect.x -= 2;
227: rect.y -= 2;
228: }
229: }
230:
231: /**
232: * Painter for left-clipped tab.
233: *
234: * @author Kirill Grouchnikov
235: */
236: private static class SubstanceLeftClippedTabPainter implements
237: TabPainter {
238:
239: public Insets getBorderInsets(Component c) {
240: SubstanceTabCellRenderer mtr = (SubstanceTabCellRenderer) c;
241: return new Insets(mtr.isSelected() ? 1 : 3, mtr
242: .isSelected() ? 10 : 9, 0, 0); // XXX
243: }
244:
245: public boolean isBorderOpaque() {
246: return true;
247: }
248:
249: public Polygon getInteriorPolygon(Component c) {
250: SubstanceTabCellRenderer mtr = (SubstanceTabCellRenderer) c;
251:
252: Polygon p = new Polygon();
253:
254: int width = c.getWidth();
255: int height = 1 + (mtr.isSelected() ? c.getHeight() + 3 : c
256: .getHeight());
257:
258: p.addPoint(-1, 0);
259: p.addPoint(-1 + width, 0);
260: p.addPoint(-1 + width, height);
261: p.addPoint(-1, height);
262:
263: return p;
264: }
265:
266: public void paintBorder(Component c, Graphics g, int x, int y,
267: int width, int height) {
268: }
269:
270: public void paintInterior(Graphics g, Component c) {
271: Graphics2D graphics = (Graphics2D) g.create();
272: graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
273: RenderingHints.VALUE_ANTIALIAS_ON);
274:
275: SubstanceTabCellRenderer mtr = (SubstanceTabCellRenderer) c;
276: int width = c.getWidth() - 2;
277: int height = c.getHeight() + 1;
278:
279: ColorScheme colorScheme = (mtr.isSelected() | mtr
280: .inCloseButton()) ? SubstanceLookAndFeel
281: .getActiveColorScheme() : SubstanceLookAndFeel
282: .getDefaultColorScheme();
283: int cyclePos = mtr.isPressed() ? 5 : 0;
284:
285: SubstanceGradientPainter painter = SubstanceLookAndFeel
286: .getCurrentGradientPainter();
287: SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
288: .getBorderPainter(c, painter);
289: int radius = (SubstanceLookAndFeel.getCurrentButtonShaper() instanceof ClassicButtonShaper) ? 2
290: : height / 3;
291: BufferedImage bi = getBackgroundImage(c, painter,
292: borderPainter, 2 * width - 2, height + 2, radius,
293: colorScheme, cyclePos);
294:
295: GeneralPath clip = new GeneralPath();
296: int zzCount = 4;
297: int zzHeight = height / zzCount;
298: clip.moveTo(2, 0);
299: for (int i = 0; i < zzCount; i++) {
300: clip.lineTo(2, i * zzHeight);
301: clip.lineTo(2 + zzHeight / 2, i * zzHeight + zzHeight
302: / 2);
303: }
304: clip.lineTo(2, 2 + height);
305:
306: clip.lineTo(width + 2, 2 + height);
307: clip.lineTo(width + 2, 0);
308: clip.lineTo(2, 0);
309: graphics.setClip(clip);
310:
311: graphics.drawImage(bi, 2 - width, 0, null);
312:
313: GeneralPath zz = new GeneralPath();
314: zz.moveTo(2, 0);
315: for (int i = 0; i < zzCount; i++) {
316: zz.lineTo(2, i * zzHeight);
317: zz
318: .lineTo(2 + zzHeight / 2, i * zzHeight
319: + zzHeight / 2);
320: }
321: zz.lineTo(2, 2 + height);
322: graphics.setColor(Color.black);
323: graphics.setClip(null);
324: graphics.draw(zz);
325:
326: graphics.dispose();
327: }
328:
329: public void getCloseButtonRectangle(JComponent jc,
330: Rectangle rect, Rectangle bounds) {
331: bounds.setBounds(-20, -20, 0, 0);
332: }
333:
334: public boolean supportsCloseButton(JComponent renderer) {
335: return false;
336: }
337: }
338:
339: /**
340: * Painter for right-clipped tab.
341: *
342: * @author Kirill Grouchnikov
343: */
344: private static class SubstanceRightClippedTabPainter implements
345: TabPainter {
346:
347: public Insets getBorderInsets(Component c) {
348: SubstanceTabCellRenderer mtr = (SubstanceTabCellRenderer) c;
349: return new Insets(mtr.isSelected() ? 1 : 3, mtr
350: .isSelected() ? 10 : 9, 0, 0); // XXX
351: }
352:
353: public boolean isBorderOpaque() {
354: return true;
355: }
356:
357: public Polygon getInteriorPolygon(Component c) {
358: SubstanceTabCellRenderer mtr = (SubstanceTabCellRenderer) c;
359:
360: Polygon p = new Polygon();
361:
362: int width = c.getWidth() + 2;
363: int height = 1 + (mtr.isSelected() ? c.getHeight() + 3 : c
364: .getHeight());
365:
366: p.addPoint(0, 0);
367: p.addPoint(width, 0);
368: p.addPoint(width, height);
369: p.addPoint(0, height);
370: return p;
371: }
372:
373: public void paintBorder(Component c, Graphics g, int x, int y,
374: int width, int height) {
375: }
376:
377: public void paintInterior(Graphics g, Component c) {
378: Graphics2D graphics = (Graphics2D) g.create();
379: graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
380: RenderingHints.VALUE_ANTIALIAS_ON);
381:
382: SubstanceTabCellRenderer mtr = (SubstanceTabCellRenderer) c;
383: int width = c.getWidth() - 2;
384: int height = c.getHeight() + 1;
385:
386: ColorScheme colorScheme = (mtr.isSelected() | mtr
387: .inCloseButton()) ? SubstanceLookAndFeel
388: .getActiveColorScheme() : SubstanceLookAndFeel
389: .getDefaultColorScheme();
390: int cyclePos = mtr.isPressed() ? 5 : 0;
391:
392: SubstanceGradientPainter painter = SubstanceLookAndFeel
393: .getCurrentGradientPainter();
394: SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
395: .getBorderPainter(c, painter);
396: int radius = (SubstanceLookAndFeel.getCurrentButtonShaper() instanceof ClassicButtonShaper) ? 2
397: : height / 3;
398: BufferedImage bi = getBackgroundImage(c, painter,
399: borderPainter, 2 * width - 2, height + 2, radius,
400: colorScheme, cyclePos);
401:
402: GeneralPath clip = new GeneralPath();
403: int zzCount = 4;
404: int zzHeight = height / zzCount;
405: clip.moveTo(width, 0);
406: for (int i = 0; i < zzCount; i++) {
407: clip.lineTo(width, 3 + i * zzHeight);
408: clip.lineTo(width - zzHeight / 2, 3 + i * zzHeight
409: + zzHeight / 2);
410: }
411: clip.lineTo(width, 2 + height);
412:
413: clip.lineTo(2, 2 + height);
414: clip.lineTo(2, 0);
415: clip.lineTo(width, 0);
416: graphics.setClip(clip);
417:
418: graphics.drawImage(bi, 2, 0, null);
419:
420: GeneralPath zz = new GeneralPath();
421: zz.moveTo(width, 0);
422: for (int i = 0; i < zzCount; i++) {
423: zz.lineTo(width, i * zzHeight);
424: zz.lineTo(width - zzHeight / 2, i * zzHeight + zzHeight
425: / 2);
426: }
427: zz.lineTo(width, 2 + height);
428: graphics.setColor(Color.black);
429: graphics.setClip(null);
430: graphics.draw(zz);
431:
432: graphics.dispose();
433: }
434:
435: public void getCloseButtonRectangle(JComponent jc,
436: Rectangle rect, Rectangle bounds) {
437: bounds.setBounds(-20, -20, 0, 0);
438: }
439:
440: public boolean supportsCloseButton(JComponent renderer) {
441: return false;
442: }
443: }
444: }
|