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-2007 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.modules.visualweb.designer.jsf.virtualforms;
043:
044: import com.sun.rave.designtime.markup.MarkupDesignBean;
045: import java.awt.BasicStroke;
046: import java.awt.Color;
047: import java.awt.Dimension;
048: import java.awt.Font;
049: import java.awt.FontMetrics;
050: import java.awt.Graphics2D;
051: import java.awt.Point;
052: import java.awt.Rectangle;
053: import java.awt.Stroke;
054: import java.awt.Toolkit;
055: import javax.faces.component.UIComponent;
056: import javax.swing.UIManager;
057: import org.netbeans.modules.visualweb.api.designer.markup.MarkupService;
058: import org.netbeans.modules.visualweb.insync.live.LiveUnit;
059: import com.sun.rave.designtime.DesignBean;
060: import com.sun.rave.designtime.ext.componentgroup.ColorWrapper;
061: import com.sun.rave.designtime.ext.componentgroup.ComponentGroup;
062: import com.sun.rave.designtime.ext.componentgroup.ComponentGroupHolder;
063: import com.sun.rave.designtime.ext.componentgroup.ComponentSubset;
064: import com.sun.rave.designtime.ext.componentgroup.impl.ColorWrapperImpl;
065: import com.sun.rave.designtime.ext.componentgroup.util.ComponentGroupHelper;
066: import javax.faces.component.NamingContainer;
067: import org.netbeans.modules.visualweb.api.designer.Designer;
068: import org.w3c.dom.Element;
069:
070: /**
071: * This class implements most of the support for Component Groups
072: * in the designer.
073: *
074: * @todo App outline
075: *
076: * @author Tor Norbye
077: * @author mbohm
078: */
079: public class ComponentGroupSupport {
080: /** Width of the drop shadow (if 0, don't paint one) */
081: private static final int DROP_SHADOW_WIDTH = 4;
082:
083: /** Border thickness painted around components */
084: private static final int THICKNESS = 2;
085:
086: /** Size to add to font height to make each line in the legend */
087: private static final int EXTRA_LINE_SPACING = 2;
088:
089: /** Legend contents offset from the outer border */
090: private static final int MARGIN = 4;
091:
092: /** Distance between color square and description text */
093: private static final int SPACING = 6;
094:
095: /** Size of the sides of the color-box shown in the legend next to each form name.
096: * This should be a constant, but can be adjusted in extreme cases where the
097: * look and feel fontsize is smaller than the requested value.
098: */
099: private static int colorBoxSize = 9;
100:
101: /** Distance from right side viewport edge to the legend righthand side -
102: * ditto for bottom */
103: private static final int LEGEND_OFFSET = 5 + DROP_SHADOW_WIDTH;
104:
105: /** Creates a new instance of VirtualFormSupport */
106: private ComponentGroupSupport() {
107: }
108:
109: /*
110: * <p>Paint legends and the colored borders of appropriate components.</p>
111: */
112: public static void paint(LiveUnit liveUnit,
113: Designer.RenderContext renderContext, Graphics2D g2d,
114: boolean showVirtualForms, boolean showAjaxTransactions) {
115: ComponentGroupHolder[] holders = ComponentGroupHelper
116: .getComponentGroupHolders(liveUnit);
117:
118: if (holders == null || holders.length == 0) {
119: return;
120: }
121:
122: ComponentGroup[][] groupArr = new ComponentGroup[holders.length][];
123:
124: Dimension d = renderContext.getVieportDimension();
125: Point p = renderContext.getViewportPosition();
126:
127: int maxX = (p.x + d.width) - LEGEND_OFFSET;
128: int maxY = (p.y + d.height) - LEGEND_OFFSET;
129:
130: DesignBean paintChildrenRootBean = liveUnit.getRootContainer(); //the top bean whose children we will walk when we call paintChildren
131: String paintChildrenRootBeanServerId = String
132: .valueOf(NamingContainer.SEPARATOR_CHAR)
133: + paintChildrenRootBean.getInstanceName();
134:
135: ComponentGroupHelper.populateColorGroupArray(liveUnit, holders,
136: groupArr);
137:
138: for (int h = 0; h < holders.length; h++) {
139: ComponentGroupHolder holder = holders[h];
140:
141: if (holder == null) {
142: continue;
143: }
144:
145: ComponentGroup[] groups = groupArr[h];
146:
147: if ((groups == null) || (groups.length == 0)) {
148: continue;
149: }
150:
151: paintChildren(renderContext, paintChildrenRootBean,
152: holders, groupArr, g2d,
153: paintChildrenRootBeanServerId);
154:
155: // Paint legend
156: Font font = (Font) UIManager.getDefaults()
157: .get("Label.font");
158: FontMetrics metrics = Toolkit.getDefaultToolkit()
159: .getFontMetrics(font);
160: g2d.setFont(font);
161:
162: int fontHeight = metrics.getHeight();
163: int baseline = metrics.getHeight() - metrics.getDescent();
164:
165: if (colorBoxSize > baseline) {
166: colorBoxSize = baseline;
167: }
168:
169: int maxStringWidth = 0;
170:
171: for (int i = 0; i < groups.length; i++) {
172: String legendEntryLabel = groups[i]
173: .getLegendEntryLabel();
174: char[] s = legendEntryLabel.toCharArray(); // XXX I should make a utility which operates on Strings directly!
175: int stringWidth = renderContext.getNonTabbedTextWidth(
176: s, 0, s.length, metrics);
177:
178: if (stringWidth > maxStringWidth) {
179: maxStringWidth = stringWidth;
180: }
181: }
182:
183: maxStringWidth += (colorBoxSize + SPACING);
184:
185: String legendText = holder.getLegendLabel();
186: char[] s = legendText.toCharArray();
187: int stringWidth = renderContext.getNonTabbedTextWidth(s, 0,
188: s.length, metrics);
189:
190: if (stringWidth > maxStringWidth) {
191: maxStringWidth = stringWidth;
192: }
193:
194: int height = (2 * MARGIN)
195: + ((groups.length + 1) * (fontHeight + EXTRA_LINE_SPACING));
196: int width = (2 * MARGIN) + maxStringWidth;
197: g2d.setColor(Color.BLACK);
198:
199: // XXX check for scrolling!
200: int x = maxX - width;
201: int y = maxY - height;
202:
203: g2d.drawRect(x, y, width, height);
204:
205: // Header
206: g2d.setColor(new Color(0, 0, 255, 128));
207: g2d.fillRect(x + 1, y + 1, width - 1, fontHeight
208: + EXTRA_LINE_SPACING);
209:
210: g2d.setColor(new Color(192, 192, 192, 128));
211: g2d.fillRect(x + 1,
212: y + 1 + fontHeight + EXTRA_LINE_SPACING, width - 1,
213: height - 1 - fontHeight - EXTRA_LINE_SPACING);
214:
215: if (DROP_SHADOW_WIDTH > 0) {
216: int alphaIncrement = 128 / DROP_SHADOW_WIDTH;
217: int alpha = 128;
218:
219: for (int sw = 0; sw < DROP_SHADOW_WIDTH; sw++, alpha -= alphaIncrement) {
220: g2d.setColor(new Color(0, 0, 0, alpha));
221:
222: int tx = x + 1 + sw;
223: int ty = y + 1 + sw + height;
224: g2d.drawLine(tx, ty, tx + width, ty);
225: tx = x + width + 1 + sw;
226: ty = y + 1 + sw;
227: g2d.drawLine(tx, ty, tx, ty + height);
228: }
229: }
230:
231: // TODO - instead of starting at y+1, start after the header!
232: x += MARGIN;
233: y += (EXTRA_LINE_SPACING / 2);
234: y += (MARGIN / 2);
235:
236: g2d.setColor(Color.WHITE);
237: g2d.drawString(legendText, x, y + baseline);
238: y += (fontHeight + EXTRA_LINE_SPACING);
239: y += (MARGIN / 2);
240:
241: int boxOffset = fontHeight - baseline;
242:
243: for (int i = 0; i < groups.length; i++) {
244: // Draw color label
245: String legendEntryLabel = groups[i]
246: .getLegendEntryLabel();
247: g2d.setColor(Color.BLACK);
248: g2d.drawString(legendEntryLabel, x + colorBoxSize
249: + SPACING, y + baseline);
250:
251: g2d.drawRect(x, y + boxOffset, colorBoxSize,
252: colorBoxSize);
253:
254: Color color = groups[i].getColor();
255: assert color != null;
256: g2d.setColor(color);
257: g2d.fillRect(x + 1, y + boxOffset + 1,
258: colorBoxSize - 1, colorBoxSize - 1);
259:
260: y += (fontHeight + EXTRA_LINE_SPACING);
261:
262: //throw this group's color information into the contextData, if not already stored
263: String key = ComponentGroupHelper
264: .getComponentGroupColorKey(holder.getName(),
265: groups[i].getName());
266: boolean colorAlreadyStored = false;
267: Object colorWrapperObj = liveUnit.getContextData(key);
268: if (colorWrapperObj instanceof ColorWrapper) {
269: ColorWrapper wrapper = (ColorWrapper) colorWrapperObj;
270: if (color.equals(wrapper.getColor())) {
271: colorAlreadyStored = true;
272: }
273: }
274: if (!colorAlreadyStored) {
275: liveUnit.setContextData(key, new ColorWrapperImpl(
276: color));
277: }
278: }
279:
280: maxX -= (width + LEGEND_OFFSET); // in case there are more forms on this page
281: }
282: }
283:
284: private static void paintChildren(
285: Designer.RenderContext renderContext, DesignBean parent,
286: ComponentGroupHolder[] holders,
287: ComponentGroup[][] groupArr, Graphics2D g2d,
288: String precedingIds) {
289: DesignBean[] childBeans = parent.getChildBeans();
290: if (childBeans == null || childBeans.length == 0) {
291: return;
292: }
293:
294: for (int c = 0; c < childBeans.length; c++) {
295: //get child bean and its instance
296: DesignBean child = childBeans[c];
297:
298: if (child == null) {
299: continue;
300: }
301:
302: Object childInstance = child.getInstance();
303:
304: if (childInstance == null) {
305: continue;
306: }
307:
308: String childFqId = precedingIds;
309:
310: if (childInstance instanceof UIComponent) {
311:
312: childFqId = precedingIds
313: + NamingContainer.SEPARATOR_CHAR
314: + child.getInstanceName();
315:
316: //paint boxes around this child for each group
317: if (childInstance instanceof UIComponent) {
318: int nestingLevel = 0;
319:
320: for (int h = 0; h < holders.length; h++) {
321: ComponentGroup[] groups = groupArr[h];
322:
323: if (groups == null || groups.length == 0) {
324: continue;
325: }
326:
327: for (int g = 0; g < groups.length; g++) {
328: ComponentGroup group = groups[g];
329:
330: if (group == null) {
331: continue;
332: }
333:
334: String groupName = group.getName();
335:
336: if (groupName == null) {
337: continue;
338: }
339:
340: Color color = group.getColor();
341:
342: if (color == null) {
343: continue;
344: }
345:
346: boolean paintSolid = false;
347: boolean paintDashed = false;
348: ComponentSubset[] subsets = group
349: .getComponentSubsets();
350: if (subsets != null) {
351: for (int s = 0; s < subsets.length; s++) {
352: ComponentSubset subset = subsets[s];
353: ComponentSubset.LineType lineType = subset
354: .getLineType();
355: if (lineType == ComponentSubset.LineType.NONE) {
356: continue;
357: }
358: String[] members = subset
359: .getMembers();
360: if (members != null) {
361: for (int m = 0; m < members.length; m++) {
362: if (childFqId
363: .endsWith(members[m])) {
364: if (lineType == ComponentSubset.LineType.SOLID) {
365: paintSolid = true;
366: break;
367: } else if (lineType == ComponentSubset.LineType.DASHED) {
368: paintDashed = true;
369: break;
370: }
371: }
372: }
373: }
374: }
375: }
376:
377: if (paintSolid || paintDashed) {
378: paintHighlight(renderContext, g2d,
379: child, color, paintSolid,
380: paintDashed, nestingLevel);
381: nestingLevel++;
382: }
383: }
384: }
385: }
386: }
387:
388: //this child is all grown up and is now a parent. paint its children.
389: paintChildren(renderContext, child, holders, groupArr, g2d,
390: childFqId);
391: }
392: }
393:
394: private static void paintHighlight(
395: Designer.RenderContext renderContext, Graphics2D g2d,
396: DesignBean bean, Color color, boolean paintSolid,
397: boolean paintDashed, int nestingLevel) {
398: if (bean != null) {
399: Element componentRootElement;
400: if (bean instanceof MarkupDesignBean) {
401: Element sourceElement = ((MarkupDesignBean) bean)
402: .getElement();
403: componentRootElement = MarkupService
404: .getRenderedElementForElement(sourceElement);
405: } else {
406: componentRootElement = null;
407: }
408: Rectangle bounds = renderContext
409: .getBoundsForComponent(componentRootElement);
410:
411: if (bounds != null) {
412: // Draw a highlight using this box' extents in the given color
413: int x = bounds.x;
414: int y = bounds.y;
415: int w = bounds.width;
416: int h = bounds.height;
417:
418: // Move outside the bounding box
419: x -= 1;
420: y -= 1;
421: w += 2;
422: h += 2;
423:
424: // Support nested highlights
425: int offset = ((THICKNESS + 1) * nestingLevel) + 2;
426: x -= offset;
427: y -= offset;
428: offset *= 2;
429: w += offset;
430: h += offset;
431:
432: g2d.setColor(color);
433:
434: Stroke prevStroke = g2d.getStroke();
435:
436: Stroke paintDashedStroke = new BasicStroke(THICKNESS,
437: BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL,
438: 1.0f,
439: //new float[] { 2 * THICKNESS, 2 * THICKNESS }, 0.0f);
440: new float[] { 2.25f * THICKNESS,
441: 4.75f * THICKNESS }, 0.0f);
442:
443: Stroke paintSolidStroke = new BasicStroke(THICKNESS,
444: BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL);
445:
446: int topLeftX = x;
447: int topLeftY = y;
448: int topRightX = x + w;
449: int topRightY = y;
450: int bottomRightX = x + w;
451: int bottomRightY = y + h;
452: int bottomLeftX = x;
453: int bottomLeftY = y + h;
454:
455: if (paintSolid) {
456: //bean participates and maybe submits
457: g2d.setStroke(paintSolidStroke);
458: } else {
459: //bean only submits
460: g2d.setStroke(paintDashedStroke);
461: }
462:
463: g2d.drawLine(topLeftX, topLeftY, topRightX, topRightY);
464: g2d.drawLine(topLeftX, topLeftY, bottomLeftX,
465: bottomLeftY);
466:
467: if (paintDashed) {
468: //bean submits and maybe participates
469: g2d.setStroke(paintDashedStroke);
470: } else {
471: //bean only participates
472: g2d.setStroke(paintSolidStroke);
473: }
474:
475: g2d.drawLine(bottomRightX, bottomRightY, bottomLeftX,
476: bottomLeftY);
477: g2d.drawLine(bottomRightX, bottomRightY, topRightX,
478: topRightY);
479:
480: g2d.setStroke(prevStroke);
481: }
482: }
483: }
484: }
|