001: package com.xoetrope.swing.survey;
002:
003: import com.xoetrope.survey.Question;
004: import java.awt.Color;
005: import java.awt.Dimension;
006: import java.awt.Font;
007: import java.awt.FontMetrics;
008: import java.awt.Graphics;
009: import java.awt.Image;
010:
011: import com.xoetrope.util.XGraphicUtils;
012: import net.xoetrope.xui.XProjectManager;
013: import net.xoetrope.xui.style.XStyle;
014: import net.xoetrope.xui.style.XStyleManager;
015:
016: /**
017: * <p>Extends XQuestion by rendering text as a single strip wrapped
018: * over multiple lines and without using a header</p>
019: *
020: * <p> Copyright (c) Xoetrope Ltd., 2001-2006, This software is licensed under
021: * the GNU Public License (GPL), please see license.txt for more details. If
022: * you make commercial use of this software you must purchase a commercial
023: * license from Xoetrope.</p>
024: * <p> $Revision: 1.6 $</p>
025: */
026: public class XWrapQuestion extends XQuestion {
027: // the width of responses surroundings
028: protected int surroundWidth = 30;
029:
030: protected int qWidthScale = 4;
031:
032: // the gap between question and responses
033: protected int qoffset = 10;
034:
035: protected int qWidth;
036:
037: protected Image offscreen;
038:
039: public XWrapQuestion() {
040: super ();
041: }
042:
043: /**
044: * Sets the scale for question layout
045: * @param newScale the new scale
046: */
047: public void setScale(int newScale) {
048: woffset = 20;
049: responseTextGap = 3;
050:
051: scale = newScale;
052: Dimension d = getSize();
053: qWidth = d.width / qWidthScale;
054: xScale = (d.width - qWidth - woffset - qoffset * 2) / scale;
055:
056: if (clickListener != null)
057: clickListener.setOffset(woffset);
058: }
059:
060: protected void paintQuestionText(Graphics g) {
061: // Draw the question text over multiple lines
062: g.setColor(questionTextColor);
063: int start = 0;
064: int end = 0;
065: String line;
066: int lineNum = 1;
067: String questionText = question.getText();
068: do {
069: boolean drawLine = false;
070:
071: // Extend the string by a word (to the next space, if any)
072: end = questionText.indexOf(' ', end + 1);
073: String ss;
074: if (end > 0)
075: ss = questionText.substring(start, end);
076: else {
077: ss = questionText.substring(start);
078: drawLine = true;
079: }
080:
081: // XStyle questionStyle = styleManager.getStyle( "Question" );
082: XStyle questionStyle = styleManager
083: .getStyle("base/Question");
084: Font f = styleManager.getFont(questionStyle);
085: g.setFont(f);
086: FontMetrics fm = g.getFontMetrics(f);
087:
088: // Does the next word fit in?
089: if (fm.getStringBounds(ss, g).getWidth() < (qWidth - 30))
090: line = ss;
091: else
092: drawLine = true;
093:
094: if (drawLine) {
095: g.drawString(ss, woffset, lineNum * fm.getAscent() + 10
096: + topBandHeight);
097: lineNum++;
098: start = end + 1;
099: }
100: } while (end >= 0);
101:
102: // If no text drawn, then do it now
103: if ((start == 0) && (lineNum == 1))
104: g.drawString(questionText, woffset, fm.getAscent()
105: + topBandHeight);
106: }
107:
108: protected void paintSurrounds(Graphics g) {
109: Dimension d = getSize();
110: g.setColor(shadeColor);
111: int x = woffset + qWidth + qoffset;
112: int y = topBandHeight + 1;
113: int w = surroundWidth;
114: int h = d.height - topBandHeight;
115:
116: int numResponses = question.getNumOptions();
117: for (int i = 0; i < numResponses; i++) {
118: g.fillRect(x, y, w, h);
119: x += xScale;
120: }
121: }
122:
123: protected void paintResponses(Graphics g) {
124: Dimension d = getSize();
125: XStyle responseStyle = styleManager
126: .getStyle("base/Question/Response");
127: Font f = styleManager.getFont(responseStyle);
128: g.setFont(f);
129: FontMetrics fm = g.getFontMetrics(f);
130:
131: int xOffset = woffset + qWidth + qoffset;
132: int posY = (d.height - checkSize) / 2;
133:
134: int numResponses = question.getNumOptions();
135: for (int i = 0; i < numResponses; i++) {
136: String responseText = question.getOptionText(i);
137:
138: // multiple choice
139: if (question.getQuestionType() == Question.MULTIPLE_CHOICE) {
140: int posX = xOffset + (surroundWidth - checkSize) / 2;
141: paintCheckBox(posX, posY, g);
142: g.setColor(responseTextColor);
143: posX = xOffset + surroundWidth + responseTextGap;
144: g.drawString(responseText, posX, posY + fm.getAscent()
145: - 3);
146: }
147: // mutually exclusive
148: else if (question.getQuestionType() == Question.MUTUALLY_EXCLUSIVE) {
149: int posX = xOffset + (surroundWidth - checkSize) / 2;
150: paintRadioButton(posX, posY, g);
151: g.setColor(responseTextColor);
152: posX = xOffset + surroundWidth + responseTextGap;
153: g.drawString(responseText, posX, posY + fm.getAscent()
154: - 3);
155: }
156: // free text
157: else if (question.getQuestionType() == Question.FREE_TEXT) {
158: g.setColor(responseTextColor);
159: int w = (int) fm.getStringBounds(responseText, g)
160: .getWidth();
161: int posX = xOffset + w + 4;
162: editTable[i].setLocation(posX, posY);
163: posX = xOffset;
164: g.drawString(responseText, posX, posY + fm.getAscent());
165: }
166: xOffset += xScale;
167: }
168: }
169:
170: protected void paintActiveResponses(Graphics g) {
171: int questionType = question.getQuestionType();
172:
173: Dimension d = getSize();
174: if ((questionType != Question.FREE_TEXT)
175: && (clickListener != null)
176: && (clickListener.getIsEntered())) {
177:
178: int activeResponse = clickListener.getActiveResponse();
179: if (activeResponse >= 0) {
180: int xOffset = woffset + qWidth + qoffset
181: + activeResponse * xScale;
182: int posY = (d.height - checkSize) / 2 - 1;
183: // multiple choice
184: if (questionType == Question.MULTIPLE_CHOICE) {
185: int posX = xOffset + (surroundWidth - checkSize)
186: / 2;
187: paintActiveCheckBox(posX, posY, g);
188: }
189: // mutually exclusive
190: else if (questionType == Question.MUTUALLY_EXCLUSIVE) {
191: int posX = xOffset + (surroundWidth - checkSize)
192: / 2;
193: paintActiveRadioButton(posX, posY, g);
194: }
195: }
196: }
197: }
198:
199: protected void paintSelectedResponses(Graphics g) {
200: if (question.getQuestionType() == Question.FREE_TEXT)
201: return;
202: Dimension d = getSize();
203: int posX = woffset + qWidth + qoffset
204: + (surroundWidth - checkSize) / 2;
205: int posY = (d.height - checkSize) / 2;
206: g.setColor(checkColor);
207: int resState = responseState;
208: while (resState > 0) {
209: if ((resState & 0x1) == 1) {
210: int x = posX + 3;
211: int y = posY + 2;
212: int w = checkSize - 5;
213: int h = checkSize - 5;
214: g.fillRect(x, y, w, h);
215: }
216:
217: resState >>= 1;
218: posX += xScale;
219: }
220: }
221:
222: public void paint(Graphics sg) {
223: // initialize
224: Image offscreen;
225: Graphics g;
226: if (!printing) {
227: offscreen = createImage(getSize().width, getSize().height);
228: g = offscreen.getGraphics();
229: } else {
230: offscreen = null;
231: g = sg;
232: }
233: Dimension d = getSize();
234: g.setClip(0, 0, d.width, d.height);
235: //////////
236:
237: paintTopBand(g);
238:
239: paintQuestionText(g);
240:
241: if (question.getQuestionType() != Question.FREE_TEXT)
242: paintSurrounds(g);
243:
244: paintResponses(g);
245:
246: if (question.getQuestionType() == Question.FREE_TEXT)
247: super Paint(g);
248:
249: paintHighlightFrame(g);
250:
251: paintActiveResponses(g);
252:
253: paintSelectedResponses(g);
254:
255: //////
256: // XStyle responseStyle = styleManager.getStyle( "base/Question/Response" );
257: // Font f = styleManager.getFont( responseStyle );
258: // g.setFont( f );
259: // FontMetrics fm = g.getFontMetrics( f );
260: //
261: // int xOffset = woffset + qWidth + qoffset;
262: // int posY = ( d.height - checkSize ) / 2;
263: //
264: // int numResponses = question.getNumOptions();
265: // for ( int i = 0; i < numResponses; i++ ) {
266: //
267: // // multiple choice
268: // if ( question.getQuestionType() == Question.MULTIPLE_CHOICE ) {
269: // int posX = xOffset + ( surroundWidth - checkSize ) / 2;
270: // paintCheckBox( posX, posY, g );
271: // }
272: // // mutually exclusive
273: // else if ( question.getQuestionType() == Question.MUTUALLY_EXCLUSIVE ) {
274: // int posX = xOffset + ( surroundWidth - checkSize ) / 2;
275: // paintRadioButton( posX, posY, g );
276: // }
277: // // free text
278: // else if ( question.getQuestionType() == Question.FREE_TEXT ) {
279: //
280: // }
281: // xOffset += xScale;
282: // }
283:
284: if (isLastQuestion)
285: paintBottomBand(g);
286:
287: /////////
288: if (!printing) {
289: sg.drawImage(offscreen, 0, 0, getSize().width,
290: getSize().height, null);
291: g.dispose();
292: offscreen = null;
293: }
294: }
295:
296: /**
297: * Renders the question. Renders question text as a single strip wrapped
298: * over multiple lines and without using a header. The responses/options are
299: * then spaced horizontally with a block/shadow behind the check marks.
300: * @param g
301: */
302: public void bpaint(Graphics g) {
303: int xOffset = woffset;
304:
305: Font f;
306: FontMetrics fm;
307: Graphics og;
308:
309: Dimension d = getSize();
310:
311: offscreen = createImage(d.width, d.height);
312:
313: og = offscreen.getGraphics();
314: og.setClip(0, 0, d.width, d.height);
315: XStyle questionStyle = styleManager.getStyle("Question");
316: f = styleManager.getFont(questionStyle);
317: og.setFont(f);
318: fm = og.getFontMetrics(f);
319:
320: // Draw light bands across the top and bottom.
321: og.setColor(cueColor);
322: og.fillRect(0, 0, d.width + 1, topBandHeight);
323: og.fillRect(0, d.height - 2, d.width + 1, 2);
324:
325: // Fill in the background
326: og.setColor(questionBkColor);
327: og.fillRect(0, topBandHeight, d.width, d.height - topBandHeight
328: - 2);
329:
330: // Draw the question text over multiple lines
331: og.setColor(questionTextColor);
332: int start = 0;
333: int end = 0;
334: String line;
335: int lineNum = 1;
336: String questionText = question.getText();
337: do {
338: boolean drawLine = false;
339:
340: // Extend the string by a word (to the next space, if any)
341: end = questionText.indexOf(' ', end + 1);
342: String ss;
343: if (end > 0)
344: ss = questionText.substring(start, end);
345: else {
346: ss = questionText.substring(start);
347: drawLine = true;
348: }
349:
350: // Does the next word fit in?
351: if (fm.getStringBounds(ss, g).getWidth() < (qWidth - 30))
352: line = ss;
353: else
354: drawLine = true;
355:
356: if (drawLine) {
357: og.drawString(ss, woffset, lineNum * fm.getAscent()
358: + 10 + topBandHeight);
359: lineNum++;
360: start = end + 1;
361: }
362: } while (end >= 0);
363:
364: // If no text drawn, then do it now
365: if ((start == 0) && (lineNum == 1))
366: og.drawString(questionText, woffset, fm.getAscent()
367: + topBandHeight);
368:
369: XStyle responseStyle = styleManager
370: .getStyle("Question/Response");
371: f = styleManager.getFont(responseStyle);
372: og.setFont(f);
373: fm = og.getFontMetrics(f);
374:
375: xOffset = qWidth + woffset + woffset / 2;
376: int responses = question.getNumOptions();
377: int ctop = d.height / 2 - 10;
378:
379: for (int i = 0; i < responses; i++) {
380: // Draw the surround
381: og.setColor(shadeColor);
382: og.fillRect(xOffset - 10, topBandHeight, 30, d.height
383: - topBandHeight - 2);
384:
385: // Draw the check background
386: XGraphicUtils.draw3dObject(og, false, xOffset, ctop, 12,
387: 12, checkBkColor, responseTextBkColor, checkColor,
388: shadowColor, responseTextBkColor);
389:
390: // Draw the text
391: og.setColor(responseTextColor);
392: og.drawString(question.getOptionText(i), xOffset + 22, fm
393: .getAscent()
394: + ctop);
395:
396: xOffset += xScale;
397: }
398:
399: xOffset = qWidth + woffset + woffset / 2;
400: if ((clickListener != null) && (clickListener.getIsEntered())) {
401: // highlight the question with a frame
402: og.setColor(responseTextBkColor);
403: og.drawRect(0, topBandHeight, d.width - 2, d.height - 3
404: - topBandHeight);
405:
406: int activeResponse = clickListener.getActiveResponse();
407: if (activeResponse >= 0) {
408: // Draw a button like object
409: XGraphicUtils.draw3dObject(og, true, xOffset
410: + activeResponse * xScale, ctop, 12, 12,
411: checkBkColor, responseTextBkColor, checkColor,
412: shadowColor, responseTextBkColor);
413: og.fillRect(xOffset + activeResponse * xScale + 2,
414: ctop + 2, 9, 9);
415: }
416: }
417: if (clickListener.getSelectedResponse() >= 0) {
418: og.setColor(selectedColor);
419: og.fillRect(xOffset + clickListener.getSelectedResponse()
420: * xScale + 2, ctop + 2, 8, 8);
421: }
422:
423: g.drawImage(offscreen, 0, 0, null);
424: og.dispose();
425: }
426:
427: /**
428: * Find a response
429: * @param x the x coordinate of the mouse click
430: * @param y the y coordinate of the mouse click
431: * @param defResponse the default response
432: * @return true if a response was found
433: */
434: public boolean setState(int x, int y, int defResponse) {
435: int numResponses = question.getNumOptions();
436: int oldResponse = defResponse;
437: int activeResponse = (x - qWidth) / xScale;
438: if ((activeResponse == 0) && (x < 0))
439: activeResponse = oldResponse;
440: else if (activeResponse >= numResponses)
441: activeResponse = oldResponse;
442:
443: clickListener.setActiveResponse(activeResponse);
444: return (defResponse != activeResponse);
445: }
446:
447: /**
448: * Called by XClickListener to check if a response event should be sent to the
449: * parent form. The control can also use this event to do post click processing
450: * @return true if the parent is to be notified
451: */
452: public boolean respond() {
453: return true;
454: }
455:
456: /**
457: * Repaint the responses
458: */
459: public void paintStates() {
460: repaint();// 0, 10, d.width+1, d.height -20 );
461: }
462:
463: /**
464: * Clear the screen
465: */
466: public void invalidate() {
467: super .invalidate();
468: offscreen = null;
469: }
470:
471: /**
472: * Update the screen
473: * @param g
474: */
475: public void update(Graphics g) {
476: paint(g);
477: }
478:
479: /**
480: * Is the prepared image drawn off screen?
481: * @return the double buffered flag
482: */
483: public boolean isDoubleBuffered() {
484: return true;
485: }
486: }
|