package net.sf.egonet.web.panel;
import java.util.ArrayList;
import java.util.List;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import net.sf.egonet.model.Alter;
import net.sf.egonet.model.Answer;
import net.sf.egonet.model.Question;
import net.sf.egonet.model.QuestionOption;
import net.sf.egonet.persistence.Options;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.model.PropertyModel;
public class MultipleSelectionAnswerFormFieldPanel extends AnswerFormFieldPanel
implements ActionListener {
private CheckboxesPanel<Object> answerField;
private ArrayList<QuestionOption> originallySelectedOptions;
// variables dealing with lower & upper limits on the
// number of checkboxes we can select:
private Label checkBoxPrompt;
private Integer maxCheckable;
private Integer minCheckable;
// these variables deal with the textbox
// that appears if the 'Other' checkbox is selected;
private Label otherSpecifyLabel;
private TextField otherSpecifyTextField;
private String otherText;
private Boolean otherSpecifyStyle;
private Boolean horizontalLayout = false;
public MultipleSelectionAnswerFormFieldPanel(String id, Question question,
ArrayList<Alter> alters, Long interviewId) {
super(id,question,Answer.SkipReason.NONE,alters,interviewId);
originallySelectedOptions = Lists.newArrayList();
setOtherText("");
setHorizontalLayout ( question.getAskingStyleList());
build();
setFirstTimeOnQuestion(true);
}
public MultipleSelectionAnswerFormFieldPanel(String id,
Question question, String answer, String otherSpecText, Answer.SkipReason skipReason, ArrayList<Alter> alters, Long interviewId)
{
super(id,question,skipReason,alters,interviewId);
originallySelectedOptions = Lists.newArrayList();
setOtherText(otherSpecText);
try {
for(String answerIdString : answer.split(",")) {
Long answerId = Long.parseLong(answerIdString);
for(QuestionOption option : getOptions()) {
if(answerId.equals(option.getId())) {
originallySelectedOptions.add(option);
}
}
}
} catch(Exception ex) {
// Most likely failed to parse answer. Fall back to no existing answer.
}
setHorizontalLayout ( question.getAskingStyleList());
build();
}
private void build() {
this.setOutputMarkupId(true);
checkBoxPrompt = new Label ("checkBoxPrompt",getCheckRangePrompt());
add(checkBoxPrompt);
if ( question.getAskingStyleList())
checkBoxPrompt.setVisible(false);
List<Object> allItems = Lists.newArrayList();
allItems.addAll(getOptions());
if ( !question.getType().equals(Question.QuestionType.EGO_ID)) {
if ( question.getDontKnowButton())
allItems.add(dontKnow);
if ( question.getRefuseButton())
allItems.add(refuse);
}
List<Object> selectedItems = Lists.newArrayList();
if(originalSkipReason.equals(Answer.SkipReason.NONE)) {
selectedItems.addAll(originallySelectedOptions);
} else if(originalSkipReason.equals(Answer.SkipReason.DONT_KNOW)) {
selectedItems.add(dontKnow);
} else if(originalSkipReason.equals(Answer.SkipReason.REFUSE)) {
selectedItems.add(refuse);
}
answerField = new CheckboxesPanel<Object>("answer",allItems,selectedItems)
{
protected String showItem(Object option) {
return option instanceof QuestionOption ?
((QuestionOption) option).getName() :
option.toString();
}
protected String hotkey(Object obj) {
if(obj instanceof QuestionOption) {
try {
QuestionOption option = (QuestionOption) obj;
Integer intVal = Integer.parseInt(option.getValue());
if(intVal > -1 && intVal < 10) {
return intVal+"";
}
} catch(Exception ex) {
}
}
if(obj.equals(dontKnow)) {
return "F8";
}
if(obj.equals(refuse)) {
return "F9";
}
return null;
}
};
add(answerField);
// features that will be visible only for
// 'other/specify' questions
otherSpecifyStyle = question.getOtherSpecify();
otherSpecifyLabel = new Label("otherSpecifyLabel", "Specify Other: ");
otherSpecifyTextField = new TextField("otherSpecifyTextField", new PropertyModel(this, "otherText"));
add(otherSpecifyLabel);
add(otherSpecifyTextField);
otherSpecifyLabel.setOutputMarkupId(true);
otherSpecifyTextField.setOutputMarkupId(true);
if ( otherSpecifyStyle ) {
answerField.addActionListener(this);
answerField.addComponentToUpdate(this);
answerField.setOtherSpecifyStyle(true);
} else {
answerField.setOtherSpecifyStyle(false);
}
if ( otherSpecifyStyle && answerField.getOtherSelected(true)) {
otherSpecifyLabel.setVisible(true);
otherSpecifyTextField.setVisible(true);
} else {
otherSpecifyLabel.setVisible(false);
otherSpecifyTextField.setVisible(false);
}
answerField.setHorizontalLayout(horizontalLayout);
}
public String getAnswer() {
List<String> optionIdStrings = Lists.newArrayList();
for(Object option : answerField.getSelected()) {
if(option instanceof QuestionOption) {
optionIdStrings.add(((QuestionOption) option).getId().toString());
}
}
return Joiner.on(",").join(optionIdStrings);
}
public boolean dontKnow() {
// System.out.println("MultipeSelectionAnswer dontKnow");
// for ( Object obj : answerField.getSelected()) {
// System.out.println (obj.toString());
// }
return answerField.getSelected().contains(dontKnow);
}
public boolean refused() {
// System.out.println("MultipleSelectionAsnwer refused");
// for ( Object obj : answerField.getSelected()) {
// System.out.println (obj.toString());
// }
return answerField.getSelected().contains(refuse);
}
public List<QuestionOption> getOptions() {
return Options.getOptionsForQuestion(getQuestion().getId());
}
public void setAutoFocus() {
answerField.setAutoFocus();
}
/**
* creates the string that will tell the surveyer how many checkboxes
* to select.
* Sets the variables minCheckable and maxCheckable as a side effect
* @return prompt to display above checkboxes
*/
private String getCheckRangePrompt() {
Integer checkBoxCount = getOptions().size();
maxCheckable = question.getMaxCheckableBoxes();
minCheckable = question.getMinCheckableBoxes();
if ( checkBoxCount<maxCheckable )
maxCheckable = checkBoxCount;
if ( maxCheckable.equals(1) && minCheckable.equals(1))
return("Select just one response please.");
return("Select " + minCheckable + " to " + maxCheckable + " responses");
}
/**
* performs simple verification that the number of selected checkboxes
* was within the limits. Returns an integer so we have the option of
* rather detailed error messages such as "Please uncheck 2 boxes.", but
* for the most part we're concerned whether or not this returns 0
* @return 0 if we are in the range , >0 if we have not selected
* enough checkboxes, <0 if too many are selected
*/
private int multipleSelectionCountStatus() {
int iSelectedCount;
// if we somehow got in a state where min/max are
// incompatible, play it safe and do NO error checking
if ( minCheckable>maxCheckable )
return(0);
iSelectedCount = answerField.getSelected().size();
if ( iSelectedCount < minCheckable ) {
return(minCheckable-iSelectedCount);
}
if (iSelectedCount > maxCheckable ) {
return(maxCheckable - iSelectedCount);
}
return(0);
}
/**
* returns the string used if an incorrect number of checkboxes
* are selected to prompt the user to check more or fewer
*/
public String getRangeCheckNotification() {
int iCheckBoxStatus;
String strNotification = "";
if ( dontKnow() || refused())
return(strNotification);
iCheckBoxStatus = multipleSelectionCountStatus();
if ( iCheckBoxStatus<0 ) {
strNotification = "Too many responses selected";
} else if ( iCheckBoxStatus>0 ) {
strNotification = "Not enough responses selected";
}
if ( otherSpecifyStyle && otherSpecifyTextField.isVisible() &&
( otherText==null || otherText.length()==0 ))
strNotification += " Specify Other blank";
return(strNotification);
}
/**
* if the user selected dontKnow or refused to answer a question
* don't bother counting the responses.
*/
public boolean rangeCheckOkay() {
boolean bOkay = true;
if ( dontKnow() || refused())
return (bOkay);
if (multipleSelectionCountStatus()!=0)
bOkay = false;
if ( otherSpecifyStyle && otherSpecifyTextField.isVisible()
&& ( otherText==null || otherText.length()==0 ))
bOkay = false;
return(bOkay);
}
/**
* responds to ActionEvents.
* The only object that is apt to send any events is the
* answerField. We will use this interaction to avoid any
* hard-coded references to MultipleSelectionAnswerFormat
* and maintain loose-binding.
*/
public void actionPerformed (ActionEvent event) {
Object src = event.getSource();
boolean on = (event.getID()==0) ? false: true;
if ( src==answerField ) {
otherSpecifyLabel.setVisible(on);
otherSpecifyTextField.setVisible(on);
if ( !on )
setNotification("");
}
}
public void setOtherText ( String otherText ) {
this.otherText = (otherText==null) ? "" : otherText;
this.otherText = this.otherText.trim();
}
public String getOtherText() {
return (( otherText==null) ? "" : otherText ) ;
}
public void setHorizontalLayout ( Boolean horizontalLayout ) {
this.horizontalLayout = horizontalLayout;
if ( answerField!=null )
answerField.setHorizontalLayout(horizontalLayout);
}
public Boolean getHorizontalLayout() {
return(horizontalLayout);
}
/**
* returns true if strAnswerToCount is among the answers
* that have been checked. This will be used an a
* list-of-alters format question to count how many times
* a specific answer has been selected
* @param strAnswerToCount the answer we'll count up the number
* of times its been used
* @return true if the answer if selected in this panel
*/
public boolean isSelected ( String strAnswerToCount ) {
String selectedAnswer;
strAnswerToCount = strAnswerToCount.trim();
for(Object option : answerField.getSelected()) {
if(option instanceof QuestionOption) {
selectedAnswer = ((QuestionOption) option).getName().trim();
if ( selectedAnswer.equalsIgnoreCase(strAnswerToCount))
return (true);
}
}
return (false);
}
public boolean forceSelectionIfNone(String selection, int iMaxSelection) {
boolean anyChange = answerField.forceSelectionIfNone(selection, iMaxSelection);
return(anyChange);
}
}