/*** * Copyright (c) 2008, Endless Loop Software, Inc. * * This file is part of EgoNet. * * EgoNet is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * EgoNet is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.egonet.gui.author; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.util.Observable; import java.util.Observer; import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.EtchedBorder; import javax.swing.border.TitledBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.text.PlainDocument; import org.egonet.model.answer.*; import org.egonet.model.question.AlterPairQuestion; import org.egonet.model.question.AlterPromptQuestion; import org.egonet.model.question.Question; import org.egonet.model.question.Selection; import org.egonet.util.CatchingAction; import org.egonet.util.WholeNumberDocument; /** * Generic Panel creation and handling routines for question editing */ public class QuestionLinkDialog extends JDialog implements Observer { private Question baseQuestion; private Answer linkAnswer = new TextAnswer(new Long(-1)); /* Containers */ private final JSplitPane questionSplit = new JSplitPane(); private final JList<Question> questionList = new JList<Question>(); private final JScrollPane questionListScroll = new JScrollPane(questionList); private final JPanel answerPanel = new JPanel(); private final JPanel radioPanel = new JPanel(); private final JPanel menuPanel = new JPanel(); private final JPanel textPanel = new JPanel(); private final JPanel questionPanelLeft = new JPanel(); private final JPanel questionPanelRight = new RightPanel(); private final GridBagLayout questionListLayout = new GridBagLayout(); private final GridBagLayout answerRadioLayout = new GridBagLayout(); private final GridBagLayout answerTextLayout = new GridBagLayout(); private final GridBagLayout answerMenuLayout = new GridBagLayout(); private final GridLayout answerPanelLayout = new GridLayout(); /* UI Elements */ private final Border listBorder; private final JLabel titleText = new JLabel(); private final JTextArea questionText = new JTextArea(); private final JTextArea answerTextField = new NoTabTextArea(); private final JButton questionButtonOK = new JButton("OK"); private final JButton questionButtonCancel = new JButton("Cancel"); private final JButton questionButtonNone = new JButton("No Link"); private final JLabel textAnswerLabel = new JLabel("Answer: "); private final JLabel menuAnswerLabel = new JLabel("Answer: "); private final JLabel radioAnswerLabel = new JLabel("Answer: "); private final JComboBox<Selection> answerMenu = new JComboBox<Selection>(); private final JCheckBox allAdjacentCheck = new JCheckBox("All Adjacent Selections"); private final ButtonGroup answerButtonGroup = new ButtonGroup(); private ActionListener answerButtonListener; private final WholeNumberDocument wholeNumberDocument = new WholeNumberDocument(); private final PlainDocument plainDocument = new PlainDocument(); /* Lists */ private final static int MAX_BUTTONS = 5; private final JRadioButton[] answerButtons = { new JRadioButton(), new JRadioButton(), new JRadioButton(), new JRadioButton(), new JRadioButton(), new JRadioButton()}; /* Question Iteration Variables */ private Question question; private EgoNet egoNet; /** * Generates Panel for question editing to insert in file tab window * @param parent parent frame for referencing composed objects */ public QuestionLinkDialog(EgoNet egoNet) throws Exception { this.egoNet = egoNet; listBorder = BorderFactory.createCompoundBorder( new TitledBorder(new EtchedBorder(EtchedBorder.RAISED,Color.white,new Color(178, 178, 178)), "Questions"), BorderFactory.createEmptyBorder(10,10,10,10)); jbInit(); } /** * Component initialization * @throws Exception */ private void jbInit() throws Exception { /* Overview Layout */ this.getContentPane().setLayout(new GridLayout()); // Configure Split Frame questionSplit.setMinimumSize(new Dimension(430, 330)); questionSplit.setPreferredSize(new Dimension(430, 330)); questionSplit.setResizeWeight(.33); questionSplit.setDividerLocation(.33); questionText.setBackground(SystemColor.window); questionText.setFont(new java.awt.Font("Serif", 0, 16)); questionText.setLineWrap(true); questionText.setTabSize(4); questionText.setWrapStyleWord(true); questionText.setEditable(false); questionSplit.add(questionPanelLeft, JSplitPane.LEFT); questionSplit.add(questionPanelRight, JSplitPane.RIGHT); questionPanelLeft.setLayout(questionListLayout); questionPanelLeft.setVisible(true); titleText.setFont(new java.awt.Font("Lucida Grande Bold", 0, 16)); // Configure List questionListScroll.setBorder(listBorder); questionListScroll.setMinimumSize(new Dimension(150, 150)); questionListScroll.setPreferredSize(new Dimension(150, 150)); questionListScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); questionList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // Configure question fields questionPanelRight.setLayout(new GridBagLayout()); radioPanel.setLayout(answerRadioLayout); textPanel.setLayout(answerTextLayout); menuPanel.setLayout(answerMenuLayout); questionText.setBorder(BorderFactory.createLoweredBevelBorder()); answerMenu.setOpaque(true); answerPanel.setLayout(answerPanelLayout); answerTextField.setFont(new java.awt.Font("SansSerif", 0, 14)); answerTextField.setMaximumSize(new Dimension(72, 64)); answerTextField.setMinimumSize(new Dimension(72, 16)); answerTextField.setPreferredSize(new Dimension(72, 16)); answerTextField.setLineWrap(true); answerTextField.setRows(1); answerTextField.setTabSize(4); answerTextField.setWrapStyleWord(true); /* List Layout */ questionPanelLeft.add(questionListScroll, new GridBagConstraints(0, 0, 2, 1, 1.0, 1.0 ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 29, 302)); /* Question Layout */ questionPanelRight.add(titleText, new GridBagConstraints(0, 1, 4, 1, 0.0, 0.1 ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); questionPanelRight.add(questionText, new GridBagConstraints(0, 0, 4, 1, 1.0, 0.3 ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 10, 10, 10), 0, 0)); /* Radio Panel Answer Layout */ radioPanel.add(radioAnswerLabel, new GridBagConstraints(0, 0, 1, 1, 0.3, 0.0 ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 5), 0, 0)); for (int i = 0; i < MAX_BUTTONS; i++) { radioPanel.add(answerButtons[i], new GridBagConstraints(1, i, 1, 1, 0.6, 0.2, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); } /* Text Answer Layout */ textPanel.add(textAnswerLabel, new GridBagConstraints(0, 0, 1, 1, 0.3, 0.0 ,GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 5), 0, 0)); textPanel.add(answerTextField, new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0 ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); /* Popup Menu Answer Layout */ menuPanel.add(menuAnswerLabel, new GridBagConstraints(0, 0, 1, 1, 0.3, 0.0 ,GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 5), 0, 0)); menuPanel.add(answerMenu, new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0 ,GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0, 10, 0, 10), 0, 0)); /* Misc Button Layout */ questionPanelRight.add(questionButtonOK, new GridBagConstraints(0, 11, 1, 1, 0.33, 0.1 ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); questionPanelRight.add(questionButtonNone, new GridBagConstraints(2, 11, 1, 1, 0.33, 0.1 ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); questionPanelRight.add(questionButtonCancel, new GridBagConstraints(3, 11, 1, 1, 0.33, 0.1 ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); questionPanelRight.add(answerPanel, new GridBagConstraints(0, 5, 4, 4, 1.0, 0.5 ,GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(10, 10, 10, 10), 0, 0)); questionPanelRight.add(allAdjacentCheck, new GridBagConstraints(0, 10, 4, 1, 0.0, 0.0 ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10, 10), 0, 0)); /* Install event Handlers */ questionList.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { question_list_selectionChanged(e); }}); questionButtonOK.addActionListener(new CatchingAction("questionButtonOK") { public void safeActionPerformed(ActionEvent e) throws Exception { questionButtonOK_actionPerformed(e);}}); questionButtonNone.addActionListener(new CatchingAction("questionButtonNone") { public void safeActionPerformed(ActionEvent e) throws Exception { questionButtonNone_actionPerformed(e);}}); questionButtonCancel.addActionListener(new CatchingAction("questionButtonCancel") { public void safeActionPerformed(ActionEvent e) throws Exception { questionButtonCancel_actionPerformed(e);}}); answerButtonListener = new CatchingAction("answerButtonListener") { public void safeActionPerformed(ActionEvent e) throws Exception { questionAnsweredEventHandler(e);}}; answerMenu.addActionListener(new CatchingAction("answerMenu") { public void safeActionPerformed(ActionEvent e) throws Exception { questionAnsweredEventHandler(e); }}); wholeNumberDocument.addDocumentListener(new DocumentListener() { public void insertUpdate(DocumentEvent e) { answerTextEvent(e); } public void changedUpdate(DocumentEvent e) { answerTextEvent(e); } public void removeUpdate(DocumentEvent e) { answerTextEvent(e); }}); plainDocument.addDocumentListener(new DocumentListener() { public void insertUpdate(DocumentEvent e) { answerTextEvent(e); } public void changedUpdate(DocumentEvent e) { answerTextEvent(e); } public void removeUpdate(DocumentEvent e) { answerTextEvent(e); }}); answerButtonListener = new CatchingAction("") { public void safeActionPerformed(ActionEvent e) throws Exception { questionAnsweredEventHandler(e);}}; allAdjacentCheck.addActionListener(new CatchingAction(""){ public void safeActionPerformed(ActionEvent e) throws Exception { allAdjacentCheck_actionPerformed(e);}}); for (int i = 0; i <= MAX_BUTTONS; i++) { answerButtonGroup.add(answerButtons[i]); answerButtons[i].addActionListener(answerButtonListener); } this.getContentPane().add(questionSplit, null); } void activate(Question q) { question = null; linkAnswer = null; baseQuestion = q; // Question vars questionList.setModel(new DefaultListModel<Question>()); egoNet.getStudy().fillList((DefaultListModel<Question>) questionList.getModel(), q.UniqueId); // Set Selection if (baseQuestion.link.isActive()) { Question selected = egoNet.getStudy().getQuestions().getQuestion(baseQuestion.link.getAnswer().getQuestionId()); questionList.setSelectedValue(selected, true); } if ((questionList.getSelectedIndex() == -1) && (questionList.getModel().getSize() > 0)) { questionList.setSelectedIndex(0); } /* Prepare starting question and answer, fill with stored values */ question = (Question) questionList.getSelectedValue(); if (question != null) { linkAnswer = Answer.newInstance(question.answerType); linkAnswer.setQuestionId(question.UniqueId); if (baseQuestion.link.isActive() && (question.UniqueId.equals(baseQuestion.link.getAnswer().getQuestionId()))) { linkAnswer.setValue(baseQuestion.link.getAnswer().getValue()); linkAnswer.string = baseQuestion.link.getAnswer().string; linkAnswer.setAnswered(true); } } // Init fillPanel(); this.setTitle("Set Link for Question \"" + q.title + "\""); this.setSize(550, 500); //Center the window /* Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); Dimension frameSize = this.getSize(); if (frameSize.height > screenSize.height) { frameSize.height = screenSize.height; } if (frameSize.width > screenSize.width) { frameSize.width = screenSize.width; } this.setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2); */ this.show(); } /** * Updates right side question fields when the selection changes * @param e event generated by selection change. */ public void question_list_selectionChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting() && !questionList.isSelectionEmpty()) { question = (Question) questionList.getSelectedValue(); if (question != null) { linkAnswer = Answer.newInstance(question.answerType); linkAnswer.setQuestionId(question.UniqueId); if (baseQuestion.link.isActive() && (question.UniqueId.equals(baseQuestion.link.getAnswer().getQuestionId()))) { linkAnswer.setValue(baseQuestion.link.getAnswer().getValue()); linkAnswer.setIndex(baseQuestion.link.getAnswer().getIndex()); linkAnswer.string = baseQuestion.link.getAnswer().string; linkAnswer.setAnswered(true); } } setOKButtonState(); fillPanel(); } } /**** * fill List with appropriate questions * Set other fields to selected question */ public void fillPanel() { allAdjacentCheck.setVisible(false); if (question != null) { titleText.setText(question.title); answerPanel.setVisible(false); answerPanel.removeAll(); questionText.setText("Please select a question and an answer which will trigger the inclusion of the current question"); if (question instanceof AlterPromptQuestion) { // Should never be here //assert(false); } else if (question.answerType.equals(TextAnswer.class)) { answerPanel.add(textPanel); answerPanel.validate(); answerTextField.setDocument(plainDocument); answerTextField.requestFocus(); if (linkAnswer.isAnswered()) { answerTextField.setText(linkAnswer.string); } else { answerTextField.setText(""); } } else if (question.answerType.equals(InformationalAnswer.class)) { answerPanel.add(textPanel); answerTextField.setDocument(wholeNumberDocument); answerTextField.requestFocus(); if (linkAnswer.isAnswered()) { answerTextField.setText(linkAnswer.string); } else { answerTextField.setText(""); } } else if (question.getSelections().size() <= 5) { allAdjacentCheck.setVisible(question instanceof AlterPairQuestion); questionText.setText(question.text); answerPanel.add(radioPanel); if (linkAnswer.isAnswered()) { if (linkAnswer.getValue() == Answer.ALL_ADJACENT) { allAdjacentCheck.setSelected(true); answerButtons[MAX_BUTTONS].setSelected(true); } else { allAdjacentCheck.setSelected(false); answerButtons[question.getSelections().size() - (linkAnswer.getValue() + 1)].setSelected(true); } } else { answerButtons[MAX_BUTTONS].setSelected(true); } for (int i = 0; i < question.getSelections().size(); i++) { answerButtons[i].setText(question.getSelections().get(i).getString()); answerButtons[i].setVisible(true); answerButtons[i].setEnabled(linkAnswer.getValue() != Answer.ALL_ADJACENT); } for (int i = question.getSelections().size(); i < MAX_BUTTONS; i++) { answerButtons[i].setVisible(false); } } else { allAdjacentCheck.setVisible(question instanceof AlterPairQuestion); questionText.setText(question.text); answerPanel.add(menuPanel); answerMenu.removeAllItems(); answerMenu.addItem(new Selection("Select an answer")); for (int i = 0; i < question.getSelections().size(); i++) { answerMenu.addItem(question.getSelections().get(i)); } if (linkAnswer.getValue() == Answer.ALL_ADJACENT) { allAdjacentCheck.setSelected(true); answerMenu.setEnabled(false); } else if (linkAnswer.getValue() != Answer.NO_ANSWER) { allAdjacentCheck.setSelected(false); answerMenu.setEnabled(true); answerMenu.setSelectedIndex(question.getSelections().size() - linkAnswer.getValue()); } else { allAdjacentCheck.setSelected(false); answerMenu.setEnabled(true); answerMenu.setSelectedIndex(0); } } answerPanel.validate(); answerPanel.setVisible(true); } } /**** * Clear all on screen editable fields * Generally called when a new survey is started */ public void clearPanel() { ((DefaultListModel) questionList.getModel()).removeAllElements(); } /**** * Parses answer fields to retrieve answer to question * @param answer Answer from interview to fill with correct values */ void fillAnswer() { linkAnswer.string = null; if(question.answerType.equals(NumericalAnswer.class)) { if (answerTextField.getText().length() > 0) { linkAnswer.string = answerTextField.getText(); linkAnswer.setValue(Integer.valueOf(linkAnswer.string).intValue()); linkAnswer.setAnswered(true); } else { linkAnswer.setValue(Answer.NO_ANSWER); linkAnswer.setAnswered(false); } } else if(question.answerType.equals(TextAnswer.class)) { linkAnswer.string = answerTextField.getText(); linkAnswer.setValue(linkAnswer.string.length()); linkAnswer.setAnswered((linkAnswer.getValue() != 0)); } else if(question.answerType.equals(InformationalAnswer.class)) { linkAnswer.string = "informational"; linkAnswer.setValue(1); linkAnswer.setAnswered(true); } else if(question.answerType.equals(CategoricalAnswer.class)) { // option/radio buttons if (question.getSelections().size() <= 5) { if (allAdjacentCheck.isSelected()) { linkAnswer.setValue(Answer.ALL_ADJACENT); linkAnswer.string = "All Adjacent"; linkAnswer.setAnswered(true); } else { int button = selectedButtonIndex(answerButtons); linkAnswer.setAnswered( (button != MAX_BUTTONS)); if (linkAnswer.isAnswered()) { linkAnswer.setValue(question.getSelections().get(button).getValue()); linkAnswer.setIndex(question.getSelections().get(button).getIndex()); linkAnswer.string = answerButtons[button].getActionCommand(); } else { linkAnswer.setValue(Answer.NO_ANSWER); linkAnswer.setIndex(Answer.NO_ANSWER); linkAnswer.string = ""; } } } // drop down buttons else { if (allAdjacentCheck.isSelected()) { linkAnswer.setValue(Answer.ALL_ADJACENT); linkAnswer.string = "All Adjacent"; linkAnswer.setAnswered(true); } else if (answerMenu.getSelectedIndex() > 0) // 0th option is "Select an answer" { int selectionIndex = answerMenu.getSelectedIndex() - 1; linkAnswer.setValue(question.getSelections().get(selectionIndex).getValue()); linkAnswer.string = answerMenu.getSelectedItem().toString(); linkAnswer.setAnswered((selectionIndex <= question.getSelections().size())); } else { linkAnswer.setValue(Answer.NO_ANSWER); linkAnswer.string = ""; linkAnswer.setAnswered(false); } } } } void jShowListButton_actionPerformed(ActionEvent e) { questionPanelLeft.setVisible(!questionPanelLeft.isVisible()); questionSplit.setDividerLocation(.33); questionSplit.repaint(); } void questionButtonNone_actionPerformed(ActionEvent e) throws IOException { if (egoNet.getStudy().confirmIncompatibleChange(egoNet.getFrame())) { baseQuestion.link.setAnswer(null); egoNet.getStudy().setModified(true); egoNet.getStudy().setCompatible(false); } egoNet.getFrame().fillCurrentPanel(); this.hide(); } void questionButtonCancel_actionPerformed(ActionEvent e) { this.hide(); } void questionButtonOK_actionPerformed(ActionEvent e) throws IOException { if ((linkAnswer != null) && (linkAnswer.isAnswered()) && egoNet.getStudy().confirmIncompatibleChange(egoNet.getFrame())) { baseQuestion.link.setAnswer(linkAnswer); egoNet.getStudy().setModified(true); egoNet.getStudy().setCompatible(false); } this.hide(); egoNet.getFrame().fillCurrentPanel(); } int selectedButtonIndex(JRadioButton[] group) { int ri = -1; for (int i = 0; i < group.length; i++) { if (group[i].isSelected()) { ri = i; break; } } return (ri); } void setOKButtonState() { questionButtonOK.setEnabled((question != null) && linkAnswer.isAnswered()); } void questionAnsweredEventHandler(ActionEvent e) { if (e.getActionCommand() != "Initialization") { fillAnswer(); setOKButtonState(); } } void answerTextEvent(DocumentEvent e) { fillAnswer(); setOKButtonState(); } public void update(Observable o, Object arg) { fillAnswer(); setOKButtonState(); } void allAdjacentCheck_actionPerformed(ActionEvent e) { fillAnswer(); fillPanel(); setOKButtonState(); } }