001: package com.xoetrope.carousel.survey;
002:
003: import com.xoetrope.survey.Condition;
004: import java.awt.Color;
005: import java.awt.Graphics;
006: import java.awt.Graphics2D;
007: import java.awt.Point;
008: import java.awt.event.ActionEvent;
009: import java.awt.event.ActionListener;
010: import java.awt.event.MouseEvent;
011: import java.awt.geom.GeneralPath;
012: import java.util.Enumeration;
013: import java.util.Observable;
014: import java.util.Observer;
015: import javax.swing.BorderFactory;
016: import javax.swing.JLabel;
017: import javax.swing.JMenuItem;
018: import javax.swing.JPopupMenu;
019:
020: /**
021: * A view of the rule in graphic editor.
022: *
023: * <p> Copyright (c) Xoetrope Ltd., 2001-2006, This software is licensed under
024: * the GNU Public License (GPL), please see license.txt for more details. If
025: * you make commercial use of this software you must purchase a commercial
026: * license from Xoetrope.</p>
027: * <p> $Revision: 1.5 $</p>
028: */
029: public class XRuleView extends XSurveyComponentBig implements XSource {
030: public static int WIDHT = 200;
031: public static int HEIGHT = 140;
032: public static int SHAPE_GAP = 20;
033: public static int LEFT_GAP = 22;
034: public static int RIGHT_GAP = 22;
035: public static int TOP_GAP = 5;
036: public static int HEADER_HEIGHT = 20;
037: public static int HEADER_GAP = 20;
038: public static int BOTTOM_GAP = 20;
039: public static double CURVE_SCALE_V = 0.1;
040: public static double CURVE_SCALE_H = 0.05;
041: public static Color HEADER_FIELD_BACKGROUND = Color.YELLOW;
042: public static Color BACKGROUND_COLOR = new Color(255, 191, 191);
043: public static int HEADERFIELD_WIDTH = 150;
044: public static int HEADERFIELD_HEIGHT = 20;
045: public static int HEADERFIELD_GAP = 3;
046: public static int HEADERID_WIDTH = 25;
047: public static int HEADERTARGET_WIDTH = 25;
048: public static int HEADERFIELD_ARC = 8;
049: public static int HEADERTEXT_GAP = 3;
050: public static Color HEADERFIELD_BACKGROUND = new Color(255, 234,
051: 234);
052: public static Color HEADERFIELD_BORDER = Color.BLACK;
053: public static int HEADER_FONT_STYLE = 0;
054: public static int HEADER_FONT_SIZE = 12;
055: public static String HEADER_FONT_NAME = "Dialog";
056: public static Color HEADER_FONT_COLOR = Color.BLACK;
057: public static double ARROW_BUTTON_Y = 0.3;
058: public static int ARROW_BUTTON_GAP = 3;
059: public static int ARROW_BUTTON_WIDTH = 20;
060: public static int ARROW_BUTTON_HEIGHT = 10;
061: public static Color ARROW_BUTTON_BACKGROUND = new Color(255, 64, 64);
062: public static Color ARROW_BUTTON_BORDER = Color.BLACK;
063:
064: protected XRule rule;
065: protected int shapeGap;
066: protected double curveScaleV, curveScaleH;
067: protected XGroupRuleLine groupRuleLine;
068: protected XLine sourceLine;
069:
070: public XRuleView(XRulesEditorPane ep, XRule r) {
071: super (ep);
072: rule = r;
073: groupRuleLine = null;
074: sourceLine = null;
075: createComponents();
076: refreshComponents();
077: headerField.repaint();
078: createObservers();
079: }
080:
081: protected void createObservers() {
082: rule.getDeleteConditionNotifier().addObserver(new Observer() {
083: public void update(Observable o, Object arg) {
084: Condition condition = (Condition) arg;
085: removeConditionView(condition);
086: }
087: });
088: rule.getChangeTargetNotifier().addObserver(new Observer() {
089: public void update(Observable o, Object arg) {
090: XQuestionGroup newTargetGroup = (XQuestionGroup) arg;
091: changeTargetGroup(newTargetGroup);
092: }
093: });
094: }
095:
096: public void selectCondition(Condition condition) {
097: Enumeration enumComponents = components.elements();
098: while (enumComponents.hasMoreElements()) {
099: XConditionView conditionView = (XConditionView) enumComponents
100: .nextElement();
101: conditionView.isSelected(conditionView.getCondition()
102: .equals(condition));
103: conditionView.repaint();
104: }
105: }
106:
107: public void changeTargetGroup(XQuestionGroup newGroup) {
108: XGroupView oldGroupView = (sourceLine != null ? sourceLine
109: .getGroupView() : null);
110:
111: if ((oldGroupView == null) && (newGroup == null))
112: return;
113:
114: if ((oldGroupView != null)
115: && oldGroupView.getGroup().equals(newGroup))
116: return;
117:
118: if ((oldGroupView == null) && (newGroup != null)) {
119: XGroupView newGroupView = editorPane.getGroupView(newGroup
120: .getId());
121: sourceLine = new XLine(this , newGroupView, editorPane);
122: newGroupView.addTargetLine(sourceLine);
123: sourceLine.setLocation();
124: }
125:
126: if ((oldGroupView != null) && (newGroup == null)) {
127: oldGroupView.removeTargetLine(sourceLine);
128: editorPane.remove(sourceLine);
129: sourceLine = null;
130: }
131:
132: if ((oldGroupView != null) && (newGroup != null)) {
133: oldGroupView.removeTargetLine(sourceLine);
134: XGroupView newGroupView = editorPane.getGroupView(newGroup
135: .getId());
136: newGroupView.addTargetLine(sourceLine);
137: sourceLine.setGroupView(newGroupView);
138: sourceLine.setLocation();
139: }
140: editorPane.repaint();
141: }
142:
143: public void removeConditionView(Condition condition) {
144: XConditionView conditionView = null;
145: Enumeration enumComponents = components.elements();
146: while (enumComponents.hasMoreElements()
147: && conditionView == null) {
148: XConditionView cv = (XConditionView) enumComponents
149: .nextElement();
150: if (cv.getCondition().equals(condition))
151: conditionView = cv;
152: }
153: if (conditionView != null) {
154: conditionView.remove();
155: refreshView();
156: }
157: }
158:
159: public void remove() {
160: super .remove();
161: if (groupRuleLine != null)
162: editorPane.remove(groupRuleLine);
163: if (sourceLine != null)
164: editorPane.remove(sourceLine);
165: }
166:
167: protected void createPopupMenu() {
168: JMenuItem menuItem;
169: popupMenu = new JPopupMenu();
170: JLabel header = new JLabel(" Rule Options");
171: header.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
172: popupMenu.add(header);
173: popupMenu.addSeparator();
174: menuItem = new JMenuItem("add new rule");
175: popupMenu.add(menuItem);
176: menuItem.addActionListener(new ActionListener() {
177: public void actionPerformed(ActionEvent e) {
178: rule.getGroup().addNewRule();
179: }
180: });
181: popupMenu.addSeparator();
182: menuItem = new JMenuItem("change id");
183: popupMenu.add(menuItem);
184: menuItem.addActionListener(new ActionListener() {
185: public void actionPerformed(ActionEvent e) {
186: editorPane.showRuleIdField(XRuleView.this );
187: }
188: });
189: menuItem = new JMenuItem("change name");
190: popupMenu.add(menuItem);
191: menuItem.addActionListener(new ActionListener() {
192: public void actionPerformed(ActionEvent e) {
193: editorPane.showRuleNameField(XRuleView.this );
194: }
195: });
196: menuItem = new JMenuItem("change target");
197: popupMenu.add(menuItem);
198: menuItem.addActionListener(new ActionListener() {
199: public void actionPerformed(ActionEvent e) {
200: editorPane.showRuleTargetField(XRuleView.this );
201: }
202: });
203: popupMenu.addSeparator();
204: menuItem = new JMenuItem("delete rule");
205: popupMenu.add(menuItem);
206: menuItem.addActionListener(new ActionListener() {
207: public void actionPerformed(ActionEvent e) {
208: rule.getGroup().deleteRule(rule);
209: }
210: });
211: menuItem = new JMenuItem("expand/collapse");
212: popupMenu.add(menuItem);
213: menuItem.addActionListener(new ActionListener() {
214: public void actionPerformed(ActionEvent e) {
215: setExpanded(isExpanded() ^ true);
216: }
217: });
218: menuItem = new JMenuItem("show table");
219: popupMenu.add(menuItem);
220: menuItem.addActionListener(new ActionListener() {
221: public void actionPerformed(ActionEvent e) {
222: editorFrame.showRule(rule);
223: }
224: });
225: }
226:
227: protected void expand() {
228: super .expand();
229: groupRuleLine.setLocation();
230: if (sourceLine != null)
231: sourceLine.setLocation();
232: editorPane.repaint();
233: }
234:
235: protected void collapse() {
236: super .collapse();
237: groupRuleLine.setLocation();
238: if (sourceLine != null)
239: sourceLine.setLocation();
240: editorPane.repaint();
241: }
242:
243: public XRule getRule() {
244: return rule;
245: }
246:
247: public XGroupRuleLine getGroupRuleLine() {
248: return groupRuleLine;
249: }
250:
251: public void setGroupRuleLine(XGroupRuleLine grl) {
252: groupRuleLine = grl;
253: }
254:
255: protected void initVariables() {
256: super .initVariables();
257: width = WIDTH;
258: height = HEIGHT;
259: leftGap = LEFT_GAP;
260: rightGap = RIGHT_GAP;
261: topGap = TOP_GAP;
262: bottomGap = BOTTOM_GAP;
263: headerHeight = HEADER_HEIGHT;
264: headerGap = HEADER_GAP;
265: componentHeight = COMPONENT_HEIGHT;
266: componentGap = COMPONENT_GAP;
267: backgroundColor = BACKGROUND_COLOR;
268: borderColor = BORDER_COLOR;
269: curveScaleV = CURVE_SCALE_V;
270: curveScaleH = CURVE_SCALE_H;
271: arrowButtonY = ARROW_BUTTON_Y;
272: arrowButtonGap = ARROW_BUTTON_GAP;
273: arrowButtonWidth = ARROW_BUTTON_WIDTH;
274: arrowButtonHeight = ARROW_BUTTON_HEIGHT;
275: arrowButtonBackground = ARROW_BUTTON_BACKGROUND;
276: arrowButtonBorder = ARROW_BUTTON_BORDER;
277: headerFieldWidth = HEADERFIELD_WIDTH;
278: headerFieldHeight = HEADERFIELD_HEIGHT;
279: headerFieldGap = HEADERFIELD_GAP;
280: headerFieldArc = HEADERFIELD_ARC;
281: headerIdWidth = HEADERID_WIDTH;
282: headerTargetWidth = HEADERTARGET_WIDTH;
283: headerFieldBackground = HEADERFIELD_BACKGROUND;
284: headerTextGap = HEADERTEXT_GAP;
285: headerFieldBorder = HEADERFIELD_BORDER;
286: headerFontStyle = HEADER_FONT_STYLE;
287: headerFontName = HEADER_FONT_NAME;
288: headerFontSize = HEADER_FONT_SIZE;
289: headerFontColor = HEADER_FONT_COLOR;
290: shapeGap = SHAPE_GAP;
291: }
292:
293: public void paint(Graphics g) {
294: Graphics2D g2d = (Graphics2D) g;
295: paintShape(g2d);
296: }
297:
298: private Point getScaledPoint(Point p1, Point p2, double scale) {
299: double p1x = p1.getX(), p1y = p1.getY();
300: double p2x = p2.getX(), p2y = p2.getY();
301: double px = p1x + scale * (p2x - p1x);
302: double py = p1y + scale * (p2y - p1y);
303: return new Point((int) px, (int) py);
304: }
305:
306: protected void createShape() {
307: Point p;
308: int w = getWidth() - 1;
309: int h = getHeight() - 1;
310: int x0 = 0;
311: int y0 = h / 2;
312: int x1 = getShapeGap();
313: int y1 = 0;
314: int x2 = w - getShapeGap();
315: int y2 = 0;
316: int x3 = w;
317: int y3 = h / 2;
318: int x4 = w - getShapeGap();
319: int y4 = h;
320: int x5 = getShapeGap();
321: int y5 = h;
322:
323: p = getScaledPoint(new Point(x1, y1), new Point(x0, y0),
324: curveScaleV);
325: int cx1 = (int) p.getX();
326: int cy1 = (int) p.getY();
327: p = getScaledPoint(new Point(x1, y1), new Point(x2, y2),
328: curveScaleH);
329: int cx2 = (int) p.getX();
330: int cy2 = (int) p.getY();
331: p = getScaledPoint(new Point(x2, y2), new Point(x1, y1),
332: curveScaleH);
333: int cx3 = (int) p.getX();
334: int cy3 = (int) p.getY();
335: p = getScaledPoint(new Point(x2, y2), new Point(x3, y3),
336: curveScaleV);
337: int cx4 = (int) p.getX();
338: int cy4 = (int) p.getY();
339: p = getScaledPoint(new Point(x4, y4), new Point(x3, y3),
340: curveScaleV);
341: int cx5 = (int) p.getX();
342: int cy5 = (int) p.getY();
343: p = getScaledPoint(new Point(x4, y4), new Point(x5, y5),
344: curveScaleH);
345: int cx6 = (int) p.getX();
346: int cy6 = (int) p.getY();
347: p = getScaledPoint(new Point(x5, y5), new Point(x4, y4),
348: curveScaleH);
349: int cx7 = (int) p.getX();
350: int cy7 = (int) p.getY();
351: p = getScaledPoint(new Point(x5, y5), new Point(x0, y0),
352: curveScaleV);
353: int cx8 = (int) p.getX();
354: int cy8 = (int) p.getY();
355:
356: shape = new GeneralPath();
357: GeneralPath gp = (GeneralPath) shape;
358: gp.moveTo(x0, y0);
359: gp.lineTo(cx1, cy1);
360: gp.quadTo(x1, y1, cx2, cy2);
361: gp.lineTo(cx3, cy3);
362: gp.quadTo(x2, y2, cx4, cy4);
363: gp.lineTo(x3, y3);
364: gp.lineTo(cx5, cy5);
365: gp.quadTo(x4, y4, cx6, cy6);
366: gp.lineTo(cx7, cy7);
367: gp.quadTo(x5, y5, cx8, cy8);
368: gp.lineTo(x0, y0);
369: }
370:
371: protected void createComponents() {
372: if (rule == null)
373: return;
374: Enumeration enumConditions = rule.getConditions().elements();
375: while (enumConditions.hasMoreElements()) {
376: Condition condition = (Condition) enumConditions
377: .nextElement();
378: XConditionView conditionView = new XConditionView(
379: condition, editorPane, this );
380: components.add(conditionView);
381: conditionView.setWidth(getContentWidth());
382: conditionView.setSize();
383: }
384: }
385:
386: public void createSourceLine() {
387: XQuestionGroup targetGroup = rule.getTarget();
388: if (targetGroup == null)
389: return;
390: XGroupView targetGroupView = editorPane
391: .getGroupView(targetGroup.getId());
392: if (targetGroupView == null)
393: return;
394: sourceLine = new XLine(this , targetGroupView, editorPane);
395: targetGroupView.addTargetLine(sourceLine);
396: }
397:
398: public void setVisible(boolean state) {
399: super .setVisible(state);
400: if (groupRuleLine != null)
401: groupRuleLine.setVisible(state);
402: if (sourceLine != null)
403: sourceLine.setVisible(state);
404: }
405:
406: public void mousePressed(MouseEvent e) {
407: groupRuleLine.getGroupView().moveToFront();
408: super .mousePressed(e);
409: }
410:
411: public void mouseClicked(MouseEvent e) {
412: if (e.getClickCount() == 2)
413: editorFrame.showRule(rule);
414: else
415: super .mouseClicked(e);
416: }
417:
418: public void setLocation(int x, int y) {
419: super .setLocation(x, y);
420:
421: if (groupRuleLine != null)
422: groupRuleLine.setLocation();
423: if (sourceLine != null)
424: sourceLine.setLocation();
425: }
426:
427: public String getHeaderId() {
428: return String.valueOf(rule.getId());
429: }
430:
431: public String getHeaderName() {
432: return rule.getName();
433: }
434:
435: public String getHeaderTarget() {
436: XQuestionGroup target = rule.getTarget();
437: return (target != null ? String.valueOf(target.getId()) : "N");
438: }
439:
440: public int getShapeGap() {
441: return (int) (getScale() * shapeGap);
442: }
443:
444: public void setShapeGap(int sg) {
445: shapeGap = (int) ((double) sg / getScale());
446: }
447:
448: public double getCurveScaleV() {
449: return curveScaleV;
450: }
451:
452: public void setCurveScaleV(double csv) {
453: curveScaleV = csv;
454: }
455:
456: public double getCurveScaleH() {
457: return curveScaleH;
458: }
459:
460: public void setCurveScaleH(double csh) {
461: curveScaleH = csh;
462: }
463:
464: public void targetChanged(XGroupView newTarget) {
465: rule.changeTarget(newTarget.getGroup());
466: }
467:
468: public Point getCenterPoint() {
469: int cx = getLocation().x + getWidth() / 2;
470: int cy = getLocation().y + getHeight() / 2;
471: return new Point(cx, cy);
472: }
473:
474: }
|