001: package com.xoetrope.swing.survey;
002:
003: import com.xoetrope.survey.Question;
004: import java.awt.Dimension;
005: import java.awt.Font;
006: import java.awt.FontMetrics;
007: import java.awt.Graphics;
008:
009: import com.xoetrope.util.XGraphicUtils;
010: import java.awt.Image;
011: import net.xoetrope.xui.style.XStyle;
012:
013: /**
014: * <p>Description: Extends XQuestion by rendering text in a form suitable for a
015: * kiosk displaying one question at a time</p>
016: * <p>The following color indexes are used</p>
017: * <p>0 - The color for the top and bottom bands</p>
018: * <p>2 - The background color</p>
019: * <p>3 - The text color and option text color</p>
020: * <p>4 - The check mark surround color</p>
021: * <p>5 - The check mark background color</p>
022: * <p>6 - The check mark shadow color</p>
023: * <p>7 - The check mark frame color</p>
024: * <p>8 - The selected check mark shadow color</p>
025: * <p>9 - The selected check mark tick color</p>
026: *
027: * <p> Copyright (c) Xoetrope Ltd., 2001-2006, This software is licensed under
028: * the GNU Public License (GPL), please see license.txt for more details. If
029: * you make commercial use of this software you must purchase a commercial
030: * license from Xoetrope.</p>
031: * <p> $Revision: 1.4 $</p>
032: */
033: public class XKioskQuestion extends XQuestion {
034: // vertical gap between responses
035: protected int posY = 0;
036: protected int stepY = 0;
037: protected int responseHeight = 0;
038:
039: /**
040: * Sets the scale for question layout
041: * @param newScale the new scale
042: */
043: public void setScale(int newScale) {
044: Dimension d = getSize();
045: responseHGap = d.width / 5;
046:
047: responseVGap = 2;
048: woffset = 5;
049:
050: scale = newScale;
051: xScale = (getSize().width - woffset * 2) / scale;
052:
053: if (clickListener != null)
054: clickListener.setOffset(woffset);
055: }
056:
057: protected void paintQuestionText(Graphics g) {
058: Font f;
059: FontMetrics fm;
060:
061: String questionText = question.getText();
062: Dimension d = getSize();
063:
064: XStyle questionStyle = styleManager.getStyle("base/Question");
065: f = styleManager.getFont(questionStyle);
066: g.setFont(f);
067: fm = g.getFontMetrics(f);
068:
069: // Draw the question text over multiple lines
070: g.setColor(questionTextColor);
071: int start = 0;
072: int end = 0;
073: String line;
074: int lineNum = 1;
075: do {
076: boolean drawLine = false;
077:
078: // Extend the string by a word (to the next space, if any)
079: end = questionText.indexOf(' ', end + 1);
080: String ss;
081: if (end > 0)
082: ss = questionText.substring(start, end);
083: else {
084: ss = questionText.substring(start);
085: drawLine = true;
086: }
087:
088: // Does the next word fit in?
089: int sWidth = fm.stringWidth(ss);
090: if (sWidth < (d.width - 120))
091: line = ss;
092: else
093: drawLine = true;
094:
095: if (drawLine) {
096: posY = lineNum * fm.getAscent() + headerHeight;
097: g
098: .drawString(ss, woffset + (d.width - sWidth)
099: / 2, posY);
100: lineNum++;
101: start = end + 1;
102: }
103: } while (end >= 0);
104:
105: // If no text drawn, then do it now
106: if ((start == 0) && (lineNum == 1)) {
107: posY = fm.getAscent() + headerHeight;
108: g.drawString(questionText, woffset, fm.getAscent()
109: + headerHeight);
110: }
111:
112: posY += responseVGap;
113: }
114:
115: protected void paintSelectedResponses(Graphics g) {
116: if (question.getQuestionType() == Question.FREE_TEXT)
117: return;
118: g.setColor(checkColor);
119:
120: Dimension d = getSize();
121: int x = (d.width - checkSize) / 2 + 3;
122: int y = posY + (stepY - responseHeight) / 2 + 2;
123: int w = checkSize - 5;
124: int h = checkSize - 5;
125:
126: int resState = responseState;
127: while (resState > 0) {
128: if ((resState & 0x1) == 1) {
129: g.fillRect(x, y + (responseHeight - checkSize) / 2, w,
130: h);
131: }
132: resState >>= 1;
133: y += stepY;
134: }
135: }
136:
137: protected void paintResponses(Graphics g) {
138: Dimension d = getSize();
139: XStyle responseStyle = styleManager
140: .getStyle("base/Question/Response");
141: Font f = styleManager.getFont(responseStyle);
142: g.setFont(f);
143: FontMetrics fm = g.getFontMetrics(f);
144: int questionType = question.getQuestionType();
145: int numOptions = question.getNumOptions();
146:
147: // set
148: if (questionType != Question.FREE_TEXT)
149: responseHeight = Math.max(fm.getAscent(), checkSize);
150: else
151: responseHeight = Math.max(fm.getAscent(), editHeight);
152:
153: int dh = responseVGap + (isLastQuestion ? topBandHeight : 0);
154: stepY = (d.height - posY - dh) / numOptions;
155:
156: int px = responseHGap;
157: int py = posY + (stepY - responseHeight) / 2;
158: for (int i = 0; i < numOptions; i++) {
159: // paint the stripe
160: g.setColor(shadeColor);
161: int sy = py - 2;
162: int sh = responseHeight + 4;
163: g.fillRect(0, sy, d.width, sh);
164:
165: String responseText = question.getOptionText(i);
166: g.setColor(responseTextColor);
167: g.drawString(responseText, px, py + fm.getAscent() - 3);
168:
169: // multiple choice
170: if (questionType == Question.MULTIPLE_CHOICE) {
171: int cx = (d.width - checkSize) / 2;
172: paintCheckBox(cx,
173: py + (responseHeight - checkSize) / 2, g);
174: }
175: // mutually exclusive
176: else if (questionType == Question.MUTUALLY_EXCLUSIVE) {
177: int rx = (d.width - checkSize) / 2;
178: paintRadioButton(rx, py + (responseHeight - checkSize)
179: / 2, g);
180: }
181: // free text
182: else if (questionType == Question.FREE_TEXT) {
183: int rx = (d.width - editWidth) / 2;
184: editTable[i].setLocation(rx, py - 1);
185: }
186:
187: py += stepY;
188: }
189: }
190:
191: protected void paintActiveResponses(Graphics g) {
192: int questionType = question.getQuestionType();
193: Dimension d = getSize();
194:
195: if ((questionType != Question.FREE_TEXT)
196: && (clickListener != null)
197: && (clickListener.getIsEntered())) {
198:
199: int activeResponse = clickListener.getActiveResponse();
200: if (activeResponse >= 0) {
201:
202: int py = posY + (stepY - responseHeight) / 2
203: + activeResponse * stepY - 1;
204: int cx = (d.width - checkSize) / 2;
205: if (questionType == Question.MULTIPLE_CHOICE) {
206: paintActiveCheckBox(cx, py
207: + (responseHeight - checkSize) / 2, g);
208: } else if (questionType == Question.MUTUALLY_EXCLUSIVE) {
209: paintActiveRadioButton(cx, py
210: + (responseHeight - checkSize) / 2, g);
211: }
212:
213: }
214: }
215: }
216:
217: public void paint(Graphics sg) {
218: // initialize
219: Image offscreen;
220: Graphics g;
221: if (!printing) {
222: offscreen = createImage(getSize().width, getSize().height);
223: g = offscreen.getGraphics();
224: } else {
225: offscreen = null;
226: g = sg;
227: }
228: Dimension d = getSize();
229: g.setClip(0, 0, d.width, d.height);
230: //////////
231: paintTopBand(g);
232:
233: paintQuestionText(g);
234: paintResponses(g);
235:
236: if (question.getQuestionType() == Question.FREE_TEXT)
237: super Paint(g);
238:
239: paintHighlightFrame(g);
240:
241: paintActiveResponses(g);
242:
243: paintSelectedResponses(g);
244:
245: if (isLastQuestion)
246: paintBottomBand(g);
247:
248: /////////
249: if (!printing) {
250: sg.drawImage(offscreen, 0, 0, getSize().width,
251: getSize().height, null);
252: g.dispose();
253: offscreen = null;
254: }
255: ////////
256: }
257:
258: /**
259: * Draws the background for each option.
260: * @param g the graphics context
261: * @param x the left edge
262: * @param y the top
263: * @param w the width
264: * @param h the height
265: */
266: public void drawOptionBackground(Graphics g, int x, int y, int w,
267: int h) {
268: g.fillRect(x, y, w, h);
269: }
270:
271: /**
272: * Find a response
273: * @param x the x coordinate of the mouse click
274: * @param y the y coordinate of the mouse click
275: * @param defResponse the default response
276: * @return true if a response was found
277: */
278: public boolean setState(int x, int y, int defResponse) {
279: int numResponses = question.getNumOptions();
280: int oldResponse = defResponse;
281: int activeResponse = (y - posY) / stepY;
282: if ((activeResponse == 0) && (x < 0))
283: activeResponse = oldResponse;
284: else if (activeResponse >= numResponses)
285: activeResponse = oldResponse;
286: clickListener.setActiveResponse(activeResponse);
287: return (defResponse != activeResponse);
288: }
289:
290: /**
291: * Clear the screen
292: */
293: public void invalidate() {
294: super .invalidate();
295: }
296:
297: /**
298: * Update the screen
299: * @param g
300: */
301: public void update(Graphics g) {
302: paint(g);
303: }
304:
305: /**
306: * Is the prepared image drawn off screen?
307: * @return flag indicating double buffered state
308: */
309: public boolean isDoubleBuffered() {
310: return false;
311: }
312: }
|