1 /***
2 *
3 */
4 package org.telscenter.pas.steps;
5
6 import info.clearthought.layout.TableLayout;
7
8 import java.awt.BorderLayout;
9 import java.awt.Component;
10 import java.awt.Dimension;
11 import java.awt.event.ActionEvent;
12 import java.awt.event.ActionListener;
13 import java.awt.event.ComponentEvent;
14 import java.awt.event.ComponentListener;
15 import java.io.InputStream;
16 import java.util.ArrayList;
17 import java.util.HashMap;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.logging.Logger;
23
24 import javax.swing.AbstractAction;
25 import javax.swing.BorderFactory;
26 import javax.swing.Box;
27 import javax.swing.BoxLayout;
28 import javax.swing.ButtonGroup;
29 import javax.swing.ImageIcon;
30 import javax.swing.JButton;
31 import javax.swing.JComponent;
32 import javax.swing.JLabel;
33 import javax.swing.JPanel;
34 import javax.swing.JRadioButton;
35 import javax.swing.JScrollPane;
36 import javax.swing.JTabbedPane;
37 import javax.swing.JTextArea;
38 import javax.swing.ScrollPaneConstants;
39 import javax.swing.event.DocumentEvent;
40 import javax.swing.event.DocumentListener;
41 import javax.swing.text.BadLocationException;
42 import javax.swing.text.Document;
43
44 import org.apache.commons.io.IOUtils;
45 import org.apache.commons.lang.StringUtils;
46 import org.telscenter.pas.common.ui.CommonUI;
47 import org.telscenter.pas.common.ui.panel.CardPanel;
48 import org.telscenter.pas.steps.domain.BlockInteraction;
49 import org.telscenter.pas.steps.domain.ChoiceInteraction;
50 import org.telscenter.pas.steps.domain.ExtendedTextInteraction;
51 import org.telscenter.pas.steps.domain.SimpleChoice;
52 import org.telscenter.pas.ui.icons.PasCommonIconProvider;
53 import org.telscenter.pas.util.QTIUtils;
54 import org.xhtmlrenderer.simple.XHTMLPanel;
55
56 /***
57 * @author aperritano
58 *
59 */
60 public abstract class AssessmentUI extends JPanel implements IQtiStepUI {
61
62 protected static final Logger logger = Logger.getLogger(AssessmentUI.class
63 .getName());
64
65 private static final long serialVersionUID = 1L;
66
67 public static final ImageIcon greenCheck = new ImageIcon(
68 PasCommonIconProvider.getImage("guessCorrect.gif"));
69
70 public static final ImageIcon redX = new ImageIcon(PasCommonIconProvider.getImage("guessWrong.gif"));
71
72 protected String RESPONSE_ID = "RESPONSE_ID";
73
74 protected transient Map<ChoiceInteraction<String>, List<JRadioButton>> choiceInteractionToChoiceButtonLists = new HashMap<ChoiceInteraction<String>, List<JRadioButton>>();
75
76 protected CardPanel cardPanel = new CardPanel();
77
78 protected JPanel mainPanel;
79
80 protected JButton previousTabButton;
81
82 protected JButton nextTabButton;
83
84 protected JTabbedPane tabbedPane;
85
86 protected Map<JButton, ExtendedTextInteraction> placeholderButtonsToTextInteractions = new HashMap<JButton, ExtendedTextInteraction>();
87
88 protected Map<ExtendedTextInteraction, JTextArea> textInteractionsToTextareas = new HashMap<ExtendedTextInteraction, JTextArea>();
89
90 protected ImageIcon checkIcon = new ImageIcon(PasCommonIconProvider
91 .getImage("accept.png"));
92
93 protected boolean showTabButtons;
94
95 protected boolean showGetStartedPanel;
96
97 protected boolean showPlaceHolderPanel = true;
98
99 protected Map<BlockInteraction, Integer> interactionsToTabindex = new HashMap<BlockInteraction, Integer>();
100
101 protected Assessment assessment;
102
103 protected static String ITEM_COMPLETED = "ITEM_COMPLETED";
104
105 private static int PROMPT_PANEL_WIDTH = 300;
106
107 private static int PROMPT_PANEL_HEIGHT = 300;
108
109 /***
110 *
111 */
112
113 public AssessmentUI(){
114
115
116 }
117
118 public AssessmentUI(Assessment assessment) {
119 this.assessment = assessment;
120 initUI();
121 }
122
123 protected abstract void initUI();
124
125 class NextTabAction extends AbstractAction {
126
127 public NextTabAction(String title, ImageIcon icon) {
128 super(title, icon);
129 }
130
131 public void actionPerformed(ActionEvent e) {
132 tabbedPane.setSelectedIndex(tabbedPane.getSelectedIndex() + 1);
133 checkTab();
134 }
135 }
136
137 class PreviousTabAction extends AbstractAction {
138
139 public PreviousTabAction(String title, ImageIcon icon) {
140 super(title, icon);
141 }
142
143 public void actionPerformed(ActionEvent e) {
144 tabbedPane.setSelectedIndex(tabbedPane.getSelectedIndex() - 1);
145 checkTab();
146 }
147 }
148
149 class GetStartedAction extends AbstractAction {
150
151 /***
152 *
153 */
154 public GetStartedAction(String title) {
155 super(title);
156
157 }
158
159 public void actionPerformed(ActionEvent e) {
160 JButton button = (JButton) e.getSource();
161
162 ExtendedTextInteraction et = placeholderButtonsToTextInteractions
163 .get(button);
164
165 JTextArea area = textInteractionsToTextareas.get(et);
166
167 String textInArea = area.getText();
168
169 String placeholderText = et.getPlaceholderText();
170 StringBuffer buff;
171 if (placeholderText != null)
172 buff = new StringBuffer(placeholderText);
173 else
174 buff = new StringBuffer();
175
176 buff.append(" ").append(textInArea);
177
178 area.setText("");
179 area.setText(buff.toString());
180 button.setEnabled(false);
181
182 }
183
184 }
185
186 protected void showTabChecked(int selectedTab) {
187 tabbedPane.setIconAt(selectedTab, checkIcon);
188 ((JComponent) tabbedPane.getComponentAt(selectedTab))
189 .putClientProperty(ITEM_COMPLETED, new Boolean(true));
190 }
191
192 protected void showTabUnChecked(int selectedTab) {
193 tabbedPane.setIconAt(selectedTab, null);
194 ((JComponent) tabbedPane.getComponentAt(selectedTab))
195 .putClientProperty(ITEM_COMPLETED, new Boolean(false));
196 }
197
198 class AssDocumentAdapter implements DocumentListener {
199
200 protected ExtendedTextInteraction eti;
201
202 public AssDocumentAdapter(ExtendedTextInteraction eti) {
203 this.eti = eti;
204 }
205
206 public void insertUpdate(DocumentEvent e) {
207
208 if(assessment.getTabbed() ) {
209 showTabChecked(tabbedPane.getSelectedIndex());
210 }
211 insertReponses(e);
212
213 }
214
215 public void removeUpdate(DocumentEvent e) {
216
217 Document document = e.getDocument();
218 if (document.getLength() == 0) {
219
220
221 Set<JButton> keySet = placeholderButtonsToTextInteractions.keySet();
222
223 for (Iterator<JButton> iter = keySet.iterator(); iter.hasNext();) {
224 JButton button = iter.next();
225 ExtendedTextInteraction newEti = placeholderButtonsToTextInteractions
226 .get(button);
227
228 if (this.eti.equals(newEti)) {
229
230 if (eti.getPlaceholderText() != null)
231 button.setEnabled(true);
232
233 }
234
235 }
236
237
238 if( assessment.getTabbed() ) {
239 showTabUnChecked(tabbedPane.getSelectedIndex());
240 }
241
242 }
243 insertReponses(e);
244 }
245
246 public void changedUpdate(DocumentEvent e) {
247
248
249 }
250
251 /***
252 * @param e
253 */
254 private void insertReponses(DocumentEvent e) {
255 Document document = e.getDocument();
256 String responseID = (String) document.getProperty(RESPONSE_ID);
257 if (responseID == null)
258 throw new NullPointerException();
259
260 String responseText = null;
261 try {
262 responseText = document.getText(0, document.getLength());
263 } catch (BadLocationException e1) {
264 e1.printStackTrace();
265 }
266 assessment.getResponses().put(responseID, responseText);
267
268
269 }
270
271 }
272
273 public boolean isCompleted() {
274
275 if( tabbedPane == null )
276 return true;
277
278 if( tabbedPane.getComponents() == null)
279 return true;
280
281 Component[] components = tabbedPane.getComponents();
282
283
284
285 for (int i = 0; i < components.length; i++) {
286 JComponent component = (JComponent) components[i];
287 Boolean clientProperty = (Boolean) component
288 .getClientProperty(ITEM_COMPLETED);
289 if (clientProperty != null) {
290 if (clientProperty.booleanValue() == false)
291 return false;
292 }
293 }
294
295 return true;
296
297 }
298
299 protected void checkTab() {
300 if(tabbedPane != null){
301 int numOfTabs = tabbedPane.getTabCount();
302 int selectedTabIndex = tabbedPane.getSelectedIndex();
303
304 nextTabButton.setToolTipText("Flip to the next tab");
305 nextTabButton.setEnabled(false);
306 previousTabButton.setEnabled(false);
307 previousTabButton.setToolTipText("Flip to the previous tab");
308
309
310 if ((numOfTabs > 1) && (selectedTabIndex != (numOfTabs - 1))) {
311 nextTabButton.setEnabled(true);
312 } else {
313 nextTabButton.setEnabled(false);
314 }
315
316
317 if ((numOfTabs > 1) && (selectedTabIndex != 0)) {
318 previousTabButton.setEnabled(true);
319 } else {
320 previousTabButton.setEnabled(false);
321 }
322 }
323 }
324
325 /***
326 * @param interaction
327 * @param itemIndex
328 * @return
329 */
330 protected JPanel createAssessmentItemPanel(
331 final BlockInteraction interaction) {
332 if (interaction instanceof ExtendedTextInteraction) {
333 final ExtendedTextInteraction textInteraction = (ExtendedTextInteraction) interaction;
334 return createTextInteractionPanel(textInteraction);
335 } else if (interaction instanceof ChoiceInteraction) {
336 final ChoiceInteraction<String> choiceInteraction = (ChoiceInteraction<String>) interaction;
337 return createChoiceInteractionPanel(choiceInteraction);
338 } else {
339 throw new IllegalArgumentException("normal BlockeInteraction not supported yet?");
340 }
341 }
342
343 protected JPanel createChoiceInteractionPanel(
344 final ChoiceInteraction<String> choiceInteraction) {
345 JPanel formPanel = new JPanel();
346
347 double pref = TableLayout.PREFERRED;
348 double fill = TableLayout.FILL;
349 double vSpace = 5;
350 double hSpace = 6;
351 double size[][] = { { fill
352 },
353
354 { pref, vSpace, pref, vSpace, pref }
355
356 };
357 formPanel.setLayout(new TableLayout(size));
358 formPanel.setOpaque(false);
359
360 JComponent promptPanel = createPromptPanel(choiceInteraction);
361
362 promptPanel.setBorder(BorderFactory.createEmptyBorder());
363
364 formPanel.add(promptPanel, "0,0");
365
366 JPanel choiceAreaPanel = createChoiceAreaPanel(choiceInteraction);
367 choiceAreaPanel.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 4));
368 formPanel.add(choiceAreaPanel, "0,2, l, t");
369
370 return formPanel;
371 }
372
373 /***
374 * @param choiceInteraction
375 * @return
376 */
377 protected JPanel createChoiceAreaPanel(
378 final ChoiceInteraction<String> choiceInteraction) {
379
380 List<JRadioButton> choiceButtonList = new ArrayList<JRadioButton>();
381 List<SimpleChoice<String>> simpleChoices = choiceInteraction.getSimpleChoices();
382 ButtonGroup group = new ButtonGroup();
383 JPanel buttonPanel = new JPanel();
384
385
386 buttonPanel.setOpaque(false);
387 buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.PAGE_AXIS));
388 String studentAnswer = Assessment.getStudentAnswer(assessment,
389 choiceInteraction);
390 for (SimpleChoice<String> sc : simpleChoices) {
391 JRadioButton scButton = new JRadioButton();
392
393
394 if (studentAnswer != null
395 && studentAnswer.equals(sc.getIdentifier())) {
396 scButton.setSelected(true);
397 } else {
398 scButton.setSelected(false);
399 }
400 scButton.setOpaque(false);
401 scButton.putClientProperty(QTIUtils.SIMPLE_CHOICE, sc);
402 scButton.putClientProperty(RESPONSE_ID, choiceInteraction
403 .getResponseIdentifier());
404 scButton.addActionListener(new ActionListener() {
405
406 public void actionPerformed(ActionEvent e) {
407 JRadioButton radioButton = (JRadioButton) e.getSource();
408
409 if (radioButton.isSelected()) {
410 SimpleChoice<String> sc = ((SimpleChoice<String>) radioButton
411 .getClientProperty(QTIUtils.SIMPLE_CHOICE));
412
413 assessment.getResponses().put(
414 (String) radioButton
415 .getClientProperty(RESPONSE_ID),
416 sc.getIdentifier());
417
418 System.out.println("responses: "
419 + assessment.getResponses().toString());
420 if(tabbedPane != null)
421 showTabChecked(tabbedPane.getSelectedIndex());
422 }
423 }
424 });
425 scButton.setText((String) sc.getContent());
426
427
428 choiceButtonList.add(scButton);
429 buttonPanel.add(scButton);
430
431 group.add(scButton);
432 }
433 buttonPanel.add(Box.createVerticalStrut(10));
434 choiceInteractionToChoiceButtonLists.put(choiceInteraction,
435 choiceButtonList);
436
437
438
439
440 return buttonPanel;
441
442 }
443
444 /***
445 * @param textInteraction
446 * @return
447 */
448 private JPanel createTextInteractionPanel(
449 final ExtendedTextInteraction textInteraction) {
450
451 JPanel formPanel = new JPanel();
452 formPanel.setBorder(BorderFactory.createEmptyBorder());
453 double pref = TableLayout.PREFERRED;
454 double fill = TableLayout.FILL;
455 double vSpace = 5;
456 double hSpace = 6;
457 double size[][] = { { fill
458 },
459
460 { pref, vSpace, pref, vSpace, fill }
461
462 };
463
464 JComponent promptPanel = createPromptPanel(textInteraction);
465
466 promptPanel.setBorder(BorderFactory.createEmptyBorder());
467
468
469
470
471
472
473 String placeholderText = textInteraction.getPlaceholderText();
474
475 JComponent textAreaPanel = createTextAreaPanel(textInteraction);
476 textAreaPanel.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 4));
477
478 formPanel.setLayout(new TableLayout(size));
479 formPanel.setOpaque(false);
480
481 formPanel.add(promptPanel, "0,0");
482
483
484 if( showPlaceHolderPanel ) {
485
486 if (placeholderText != null
487 || StringUtils.trimToNull(placeholderText) != null) {
488 if( assessment.isInjectPrompt() ) {
489 formPanel.add(textAreaPanel, "0,2");
490 } else {
491 JPanel getStartedButtonPanel = createGetStartedButtonPanel(textInteraction);
492 getStartedButtonPanel.setBorder(BorderFactory.createEmptyBorder(0,
493 4, 0, 0));
494 formPanel.add(getStartedButtonPanel, "0,2");
495 formPanel.add(textAreaPanel, "0,4");
496 }
497
498
499
500 } else {
501 formPanel.add(textAreaPanel, "0,2");
502 }
503 } else {
504
505 formPanel.add(textAreaPanel, "0,2");
506 }
507 JPanel t = new JPanel(new BorderLayout(0, 0));
508 t.add(formPanel, BorderLayout.NORTH);
509
510 return t;
511
512 }
513
514 /***
515 * @param textInteraction
516 * @param itemIndex
517 * @return
518 */
519 private JComponent createTextAreaPanel(
520 ExtendedTextInteraction textInteraction) {
521
522 JTextArea answerArea = getAnswerArea(textInteraction);
523
524 String studentAnswer = Assessment.getStudentAnswer(assessment,
525 textInteraction);
526
527 String placeholderText = textInteraction.getPlaceholderText();
528
529 if (studentAnswer != null) {
530 answerArea.setText(studentAnswer);
531 } else {
532 if (placeholderText != null || StringUtils.trimToNull(placeholderText) != null) {
533 if( assessment.isInjectPrompt() ) {
534 answerArea.setText(placeholderText);
535 }
536 }
537 }
538
539 textInteractionsToTextareas.put(textInteraction, answerArea);
540
541 int expectedLines = textInteraction.getExpectedLines();
542
543 if (expectedLines == 0) {
544 answerArea.setRows(10);
545 } else {
546 answerArea.setRows(expectedLines);
547 }
548
549 answerArea.setColumns(35);
550
551 answerArea.getDocument().putProperty(RESPONSE_ID,
552 answerArea.getClientProperty(RESPONSE_ID));
553
554 answerArea.getDocument().addDocumentListener(
555 new AssDocumentAdapter(textInteraction));
556
557 JScrollPane spane = getScrollPane(answerArea);
558
559 return spane;
560 }
561
562 /***
563 * @param answerArea
564 * @return
565 */
566 private JScrollPane getScrollPane(JTextArea answerArea) {
567 JScrollPane spane = new JScrollPane(answerArea);
568 spane.setOpaque(false);
569 spane
570 .setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
571 spane
572 .setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
573 spane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
574 spane.getVerticalScrollBar().getModel().setValue(
575 spane.getVerticalScrollBar().getModel().getMinimum());
576 spane.setPreferredSize(new Dimension(answerArea
577 .getPreferredScrollableViewportSize().width, answerArea
578 .getPreferredScrollableViewportSize().height + 5));
579
580
581 spane.requestFocusInWindow();
582
583 return spane;
584 }
585
586 /***
587 * @param textInteraction
588 * @return
589 */
590 private JTextArea getAnswerArea(ExtendedTextInteraction textInteraction) {
591 JTextArea answerArea = new JTextArea();
592 answerArea.setName(textInteraction.getResponseIdentifier());
593
594 answerArea.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
595
596
597
598 answerArea.putClientProperty(RESPONSE_ID, textInteraction
599 .getResponseIdentifier());
600 answerArea.setFocusable(true);
601 answerArea.requestFocusInWindow();
602 answerArea.setEditable(true);
603 answerArea.setBorder(CommonUI.createEtchedBorder());
604 answerArea.setOpaque(true);
605 answerArea.setWrapStyleWord(true);
606 answerArea.setLineWrap(true);
607 answerArea.setFocusCycleRoot(true);
608 return answerArea;
609 }
610
611 /***
612 * @param textInteraction
613 * @return
614 */
615 private JPanel createGetStartedButtonPanel(
616 ExtendedTextInteraction textInteraction) {
617 JPanel startedPanel = new JPanel(new BorderLayout(0, 0));
618 startedPanel.setOpaque(false);
619
620 JButton showPlaceholderButton = new JButton(new GetStartedAction(Messages.getString("Note.GET_STARTED")));
621
622 CommonUI.makeButtonTransparent(showPlaceholderButton);
623 CommonUI.makeLink(showPlaceholderButton);
624
625
626 startedPanel.add(showPlaceholderButton, BorderLayout.WEST);
627 placeholderButtonsToTextInteractions.put(showPlaceholderButton,
628 textInteraction);
629
630 return startedPanel;
631 }
632
633 /***
634 * @param formPanel
635 * @param textI
636 * @return
637 * @return
638 */
639 public static JComponent createPromptPanel(BlockInteraction blockInteraction) {
640
641 if (blockInteraction == null)
642 return new JLabel("There was an error creating this prompt.");
643
644 final XHTMLPanel panel = new XHTMLPanel();
645 panel.setName("promptPanel");
646
647
648 panel.setOpaque(false);
649 panel.addComponentListener(new ComponentListener() {
650
651 public void componentHidden(ComponentEvent e) {
652
653
654 }
655
656 public void componentMoved(ComponentEvent e) {
657
658
659 }
660
661 public void componentResized(ComponentEvent e) {
662
663
664 }
665
666 public void componentShown(ComponentEvent e) {
667
668
669 }
670 });
671
672
673
674 InputStream stream = null;
675 try {
676 stream = IOUtils.toInputStream(blockInteraction.getPrompt());
677 panel.setDocument(stream, null);
678 } catch (NullPointerException e) {
679 e.printStackTrace();
680 } catch (Exception e) {
681 e.printStackTrace();
682 } finally {
683 IOUtils.closeQuietly(stream);
684 }
685
686
687
688 return panel;
689
690 }
691
692 public static JComponent createIntroTextPanel(String introText) {
693
694 if (introText == null)
695 return new JLabel("There is no introductory text.");
696
697 final XHTMLPanel panel = new XHTMLPanel();
698 panel.setName("introPanel");
699
700
701 panel.setOpaque(false);
702 panel.addComponentListener(new ComponentListener() {
703
704 public void componentHidden(ComponentEvent e) {
705
706
707 }
708
709 public void componentMoved(ComponentEvent e) {
710
711
712 }
713
714 public void componentResized(ComponentEvent e) {
715
716
717 }
718
719 public void componentShown(ComponentEvent e) {
720
721
722 }
723 });
724
725
726
727 InputStream stream = null;
728 try {
729 stream = IOUtils.toInputStream(introText);
730 panel.setDocument(stream, null);
731 } catch (NullPointerException e) {
732 e.printStackTrace();
733 } catch (Exception e) {
734 e.printStackTrace();
735 } finally {
736 IOUtils.closeQuietly(stream);
737 }
738
739
740
741 return panel;
742
743 }
744
745
746 /***
747 * @return the tabbedPane
748 */
749 public JTabbedPane getTabbedPane() {
750 return tabbedPane;
751 }
752
753 /***
754 * @param tabbedPane
755 * the tabbedPane to set
756 */
757 public void setTabbedPane(JTabbedPane tabbedPane) {
758 this.tabbedPane = tabbedPane;
759 }
760
761 }