diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..c465cdd --- /dev/null +++ b/.classpath @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..5e7b252 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + Enet + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Egoauthor.jsmooth b/Egoauthor.jsmooth new file mode 100644 index 0000000..039db57 --- /dev/null +++ b/Egoauthor.jsmooth @@ -0,0 +1,36 @@ + + +registry +javahome +jrepath +jdkpath +exepath +jview + +true +EgoAuthor.exe +-1 +EgoClient.jar +com.endlessloopsoftware.ego.author.EgoNet +Egonet-lib.jar +-1 + + +Windowed Wrapper + +Message +Java has not been found on your computer. Do you want to download it? + + +URL +http://www.java.com + + +SingleProcess +0 + + +Debug +0 + + \ No newline at end of file diff --git a/Egonet.jsmooth b/Egonet.jsmooth new file mode 100644 index 0000000..09a9089 --- /dev/null +++ b/Egonet.jsmooth @@ -0,0 +1,36 @@ + + +registry +javahome +jrepath +jdkpath +exepath +jview + +true +EgoClient.exe +-1 +EgoClient.jar +com.endlessloopsoftware.ego.client.EgoClient +Egonet-lib.jar +-1 + + +Windowed Wrapper + +Message +Java has not been found on your computer. Do you want to download it? + + +URL +http://www.java.com + + +SingleProcess +0 + + +Debug +0 + + \ No newline at end of file diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..6b0e627 --- /dev/null +++ b/build.xml @@ -0,0 +1,135 @@ + + + + + + Egonet + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/EXML.jar b/lib/EXML.jar new file mode 100644 index 0000000..6adc20e Binary files /dev/null and b/lib/EXML.jar differ diff --git a/lib/FoamRuntime.jar b/lib/FoamRuntime.jar new file mode 100644 index 0000000..64b7250 Binary files /dev/null and b/lib/FoamRuntime.jar differ diff --git a/lib/commons-collections-3.2.jar b/lib/commons-collections-3.2.jar new file mode 100644 index 0000000..75580be Binary files /dev/null and b/lib/commons-collections-3.2.jar differ diff --git a/lib/elsutils.jar b/lib/elsutils.jar new file mode 100644 index 0000000..bcd1b79 Binary files /dev/null and b/lib/elsutils.jar differ diff --git a/lib/forms-1.1.0.jar b/lib/forms-1.1.0.jar new file mode 100644 index 0000000..50c1eb8 Binary files /dev/null and b/lib/forms-1.1.0.jar differ diff --git a/lib/jsmooth/console-wrapper/consolewrapper.exe b/lib/jsmooth/console-wrapper/consolewrapper.exe new file mode 100644 index 0000000..0cba040 Binary files /dev/null and b/lib/jsmooth/console-wrapper/consolewrapper.exe differ diff --git a/lib/jsmooth/console-wrapper/description.skel b/lib/jsmooth/console-wrapper/description.skel new file mode 100644 index 0000000..7c18bf7 --- /dev/null +++ b/lib/jsmooth/console-wrapper/description.skel @@ -0,0 +1,40 @@ + + +false +console applications. + +Although it is designed for console application (i.e. launched from the command.com shell prompt), it can launch standard GUI application. In such a case, any output of the java application (from System.out or System.err) is displayed in the a DOS Console. +]]> + +consolewrapper.exe +JAVA +102 +103 +Console Wrapper + +When no JVM is found in the target computer, the following message is displayed on the console. +Message + +textarea +This program needs Java to run. +Please download it at http://www.java.com + + +The wrapper waits a keypress on the console when the application exits. +PressKey + +boolean +0 + + +Enable the jsmooth debug traces +Debug + +boolean +0 + + diff --git a/lib/jsmooth/dtdparser113.jar b/lib/jsmooth/dtdparser113.jar new file mode 100644 index 0000000..4172640 Binary files /dev/null and b/lib/jsmooth/dtdparser113.jar differ diff --git a/lib/jsmooth/jox116.jar b/lib/jsmooth/jox116.jar new file mode 100644 index 0000000..e470b8d Binary files /dev/null and b/lib/jsmooth/jox116.jar differ diff --git a/lib/jsmooth/jsmoothgen-ant.jar b/lib/jsmooth/jsmoothgen-ant.jar new file mode 100644 index 0000000..4d27a0f Binary files /dev/null and b/lib/jsmooth/jsmoothgen-ant.jar differ diff --git a/lib/jsmooth/jsmoothgen.jar b/lib/jsmooth/jsmoothgen.jar new file mode 100644 index 0000000..8c74a25 Binary files /dev/null and b/lib/jsmooth/jsmoothgen.jar differ diff --git a/lib/jsmooth/l2fprod-common.jar b/lib/jsmooth/l2fprod-common.jar new file mode 100644 index 0000000..0b62505 Binary files /dev/null and b/lib/jsmooth/l2fprod-common.jar differ diff --git a/lib/jsmooth/windowed-wrapper/description.skel b/lib/jsmooth/windowed-wrapper/description.skel new file mode 100644 index 0000000..ece98a8 --- /dev/null +++ b/lib/jsmooth/windowed-wrapper/description.skel @@ -0,0 +1,44 @@ + + +false +GUI applications. +Arguments can be passed to the application (either use the JSmooth default argument mechanism, or create a shortcut with arguments).]]> + +jwrap.exe +JAVA +102 +103 +Windowed Wrapper + +When no JVM is found in the target computer, the user is prompted with the message defined below. Then, the default browser is launched with the URL defined here. +Message + +textarea +Java has not been found on your computer. Do you want to download it? + + +If the user selects YES to the message prompted above, the default web browser is launched with this URL. +URL + +string +http://www.java.com + + +The default behaviour is to launch the java application in a different (detached) process. If you want to force the wrapper to launch the Java application in the same process than the exe, than select this option. +SingleProcess + +boolean +0 + + +Enable the jsmooth debug console. +Debug + +boolean +0 + + diff --git a/lib/jsmooth/windowed-wrapper/jwrap.exe b/lib/jsmooth/windowed-wrapper/jwrap.exe new file mode 100644 index 0000000..844087f Binary files /dev/null and b/lib/jsmooth/windowed-wrapper/jwrap.exe differ diff --git a/lib/jung-1.7.6.jar b/lib/jung-1.7.6.jar new file mode 100644 index 0000000..735b5d5 Binary files /dev/null and b/lib/jung-1.7.6.jar differ diff --git a/lib/looks-2.1.4.jar b/lib/looks-2.1.4.jar new file mode 100644 index 0000000..d2c47c7 Binary files /dev/null and b/lib/looks-2.1.4.jar differ diff --git a/src/com/endlessloopsoftware/ego/Answer.java b/src/com/endlessloopsoftware/ego/Answer.java new file mode 100644 index 0000000..0eb2e9e --- /dev/null +++ b/src/com/endlessloopsoftware/ego/Answer.java @@ -0,0 +1,193 @@ +package com.endlessloopsoftware.ego; + +/** + *

Title: Egocentric Networks Client Program

+ *

Description: Subject Interview Client

+ *

Copyright: Copyright (c) 2002 - 2004

+ *

Company: Endless Loop Software

+ * @author Peter Schoaff + * + * $Id: Answer.java,v 1.1 2005/08/02 19:36:02 samag Exp $ + */ +import com.endlessloopsoftware.ego.client.EgoClient; +import com.endlessloopsoftware.egonet.util.AnswerDataValue; + +import electric.xml.Element; +import electric.xml.Elements; +import java.util.Date; +import java.text.*; + +public class Answer implements Cloneable { + /** + * Unique ID for every question + */ + public Long questionId; + + private int[] alters; + + public boolean answered; + + public boolean adjacent; + + public int value; + + public int index; + + public String string; + + public String timestamp; + + public static final int NO_ANSWER = -1; + + public static final int ALL_ADJACENT = -2; + + private Answer() { + } + + public Answer(Long Id) { + this(Id, null); + } + + public Answer(Long Id, int[] alters) { + questionId = Id; + answered = false; + adjacent = false; + value = -1; + string = ""; + timestamp = DateFormat.getDateInstance().format(new Date()); + + if (alters == null) { + this.alters = new int[0]; + } else { + this.alters = alters; + } + } + + public Answer(AnswerDataValue data) { + questionId = data.getQuestionId(); + answered = data.getAnswered(); + adjacent = data.getAnswerAdjacent(); + value = data.getAnswerValue(); + string = data.getAnswerString(); + alters = data.getAlters().toArray(); + } + + public Object clone() throws CloneNotSupportedException { + return (super.clone()); + } + + /*************************************************************************** + * Add answer information to an xml structure for output to a file + * + * @param e + * XML Element, parent of answer tree + */ + public void writeAnswer(Element e) throws Exception { + Element answerElement = new Element("Answer"); + Element altersElement; + + try { + answerElement.addElement("QuestionId").setLong( + questionId.longValue()); + answerElement.addElement("Answered").setBoolean(answered); + + if (answered) { + answerElement.addElement("Value").setInt(value); + answerElement.addElement("Index").setInt(index); + answerElement.addElement("Adjacent").setBoolean(adjacent); + answerElement.addElement("String").setText(string); + answerElement.addElement("TimeStamp").setText(timestamp); + } + + if (alters.length > 0) { + altersElement = answerElement.addElement("Alters"); + for (int i = 0; i < alters.length; i++) { + altersElement.addElement("Index").setInt(alters[i]); + } + } + + e.addElement(answerElement); + } catch (Exception ex) { + System.err.println("Failure in Answer::writeAnswer; " + ex); + ex.printStackTrace(); + throw ex; + } + } + + /*************************************************************************** + * Read alter list from an xml tree + * + * @param interview + * Interview to read answers into + * @param e + * XML Element, parent of alter list + */ + public static Answer readAnswer(Element e) { + Answer r = null; + Elements alterElems = null; + int qAlters[] = null; + Long qId = new Long(e.getLong("QuestionId")); + Question q = (Question) EgoClient.study.getQuestions().getQuestion(qId); + Element alterElem = e.getElement("Alters"); + + if (alterElem != null) { + alterElems = alterElem.getElements("Index"); + qAlters = new int[alterElems.size()]; + + for (int i = 0; i < alterElems.size(); i++) { + qAlters[i] = alterElems.next().getInt(); + } + } + + r = new Answer(qId, qAlters); + + r.answered = e.getBoolean("Answered"); + + if (r.answered) { + r.string = e.getString("String"); + r.value = e.getInt("Value"); + r.index = e.getInt("Index"); + r.adjacent = q.selectionAdjacent(r.value); + } else { + r.string = null; + } + + return r; + } + + public int[] getAlters() { + return alters; + } + + public void setAlters(int[] alters) { + this.alters = alters; + } + + /* + * public AnswerDataValue getDataValue() { AnswerDataValue adv = new + * AnswerDataValue(); + * + * adv.setAnswered(this.answered); adv.setAnswerString(this.string); + * adv.setAnswerValue(this.value); adv.setAnswerAdjacent(this.adjacent); + * + * return adv; } + */ + public String toString() { + String str = null; + if (string == null) { + Integer val = value; + str = val.toString(); + ; + } else { + str = string; + } + return str; + } + + public String getString() { + String str = ""; + str = "Answered: " + answered + " string: " + string + " value : " + value; + return str; + + } +} \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/Question.java b/src/com/endlessloopsoftware/ego/Question.java new file mode 100644 index 0000000..c7bcb4c --- /dev/null +++ b/src/com/endlessloopsoftware/ego/Question.java @@ -0,0 +1,582 @@ +package com.endlessloopsoftware.ego; + +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: Question.java,v 1.1 2005/08/02 19:36:02 samag Exp $ + * + */ + +import java.util.Date; + +import com.endlessloopsoftware.egonet.data.QuestionLinkDataValue; +import com.endlessloopsoftware.egonet.interfaces.QuestionEJBPK; +import com.endlessloopsoftware.egonet.util.QuestionDataValue; +import com.endlessloopsoftware.egonet.util.SelectionDataValue; + +import org.egonet.exceptions.MalformedQuestionException; +import org.egonet.util.listbuilder.Selection; + +import electric.xml.Element; +import electric.xml.Elements; + +/******************************************************************************* + * Routines for creating and handling atomic question elements + */ +public class Question implements Cloneable { + public boolean centralMarker = false; + + public boolean statable = false; + + public Long UniqueId = new Long(new Date().getTime()); + + public int questionType = Question.EGO_QUESTION; + + public String title = ""; + + public String text = ""; + + public String citation = ""; + + public int answerType = Question.TEXT; + + public int numQAlters = -1; + + public QuestionLink link = new QuestionLink(); + + /* + * public class selection extends Selection { public boolean modified=false; } + */ + + public Selection[] selections = new Selection[0]; + + public Answer answer = new Answer(new Long(-1)); + + /* Constants */ + public static final int MIN_QUESTION_TYPE = 1; + + public static final int STUDY_CONFIG = 0; + + public static final int EGO_QUESTION = 1; + + public static final int ALTER_PROMPT = 2; + + public static final int ALTER_QUESTION = 3; + + public static final int ALTER_PAIR_QUESTION = 4; + + public static final int NUM_QUESTION_TYPES = 5; + + public static final int ALL_QUESTION_TYPES = 5; + + public static final int MAX_QUESTION_TYPE = 4; + + public static final int MIN_ANSWER_TYPE = 0; + + public static final int CATEGORICAL = 0; + + public static final int NUMERICAL = 1; + + public static final int TEXT = 2; + + public static final int MAX_ANSWER_TYPE = 2; + + public static final int MAX_CATEGORICAL_CHOICES = 5; + + public static final String[] questionName = { "Study", "Ego", + "Alter Prompt", "Alter", "Alter Pair" }; + + /*************************************************************************** + * Creates question + * + * @return question new question + */ + public Question() { + } + + /*************************************************************************** + * Creates question with string as title + * + * @param s + * question title + * @return question new question + */ + public Question(String s) { + this.title = s; + } + + /*************************************************************************** + * Converts a QuestionDataValue to a question object + * + * @param question + * XML element of question + * @param base + * whether this is from the base file + * @throws MalformedQuestionException + * if theres is a problem with the XML representation + */ + public Question(QuestionDataValue data) { + this.UniqueId = data.getId(); + this.questionType = data.getQuestionType(); + this.answerType = data.getAnswerType(); + this.title = data.getTitle(); + this.text = data.getText(); + this.citation = data.getCitation(); + + SelectionDataValue[] selectionData = data.getSelectionDataValues(); + + /* Fix to properly display Apple UI interviews */ + if (this.questionType == Question.ALTER_PROMPT) { + this.answerType = Question.TEXT; + } + + /* + * temp vars for determining statable, a question must have at least one + * of each selection type to be statable + */ + boolean adjacent = false; + boolean nonadjacent = false; + + this.selections = new Selection[selectionData.length]; + for (int i = 0; i < selectionData.length; ++i) { + + Selection selection = new Selection(); + selection.setString(selectionData[i].getText()); + selection.setIndex(selectionData[i].getIndex()); + selection.setValue(selectionData[i].getValue()); + selection.setAdjacent(selectionData[i].getAdjacent()); + + /* + * selection selectionobj = new selection(); selectionobj.string = + * selectionData[i].getText(); selectionobj.index = + * selectionData[i].getIndex(); selectionobj.value = + * selectionData[i].getValue(); selectionobj.adjacent = + * selectionData[i].getAdjacent(); + */ + if (selection.isAdjacent()) + adjacent = true; + else + nonadjacent = true; + + selections[i] = selection; + } + + /* + * a question must have at least one of each selection type to be + * statable + */ + this.statable = adjacent && nonadjacent; + + if (data.getQuestionLinkDataValue() != null) { + QuestionLinkDataValue qlData = data.getQuestionLinkDataValue(); + + this.link.active = true; + this.link.answer = new Answer(qlData.getQuestionId()); + this.link.answer.value = qlData.getAnswerValue(); + this.link.answer.string = qlData.getAnswerString(); + } else { + this.link.active = false; + } + } + + /*************************************************************************** + * Reads a single question from an input stream + * + * @param question + * XML element of question + * @param base + * whether this is from the base file + * @throws MalformedQuestionException + * if theres is a problem with the XML representation + */ + public Question(Element question) throws MalformedQuestionException { + if ((question.getElement("QuestionTitle") == null) + || (question.getElement("QuestionText") == null) + || (question.getElement("Id") == null) + || (question.getElement("QuestionType") == null) + || (question.getElement("AnswerType") == null)) { + throw (new MalformedQuestionException()); + } + + this.title = question.getTextString("QuestionTitle"); + this.title = (this.title == null) ? "" : this.title; + + this.text = question.getTextString("QuestionText"); + this.text = (this.text == null) ? "" : this.text; + + this.citation = question.getTextString("Citation"); + this.citation = (this.citation == null) ? "" : this.citation; + + this.UniqueId = new Long(question.getLong("Id")); + this.questionType = question.getInt("QuestionType"); + this.answerType = question.getInt("AnswerType"); + + if (this.questionType == Question.ALTER_PROMPT) { + this.answerType = Question.TEXT; + } + + if (question.getAttribute("CentralityMarker") != null) { + boolean centrality = question.getAttribute("CentralityMarker") + .equals("true"); + + if (centrality + && (this.questionType != Question.ALTER_PAIR_QUESTION)) { + throw (new MalformedQuestionException()); + } + } + + Element link = question.getElement("Link"); + if (link != null) { + this.link.active = true; + this.link.answer = new Answer(new Long(link.getLong("Id"))); + this.link.answer.value = link.getInt("value"); + + /* Only support questions with single answers for link */ + this.link.answer.string = link.getTextString("string"); + } + + if (this.answerType == Question.CATEGORICAL) { + Element answerList = question.getElement("Answers"); + + if (answerList != null) { + Elements selections = answerList.getElements("AnswerText"); + + if (selections.size() == 0) { + throw (new MalformedQuestionException()); + } + + /* + * temp vars for determining statable, a question must have at + * least one of each selection type to be statable + */ + boolean adjacent = false; + boolean nonadjacent = false; + + this.selections = new Selection[selections.size()]; + + while (selections.hasMoreElements()) { + + Element selection = selections.next(); + int index = Integer.parseInt(selection + .getAttributeValue("index")); + + try { + this.selections[index] = new Selection(); + this.selections[index].setString(selection + .getTextString()); + this.selections[index] + .setValue(Integer.parseInt(selection + .getAttributeValue("value"))); + + this.selections[index].setAdjacent(Boolean.valueOf( + selection.getAttributeValue("adjacent")) + .booleanValue()); + this.selections[index].setIndex(index); + + } catch (NumberFormatException ex) { + this.selections[index].setValue(selections.size() + - (index + 1)); + this.selections[index].setAdjacent(false); + } + + if (this.selections[index].isAdjacent()) + adjacent = true; + else + nonadjacent = true; + } + + /* + * a question must have at least one of each selection type to + * be statable + */ + this.statable = adjacent && nonadjacent; + + /* Check to make sure all answers are contiguous */ + for (int i = 0; i < selections.size(); i++) { + if (this.selections[i] == null) { + throw (new MalformedQuestionException()); + } + } + } + } + } + + /*************************************************************************** + * Returns whether a given selection is adjacent based on the values stored + * in the question. Is used to override value found in an interview file + * + * @param value + * @return true iff that selection is marked as adjacent + */ + public boolean selectionAdjacent(int value) { + boolean rval = false; + + if (this.selections.length > 0) { + int size = this.selections.length; + + for (int i = 0; i < size; i++) { + if (value == this.selections[i].getValue()) { + rval = this.selections[i].isAdjacent(); + break; + } + } + } + + return rval; + } + + /*************************************************************************** + * Returns String representation of questionType + * + * @param type + * Question type to return as string + * @return string Question Type string + */ + public static String questionTypeString(int type) { + return (new String(questionName[type])); + } + + /*************************************************************************** + * Overrides toString method for question, returns title + * + * @return String title of question + */ + public String toString() { + if (title == null) { + return (new String("Untitled")); + } else { + return (title); + } + } + + /*************************************************************************** + * Writes a single question to an output stream + * + * @param w + * Print Writer of open output file + * @param q + * question + */ + public void writeQuestion(Element e, QuestionList list) { + if (this.centralMarker) { + e.setAttribute("CentralityMarker", "true"); + } + + e.addElement("Id").setLong(this.UniqueId.longValue()); + e.addElement("QuestionType").setInt(this.questionType); + e.addElement("AnswerType").setInt(this.answerType); + + if ((this.title != null) && (!this.title.equals(""))) { + e.addElement("QuestionTitle").setText(this.title); + } + + if ((this.text != null) && (!this.text.equals(""))) { + e.addElement("QuestionText").setText(this.text); + } + + if ((this.citation != null) && (!this.citation.equals(""))) { + e.addElement("Citation").setText(this.citation); + } + + if (this.selections.length > 0) { + int size = this.selections.length; + Element selections = e.addElement("Answers"); + + for (int i = 0; i < size; i++) { + Element answer = selections.addElement("AnswerText"); + answer.setText(this.selections[i].getString()); + answer.setAttribute("index", Integer.toString(i)); + answer.setAttribute("value", Integer + .toString(this.selections[i].getValue())); + answer.setAttribute("adjacent", + this.selections[i].isAdjacent() ? "true" : "false"); + } + } + + if (this.link.active + && (list.getQuestion(this.link.answer.questionId) != null)) { + try { + Element link = e.addElement("Link"); + link.addElement("Id").setLong( + this.link.answer.questionId.longValue()); + link.addElement("value").setInt(this.link.answer.value); + link.addElement("string").setText(this.link.answer.string); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + + public QuestionDataValue getDataValue(QuestionList list, Long studyId) { + QuestionEJBPK pk = new QuestionEJBPK(this.UniqueId, studyId); + QuestionDataValue data = new QuestionDataValue(pk); + + data.setId(this.UniqueId); + data.setQuestionType(this.questionType); + data.setAnswerType(this.answerType); + data.setTitle(this.title); + data.setText(this.text); + data.setCitation(this.citation); + + if (this.selections.length > 0) { + int size = this.selections.length; + + for (int i = 0; i < size; i++) { + SelectionDataValue selectionData = new SelectionDataValue(); + selectionData.setText(this.selections[i].getString()); + selectionData.setIndex(i); + selectionData.setValue(this.selections[i].getValue()); + selectionData.setAdjacent(this.selections[i].isAdjacent()); + + data.addSelectionDataValue(selectionData); + } + } + + if (this.link.active + && (list.getQuestion(this.link.answer.questionId) != null)) { + try { + QuestionLinkDataValue qlData = new QuestionLinkDataValue(); + qlData.setActive(true); + qlData.setAnswerValue(this.link.answer.value); + qlData.setAnswerString(this.link.answer.string); + qlData.setQuestionId(this.link.answer.questionId); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + return data; + } + + /*************************************************************************** + * Implements Clone interface + * + * @return Clone of Question + */ + public Object clone() { + Question q; + + try { + q = (Question) super.clone(); + q.link = (QuestionLink) this.link.clone(); + + /******************************************************************* + * Dangerous to clone answers as multiple answers refer to same + * question Make sure they are assigned explicitly + */ + q.answer = null; + } catch (CloneNotSupportedException ex) { + q = null; + } + + return q; + } + + public String getString() { + String str = ""; + str = "ID : " + UniqueId + " Title : " + title + " text : " + text + + "\nAnswer : " + answer.getString(); + return str; + } +} + +/** + * $Log: Question.java,v $ Revision 1.1 2005/08/02 19:36:02 samag Initial + * checkin + * + * Revision 1.18 2004/04/11 00:24:48 admin Fixing headers + * + * Revision 1.17 2004/04/11 00:17:13 admin Improving display of Alter Prompt + * questions from Applet UI Interviews + * + * Revision 1.16 2004/04/06 20:29:22 admin First pass as supporting interactive + * applet linking interviews + * + * Revision 1.15 2004/04/06 14:56:02 admin Work to integrate with Applet Linking + * UI + * + * Revision 1.14 2004/04/01 15:11:16 admin Completing Original UI work + * + * Revision 1.13 2004/03/29 16:13:38 admin Fixed bug calculating statable + * questions + * + * Revision 1.12 2004/03/29 00:35:09 admin Downloading Interviews Fixing some + * bugs creating Interviews from Data Objects + * + * Revision 1.11 2004/03/28 17:31:31 admin More error handling when uploading + * study to server Server URL selection dialog for upload + * + * Revision 1.10 2004/03/22 00:00:34 admin Extended text entry area Started work + * on importing studies from server + * + * Revision 1.9 2004/03/21 14:00:38 admin Cleaned up Question Panel Layout using + * FOAM + * + * Revision 1.8 2004/03/10 14:32:39 admin Adding client library cleaning up code + * + * Revision 1.7 2004/02/10 20:10:42 admin Version 2.0 beta 3 + * + * Revision 1.6 2004/01/23 13:36:07 admin Updating Libraries Allowing upload to + * web server + * + * Revision 1.5 2003/12/18 19:30:05 admin Small mods to support EJB persistence + * + * Revision 1.4 2003/12/09 16:17:00 admin Fixing bug reading in adjacency + * selections Clearing identity diagonal of Weighted Adjacency Matrix + * + * Revision 1.3 2003/12/08 15:57:50 admin Modified to generate matrix files on + * survey completion or summarization Extracted statistics models + * + * Revision 1.2 2003/12/05 19:15:43 admin Extracting Study + * + * Revision 1.1 2003/12/04 15:14:08 admin Merging EgoNet and EgoClient projects + * so that they can share some common classes more easily. + * + * Revision 1.2 2003/11/25 19:25:43 admin Warn before closing window + * + * Revision 1.1.1.1 2003/06/08 15:09:40 admin Egocentric Network Survey + * Authoring Module + * + * Revision 1.16 2002/08/30 16:50:27 admin Using Selections + * + * Revision 1.15 2002/08/30 09:35:38 admin Using Selection Class + * + * Revision 1.14 2002/08/11 22:26:05 admin Final Statistics window, new file + * handling + * + * Revision 1.13 2002/08/08 17:07:25 admin Preparing to change file system + * + * Revision 1.12 2002/07/25 14:54:24 admin Question Links + * + * Revision 1.11 2002/07/24 14:17:51 admin new files + * + * Revision 1.9 2002/07/18 14:43:06 admin New Alter Prompt Panel, packages + * + * Revision 1.8 2002/06/30 15:59:18 admin Moving questions in lists, between + * lists Better category input + * + * Revision 1.7 2002/06/26 15:43:43 admin More selection dialog work File + * loading fixes + * + * Revision 1.6 2002/06/26 00:10:48 admin UI Work including base question + * coloring and category selections + * + * Revision 1.5 2002/06/25 15:41:02 admin Lots of UI work + * + * Revision 1.4 2002/06/21 21:52:50 admin Many changes to event handling, file + * handling + * + * Revision 1.3 2002/06/19 01:57:04 admin Much UI work done + * + * Revision 1.2 2002/06/16 17:53:10 admin Working with files + * + * Revision 1.1 2002/06/15 14:19:51 admin Initial Checkin of question and survey + * General file system work + * + */ + diff --git a/src/com/endlessloopsoftware/ego/QuestionLink.java b/src/com/endlessloopsoftware/ego/QuestionLink.java new file mode 100644 index 0000000..13e0135 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/QuestionLink.java @@ -0,0 +1,33 @@ +package com.endlessloopsoftware.ego; + + +/** + *

Title: Egocentric Networks Client Program

+ *

Description: Subject Interview Client

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter Schoaff + * @version 1.0 + */ + +public class QuestionLink + implements Cloneable +{ + public boolean active = false; + public Answer answer = null; + + public Object clone() + throws CloneNotSupportedException + { + QuestionLink q; + + q = (QuestionLink) super.clone(); + + if (active) + { + q.answer = (Answer) this.answer.clone(); + } + + return(q); + } +} \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/QuestionList.java b/src/com/endlessloopsoftware/ego/QuestionList.java new file mode 100644 index 0000000..b69b51e --- /dev/null +++ b/src/com/endlessloopsoftware/ego/QuestionList.java @@ -0,0 +1,199 @@ +package com.endlessloopsoftware.ego; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Observable; +import java.util.Observer; +import java.util.Set; + + +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2002 - 2004

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * + * $Id: QuestionList.java,v 1.1 2005/08/02 19:36:02 samag Exp $ + */ + +public class QuestionList extends Observable + implements Observer +{ + private final Map questionMap = new HashMap(); + + public QuestionList() + { + } + + /**** + * Notifies observers that a field in the study has changed + */ + public void notifyObservers() + { + setChanged(); + super.notifyObservers(this); + } + + /**** + * Returns Map containing all questions + * @return questionList Map of questions + */ + public Map getQuestionMap() + { + return(questionMap); + } + + /**** + * Deletes all questions from map + */ + public void removeAll() + { + questionMap.clear(); + } + + /**** + * Removes a single question from the map + * @param q question to add + */ + public void remove(Question q) + { + questionMap.remove(q.UniqueId); + } + + /**** + * Adds question to the map + * @param q question to add + */ + public void addQuestion(Question q) + { + questionMap.put(q.UniqueId, q); + } + + /**** + * Returns question from map identified by its UniqueId + * @param l UniqueId of question + * @return q question in list + */ + public Question getQuestion(Long l) + { + return((Question) questionMap.get((Object) l)); + } + + /**** + * Returns a Collection of all the values in the map + * usually used to iterate over questions + * @return collection collection of questions + */ + public Collection values() + { + return (questionMap.values()); + } + + /**** + * Determines if question is already in map + * @param q question to seek + * @return bool true iff question in map + */ + public boolean contains(Question q) + { + return(questionMap.containsValue(q)); + } + + /**** + * Determines if question with a given UniqueId is already in map + * @param l UniqueId of question to seek + * @return bool true iff question in map + */ + public boolean contains(Long l) + { + return(questionMap.containsKey(l)); + } + + /**** + * Function called when observables updated + * @param o Observable + * @param arg param from Observable + */ + public void update(Observable o, Object arg) + { + /**@todo Implement this java.util.Observer method*/ + throw new java.lang.UnsupportedOperationException("Method update() not yet implemented."); + } + + /* (non-Javadoc) + * @see java.util.List#size() + */ + public int size() + { + return questionMap.size(); + } + + public String dump() + { + StringBuffer buffer = new StringBuffer(); + + Set keys = questionMap.keySet(); + for (Iterator it = keys.iterator(); it.hasNext();) + { + Object key = it.next(); + buffer.append(key + " : " + questionMap.get(key) + "\n"); + } + + return buffer.toString(); + } +} + +/** + * $Log: QuestionList.java,v $ + * Revision 1.1 2005/08/02 19:36:02 samag + * Initial checkin + * + * Revision 1.6 2004/04/11 00:24:48 admin + * Fixing headers + * + * Revision 1.5 2004/03/29 00:35:09 admin + * Downloading Interviews + * Fixing some bugs creating Interviews from Data Objects + * + * Revision 1.4 2004/03/21 14:00:38 admin + * Cleaned up Question Panel Layout using FOAM + * + * Revision 1.3 2003/12/18 19:30:05 admin + * Small mods to support EJB persistence + * + * Revision 1.2 2003/12/05 19:15:43 admin + * Extracting Study + * + * Revision 1.1 2003/12/04 15:14:08 admin + * Merging EgoNet and EgoClient projects so that they can share some + * common classes more easily. + * + * Revision 1.2 2003/11/25 19:25:43 admin + * Warn before closing window + * + * Revision 1.1.1.1 2003/06/08 15:09:40 admin + * Egocentric Network Survey Authoring Module + * + * Revision 1.6 2002/08/08 17:07:25 admin + * Preparing to change file system + * + * Revision 1.5 2002/07/24 14:17:09 admin + * xml files, links + * + * Revision 1.4 2002/06/30 15:59:17 admin + * Moving questions in lists, between lists + * Better category input + * + * Revision 1.3 2002/06/25 15:41:01 admin + * Lots of UI work + * + * Revision 1.2 2002/06/21 22:47:12 admin + * question lists working again + * + * Revision 1.1 2002/06/21 21:53:29 admin + * new files + * + */ diff --git a/src/com/endlessloopsoftware/ego/Shared.java b/src/com/endlessloopsoftware/ego/Shared.java new file mode 100644 index 0000000..80fe9da --- /dev/null +++ b/src/com/endlessloopsoftware/ego/Shared.java @@ -0,0 +1,91 @@ +/* + * Created on Jan 23, 2004 + * + * To change the template for this generated file go to + * Window - Preferences - Java - Code Generation - Code and Comments + */ +package com.endlessloopsoftware.ego; + +import java.awt.Cursor; +//import java.awt.Dimension; + +import javax.swing.JFrame; +//import javax.swing.UIManager; + +/* + * import com.jgoodies.plaf.FontSizeHints; + * import com.jgoodies.plaf.LookUtils; + * import com.jgoodies.plaf.Options; + */ + +/** + * @author admin + * + * To change the template for this generated type comment go to + * Window - Preferences - Java - Code Generation - Code and Comments + */ +public final class Shared +{ + public static final String version = "2.0 Beta 9 (7 Apr 2004)"; + + /** + * Configures the UI; tries to set the system look on Mac, + * ExtWindowsLookAndFeel on general Windows, and + * Plastic3DLookAndFeel on Windows XP and all other OS.

+ * + * The JGoodies Swing Suite's ApplicationStarter, + * ExtUIManager, and LookChoiceStrategies + * classes provide a much more fine grained algorithm to choose and + * restore a look and theme. + */ + /* Added by sonam : 08/20.2007 */ + + public static final String USE_SYSTEM_FONTS_APP_KEY = + "Application.useSystemFontSettings"; + + /* end of code added by sonam */ + public static void configureUI() + { + + /* + * Commented by sonam : 08/20/2007 + * UIManager.put(Options.USE_SYSTEM_FONTS_APP_KEY, Boolean.TRUE); + * Options.setGlobalFontSizeHints(FontSizeHints.MIXED); + * Options.setDefaultIconSize(new Dimension(18, 18)); + * String lafName = LookUtils.IS_OS_WINDOWS_XP ? + Options.getCrossPlatformLookAndFeelClassName() : + Options.getSystemLookAndFeelClassName(); + */ + + /* + UIManager.put(USE_SYSTEM_FONTS_APP_KEY, Boolean.TRUE); + String lafName = (System.getProperty("os.name").toLowerCase()=="windows xp") ? + UIManager.getCrossPlatformLookAndFeelClassName() : + UIManager.getSystemLookAndFeelClassName(); + try + { + UIManager.setLookAndFeel(lafName); + } + catch (Exception e) + { + System.err.println("Can't set look & feel:" + e); + } + */ + + } + + public static void setWaitCursor(JFrame frame, boolean waitCursor) + { + if (waitCursor) + { + frame.getGlassPane().setVisible(true); + frame.getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } + else + { + frame.getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + frame.getGlassPane().setVisible(false); + } + } + +} diff --git a/src/com/endlessloopsoftware/ego/Study.java b/src/com/endlessloopsoftware/ego/Study.java new file mode 100644 index 0000000..9be8f4c --- /dev/null +++ b/src/com/endlessloopsoftware/ego/Study.java @@ -0,0 +1,1180 @@ +package com.endlessloopsoftware.ego; + +/** + *

Title: Egocentric Networks Client Program

+ *

Description: Subject Interview Client

+ *

Copyright: Copyright (c) 2002 - 2004

+ *

Company: Endless Loop Software

+ * @author Peter Schoaff + * + * $Id: Study.java,v 1.1 2005/08/02 19:36:02 samag Exp $ + */ + +import java.io.IOException; +import java.util.*; + +import javax.ejb.CreateException; +import javax.swing.DefaultListModel; +import javax.swing.JFrame; +import javax.swing.JOptionPane; + +import org.egonet.exceptions.DuplicateQuestionException; +import org.egonet.exceptions.MalformedQuestionException; + +import com.endlessloopsoftware.egonet.Shared; +import com.endlessloopsoftware.egonet.interfaces.StudySBRemote; +import com.endlessloopsoftware.egonet.interfaces.StudySBRemoteHome; +import com.endlessloopsoftware.egonet.interfaces.StudySBUtil; +import com.endlessloopsoftware.egonet.util.QuestionDataValue; +import com.endlessloopsoftware.egonet.util.StudyDataValue; +import electric.xml.Document; +import electric.xml.Element; +import electric.xml.Elements; + +/******************************************************************************* + * Stores basic configuration data for the study including question order lists + */ +public class Study extends Observable +{ + private long _uniqueId = -1L; + private String _uiType = Shared.TRADITIONAL_QUESTIONS; + private int _numAlters = -1; + private boolean _studyDirty = false; + private boolean _compatible = true; + private boolean _inUse = false; + private String _studyName = "New Study"; + private List[] _questionOrder = new List[Question.NUM_QUESTION_TYPES]; + private Question _firstQuestion = new Question("none"); + private QuestionList _questions = new QuestionList(); + private int _totalQuestions = -1; + private static Properties _prop = new Properties(); + + /** + * Instantiates Default Study + */ + public Study() + { + _studyName = "New Study"; + _uiType = Shared.TRADITIONAL_QUESTIONS; + _numAlters = 40; + _studyDirty = false; + _compatible = true; + _uniqueId = -1; + + // @TODO Move storage to parent package + // EgoNet.storage.setStudyFile(null); + this.getQuestions().removeAll(); + + for (int i = 0; i < _questionOrder.length; i++) + { + _questionOrder[i] = new ArrayList(); + } + } + + /** + * Instantiates Study based on a StudyDataValue downloaded from a survey server + */ + public Study(StudyDataValue data) + { + _uniqueId = data.getId().longValue(); + _uiType = data.getUIType(); + _numAlters = data.getNumAlters(); + _studyName = data.getStudyName(); + + Long[][] orders = data.getQuestionOrder(); + + for (int i = 0; i < Question.NUM_QUESTION_TYPES; ++i) + { + if (orders[i] != null) + { + _questionOrder[i] = Arrays.asList(orders[i]); + } + else + { + _questionOrder[i] = new ArrayList(); + } + } + + QuestionDataValue[] questionData = data.getQuestionDataValues(); + for (int i = 0; i < questionData.length; ++i) + { + Question question = new Question(questionData[i]); + + if ((question.questionType == Question.ALTER_PAIR_QUESTION) && isAppletUI()) + { + question.statable = true; + } + + _questions.addQuestion(question); + } + + verifyStudy(); + } + + /********** + * Instantiates study from an XML Document + * @param document + */ + public Study(Document document) + { + // Start with default study + this(); + + readPackageStudy(document); + readQuestions(document); + verifyStudy(); + } + + + /*************************************************************************** + * Returns UniqueId of study read from file + * + * @return long Unique Id of study + */ + public long getStudyId() + { + return (_uniqueId); + } + + /*************************************************************************** + * Notifies observers that a field in the study has changed + */ + public void notifyObservers() + { + setChanged(); + super.notifyObservers(this); + } + + /*************************************************************************** + * Returns name of study + * + * @return name name of study + */ + public String getStudyName() + { + return (_studyName); + } + + /*************************************************************************** + * Returns number of alters for which to prompt + * + * @return numAlters number of alters for which to prompt + */ + public int getNumAlters() + { + return (_numAlters); + } + + /*************************************************************************** + * Returns array of question order lists + * + * @return questionOrder array of lists + */ + public List[] getQuestionOrderArray() + { + return (_questionOrder); + } + + /** + * @return Returns the questions. + */ + public QuestionList getQuestions() + { + return _questions; + } + + /** + * @return Returns the questions. + */ + public Question getQuestion(Long id) + { + return getQuestions().getQuestion(id); + } + + + /** + * @return Returns the firstQuestion. + */ + public Question getFirstQuestion() + { + return _firstQuestion; + } + + /*************************************************************************** + * Returns array of questions for a specified category + * + * @param category + * category of questions to return + * @return questionOrder array of question Ids + * @throws NoSuchElementException + * for category out of range + */ + public List getQuestionOrder(int category) throws NoSuchElementException + { + if (category >= _questionOrder.length) + { + throw (new NoSuchElementException()); + } + + return (_questionOrder[category]); + } + + /*************************************************************************** + * Working forward from beginning, find first unanswered question + * + * @return index of first unanswered question + */ + public Question getFirstStatableQuestion() + { + Question statable = null; + Iterator questions; + + /** + * Try to find one all alters answer + */ + questions = getQuestionOrder(Question.ALTER_PAIR_QUESTION).iterator(); + while (questions.hasNext()) + { + Question q = (Question) getQuestions().getQuestion((Long) questions.next()); + + if (q.statable && !q.link.active) + { + statable = q; + break; + } + } + + /** + * Settle for any statable + */ + if (statable == null) + { + questions = getQuestionOrder(Question.ALTER_QUESTION).iterator(); + while (questions.hasNext()) + { + Question q = (Question) getQuestions().getQuestion((Long) questions.next()); + + if (q.statable) + { + statable = q; + } + } + } + + return (statable); + } + + /*************************************************************************** + * Returns UniqueId of study read from file + * + * @param long + * Unique Id of study + */ + public void setStudyId(long id) + { + _uniqueId = id; + } + + /*************************************************************************** + * Sets name of study + * + * @param name + * name of study + */ + public void setStudyName(String name) + { + if (!_studyName.equals(name)) + { + _studyName = name; + setModified(true); + } + } + + /*************************************************************************** + * Sets numAlters variable and notifies observers of change to study + * + * @param n + * number of alters for which to elicit + */ + public void setNumAlters(int n) + { + if (_numAlters != n) + { + _numAlters = n; + setModified(true); + } + } + + /*************************************************************************** + * Sets study dirty flag; generally done when the study is written to a + * file + */ + public void setModified(boolean dirty) + { + _studyDirty = dirty; + notifyObservers(); + } + + /*************************************************************************** + * gets dirty state of study + * + * @return dirty + */ + public boolean isModified() + { + return (_studyDirty); + } + + /** + * @return Returns the compatible. + */ + public boolean isCompatible() + { + return _compatible; + } + + /** + * @param compatible The compatible to set. + */ + public void setCompatible(boolean compatible) + { + this._compatible = compatible; + notifyObservers(); + } + + /** + * @return Returns the inUse. + */ + public boolean isInUse() + { + return _inUse; + } + + /** + * @param inUse The inUse to set. + */ + public void setInUse(boolean inUse) + { + this._inUse = inUse; + } + + /** + * @return Returns the uiType + */ + public String getUIType() + { + return _uiType; + } + + public boolean isAppletUI() + { + return (getUIType().equals(Shared.PAIR_ELICITATION) || getUIType().equals(Shared.THREE_STEP_ELICITATION)); + } + + /** + * Warn user this change will make study no longer compatible with previous interviews + * @param q + * @throws DuplicateQuestionException + */ + public boolean confirmIncompatibleChange(JFrame frame) + { + boolean ok = true; + if (isInUse() && isCompatible()) + { + int confirm = + JOptionPane.showConfirmDialog( + frame, + "This study has already been used for at least one interview.\n" + + "If you make this change you will have to save this as a new study and will \n" + + "no longer be able to access prior interviews with this study.\n" + + "Do you still wish to make this change?", + "Incompatible Study Modification", + JOptionPane.OK_CANCEL_OPTION); + + if (confirm != JOptionPane.OK_OPTION) + { + ok = false; + } + } + + return ok; + } + + /*************************************************************************** + * Adds a question to the full question list + * + * @param q + * question to add + */ + public void addQuestion(Question q) throws DuplicateQuestionException + { + if (!this.getQuestions().contains(q)) + { + this.getQuestions().addQuestion(q); + setModified(true); + } + else + { + throw new DuplicateQuestionException(); + } + + /* If not in appropriate array list, add to that list too */ + if (!_questionOrder[q.questionType].contains(q.UniqueId)) + { + _questionOrder[q.questionType].add(q.UniqueId); + } + } + + /*************************************************************************** + * Changes position of a question in an order list + * + * @param q + * question to move + * @param follow + * question q should follow + */ + public void moveQuestionAfter(Question q, Question follow) + { + int followloc; + + if (_questionOrder[q.questionType].contains(follow.UniqueId) || (follow == _firstQuestion)) + { + _questionOrder[q.questionType].remove(q.UniqueId); + + if (follow == _firstQuestion) + { + _questionOrder[q.questionType].add(0, q.UniqueId); + } + else + { + followloc = _questionOrder[q.questionType].indexOf(follow.UniqueId); + _questionOrder[q.questionType].add(followloc + 1, q.UniqueId); + } + + if (q.link.active) + { + if (!doesQuestionPreceed(q.link.answer.questionId, q.UniqueId)) + { + q.link.active = false; + q.link.answer = null; + } + } + + setModified(true); + } + } + + /*************************************************************************** + * moves question from one order list to another + * + * @param q + * question to move + * @param type + * new question type + */ + public void changeQuestionType(Question q, int type) + { + removeQuestion(q); + + q.questionType = type; + + try + { + addQuestion(q); + } + catch (DuplicateQuestionException e) + { + // This shouldn't happen + e.printStackTrace(); + } + + setModified(true); + } + + /*************************************************************************** + * Go through question list making sure any interquestion dependencies are + * met + * + * @param q + * question to move + * @param type + * new question type + */ + public void setCentralQuestion(Question q) + { + Long key; + Question listQ; + Iterator i = _questionOrder[Question.ALTER_PAIR_QUESTION].iterator(); + + while (i.hasNext()) + { + key = (Long) i.next(); + listQ = this.getQuestions().getQuestion(key); + + if (listQ != null) + { + if (listQ.equals(q)) + { + if (!listQ.centralMarker) + { + listQ.centralMarker = true; + setModified(true); + } + } + else + { + /* Only one centralMarker allowed */ + if (listQ.centralMarker) + { + listQ.centralMarker = false; + setModified(true); + } + } + } + } + } + + /*************************************************************************** + * Go through question list making sure any interquestion dependencies are + * met + * + * @param q + * question to move + * @param type + * new question type + */ + public void validateQuestions() + { + Long key; + Question q; + boolean foundCentral = false; + ; + Iterator i = _questionOrder[Question.ALTER_PAIR_QUESTION].iterator(); + + while (i.hasNext()) + { + key = (Long) i.next(); + q = this.getQuestions().getQuestion(key); + + if (q != null) + { + if (!foundCentral && q.centralMarker) + { + foundCentral = true; + } + else + { + /* Only one centralMarker allowed */ + q.centralMarker = false; + } + } + } + + if (!foundCentral) + { + /* Tag first Alter pair categorical question */ + i = _questionOrder[Question.ALTER_PAIR_QUESTION].iterator(); + + while (i.hasNext() && !foundCentral) + { + key = (Long) i.next(); + q = this.getQuestions().getQuestion(key); + + if ((q != null) && (q.answerType == Question.CATEGORICAL)) + { + q.centralMarker = true; + foundCentral = true; + } + } + } + } + + /*************************************************************************** + * Searches question list for all questions of a given tpe, places them in + * list + * + * @param questionType + * type filter for question list + * @param dlm + * list model to use in inserting questions + */ + public void fillList(int questionType, DefaultListModel dlm) + { + int startType, endType; + Iterator i; + Long key; + + if (questionType == Question.ALL_QUESTION_TYPES) + { + startType = Question.MIN_QUESTION_TYPE; + endType = Question.MAX_QUESTION_TYPE; + } + else + { + startType = questionType; + endType = questionType; + } + + for (int type = startType; type <= endType; type++) + { + if ((questionType != Question.ALL_QUESTION_TYPES) || (type != Question.ALTER_PROMPT)) + { + i = _questionOrder[type].iterator(); + while (i.hasNext()) + { + key = (Long) i.next(); + if (this.getQuestions().contains(key)) + { + dlm.addElement(this.getQuestions().getQuestion(key)); + } + } + } + } + } + + /*************************************************************************** + * Searches question list for all questions of a given tpe, places them in + * list until a given question is reached + * + * @param questionType + * type filter for question list + * @param dlm + * list model to use in inserting questions + * @param endId + * question list end, stop when you see this question + */ + public void fillList(int questionType, DefaultListModel dlm, Long endId) + { + int startType, endType; + Iterator i; + Long key; + boolean found = false; + + if (questionType == Question.ALL_QUESTION_TYPES) + { + startType = Question.MIN_QUESTION_TYPE; + endType = Question.MAX_QUESTION_TYPE; + } + else + { + startType = questionType; + endType = questionType; + } + + for (int type = startType;(type <= endType) && !found; type++) + { + if ((questionType != Question.ALL_QUESTION_TYPES) || (type != Question.ALTER_PROMPT)) + { + i = _questionOrder[type].iterator(); + while (i.hasNext() && !found) + { + key = (Long) i.next(); + if (key.equals(endId)) + { + found = true; + } + else if (this.getQuestions().contains(key)) + { + dlm.addElement(this.getQuestions().getQuestion(key)); + } + } + } + } + } + + /*************************************************************************** + * Returns true iff q1 preceeds q2 in study + * + * @param q1 + * Id of question which may preceed q2 + * @param q2 + * Id of question which may be preceeded by q1 + */ + public boolean doesQuestionPreceed(Long q1, Long q2) + { + int startType, endType; + Iterator i; + Long key; + boolean found = false; + + startType = Question.MIN_QUESTION_TYPE; + endType = Question.MAX_QUESTION_TYPE; + + for (int type = startType;(type <= endType) && !found; type++) + { + i = _questionOrder[type].iterator(); + while (i.hasNext() && !found) + { + key = (Long) i.next(); + if (key.equals(q1)) + { + return true; + } + else if (key.equals(q2)) + { + return false; + } + } + } + + return false; + } + + /*************************************************************************** + * Essentially makes sure order list matches question list + */ + public void verifyStudy() + { + for (int i = 0; i < Question.NUM_QUESTION_TYPES; i++) + { + Iterator it = getQuestionIterator(i); + + while (it.hasNext()) + { + Long qid = (Long) it.next(); + + if (getQuestions().getQuestion(qid) == null) + { + it.remove(); + } + } + } + } + + /*************************************************************************** + * Returns a bi-directional list iterator of questions for a category + * + * @param category + * category of question + * @return iterator list iterator or questions + */ + public ListIterator getQuestionIterator(int category) + { + return (_questionOrder[category].listIterator()); + } + + /*************************************************************************** + * Remove all base or custom Questions from question list and order lists + * + * @param base + * Remove questions from base file or custom file + */ + public void removeQuestions() + { + Question q; + + Iterator i = this.getQuestions().values().iterator(); + + while (i.hasNext()) + { + q = (Question) i.next(); + + i.remove(); + removeQuestion(q); + } + } + + /*************************************************************************** + * Remove one Question from question map and order lists + * + * @param q + * question to remove + */ + public void removeQuestion(Question q) + { + removeLinksToQuestion(q); + + for (int i = 0; i < _questionOrder.length; i++) + { + _questionOrder[i].remove(q.UniqueId); + } + + this.getQuestions().remove(q); + setModified(true); + } + + /*************************************************************************** + * Searches question list for any questions linked to a question about to + * be removed, and removes those question links. + * + * @param questionType + * type filter for question list + * @param dlm + * list model to use in inserting questions + */ + public void removeLinksToQuestion(Question lq) + { + Question q; + + Iterator i = this.getQuestions().values().iterator(); + + while (i.hasNext()) + { + q = (Question) i.next(); + + if (q.link.active && (q.link.answer.questionId.equals(lq.UniqueId))) + { + q.link.active = false; + q.link.answer = null; + } + } + } + + /*************************************************************************** + * Writes study specific information to xml output file + */ + public void writeInterviewStudy(Element e) + { + e.setInt("numalters", getNumAlters()); + } + + /*************************************************************************** + * Reads in study information from an XML input file Includes files paths + * and arrays of question orders + * + * @param studyFile + * File from which to read study + */ + public void readInterviewStudy(Element e) + { + // String data; + + try + { + if (e.getElement("numalters") != null) + { + setNumAlters(e.getInt("numalters")); + } + + } + catch (Exception ex) + { + /** @todo handle exception */ + ex.printStackTrace(); + } + } + + /*************************************************************************** + * Reads in study information from an XML input file Includes files paths + * and arrays of question orders + * + * @param studyFile + * File from which to read study + */ + public void readPackageStudy(Document document) + { + // String data; + + try + { + Element root = document.getRoot(); + setStudyId(Long.parseLong(root.getAttributeValue("Id"))); + + root = root.getElement("Study"); + + if (root.getElement("name") != null) + { + setStudyName(root.getTextString("name")); + } + + if (root.getElement("numalters") != null) + { + setNumAlters(root.getInt("numalters")); + } + + Elements elements = root.getElements("questionorder"); + while (elements.hasMoreElements()) + { + int qOrderId; + List questionOrder; + Elements ids; + + Element element = elements.next(); + qOrderId = Integer.parseInt(element.getAttribute("questiontype")); + questionOrder = (getQuestionOrderArray())[qOrderId]; + + ids = element.getElements("id"); + while (ids.hasMoreElements()) + { + questionOrder.add(new Long(ids.next().getLong())); + } + } + } + catch (Exception e) + { + /** @todo handle exception */ + e.printStackTrace(); + } + } + + /*************************************************************************** + * Reads all the questions from a file + * + * @param f + * File from which to read questions + */ + public void readQuestions(Document document) + { + Element root, question; + Elements questions; + + /** + * Parse XML file + */ + root = document.getRoot(); + root = root.getElement("QuestionList"); + questions = root.getElements("Question"); + + while (questions.hasMoreElements()) + { + try + { + /* Question complete, add it */ + Question q = new Question(questions.next()); + addQuestion(q); + } + catch (MalformedQuestionException e) + { + /* Don't create this question. Incomplete */ + System.err.println("Malformed Question in file."); + } + catch (DuplicateQuestionException e) + { + /* Don't create this question. Incomplete */ + System.err.println("Duplicate Question in file."); + } + } + } + + /************************************************************** + * Writes Study information to a file for later retrieval + * Includes files paths and arrays of question orders + * + * @param document XML element to which to add study information + * @todo prune order lists, possibly need to load question files to do this + */ + public void writeStudyData(Element document) + { + Iterator it; + int i; + + try + { + Element study = document.addElement("Study"); + + study.addElement("name").setText(getStudyName()); + study.addElement("numalters").setInt(getNumAlters()); + + for (i = 1; i < Question.NUM_QUESTION_TYPES; i++) + { + Element qorder = new Element("questionorder"); + it = (getQuestionOrderArray())[i].iterator(); + + if (it.hasNext()) + { + study.addElement(qorder).setAttribute("questiontype", Integer.toString(i)); + while (it.hasNext()) + { + qorder.addElement("id").setLong(((Long) it.next()).longValue()); + } + } + } + } + catch (Exception ex) + { + JOptionPane.showMessageDialog( + null, + "Unable to write to this study file", + "Study Writing Error", + JOptionPane.ERROR_MESSAGE); + + } + } + + /*********************************************************** + * Writes all questions to a package file for later use + * + * @param document + * XML tree to which to add question + * @throws IOException + */ + public void writeAllQuestionData(Element document) + throws IOException + { + Iterator it; + Element element; + + element = document.addElement("QuestionList"); + + it = getQuestions().getQuestionMap().values().iterator(); + + while (it.hasNext()) + { + Question q = (Question) it.next(); + q.writeQuestion(element.addElement("Question"), getQuestions()); + } + } + + /** + * @author Peter Schoaff + * + * Store study in selected server. + */ + public boolean writeDBStudy(JFrame frame, String server, char[] password) + { + boolean rval = true; + + try + { + _prop.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory"); + _prop.setProperty("java.naming.provider.url", server + ":1099"); + //System.out.println(_prop.getProperty("java.naming.provider.url")); + + StudySBRemoteHome studyHome = StudySBUtil.getHome(_prop); + StudySBRemote studySB = studyHome.create(); + StudyDataValue data = new StudyDataValue(); + + data.setUIType(com.endlessloopsoftware.egonet.Shared.THREE_STEP_ELICITATION); + data.setNumAlters(getNumAlters()); + data.setStudyName(getStudyName()); + + Long[][] questionOrder = new Long[Question.NUM_QUESTION_TYPES][]; + + for (int i = 1; i < Question.NUM_QUESTION_TYPES; ++i) + { + List qorder = getQuestionOrder(i); + questionOrder[i] = new Long[qorder.size()]; + qorder.toArray(questionOrder[i]); + } + + data.setQuestionOrder(questionOrder); + + Iterator it = getQuestions().getQuestionMap().values().iterator(); + + while (it.hasNext()) + { + Question q = (Question) it.next(); + data.addQuestionDataValue(q.getDataValue(this.getQuestions(), data.getId())); + } + + String epassword = "215-121-242-47-99-238-5-61-133-183-0-216-187-250-253-30-115-177-254-142-161-83-108-56";//SymmetricKeyEncryption.encrypt(new String(password)); + studySB.createStudy(data, epassword); + } + catch (CreateException e) + { + JOptionPane.showMessageDialog(frame, + "Unable to store study.\n" + e.getMessage(), + "Server error", + JOptionPane.ERROR_MESSAGE); + + rval = false; + } + catch (Exception e) + { + JOptionPane.showMessageDialog(frame, + "Unable to store study.\n", + "Server error", + JOptionPane.ERROR_MESSAGE); + + // TODO Auto-generated catch block + e.printStackTrace(); + rval = false; + } + + return rval; + } + +} + +/** + * $Log: Study.java,v $ + * Revision 1.1 2005/08/02 19:36:02 samag + * Initial checkin + * + * Revision 1.14 2004/04/11 15:19:28 admin + * Using password to access server + * + * Remote study summary in seperate thread with progress monitor + * + * Revision 1.13 2004/04/11 00:17:13 admin + * Improving display of Alter Prompt questions from Applet UI Interviews + * + * Revision 1.12 2004/04/07 00:08:31 admin + * updating manifests, jar creation. Removing author specific objects from + * client specific references + * + * Revision 1.11 2004/04/06 20:29:22 admin + * First pass as supporting interactive applet linking interviews + * + * Revision 1.10 2004/04/06 14:56:02 admin + * Work to integrate with Applet Linking UI + * + * Revision 1.9 2004/04/02 19:48:58 admin + * Keep Study Id when possible + * Store updated time in file + * + * Revision 1.8 2004/03/29 00:35:09 admin + * Downloading Interviews + * Fixing some bugs creating Interviews from Data Objects + * + * Revision 1.7 2004/03/28 17:31:31 admin + * More error handling when uploading study to server + * Server URL selection dialog for upload + * + * Revision 1.6 2004/03/23 14:58:47 admin + * Update UI + * Study creation now occurs in instantiators + * + * Revision 1.5 2004/03/21 20:29:37 admin + * Warn before making incompatible changes to in use study file + * + * Revision 1.4 2004/03/21 14:00:38 admin + * Cleaned up Question Panel Layout using FOAM + * + * Revision 1.3 2004/02/10 20:10:42 admin + * Version 2.0 beta 3 + * + * Revision 1.2 2004/01/23 13:36:07 admin + * Updating Libraries + * Allowing upload to web server + * + * Revision 1.1 2003/12/05 19:15:43 admin + * Extracting Study + * Revision 1.3 2003/12/04 15:14:08 admin Merging EgoNet + * and EgoClient projects so that they can share some common classes more + * easily. + * + * Revision 1.2 2003/11/25 19:25:44 admin Warn before closing window + * + * Revision 1.1.1.1 2003/06/08 15:09:40 admin Egocentric Network Survey + * Authoring Module + * + * Revision 1.10 2002/08/11 22:26:06 admin Final Statistics window, new file + * handling + * + * Revision 1.9 2002/08/08 17:07:26 admin Preparing to change file system + * + * Revision 1.8 2002/07/25 14:54:24 admin Question Links + * + * Revision 1.7 2002/07/24 14:17:10 admin xml files, links + * + * Revision 1.6 2002/07/18 14:43:06 admin New Alter Prompt Panel, packages + * + * Revision 1.5 2002/06/30 15:59:18 admin Moving questions in lists, between + * lists Better category input + * + * Revision 1.4 2002/06/26 15:43:43 admin More selection dialog work File + * loading fixes + * + * Revision 1.3 2002/06/25 15:41:02 admin Lots of UI work + * + * Revision 1.2 2002/06/21 22:47:12 admin question lists working again + * + * Revision 1.1 2002/06/21 21:53:29 admin new files + * + * Revision 1.2 2002/06/16 17:53:10 admin Working with files + * + * Revision 1.1 2002/06/15 14:19:51 admin Initial Checkin of question and + * survey General file system work + * + */ diff --git a/src/com/endlessloopsoftware/ego/author/AuthoringQuestionPanel.java b/src/com/endlessloopsoftware/ego/author/AuthoringQuestionPanel.java new file mode 100644 index 0000000..464c62e --- /dev/null +++ b/src/com/endlessloopsoftware/ego/author/AuthoringQuestionPanel.java @@ -0,0 +1,721 @@ +package com.endlessloopsoftware.ego.author; + +/** + *

Title: Survey Center Database Client

+ *

Description: Database Manipulation program for Survey Center Suite

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: QuestionPanel.java,v 1.1 2005/08/02 19:36:05 samag Exp $ + */ + + +import java.awt.*; +import java.awt.event.ActionEvent; + +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 org.egonet.exceptions.DuplicateQuestionException; + +import com.endlessloopsoftware.ego.Question; + +/** + * Generic Panel creation and handling routines for question editing + */ +public class AuthoringQuestionPanel extends EgoQPanel +{ + private final int questionType; + private boolean inUpdate; + + private final JSplitPane question_split = new JSplitPane(); + private final JList question_list = new JList(); + private final JScrollPane question_list_scroll = new JScrollPane(question_list); + private final JPanel question_panel_right = new RightPanel(); + private final JLabel question_title_label = new JLabel("Title:"); + private final JLabel question_question_label = new JLabel("Question:"); + private final JLabel question_citation_label = new JLabel("Citation:"); + private final JLabel question_type_label = new JLabel("Question Type:"); + private final JComboBox question_type_menu = new JComboBox(questionTypes); + private final JLabel question_answer_type_label = new JLabel("Answer Type:"); + private final JButton question_answer_type_button = new JButton("Selections"); + private final JLabel question_link_label = new JLabel("Question Link:"); + private final JLabel question_link_field = new JLabel("None"); + private final JLabel question_follows_label = new JLabel("Follows Question:"); + private final JComboBox question_answer_type_menu = new JComboBox(answerTypes); + private final JComboBox question_follows_menu = new JComboBox(); + private final JTextArea question_question_field = new NoTabTextArea(); + private final JTextArea question_citation_field = new NoTabTextArea(); + private final JTextField question_title_field = new JTextField(); + private final JButton question_new_button = new JButton("New"); + private final JButton question_link_button = new JButton("Set Link"); + private final JButton question_delete_button = new JButton("Delete"); + private final JLabel question_central_label = new JLabel(); + private final CategoryInputPane selectionsDialog; + private final QuestionLinkDialog questionLinkDialog; + private final Border listBorder; + + private final static String[] questionTypes = {Question.questionTypeString(1), Question.questionTypeString(2), + Question.questionTypeString(3), Question.questionTypeString(4)}; + private final static String[] answerTypes = {"Categorical", "Numerical", "Text"}; + + /** + * Generates Panel for question editing to insert in file tab window + * @param type Type of questions on Page (e.g. Alter Questions) + * @param parent parent frame for referencing composed objects + */ + public AuthoringQuestionPanel(int type) + { + questionType = type; + questionLinkDialog = new QuestionLinkDialog(); + selectionsDialog = new CategoryInputPane(question_list); + + listBorder = BorderFactory.createCompoundBorder( + new TitledBorder(new EtchedBorder(EtchedBorder.RAISED,Color.white,new Color(178, 178, 178)), + Question.questionTypeString(questionType)), + BorderFactory.createEmptyBorder(10,10,10,10)); + + try + { + jbInit(); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + + /** + * Component initialization + * @throws Exception + */ + private void jbInit() + throws Exception + { + inUpdate = true; + + // Configure Split Frame + question_split.setMinimumSize(new Dimension(430, 330)); + question_split.setPreferredSize(new Dimension(430, 330)); + question_split.setResizeWeight(.33); + question_split.setDividerLocation(.33); + question_list_scroll.setRequestFocusEnabled(false); + question_split.add(question_list_scroll, JSplitPane.LEFT); + question_split.add(question_panel_right, JSplitPane.RIGHT); + + this.setLayout(new GridLayout()); + + // Configure List + question_list_scroll.setBorder(listBorder); + question_list_scroll.setMinimumSize(new Dimension(150, 150)); + question_list_scroll.setPreferredSize(new Dimension(150, 150)); + question_list_scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + + question_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); +// question_list.setCellRenderer(new QuestionListCellRenderer()); + + // Configure question fields + question_panel_right.setLayout(new GridBagLayout()); + question_question_field.setMaximumSize(new Dimension(280, 64)); + question_question_field.setMinimumSize(new Dimension(72, 16)); + question_question_field.setPreferredSize(new Dimension(72, 16)); + question_question_field.setLineWrap(true); + question_question_field.setRows(1); + question_question_field.setTabSize(4); + question_question_field.setWrapStyleWord(true); + + question_citation_field.setMaximumSize(new Dimension(280, 64)); + question_citation_field.setMinimumSize(new Dimension(72, 16)); + question_citation_field.setPreferredSize(new Dimension(72, 16)); + question_citation_field.setLineWrap(true); + question_citation_field.setRows(1); + question_citation_field.setTabSize(4); + question_citation_field.setWrapStyleWord(true); + + /* Question Layout */ + question_panel_right.add(question_title_label, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_title_field, new GridBagConstraints(1, 0, 2, 1, 0.0, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10, 10), 0, 4)); + question_panel_right.add(question_question_label, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_question_field, new GridBagConstraints(1, 1, 2, 3, 0.0, 0.4 + ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_citation_label, new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_citation_field, new GridBagConstraints(1, 4, 2, 3, 0.0, 0.3 + ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_type_label, new GridBagConstraints(0, 7, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_type_menu, new GridBagConstraints(1, 7, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_central_label, new GridBagConstraints(2, 7, 1, 1, 0.2, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_answer_type_label, new GridBagConstraints(0, 8, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_answer_type_menu, new GridBagConstraints(1, 8, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_answer_type_button, new GridBagConstraints(2, 8, 1, 1, 0.2, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_follows_label, new GridBagConstraints(0, 9, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_follows_menu, new GridBagConstraints(1, 9, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_link_label, new GridBagConstraints(0, 10, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_link_field, new GridBagConstraints(1, 10, 2, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_new_button, new GridBagConstraints(0, 11, 1, 1, 0.33, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_link_button, new GridBagConstraints(1, 11, 1, 1, 0.33, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_delete_button, new GridBagConstraints(2, 11, 1, 1, 0.33, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + + question_list.setModel(new DefaultListModel()); + EgoNet.study.fillList(questionType, (DefaultListModel) question_list.getModel()); + + question_list.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + public void valueChanged(ListSelectionEvent e) { + question_list_selectionChanged(e); }}); + + question_new_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + question_new_button_actionPerformed(e);}}); + + question_delete_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + question_delete_button_actionPerformed(e);}}); + + question_link_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + question_link_button_actionPerformed(e);}}); + + question_follows_menu.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + question_follows_menu_actionPerformed(e);}}); + + question_type_menu.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + question_type_menu_actionPerformed(e);}}); + + question_answer_type_menu.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + question_answer_type_menu_actionPerformed(e);}}); + + question_answer_type_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + set_selections_button_actionPerformed(e);}}); + + question_title_field.getDocument().addDocumentListener(new DocumentListener() { + public void insertUpdate(DocumentEvent e) { questionTitleEvent(); } + public void changedUpdate(DocumentEvent e) { questionTitleEvent(); } + public void removeUpdate(DocumentEvent e) { questionTitleEvent(); }}); + + question_question_field.getDocument().addDocumentListener(new DocumentListener() { + public void insertUpdate(DocumentEvent e) { questionTextEvent(); } + public void changedUpdate(DocumentEvent e) { questionTextEvent(); } + public void removeUpdate(DocumentEvent e) { questionTextEvent(); }}); + + question_citation_field.getDocument().addDocumentListener(new DocumentListener() { + public void insertUpdate(DocumentEvent e) { questionCitationEvent(); } + public void changedUpdate(DocumentEvent e) { questionCitationEvent(); } + public void removeUpdate(DocumentEvent e) { questionCitationEvent(); }}); + + this.add(question_split, null); + + inUpdate = false; + } + + /** + * Updates right side question fields when the selection changes + * @param e event generated by selection change. + */ + private void question_list_selectionChanged(ListSelectionEvent e) + { + if (!e.getValueIsAdjusting()) + { + if (!inUpdate) + { + questionUpdate(); + } + } + } + + /**** + * fill List with appropriate questions + * Set other fields to selected question + */ + public void fillPanel() + { + if (questionType == EgoNet.frame.curTab) + { + storageUpdate(); + questionUpdate(); + } + } + + /** + * Called when file changes to load new questions into list + */ + private void storageUpdate() + { + inUpdate = true; + + if (questionType == EgoNet.frame.curTab) + { + Object o = question_list.getSelectedValue(); + ((DefaultListModel) question_list.getModel()).removeAllElements(); + EgoNet.study.fillList(questionType, (DefaultListModel) question_list.getModel()); + question_list.setSelectedValue(o, true); + } + + inUpdate = false; + } + + private void questionUpdate() + { + Question q; + int index; + + inUpdate = true; + + if (questionType == EgoNet.frame.curTab) + { + /* If no element selected, assume first */ + index = question_list.getSelectedIndex(); + if ((index == -1) && (question_list.getModel().getSize() > 0)) + { + index = 0; + } + + /* Load questions from list into follows menu */ + question_follows_menu.removeAllItems(); + question_follows_menu.addItem(EgoNet.study.getFirstQuestion()); + for (int i = 0; i < question_list.getModel().getSize(); i++) + { + if (i != index) + { + question_follows_menu.addItem(question_list.getModel().getElementAt(i)); + } + } + + question_list.setSelectedIndex(index); + q = (Question) question_list.getSelectedValue(); + if (q != null) + { + question_type_menu.setSelectedIndex(questionType - 1); + question_answer_type_menu.setSelectedIndex(q.answerType); + question_question_field.setText(q.text); + question_citation_field.setText(q.citation); + question_title_field.setText(q.title); + question_follows_menu.setSelectedIndex(index); + + question_type_menu.setEnabled(true); + question_answer_type_menu.setEnabled(q.questionType != Question.ALTER_PROMPT); + + System.out.println("AnswerType : " + answerTypes[q.answerType]); + question_answer_type_button.setEnabled(q.answerType == Question.CATEGORICAL); + question_question_field.setEditable(true); + question_citation_field.setEditable(true); + question_title_field.setEditable(true); + question_delete_button.setEnabled(true); + question_link_button.setEnabled(true); + + /* Box only appears on alter pair page */ + question_central_label.setVisible(false); + if (q.answerType == Question.CATEGORICAL) + { + if (q.selections.length == 0) + { + question_central_label.setText("No Selections"); + question_central_label.setForeground(Color.red); + question_central_label.setVisible(true); + } + else if (questionType == Question.ALTER_PAIR_QUESTION) + { + question_central_label.setText("No Adjacency Selections"); + question_central_label.setForeground(Color.red); + + for (int i = 0; i < q.selections.length; i++) + { + if (q.selections[i].isAdjacent()) + { + question_central_label.setText("Adjacency Selections Set"); + question_central_label.setForeground(Color.black); + } + } + + question_central_label.setVisible(true); + } + } + + /* Fill in link field */ + if (q.link.active) + { + Question linkQuestion = EgoNet.study.getQuestions().getQuestion(q.link.answer.questionId); + + if (linkQuestion == null) + { + question_link_field.setText("< none >"); + } + else + { + if (linkQuestion.title.length() > 32) + { + question_link_field.setText(linkQuestion.title.substring(0, 32) + ": " + q.link.answer.string); + } + else + { + question_link_field.setText(linkQuestion.title + ": " + q.link.answer.string); + } + } + } + else + { + question_link_field.setText("< none >"); + } + } + else + { + question_answer_type_menu.setSelectedIndex(0); + question_question_field.setText(null); + question_citation_field.setText(null); + question_title_field.setText(null); + question_central_label.setVisible(false); + question_link_field.setText(""); + + question_type_menu.setEnabled(false); + question_answer_type_menu.setEnabled(false); + question_answer_type_button.setEnabled(false); + question_question_field.setEditable(false); + question_citation_field.setEditable(false); + question_title_field.setEditable(false); + question_delete_button.setEnabled(false); + question_link_button.setEnabled(false); + } + } + + inUpdate = false; + } + + + /**** + * Clear all on screen editable fields + * Generally called when a new survey is started + */ + public void clearPanel() + { + inUpdate = true; + ((DefaultListModel) question_list.getModel()).removeAllElements(); + inUpdate = false; + } + + /**** + * Document event handler used to read text fields + */ + private void questionTitleEvent() + { + Question q = (Question) question_list.getSelectedValue(); + String s; + + if ((q != null) && !inUpdate) + { + s = question_title_field.getText().trim(); + if ((q.title == null) || (!q.title.equals(s))) + { + q.title = question_title_field.getText().trim(); + EgoNet.study.setModified(true); + question_list.repaint(); + } + } + } + + + /**** + * Document event handler used to read text fields + */ + private void questionTextEvent() + { + Question q = (Question) question_list.getSelectedValue(); + String s; + + if ((q != null) && !inUpdate) + { + s = question_question_field.getText().trim(); + if ((q.text == null) || (!q.text.equals(s))) + { + q.text = question_question_field.getText().trim(); + EgoNet.study.setModified(true); + } + } + } + + + /**** + * Document event handler used to read text fields + */ + private void questionCitationEvent() + { + Question q = (Question) question_list.getSelectedValue(); + String s; + + if ((q != null) && !inUpdate) + { + s = question_citation_field.getText().trim(); + if ((q.citation == null) || (!q.citation.equals(s))) + { + q.citation = question_citation_field.getText().trim(); + EgoNet.study.setModified(true); + } + } + } + + + + /**** + * Event handler for new question button + * @param e Action Event + */ + private void question_new_button_actionPerformed(ActionEvent e) + { + if (EgoNet.study.confirmIncompatibleChange(EgoNet.frame)) + { + Question q = new Question(); + + q.questionType = questionType; + q.title = new String(Question.questionTypeString(questionType) + ":Untitled Question"); + + if (q.questionType == Question.ALTER_PROMPT) + { + q.answerType = Question.TEXT; + } + + try + { + EgoNet.study.addQuestion(q); + } + catch (DuplicateQuestionException e1) + { + e1.printStackTrace(); + } + + fillPanel(); + question_list.setSelectedValue(q, true); + + question_title_field.requestFocus(); + question_title_field.setSelectionStart(0); + question_title_field.setSelectionEnd(question_title_field.getText().length()); + + EgoNet.study.setModified(true); + EgoNet.study.setCompatible(false); + } + } + + + /**** + * Event handler for delete question button + * @param e Action Event + */ + private void question_delete_button_actionPerformed(ActionEvent e) + { + Question q = (Question) question_list.getSelectedValue(); + + if (q != null) + { + int confirm = JOptionPane.showConfirmDialog(EgoNet.frame, "Permanently remove this questions?", + "Delete Question", JOptionPane.OK_CANCEL_OPTION); + + if ((confirm == JOptionPane.OK_OPTION) && EgoNet.study.confirmIncompatibleChange(EgoNet.frame)) + { + EgoNet.study.removeQuestion(q); + EgoNet.study.setModified(true); + EgoNet.study.setCompatible(false); + fillPanel(); + } + } + } + + /** + * Opens Set Link Dialog + * @param e UI event + */ + void question_link_button_actionPerformed(ActionEvent e) + { + Question q = (Question) question_list.getSelectedValue(); + questionLinkDialog.pack(); + questionLinkDialog.activate(q); + } + + /** + * Change question type in question record, move to new ordering list + * @param e UI event + */ + void question_type_menu_actionPerformed(ActionEvent e) + { + if (!inUpdate) + { + if (EgoNet.study.confirmIncompatibleChange(EgoNet.frame)) + { + Question q = (Question) question_list.getSelectedValue(); + int type = question_type_menu.getSelectedIndex() + 1; + + EgoNet.study.changeQuestionType(q, type); + EgoNet.study.setCompatible(false); + fillPanel(); + } + else + { + questionUpdate(); + } + } + } + + /** + * Change answer type in pop up menu, save in question record + * @param e UI event + */ + void question_answer_type_menu_actionPerformed(ActionEvent e) + { + if (!inUpdate) + { + if (EgoNet.study.confirmIncompatibleChange(EgoNet.frame)) + { + int i = question_answer_type_menu.getSelectedIndex(); + Question q = (Question) question_list.getSelectedValue(); + + if (q != null) + { + if (q.answerType != i) + { + q.answerType = i; + EgoNet.study.setModified(true); + EgoNet.study.setCompatible(false); + questionUpdate(); + } + } + } + else + { + // Change back + questionUpdate(); + } + } + } + + /** + * Brings up category selection modal dialog box + * @param e UI event + */ + void set_selections_button_actionPerformed(ActionEvent e) + { + Point loc = getLocationOnScreen(); + + selectionsDialog.pack(); + selectionsDialog.setSize(300,300); + selectionsDialog.activate(); + } + + /** + * Changes order of questions + * @param e UI event + */ + void question_follows_menu_actionPerformed(ActionEvent e) + { + if (!inUpdate) + { + if (EgoNet.study.confirmIncompatibleChange(EgoNet.frame)) + { + Question follows = (Question) question_follows_menu.getSelectedItem(); + Question q = (Question) question_list.getSelectedValue(); + + EgoNet.study.moveQuestionAfter(q, follows); + EgoNet.study.setCompatible(false); + EgoNet.study.setModified(true); + fillPanel(); + } + else + { + questionUpdate(); + } + } + } + + void question_central_checkBox_actionPerformed(ActionEvent e) + { + Question q = (Question) question_list.getSelectedValue(); + EgoNet.study.setCentralQuestion(q); + } +} + + +/** + * Implements ListCellRenderer to differentiate between base and custom questions + */ +class QuestionListCellRenderer + implements ListCellRenderer +{ + protected final DefaultListCellRenderer defaultRenderer = new DefaultListCellRenderer(); + + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) + { + JLabel renderer = (JLabel) defaultRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + return renderer; + } +} + + +/** + * $Log: QuestionPanel.java,v $ + * Revision 1.1 2005/08/02 19:36:05 samag + * Initial checkin + * + * Revision 1.12 2004/04/11 00:24:48 admin + * Fixing headers + * + * Revision 1.11 2004/04/11 00:17:13 admin + * Improving display of Alter Prompt questions from Applet UI Interviews + * + * Revision 1.10 2004/04/07 00:08:31 admin + * updating manifests, jar creation. Removing author specific objects from + * client specific references + * + * Revision 1.9 2004/03/28 17:31:31 admin + * More error handling when uploading study to server + * Server URL selection dialog for upload + * + * Revision 1.8 2004/03/21 20:29:37 admin + * Warn before making incompatible changes to in use study file + * + * Revision 1.7 2004/03/21 14:00:38 admin + * Cleaned up Question Panel Layout using FOAM + * + * Revision 1.6 2004/02/10 20:10:43 admin + * Version 2.0 beta 3 + * + * Revision 1.5 2003/12/08 15:57:50 admin + * Modified to generate matrix files on survey completion or summarization + * Extracted statistics models + * + * Revision 1.4 2003/12/05 19:15:43 admin + * Extracting Study + * + * Revision 1.3 2003/12/04 15:14:08 admin + * Merging EgoNet and EgoClient projects so that they can share some + * common classes more easily. + * + * Revision 1.2 2003/11/25 19:25:44 admin + * Warn before closing window + * + * Revision 1.1.1.1 2003/06/08 15:09:40 admin + * Egocentric Network Survey Authoring Module + * + */ \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/author/CategoryInputPane.java b/src/com/endlessloopsoftware/ego/author/CategoryInputPane.java new file mode 100644 index 0000000..54903ea --- /dev/null +++ b/src/com/endlessloopsoftware/ego/author/CategoryInputPane.java @@ -0,0 +1,251 @@ +package com.endlessloopsoftware.ego.author; + +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; + +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JList; + +import com.endlessloopsoftware.ego.Question; +import org.egonet.util.listbuilder.ListBuilder; +import org.egonet.util.listbuilder.Selection; + +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: CategoryInputPane.java,v 1.1 2005/08/02 19:36:04 samag Exp $ + * + */ + +/** + * Dialog box for collecting categorical answer selections + */ +public class CategoryInputPane extends JDialog { + private final JList parentList; + + private final GridBagLayout gridBagLayout1 = new GridBagLayout(); + + // create list builder with preset values turned on. + private final ListBuilder listBuilder = new ListBuilder(); + + private final JButton jOKButton = new JButton("OK"); + + private final JButton jCancelButton = new JButton("Cancel"); + + private Box box1; + + /** + * Constructor for CategoryInputPane + * + * @param list + * question list from parent frame used to determine which + * question we are operating on + */ + public CategoryInputPane(JList list) { + parentList = list; + + try { + jbInit(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Initializes layout and fields for the dialog + * + * @throws Exception + * No idea, sorry + */ + private void jbInit() throws Exception { + box1 = Box.createHorizontalBox(); + this.getContentPane().setLayout(gridBagLayout1); + + this.setModal(true); + this.setTitle("Category Options"); + this.setSize(400, 300); + + // 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.getContentPane().add( + listBuilder, + new GridBagConstraints(0, 0, 4, 1, 1.0, 0.9, + GridBagConstraints.CENTER, GridBagConstraints.BOTH, + new Insets(0, 0, 0, 0), 0, 0)); + this.getContentPane().add( + jCancelButton, + new GridBagConstraints(3, 1, 1, 1, 0.2, 0.0, + GridBagConstraints.EAST, GridBagConstraints.NONE, + new Insets(10, 0, 10, 10), 0, 0)); + this.getContentPane().add( + jOKButton, + new GridBagConstraints(2, 1, 1, 1, 0.2, 0.0, + GridBagConstraints.EAST, GridBagConstraints.NONE, + new Insets(10, 20, 10, 0), 26, 0)); + this.getContentPane().add( + box1, + new GridBagConstraints(0, 1, 1, 1, 0.5, 0.0, + GridBagConstraints.CENTER, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), + 40, 0)); + + jOKButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + OKButton_actionPerformed(e); + } + }); + + jCancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + cancelButton_actionPerformed(e); + } + }); + + } + + void OKButton_actionPerformed(ActionEvent e) { + boolean changed = false; + boolean compatible = true; + // boolean abort = false; + + Question q = (Question) parentList.getSelectedValue(); + if (q != null) { + /* count choices */ + + Selection[] newSelections = listBuilder.getListSelections(); + + // code added 09/05/2007 sonam + // ask for values for each question + + /* + * for (int i = 0; i < newSelections.length; i++) { String + * inputValue = null; String extraInformation = ""; int + * intInputValue = -1; + * + * do { inputValue = JOptionPane.showInputDialog(this, + * extraInformation + " Please input a value for " + + * newSelections[i].getString() + ": ", new + * Integer(newSelections[i].getValue()) ); if (inputValue == null) { // + * if user actually selected cancel + * JOptionPane.showMessageDialog(this, "Your question weight will + * default to " + Integer.toString(i) + "!"); inputValue = + * Integer.toString(i); } try { intInputValue = + * Integer.parseInt(inputValue); } catch (NumberFormatException ex) { + * inputValue = null; extraInformation = "Sorry! That was not a + * valid value for :\"" + newSelections[i].getString() + "\""; + * continue; } } while (inputValue == null); + * newSelections[i].setValue(intInputValue); } + */ + + if (newSelections.length != q.selections.length) { + if (EgoNet.study.confirmIncompatibleChange(EgoNet.frame)) { + compatible = false; + changed = true; + + // If the number changed we know the list has changed, so + // just copy over + // the reference and let the loop trim the strings + q.selections = newSelections; + } else { + // Don't make this change + EgoNet.frame.fillCurrentPanel(); + this.hide(); + return; + } + } + + // Trim the strings, check for changes + for (int i = 0; i < q.selections.length; i++) { + if (!q.selections[i] + .equals(newSelections[i].getString().trim())) { + q.selections[i].setString(newSelections[i].getString() + .trim()); + changed = true; + } + // q.selections[i].value= newSelections[i].value; + } + + /* + * original code by Peter: // change all the values at once for (int + * i = 0; i < newWeights.length; i++) newSelections[i].value = + * newWeights[i]; + */ + // End of code change + EgoNet.study.setModified(changed); + EgoNet.study.setCompatible(compatible); + + EgoNet.frame.fillCurrentPanel(); + this.hide(); + } + + } + + void cancelButton_actionPerformed(ActionEvent e) { + this.hide(); + } + + void activate() { + Question q = (Question) parentList.getSelectedValue(); + + if (q != null) { + listBuilder.setListSelections(q.selections); + } else { + System.err.println("Parent list had no selections"); + } + + listBuilder.setEditable(true); + + listBuilder.setElementName("Option: "); + listBuilder.setTitle("Category Options"); + listBuilder + .setDescription("Enter possible answers to this question below. Press Return to add the option " + + "to the options list. Press OK to set options or Cancel to undo changes."); + listBuilder.setNameList(q.questionType == Question.ALTER_PROMPT); + listBuilder.setLetUserPickValues(true); + listBuilder + .setPresetListsActive(q.answerType == Question.CATEGORICAL); + +// boolean preset = (q.answerType == Question.CATEGORICAL) ? true : false; +// System.out.println("Is question categorical? " + preset); +// + listBuilder + .setAdjacencyActive(q.questionType == Question.ALTER_PAIR_QUESTION); + + this.setSize(450, 400); + jOKButton.setVisible(true); + jCancelButton.setText("Cancel"); + + /* Pack since we've modified the GUI elements */ + this.pack(); + this.setVisible(true); + } +} + +/** + * $Log: CategoryInputPane.java,v $ Revision 1.1 2005/08/02 19:36:04 samag + * Initial checkin + * + * Revision 1.11 2004/04/11 00:24:48 admin Fixing headers + * + */ diff --git a/src/com/endlessloopsoftware/ego/author/EgoFrame.java b/src/com/endlessloopsoftware/ego/author/EgoFrame.java new file mode 100644 index 0000000..21511c0 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/author/EgoFrame.java @@ -0,0 +1,570 @@ +package com.endlessloopsoftware.ego.author; + +import java.awt.AWTEvent; +import java.awt.BorderLayout; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.Observable; +import java.util.Observer; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.text.DefaultEditorKit; + +import com.endlessloopsoftware.ego.Question; +import com.endlessloopsoftware.ego.Shared; +import com.endlessloopsoftware.ego.Study; +import com.endlessloopsoftware.elsutils.AboutBox; + +/** + *

+ * Title: Egocentric Network Researcher + *

+ *

+ * Description: Configuration Utilities for an Egocentric network study + *

+ *

+ * Copyright: Copyright (c) 2002 + *

+ *

+ * Company: Endless Loop Software + *

+ * + * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: EgoFrame.java,v 1.1 2005/08/02 19:36:04 samag Exp $ + */ + +public class EgoFrame extends JFrame implements Observer { + int lastTab = 0; + int curTab = 0; + + private JPanel contentPane; + private boolean waitCursor = false; + + private final JMenuBar jEgonetMenuBar = new JMenuBar(); + private final JMenu jMenuFile = new JMenu("File"); + private final JMenuItem jMenuFileNew = new JMenuItem("New Study"); + private final JMenuItem jMenuFileOpen = new JMenuItem("Open Study"); + private final JMenuItem jMenuFileClose = new JMenuItem("Close Study"); + private final JMenuItem jMenuFileImport = new JMenuItem( + "Import Questions..."); + private final JMenuItem jMenuFileExport = new JMenuItem( + "Export Questions..."); + private final JMenuItem jMenuFileSaveAs = new JMenuItem("Save Study As..."); + private final JMenuItem jMenuFileSave = new JMenuItem("Save Study"); + private final JMenuItem jMenuFileUpload = new JMenuItem("Upload Study"); + private final JMenuItem jMenuFileSelectStudy = new JMenuItem( + "Select Active Study"); + private final JMenuItem jMenuFileExit = new JMenuItem("Quit"); + private final JMenu jMenuEdit = new JMenu("Edit"); + private final JMenuItem jMenuEditCut = new JMenuItem( + new DefaultEditorKit.CutAction()); + private final JMenuItem jMenuEditCopy = new JMenuItem( + new DefaultEditorKit.CopyAction()); + private final JMenuItem jMenuEditPaste = new JMenuItem( + new DefaultEditorKit.PasteAction()); + private final JMenu jMenuHelp = new JMenu("Help"); + private final JMenuItem jMenuHelpAbout = new JMenuItem("About"); + + private final JTabbedPane jTabbedPane = new JTabbedPane(); + private final BorderLayout borderLayout1 = new BorderLayout(); + + private final StudyPanel study_panel = new StudyPanel(this); + + private final EgoQPanel[] questionPanel = { + null, + (EgoQPanel) new AuthoringQuestionPanel(Question.EGO_QUESTION), + (EgoQPanel) new PromptPanel(Question.ALTER_PROMPT), + (EgoQPanel) new AuthoringQuestionPanel(Question.ALTER_QUESTION), + (EgoQPanel) new AuthoringQuestionPanel(Question.ALTER_PAIR_QUESTION), }; + + // Construct the frame + public EgoFrame() { + enableEvents(AWTEvent.WINDOW_EVENT_MASK); + try { + jbInit(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + // Component initialization + private void jbInit() throws Exception { + // Listen for window closing + this.addWindowListener(new CloseListener()); + this.setDefaultCloseOperation(EXIT_ON_CLOSE); + + contentPane = (JPanel) this.getContentPane(); + contentPane.setLayout(borderLayout1); + this.setSize(new Dimension(815, 600)); + //this.setExtendedState(this.getExtendedState() | JFrame.MAXIMIZED_BOTH); + this.setTitle("Egocentric Network Study"); + + jMenuFileExit.setAccelerator(javax.swing.KeyStroke.getKeyStroke( + KeyEvent.VK_Q, Toolkit.getDefaultToolkit() + .getMenuShortcutKeyMask())); + jMenuFileNew.setAccelerator(javax.swing.KeyStroke.getKeyStroke( + KeyEvent.VK_N, Toolkit.getDefaultToolkit() + .getMenuShortcutKeyMask())); + jMenuFileOpen.setAccelerator(javax.swing.KeyStroke.getKeyStroke( + KeyEvent.VK_O, Toolkit.getDefaultToolkit() + .getMenuShortcutKeyMask())); + jMenuFileClose.setAccelerator(javax.swing.KeyStroke.getKeyStroke( + KeyEvent.VK_W, Toolkit.getDefaultToolkit() + .getMenuShortcutKeyMask())); + jMenuEditCopy.setAccelerator(javax.swing.KeyStroke.getKeyStroke( + KeyEvent.VK_C, Toolkit.getDefaultToolkit() + .getMenuShortcutKeyMask())); + jMenuEditCut.setAccelerator(javax.swing.KeyStroke.getKeyStroke( + KeyEvent.VK_X, Toolkit.getDefaultToolkit() + .getMenuShortcutKeyMask())); + jMenuEditPaste.setAccelerator(javax.swing.KeyStroke.getKeyStroke( + KeyEvent.VK_V, Toolkit.getDefaultToolkit() + .getMenuShortcutKeyMask())); + + jMenuEditCopy.setText("Copy"); + jMenuEditCut.setText("Cut"); + jMenuEditPaste.setText("Paste"); + + jMenuFile.add(jMenuFileNew); + jMenuFile.add(jMenuFileOpen); + jMenuFile.add(jMenuFileClose); + jMenuFile.addSeparator(); + jMenuFile.add(jMenuFileImport); + jMenuFile.add(jMenuFileExport); + jMenuFile.addSeparator(); + jMenuFile.add(jMenuFileSave); + jMenuFile.add(jMenuFileSaveAs); + jMenuFile.addSeparator(); + jMenuFile.add(jMenuFileUpload); + jMenuFile.add(jMenuFileSelectStudy); + jMenuFile.addSeparator(); + jMenuFile.add(jMenuFileExit); + + jMenuEdit.add(jMenuEditCut); + jMenuEdit.add(jMenuEditCopy); + jMenuEdit.add(jMenuEditPaste); + jMenuHelp.add(jMenuHelpAbout); + jEgonetMenuBar.add(jMenuFile); + jEgonetMenuBar.add(jMenuEdit); + jEgonetMenuBar.add(jMenuHelp); + this.setJMenuBar(jEgonetMenuBar); + + jTabbedPane.setTabPlacement(JTabbedPane.TOP); + jTabbedPane.add(study_panel, "Study"); + + for (int i = Question.MIN_QUESTION_TYPE; i <= Question.MAX_QUESTION_TYPE; i++) { + jTabbedPane.add(questionPanel[i], Question.questionTypeString(i)); + } + contentPane.add(jTabbedPane); + + /*********************************************************************** + * Action Listeners for Menu Events + */ + jMenuFileNew.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + jMenuFileNew_actionPerformed(e); + } + }); + + jMenuFileOpen.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + jMenuFileOpen_actionPerformed(e); + } + }); + + jMenuFileClose.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + jMenuFileClose_actionPerformed(e); + } + }); + + jMenuFileSave.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + jMenuFileSave_actionPerformed(e); + } + }); + + jMenuFileSaveAs.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + jMenuFileSaveAs_actionPerformed(e); + } + }); + + jMenuFileUpload.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + jMenuFileUpload_actionPerformed(e); + } + }); + + jMenuFileSelectStudy.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + jMenuFileSelectStudy_actionPerformed(e); + } + }); + + jMenuFileImport.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + jMenuFileImport_actionPerformed(e); + } + }); + + jMenuFileExport.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + jMenuFileExport_actionPerformed(e); + } + }); + + jMenuFileExit.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + jMenuFileExit_actionPerformed(e); + } + }); + + jMenuHelpAbout.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + jMenuHelpAbout_actionPerformed(e); + } + }); + + /*********************************************************************** + * Change Listener for tabs + */ + jTabbedPane.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + jTabbedPane_stateChanged(e); + } + }); + + /* Fill panel, initialize frame */ + EgoNet.study = new Study(); + fillCurrentPanel(); + EgoNet.study.setModified(false); + updateMenus(); + } + + /*************************************************************************** + * Updates menus to take dirty question and study into account + */ + public void updateMenus() { + if (EgoNet.storage.getStudyFile() == null) { + jMenuFileImport.setEnabled(false); + jMenuFileClose.setEnabled(false); + jMenuFileSave.setEnabled(false); + jMenuFileSaveAs.setEnabled(false); + jMenuFileUpload.setEnabled(false); + jMenuFileSelectStudy.setEnabled(true); + jMenuFileExport.setEnabled(false); + jTabbedPane.setEnabledAt(1, false); + jTabbedPane.setEnabledAt(2, false); + jTabbedPane.setEnabledAt(3, false); + jTabbedPane.setEnabledAt(4, false); + jTabbedPane.setSelectedIndex(0); + } else { + jMenuFileImport.setEnabled(true); + jMenuFileClose.setEnabled(true); + jMenuFileSave.setEnabled(EgoNet.study.isCompatible() + && EgoNet.study.isModified()); + jMenuFileSaveAs.setEnabled(true); + jMenuFileUpload.setEnabled(true); + jMenuFileSelectStudy.setEnabled(true); + jMenuFileExport.setEnabled(true); + jTabbedPane.setEnabledAt(1, true); + jTabbedPane.setEnabledAt(2, true); + jTabbedPane.setEnabledAt(3, true); + jTabbedPane.setEnabledAt(4, true); + } + } + + /** + * New Study menu handler + * + * @param e + * Menu UI Event + */ + private void jMenuFileNew_actionPerformed(ActionEvent e) { + boolean ok = closeStudyFile(); + + if (ok) { + EgoNet.storage.newStudyFiles(); + fillCurrentPanel(); + EgoNet.study.setModified(false); + EgoNet.study.setCompatible(true); + EgoNet.study.addObserver(this); + updateMenus(); + } + } + + /*************************************************************************** + * Open Study menu handler + * + * @param e + * Menu UI Event + */ + private void jMenuFileOpen_actionPerformed(ActionEvent e) { + boolean ok = closeStudyFile(); + + if (ok) { + EgoNet.storage.selectStudy(); + fillCurrentPanel(); + EgoNet.study.setModified(false); + EgoNet.study.setCompatible(true); + EgoNet.study.addObserver(this); + updateMenus(); + } + } + + private void jMenuFileClose_actionPerformed(ActionEvent e) { + boolean ok = closeStudyFile(); + + if (ok) { + EgoNet.storage.setStudyFile(null); + EgoNet.study = new Study(); + fillCurrentPanel(); + EgoNet.study.addObserver(this); + EgoNet.study.setModified(false); + } + } + + private void jMenuFileImport_actionPerformed(ActionEvent e) { + EgoNet.storage.importQuestions(); + fillCurrentPanel(); + } + + private void jMenuFileExport_actionPerformed(ActionEvent e) { + EgoNet.storage.exportQuestions(); + } + + private void jMenuFileUpload_actionPerformed(ActionEvent e) { + JDialog storeDialog = new StoreStudyDialog(this); + storeDialog.pack(); + /* + * this.show(); Code above deprecated. Code change done Changed by sonam + * on 08/20/2007 + */ + storeDialog.setVisible(true); + } + + private void jMenuFileSelectStudy_actionPerformed(ActionEvent e) { + JDialog storeDialog = new SetActiveStudyDialog(this); + storeDialog.pack(); + /* + * this.show(); Code above deprecated. Code change done Changed by sonam + * on 08/20/2007 + */ + storeDialog.setVisible(true); + } + + private void jMenuFileSave_actionPerformed(ActionEvent e) { + if (EgoNet.storage.getStudyFile() == null) { + jMenuFileSaveAs_actionPerformed(e); + } else { + EgoNet.storage.saveStudyFile(); + EgoNet.study.setModified(false); + } + } + + private void jMenuFileSaveAs_actionPerformed(ActionEvent e) { + EgoNet.storage.saveAsStudyFile(); + fillStudyPanel(); + EgoNet.study.addObserver(this); + EgoNet.study.setModified(false); + EgoNet.study.setCompatible(true); + } + + // File | Exit action performed + private void jMenuFileExit_actionPerformed(ActionEvent e) { + boolean exit = closeStudyFile(); + + if (exit) { + System.exit(0); + } + } + + // Help | About action performed + public void jMenuHelpAbout_actionPerformed(ActionEvent e) { + + JOptionPane.showMessageDialog(this, + "Egonet is an egocentric network study tool." + + "\n\nThanks to: Dr. Chris McCarty, University of Florida", + "About Egonet", JOptionPane.PLAIN_MESSAGE); + + } + + /** + * Closes question file. If changes made gives user the option of saving. + * + * @return False iff user cancels save, True otherwise + */ + public boolean closeStudyFile() { + boolean exit = true; + + if (EgoNet.study.isModified()) { + int confirm = JOptionPane + .showConfirmDialog( + this, + "There are unsaved changes to the study. Would you like to save the study now?", + "Save Study Changes", + JOptionPane.YES_NO_CANCEL_OPTION); + + if (confirm == JOptionPane.YES_OPTION) { + jMenuFileSave_actionPerformed(null); + } else if (confirm == JOptionPane.CANCEL_OPTION) { + exit = false; + } + } + + return exit; + } + + public void fillCurrentPanel() { + boolean sd = EgoNet.study.isModified(); + boolean sc = EgoNet.study.isCompatible(); + + if (curTab == Question.STUDY_CONFIG) { + study_panel.fillPanel(); + } else if ((curTab >= Question.MIN_QUESTION_TYPE) + && (curTab <= Question.MAX_QUESTION_TYPE)) { + questionPanel[curTab].fillPanel(); + } + + EgoNet.study.setModified(sd); + EgoNet.study.setCompatible(sc); + } + + public void fillStudyPanel() { + boolean sd = EgoNet.study.isModified(); + + if (curTab == Question.STUDY_CONFIG) { + study_panel.fillPanel(); + } + + EgoNet.study.setModified(sd); + } + + private void jTabbedPane_stateChanged(ChangeEvent e) { + lastTab = curTab; + curTab = jTabbedPane.getSelectedIndex(); + + if ((lastTab == Question.STUDY_CONFIG) && (curTab != lastTab)) { + EgoNet.study.validateQuestions(); + } + + if ((curTab >= Question.MIN_QUESTION_TYPE) + && (curTab <= Question.MAX_QUESTION_TYPE)) { + questionPanel[curTab].fillPanel(); + } else if (curTab == Question.STUDY_CONFIG) { + study_panel.fillPanel(); + } + } + + protected void setWaitCursor(boolean waitCursor) { + this.waitCursor = waitCursor; + + if (waitCursor) { + this.getGlassPane().setVisible(true); + this.getGlassPane().setCursor( + Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } else { + this.getGlassPane().setCursor( + Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + this.getGlassPane().setVisible(false); + } + } + + class CloseListener extends WindowAdapter { + /* + * (non-Javadoc) + * + * @see java.awt.event.WindowListener#windowClosed(java.awt.event.WindowEvent) + */ + public void windowClosing(WindowEvent arg0) { + jMenuFileExit_actionPerformed(null); + } + } + + /* + * (non-Javadoc) + * + * @see java.util.Observer#update(java.util.Observable, java.lang.Object) + */ + public void update(Observable o, Object arg) { + updateMenus(); + } +} + +/******************************************************************************* + * $Id: EgoFrame.java,v 1.1 2005/08/02 19:36:04 samag Exp $ + * + * $Log: EgoFrame.java,v $ Revision 1.1 2005/08/02 19:36:04 samag Initial + * checkin + * + * Revision 1.12 2004/04/11 00:24:48 admin Fixing headers + * + * Revision 1.11 2004/04/08 15:06:06 admin EgoClient now creates study summaries + * from Server EgoAuthor now sets active study on server + * + * Revision 1.10 2004/03/28 17:31:31 admin More error handling when uploading + * study to server Server URL selection dialog for upload + * + * Revision 1.9 2004/03/23 14:58:48 admin Update UI Study creation now occurs in + * instantiators + * + * Revision 1.8 2004/03/21 14:00:38 admin Cleaned up Question Panel Layout using + * FOAM + * + * Revision 1.7 2004/03/10 14:32:39 admin Adding client library cleaning up code + * + * Revision 1.6 2004/02/10 20:10:42 admin Version 2.0 beta 3 + * + * Revision 1.5 2004/01/23 13:36:07 admin Updating Libraries Allowing upload to + * web server + * + * Revision 1.4 2003/12/05 19:15:43 admin Extracting Study + * + * Revision 1.3 2003/12/04 15:14:08 admin Merging EgoNet and EgoClient projects + * so that they can share some common classes more easily. + * + * Revision 1.2 2003/11/25 19:25:43 admin Warn before closing window + * + * Revision 1.1.1.1 2003/06/08 15:09:40 admin Egocentric Network Survey + * Authoring Module + * + * Revision 1.13 2002/08/30 16:50:27 admin Using Selections + * + * Revision 1.12 2002/08/11 22:26:05 admin Final Statistics window, new file + * handling + * + * Revision 1.11 2002/08/08 17:07:24 admin Preparing to change file system + * + * Revision 1.10 2002/07/24 14:17:08 admin xml files, links + * + * Revision 1.9 2002/07/18 14:43:05 admin New Alter Prompt Panel, packages + * + * Revision 1.8 2002/06/26 15:43:42 admin More selection dialog work File + * loading fixes + * + * Revision 1.7 2002/06/25 15:41:01 admin Lots of UI work + * + * Revision 1.6 2002/06/21 22:47:12 admin question lists working again + * + * Revision 1.5 2002/06/21 21:52:50 admin Many changes to event handling, file + * handling + * + * Revision 1.4 2002/06/19 01:57:04 admin Much UI work done + * + * Revision 1.3 2002/06/16 17:53:10 admin Working with files + * + * Revision 1.2 2002/06/15 14:19:50 admin Initial Checkin of question and survey + * General file system work + * + */ diff --git a/src/com/endlessloopsoftware/ego/author/EgoNet.java b/src/com/endlessloopsoftware/ego/author/EgoNet.java new file mode 100644 index 0000000..220c4bf --- /dev/null +++ b/src/com/endlessloopsoftware/ego/author/EgoNet.java @@ -0,0 +1,96 @@ +package com.endlessloopsoftware.ego.author; + +import java.awt.Dimension; +import java.awt.Toolkit; + +import com.endlessloopsoftware.ego.Shared; +import com.endlessloopsoftware.ego.Study; + +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: EgoNet.java,v 1.1 2005/08/02 19:36:04 samag Exp $ + */ + + +public class EgoNet +{ + public static final EgoStore storage = new EgoStore(); + public static Study study = new Study(); + public static final EgoFrame frame = new EgoFrame(); + + //Construct the application + public EgoNet() + { + frame.validate(); + + //Center the window + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Dimension frameSize = frame.getSize(); + if (frameSize.height > screenSize.height) + { + frameSize.height = screenSize.height; + } + if (frameSize.width > screenSize.width) + { + frameSize.width = screenSize.width; + } + frame.setLocation( + (screenSize.width - frameSize.width) / 2, + (screenSize.height - frameSize.height) / 2); + frame.setVisible(true); + } + + //Main method + public static void main(String[] args) + { + Shared.configureUI(); + new EgoNet(); + } +} + +/** + * $Log: EgoNet.java,v $ + * Revision 1.1 2005/08/02 19:36:04 samag + * Initial checkin + * + * Revision 1.6 2004/04/11 00:24:48 admin + * Fixing headers + * + * Revision 1.5 2004/03/23 14:58:48 admin + * Update UI + * Study creation now occurs in instantiators + * + * Revision 1.4 2003/12/05 19:15:43 admin + * Extracting Study + * + * Revision 1.3 2003/12/04 15:14:08 admin + * Merging EgoNet and EgoClient projects so that they can share some + * common classes more easily. + * + * Revision 1.2 2003/11/25 19:25:44 admin + * Warn before closing window + * + * Revision 1.1.1.1 2003/06/08 15:09:40 admin + * Egocentric Network Survey Authoring Module + * + * Revision 1.5 2002/06/30 15:59:18 admin + * Moving questions in lists, between lists + * Better category input + * + * Revision 1.4 2002/06/26 00:10:48 admin + * UI Work including base question coloring and category selections + * + * Revision 1.3 2002/06/25 15:41:01 admin + * Lots of UI work + * + * Revision 1.2 2002/06/15 14:19:50 admin + * Initial Checkin of question and survey + * General file system work + * + */ \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/author/EgoQPanel.java b/src/com/endlessloopsoftware/ego/author/EgoQPanel.java new file mode 100644 index 0000000..b4ec34c --- /dev/null +++ b/src/com/endlessloopsoftware/ego/author/EgoQPanel.java @@ -0,0 +1,17 @@ +package com.endlessloopsoftware.ego.author; + +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + */ +import javax.swing.JPanel; + +public abstract class EgoQPanel extends JPanel +{ + abstract public void fillPanel(); + abstract public void clearPanel(); +} \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/author/EgoStore.java b/src/com/endlessloopsoftware/ego/author/EgoStore.java new file mode 100644 index 0000000..2c21ebe --- /dev/null +++ b/src/com/endlessloopsoftware/ego/author/EgoStore.java @@ -0,0 +1,764 @@ +package com.endlessloopsoftware.ego.author; + + +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: EgoStore.java,v 1.1 2005/08/02 19:36:04 samag Exp $ + * + * + */ + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.prefs.Preferences; + +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.filechooser.FileFilter; + +import com.endlessloopsoftware.ego.Question; +import com.endlessloopsoftware.ego.Shared; +import com.endlessloopsoftware.ego.Study; +import com.endlessloopsoftware.elsutils.DateUtils; +import com.endlessloopsoftware.elsutils.files.DirList; +import com.endlessloopsoftware.elsutils.files.ExtensionFileFilter; +import com.endlessloopsoftware.elsutils.files.FileCreateException; +import com.endlessloopsoftware.elsutils.files.FileReadException; +import com.endlessloopsoftware.elsutils.files.FileWriteException; + +import electric.xml.Document; +import electric.xml.Element; +import electric.xml.Elements; + +/**** + * Handles IO for the EgoNet program + * Tracks data files and changes to those files + */ +public class EgoStore +{ + private File studyFile = null; + private boolean studyFileInUse = false; + + private static final String[] questionExtensions = { "qst", "qtp"}; + private static FileFilter readQuestionFilter = (FileFilter) new ExtensionFileFilter("Question Files", questionExtensions[0]); + private static FileFilter writeQuestionFilter = (FileFilter) new ExtensionFileFilter("Question Templates", questionExtensions); + private static FileFilter studyFilter = new ExtensionFileFilter("Study Files", "ego"); + + private static final String FILE_PREF = "FILE_PREF"; + + /** + * Sets parent frame + * + * @param frame + * parent + */ + public EgoStore() + { + } + + /************************************************************************************************************************************************************ + * Returns study file + * + * @return studyFile file containing study overview information + */ + public File getStudyFile() + { + return (studyFile); + } + + /************************************************************************************************************************************************************ + * Returns study file + * + * @return studyFile file containing study overview information + */ + public boolean getStudyInUse() + { + return (studyFileInUse); + } + + /************************************************************************************************************************************************************ + * Sets baseQuestionFile variable and notifies observers of change to study + * + * @param f + * question file + */ + public void setStudyFile(File f) + { + studyFile = f; + } + + /************************************************************************************************************************************************************ + * Select a directory in which to store project related files Create subdirectories if needed. + */ + public void newStudyFiles() + { + Preferences prefs = Preferences.userNodeForPackage(EgoNet.class); + JFileChooser jNewStudyChooser = new JFileChooser(); + File dirFile, newStudyFile; + String projectPath = null; + String projectName = null; + + jNewStudyChooser.addChoosableFileFilter(studyFilter); + jNewStudyChooser.setDialogTitle("Select Study Path"); + + if (getStudyFile() != null) + { + jNewStudyChooser.setCurrentDirectory(getStudyFile().getParentFile()); + } + else + { + File directory = new File(prefs.get(FILE_PREF, ".")); + jNewStudyChooser.setCurrentDirectory(directory); + } + + try + { + if (JFileChooser.APPROVE_OPTION == jNewStudyChooser.showSaveDialog(EgoNet.frame)) + { + projectPath = jNewStudyChooser.getSelectedFile().getParent(); + projectName = jNewStudyChooser.getSelectedFile().getName(); + + if (projectName.indexOf(".") != -1) + { + projectName = projectName.substring(0, projectName.indexOf(".")); + } + + try + { + String folder = projectPath.substring(projectPath.lastIndexOf(File.separator) + 1); + if (!folder.equals(projectName)) + { + dirFile = new File(projectPath, projectName); + dirFile.mkdir(); + projectPath = dirFile.getPath(); + } + } + catch (SecurityException e) + { + JOptionPane.showMessageDialog( + EgoNet.frame, + "Unable to create study directories.", + "New Study Error", + JOptionPane.ERROR_MESSAGE); + throw new FileCreateException(false); + } + + try + { + newStudyFile = new File(projectPath, projectName); + newStudyFile = ((ExtensionFileFilter) studyFilter).getCorrectFileName(newStudyFile); + if (!newStudyFile.createNewFile()) + { + int confirm = + JOptionPane.showConfirmDialog( + EgoNet.frame, + "

Study already exists at this location.

" + "

Shall I overwrite it?

", + "Overwrite Study File", + JOptionPane.OK_CANCEL_OPTION); + + if (confirm != JOptionPane.OK_OPTION) + { + throw new FileCreateException(true); + } + } + + /* Clean out study variables */ + EgoNet.study = new Study(); + setStudyFile(newStudyFile); + EgoNet.study.setStudyName(projectName); + + /* Write out default info */ + writeStudy(newStudyFile, new Long(System.currentTimeMillis())); + studyFileInUse = false; + + // Store location in prefs file + prefs.put(FILE_PREF, newStudyFile.getParent()); + } + catch (java.io.IOException e) + { + JOptionPane.showMessageDialog( + EgoNet.frame, + "Unable to create study file.", + "File Error", + JOptionPane.ERROR_MESSAGE); + throw new FileCreateException(false); + } + + try + { + dirFile = new File(projectPath, "Statistics"); + dirFile.mkdir(); + + dirFile = new File(projectPath, "Interviews"); + dirFile.mkdir(); + } + catch (SecurityException e) + { + JOptionPane.showMessageDialog( + EgoNet.frame, + "Unable to create study directories.", + "New Study Error", + JOptionPane.ERROR_MESSAGE); + throw new FileCreateException(false); + } + } + } + catch (FileCreateException e) + { + if (e.report) + { + JOptionPane.showMessageDialog(EgoNet.frame, "Study not created."); + } + + setStudyFile(null); + } + } + + /************************************************************************************************************************************************************ + * Select a directory in which to store project related files Create subdirectories if needed. + */ + public void selectStudy() + { + Preferences prefs = Preferences.userNodeForPackage(EgoNet.class); + JFileChooser jNewStudyChooser = new JFileChooser(); + File f; + + jNewStudyChooser.addChoosableFileFilter(studyFilter); + jNewStudyChooser.setDialogTitle("Select Study"); + + if (getStudyFile() != null) + { + jNewStudyChooser.setCurrentDirectory(getStudyFile().getParentFile()); + } + else + { + jNewStudyChooser.setCurrentDirectory(new File(prefs.get(FILE_PREF, "."))); + } + + if (JFileChooser.APPROVE_OPTION == jNewStudyChooser.showOpenDialog(EgoNet.frame)) + { + f = jNewStudyChooser.getSelectedFile(); + + try + { + if (!f.canRead()) + { + throw new FileReadException(); + } + else + { + readStudy(f); + setStudyFile(f); + + // Store location in prefs file + prefs.put(FILE_PREF, f.getParent()); + } + } + catch (Exception e) + { + e.printStackTrace(); + + setStudyFile(null); + JOptionPane.showMessageDialog(null, "Unable to read study file.", "File Error", JOptionPane.ERROR_MESSAGE); + } + } + } + + /************************************************************************************************************************************************************ + * Select a question file to use for custom questions + */ + public void importQuestions() + { + JFileChooser jNewStudyChooser = new JFileChooser(); + File newFile; + // FileReader file = null; + Study newStudy = null; + + jNewStudyChooser.setCurrentDirectory(DirList.getLibraryDirectory()); + jNewStudyChooser.addChoosableFileFilter(readQuestionFilter); + jNewStudyChooser.setDialogTitle("Select Custom Questions File"); + + if (JFileChooser.APPROVE_OPTION == jNewStudyChooser.showOpenDialog(EgoNet.frame)) + { + newFile = jNewStudyChooser.getSelectedFile(); + + try + { + if (!newFile.canRead()) + { + throw (new FileReadException()); + } + + /* read question file here */ + Document document = new Document(newFile); + readQuestions(document); + } + catch (Exception e) + { + JOptionPane.showMessageDialog( + null, + "Unable to read question file.", + "File Error", + JOptionPane.ERROR_MESSAGE); + } + } + } + + /************************************************************************************************************************************************************ + * Save study information to a file with a new name + */ + public void saveStudyFile() + { + // FileWriter file = null; + PrintWriter out = null; + File studyFile = getStudyFile(); + + try + { + if (!studyFile.canWrite()) + { + throw (new FileWriteException()); + } + + writeStudy(studyFile, new Long(EgoNet.study.getStudyId())); + } + catch (Exception e) + { + JOptionPane.showMessageDialog(EgoNet.frame, "Unable to write to study file. Study not saved."); + } + } + + /************************************************************************************************************************************************************ + * Save question information to a file with a new name + */ + public void exportQuestions() + { + JFileChooser jNewQuestionsChooser = new JFileChooser(); + File newQuestionFile; + + jNewQuestionsChooser.setCurrentDirectory(new File(getStudyFile().getParent(), "/Questions/")); + jNewQuestionsChooser.addChoosableFileFilter(writeQuestionFilter); + jNewQuestionsChooser.setDialogTitle("Save Custom Questions As..."); + + if (JFileChooser.APPROVE_OPTION == jNewQuestionsChooser.showSaveDialog(EgoNet.frame)) + { + try + { + newQuestionFile = + ((ExtensionFileFilter) writeQuestionFilter).getCorrectFileName(jNewQuestionsChooser.getSelectedFile()); + if (!newQuestionFile.createNewFile()) + { + int confirm = + JOptionPane.showConfirmDialog( + EgoNet.frame, + "

Question File already exists at this location.

" + + "

Shall I overwrite it?

", + "Overwrite Questions File", + JOptionPane.OK_CANCEL_OPTION); + + if (confirm != JOptionPane.OK_OPTION) + { + throw new FileCreateException(true); + } + } + + writeAllQuestions(newQuestionFile); + } + catch (Exception e) + { + JOptionPane.showMessageDialog( + EgoNet.frame, + "Unable to create question file.", + "File Error", + JOptionPane.ERROR_MESSAGE); + } + } + } + + /************************************************************************************************************************************************************ + * Save study info and questions as a package + */ + public void saveAsStudyFile() + { + JFileChooser jNewQuestionsChooser = new JFileChooser("Save Study As..."); + File newStudyFile; + // FileWriter file = null; + PrintWriter out = null; + boolean complete = false; + + jNewQuestionsChooser.setCurrentDirectory(getStudyFile().getParentFile()); + jNewQuestionsChooser.addChoosableFileFilter(studyFilter); + + while (!complete) + { + if (JFileChooser.APPROVE_OPTION == jNewQuestionsChooser.showSaveDialog(EgoNet.frame)) + { + try + { + int confirm = JOptionPane.OK_OPTION; + newStudyFile = ((ExtensionFileFilter) studyFilter).getCorrectFileName(jNewQuestionsChooser.getSelectedFile()); + + if (!newStudyFile.createNewFile()) + { + if (newStudyFile.canWrite()) + { + confirm = JOptionPane.showConfirmDialog(EgoNet.frame, + "

A Study File already exists at this location.

" + + "

Shall I overwrite it?

", "Overwrite Study Package File", + JOptionPane.OK_CANCEL_OPTION); + } + else + { + confirm = JOptionPane.showConfirmDialog(EgoNet.frame, + "

An Active Study File already exists at this location.

" + + "

If you overwrite it, any interviews created with it will be unreadable!

" + + "

Shall I overwrite it?

", "Overwrite Study Package File", + JOptionPane.OK_CANCEL_OPTION); + } + } + + if (confirm == JOptionPane.OK_OPTION) + { + if (!newStudyFile.canWrite()) { throw (new FileWriteException()); } + + writeStudy(newStudyFile, new Long(System.currentTimeMillis())); + setStudyFile(newStudyFile); + studyFileInUse = false; + complete = true; + + // Store location in prefs file + Preferences prefs = Preferences.userNodeForPackage(EgoNet.class); + prefs.put(FILE_PREF, newStudyFile.getParent()); + } + } + catch (FileWriteException e) + { + JOptionPane.showMessageDialog(EgoNet.frame, "Unable to write to study file. Study not saved."); + } + catch (java.io.IOException e) + { + JOptionPane.showMessageDialog(EgoNet.frame, "Unable to write to study file. Study not saved."); + } + } + else + { + complete = true; + } + } + } + + /********************************************************************************* + * Reads in study information from an XML DOM + * and arrays of question orders + * + * @param document + * XML tree containing study data + */ + private void readStudyData(Document document) + { + // String data; + + Element root = document.getRoot(); + root = root.getElement("Study"); + + if (root.getElement("name") != null) + { + EgoNet.study.setStudyName(root.getTextString("name")); + } + + if (root.getElement("numalters") != null) + { + EgoNet.study.setNumAlters(root.getInt("numalters")); + } + + Elements elements = root.getElements("questionorder"); + while (elements.hasMoreElements()) + { + int qOrderId; + List questionOrder; + Elements ids; + + Element element = elements.next(); + qOrderId = Integer.parseInt(element.getAttribute("questiontype")); + questionOrder = (EgoNet.study.getQuestionOrderArray())[qOrderId]; + + ids = element.getElements("id"); + while (ids.hasMoreElements()) + { + questionOrder.add(new Long(ids.next().getLong())); + } + } + } + + /************************************************************************************************************************************************************ + * Writes Study information to a file for later retrieval Includes files paths and arrays of question orders + * + * @param f File into which to write study @todo prune order lists, possibly need to load question files to do this + * @throws IOException + */ + private void writeStudy(File f, Long id) throws IOException + { + Document document = new Document(); + + document.setEncoding("UTF-8"); + document.setVersion("1.0"); + Element studyElement = document.setRoot("Package"); + studyElement.setAttribute("Id", id.toString()); + studyElement.setAttribute("InUse", studyFileInUse ? "Y" : "N"); + studyElement.setAttribute("Creator", Shared.version); + studyElement.setAttribute("Updated", DateUtils.getDateString(Calendar.getInstance().getTime(), "dd/MM/yyyy hh:mm a")); + + EgoNet.study.writeStudyData(studyElement); + EgoNet.study.writeAllQuestionData(studyElement); + + document.write(f); + } + + /************************************************************************************************************************************************************ + * Reads in questions from an XML like input file Includes files paths and arrays of question orders + * + * @param document XML tree containing question list + */ + private void readQuestions(Document document) + { + // File f; + Element root, question; + Elements questions; + + /** + * Load new questions from array + */ + try + { + /** + * Parse XML file + */ + root = document.getRoot(); + root = root.getElement("QuestionList"); + questions = root.getElements("Question"); + + while (questions.hasMoreElements()) + { + try + { + Question q = new Question(questions.next()); + + if (q != null) + { + /* Question complete, add it */ + EgoNet.study.addQuestion(q); + } + } + catch (Exception ex) + { + // ex.printStackTrace(); + throw ex; + } + } + } + catch (Exception e) + { + JOptionPane.showMessageDialog( + EgoNet.frame, + "Unable to read question file", + "Question Reading Error", + JOptionPane.ERROR_MESSAGE); + } + } + + /************************************************************************************************************************************************************ + * Writes all questions to a package file for later use + * + * @param f + * File to write data to + * @throws IOException + */ + private void writeAllQuestions(File f) throws IOException + { + Document document = new Document(); + + //document.addChild( new XMLDecl( "1.0", "UTF-8" ) ); + document.setEncoding("UTF-8"); + document.setVersion("1.0"); + Element study = document.setRoot("QuestionFile"); + study.setAttribute("Id", Long.toString(new Date().getTime())); + + EgoNet.study.writeAllQuestionData(study); + + document.write(f); + } + + /************************************************************************************************************************************************************ + * Reads in study information from an XML like input file Includes files paths and arrays of question orders + * + * @param file + * XML file from which to read study + */ + public void readStudy(File file) + { + if (file != null) + { + try + { + Document document = new Document(file); + Element root = document.getRoot(); + String inUse = root.getAttribute("InUse"); + + if ((inUse != null) && inUse.equals("Y")) + { + studyFileInUse = true; + + JOptionPane.showMessageDialog( + EgoNet.frame, + "This study has already been used for at least one interview.\n" + + "You may change the text of questions while still using previously generated interview files. However, \n" + + "if you add, delete, reorder, or modify the answer types of any questions you will no longer be able to use \n" + + "it to view existing interview files.", + "File In Use", + JOptionPane.WARNING_MESSAGE); + } + + EgoNet.study = new Study(document); + EgoNet.study.setInUse(studyFileInUse); + } + catch (Exception e) + { + JOptionPane.showMessageDialog( + EgoNet.frame, + "Unable to read this study file", + "Study Reading Error", + JOptionPane.ERROR_MESSAGE); + + EgoNet.study = new Study(); + } + } + } +} + + +/******************** + * + * $Log: EgoStore.java,v $ + * Revision 1.1 2005/08/02 19:36:04 samag + * Initial checkin + * + * Revision 1.16 2004/04/11 00:24:48 admin + * Fixing headers + * + * Revision 1.15 2004/04/02 20:02:51 admin + * Maintaining InUse State in study files + * + * Revision 1.14 2004/04/02 19:48:58 admin + * Keep Study Id when possible + * Store updated time in file + * + * Revision 1.13 2004/04/01 21:50:52 admin + * Aborting interview if unable to write answers to file + * + * Revision 1.12 2004/03/23 14:58:48 admin + * Update UI + * Study creation now occurs in instantiators + * + * Revision 1.11 2004/03/22 00:00:34 admin + * Extended text entry area + * Started work on importing studies from server + * + * Revision 1.10 2004/03/21 20:29:37 admin + * Warn before making incompatible changes to in use study file + * + * Revision 1.9 2004/03/21 14:00:38 admin + * Cleaned up Question Panel Layout using FOAM + * + * Revision 1.8 2004/02/26 21:19:17 admin + * adding jardescs + * + * Revision 1.7 2004/02/10 20:10:43 admin + * Version 2.0 beta 3 + * + * Revision 1.6 2004/01/23 13:36:07 admin + * Updating Libraries + * Allowing upload to web server + * + * Revision 1.5 2003/12/05 19:15:43 admin + * Extracting Study + * + * Revision 1.4 2003/12/04 15:14:08 admin + * Merging EgoNet and EgoClient projects so that they can share some + * common classes more easily. + * + * Revision 1.3 2003/11/25 19:29:53 admin + * Formatting + * + * Revision 1.2 2003/11/25 19:25:44 admin + * Warn before closing window + * + * Revision 1.1.1.1 2003/06/08 15:09:40 admin + * Egocentric Network Survey Authoring Module + * + * Revision 1.17 2002/09/01 20:06:16 admin + * Structural question now selected in client. Visual feedback for alter pair + * categorical questions. + * + * Revision 1.16 2002/08/30 09:30:37 admin + * Allowing user to select study file name. Using it for study name. + * + * Revision 1.15 2002/08/11 22:26:05 admin + * Final Statistics window, new file handling + * + * Revision 1.14 2002/08/08 17:07:25 admin + * Preparing to change file system + * + * Revision 1.13 2002/07/25 14:54:24 admin + * Question Links + * + * Revision 1.12 2002/07/24 14:17:09 admin + * xml files, links + * + * Revision 1.11 2002/07/18 14:43:06 admin + * New Alter Prompt Panel, packages + * + * Revision 1.10 2002/06/30 15:59:18 admin + * Moving questions in lists, between lists + * Better category input + * + * Revision 1.9 2002/06/26 15:43:42 admin + * More selection dialog work + * File loading fixes + * + * Revision 1.8 2002/06/26 00:10:48 admin + * UI Work including base question coloring and category selections + * + * Revision 1.7 2002/06/25 15:41:01 admin + * Lots of UI work + * + * Revision 1.6 2002/06/21 22:47:12 admin + * question lists working again + * + * Revision 1.5 2002/06/21 21:52:50 admin + * Many changes to event handling, file handling + * + * Revision 1.4 2002/06/19 01:57:04 admin + * Much UI work done + * + * Revision 1.3 2002/06/16 17:52:01 admin + * New Project, Open Project methods + * DirList class w/method + * + * Revision 1.2 2002/06/15 14:19:50 admin + * Initial Checkin of question and survey + * General file system work + * + * Revision 1.1 2002/06/14 20:34:35 admin + * Created + * + */ diff --git a/src/com/endlessloopsoftware/ego/author/NoTabTextArea.java b/src/com/endlessloopsoftware/ego/author/NoTabTextArea.java new file mode 100644 index 0000000..a285ee9 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/author/NoTabTextArea.java @@ -0,0 +1,28 @@ +package com.endlessloopsoftware.ego.author; + +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + */ + +import java.awt.KeyboardFocusManager; +import java.util.Collections; + +import javax.swing.JTextArea; + +/** + * Extends JTextArea to make tabs focus change events in question text areas + */ +public class NoTabTextArea extends JTextArea +{ + public NoTabTextArea() + { + super(); + setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, Collections.EMPTY_SET); + setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, Collections.EMPTY_SET); + } +} \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/author/PromptPanel.java b/src/com/endlessloopsoftware/ego/author/PromptPanel.java new file mode 100644 index 0000000..d8b88f3 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/author/PromptPanel.java @@ -0,0 +1,460 @@ +package com.endlessloopsoftware.ego.author; + +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: PromptPanel.java,v 1.1 2005/08/02 19:36:03 samag Exp $ + * + * $Log: PromptPanel.java,v $ + * Revision 1.1 2005/08/02 19:36:03 samag + * Initial checkin + * + * Revision 1.6 2004/04/11 00:24:48 admin + * Fixing headers + * + * Revision 1.5 2004/03/21 14:00:38 admin + * Cleaned up Question Panel Layout using FOAM + * + * Revision 1.4 2003/12/05 19:15:43 admin + * Extracting Study + * + * Revision 1.3 2003/12/04 15:14:08 admin + * Merging EgoNet and EgoClient projects so that they can share some + * common classes more easily. + * + * Revision 1.2 2003/11/25 19:25:43 admin + * Warn before closing window + * + * Revision 1.1.1.1 2003/06/08 15:09:40 admin + * Egocentric Network Survey Authoring Module + * + * Revision 1.4 2002/08/11 22:26:05 admin + * Final Statistics window, new file handling + * + * Revision 1.3 2002/08/08 17:07:25 admin + * Preparing to change file system + * + * Revision 1.2 2002/07/24 14:17:08 admin + * xml files, links + * + * Revision 1.1 2002/07/18 14:43:06 admin + * New Alter Prompt Panel, packages + * + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; + +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 org.egonet.exceptions.DuplicateQuestionException; + +import com.endlessloopsoftware.ego.Question; + +/** + * Generic Panel creation and handling routines for question editing + */ +public class PromptPanel extends EgoQPanel +{ + private final int questionType; + private boolean inUpdate; + + private final JSplitPane question_split = new JSplitPane(); + private final JList question_list = new JList(); + private final JScrollPane question_list_scroll = new JScrollPane(question_list); + private final JPanel question_panel_right = new RightPanel(); + private final JLabel question_title_label = new JLabel("Title:"); + private final JLabel question_question_label = new JLabel("Question:"); + private final JLabel question_citation_label = new JLabel("Citation:"); + private final JLabel question_follows_label = new JLabel("Follows Question:"); + private final JTextArea question_question_field = new NoTabTextArea(); + private final JTextArea question_citation_field = new NoTabTextArea(); + private final JTextField question_title_field = new JTextField(); + private final JButton question_new_button = new JButton("New"); + private final JComboBox question_follows_menu = new JComboBox(); + private final JButton question_delete_button = new JButton("Delete"); + private final Border listBorder; + + /** + * Generates Panel for question editing to insert in file tab window + * @param type Type of questions on Page (e.g. Alter Questions) + * @param parent parent frame for referencing composed objects + */ + public PromptPanel(int type) + { + questionType = type; + listBorder = BorderFactory.createCompoundBorder( + new TitledBorder(new EtchedBorder(EtchedBorder.RAISED,Color.white,new Color(178, 178, 178)), + Question.questionTypeString(questionType)), + BorderFactory.createEmptyBorder(10,10,10,10)); + + try + { + jbInit(); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + + /** + * Component initialization + * @throws Exception + */ + private void jbInit() + throws Exception + { + inUpdate = true; + + // Configure Split Frame + question_split.setMinimumSize(new Dimension(430, 330)); + question_split.setPreferredSize(new Dimension(430, 330)); + question_split.setResizeWeight(.33); + question_split.setDividerLocation(.33); + question_list_scroll.setRequestFocusEnabled(false); + question_split.add(question_list_scroll, JSplitPane.LEFT); + question_split.add(question_panel_right, JSplitPane.RIGHT); + + this.setLayout(new GridLayout()); + + // Configure List + question_list_scroll.setBorder(listBorder); + question_list_scroll.setMinimumSize(new Dimension(150, 150)); + question_list_scroll.setPreferredSize(new Dimension(150, 150)); + question_list_scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + + question_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + question_list.setCellRenderer(new QuestionListCellRenderer()); + + // Configure question fields + question_panel_right.setLayout(new GridBagLayout()); + question_question_field.setMaximumSize(new Dimension(280, 64)); + question_question_field.setMinimumSize(new Dimension(72, 16)); + question_question_field.setPreferredSize(new Dimension(72, 16)); + question_question_field.setLineWrap(true); + question_question_field.setRows(1); + question_question_field.setTabSize(4); + question_question_field.setWrapStyleWord(true); + + question_citation_field.setMaximumSize(new Dimension(280, 64)); + question_citation_field.setMinimumSize(new Dimension(72, 16)); + question_citation_field.setPreferredSize(new Dimension(72, 16)); + question_citation_field.setLineWrap(true); + question_citation_field.setRows(1); + question_citation_field.setTabSize(4); + question_citation_field.setWrapStyleWord(true); + + /* Question Layout */ + question_panel_right.add(question_title_label, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_title_field, new GridBagConstraints(1, 0, 2, 1, 0.0, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10, 10), 0, 4)); + question_panel_right.add(question_question_label, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_question_field, new GridBagConstraints(1, 1, 2, 2, 0.0, 0.4 + ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_citation_label, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_citation_field, new GridBagConstraints(1, 3, 2, 2, 0.0, 0.3 + ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_new_button, new GridBagConstraints(0, 7, 1, 1, 0.33, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_delete_button, new GridBagConstraints(2, 7, 1, 1, 0.33, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_follows_label, new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); + question_panel_right.add(question_follows_menu, new GridBagConstraints(1, 5, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10, 10), 0, 0)); + + question_list.setModel(new DefaultListModel()); + EgoNet.study.fillList(questionType, (DefaultListModel) question_list.getModel()); + + question_list.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + public void valueChanged(ListSelectionEvent e) { + question_list_selectionChanged(e); }}); + + question_new_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + question_new_button_actionPerformed(e);}}); + + question_delete_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + question_delete_button_actionPerformed(e);}}); + + question_follows_menu.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + question_follows_menu_actionPerformed(e);}}); + + question_title_field.getDocument().addDocumentListener(new DocumentListener() { + public void insertUpdate(DocumentEvent e) { questionTitleEvent(); } + public void changedUpdate(DocumentEvent e) { questionTitleEvent(); } + public void removeUpdate(DocumentEvent e) { questionTitleEvent(); }}); + + question_question_field.getDocument().addDocumentListener(new DocumentListener() { + public void insertUpdate(DocumentEvent e) { questionTextEvent(); } + public void changedUpdate(DocumentEvent e) { questionTextEvent(); } + public void removeUpdate(DocumentEvent e) { questionTextEvent(); }}); + + question_citation_field.getDocument().addDocumentListener(new DocumentListener() { + public void insertUpdate(DocumentEvent e) { questionCitationEvent(); } + public void changedUpdate(DocumentEvent e) { questionCitationEvent(); } + public void removeUpdate(DocumentEvent e) { questionCitationEvent(); }}); + + this.add(question_split, null); + + inUpdate = false; + } + + + /** + * Updates right side question fields when the selection changes + * @param e event generated by selection change. + */ + public void question_list_selectionChanged(ListSelectionEvent e) + { + if (!inUpdate) + { + questionUpdate(); + } + } + + /**** + * fill List with appropriate questions + * Set other fields to selected question + */ + public void fillPanel() + { + if (questionType == EgoNet.frame.curTab) + { + storageUpdate(); + questionUpdate(); + } + } + + /** + * Called when file changes to load new questions into list + */ + void storageUpdate() + { + inUpdate = true; + + if (questionType == EgoNet.frame.curTab) + { + ((DefaultListModel) question_list.getModel()).removeAllElements(); + EgoNet.study.fillList(questionType, (DefaultListModel) question_list.getModel()); + } + + inUpdate = false; + } + + void questionUpdate() + { + Question q; + int index; + + inUpdate = true; + + /** @todo Use List Data Listener? */ + if (questionType == EgoNet.frame.curTab) + { + index = question_list.getSelectedIndex(); + if ((question_list.getModel().getSize() > 0) && (index == -1)) + { + index = 0; + } + + question_follows_menu.removeAllItems(); + question_follows_menu.addItem(EgoNet.study.getFirstQuestion()); + for (int i = 0; i < question_list.getModel().getSize(); i++) + { + if (i != index) + { + question_follows_menu.addItem(question_list.getModel().getElementAt(i)); + } + } + + question_list.setSelectedIndex(index); + q = (Question) question_list.getSelectedValue(); + if (q != null) + { + question_question_field.setText(q.text); + question_citation_field.setText(q.citation); + question_title_field.setText(q.title); + question_follows_menu.setSelectedIndex(index); + + question_question_field.setEditable(true); + question_citation_field.setEditable(true); + question_title_field.setEditable(true); + question_delete_button.setEnabled(true); + question_follows_menu.setEnabled(true); + } + else + { + question_question_field.setText(null); + question_citation_field.setText(null); + question_title_field.setText(null); + + question_question_field.setEditable(false); + question_citation_field.setEditable(false); + question_title_field.setEditable(false); + question_delete_button.setEnabled(false); + question_follows_menu.setEnabled(false); + } + } + + inUpdate = false; + } + + + /**** + * Clear all on screen editable fields + * Generally called when a new survey is started + */ + public void clearPanel() + { + inUpdate = true; + ((DefaultListModel) question_list.getModel()).removeAllElements(); + inUpdate = false; + } + + /**** + * Document event handler used to read text fields + * @param e Document Event + */ + private void questionTitleEvent() + { + Question q = (Question) question_list.getSelectedValue(); + String s; + + if ((q != null) && !inUpdate) + { + s = question_title_field.getText().trim(); + if ((q.title == null) || (!q.title.equals(s))) + { + q.title = question_title_field.getText().trim(); + EgoNet.study.setModified(true); + question_list.repaint(); + } + } + } + + + /**** + * Document event handler used to read text fields + * @param e Document Event + */ + private void questionTextEvent() + { + Question q = (Question) question_list.getSelectedValue(); + String s; + + if ((q != null) && !inUpdate) + { + s = question_question_field.getText().trim(); + if ((q.text == null) || (!q.text.equals(s))) + { + q.text = question_question_field.getText().trim(); + EgoNet.study.setModified(true); + } + } + } + + + /**** + * Document event handler used to read text fields + * @param e Document Event + */ + private void questionCitationEvent() + { + Question q = (Question) question_list.getSelectedValue(); + String s; + + if ((q != null) && !inUpdate) + { + s = question_citation_field.getText().trim(); + if ((q.citation == null) || (!q.citation.equals(s))) + { + q.citation = question_citation_field.getText().trim(); + EgoNet.study.setModified(true); + } + } + } + + + void question_new_button_actionPerformed(ActionEvent e) + { + Question q = new Question(); + + q.questionType = questionType; + q.title = new String("Untitled Question"); + + try + { + EgoNet.study.addQuestion(q); + } + catch (DuplicateQuestionException e1) + { + } + + fillPanel(); + question_list.setSelectedValue(q, true); + + question_title_field.requestFocus(); + question_title_field.setSelectionStart(0); + question_title_field.setSelectionEnd(question_title_field.getText().length()); + } + + void question_delete_button_actionPerformed(ActionEvent e) + { + Question q = (Question) question_list.getSelectedValue(); + + if (q != null) + { + int confirm = JOptionPane.showConfirmDialog(EgoNet.frame, "Permanently remove this questions?", + "Delete Question", JOptionPane.OK_CANCEL_OPTION); + + if (confirm == JOptionPane.OK_OPTION) + { + EgoNet.study.removeQuestion(q); + fillPanel(); + } + } + } + + /** + * Changes order of questions + * @param e UI event + */ + void question_follows_menu_actionPerformed(ActionEvent e) + { + if (!inUpdate) + { + Question follows = (Question) question_follows_menu.getSelectedItem(); + Question q = (Question) question_list.getSelectedValue(); + + EgoNet.study.moveQuestionAfter(q, follows); + fillPanel(); + } + } +} + + + diff --git a/src/com/endlessloopsoftware/ego/author/QuestionLinkDialog.java b/src/com/endlessloopsoftware/ego/author/QuestionLinkDialog.java new file mode 100644 index 0000000..5c4a1e7 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/author/QuestionLinkDialog.java @@ -0,0 +1,720 @@ +package com.endlessloopsoftware.ego.author; + +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: QuestionLinkDialog.java,v 1.1 2005/08/02 19:36:03 samag Exp $ + */ + + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +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 com.endlessloopsoftware.ego.Answer; +import com.endlessloopsoftware.ego.Question; +import com.endlessloopsoftware.elsutils.documents.WholeNumberDocument; + +/** + * Generic Panel creation and handling routines for question editing + */ +public class QuestionLinkDialog extends JDialog + implements Observer +{ + private Question baseQuestion; + private Answer linkAnswer = new Answer(new Long(-1)); + + /* Containers */ + private final JSplitPane questionSplit = new JSplitPane(); + private final JList questionList = new JList(); + 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 answerMenu = new JComboBox(); + 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; + + + /** + * Generates Panel for question editing to insert in file tab window + * @param parent parent frame for referencing composed objects + */ + public QuestionLinkDialog() + { + listBorder = BorderFactory.createCompoundBorder( + new TitledBorder(new EtchedBorder(EtchedBorder.RAISED,Color.white,new Color(178, 178, 178)), "Questions"), + BorderFactory.createEmptyBorder(10,10,10,10)); + + try + { + jbInit(); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + + /** + * 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 java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + questionButtonOK_actionPerformed(e);}}); + + questionButtonNone.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + questionButtonNone_actionPerformed(e);}}); + + questionButtonCancel.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + questionButtonCancel_actionPerformed(e);}}); + + answerButtonListener = new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + questionAnsweredEventHandler(e);}}; + + answerMenu.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + 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 java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + questionAnsweredEventHandler(e);}}; + + allAdjacentCheck.addActionListener(new java.awt.event.ActionListener(){ + public void actionPerformed(ActionEvent e){ + 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()); + EgoNet.study.fillList(Question.ALL_QUESTION_TYPES, (DefaultListModel) questionList.getModel(), q.UniqueId); + + // Set Selection + if (baseQuestion.link.active) + { + Question selected = EgoNet.study.getQuestions().getQuestion(baseQuestion.link.answer.questionId); + 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 = new Answer(question.UniqueId); + + if (baseQuestion.link.active && (question.UniqueId.equals(baseQuestion.link.answer.questionId))) + { + linkAnswer.value = baseQuestion.link.answer.value; + linkAnswer.string = baseQuestion.link.answer.string; + linkAnswer.answered = 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 = new Answer(question.UniqueId); + + if (baseQuestion.link.active && (question.UniqueId.equals(baseQuestion.link.answer.questionId))) + { + linkAnswer.value = baseQuestion.link.answer.value; + linkAnswer.index = baseQuestion.link.answer.index; + linkAnswer.string = baseQuestion.link.answer.string; + linkAnswer.answered = 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.questionType == Question.ALTER_PROMPT) + { + // Should never be here + //assert(false); + } + else if (question.answerType == Question.TEXT) + { + answerPanel.add(textPanel); + answerPanel.validate(); + answerTextField.setDocument(plainDocument); + answerTextField.requestFocus(); + + if (linkAnswer.answered) + { + answerTextField.setText(linkAnswer.string); + } + else + { + answerTextField.setText(""); + } + } + else if (question.answerType == Question.NUMERICAL) + { + answerPanel.add(textPanel); + answerTextField.setDocument(wholeNumberDocument); + answerTextField.requestFocus(); + + if (linkAnswer.answered) + { + answerTextField.setText(linkAnswer.string); + } + else + { + answerTextField.setText(""); + } + } + else if (question.selections.length <= 5) + { + allAdjacentCheck.setVisible(question.questionType == Question.ALTER_PAIR_QUESTION); + questionText.setText(question.text); + + answerPanel.add(radioPanel); + + if (linkAnswer.answered) + { + if (linkAnswer.value == Answer.ALL_ADJACENT) + { + allAdjacentCheck.setSelected(true); + answerButtons[MAX_BUTTONS].setSelected(true); + } + else + { + allAdjacentCheck.setSelected(false); + answerButtons[question.selections.length - (linkAnswer.value + 1)].setSelected(true); + } + } + else + { + answerButtons[MAX_BUTTONS].setSelected(true); + } + + for (int i = 0; i < question.selections.length; i++) + { + answerButtons[i].setText(question.selections[i].getString()); + answerButtons[i].setVisible(true); + answerButtons[i].setEnabled(linkAnswer.value != Answer.ALL_ADJACENT); + } + + for (int i = question.selections.length; i < MAX_BUTTONS; i++) + { + answerButtons[i].setVisible(false); + } + } + else + { + allAdjacentCheck.setVisible(question.questionType == Question.ALTER_PAIR_QUESTION); + questionText.setText(question.text); + answerPanel.add(menuPanel); + + answerMenu.removeAllItems(); + + answerMenu.addItem("Select an answer"); + for (int i = 0; i < question.selections.length; i++) + { + answerMenu.addItem(question.selections[i]); + } + + if (linkAnswer.value == Answer.ALL_ADJACENT) + { + allAdjacentCheck.setSelected(true); + answerMenu.setEnabled(false); + } + else if (linkAnswer.value != Answer.NO_ANSWER) + { + allAdjacentCheck.setSelected(false); + answerMenu.setEnabled(true); + answerMenu.setSelectedIndex(question.selections.length - linkAnswer.value); + } + 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; + + switch (question.answerType) + { + case Question.NUMERICAL: + if (answerTextField.getText().length() > 0) + { + linkAnswer.string = answerTextField.getText(); + linkAnswer.value = Integer.valueOf(linkAnswer.string).intValue(); + linkAnswer.answered = true; + } + else + { + linkAnswer.value = Answer.NO_ANSWER; + linkAnswer.answered = false; + } + break; + + case Question.TEXT: + linkAnswer.string = answerTextField.getText(); + linkAnswer.value = linkAnswer.string.length(); + linkAnswer.answered = (linkAnswer.value != 0); + break; + + case Question.CATEGORICAL: + if (question.selections.length <= 5) + { + if (allAdjacentCheck.isSelected()) + { + linkAnswer.value = Answer.ALL_ADJACENT; + linkAnswer.string = "All Adjacent"; + linkAnswer.answered = true; + } + else + { + int button = selectedButtonIndex(answerButtons); + linkAnswer.answered = (button != MAX_BUTTONS); + + if (linkAnswer.answered) + { + linkAnswer.value = question.selections[button].getValue(); + linkAnswer.index = question.selections[button].getIndex(); + linkAnswer.string = answerButtons[button].getActionCommand(); + } + else + { + linkAnswer.value = Answer.NO_ANSWER; + linkAnswer.index = Answer.NO_ANSWER; + linkAnswer.string = ""; + } + } + } + else + { + if (allAdjacentCheck.isSelected()) + { + linkAnswer.value = Answer.ALL_ADJACENT; + linkAnswer.string = "All Adjacent"; + linkAnswer.answered = true; + } + else if (answerMenu.getSelectedIndex() > 0) + { + linkAnswer.value = question.selections[answerMenu.getSelectedIndex() - 1].getValue(); + linkAnswer.string = answerMenu.getSelectedItem().toString(); + linkAnswer.answered = (linkAnswer.value < question.selections.length); + } + else + { + linkAnswer.value = Answer.NO_ANSWER; + linkAnswer.string = ""; + linkAnswer.answered = false; + } + } + break; + } + } + + void jShowListButton_actionPerformed(ActionEvent e) + { + questionPanelLeft.setVisible(!questionPanelLeft.isVisible()); + questionSplit.setDividerLocation(.33); + questionSplit.repaint(); + } + + void questionButtonNone_actionPerformed(ActionEvent e) + { + if (EgoNet.study.confirmIncompatibleChange(EgoNet.frame)) + { + baseQuestion.link.active = false; + baseQuestion.link.answer = null; + EgoNet.study.setModified(true); + EgoNet.study.setCompatible(false); + } + + EgoNet.frame.fillCurrentPanel(); + this.hide(); + } + + void questionButtonCancel_actionPerformed(ActionEvent e) + { + this.hide(); + } + + void questionButtonOK_actionPerformed(ActionEvent e) + { + if ((linkAnswer != null) && (linkAnswer.answered) && EgoNet.study.confirmIncompatibleChange(EgoNet.frame)) + { + baseQuestion.link.active = true; + baseQuestion.link.answer = linkAnswer; + EgoNet.study.setModified(true); + EgoNet.study.setCompatible(false); + } + + this.hide(); + EgoNet.frame.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.answered); + } + + + 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(); + } +} + +/** + * $Log: QuestionLinkDialog.java,v $ + * Revision 1.1 2005/08/02 19:36:03 samag + * Initial checkin + * + * Revision 1.12 2004/04/11 00:24:48 admin + * Fixing headers + * + * Revision 1.11 2004/04/07 00:08:31 admin + * updating manifests, jar creation. Removing author specific objects from + * client specific references + * + * Revision 1.10 2004/03/28 17:31:31 admin + * More error handling when uploading study to server + * Server URL selection dialog for upload + * + * Revision 1.9 2004/03/21 20:29:37 admin + * Warn before making incompatible changes to in use study file + * + * Revision 1.8 2004/03/21 14:00:38 admin + * Cleaned up Question Panel Layout using FOAM + * + * Revision 1.7 2004/03/05 19:47:20 admin + * Fixing problem with links from popup menu questions + * + * Revision 1.6 2004/02/10 20:10:43 admin + * Version 2.0 beta 3 + * + * Revision 1.5 2003/12/08 15:57:50 admin + * Modified to generate matrix files on survey completion or summarization + * Extracted statistics models + * + * Revision 1.4 2003/12/05 19:15:43 admin + * Extracting Study + * + * Revision 1.3 2003/12/04 15:14:08 admin + * Merging EgoNet and EgoClient projects so that they can share some + * common classes more easily. + * + * Revision 1.2 2003/11/25 19:25:43 admin + * Warn before closing window + * + * Revision 1.1.1.1 2003/06/08 15:09:40 admin + * Egocentric Network Survey Authoring Module + * + * Revision 1.6 2002/08/30 16:50:27 admin + * Using Selections + * + * Revision 1.5 2002/08/26 16:01:11 admin + * UI fixes + * + * Revision 1.4 2002/08/11 22:26:05 admin + * Final Statistics window, new file handling + * + * Revision 1.3 2002/08/08 17:07:25 admin + * Preparing to change file system + * + * Revision 1.2 2002/07/25 14:54:24 admin + * Question Links + */ \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/author/RightPanel.java b/src/com/endlessloopsoftware/ego/author/RightPanel.java new file mode 100644 index 0000000..f935461 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/author/RightPanel.java @@ -0,0 +1,23 @@ +package com.endlessloopsoftware.ego.author; + +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + */ + +import javax.swing.JPanel; + +/** + * Extends JPanel class to keep focus in right panel of split question panel + */ +public class RightPanel extends JPanel +{ + public boolean isFocusCycleRoot() + { + return (true); + } +} diff --git a/src/com/endlessloopsoftware/ego/author/SelectStudyDialog.gui_xml b/src/com/endlessloopsoftware/ego/author/SelectStudyDialog.gui_xml new file mode 100644 index 0000000..5a2b7f3 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/author/SelectStudyDialog.gui_xml @@ -0,0 +1,341 @@ + + + + + + + + + + + 31 + + + + + + + surveyNames + + + + + + 261 + 149 + + + + + + + + + 7 + + + + + + + 9 + + + Server URL + + + + + + + serverURL + + + + + + + 13 + + + Password + + + + + + Load Studies + + loadStudies + + + + + + Set Active Study + + select + + + + + + + password + + + + + + 332 + 353 + + + + + 332 + 353 + + + + + + + + + + + West + + + West + 0 + + + + + East + + + East + 0 + + + + + North + + + South + -24 + + + + + South + + + North + -24 + + + + + + + + East + + + West + -16 + + + + + South + + + 95 + Height + 0 + + + + + + + + West + + + West + 4 + + + + + East + + + East + 0 + + + + + South + + + North + -24 + + + + + + + + East + + + East + 3 + + + + + North + + + North + 0 + + + + + + + + West + + + East + 8 + + + + + East + + + East + 0 + + + + + North + + + South + -24 + + + + + South + + + North + -14 + + + + + + + + West + + + West + 0 + + + + + North + + + North + 0 + + + + + + + + West + + + 10 + Width + 0 + + + + + East + + + 90 + Width + 0 + + + + + North + + + 5 + Height + 0 + + + + + South + + + North + -24 + + + + + + + + West + + + West + 0 + + + + + North + + + North + 0 + + + + + + diff --git a/src/com/endlessloopsoftware/ego/author/SetActiveStudyDialog.java b/src/com/endlessloopsoftware/ego/author/SetActiveStudyDialog.java new file mode 100644 index 0000000..9725c7e --- /dev/null +++ b/src/com/endlessloopsoftware/ego/author/SetActiveStudyDialog.java @@ -0,0 +1,229 @@ +package com.endlessloopsoftware.ego.author; + +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: SetActiveStudyDialog.java,v 1.1 2005/08/02 19:36:04 samag Exp $ + * + */ + +import java.awt.Cursor; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Iterator; +import java.util.Properties; +import java.util.Set; +import java.util.prefs.Preferences; + +import javax.swing.*; + +import com.cim.dlgedit.loader.DialogResource; +import com.cim.util.swing.DlgUtils; +import com.endlessloopsoftware.egonet.interfaces.ConfigurationSBRemote; +import com.endlessloopsoftware.egonet.interfaces.ConfigurationSBRemoteHome; +import com.endlessloopsoftware.egonet.interfaces.ConfigurationSBUtil; +import com.endlessloopsoftware.egonet.interfaces.StudySBRemote; +import com.endlessloopsoftware.egonet.interfaces.StudySBRemoteHome; +import com.endlessloopsoftware.egonet.interfaces.StudySBUtil; +import com.endlessloopsoftware.elsutils.security.SymmetricKeyEncryption; + +public class SetActiveStudyDialog + extends JDialog + implements ActionListener +{ + // Declare beans. + private JButton loadStudies; + private JButton select; + private JList surveyNameList; + private JTextField serverURL; + private JPasswordField password; + + // Get default values + private Preferences prefs = Preferences.userNodeForPackage(this.getClass()); + + Properties prop = new Properties(); + { + prop.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory"); + } + + private static final String SERVER_URL = "ServerURL"; + + // Constructor. + public SetActiveStudyDialog(Frame owner) + { + // This dialog is modal. + super(owner, "Select Active Survey", true); + + // Load User Interface + JPanel panel = DialogResource.load("com/endlessloopsoftware/ego/author/SelectStudyDialog.gui_xml"); + + setContentPane(panel); + + // Attach beans to fields. + loadStudies = (JButton) DialogResource.getComponentByName(panel, "loadStudies"); + select = (JButton) DialogResource.getComponentByName(panel, "select"); + surveyNameList = (JList) DialogResource.getComponentByName(panel, "surveyNames"); + password = (JPasswordField) DialogResource.getComponentByName(panel, "password"); + serverURL = (JTextField) DialogResource.getComponentByName(panel, "serverURL"); + + // Set default value + serverURL.setText(prefs.get(SERVER_URL, "")); + password.setText(null); + surveyNameList.setModel(new DefaultListModel()); + + // Fill In Survey List + + // Add dialog as ActionListener. + loadStudies.addActionListener(this); + select.addActionListener(this); + + pack(); + setLocationRelativeTo(owner); + + // Set Enter and Escape keys as default keys. + //getRootPane().setDefaultButton(store); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + } + + /** + * Invoke the onXxx() action handlers. + * + * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) + */ + public void actionPerformed(ActionEvent e) + { + DlgUtils.invokeHandler(this, e); + } + + public void onloadStudies() + { + surveyNameList.setModel(getList()); + } + + public void onselect() + { + String message = ""; + boolean success = false; + setWaitCursor(true); + + String studyName = (String) surveyNameList.getSelectedValue(); + + if (!("".equals(studyName) || (studyName == null))) + { + try + { + String epassword = "215-121-242-47-99-238-5-61-133-183-0-216-187-250-253-30-115-177-254-142-161-83-108-56";//SymmetricKeyEncryption.encrypt(new String(password.getPassword())); + + ConfigurationSBRemoteHome configurationSBHome = ConfigurationSBUtil.getHome(prop); + ConfigurationSBRemote configurationSession = configurationSBHome.create(); + configurationSession.setActiveStudy(studyName, epassword); + + success = true; + } + catch (Exception ex) + { + message = ex.getMessage(); + } + } + else + { + JOptionPane.showMessageDialog(this, "No Survey Selected"); + } + + prefs.put(SERVER_URL, serverURL.getText()); + + if (success) + { + JOptionPane.showMessageDialog(this, studyName + " is now the active survey."); + } + else + { + JOptionPane.showMessageDialog(this, "Unable to set active survey.\n" + message, "Server Error", JOptionPane.ERROR_MESSAGE); + } + + setWaitCursor(false); + + this.hide(); + } + + protected void setWaitCursor(boolean waitCursor) + { + if (waitCursor) + { + this.getGlassPane().setVisible(true); + this.getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } + else + { + this.getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + this.getGlassPane().setVisible(false); + } + } + + /** + * @author admin + * + * To change the template for this generated type comment go to + * Window - Preferences - Java - Code Generation - Code and Comments + */ + public ListModel getList() + { + DefaultListModel listModel = new DefaultListModel(); + + if ("".equals(serverURL.getText())) + return listModel; + + try + { + setWaitCursor(true); + + prop.setProperty("java.naming.provider.url", serverURL.getText()+":1099"); + + StudySBRemoteHome studySBHome = StudySBUtil.getHome(prop); + StudySBRemote studySession = studySBHome.create(); + Set studyNames = studySession.getStudyNames(); + + for (Iterator it = studyNames.iterator(); it.hasNext();) + { + listModel.addElement(it.next()); + } + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + finally + { + setWaitCursor(false); + } + + return (ListModel) listModel; + } + + public String encryptPassword(String password) + { + return SymmetricKeyEncryption.encrypt(password); + } +} + +/** + * $Log: SetActiveStudyDialog.java,v $ + * Revision 1.1 2005/08/02 19:36:04 samag + * Initial checkin + * + * Revision 1.3 2004/04/11 15:19:28 admin + * Using password to access server + * + * Remote study summary in seperate thread with progress monitor + * + * Revision 1.2 2004/04/11 00:24:48 admin + * Fixing headers + * + */ \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/author/StoreStudyDialog.java b/src/com/endlessloopsoftware/ego/author/StoreStudyDialog.java new file mode 100644 index 0000000..3aa0297 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/author/StoreStudyDialog.java @@ -0,0 +1,135 @@ +package com.endlessloopsoftware.ego.author; + +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: StoreStudyDialog.java,v 1.1 2005/08/02 19:36:05 samag Exp $ + * + */ + +import java.awt.Cursor; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.prefs.Preferences; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; + +import com.cim.dlgedit.loader.DialogResource; +import com.cim.util.swing.DlgUtils; + +public class StoreStudyDialog + extends JDialog + implements ActionListener +{ + // Declare beans. + private JButton cancel; + private JButton store; + private JLabel surveyName; + private JTextField serverURL; + private JPasswordField password; + + // Get default values + private Preferences prefs = Preferences.userNodeForPackage(this.getClass()); + + private static final String SERVER_URL = "ServerURL"; + + // Constructor. + public StoreStudyDialog(Frame owner) + { + // This dialog is modal. + super(owner, "Upload Survey to Server", true); + + // Load User Interface + JPanel panel = DialogResource.load("com/endlessloopsoftware/ego/author/StoreSurvey.gui_xml"); + + setContentPane(panel); + + // Attach beans to fields. + cancel = (JButton) DialogResource.getComponentByName(panel, "cancel"); + store = (JButton) DialogResource.getComponentByName(panel, "store"); + surveyName = (JLabel) DialogResource.getComponentByName(panel, "surveyName"); + password = (JPasswordField) DialogResource.getComponentByName(panel, "password"); + serverURL = (JTextField) DialogResource.getComponentByName(panel, "serverURL"); + + // Set default value + surveyName.setText(EgoNet.study.getStudyName()); + serverURL.setText(prefs.get(SERVER_URL, "")); + + // Add dialog as ActionListener. + cancel.addActionListener(this); + store.addActionListener(this); + + pack(); + setLocationRelativeTo(owner); + + // Set Enter and Escape keys as default keys. + //getRootPane().setDefaultButton(store); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + } + + /** + * Invoke the onXxx() action handlers. + * + * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) + */ + public void actionPerformed(ActionEvent e) + { + DlgUtils.invokeHandler(this, e); + } + + public void oncancel() + { + this.hide(); + } + + public void onstore() + { + setWaitCursor(true); + boolean success = EgoNet.study.writeDBStudy(EgoNet.frame, serverURL.getText(), password.getPassword()); + + if (success) + { + prefs.put(SERVER_URL, serverURL.getText()); + } + setWaitCursor(false); + + this.hide(); + } + + protected void setWaitCursor(boolean waitCursor) + { + if (waitCursor) + { + this.getGlassPane().setVisible(true); + this.getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } + else + { + this.getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + this.getGlassPane().setVisible(false); + } + } + + +} + +/** + * $Log: StoreStudyDialog.java,v $ + * Revision 1.1 2005/08/02 19:36:05 samag + * Initial checkin + * + * Revision 1.3 2004/04/11 00:24:48 admin + * Fixing headers + * + */ \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/author/StoreSurvey.gui_xml b/src/com/endlessloopsoftware/ego/author/StoreSurvey.gui_xml new file mode 100644 index 0000000..4fa3b48 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/author/StoreSurvey.gui_xml @@ -0,0 +1,288 @@ + + + + + + + 1 + + + Survey Name: + + + + + + + 3 + + + Server URL: + + + + + + + 5 + + + Password: + + + + + + + surveyName + + + New Survey + + + + + + + serverURL + + + + + + + password + + + + + + Cancel + + cancel + + + + + + Store + + store + + + + + + 431 + 179 + + + + + 431 + 179 + + + + + + + + + + + West + + + East + 34 + + + + + East + + + East + 0 + + + + + North + + + North + 0 + + + + + + + + East + + + West + -14 + + + + + North + + + North + 0 + + + + + + + + West + + + West + 0 + + + + + East + + + East + 0 + + + + + North + + + North + 0 + + + + + + + + West + + + West + 31 + + + + + North + + + North + 14 + + + + + + + + East + + + East + 0 + + + + + North + + + South + 24 + + + + + + + + West + + + West + 0 + + + + + East + + + 95 + Width + 0 + + + + + North + + + North + 0 + + + + + + + + East + + + East + 2 + + + + + North + + + South + 24 + + + + + + + + East + + + East + 0 + + + + + North + + + South + 24 + + + + + + diff --git a/src/com/endlessloopsoftware/ego/author/StudyPanel.java b/src/com/endlessloopsoftware/ego/author/StudyPanel.java new file mode 100644 index 0000000..0e189fb --- /dev/null +++ b/src/com/endlessloopsoftware/ego/author/StudyPanel.java @@ -0,0 +1,283 @@ +package com.endlessloopsoftware.ego.author; + +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: StudyPanel.java,v 1.1 2005/08/02 19:36:05 samag Exp $ + */ +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.io.File; +import java.io.IOException; + +import javax.swing.BorderFactory; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.JTextPane; +import javax.swing.SwingConstants; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.Document; + +import com.endlessloopsoftware.ego.client.EgoClient; +import com.endlessloopsoftware.elsutils.documents.WholeNumberDocument; + +/** + * Generic Panel creation and handling routines for question editing + */ +public class StudyPanel extends JPanel +{ + private final EgoFrame frame; + private final GridBagLayout study_layout = new GridBagLayout(); + private final JLabel study_path_label = new JLabel("Study Path:"); + private final JLabel study_path_field = new JLabel("< none selected >"); + private final JLabel study_name_label = new JLabel("Study Name:"); + private final JTextField study_name_field = new JTextField("< none selected >"); + private final JLabel study_num_alters_label = new JLabel("Number of Alters:"); + private final JTextField study_num_alters_field = new JTextField(); + private final JLabel titleLabel = new JLabel("EgoCentric Network Study Configuration"); + private final JTextPane instructionPane = new JTextPane(); + private final Document altersDocument = new WholeNumberDocument(); + + private final String[] instructionStrings = { + "Start by selecting a study or choosing \"New Study\" from the file menu.", "Please name the study.", + "You may add predefined questions to the study by selecting Import Questions from the file menu"}; + + + /** + * Generates Panel for study configuration info + * @param parent parent frame for this panel + */ + public StudyPanel(EgoFrame parent) + { + frame = parent; + + try + { + jbInit(); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + + /** + * Component initialization + */ + private void jbInit() + { + this.setLayout(study_layout); + this.setMinimumSize(new Dimension(300, 200)); + this.setPreferredSize(new Dimension(400, 400)); + + study_num_alters_field.setDocument(altersDocument); + + /* Question Layout */ + titleLabel.setBorder(BorderFactory.createEtchedBorder()); + titleLabel.setHorizontalAlignment(SwingConstants.CENTER); + instructionPane.setBackground(Color.lightGray); + instructionPane.setBorder(BorderFactory.createLoweredBevelBorder()); + instructionPane.setEditable(false); + instructionPane.setText(instructionStrings[0]); + + add(titleLabel, new GridBagConstraints(0, 0, 4, 1, 1.0, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + add(study_name_label, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.1 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 4)); + add(study_name_field, new GridBagConstraints(1, 1, 2, 1, 0.33, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10, 10), 0, 6)); + add(study_path_label, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.1 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 4)); + add(study_path_field, new GridBagConstraints(1, 2, 2, 1, 0.33, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10, 10), 0, 4)); + add(study_num_alters_label, new GridBagConstraints(0, 4, 2, 1, 0.0, 0.1 + ,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10, 10), 0, 0)); + add(study_num_alters_field, new GridBagConstraints(2, 4, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 50, 8)); + add(instructionPane, new GridBagConstraints(0, 5, 4, 1, 1.0, 0.15 + ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(10, 10, 10, 10), 0, 4)); + + /**** + * Action Listeners for buttons and other UI elements + * @param e event to be handled + */ + study_name_field.getDocument().addDocumentListener(new DocumentListener() { + public void insertUpdate(DocumentEvent e) { studyNameTextEvent(); } + public void changedUpdate(DocumentEvent e) { studyNameTextEvent(); } + public void removeUpdate(DocumentEvent e) { studyNameTextEvent(); }}); + + study_num_alters_field.getDocument().addDocumentListener(new DocumentListener() { + public void insertUpdate(DocumentEvent e) { studyAltersTextEvent(); } + public void changedUpdate(DocumentEvent e) { studyAltersTextEvent(); } + public void removeUpdate(DocumentEvent e) { studyAltersTextEvent(); }}); + } + + + /**** + * Clear all on screen editable fields + * Generally called when a new survey is started + */ + public void clearPanel() + { + study_name_field.setText(""); + study_path_field.setText("< none selected >"); + } + + /**** + * Clear all on screen editable fields + * Generally called when a new survey is started + */ + public void fillPanel() + { + boolean hasStudy = (EgoNet.storage.getStudyFile() != null); + + try + { + study_name_field. setEnabled(hasStudy); + study_name_label. setEnabled(hasStudy); + study_num_alters_label. setEnabled(hasStudy); + study_num_alters_field. setEnabled(hasStudy && !EgoNet.storage.getStudyInUse()); + study_path_label. setEnabled(hasStudy); + study_path_field. setEnabled(hasStudy); + + study_name_field. setText(EgoNet.study.getStudyName()); + study_path_field. setText(filename(EgoNet.storage.getStudyFile())); + + study_num_alters_field.setText(Integer.toString(EgoNet.study.getNumAlters())); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + private String filename(File f) + throws IOException + { + if (f == null) + { + return("< none selected >"); + } + else + { + return(f.getPath().toString()); + } + } + + private void studyNameTextEvent() + { + String s = study_name_field.getText(); + + if (!EgoNet.study.getStudyName().equals(s)) + { + EgoNet.study.setStudyName(s); + } + } + + private void studyAltersTextEvent() + { + String s = study_num_alters_field.getText(); + int i = 0; + + if (!s.equals("")) + { + i = Integer.parseInt(s); + } + + if (i != EgoNet.study.getNumAlters()) + { + EgoNet.study.setNumAlters(i); + //EgoNet.study.setCompatible(false); + } + } + +} + +/** + * $Log: StudyPanel.java,v $ + * Revision 1.1 2005/08/02 19:36:05 samag + * Initial checkin + * + * Revision 1.9 2004/03/22 18:01:28 admin + * Fixed problem confirming alterNums by simply making it non-editable when + * the study is in use + * + * Now confirming changes to number of selections + * + * Revision 1.8 2004/03/21 20:29:37 admin + * Warn before making incompatible changes to in use study file + * + * Revision 1.7 2004/03/21 14:00:38 admin + * Cleaned up Question Panel Layout using FOAM + * + * Revision 1.6 2004/02/26 21:19:17 admin + * adding jardescs + * + * Revision 1.5 2004/02/10 20:10:43 admin + * Version 2.0 beta 3 + * + * Revision 1.4 2003/12/05 19:15:44 admin + * Extracting Study + * + * Revision 1.3 2003/12/04 15:14:09 admin + * Merging EgoNet and EgoClient projects so that they can share some + * common classes more easily. + * + * Revision 1.2 2003/11/25 19:25:44 admin + * Warn before closing window + * + * Revision 1.1.1.1 2003/06/08 15:09:40 admin + * Egocentric Network Survey Authoring Module + * + * Revision 1.12 2002/08/30 16:50:28 admin + * Using Selections + * + * Revision 1.11 2002/08/11 22:26:06 admin + * Final Statistics window, new file handling + * + * Revision 1.10 2002/08/08 17:07:26 admin + * Preparing to change file system + * + * Revision 1.9 2002/07/24 14:17:10 admin + * xml files, links + * + * Revision 1.7 2002/06/30 15:59:18 admin + * Moving questions in lists, between lists + * Better category input + * + * Revision 1.6 2002/06/26 15:43:43 admin + * More selection dialog work + * File loading fixes + * + * Revision 1.5 2002/06/25 15:41:02 admin + * Lots of UI work + * + * Revision 1.4 2002/06/21 22:47:13 admin + * question lists working again + * + * Revision 1.3 2002/06/21 21:52:50 admin + * Many changes to event handling, file handling + * + * Revision 1.2 2002/06/19 01:57:05 admin + * Much UI work done + * + * Revision 1.1 2002/06/16 17:53:31 admin + * new File + * + * Revision 1.2 2002/06/15 14:19:51 admin + * Initial Checkin of question and survey + * General file system work + * + */ + diff --git a/src/com/endlessloopsoftware/ego/client/ClientFrame.java b/src/com/endlessloopsoftware/ego/client/ClientFrame.java new file mode 100644 index 0000000..7ce335d --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/ClientFrame.java @@ -0,0 +1,263 @@ +package com.endlessloopsoftware.ego.client; + +import java.awt.AWTEvent; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.PrintWriter; + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileFilter; +import com.endlessloopsoftware.elsutils.files.ExtensionFileFilter; +import com.endlessloopsoftware.elsutils.files.FileCreateException; + +import com.endlessloopsoftware.ego.Shared; +import com.endlessloopsoftware.elsutils.AboutBox; +import com.endlessloopsoftware.elsutils.files.FileHelpers; +import com.endlessloopsoftware.ego.client.graph.GraphData; + +/** + *

+ * Title: Egocentric Network Researcher + *

+ *

+ * Description: Configuration Utilities for an Egocentric network study + *

+ *

+ * Copyright: Copyright (c) 2004 + *

+ *

+ * Company: Endless Loop Software + *

+ * + * @author Peter C. Schoaff + * @version 2.1 + */ + +public class ClientFrame extends JFrame { + private final JMenuBar jMenuBar1 = new JMenuBar(); + + private final JMenu jMenuFile = new JMenu("File"); + + private final JMenu jMenuHelp = new JMenu("Help"); + + private final JMenuItem jMenuHelpAbout = new JMenuItem("About"); + + private final JMenuItem saveStudySummary = new JMenuItem( + "Save Study Summary"); + + private final JMenuItem exit = new JMenuItem("Exit"); + + public final JMenuItem saveAlterSummary = new JMenuItem( + "Save Alter Summary"); + + public final JMenuItem saveTextSummary = new JMenuItem( + "Save Text Answer Summary"); + + public final JMenuItem saveAdjacencyMatrix = new JMenuItem( + "Save Adjacency Matrix"); + + public final JMenuItem saveWeightedAdjacencyMatrix = new JMenuItem( + "Save Weighted Adjacency Matrix"); + + public final JMenuItem saveGraph = new JMenuItem("Save Graph as JPEG image"); + + public final JMenuItem saveInterview = new JMenuItem("Save Interview"); + + public final JMenuItem recalculateStatistics = new JMenuItem("Recalculate Statistics"); + + public final JMenuItem close = new JMenuItem("Return to Main Menu"); + + public final JMenuItem saveInterviewStatistics = new JMenuItem( + "Save Interview Statistics"); + + // Construct the frame + public ClientFrame() { + enableEvents(AWTEvent.WINDOW_EVENT_MASK); + + try { + jbInit(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + // Component initialization + private void jbInit() throws Exception { + this.setSize(new Dimension(700, 600)); + this.setTitle("Egocentric Networks Study Tool"); + + createMenuBar(EgoClient.SELECT); + + this.setContentPane(new JPanel()); + + jMenuHelpAbout.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + jMenuHelpAbout_actionPerformed(e); + } + }); + + exit.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + jMenuFileExit_actionPerformed(e); + } + }); + + saveStudySummary.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + saveStudySummary_actionPerformed(e); + } + }); + + saveGraph.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + saveGraph_actionPerformed(e); + } + }); + + saveInterview.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + try { + EgoClient.interview.completeInterview(); + } catch (FileCreateException ex) { + ex.printStackTrace(); + } + } + }); + + recalculateStatistics.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + EgoClient.interview = EgoClient.storage.readInterview(); + if(EgoClient.interview != null) + ViewInterviewPanel.gotoPanel(); + } + }); + + close + .addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + SourceSelectPanel.gotoPanel(false); + } + }); + } + + public void flood() { + Dimension size = this.getSize(); + this.pack(); + this.setSize(size); + this.validate(); + } + + // File | Exit action performed + public void jMenuFileExit_actionPerformed(ActionEvent e) { + if (EgoClient.interview != null) { + EgoClient.interview.exit(); + } + + System.exit(0); + } + + // Help | About action performed + public void jMenuHelpAbout_actionPerformed(ActionEvent e) { + JOptionPane.showMessageDialog(this, + "Egonet is an egocentric network study tool." + + "\n\nThanks to: Dr. Chris McCarty, University of Florida", + "About Egonet", JOptionPane.PLAIN_MESSAGE); + } + + // Overridden so we can exit when window is closed + protected void processWindowEvent(WindowEvent e) { + super.processWindowEvent(e); + if (e.getID() == WindowEvent.WINDOW_CLOSING) { + jMenuFileExit_actionPerformed(null); + } + } + + public void createMenuBar(int mode) { + jMenuBar1.removeAll(); + jMenuFile.removeAll(); + jMenuHelp.removeAll(); + + // File Menu + if (mode == EgoClient.VIEW_SUMMARY) { + jMenuFile.add(saveStudySummary); + jMenuFile.add(close); + jMenuFile.addSeparator(); + jMenuFile.add(exit); + } else if (mode == EgoClient.VIEW_INTERVIEW) { + /******************************************************************* + * Create Menu Bar + ******************************************************************/ + jMenuFile.add(saveAlterSummary); + jMenuFile.add(saveTextSummary); + jMenuFile.add(saveAdjacencyMatrix); + jMenuFile.add(saveWeightedAdjacencyMatrix); + jMenuFile.add(saveGraph); + jMenuFile.add(saveInterview); + jMenuFile.add(recalculateStatistics); + jMenuFile.addSeparator(); + jMenuFile.add(close); + } else { + jMenuFile.add(exit); + } + jMenuBar1.add(jMenuFile); + + // Help Menu + jMenuHelp.add(jMenuHelpAbout); + jMenuBar1.add(jMenuHelp); + + this.setJMenuBar(jMenuBar1); + } + + void saveStudySummary_actionPerformed(ActionEvent e) { + String name = FileHelpers.formatForCSV(EgoClient.study.getStudyName()); + String filename = name + "_Summary"; + PrintWriter w = EgoClient.storage.newStatisticsPrintWriter( + "Study Summary", "csv", filename); + + if (w != null) { + try { + ((SummaryPanel) EgoClient.frame.getContentPane()) + .writeStudySummary(w); + } finally { + w.close(); + } + } + } + + void saveGraph_actionPerformed(ActionEvent e) { + String fileName; + fileName = EgoClient.interview.getName() + "_graph"; + File currentDirectory = new File(EgoClient.storage.getPackageFile() + .getParent() + + "/Graphs"); + currentDirectory.mkdir(); + + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setCurrentDirectory(currentDirectory); + fileChooser.setSelectedFile(new File(fileName + ".jpeg")); + fileChooser.setDialogTitle("Save Graph"); + fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + + // ExtensionFileFilter jpegFilter = new ExtensionFileFilter("JPEG + // Files",".jpeg"); + FileFilter imageFilter = new ImageFilter(); + fileChooser.addChoosableFileFilter(imageFilter); + + int returnValue = fileChooser.showSaveDialog(this); + if (returnValue == JFileChooser.APPROVE_OPTION) { + File imageFile = fileChooser.getSelectedFile(); + System.out.println(imageFile.getName()); + GraphData.writeJPEGImage(imageFile); + } + + } +} \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/ClientPanel.java b/src/com/endlessloopsoftware/ego/client/ClientPanel.java new file mode 100644 index 0000000..d8f279f --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/ClientPanel.java @@ -0,0 +1,204 @@ +package com.endlessloopsoftware.ego.client; + +/** + *

Copyright: Copyright (c) 2002 - 2004

+ *

Company: Endless Loop Software

+ * @author Peter Schoaff + * + * $Id: ClientPanel.java,v 1.1 2005/08/02 19:36:01 samag Exp $ + */ + +import java.awt.Color; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.SwingConstants; + +import org.egonet.exceptions.CorruptedInterviewException; + +import com.cim.dlgedit.loader.DialogResource; +import com.endlessloopsoftware.ego.Study; + +public class ClientPanel + extends JPanel +{ + + private final GridBagLayout gridBagLayout1 = new GridBagLayout(); + + + private JLabel titleLabel; + private JButton selectStudyButton; + private JButton statisticsButton; + private JButton gradientStatisticsButton; + private JButton viewInterviewButton; + private JButton startInterviewButton; + + private JLabel studyNameLabel = new JLabel(); + + public ClientPanel() + { + try + { +// Load up the dialog contents. + java.io.InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/endlessloopsoftware/ego/client/localSelect.gui_xml"); + JPanel panel = DialogResource.load(is); + //JPanel panel = DialogResource.load("com/endlessloopsoftware/ego/client/localSelect.gui_xml"); + +// Attach beans to fields. + selectStudyButton = (JButton) DialogResource.getComponentByName(panel, "SelectStudy"); + viewInterviewButton = (JButton) DialogResource.getComponentByName(panel, "ViewInterview"); + statisticsButton = (JButton) DialogResource.getComponentByName(panel, "SummaryStatistics"); + gradientStatisticsButton = (JButton)DialogResource.getComponentByName(panel,"GradientStatistics"); + startInterviewButton = (JButton) DialogResource.getComponentByName(panel, "StartInterview"); + titleLabel = (JLabel) DialogResource.getComponentByName(panel, "Title"); + studyNameLabel = (JLabel) DialogResource.getComponentByName(panel, "StudyName"); + + jbInit(); + + this.setLayout(new GridLayout(1, 1)); + this.add(panel); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + + //Component initialization + private void jbInit() throws Exception + { + titleLabel.setBackground(Color.lightGray); + titleLabel.setBorder(BorderFactory.createRaisedBevelBorder()); + titleLabel.setHorizontalAlignment(SwingConstants.CENTER); + + studyNameLabel.setBorder(BorderFactory.createLoweredBevelBorder()); + studyNameLabel.setHorizontalAlignment(SwingConstants.CENTER); + studyNameLabel.setText(" "); + + selectStudyButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + doSelectStudy(e);}}); + + viewInterviewButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + doViewInterview(e);}}); + + statisticsButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + doSummaryStatistics(e);}}); + + startInterviewButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + doStartInterview(e);}}); + + fillPanel(); + } + + void fillPanel() + { + startInterviewButton.setEnabled(EgoClient.storage.getStudyLoaded()); + viewInterviewButton.setEnabled(EgoClient.storage.getStudyLoaded()); + statisticsButton.setEnabled(EgoClient.storage.getStudyLoaded()); + + studyNameLabel.setText(EgoClient.study.getStudyName()); + if (studyNameLabel.getText() == null) + { + studyNameLabel.setText(" "); + } + + if (EgoClient.storage.getStudyLoaded()) + { + + } + } + + private void doSelectStudy(ActionEvent e) + { + /* Clear out old data */ + EgoClient.study = new Study(); + EgoClient.storage = new EgoStore(); + EgoClient.interview = null; + + /* Read new study */ + EgoClient.storage.selectStudy(); + EgoClient.storage.readPackage(); + studyNameLabel.setText(EgoClient.study.getStudyName()); + + fillPanel(); + } + + private void doStartInterview(ActionEvent e) + { + EgoClient.uiPath = EgoClient.DO_INTERVIEW; + EgoClient.storage.setPackageInUse(); + try + { + EgoClient.interview = new Interview(EgoClient.study); + if (!EgoClient.interview._statisticsAvailable) + { + /* No Structural question for this study, warn user */ + int option = JOptionPane.showConfirmDialog(EgoClient.frame, "

This study has no questions with specified adjacency selections.

" + + "

You will be unable to generate any structural statistics for it.

" + + "

Continue anyway?

", + "No Statistics Available", JOptionPane.YES_NO_OPTION); + + if (option == JOptionPane.NO_OPTION) + { + EgoClient.interview = null; + } + } + } + catch (CorruptedInterviewException ex) { + /* No Structural question for this study, warn user */ + JOptionPane.showMessageDialog(EgoClient.frame, "Unable to create an interview from this file", + "No Statistics Available", JOptionPane.ERROR_MESSAGE); + EgoClient.interview = null; + } + + if (EgoClient.interview != null) + { + StartPanel.gotoPanel(); + } + } + + private void doViewInterview(ActionEvent e) + { + EgoClient.uiPath = EgoClient.VIEW_INTERVIEW; + + EgoClient.storage.setInterviewFile(null); + EgoClient.interview = null; + EgoClient.storage.selectInterview(); + } + + private void doSummaryStatistics(ActionEvent e) + { + /* Warn User this could take awhile */ + int ok = JOptionPane.showConfirmDialog(EgoClient.frame, "This operation could take over a minute. Should I continue?", + "Load Interview Statistics", JOptionPane.OK_CANCEL_OPTION); + + if (ok == JOptionPane.OK_OPTION) + { + SummaryPanel.gotoPanel(); + } + } +} + +/** + * $Log: ClientPanel.java,v $ + * Revision 1.1 2005/08/02 19:36:01 samag + * Initial checkin + * + * Revision 1.10 2004/04/08 15:06:06 admin + * EgoClient now creates study summaries from Server + * EgoAuthor now sets active study on server + * + * Revision 1.9 2004/04/06 15:46:11 admin + * cvs tags in headers + * + */ \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/ClientQuestionPanel.java b/src/com/endlessloopsoftware/ego/client/ClientQuestionPanel.java new file mode 100644 index 0000000..ef19ed7 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/ClientQuestionPanel.java @@ -0,0 +1,1017 @@ +package com.endlessloopsoftware.ego.client; + +/** + *

+ * Title: Egocentric Network Researcher + *

+ *

+ * Description: Configuration Utilities for an Egocentric network study + *

+ *

+ * Copyright: Copyright (c) 2002 + *

+ *

+ * Company: Endless Loop Software + *

+ * + * @author Peter C. Schoaff + * @version 1.0 $Id: QuestionPanel.java,v 1.1 2005/08/02 19:36:00 samag Exp $ + */ + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.util.Collections; +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 com.cim.dlgedit.loader.DialogResource; +import com.endlessloopsoftware.ego.Answer; +import com.endlessloopsoftware.ego.Question; +import com.endlessloopsoftware.egonet.Shared; +import com.endlessloopsoftware.elsutils.documents.WholeNumberDocument; +import com.endlessloopsoftware.elsutils.files.FileCreateException; +import com.endlessloopsoftware.elsutils.layout.CardPanel; +import org.egonet.util.listbuilder.ListBuilder; +import org.egonet.util.listbuilder.Selection; +import com.jgoodies.forms.builder.PanelBuilder; +import com.jgoodies.forms.layout.CellConstraints; +import com.jgoodies.forms.layout.FormLayout; + +// import java.text.*; +import java.util.Date; + +/** + * Generic Panel creation and handling routines for question editing + */ +public class ClientQuestionPanel extends JPanel implements Observer { + /* Lists */ + private final static int MAX_BUTTONS = 5; + + private final JRadioButton[] answerButtons = { new JRadioButton(), + new JRadioButton(), new JRadioButton(), new JRadioButton(), + new JRadioButton(), new JRadioButton() }; + + private final KeyStroke enter = KeyStroke + .getKeyStroke(KeyEvent.VK_ENTER, 0); + + private final KeyStroke[] numKey = { + KeyStroke.getKeyStroke(KeyEvent.VK_0, 0), + KeyStroke.getKeyStroke(KeyEvent.VK_1, 0), + KeyStroke.getKeyStroke(KeyEvent.VK_2, 0), + KeyStroke.getKeyStroke(KeyEvent.VK_3, 0), + KeyStroke.getKeyStroke(KeyEvent.VK_4, 0), + KeyStroke.getKeyStroke(KeyEvent.VK_5, 0), + KeyStroke.getKeyStroke(KeyEvent.VK_6, 0), + KeyStroke.getKeyStroke(KeyEvent.VK_7, 0), + KeyStroke.getKeyStroke(KeyEvent.VK_8, 0), + KeyStroke.getKeyStroke(KeyEvent.VK_9, 0) }; + + private final ActionListener keyActionListener = new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + numberKey_actionPerformed(e); + } + }; + + private final String ALTER_CARD = "ALTER"; + + private final String TEXT_CARD = "TEXT"; + + private final String NUMERICAL_CARD = "NUMERICAL"; + + private final String RADIO_CARD = "RADIO"; + + private final String MENU_CARD = "MENU"; + + /* UI Elements */ + private final Border listBorder; + + private JLabel titleText = new JLabel(); + + private JTextArea questionText = new JTextArea(); + + private final JTextArea answerTextField = new NoTabTextArea(); + + private final JTextArea numericalTextField = new NoTabTextArea(); + + private JButton questionButtonPrevious; + + private JButton questionButtonNext; + + private JProgressBar questionProgress; + + private final JComboBox answerMenu = new JComboBox(); + + private final ListBuilder alterList = new ListBuilder(); + + private final JCheckBox noAnswerBox = new JCheckBox("Don't Know"); + + /* Containers */ + private final JSplitPane questionSplit = new JSplitPane(); + + private final JList questionList = new JList(); + + private CardPanel answerPanel = new CardPanel(); + + private final JPanel radioPanel = new RadioPanel(); + + private final JPanel menuPanel = new MenuAnswerPanel(); + + private final JPanel textPanel = new TextAnswerPanel(); + + private final JPanel numericalPanel = new NumericalAnswerPanel(); + + private final JPanel questionPanelLeft; + + private final JPanel questionPanelRight; + + private final ButtonGroup answerButtonGroup = new ButtonGroup(); + + private ActionListener answerButtonListener; + + private final WholeNumberDocument wholeNumberDocument = new WholeNumberDocument(); + + private final PlainDocument plainDocument = new PlainDocument(); + + /* Question Iteration Variables */ + private Question question; + + private String alterName, pairName; + + /** + * Generates Panel for question editing to insert in file tab window + * + * @param parent + * parent frame for referencing composed objects + */ + public ClientQuestionPanel() { + listBorder = BorderFactory.createCompoundBorder(new TitledBorder( + new EtchedBorder(EtchedBorder.RAISED, Color.white, new Color( + 178, 178, 178)), "Questions"), BorderFactory + .createEmptyBorder(10, 10, 10, 10)); + + questionPanelLeft = getLeftPanel(); + questionPanelRight = getRightPanel(); + + try { + jbInit(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Component initialization + * + * @throws Exception + */ + private void jbInit() throws Exception { + // Question vars + question = EgoClient.interview.setInterviewIndex(EgoClient.interview + .getFirstUnansweredQuestion(), false); + + this.setMinimumSize(new Dimension(330, 330)); + this.setLayout(new GridLayout()); + + // Configure Split Frame + questionSplit.add(questionPanelLeft, JSplitPane.LEFT); + questionSplit.add(questionPanelRight, JSplitPane.RIGHT); + + questionSplit.setOneTouchExpandable(false); + questionSplit.setDividerLocation(0.33); + + // Provide minimum sizes for the two components in the split pane + Dimension minimumSize = new Dimension(100, 100); + questionPanelLeft.setMinimumSize(minimumSize); + questionPanelRight.setMinimumSize(minimumSize); + + /* Install event Handlers */ + questionList.getSelectionModel().addListSelectionListener( + new ListSelectionListener() { + public void valueChanged(ListSelectionEvent e) { + question_list_selectionChanged(e); + } + }); + + questionButtonPrevious + .addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + questionButtonPrevious_actionPerformed(e); + } + }); + + questionButtonNext + .addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + questionButtonNext_actionPerformed(e); + } + }); + + answerButtonListener = new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + questionAnsweredEventHandler(e); + } + }; + + answerMenu.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + 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); + } + }); + + noAnswerBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + noAnswerBox_actionPerformed(e); + } + }); + + for (int i = 0; i <= MAX_BUTTONS; i++) { + answerButtonGroup.add(answerButtons[i]); + answerButtons[i].addActionListener(answerButtonListener); + } + + // register all number buttons instead of till MAX_BUTTONS alone + // for (int i = 0; i <= MAX_BUTTONS; i++) + for (int i = 0; i <= 9; i++) { + this.registerKeyboardAction(keyActionListener, Integer + .toString(i + 1), numKey[i], + JComponent.WHEN_IN_FOCUSED_WINDOW); + } + this.registerKeyboardAction(keyActionListener, Integer.toString(0), + enter, JComponent.WHEN_IN_FOCUSED_WINDOW); + numericalTextField.registerKeyboardAction(keyActionListener, Integer + .toString(0), enter, JComponent.WHEN_FOCUSED); + answerTextField.registerKeyboardAction(keyActionListener, Integer + .toString(0), enter, JComponent.WHEN_FOCUSED); + answerMenu.registerKeyboardAction(keyActionListener, Integer + .toString(0), enter, JComponent.WHEN_FOCUSED); + + alterList.addListObserver(this); + + questionProgress.setMaximum(EgoClient.interview.getNumQuestions()); + questionProgress.setValue(EgoClient.interview.getQuestionIndex()); + + questionList.setModel(new DefaultListModel()); + EgoClient.interview + .fillList((DefaultListModel) questionList.getModel()); + + if (EgoClient.uiPath == EgoClient.VIEW_INTERVIEW) + questionList.setSelectedIndex(0); + + this.add(questionSplit, null); + + /* Differences in UI in conducting interview vs. viewing it */ + questionPanelLeft + .setVisible(EgoClient.uiPath == EgoClient.VIEW_INTERVIEW); + + fillPanel(); + } + + /** + * Hides the static frame EgoClient.frame and initializes it with an + * entirely new QuestionPanel + */ + static void gotoPanel() { + /* Return to first screen */ + EgoClient.frame.setVisible(false); + EgoClient.frame.setContentPane(new ClientQuestionPanel()); + EgoClient.frame.pack(); + + if (EgoClient.uiPath == EgoClient.DO_INTERVIEW) { + // EgoClient.frame.setSize(600, 530); + EgoClient.frame.setExtendedState(EgoClient.frame.getExtendedState() + | JFrame.MAXIMIZED_BOTH); + } else { + // EgoClient.frame.setSize(640, 530); + EgoClient.frame.setExtendedState(EgoClient.frame.getExtendedState() + | JFrame.MAXIMIZED_BOTH); + } + + EgoClient.frame.setVisible(true); + } + + private JPanel getLeftPanel() { + JPanel panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + // Configure List + JScrollPane questionListScroll = new JScrollPane(questionList); + questionListScroll.setBorder(listBorder); + questionListScroll.setMinimumSize(new Dimension(150, 150)); + questionListScroll + .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + questionListScroll.setAutoscrolls(true); + + questionList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + /* List Layout */ + panel.add(questionListScroll, new GridBagConstraints(0, 0, 2, 1, 1.0, + 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, + new Insets(0, 0, 0, 0), 29, 302)); + + return panel; + } + + private JPanel getRightPanel() { + // Load up the dialog contents. + JPanel panel = DialogResource + .load("com/endlessloopsoftware/ego/client/QuestionPanel.gui_xml"); + + // Attach beans to fields. + questionButtonPrevious = (JButton) DialogResource.getComponentByName( + panel, "questionButtonPrevious"); + questionButtonNext = (JButton) DialogResource.getComponentByName(panel, + "questionButtonNext"); + titleText = (JLabel) DialogResource.getComponentByName(panel, + "titleText"); + answerPanel = (CardPanel) DialogResource.getComponentByName(panel, + "answerPanel"); + questionProgress = (JProgressBar) DialogResource.getComponentByName( + panel, "questionProgress"); + questionText = (JTextArea) DialogResource.getComponentByName(panel, + "questionText"); + + /* Set up answer panel cards */ + answerPanel.add(new JScrollPane(alterList), ALTER_CARD); + answerPanel.add(new JScrollPane(textPanel), TEXT_CARD); + answerPanel.add(new JScrollPane(numericalPanel), NUMERICAL_CARD); + answerPanel.add(new JScrollPane(radioPanel), RADIO_CARD); + answerPanel.add(new JScrollPane(menuPanel), MENU_CARD); + + titleText.setFont(new java.awt.Font("Lucida Grande Bold", 0, 15)); + + 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); + questionText.setMargin(new Insets(4, 4, 4, 4)); + questionText.setBorder(BorderFactory.createLoweredBevelBorder()); + + answerMenu.setOpaque(true); + + answerTextField.setFont(new java.awt.Font("SansSerif", 0, 14)); + answerTextField.setLineWrap(true); + answerTextField.setRows(1); + answerTextField.setTabSize(4); + answerTextField.setWrapStyleWord(true); + + numericalTextField.setFont(new java.awt.Font("SansSerif", 0, 14)); + numericalTextField.setLineWrap(true); + numericalTextField.setRows(1); + numericalTextField.setTabSize(4); + numericalTextField.setWrapStyleWord(false); + + return panel; + } + + /** + * Updates right side question fields when the selection changes + * + * @param e + * event generated by selection change. + */ + public void question_list_selectionChanged(ListSelectionEvent e) { + question = EgoClient.interview.setInterviewIndex(questionList + .getSelectedIndex(), true); + fillPanel(); + } + + /*************************************************************************** + * fill List with appropriate questions Set other fields to selected + * question + */ + public void fillPanel() { + if (question != null) { + String[] alterNames = EgoClient.interview.getAlterStrings(question); + setButtonNextState(); + questionButtonPrevious + .setEnabled(EgoClient.interview.hasPrevious()); + + switch (question.questionType) { + case Question.EGO_QUESTION: + titleText.setText("Questions About You"); + break; + + case Question.ALTER_PROMPT: + titleText.setText("Whom do you know?"); + break; + + case Question.ALTER_QUESTION: + titleText.setText("

Questions About " + + alterNames[0] + "

"); + break; + + case Question.ALTER_PAIR_QUESTION: + titleText.setText("

Questions About " + + alterNames[0] + " and " + + alterNames[1] + "

"); + break; + } + + answerPanel.setVisible(false); + if ((question.questionType == Question.ALTER_PROMPT) + && EgoClient.study.getUIType().equals( + Shared.TRADITIONAL_QUESTIONS)) { + String qs = "Enter the names of " + + EgoClient.study.getNumAlters() + " people. "; + + if (EgoClient.interview.isLastAlterPrompt()) { + qs += "After entering " + EgoClient.study.getNumAlters() + + " names you can continue."; + } else { + qs += ""; + } + + questionText.setText(question.text); + + answerPanel.showCard(ALTER_CARD); + alterList.setMaxListSize(EgoClient.study.getNumAlters()); + alterList.setDescription(qs); + alterList.setElementName("Name: "); + alterList.setPresetListsActive(false); + alterList.setNameList(true); + alterList.setTitle("Your Acquaintances"); + alterList.setListStrings(EgoClient.interview.getAlterList()); + alterList + .setEditable(EgoClient.uiPath == EgoClient.DO_INTERVIEW); + + EgoClient.frame.flood(); + answerPanel.setVisible(true); + alterList.requestFocusOnFirstVisibleComponent(); + } else if (question.answerType == Question.TEXT) { + questionText.setText(question.text); + + answerPanel.showCard(TEXT_CARD); + answerPanel.validate(); + answerTextField.setDocument(plainDocument); + answerTextField.requestFocus(); + + if (question.answer.answered) { + answerTextField.setText(question.answer.string); + } else { + answerTextField.setText(""); + } + + answerTextField + .setEditable((EgoClient.uiPath == EgoClient.DO_INTERVIEW) + || (EgoClient.uiPath == EgoClient.VIEW_INTERVIEW)); + answerPanel.setVisible(true); + answerTextField.requestFocusInWindow(); + } else if (question.answerType == Question.NUMERICAL) { + questionText.setText(question.text); + + answerPanel.showCard(NUMERICAL_CARD); + numericalTextField.setDocument(wholeNumberDocument); + numericalTextField.requestFocusInWindow(); + + if (question.answer.answered) { + if (question.answer.value != -1) { + numericalTextField.setText(question.answer.string); + noAnswerBox.setSelected(false); + } else { + noAnswerBox.setSelected(true); + numericalTextField.setText(""); + } + } else { + numericalTextField.setText(""); + noAnswerBox.setSelected(false); + } + + boolean doInterview = (EgoClient.uiPath == EgoClient.DO_INTERVIEW); + numericalTextField.setEditable(doInterview + || (EgoClient.uiPath == EgoClient.VIEW_INTERVIEW)); + noAnswerBox.setEnabled(doInterview + || (EgoClient.uiPath == EgoClient.VIEW_INTERVIEW)); + answerPanel.setVisible(true); + numericalTextField.requestFocusInWindow(); + } else if (question.selections.length <= 5) { + questionText.setText(question.text); + + answerPanel.showCard(RADIO_CARD); + + if (question.answer.answered && (question.answer.value >= 0)) { + // int idx = question.selections.length - + // (question.answer.value + 1); + // if (answerButtons != null && idx < answerButtons.length + // && idx >= 0 + // && answerButtons.length != 0) + // answerButtons[question.answer.index].setSelected(true); + + int idx = question.selections.length + - (question.answer.value + 1); + if (answerButtons != null && answerButtons.length != 0) { + answerButtons[question.answer.index].setSelected(true); +// System.out.println("Question : " +// + question.toString() +// + "\n" +// + "Answer : " +// + question.answer.string +// + "\n" +// + "Answer index : " +// + question.answer.index +// + "\n" +// + "Answerbutton[answerindex].txt : " +// + answerButtons[question.answer.index] +// .getText()); + } + + } else { + answerButtons[MAX_BUTTONS].setSelected(true); + } + + for (int i = 0; i < question.selections.length; i++) { + // answerButtons[i].setText("(" + (i + 1) + ") " + + // question.selections[i].string); + answerButtons[i].setText("(" + + question.selections[i].getValue() + ") " + + question.selections[i].getString()); + System.out.println("i : " + i + " " + "Text : " + + answerButtons[i].getText()); + answerButtons[i].setVisible(true); + answerButtons[i] + .setEnabled((EgoClient.uiPath == EgoClient.DO_INTERVIEW) + || (EgoClient.uiPath == EgoClient.VIEW_INTERVIEW)); + } + + for (int i = question.selections.length; i < MAX_BUTTONS; i++) { + answerButtons[i].setVisible(false); + } + answerPanel.setVisible(true); + } else { + questionText.setText(question.text); + answerPanel.showCard(MENU_CARD); + + answerMenu.setActionCommand("Initialization"); + answerMenu.removeAllItems(); + + answerMenu.addItem("Select an answer"); + for (int i = 0; i < question.selections.length; i++) { + answerMenu.addItem(question.selections[i]); + } + + if (question.answer.value > -1) { + answerMenu.setSelectedIndex(question.answer.index + 1); + } else { + answerMenu.setSelectedIndex(0); + } + answerMenu.setActionCommand("User Input"); + answerMenu + .setEnabled((EgoClient.uiPath == EgoClient.DO_INTERVIEW) + || (EgoClient.uiPath == EgoClient.VIEW_INTERVIEW)); + answerPanel.setVisible(true); + answerMenu.requestFocusInWindow(); + answerMenu.showPopup(); + } + } else { + /** @todo no question, clear */ + /** Abort study */ + } + } + + /*************************************************************************** + * 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 + */ + private void fillAnswer(Answer answer) { + // Don't touch values if we are just viewing interview + // if (EgoClient.uiPath == EgoClient.VIEW_INTERVIEW) + // return; + + if (question.questionType == Question.ALTER_PROMPT) { + answer.string = "Egonet - University of Florida"; + answer.value = (alterList.getListStrings().length); + answer.answered = (!EgoClient.interview.isLastAlterPrompt() || (answer.value >= EgoClient.study + .getNumAlters())); + EgoClient.interview.setAlterList(alterList.getListStrings()); + } else { + switch (question.answerType) { + case Question.NUMERICAL: + if (noAnswerBox.isSelected() + || (numericalTextField.getText().length() > 0)) { + answer.timestamp = generateTimeStamp(); + System.out.println("Timestamp: " + answer.timestamp); + if (noAnswerBox.isSelected()) { + answer.answered = true; + answer.value = (Answer.NO_ANSWER); + answer.string = "No Answer"; + } else { + answer.string = numericalTextField.getText(); + answer.value = (Integer.valueOf(answer.string) + .intValue()); + answer.answered = true; + } + } else { + answer.value = (Answer.NO_ANSWER); + answer.answered = false; + } + break; + + case Question.TEXT: + answer.timestamp = generateTimeStamp(); + System.out.println("Timestamp: " + answer.timestamp); + answer.string = answerTextField.getText(); + answer.value = (answer.string.length()); + answer.answered = (answer.value != 0); + break; + + case Question.CATEGORICAL: + if (question.selections.length <= 5) { + int button = selectedButtonIndex(answerButtons); + answer.answered = (button != MAX_BUTTONS); + + if (answer.answered) { + answer.timestamp = generateTimeStamp(); + System.out.println("Timestamp: " + answer.timestamp); + answer.value = (question.selections[button].getValue()); + + // added 11/27/2007 + answer.index = question.selections[button].getIndex(); + // answer.index = question.selections.length - + // question.selections[button].getIndex(); + answer.adjacent = question.selections[button] + .isAdjacent(); + answer.string = question.selections[button].getString(); + // answer.timestamp = + // DateFormat.getDateInstance().format(new Date()); + } + } else { + int selection = answerMenu.getSelectedIndex() - 1; + answer.answered = (selection >= 0) + && (selection < question.selections.length); + + if (answer.answered) { + answer.timestamp = generateTimeStamp(); + System.out.println("Timestamp: " + answer.timestamp); + + answer.value = (question.selections[selection] + .getValue()); + + // added 11/27/2007 + answer.index = question.selections[selection] + .getIndex(); + + answer.adjacent = question.selections[selection] + .isAdjacent(); + answer.string = question.selections[selection] + .getString(); + // answer.timestamp = + // DateFormat.getDateInstance().format(new Date()); + } + } + break; + } + } + } + + // Generate a time stamp of the form mmddyyyyhhmmss + public String generateTimeStamp() { + Date tempdate = new Date(); + String month = String.valueOf(tempdate.getMonth()); + month = month.length() < 2 ? "0" + month : month; + String date = String.valueOf(tempdate.getDate()); + date = date.length() < 2 ? "0" + date : date; + String hours = String.valueOf(tempdate.getHours()); + hours = hours.length() < 2 ? "0" + hours : hours; + String minutes = String.valueOf(tempdate.getMinutes()); + minutes = minutes.length() < 2 ? "0" + minutes : minutes; + String seconds = String.valueOf(tempdate.getSeconds()); + seconds = seconds.length() < 2 ? "0" + seconds : seconds; + + String timestamp = month + " " + date + " " + + String.valueOf(tempdate.getYear() + 1900) + " " + hours + " " + + minutes + " " + seconds; + return timestamp; + } + + private void questionButtonDone_actionPerformed(ActionEvent e) { + SourceSelectPanel.gotoPanel(false); + } + + private void numberKey_actionPerformed(ActionEvent e) { + int key = Integer.parseInt(e.getActionCommand()) - 1; + System.out.println("Key pressed " + key); + if (key == -1 && questionButtonNext.isEnabled()) + questionButtonNext_actionPerformed(e); + + if (question.answerType == Question.CATEGORICAL) { + for (Selection sel : question.selections) { + // int val = question.selections[i].value; + // System.out.println("Selection value :" +sel.getValue()); + if (key == sel.getValue()) { + // answerButtons[key - 1].setSelected(true); + + answerButtons[sel.getIndex()].setSelected(true); + questionAnsweredEventHandler(e); + break; + } + + } + } + } + + private void questionButtonNext_actionPerformed(ActionEvent e) { + if (EgoClient.interview.hasNext()) { + question = EgoClient.interview.next(); + + if ((EgoClient.uiPath == EgoClient.DO_INTERVIEW) + && (question.questionType == Question.ALTER_PAIR_QUESTION)) { + setDefaultAnswer(); + } + + fillPanel(); + + if (EgoClient.uiPath == EgoClient.VIEW_INTERVIEW) { + questionList.setSelectedIndex(EgoClient.interview + .getQuestionIndex()); + } + } else { + try { + EgoClient.interview.completeInterview(); + } catch (FileCreateException ex) { + JOptionPane.showMessageDialog(EgoClient.frame, + "Unable to create interview statistics summary file.", + "Statistics Error", JOptionPane.WARNING_MESSAGE); + } + + JOptionPane.showMessageDialog(EgoClient.frame, + "You have completed this interview.", "Interview Complete", + JOptionPane.INFORMATION_MESSAGE); + + /* Return to first screen */ + SourceSelectPanel.gotoPanel(false); + } + + questionProgress.setValue(EgoClient.interview.getQuestionIndex()); + } + + private void questionButtonPrevious_actionPerformed(ActionEvent e) { + if (EgoClient.interview.hasPrevious()) { + question = EgoClient.interview.previous(); + fillPanel(); + + if (EgoClient.uiPath == EgoClient.VIEW_INTERVIEW) { + questionList.setSelectedIndex(EgoClient.interview + .getQuestionIndex()); + } + } + + questionProgress.setValue(EgoClient.interview.getQuestionIndex()); + } + + private int selectedButtonIndex(JRadioButton[] button) { + int ri = -1; + + for (int i = 0; i < button.length; i++) { + if (button[i].isSelected()) { + ri = i; + break; + } + } + + return (ri); + } + + private void setButtonNextState() { + if (question.questionType == Question.ALTER_PROMPT) { + questionButtonNext.setEnabled(question.answer.answered); + questionButtonNext.setText("Next Question"); + } else { + boolean next = EgoClient.interview.hasNext(); + + if (next == false) { + questionButtonNext.setText("Study Complete"); + questionButtonNext + .setEnabled((EgoClient.uiPath == EgoClient.DO_INTERVIEW) + && question.answer.answered); + } else { + questionButtonNext.setText("Next Question"); + questionButtonNext.setEnabled(question.answer.answered); + } + } + } + + private void questionAnsweredEventHandler(ActionEvent e) { + if (e.getActionCommand() != "Initialization") { + fillAnswer(question.answer); + setButtonNextState(); + } + } + + private void answerTextEvent(DocumentEvent e) { + fillAnswer(question.answer); + setButtonNextState(); + } + + public void update(Observable o, Object arg) { + fillAnswer(question.answer); + setButtonNextState(); + } + + private void noAnswerBox_actionPerformed(ActionEvent e) { + if (noAnswerBox.isSelected()) { + numericalTextField.setText(""); + } + fillAnswer(question.answer); + setButtonNextState(); + } + + private void setDefaultAnswer() { + if (!question.answer.answered + && (question.answerType == Question.CATEGORICAL) + && (question.answer.getAlters()[1] > (question.answer + .getAlters()[0] + 1))) { + int defaultAnswer = -1; + if (!question.selections[question.selections.length - 1] + .isAdjacent()) { + defaultAnswer = question.selections.length - 1; + } else { + for (int i = 0; i < question.selections.length; i++) { + if (!question.selections[i].isAdjacent()) { + defaultAnswer = i; + break; + } + } + } + + if ((defaultAnswer >= 0) + && (defaultAnswer < question.selections.length)) { + question.answer.value = (question.selections[defaultAnswer] + .getValue()); + question.answer.string = question.selections[defaultAnswer] + .getString(); + question.answer.adjacent = false; + question.answer.answered = true; + } + } + } + + /** + * Radio Panel + */ + class RadioPanel extends JPanel { + public RadioPanel() { + FormLayout layout = new FormLayout("r:p, 4dlu, max(250dlu;p):g", + "t:p, 4dlu, d, 4dlu, d, 4dlu, d, 4dlu, d"); + + layout.setRowGroups(new int[][] { { 1, 3, 5, 7, 9 } }); + + PanelBuilder builder = new PanelBuilder(layout); + + builder.addLabel("Answer:"); + builder.nextColumn(2); + builder.add(answerButtons[0]); + + for (int i = 1; i < MAX_BUTTONS; i++) { + builder.nextLine(2); + builder.nextColumn(2); + builder.add(answerButtons[i]); + } + + CellConstraints cc = new CellConstraints(); + this.setLayout(new FormLayout("f:p", "t:p")); + this.add(builder.getPanel(), cc.xy(1, 1)); + } + } + + /** + * Single Field Panel + */ + class TextAnswerPanel extends JPanel { + public TextAnswerPanel() { + CellConstraints cc = new CellConstraints(); + FormLayout layout = new FormLayout("r:p, 4dlu, max(250dlu;p):g", + "f:50dlu"); + + PanelBuilder builder = new PanelBuilder(layout); + + builder.addLabel("Answer:", cc.xy(1, 1, "right, top")); + builder.nextColumn(2); + builder.add(answerTextField); + + this.setLayout(new FormLayout("f:p", "t:p")); + this.add(builder.getPanel(), cc.xy(1, 1)); + } + } + + /** + * Single Field Panel + */ + class MenuAnswerPanel extends JPanel { + public MenuAnswerPanel() { + CellConstraints cc = new CellConstraints(); + FormLayout layout = new FormLayout("r:p, 4dlu, max(250dlu;p):g", + "t:d:g"); + + PanelBuilder builder = new PanelBuilder(layout); + + builder.addLabel("Answer:", cc.xy(1, 1, "right, top")); + builder.nextColumn(2); + builder.add(answerMenu); + + this.setLayout(new FormLayout("f:p", "t:p")); + this.add(builder.getPanel(), cc.xy(1, 1)); + } + } + + /** + * Numerical Panel + */ + class NumericalAnswerPanel extends JPanel { + public NumericalAnswerPanel() { + CellConstraints cc = new CellConstraints(); + FormLayout layout = new FormLayout("r:p, 4dlu, max(250dlu;p):g", + "f:d, 4dlu, d"); + + PanelBuilder builder = new PanelBuilder(layout); + + builder.addLabel("Answer:", cc.xy(1, 1, "right, top")); + builder.nextColumn(2); + builder.add(numericalTextField); + builder.nextLine(2); + builder.nextColumn(2); + builder.add(noAnswerBox); + + this.setLayout(new FormLayout("f:p", "t:p")); + this.add(builder.getPanel(), cc.xy(1, 1)); + } + } + +} + +/** + * Extends JPanel class to keep focus in right panel of split question panel + */ +class RightPanel extends JPanel { + public boolean isFocusCycleRoot() { + return (true); + } +} + +/** + * Extends JTextArea to make tabs focus change events in question text areas + */ +class NoTabTextArea extends JTextArea { + public NoTabTextArea() { + super(); + setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, + Collections.EMPTY_SET); + setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, + Collections.EMPTY_SET); + } +} + +/** + * $Log: QuestionPanel.java,v $ Revision 1.1 2005/08/02 19:36:00 samag Initial + * checkin Revision 1.10 2004/04/11 00:24:48 admin Fixing headers + */ diff --git a/src/com/endlessloopsoftware/ego/client/EgoClient.java b/src/com/endlessloopsoftware/ego/client/EgoClient.java new file mode 100644 index 0000000..37b10e4 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/EgoClient.java @@ -0,0 +1,63 @@ +package com.endlessloopsoftware.ego.client; + +import com.endlessloopsoftware.ego.Shared; +import com.endlessloopsoftware.ego.Study; +import javax.swing.JFrame; + +/** + *

Title: Egocentric Networks Client Program

+ *

Description: Subject Interview Client.

+ *

Copyright: Copyright (c) 2002 - 2004

+ *

Company: Endless Loop Software

+ * @author Peter Schoaff + * + * $Id: EgoClient.java,v 1.1 2005/08/02 19:36:01 samag Exp $ + */ + +public class EgoClient +{ + public static Study study = new Study(); + public static EgoStore storage = new EgoStore(); + public static ClientFrame frame = new ClientFrame(); + public static Interview interview = null; + public static int uiPath; + + /** + * Used to create drop down menus of different "modes" + */ + public static final int SELECT = 0; + public static final int DO_INTERVIEW = 1; + public static final int VIEW_INTERVIEW = 2; + public static final int VIEW_SUMMARY = 3; + + private boolean packFrame = false; + + //Construct the application + public EgoClient() + { + SourceSelectPanel.gotoPanel(true); + + frame.setVisible(true); + frame.setExtendedState(frame.getExtendedState()|JFrame.MAXIMIZED_BOTH); + + } + + public static ClientFrame getFrame() + { + return (frame); + } + + //Main method + public static void main(String[] args) + { + // save this -- it might be useful for the web part later + /*System.setProperty("java.security.policy", "security.policy"); + if(System.getSecurityManager() == null) { + System.setSecurityManager(new java.rmi.RMISecurityManager()); + }*/ + + //if(true) return; + Shared.configureUI(); + new EgoClient(); + } +} \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/EgoStore.java b/src/com/endlessloopsoftware/ego/client/EgoStore.java new file mode 100644 index 0000000..5774dd6 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/EgoStore.java @@ -0,0 +1,854 @@ +package com.endlessloopsoftware.ego.client; + +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: EgoStore.java,v 1.1 2005/08/02 19:36:01 samag Exp $ + */ + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.*; +import java.util.prefs.Preferences; + +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.filechooser.FileFilter; +import javax.swing.ProgressMonitor; +import javax.swing.SwingWorker; + +import org.egonet.exceptions.CorruptedInterviewException; +import org.egonet.exceptions.FileMismatchException; + +import com.endlessloopsoftware.ego.Shared; +import com.endlessloopsoftware.ego.Study; +import com.endlessloopsoftware.ego.client.statistics.Statistics; +import com.endlessloopsoftware.elsutils.files.ExtensionFileFilter; +import com.endlessloopsoftware.elsutils.files.FileCreateException; +import com.endlessloopsoftware.elsutils.files.FileHelpers; +import com.endlessloopsoftware.elsutils.files.FileReadException; + +import electric.xml.Document; +import electric.xml.Element; +import electric.xml.ParseException; + +/******************************************************************************* + * Handles IO for the EgoNet program Tracks data files and changes to those + * files + */ +public class EgoStore extends Observable { + private File packageFile = null; + + private File interviewFile = null; + + private boolean loaded = false; + + private boolean interviewLoaded = false; + + private Document packageDocument = null; + + private static final FileFilter packageFilter = new ExtensionFileFilter( + "Study Definition Files", "ego"); + + private static final FileFilter interviewFilter = new ExtensionFileFilter( + "Interview Files", "int"); + + private static final String FILE_PREF = "FILE_PREF"; + + + + /** + * Sets parent frame + * + * @param g + * EgoClient + */ + public EgoStore() { + } + + /*************************************************************************** + * Notifies observers that a field in the study has changed + */ + public void notifyObservers() { + setChanged(); + super.notifyObservers(this); + } + + /*************************************************************************** + * Returns study file + * + * @return studyFile file containing study overview information + */ + public boolean getStudyLoaded() { + return (loaded); + } + + /*************************************************************************** + * Returns study file + * + * @return studyFile file containing study overview information + */ + public File getPackageFile() { + return (packageFile); + } + + /*************************************************************************** + * Returns interview file + * + * @return interview file containing answers + */ + public File getInterviewFile() { + return (interviewFile); + } + + /*************************************************************************** + * Sets interview file variable and notifies observers of change to study + * + * @param f + * question file + */ + public void setInterviewFile(File f) { + interviewFile = f; + notifyObservers(); + } + + /*************************************************************************** + * Sets baseQuestionFile variable and notifies observers of change to study + * + * @param f + * question file + */ + public void setPackageFile(File f) { + packageFile = f; + notifyObservers(); + } + + /*************************************************************************** + * Select a directory in which to store project related files Create + * subdirectories if needed. + */ + public void selectStudy() { + + JFileChooser jNewStudyChooser = new JFileChooser(); + File f, directory; + + Preferences prefs = null; + try { + prefs = Preferences.userNodeForPackage(EgoClient.class); + } catch (Throwable t) { + // eat this exception + } + + + jNewStudyChooser.addChoosableFileFilter(packageFilter); + jNewStudyChooser.setDialogTitle("Select Study Definition File"); + + if (getPackageFile() != null) { + jNewStudyChooser.setCurrentDirectory(getPackageFile().getParentFile()); + } else { + if(prefs!=null) + directory = new File(prefs.get(FILE_PREF, ".")); + directory = new File("."); + jNewStudyChooser.setCurrentDirectory(directory); + } + + if (JFileChooser.APPROVE_OPTION == jNewStudyChooser.showOpenDialog(EgoClient.getFrame())) { + f = jNewStudyChooser.getSelectedFile(); + + if (f != null) { + try { + if (!f.canRead()) { + throw new FileReadException(); + } else { + setPackageFile(f); + + // Store location in prefs file + if(prefs!=null) + prefs.put(FILE_PREF, f.getParent()); + } + } catch (Exception e) { + JOptionPane.showMessageDialog(null, + "Unable to read study file.", "File Error", + JOptionPane.ERROR_MESSAGE); + } + } + } + } + + /** + * File filter to filter the interview files based on selected study The + * file chooser displays only the interview files compatible with the + * currently chosen study + * + * @author sonam + * + */ + public class VersionFileFilter extends ExtensionFileFilter { + Map cacheResults = new HashMap(); + + public VersionFileFilter(String description, String extension) { + super(description, extension); + } + + public void cacheList(File currentDirectory, final ProgressMonitor progress) + { + int ct = 0; + + for(File ptr : currentDirectory.listFiles()) + { + final Integer tct = ++ct; + cacheResults.put(ptr, ptr.canRead() && ptr.isFile() && cacheAccept(ptr)); + javax.swing.SwingUtilities.invokeLater(new Runnable() {public void run() {progress.setProgress(tct);}} ); + + } + } + + public boolean accept(File ptr) + { + if(cacheResults.containsKey(ptr)) { + // cache hit + return cacheResults.get(ptr); + } else { + // cache miss + boolean accept = ptr.canRead() && cacheAccept(ptr); + cacheResults.put(ptr, accept); + return accept; + } + } + + public boolean cacheAccept(java.io.File f) { + + if(f.isDirectory()) + return true; + + boolean cantread = (!f.isFile()) || (!f.canRead()); + if(cantread) + return !cantread; + + boolean accept = true; + try { + // compare study id of interview file with id of currently + // selected study + Document document = new Document(f); + long studyId = Long.parseLong(document.getRoot().getAttribute( + "StudyId")); + if (studyId != EgoClient.study.getStudyId()) { + throw (new FileMismatchException()); + } + // readInterview(f); + } catch (FileMismatchException exception) { + accept = false; + //exception.printStackTrace(); + } catch (ParseException ex) { + accept = false; + //ex.printStackTrace(); + } catch (Throwable t) { + accept = false; + } + return accept; + } + + public String getDescription() { + String str = "Interview files"; + return str; + } + } + + /*************************************************************************** + * Select a directory in which to store project related files Create + * subdirectories if needed. + */ + + public void selectInterview() { + + int option=-1; + + final File currentDirectory = new File(getPackageFile().getParent(), "/Interviews/"); + + + final int numFiles = currentDirectory.list().length; + + final VersionFileFilter filter = new VersionFileFilter("Interview Files", "int"); + final JFileChooser jNewInterviewChooser = new JFileChooser(); + final ProgressMonitor progressMonitor = new ProgressMonitor( + EgoClient.frame, + "Searching for interviews and caching the list of files that match the current study", "", 0, numFiles); + jNewInterviewChooser.setCurrentDirectory(currentDirectory); + jNewInterviewChooser.setDialogTitle("Select Interview File"); + + progressMonitor.setMaximum(numFiles); + + SwingWorker filterWorker = new SwingWorker() { + public Integer doInBackground() + { + filter.cacheList(currentDirectory, progressMonitor); + return 0; + } + + public void done() { + progressMonitor.close(); + + jNewInterviewChooser.addChoosableFileFilter(filter); + int result = jNewInterviewChooser.showOpenDialog(EgoClient.frame); + + if (JFileChooser.APPROVE_OPTION == result) { + final File f = jNewInterviewChooser.getSelectedFile(); + + try { + //if (!(f != null && f.canRead())) { + if (!f.canRead()) { + throw new IOException("Couldn't read file"); + } else { + setInterviewFile(f); + EgoClient.interview = EgoClient.storage.readInterview(); + if(EgoClient.interview != null) + ViewInterviewPanel.gotoPanel(); + } + } catch (Throwable e) { + /** @todo Handle file failure */ + e.printStackTrace(); + + JOptionPane.showMessageDialog(null, + "Unable to read interview file.", "File Error", + JOptionPane.ERROR_MESSAGE); + } + } + + } + + }; + progressMonitor.setProgress(0); + progressMonitor.setMillisToDecideToPopup(0); + progressMonitor.setMillisToPopup(0); + + filterWorker.execute(); + } + + /*************************************************************************** + * Reads in study information from an XML like input file Includes files + * paths and arrays of question orders + */ + public void readPackage() { + File file = getPackageFile(); + + if (file != null) { + try { + packageDocument = new Document(file); + EgoClient.study = new Study(packageDocument); + loaded = true; + } catch (ParseException ex) { + /** @todo handle package parsing error */ + loaded = false; + } + } + } + + /*************************************************************************** + * Writes an attribute to the package file to indicate that it is being used + * for interviews. EgoNet will then warn before allowing user to save + */ + public void setPackageInUse() { + try { + if (getPackageFile().canWrite()) { + Element root = packageDocument.getRoot(); + root.setAttribute("InUse", "Y"); + packageDocument.write(getPackageFile()); + + getPackageFile().setReadOnly(); + } + } catch (IOException ex) { + JOptionPane.showMessageDialog(null, "Unable to update study file.", + "File Error", JOptionPane.ERROR_MESSAGE); + } catch (SecurityException ignored) { + } + } + + /*************************************************************************** + * Reads in study information from an XML like input file Includes files + * paths and arrays of question orders + * + * @return interview structure derived from file + */ + public Interview readInterview() { + Interview interview = null; + + try { + interview = readInterview(getInterviewFile()); + } catch (FileReadException e) { + JOptionPane.showMessageDialog(EgoClient.frame, + "Unable to Read Interview.", "Read Interview Error", + JOptionPane.ERROR_MESSAGE); + } catch (FileMismatchException e) { + JOptionPane.showMessageDialog(EgoClient.frame, + "Interview file not created from this study file.", + "Read Interview Error", JOptionPane.ERROR_MESSAGE); + } + + return (interview); + } + + /*************************************************************************** + * Reads in study information from an XML like input file Includes files + * paths and arrays of question orders + * + * @param f + * file from which to read interview + * @return Interview derived from file + * @throws FileReadException + * @throws FileMismatchException + */ + private Interview readInterview(File f) throws FileReadException, + FileMismatchException { + Interview interview = null; + long studyId; + + try { + + Document document = new Document(f); + + /* make sure id matches study */ + studyId = Long + .parseLong(document.getRoot().getAttribute("StudyId")); + if (studyId != EgoClient.study.getStudyId()) { + interviewLoaded = false; + interview = null; + throw (new FileMismatchException()); + } + interview = Interview.readInterview(document.getRoot()); + interviewLoaded = true; + } catch (CorruptedInterviewException ex) { + interviewLoaded = false; + interview = null; + + throw (new FileReadException()); + } catch (ParseException ex) { + interviewLoaded = false; + interview = null; + + throw (new FileReadException()); + } + + return (interview); + } + + /*************************************************************************** + * Writes all questions to a package file for later use + * + * @return successful + * @throws FileCreateException + * @throws FileReadException + */ + public boolean saveInterview() throws FileCreateException, + FileReadException { + boolean exists = false; + boolean complete = false; + boolean confirmed = false; + boolean resume = false; + boolean success = false; + + try { + String[] name = EgoClient.interview.getName(); + File path = new File(getPackageFile().getParent(), "/Interviews/"); + File f = new File(path, name[0].toLowerCase() + "_" + + name[1].toLowerCase() + ".int"); + + if (!path.exists()) { + path.mkdir(); + } + + if (f.exists()) { + exists = true; + + try { + Document document = new Document(f); + Element e = document.getRoot(); + complete = e.getBoolean("Complete"); + } catch (Exception ex) { + exists = false; + complete = false; + } + + if (exists && complete) { + int selected = JOptionPane + .showConfirmDialog( + EgoClient.frame, + "There is already a complete interview for " + + name[0] + + " " + + name[1] + + "\nDo you wish to replace it with a new interview?", + "Completed Interview Exists", + JOptionPane.YES_NO_OPTION); + + if (selected == JOptionPane.YES_OPTION) { + exists = false; + } + confirmed = true; + } else if (exists && !complete) { + int selected = JOptionPane + .showConfirmDialog( + EgoClient.frame, + "There is already an incomplete interview for " + + name[0] + + " " + + name[1] + + "\nWould you like to continue this interview?", + "Incomplete Interview Exists", + JOptionPane.YES_NO_OPTION); + + if (selected == JOptionPane.YES_OPTION) { + resume = true; + confirmed = true; + } + } + + if (exists && !confirmed) { + int selected = JOptionPane + .showConfirmDialog( + EgoClient.frame, + "Should I erase the old interview and start a new one?", + "Delete Interview", + JOptionPane.YES_NO_OPTION); + + if (selected == JOptionPane.YES_OPTION) { + exists = false; + } + confirmed = true; + } + } + + if (!exists) { + writeInterviewFile(f); + success = true; + setInterviewFile(f); + } else if (resume) { + EgoClient.interview = readInterview(f); + + if (EgoClient.interview != null) { + success = true; + setInterviewFile(f); + } + } + } catch (SecurityException e) { + JOptionPane.showMessageDialog(EgoClient.frame, + "Unable to create interview directory.", + "New Interview Error", JOptionPane.ERROR_MESSAGE); + throw new FileCreateException(); + } catch (FileReadException e) { + JOptionPane.showMessageDialog(EgoClient.frame, + "Unable to Read Interview.", "Read Interview Error", + JOptionPane.ERROR_MESSAGE); + throw new FileCreateException(); + } catch (FileMismatchException e) { + JOptionPane.showMessageDialog(EgoClient.frame, + "Interview file not created from this study file.", + "Read Interview Error", JOptionPane.ERROR_MESSAGE); + throw new FileCreateException(); + } + + return (success); + } + + /*************************************************************************** + * Writes all questions to a package file for later use + * + * @throws FileCreateException + */ + public void writeInterviewFile() throws FileCreateException { + if (getInterviewFile() != null) { + writeInterviewFile(getInterviewFile()); + } + } + + /*************************************************************************** + * Writes all questions to a package file for later use + * + * @param f + * File to write data to + * @throws FileCreateException + */ + private void writeInterviewFile(File f) throws FileCreateException { + Document document = new Document(); + + if (f != null) { + document.setEncoding("UTF-8"); + document.setVersion("1.0"); + Element interviewDocument = document.setRoot("Interview"); + + interviewDocument.setAttribute("StudyId", Long + .toString(EgoClient.study.getStudyId())); + interviewDocument.setAttribute("StudyName", EgoClient.study + .getStudyName()); + interviewDocument.setAttribute("NumAlters", Integer + .toString(EgoClient.study.getNumAlters())); + interviewDocument.setAttribute("Creator", Shared.version); + + EgoClient.interview.writeInterview(interviewDocument); + + try { + document.write(f); + } catch (Exception e) { + JOptionPane + .showMessageDialog( + EgoClient.frame, + "Unable to write interview. \nYour answers are not being saved so the interview will now abort.\n Please report this error.", + "Interview Error", JOptionPane.ERROR_MESSAGE); + System.exit(1); + } + } + } + + /*************************************************************************** + * Writes all questions to a package file for later use + * + * @param stats + * Statistics object + * @throws FileCreateException + */ + public void writeStatisticsFiles(Statistics stats, String[] egoName) + throws FileCreateException { + File file = getInterviewFile(); + String name = file.getName(); + String statdir; + String parentDir; + + if (getPackageFile() != null) { + try { + parentDir = getPackageFile().getParent(); + statdir = (new File(parentDir, "/Statistics/")) + .getCanonicalPath(); + } catch (IOException e) { + e.printStackTrace(); + throw new FileCreateException(); + } + + file = int2ist(statdir, name); + writeStatisticsFile(file, stats); + + file = int2matrix(statdir, name); + writeAdjacencyFile(file, stats, egoName, false); + + file = int2weightedmatrix(statdir, name); + writeAdjacencyFile(file, stats, egoName, true); + } + + } + + /*************************************************************************** + * Writes all questions to a package file for later use + * + * @param f + * File to write data to + * @param stats + * Statistics Object + * @throws FileCreateException + */ + private void writeAdjacencyFile(File f, Statistics stats, String[] name, + boolean weighted) { + PrintWriter adjacencyWriter; + try { + adjacencyWriter = new PrintWriter(new BufferedWriter( + new FileWriter(f), (32 * 1024))); + } catch (IOException e1) { + adjacencyWriter = null; + } + + if (adjacencyWriter != null) { + try { + stats.writeAdjacencyArray(name[0] + " " + name[1], + adjacencyWriter, weighted); + } finally { + adjacencyWriter.close(); + } + } + } + + /*************************************************************************** + * Writes all questions to a package file for later use + * + * @param f + * File to write data to + * @param stats + * Statistics Object + * @throws FileCreateException + */ + private void writeStatisticsFile(File f, Statistics stats) + throws FileCreateException { + Document document = new Document(); + + document.setEncoding("UTF-8"); + document.setVersion("1.0"); + Element study = document.setRoot("Statistics"); + + study.setAttribute("StudyId", Long.toString(EgoClient.study + .getStudyId())); + study.setAttribute("Creator", Shared.version); + + stats.writeStructuralStatistics(study); + EgoClient.interview.writeEgoAnswers(study); + stats.writeCompositionalStatistics(study); + + try { + document.write(f); + } catch (IOException e) { + JOptionPane.showMessageDialog(EgoClient.frame, + "Unable to write statistics.", "Interview Error", + JOptionPane.ERROR_MESSAGE); + throw new FileCreateException(); + } + } + + /*************************************************************************** + * Creates a new csv file to store statistics in + * + * @param parent + * Frame for centering error messages + * @param filetype + * file type for newFile call + * @param defaultname + * Default name of new file for newFile call + * @returns PrintWriter for csv file + * @throws IOException + */ + public PrintWriter newStatisticsPrintWriter(String filetype, String suffix, + String defaultname) { + Preferences prefs = Preferences.userNodeForPackage(EgoClient.class); + File startdir; + PrintWriter out = null; + File file; + + if (getPackageFile() != null) { + startdir = new File(getPackageFile().getParent(), "/Statistics/"); + } else { + startdir = new File(prefs.get("STAT_DIR", "Statistics/")); + } + + try { + file = FileHelpers.newFile(filetype, defaultname, "Statistics", "." + + suffix, startdir, EgoClient.frame, false); + + if (file == null) { + throw new FileCreateException(); + } + + out = new PrintWriter(new BufferedWriter(new FileWriter(file), + (32 * 1024))); + prefs.put("STAT_DIR", file.getParent()); + } catch (Exception fce) { + JOptionPane.showMessageDialog(EgoClient.frame, "Unable to create " + + filetype + "file.", "New Statics File Error", + JOptionPane.ERROR_MESSAGE); + out = null; + } + + return out; + } + + /*************************************************************************** + * Creates a new .ist file from an existing complete interview file + * + * @param interviewFile + * File from which to read interview + * @throws FileCreateException + */ + public void generateStatisticsFile(File interviewFile) + throws FileCreateException { + EgoClient.interview = null; + + setInterviewFile(interviewFile); + + if (EgoClient.storage.getInterviewFile() != null) { + EgoClient.interview = EgoClient.storage.readInterview(); + } + + if ((EgoClient.interview != null) && EgoClient.interview.isComplete()) { + EgoClient.interview.completeInterview(); + } else { + throw new FileCreateException(); + } + } + + public static File int2ist(String istPath, String intFile) { + return (new File(istPath, intFile + .substring(0, intFile.lastIndexOf(".")) + + ".ist")); + } + + public static File int2matrix(String istPath, String intFile) { + return (new File(istPath, intFile + .substring(0, intFile.lastIndexOf(".")) + + "_matrix.csv")); + } + + public static File int2weightedmatrix(String istPath, String intFile) { + return (new File(istPath, intFile + .substring(0, intFile.lastIndexOf(".")) + + "_weighted_matrix.csv")); + } +} + +/** + * $Log: EgoStore.java,v $ Revision 1.1 2005/08/02 19:36:01 samag Initial + * checkin + * + * Revision 1.10 2004/04/11 00:24:48 admin Fixing headers + * + * Revision 1.9 2004/04/08 15:06:06 admin EgoClient now creates study summaries + * from Server EgoAuthor now sets active study on server + * + * Revision 1.8 2004/04/06 23:09:19 admin storing statistics file path in prefs + * for server interviews + * + * Revision 1.7 2004/04/02 19:48:58 admin Keep Study Id when possible Store + * updated time in file + * + * Revision 1.6 2004/04/01 21:50:52 admin Aborting interview if unable to write + * answers to file + * + * Revision 1.5 2004/03/23 14:58:48 admin Update UI Study creation now occurs in + * instantiators + * + * Revision 1.4 2004/03/19 20:28:45 admin Converted statistics frome to a panel. + * Incorporated in a tabbed panel as part of main frame. + * + * Revision 1.3 2004/02/10 20:10:43 admin Version 2.0 beta 3 + * + * Revision 1.2 2003/12/08 15:57:50 admin Modified to generate matrix files on + * survey completion or summarization Extracted statistics models + * + * Revision 1.1 2003/12/04 15:14:09 admin Merging EgoNet and EgoClient projects + * so that they can share some common classes more easily. + * + * Revision 1.3 2003/12/03 15:12:08 admin creating subdirectories for graph and + * statistics + * + * Revision 1.2 2003/11/25 19:23:35 admin Adding weighted matrix output First + * checking of graph panel + * + * Revision 1.1.1.1 2003/06/08 14:50:21 admin Egocentric Network client program + * + * Revision 1.7 2002/09/01 20:05:11 admin UI Changes to allow selection of + * arbitrary ALTER_PAIR question for stats all sorts of bug fixes optimized + * statistics allow no answer for numerical questions + * + * Revision 1.6 2002/08/12 19:19:15 admin Summary Screen + * + * Revision 1.5 2002/08/11 22:29:37 admin statistics frame, new file structure + * + * Revision 1.4 2002/07/31 20:19:36 admin Statistics + * + * Revision 1.3 2002/07/25 14:55:50 admin question Links + * + * Revision 1.2 2002/07/22 02:53:21 admin Interview implemented and working + * + * Revision 1.1.1.1 2002/07/19 15:51:30 admin Client Module for EgoNet project + * + */ diff --git a/src/com/endlessloopsoftware/ego/client/ImageFilter.java b/src/com/endlessloopsoftware/ego/client/ImageFilter.java new file mode 100644 index 0000000..5a3c1d6 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/ImageFilter.java @@ -0,0 +1,48 @@ +package com.endlessloopsoftware.ego.client; + +import java.io.File; +import javax.swing.*; +import javax.swing.filechooser.*; + +public class ImageFilter extends FileFilter { + + //Accept all directories and all gif, jpg, tiff, or png files. + public boolean accept(File f) { + if (f.isDirectory()) { + return true; + } + + String extension = getExtension(f); + if (extension != null) { + if (extension.equals("tiff") || + extension.equals("tif") || + extension.equals("gif") || + extension.equals("jpeg") || + extension.equals("jpg") || + extension.equals("png")) { + //System.out.println("Extension : " + extension); + return true; + } else { + return false; + } + } + + return false; + } + + public static String getExtension(File f) { + String ext = null; + String s = f.getName(); + int i = s.lastIndexOf('.'); + + if (i > 0 && i < s.length() - 1) { + ext = s.substring(i+1).toLowerCase(); + } + return ext; + } + //The description of this filter + public String getDescription() { + return "Images"; + } +} + diff --git a/src/com/endlessloopsoftware/ego/client/Interview.java b/src/com/endlessloopsoftware/ego/client/Interview.java new file mode 100644 index 0000000..dcb6ae5 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/Interview.java @@ -0,0 +1,1167 @@ +package com.endlessloopsoftware.ego.client; + +/** + *

Copyright: Copyright (c) 2002 - 2004

+ *

Company: Endless Loop Software

+ * @author Peter Schoaff + * + * $Id: Interview.java,v 1.1 2005/08/02 19:35:59 samag Exp $ + */ + +import java.util.Collection; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.logging.Logger; + +import javax.swing.DefaultListModel; +import javax.swing.JOptionPane; + +import org.egonet.exceptions.CorruptedInterviewException; +import org.egonet.exceptions.MissingPairException; + +import com.endlessloopsoftware.ego.Answer; +import com.endlessloopsoftware.ego.Question; +import com.endlessloopsoftware.ego.Study; +import com.endlessloopsoftware.ego.client.StatRecord.EgoAnswer; +import com.endlessloopsoftware.ego.client.statistics.Statistics; +import com.endlessloopsoftware.egonet.Shared; +import com.endlessloopsoftware.egonet.util.AnswerDataValue; +import com.endlessloopsoftware.egonet.util.InterviewDataValue; +import com.endlessloopsoftware.elsutils.ELSMath; +import com.endlessloopsoftware.elsutils.files.FileCreateException; + +import electric.xml.Element; +import electric.xml.Elements; + +public class Interview +{ + private final static Logger logger = Logger.getLogger("Interview"); + + private final Answer[] _answers; + private final Study _study; + private int[][] _matrix; + private Statistics _stats = null; + private String[] _egoName = { "", ""}; + private boolean _complete; + private String[] _alterList = new String[0]; + + private int _qIndex = 0; + private final int _numAlterPairs; + private int _numAnswers; + private int _numAlters; + + boolean _statisticsAvailable = false; + + /******** + * Create interview from question list + * @param client parent object for globals + * @param numAlters number of alters to be elicited + * @throws CorruptedInterviewException if unable to read interview + */ + public Interview(Study study) + throws CorruptedInterviewException + { + /* Locals */ + int j, k; + Iterator questions; + + /* Calculate some interview values */ + _study = study; + _numAlters = study.getNumAlters(); + _numAlterPairs = ELSMath.summation(_numAlters - 1); + _numAnswers = + EgoClient.study.getQuestionOrder(Question.EGO_QUESTION).size() + + EgoClient.study.getQuestionOrder(Question.ALTER_PROMPT).size() + + (_numAlters * EgoClient.study.getQuestionOrder(Question.ALTER_QUESTION).size()) + + (_numAlterPairs * EgoClient.study.getQuestionOrder(Question.ALTER_PAIR_QUESTION).size()); + _answers = new Answer[_numAnswers]; + + /* Generate answer instances */ + _qIndex = 0; + + /* Ego Questions */ + questions = EgoClient.study.getQuestionOrder(Question.EGO_QUESTION).iterator(); + while (questions.hasNext()) + { + Long questionId = (Long) questions.next(); + Question question = _study.getQuestions().getQuestion(questionId); + + if (question == null) + { + throw new CorruptedInterviewException(); + } + else + { + _answers[_qIndex++] = new Answer(question.UniqueId, null); + } + } + + /* Alter Prompt Questions */ + questions = EgoClient.study.getQuestionOrder(Question.ALTER_PROMPT).iterator(); + while (questions.hasNext()) + { + Long questionId = (Long) questions.next(); + Question question = _study.getQuestions().getQuestion(questionId); + + if (question == null) + { + throw new CorruptedInterviewException(); + } + else + { + _answers[_qIndex++] = new Answer(question.UniqueId, null); + } + } + + /* Alter Questions */ + for (j = 0; j < _numAlters; j++) + { + questions = EgoClient.study.getQuestionOrder(Question.ALTER_QUESTION).iterator(); + int[] alter = { j }; + while (questions.hasNext()) + { + Long questionId = (Long) questions.next(); + Question question = _study.getQuestions().getQuestion(questionId); + if (question == null) + { + throw new CorruptedInterviewException(); + } + else + { + _answers[_qIndex++] = new Answer(question.UniqueId, alter); + } + } + } + + /* Alter Pair Questions */ + for (k = 0; k < _numAlters; k++) + { + for (j = (k + 1); j < _numAlters; j++) + { + questions = EgoClient.study.getQuestionOrder(Question.ALTER_PAIR_QUESTION).iterator(); + int[] alters = { k, j }; + while (questions.hasNext()) + { + Question question = _study.getQuestions().getQuestion((Long) questions.next()); + + if (question == null) + { + throw new CorruptedInterviewException(); + } + else + { + if (question.statable) + { + _statisticsAvailable = true; + } + + _answers[_qIndex++] = new Answer(question.UniqueId, alters); + } + } + } + } + } + + /***************************************** + * Generate an interview from a datavalue downloaded from a server + * @param data + */ + public Interview(Study study, InterviewDataValue data) + { + System.out.println("Creating Interview from data object"); + _study = study; + _numAlters = study.getNumAlters(); + _statisticsAvailable = true; + + _matrix = data.getAdjacencyMatrix(); + _egoName = new String[] {data.getFirstName(), data.getLastName()}; + _complete = data.getComplete().booleanValue(); + _alterList = data.getAlters(); + _numAnswers = data.getAnswerDataValues().length; + + _numAlterPairs = ELSMath.summation(_alterList.length - 1); + _numAnswers = data.getAnswerDataValues().length; + _answers = new Answer[_numAnswers]; + + //System.out.println(_study.getQuestions().size()); + //System.out.println(_study.getQuestions().dump()); + + for (int i = 0; i < data.getAnswerDataValues().length; ++i) + { + AnswerDataValue answerData = data.getAnswerDataValues()[i]; + _answers[i] = new Answer(answerData); + + //System.out.println("Answer for: " + _study.getQuestion(_answers[i].questionId)); + } + } + + /**** + * Called when user shutting down program + */ + public void exit() + { + if (!_complete) + { + try + { + EgoClient.storage.writeInterviewFile(); + } + catch (FileCreateException ignored) + { + System.err.println("Unable to write Interview File"); + } + } + } + + /**** + * Searches question list for all questions and places them in list + * @param dlm list model to use in inserting questions + */ + public void fillList(DefaultListModel dlm) + { + dlm.removeAllElements(); + + for (int i = 0; i < _numAnswers; i++) + { + //Question q = getQuestion(i); + + Question q = _study.getQuestions().getQuestion(_answers[i].questionId); + + if (q.questionType == Question.ALTER_PAIR_QUESTION && + (_study.getUIType().equals(Shared.PAIR_ELICITATION) || + _study.getUIType().equals(Shared.THREE_STEP_ELICITATION))) + { + /* Skip Alter Pair Questions for Interactive Linking Studies */ + } + else + { + String s = q.toString(); + + if (q.questionType == Question.ALTER_QUESTION) + { + s = s + "; alter " + _answers[i].getAlters()[0]; + } + else if (q.questionType == Question.ALTER_PAIR_QUESTION) + { + s = s + "; alters " + _answers[i].getAlters()[0] + "& " + _answers[i].getAlters()[1]; + } + + s = Question.questionTypeString(q.questionType) + ": " + s; + + dlm.addElement(s); + } + } + } + + /**** + * Returns total number of questions in an interview + * @return i number of questions + */ + public int getNumQuestions() + { + return _numAnswers; + } + + /**** + * Returns current answer from an interview + * Note, multiple answers may refer to same question + * @return i question index + */ + private Answer getCurrentAnswer() + { + return _answers[_qIndex]; + } + + /**** + * Sets current answer from an interview + * @param a new Answer + */ + private void setCurrentAnswer(Answer a) + { + /** @todo Validate answer */ + _answers[_qIndex] = a; + } + + /**** + * Returns current question from an interview + * @return i question index + */ + public int getQuestionIndex() + { + return _qIndex; + } + + /**** + * Returns current list of alters + * @return s String Array of alters + */ + public String[] getAlterList() + { + return _alterList; + } + + /**** + * Sets current list of alters + * @param s String Array of alters + */ + public void setAlterList(String[] s) + { + _alterList = s; + } + + /**** + * Gets a set containing all the answers which use a selected question + * @param qId Unique Identifier of question + * @return Set of answers using this question + */ + public Set getAnswerSubset(Long qId) + { + Set s = new HashSet(_numAlterPairs); + + for (int i = 0; i < _answers.length; i++) + { + if (_answers[i].questionId.equals(qId)) + { + s.add(_answers[i]); + } + } + + return (s); + } + + /**** + * Gets a List containing all the answers to ego questions + * @return List of answers using this question + */ + public List getEgoAnswers() + { + List l = new ArrayList(); + int index = 0; + Question q = _study.getQuestions().getQuestion(_answers[index].questionId); + + while (q.questionType == Question.EGO_QUESTION) + { + l.add(_answers[index]); + q = _study.getQuestions().getQuestion(_answers[++index].questionId); + } + + return (l); + } + + /**** + * Gets a List containing all the answers to alter questions + * @return List of answers using this question + */ + public List getAlterAnswers() + { + List l = new ArrayList (); + + Collection questionList = _study.getQuestions().getQuestionMap().values(); + for(Question q : questionList) + { + if(q.questionType != Question.ALTER_QUESTION) + continue; + + l.add(q); + } + + return (l); + } + + /**** + * Gets name of interview subject + * @return String Array of first and last name + */ + public String[] getName() + { + String[] s = { _egoName[0], _egoName[1] }; + + return s; + } + + /**** + * Sets name of interviewee + * @param first first name + * @param last last name + */ + public void setName(String first, String last) + { + _egoName[0] = first; + _egoName[1] = last; + } + + /**** + * Sets current question Index + * @param i index of question + * @param force choose the answer even if it's not valid (e.g. may be linked) + * @return Choosen question + */ + public Question setInterviewIndex(int i, boolean force) + { + Question q = null; + + if (!force) + { + i = nextValidAnswer(i, true); + } + + if ((i >= 0) && (i < _numAnswers)) + { + _qIndex = i; + + q = (Question) getQuestion(_qIndex).clone(); + q.answer = _answers[_qIndex]; + + // replaces $$n in String with alter name indexed as alter #n + q.text = completeText(q.text, q.answer.getAlters()); + } + + return (q); + } + + /**** + * Working forward from beginning, find first unanswered question + * @return index of first unanswered question + */ + public int getFirstUnansweredQuestion() + { + int i = 0; + int rv = getNumQuestions() - 1; + + i = nextValidAnswer(0, true); + while (i != -1) + { + if (!_answers[i].answered) + { + rv = i; + break; + } + + i = nextValidAnswer(i + 1, true); + } + + return (rv); + } + + /**** + * Working backwards from the current Answer, find the first answer + * which references a specified question. This is generally used for + * linked questions + * @param startIndex starting point from which to search + * @param id Unique Id of question + * @return answer Answer of matching question + */ + private Answer getPriorQuestionInstance(int startIndex, Long id) + { + Answer a = null; + int i; + + for (i = (startIndex - 1); i >= 0; i--) + { + if (_answers[i].questionId.equals(id)) + break; + } + + if (i >= 0) + { + a = _answers[i]; + } + + return (a); + } + + /**** + * Checks question link info of current answer to see if + * the question should be included in interview. + * @param index index of answer to check. + * @return bool true iff question passes link check + */ + private boolean checkQuestionLink(int index) + { + boolean b = false; + Question q = getQuestion(index); + + if (q.link.active) + { + Answer a = getPriorQuestionInstance(index, q.link.answer.questionId); + + if (a != null) + { + if (q.link.answer.value == Answer.ALL_ADJACENT) + { + if (a.adjacent) + { + b = true; + } + } + else + { + if (a.value == q.link.answer.value) + { + b = true; + } + } + } + else + { + /* This case means one of 2 things happened, the user linked to a non-existent question + or the linked question comes after this question. Neither case is proper but the + less evil appears to be to always ask this question in this case + */ + b = true; + } + } + else + { + /* No link to check */ + b = true; + } + + return (b); + } + + /**** + * Returns current question from an interview + * @return b true iff there are more questions + */ + public boolean hasNext() + { + int checkIndex = nextValidAnswer(_qIndex + 1, true); + + return (checkIndex != -1); + } + + /**** + * Returns next question from an interview which passes all link checks + * @param checkIndex answer at which to start + * @param forward true iff search forward, else search backwards + * @return answerIndex index of next or previous valid answer, -1 if none found + */ + private int nextValidAnswer(int checkIndex, boolean forward) + { + boolean b = false; + + while (!b && (checkIndex < _numAnswers) && (checkIndex >= 0)) + { + if (checkQuestionLink(checkIndex)) + { + b = true; + } + else + { + checkIndex = forward ? (checkIndex + 1) : (checkIndex - 1); + } + } + + if (!b) + { + checkIndex = -1; + } + + return (checkIndex); + } + + /**** + * Returns current question from an interview + * @return i question index + */ + public boolean hasPrevious() + { + int checkIndex = nextValidAnswer(_qIndex - 1, false); + + return (checkIndex != -1); + } + + /**** + * Returns current question from an interview + * @return i question index + */ + public Question next() + { + Question q; + + _qIndex = nextValidAnswer(_qIndex + 1, true); + + q = (Question) getQuestion(_qIndex).clone(); + q.answer = _answers[_qIndex]; + q.text = completeText(q.text, q.answer.getAlters()); + + if ((EgoClient.uiPath == EgoClient.DO_INTERVIEW) && ((_qIndex % 20) == 0)) + { + try + { + EgoClient.storage.writeInterviewFile(); + } + catch (FileCreateException ex) + { + /* reported at lower level */ + } + } + + return (q); + } + + /**** + * Returns current question from an interview + * @return i question index + */ + public Question previous() + { + Question q; + + _qIndex = nextValidAnswer(_qIndex - 1, false); + // assert (qIndex != -1); + + q = (Question) getQuestion(_qIndex).clone(); + q.answer = _answers[_qIndex]; + q.text = completeText(q.text, q.answer.getAlters()); + + return (q); + } + + /**** + * Is this question last alter prompt + * @return b true iff current question is last alter prompt + */ + public boolean isLastAlterPrompt() + { + boolean b = false; + Question q = (Question) _study.getQuestions().getQuestion(_answers[_qIndex].questionId); + + b = + (getQuestion(_qIndex).questionType == Question.ALTER_PROMPT) + && hasNext() + && (getQuestion(_qIndex + 1).questionType != Question.ALTER_PROMPT); + + return (b); + } + + /**** + */ + public String[] getAlterStrings(Question q) + { + String[] s = new String[2]; + + try + { + if ((q.answer.getAlters().length > 0) && (q.answer.getAlters()[0] != -1)) + { + s[0] = _alterList[q.answer.getAlters()[0]]; + } + + if ((q.answer.getAlters().length > 1) && (q.answer.getAlters()[1] != -1)) + { + s[1] = _alterList[q.answer.getAlters()[1]]; + } + } + catch (Exception ex) + { + s[0] = ""; + s[1] = ""; + } + + return s; + } + + /**** + * Returns alter or alter pair from the index into an interview + * @param index question index + * @return pair int array containing alter pair + * @throws NoSuchElementException + */ + private int[] calculateAlterPair(int index) + { + int na = EgoClient.study.getNumAlters(); + int strip = 0; + int primary = -1; + int secondary = -1; + int[] rval = new int[2]; + + try + { + /* Start by stripping ego and alter prompt questions */ + strip = + EgoClient.study.getQuestionOrder(Question.EGO_QUESTION).size() + + EgoClient.study.getQuestionOrder(Question.ALTER_PROMPT).size(); + + if (index < strip) + { + throw new NoSuchElementException(); + } + + index -= strip; + + /* Check alter questions */ + strip = na * EgoClient.study.getQuestionOrder(Question.ALTER_QUESTION).size(); + + if (index < strip) + { + /* It's an alter question */ + primary = index / EgoClient.study.getQuestionOrder(Question.ALTER_QUESTION).size(); + secondary = -1; + } + else + { + index -= strip; + + if (index >= _numAlterPairs) + { + throw new NoSuchElementException(); + } + + primary = 1; + while ((na - primary) < index) + { + index -= primary; + primary++; + } + } + } + catch (NoSuchElementException ex) + { + primary = -1; + secondary = -1; + } + + rval[0] = primary; + rval[1] = secondary; + return (rval); + } + + private Question getQuestion(int index) + { + return ((Question) _study.getQuestion(_answers[index].questionId)); + } + + /**** + * Replaces alter name placeholders with alter names + * @param s Question string to parse + * @param alters names of alters to put in placeholders + * @return modified string + */ + private String completeText(String s, int[] alters) + { + int parsePtr; + String oldS = s; + + try + { + for (parsePtr = s.indexOf("$$1");(parsePtr != -1); parsePtr = s.indexOf("$$1")) + { + s = s.substring(0, parsePtr) + _alterList[alters[0]] + s.substring(parsePtr + 3); + } + + for (parsePtr = s.indexOf("$$2");(parsePtr != -1); parsePtr = s.indexOf("$$2")) + { + s = s.substring(0, parsePtr) + _alterList[alters[1]] + s.substring(parsePtr + 3); + } + + for (parsePtr = s.indexOf("$$-1");(parsePtr != -1); parsePtr = s.indexOf("$$-1")) + { + s = s.substring(0, parsePtr) + _alterList[alters[0] - 1] + s.substring(parsePtr + 4); + } + + for (parsePtr = s.indexOf("$$");(parsePtr != -1); parsePtr = s.indexOf("$$")) + { + s = s.substring(0, parsePtr) + _alterList[alters[0]] + s.substring(parsePtr + 2); + } + } + catch (Exception ex) + { + s = oldS; + } + + return s; + } + + /******** + * Read interview information from an xml structure + * @param parent scope for extracting globals + * @param e XML Element, parent of interview tree + * @return Interview which is read + * @throws CorruptedInterviewException if unable to read interview + */ + public static Interview readInterview(Element e) throws CorruptedInterviewException + { + Interview interview; + String[] lAlterList; + Element alterListElem = e.getElement("AlterList"); + Element answerListElem = e.getElement("AnswerList"); + + try + { + /* Read alter list so we can size interview record */ + lAlterList = readAlters(alterListElem); + interview = new Interview(EgoClient.study); + interview._alterList = lAlterList; + + /* Read answers */ + EgoClient.study.readInterviewStudy(e); + interview._complete = e.getBoolean("Complete"); + + /* Read interviewee name */ + Element egoNameElem = e.getElement("EgoName"); + + if (egoNameElem != null) + { + interview._egoName[0] = egoNameElem.getString("First"); + interview._egoName[1] = egoNameElem.getString("Last"); + } + readAnswers(interview, answerListElem); + } + catch (CorruptedInterviewException ex) + { + interview = null; + throw (ex); + } + catch (Exception ex) + { + interview = null; + ex.printStackTrace(); + } + + return (interview); + } + + /******** + * Read alter list from an xml tree + * @param e XML Element, parent of alter list + * @return list List of Alters + */ + private static String[] readAlters(Element e) + { + Elements alterIter = e.getElements("Name"); + String[] lAlterList; + int lNumAlters; + int index = 0; + + lNumAlters = alterIter.size(); + lAlterList = new String[lNumAlters]; + + while (alterIter.hasMoreElements()) + { + lAlterList[index++] = alterIter.next().getTextString(); + } + + return (lAlterList); + } + + /******** + * Read alter list from an xml tree + * @param interview Interview to read answers into + * @param e XML Element, parent of alter list + * @throws CorruptedInterviewException if unable to read interview + */ + private static void readAnswers(Interview interview, Element e) throws CorruptedInterviewException + { + Elements answerIter = e.getElements("Answer"); + int index = 0; + + if (interview._numAnswers != answerIter.size()) + { + System.err.println("interview.numAnswers != answerIter.size in Interview::readAnswers; "); + throw (new CorruptedInterviewException()); + } + + while (answerIter.hasMoreElements()) + { + try + { + Answer oldAnswer = interview._answers[index]; + Answer newAnswer = Answer.readAnswer(answerIter.next()); + + if (oldAnswer.questionId.equals(newAnswer.questionId)) + { + interview._answers[index++] = newAnswer; + } + else + { + throw (new CorruptedInterviewException()); + } + } + catch (Exception ex) + { + System.err.println("Answer::readAnswer failed in Interview::readAnswers; " + ex); + } + } + } + + /******** + * Add interview information to an xml structure for output to a file + * @param e XML Element, parent of interview tree + */ + public void writeInterview(Element e) + { + boolean success = true; + Element alterListElem = e.addElement("AlterList"); + Element answerListElem = e.addElement("AnswerList"); + Element egoNameElem = e.addElement("EgoName"); + + EgoClient.study.writeInterviewStudy(e); + e.addElement("Complete").setBoolean(_complete); + egoNameElem.addElement("First").setString(_egoName[0]); + egoNameElem.addElement("Last").setString(_egoName[1]); + + for (int i = 0; i < _alterList.length; i++) + { + alterListElem.addElement("Name").setText(_alterList[i]); + } + + for (int i = 0; i < _answers.length; i++) + { + try + { + _answers[i].writeAnswer(answerListElem); + } + catch (Exception ex) + { + JOptionPane.showMessageDialog( + EgoClient.frame, + "An error occurred while attempting to write an answer to the interview file. " + ex, + "Unable to Write Interview", + JOptionPane.ERROR_MESSAGE); + success = false; + } + } + + if (!success) + { + JOptionPane.showMessageDialog( + EgoClient.frame, + "An error occurred while attempting to write this interview.", + "Unable to Write Interview", + JOptionPane.ERROR_MESSAGE); + } + } + + /******** + * Returns complete attribut for interview + * @returns complete + */ + public boolean isComplete() + { + return _complete; + } + + /******** + * Ego has answered all questions. Write file and generate stats + * @throws FileCreateException + */ + public void completeInterview() throws FileCreateException + { + /***** + * Generate statistics for the first statable question + */ + Question q = EgoClient.study.getFirstStatableQuestion(); + + _complete = true; + EgoClient.storage.writeInterviewFile(); + + if (q != null) + { + Statistics stats = EgoClient.interview.generateStatistics(q); + EgoClient.storage.writeStatisticsFiles(stats, _egoName); + } + } + + /******** + * Add interview information to an xml structure for output to a file + * @param e XML Element, parent of interview tree + */ + public void writeEgoAnswers(Element e) + { + Iterator egoAnswers = getEgoAnswers().iterator(); + Element eqList = e.addElement("EgoAnswers"); + + while (egoAnswers.hasNext()) + { + Answer answer = (Answer) egoAnswers.next(); + + try + { + Element aElement = eqList.addElement("EgoAnswer"); + aElement.addElement("Title").setString(_study.getQuestions().getQuestion(answer.questionId).title); + + if (answer.answered) + { + aElement.addElement("Answer").setString(answer.string); + aElement.addElement("AnswerIndex").setInt(answer.value); + } + else + { + aElement.addElement("Answer").setString("N/A"); + aElement.addElement("AnswerIndex").setInt(Answer.NO_ANSWER); + } + } + catch (Exception ex) + { + System.err.println("Failure in Interview::writeEgoAnswers; " + ex); + } + } + } + + /******** + * Add interview information to an xml structure for output to a file + * @param e XML Element, parent of interview tree + */ + public EgoAnswer[] getEgoAnswerArray(StatRecord record) + { + Iterator egoAnswerIter = getEgoAnswers().iterator(); + EgoAnswer[] egoAnswers = new EgoAnswer[getEgoAnswers().size()]; + int index = 0; + + while (egoAnswerIter.hasNext()) + { + Answer answer = (Answer) egoAnswerIter.next(); + + if (answer.answered) + { + egoAnswers[index++] = record.new EgoAnswer(_study.getQuestions().getQuestion(answer.questionId).title, + answer.string, answer.value); + } + else + { + egoAnswers[index++] = record.new EgoAnswer(_study.getQuestions().getQuestion(answer.questionId).title, + "N/A", Answer.NO_ANSWER); + } + } + + return egoAnswers; + } + + /******** + * Has ego completed all questions? + * @return Complete + */ + public boolean getInterviewComplete() + { + return (_complete); + } + + /******** + * How many alters were used to generate this interview + * @return _numAlters + */ + public int getNumAlters() + { + return (_numAlters); + } + + /******** + * What study is used for this interview + * @return _numAlters + */ + public Study getStudy() + { + return (_study); + } + + /** + * Returns statistics + * @return the statistics + */ + public Statistics getStats() + { + return _stats; + } + + /** + * @param q The stats to set. + */ + public Statistics generateStatistics(Question q) + { + _stats = Statistics.generateInterviewStatistics(this, q); + return _stats; + } + + /******** + * Goes through Set of answers creating a matrix representing the adjacency graph + * for a set of alters. For each pair of alters if answer value > 0 an edge is placed + * between the alters + * @param answers Set of alter pair answers + * @param numAlters Total alters in interview + * @param weighted whether to use actual answer values for matrix or 1:0 + * @return Matrix representing non-directed graph of alters + * @throws MissingPairException + */ + public int[][] generateAdjacencyMatrix(Question q, boolean weighted) + throws MissingPairException + { + if (_study.getUIType().equals(Shared.TRADITIONAL_QUESTIONS)) + { + int[][] m = new int[_numAlters][_numAlters]; + + /* + * init to make sure we get all pairs, in the case of linked questions + * not all will be answered + */ + + for (int i = 0; i < _numAlters; i++) + { + for (int j = 0; j < _numAlters; j++) + { + m[i][j] = 0; + } + + m[i][i] = 1; + } + + for (Iterator it = getAnswerSubset(q.UniqueId).iterator(); it.hasNext();) + { + Answer a = (Answer) it.next(); + + if (weighted) + { + // m[a.getAlters()[0]][a.getAlters()[1]] = a.value; + //m[a.getAlters()[1]][a.getAlters()[0]] = a.value; + m[a.getAlters()[0]][a.getAlters()[1]] = (a.adjacent) ? a.value : 0; + m[a.getAlters()[1]][a.getAlters()[0]] = (a.adjacent) ? a.value : 0; + } + else + { + m[a.getAlters()[0]][a.getAlters()[1]] = (a.adjacent) ? 1 : 0; + m[a.getAlters()[1]][a.getAlters()[0]] = (a.adjacent) ? 1 : 0; + } + } + + _matrix = m; + } + + return (_matrix); + } + + public void rewind() + { + while(hasPrevious()) + previous(); + } + +public Answer[] get_answers() { + return _answers; +} + +} + +/** + * $Log: Interview.java,v $ + * Revision 1.1 2005/08/02 19:35:59 samag + * Initial checkin + * + * Revision 1.12 2004/04/11 00:17:13 admin + * Improving display of Alter Prompt questions from Applet UI Interviews + * + * Revision 1.11 2004/04/08 15:06:07 admin + * EgoClient now creates study summaries from Server + * EgoAuthor now sets active study on server + * + * Revision 1.10 2004/04/06 20:29:22 admin + * First pass as supporting interactive applet linking interviews + * + * Revision 1.9 2004/04/06 15:46:11 admin + * cvs tags in headers + * + */ \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/QuestionPanel.gui_xml b/src/com/endlessloopsoftware/ego/client/QuestionPanel.gui_xml new file mode 100644 index 0000000..697e33f --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/QuestionPanel.gui_xml @@ -0,0 +1,253 @@ + + + + + + + titleText + + + title + + + + + + + + + + + true + + + questionText + + + true + + + + + + Previous Question + + questionButtonPrevious + + + + + + Next Question + + true + + + questionButtonNext + + + + + + + questionProgress + + + + + + + answerPanel + + + + + + 500 + 400 + + + + + + + + + + + + + + West + + + 10 + Width + 0 + + + + + North + + + North + 12 + + + + + + + + West + + + West + 0 + + + + + East + + + East + 0 + + + + + South + + + 95 + Height + 0 + + + + + + + + West + + + West + 0 + + + + + East + + + East + 0 + + + + + North + + + North + 163 + + + + + South + + + North + -36 + + + + + + + + East + + + East + 0 + + + + + North + + + North + 0 + + + + + + + + West + + + West + 0 + + + + + East + + + 90 + Width + 0 + + + + + North + + + South + 12 + + + + + South + + + North + 151 + + + + + + + + West + + + West + 0 + + + + + South + + + North + -12 + + + + + + diff --git a/src/com/endlessloopsoftware/ego/client/ServerInterviewChooser.gui_xml b/src/com/endlessloopsoftware/ego/client/ServerInterviewChooser.gui_xml new file mode 100644 index 0000000..508a97b --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/ServerInterviewChooser.gui_xml @@ -0,0 +1,373 @@ + + + + + + + serverURL + + + + + + + serverPassword + + + + + + Load Studies + + LoadInterviews + + + + + + + + + + + + -48 + -19 + + + + + interviewTree + + + + + + 345 + 205 + + + + + + + + + 3 + + + + + + + 5 + + + Server URL: + + + + + + + 7 + + + Password + + + + + + Select + + Select + + + + + + 390 + 386 + + + + + 390 + 386 + + + + + + + + + + + West + + + West + 0 + + + + + East + + + East + -3 + + + + + North + + + South + 14 + + + + + South + + + North + 24 + + + + + + + + West + + + West + 0 + + + + + North + + + South + 14 + + + + + South + + + North + 24 + + + + + + + + West + + + East + 32 + + + + + East + + + East + 0 + + + + + North + + + North + 0 + + + + + South + + + South + 0 + + + + + + + + West + + + West + 0 + + + + + East + + + East + 0 + + + + + North + + + South + 14 + + + + + South + + + North + 7 + + + + + + + + West + + + West + 0 + + + + + East + + + East + 0 + + + + + North + + + North + 0 + + + + + South + + + South + 0 + + + + + + + + West + + + West + 20 + + + + + East + + + East + -21 + + + + + North + + + North + 20 + + + + + South + + + South + -157 + + + + + + + + East + + + West + -12 + + + + + North + + + North + 0 + + + + + + + + East + + + East + 0 + + + + + North + + + South + 14 + + + + + + diff --git a/src/com/endlessloopsoftware/ego/client/ServerInterviewChooser.java b/src/com/endlessloopsoftware/ego/client/ServerInterviewChooser.java new file mode 100644 index 0000000..1d6d859 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/ServerInterviewChooser.java @@ -0,0 +1,471 @@ +/** + *

Copyright: Copyright (c) 2002 - 2004

+ *

Company: Endless Loop Software

+ * @author Peter Schoaff + * + * $Id: ServerInterviewChooser.java,v 1.1 2005/08/02 19:36:00 samag Exp $ + */ +package com.endlessloopsoftware.ego.client; + +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Iterator; +import java.util.Properties; +import java.util.Set; + +import javax.ejb.FinderException; +import javax.swing.JButton; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; +import javax.swing.JTree; +import javax.swing.ProgressMonitor; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeSelectionModel; + +import com.cim.dlgedit.loader.DialogResource; +import com.cim.util.swing.DlgUtils; +import com.endlessloopsoftware.ego.Question; +import com.endlessloopsoftware.ego.Shared; +import com.endlessloopsoftware.ego.Study; +import com.endlessloopsoftware.ego.client.statistics.Statistics; +import com.endlessloopsoftware.egonet.interfaces.InterviewSBRemote; +import com.endlessloopsoftware.egonet.interfaces.InterviewSBRemoteHome; +import com.endlessloopsoftware.egonet.interfaces.InterviewSBUtil; +import com.endlessloopsoftware.egonet.interfaces.StudySBRemote; +import com.endlessloopsoftware.egonet.interfaces.StudySBRemoteHome; +import com.endlessloopsoftware.egonet.interfaces.StudySBUtil; +import com.endlessloopsoftware.egonet.util.InterviewDataValue; +import com.endlessloopsoftware.egonet.util.InterviewIdentifier; +import com.endlessloopsoftware.egonet.util.StudyAndInterviewTransfer; +import com.endlessloopsoftware.egonet.util.StudyDataValue; +import com.endlessloopsoftware.elsutils.SwingWorker; + +public class ServerInterviewChooser + extends JPanel + implements ActionListener, TreeSelectionListener +{ + // Types + private final int DECORATION = 0; + private final int STUDY = 1; + private final int INTERVIEW = 2; + + // Declare beans. + private JButton loadInterviews; + private JButton select; + private JPasswordField serverPassword; + private JTextField serverURL; + private JTree interviewTree; + + // Tree handling variables + private DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(); + private DefaultTreeModel treeModel = new DefaultTreeModel(rootNode); + + // Study Server Communication + StudySBRemote studySession; + InterviewSBRemote interviewSession; + + // Constructor. + public ServerInterviewChooser() + { + // Load up the dialog contents. + //JPanel panel = DialogResource.load("com/endlessloopsoftware/ego/client/ServerInterviewChooser.gui_xml"); + java.io.InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/endlessloopsoftware/ego/client/ServerInterviewChooser.gui_xml"); + JPanel panel = DialogResource.load(is); + + // Attach beans to fields. + loadInterviews = (JButton) DialogResource.getComponentByName(panel, "LoadInterviews"); + select = (JButton) DialogResource.getComponentByName(panel, "Select"); + serverPassword = (JPasswordField) DialogResource.getComponentByName(panel, "serverPassword"); + serverURL = (JTextField) DialogResource.getComponentByName(panel, "serverURL"); + interviewTree = (JTree) DialogResource.getComponentByName(panel, "interviewTree"); + + // Clear out Tree to start + interviewTree.addTreeSelectionListener(this); + interviewTree.setModel(treeModel); + rootNode.setUserObject(new TreeSelection("No Server Selected", DECORATION)); + interviewTree.setEditable(false); + interviewTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + interviewTree.setShowsRootHandles(false); + treeModel.reload(); + + select.setEnabled(false); + + // Add dialog as ActionListener. + loadInterviews.setText("Load Studies"); + loadInterviews.addActionListener(this); + select.addActionListener(this); + + this.setLayout(new GridLayout(1, 1)); + this.add(panel); + } + + /** + * Invoke the onXxx() action handlers. + * + * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) + */ + public void actionPerformed(ActionEvent e) + { + DlgUtils.invokeHandler(this, e); + } + + public void onLoadInterviews() + { + fillTree(); + } + + public void onSelect() + { + Properties prop = new Properties(); + prop.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory"); + prop.setProperty("java.naming.provider.url", serverURL.getText()+":1099"); + + final DefaultMutableTreeNode node = (DefaultMutableTreeNode) interviewTree.getLastSelectedPathComponent(); + + if (node == null) + { + return; + } + + final String epassword = "215-121-242-47-99-238-5-61-133-183-0-216-187-250-253-30-115-177-254-142-161-83-108-56"; + //SymmetricKeyEncryption.encrypt(new String(serverPassword.getPassword())); + final TreeSelection selection = (TreeSelection) node.getUserObject(); + + int type = selection.getType(); + + switch (type) + { + case STUDY: + { + try + { + final StatRecord[] statRecords = new StatRecord[node.getChildCount()]; + Shared.setWaitCursor(EgoClient.frame, true); + StudyDataValue data = studySession.fetchDataByStudyName(selection.toString(), epassword); + final Study study = new Study(data); + + final ProgressMonitor progressMonitor = new ProgressMonitor(EgoClient.frame, + "Calculating Statistics", "", 0, + node.getChildCount()); + final SwingWorker worker = new SwingWorker() + { + public Object construct() + { + for (int i = 0; i < node.getChildCount(); ++i) + { + DefaultMutableTreeNode interviewNode = (DefaultMutableTreeNode) node.getChildAt(i); + TreeSelection interviewSelection = (TreeSelection) interviewNode.getUserObject(); + + System.out.println("Loading " + interviewSelection.getIdentifier()); + + InterviewIdentifier id = (InterviewIdentifier) interviewSelection.getIdentifier(); + InterviewDataValue interviewData; + try + { + interviewData = interviewSession.fetchUserInterviewData(selection.toString(), + id.getFirstName(), + id.getLastName(), + epassword); + + System.out.println("Creating Interview " + interviewSelection.getIdentifier()); + Interview interview = new Interview(study, interviewData); + + System.out.println("Generating Statistics " + interviewSelection.getIdentifier()); + Question q = study.getFirstStatableQuestion(); + Statistics statistics = interview.generateStatistics(q); + + System.out.println("Most Central Degree Alter " + statistics.mostCentralBetweenAlterName); + + System.out.println("To StatRecord " + interviewSelection.getIdentifier()); + + statRecords[i] = new StatRecord(statistics); + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + //System.out.println("Complete " + interviewSelection.getIdentifier()); + progressMonitor.setProgress(i); + } + + return statRecords; + } + + public void finished() + { + Shared.setWaitCursor(EgoClient.frame, false); + progressMonitor.close(); + SummaryPanel.gotoPanel(statRecords); + } + }; + + progressMonitor.setProgress(0); + progressMonitor.setMillisToDecideToPopup(0); + progressMonitor.setMillisToPopup(0); + + worker.start(); + + } + catch (FinderException e) + { + e.printStackTrace(); + } + catch(java.lang.reflect.UndeclaredThrowableException e) + { + e.printStackTrace(); + System.out.println(e.getUndeclaredThrowable()); + } + catch (Exception e) + { + JOptionPane.showMessageDialog(this, "Unable to load study.\n" + e.getMessage(), "Server Error", + JOptionPane.ERROR_MESSAGE); + e.printStackTrace(); + } + finally + { + Shared.setWaitCursor(EgoClient.frame, false); + } + } + break; + + case INTERVIEW: + { + DefaultMutableTreeNode studyNode = (DefaultMutableTreeNode) node.getParent(); + TreeSelection studySelect = (TreeSelection) studyNode.getUserObject(); + + try + { + Shared.setWaitCursor(EgoClient.frame, true); + + StudyDataValue studyData = studySession.fetchDataByStudyName(studySelect.toString(), epassword); + + InterviewIdentifier id = (InterviewIdentifier) selection.getIdentifier(); + InterviewDataValue interviewData = interviewSession.fetchUserInterviewData(studySelect.toString(), + id.getFirstName(), + id.getLastName(), + epassword); + + EgoClient.uiPath = EgoClient.VIEW_INTERVIEW; + + EgoClient.storage.setInterviewFile(null); + EgoClient.interview = null; + + Study study = new Study(studyData); + EgoClient.study = study; + +// System.out.println(EgoClient.study.getQuestions().size()); +// System.out.println(EgoClient.study.getQuestions().dump()); +// + EgoClient.interview = new Interview(study, interviewData); + + if (EgoClient.interview != null) + { + ViewInterviewPanel.gotoPanel(); + } + } + catch (FinderException e) + { + JOptionPane.showMessageDialog(this, "Unable to load interview.", "Server Error", + JOptionPane.ERROR_MESSAGE); + } + catch (Exception e) + { + JOptionPane.showMessageDialog(this, "Unable to load interview.\n" + e.getMessage(), "Server Error", + JOptionPane.ERROR_MESSAGE); + e.printStackTrace(); + } + finally + { + Shared.setWaitCursor(EgoClient.frame, false); + } + + } + + default: + break; + } + } + + /** + * @author admin + * + * To change the template for this generated type comment go to + * Window - Preferences - Java - Code Generation - Code and Comments + */ + public void fillTree() + { + try + { + Shared.setWaitCursor(EgoClient.frame, true); + + Properties prop = new Properties(); + prop.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory"); + prop.setProperty("java.naming.provider.url", serverURL.getText()+":1099"); + + StudySBRemoteHome studySBHome = StudySBUtil.getHome(prop); + studySession = studySBHome.create(); + InterviewSBRemoteHome interviewSBHome = InterviewSBUtil.getHome(prop); + interviewSession = interviewSBHome.create(); + Set studyNames = studySession.getStudyAndInterviewNames(); + + //System.out.println("Found " + studyNames.size() + " studies."); + + rootNode.removeAllChildren(); + + for (Iterator it = studyNames.iterator(); it.hasNext();) + { + StudyAndInterviewTransfer xfer = (StudyAndInterviewTransfer) it.next(); + + DefaultMutableTreeNode studyNode = new DefaultMutableTreeNode(new TreeSelection(xfer.studyName, STUDY)); + + for (Iterator ints = xfer.interviewIdentifiers.iterator(); ints.hasNext();) + { + InterviewIdentifier id = (InterviewIdentifier) ints.next(); + studyNode.add(new DefaultMutableTreeNode(new TreeSelection(id, INTERVIEW))); + } + + rootNode.add(studyNode); + } + + if (studyNames.size() > 0) rootNode.setUserObject(new TreeSelection("Studies", DECORATION)); + else rootNode.setUserObject(new TreeSelection("No Studies Found", DECORATION)); + + treeModel.reload(); + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + finally + { + Shared.setWaitCursor(EgoClient.frame, false); + } + } + + /* (non-Javadoc) + * @see javax.swing.event.TreeSelectionListener#valueChanged(javax.swing.event.TreeSelectionEvent) + */ + public void valueChanged(TreeSelectionEvent e) + { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) interviewTree.getLastSelectedPathComponent(); + + if (node == null) + { + select.setEnabled(false); + return; + } + + TreeSelection selection = (TreeSelection) node.getUserObject(); + + if (select != null) + { + if (selection.getType() == STUDY) + { + select.setText("View Study Summary"); + select.setEnabled(true); + } + else if (selection.getType() == INTERVIEW) + { + select.setText("View Interview"); + select.setEnabled(true); + } + else + { + select.setEnabled(false); + } + } + else + { + select.setEnabled(false); + } + select.setEnabled((selection != null) && ((selection.getType() == STUDY) || (selection.getType() == INTERVIEW))); + } + + private class TreeSelection + { + private Object _id; + private int _type; + + public TreeSelection(Object id, int type) + { + _id = id; + _type = type; + } + + public String toString() + { + return _id.toString(); + } + + public int getType() + { + return _type; + } + + public Object getIdentifier() + { + return _id; + } + } + +} + +/** + * $Log: ServerInterviewChooser.java,v $ + * Revision 1.1 2005/08/02 19:36:00 samag + * Initial checkin + * + * Revision 1.11 2004/04/11 15:19:28 admin + * Using password to access server + * + * Remote study summary in seperate thread with progress monitor + * + * Revision 1.10 2004/04/08 15:06:07 admin + * EgoClient now creates study summaries from Server + * EgoAuthor now sets active study on server + * + * Revision 1.9 2004/04/07 00:08:31 admin + * updating manifests, jar creation. Removing author specific objects from + * client specific references + * + * Revision 1.8 2004/04/06 15:43:26 admin + * Moving matrix generation into interview to support Applet Linking UI. + * An interview generated with applet linking will have no meaningful alter pair + * questions. The adjacency matrix will be returned in an Athenian manner from + * the server. + * + * Revision 1.7 2004/04/06 14:56:02 admin + * Work to integrate with Applet Linking UI + * + * Revision 1.6 2004/03/29 00:35:09 admin + * Downloading Interviews + * Fixing some bugs creating Interviews from Data Objects + * + * Revision 1.5 2004/03/28 17:31:32 admin + * More error handling when uploading study to server + * Server URL selection dialog for upload + * + * Revision 1.4 2004/03/23 14:58:48 admin + * Update UI + * Study creation now occurs in instantiators + * + * Revision 1.3 2004/03/22 20:09:17 admin + * Includes interviews in selection box + * + * Revision 1.2 2004/03/22 00:00:34 admin + * Extended text entry area + * Started work on importing studies from server + * + * Revision 1.1 2004/03/20 18:13:59 admin + * Adding remote selection dialog + * + */ \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/SourceSelectPanel.java b/src/com/endlessloopsoftware/ego/client/SourceSelectPanel.java new file mode 100644 index 0000000..373f33e --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/SourceSelectPanel.java @@ -0,0 +1,80 @@ +/** + *

Copyright: Copyright (c) 2002 - 2004

+ *

Company: Endless Loop Software

+ * @author Peter Schoaff + * + * $Id: SourceSelectPanel.java,v 1.1 2005/08/02 19:36:01 samag Exp $ + */ +package com.endlessloopsoftware.ego.client; + +import java.awt.Dimension; +import java.awt.Toolkit; + +import javax.swing.JFrame; +import javax.swing.JTabbedPane; + +/** + * @author admin + * + * To change the template for this generated type comment go to + * Window - Preferences - Java - Code Generation - Code and Comments + */ +public class SourceSelectPanel + extends JTabbedPane +{ + public SourceSelectPanel() + { + super(); + this.addTab("Local Files", new ClientPanel()); + // this.addTab("Remote Server", new ServerInterviewChooser()); + //this.addTab("Test Panel", new TestPanel()); + } + + public static void gotoPanel(boolean center) + { + /* Return to first screen */ +// EgoClient.frame.setVisible(false); + EgoClient.frame.setContentPane(new SourceSelectPanel()); + EgoClient.frame.createMenuBar(EgoClient.SELECT); + EgoClient.frame.pack(); + //EgoClient.frame.setSize(600, 500); + EgoClient.frame.setExtendedState(EgoClient.frame.getExtendedState()|JFrame.MAXIMIZED_BOTH); + + if (center) + { + //Center the window + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Dimension frameSize = EgoClient.frame.getSize(); + if (frameSize.height > screenSize.height) + { + frameSize.height = screenSize.height; + } + if (frameSize.width > screenSize.width) + { + frameSize.width = screenSize.width; + } + EgoClient.frame.setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2); + + } + + EgoClient.frame.setVisible(true); + } +} + + +/** + * $Log: SourceSelectPanel.java,v $ + * Revision 1.1 2005/08/02 19:36:01 samag + * Initial checkin + * + * Revision 1.2 2004/03/21 14:00:39 admin + * Cleaned up Question Panel Layout using FOAM + * + * Revision 1.1 2004/03/20 18:13:59 admin + * Adding remote selection dialog + * + * Revision 1.1 2004/03/19 20:28:45 admin + * Converted statistics frome to a panel. Incorporated in a tabbed panel + * as part of main frame. + * + */ \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/StartPanel.java b/src/com/endlessloopsoftware/ego/client/StartPanel.java new file mode 100644 index 0000000..da3d561 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/StartPanel.java @@ -0,0 +1,295 @@ +package com.endlessloopsoftware.ego.client; + +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.SwingConstants; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import com.endlessloopsoftware.elsutils.documents.AlphaDocument; +import com.endlessloopsoftware.elsutils.files.FileCreateException; +import com.endlessloopsoftware.elsutils.files.FileReadException; + +/** + *

Title: Egocentric Networks Client Program

+ *

Description: Subject Interview Client

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter Schoaff + * @version 1.0 + */ + +public class StartPanel extends JPanel +{ + private final GridBagLayout gridBagLayout1 = new GridBagLayout(); + private final JLabel titleLabel = new JLabel("What is your name?"); + private final JLabel firstNameLabel = new JLabel("First: "); + private final JTextField firstNameField = new JTextField(); + private final JLabel lastNameLabel = new JLabel("Last: "); + private final JTextField lastNameField = new JTextField(); + private final JButton startInterviewButton = new JButton("Start Interview"); + private final AlphaDocument firstNameDocument = new AlphaDocument(); + private final AlphaDocument lastNameDocument = new AlphaDocument(); + + public StartPanel() + { + try + { + jbInit(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + private void jbInit() throws Exception + { + this.setLayout(gridBagLayout1); + + titleLabel.setFont(new java.awt.Font("Lucida Grande", 1, 16)); + titleLabel.setHorizontalAlignment(SwingConstants.CENTER); + titleLabel.setHorizontalTextPosition(SwingConstants.CENTER); + firstNameLabel.setHorizontalTextPosition(SwingConstants.RIGHT); + lastNameLabel.setHorizontalTextPosition(SwingConstants.RIGHT); + startInterviewButton.setEnabled(false); + + this.setBorder(BorderFactory.createEtchedBorder()); + this.add( + titleLabel, + new GridBagConstraints( + 0, + 0, + 2, + 1, + 1.0, + 0.2, + GridBagConstraints.CENTER, + GridBagConstraints.HORIZONTAL, + new Insets(0, 0, 0, 0), + 0, + 0)); + this.add( + firstNameLabel, + new GridBagConstraints( + 0, + 1, + 1, + 1, + 0.3, + 0.1, + GridBagConstraints.CENTER, + GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), + 0, + 0)); + this.add( + firstNameField, + new GridBagConstraints( + 1, + 1, + 1, + 1, + 0.7, + 0.0, + GridBagConstraints.CENTER, + GridBagConstraints.HORIZONTAL, + new Insets(10, 10, 10, 10), + 0, + 6)); + this.add( + lastNameLabel, + new GridBagConstraints( + 0, + 2, + 1, + 1, + 0.0, + 0.1, + GridBagConstraints.CENTER, + GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), + 0, + 0)); + this.add( + lastNameField, + new GridBagConstraints( + 1, + 2, + 1, + 1, + 0.0, + 0.0, + GridBagConstraints.CENTER, + GridBagConstraints.HORIZONTAL, + new Insets(10, 10, 10, 10), + 0, + 6)); + this.add( + startInterviewButton, + new GridBagConstraints( + 0, + 3, + 2, + 1, + 0.0, + 0.0, + GridBagConstraints.CENTER, + GridBagConstraints.HORIZONTAL, + new Insets(20, 80, 20, 80), + 0, + 0)); + + startInterviewButton.addActionListener(new java.awt.event.ActionListener() + { + public void actionPerformed(ActionEvent e) + { + startInterviewButton_actionPerformed(e); + } + }); + + firstNameField.addActionListener(new java.awt.event.ActionListener() + { + public void actionPerformed(ActionEvent e) + { + firstNameField_actionPerformed(e); + } + }); + + lastNameField.addActionListener(new java.awt.event.ActionListener() + { + public void actionPerformed(ActionEvent e) + { + lastNameField_actionPerformed(e); + } + }); + + firstNameField.setDocument(firstNameDocument); + firstNameDocument.addDocumentListener(new DocumentListener() + { + public void insertUpdate(DocumentEvent e) + { + textEvent(e); + } + public void changedUpdate(DocumentEvent e) + { + textEvent(e); + } + public void removeUpdate(DocumentEvent e) + { + textEvent(e); + } + }); + + lastNameField.setDocument(lastNameDocument); + lastNameDocument.addDocumentListener(new DocumentListener() + { + public void insertUpdate(DocumentEvent e) + { + textEvent(e); + } + public void changedUpdate(DocumentEvent e) + { + textEvent(e); + } + public void removeUpdate(DocumentEvent e) + { + textEvent(e); + } + }); + } + + static void gotoPanel() + { + /* Return to first screen */ + EgoClient.frame.setVisible(false); + EgoClient.frame.setContentPane(new StartPanel()); + EgoClient.frame.pack(); + EgoClient.frame.setSize(350, 350); + //EgoClient.frame.setExtendedState(EgoClient.frame.getExtendedState()|JFrame.MAXIMIZED_BOTH); + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Dimension frameSize = EgoClient.frame.getSize(); + if (frameSize.height > screenSize.height) + { + frameSize.height = screenSize.height; + } + if (frameSize.width > screenSize.width) + { + frameSize.width = screenSize.width; + } + EgoClient.frame.setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2); + EgoClient.frame.setVisible(true); + } + + void startInterviewButton_actionPerformed(ActionEvent e) + { + boolean success = false; + + /* Logic */ + try + { + EgoClient.interview.setName(firstNameField.getText(), lastNameField.getText()); + + success = EgoClient.storage.saveInterview(); + } + catch (FileCreateException ex) + { + success = false; + } + catch (FileReadException ex) + { + success = false; + } + + /* UI */ + if (success) + { + ClientQuestionPanel.gotoPanel(); + } + else + { + SourceSelectPanel.gotoPanel(false); + } + } + + protected void lastNameField_actionPerformed(ActionEvent e) + { + if (firstNameField.getText().length() == 0) + { + firstNameField.requestFocus(); + } + else + { + startInterviewButton_actionPerformed(e); + } + } + + protected void firstNameField_actionPerformed(ActionEvent e) + { + if (lastNameField.getText().length() == 0) + { + lastNameField.requestFocus(); + } + else + { + startInterviewButton_actionPerformed(e); + } + } + + protected void textEvent(DocumentEvent e) + { + startInterviewButton.setEnabled( + (firstNameField.getText().length() > 0) && (lastNameField.getText().length() > 0)); + } +} \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/StatRecord.java b/src/com/endlessloopsoftware/ego/client/StatRecord.java new file mode 100644 index 0000000..cdbb303 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/StatRecord.java @@ -0,0 +1,215 @@ +/** + *

Copyright: Copyright (c) 2002 - 2004

+ *

Company: Endless Loop Software

+ * @author Peter Schoaff + * + * $Id: StatRecord.java,v 1.1 2005/08/02 19:36:00 samag Exp $ + */ +package com.endlessloopsoftware.ego.client; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.endlessloopsoftware.ego.client.statistics.AlterStats; +import com.endlessloopsoftware.ego.client.statistics.Statistics; + +import electric.xml.Element; +import electric.xml.Elements; + +public class StatRecord +{ + String name = ""; + String degreeName = ""; + Integer degreeValue = new Integer(0); + Float degreeMean = new Float(0); + Float degreeNC = new Float(0); + + String betweenName = ""; + Float betweenValue = new Float(0); + Float betweenMean = new Float(0); + Float betweenNC = new Float(0); + + String closenessName = ""; + Float closenessValue = new Float(0); + Float closenessMean = new Float(0); + Float closenessNC = new Float(0); + + Integer numCliques = new Integer(0); + Integer numComponents = new Integer(0); + Integer numIsolates = new Integer(0); + Integer numDyads = new Integer(0); + + List egoAnswers = new ArrayList(); + List alterAnswers = new ArrayList(); + + + public List getEgoAnswers() + { + return egoAnswers; + } + public List getAlterAnswers() + { + return alterAnswers; + } + + public StatRecord(Element e) + { + Element nameElem = e.getElement("EgoName"); + if (nameElem != null) + { + name = nameElem.getString("First") + " " + nameElem.getString("Last"); + } + + degreeName = e.getString("DegreeName"); + degreeValue = new Integer(e.getInt("DegreeValue")); + degreeMean = new Float(e.getFloat("DegreeMean")); + degreeNC = new Float(e.getFloat("DegreeNC")); + + closenessName = e.getString("ClosenessName"); + closenessValue = new Float(e.getFloat("ClosenessValue")); + closenessMean = new Float(e.getFloat("ClosenessMean")); + closenessNC = new Float(e.getFloat("ClosenessNC")); + + betweenName = e.getString("BetweenName"); + betweenValue = new Float(e.getFloat("BetweenValue")); + betweenMean = new Float(e.getFloat("BetweenMean")); + betweenNC = new Float(e.getFloat("BetweenNC")); + + numCliques = new Integer(e.getInt("NumCliques")); + numComponents = new Integer(e.getInt("NumComponents")); + numIsolates = new Integer(e.getInt("NumIsolates")); + numDyads = new Integer(e.getInt("NumDyads")); + + Elements egoList = e.getElement("EgoAnswers").getElements("EgoAnswer"); + while (egoList.hasMoreElements()) + { + egoAnswers.add(new EgoAnswer(egoList.next())); + } + + Elements alterList = e.getElement("AlterQuestionSummaries").getElements("AlterQuestionSummary"); + while (alterList.hasMoreElements()) + { + alterAnswers.add(new AlterAnswer(alterList.next())); + } + } + + public StatRecord(Statistics stats) + { + name = stats.getInterview().getName()[0] + " " + stats.getInterview().getName()[1]; + + degreeName = stats.mostCentralDegreeAlterName; + degreeValue = new Integer(stats.mostCentralDegreeAlterValue); + degreeMean = new Float(stats.meanCentralDegreeValue); + degreeNC = new Float(stats.degreeNC); + + closenessName = stats.mostCentralClosenessAlterName; + closenessValue = new Float(stats.mostCentralClosenessAlterValue); + closenessMean = new Float(stats.meanCentralClosenessValue); + closenessNC = new Float(stats.closenessNC); + + betweenName = stats.mostCentralBetweenAlterName; + betweenValue = new Float(stats.mostCentralBetweenAlterValue); + betweenMean = new Float(stats.meanCentralBetweenAlterValue); + betweenNC = new Float(stats.betweenNC); + + numCliques = new Integer(stats.cliqueSet.size()); + numComponents = new Integer(stats.componentSet.size() - stats.isolates - stats.dyads); + numIsolates = new Integer(stats.isolates); + numDyads = new Integer(stats.dyads); + + egoAnswers = Arrays.asList(stats.getInterview().getEgoAnswerArray(this)); + + for (int i = 0; i < stats.alterStatArray.length; ++i) + { + alterAnswers.add(new AlterAnswer(stats.alterStatArray[i])); + } + } + + public class EgoAnswer + { + String title; + String answer; + int index; + + protected EgoAnswer(Element e) + { + title = e.getString("Title"); + answer = e.getString("Answer"); + index = e.getInt("AnswerIndex"); + } + + public EgoAnswer(String title, String answer, int index) + { + this.title = title; + this.answer = answer; + this.index = index; + } + } + + public class AlterAnswer + { + String title; + int count; + String[] selections; + int[] totals; + //code added + int[] AnswerIndex; + //end of add + + protected AlterAnswer(Element e) + { + int index=0; + + title = e.getString("Title"); + count = e.getInt("Count"); + + + Elements answerList = e.getElement("Answers").getElements("Answer"); + selections = new String[answerList.size()]; + totals = new int[answerList.size()]; + //added by sonam 08/24/07 + AnswerIndex = new int[answerList.size()]; + //end + while (answerList.hasMoreElements()) + { + Element a = answerList.next(); + + index = a.getInt("AnswerIndex"); + selections[index] = a.getString("Text"); + totals[index] = a.getInt("Total"); + //added by sonam 08/24/07 + AnswerIndex[index] = a.getInt("AnswerIndex"); + //end + } + } + + protected AlterAnswer(AlterStats alterStats) + { + title = alterStats.qTitle; + count = alterStats.answerCount; + selections = (String[]) alterStats.answerText.clone(); + totals = (int[]) alterStats.answerTotals.clone(); + } + + public String[] getSelections() { + return selections; + } + + public String getTitle() { + return title; + } + + } +} + +/** + * $Log: StatRecord.java,v $ + * Revision 1.1 2005/08/02 19:36:00 samag + * Initial checkin + * + * Revision 1.1 2004/04/08 15:06:07 admin + * EgoClient now creates study summaries from Server + * EgoAuthor now sets active study on server + * + */ \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/SummaryPanel.java b/src/com/endlessloopsoftware/ego/client/SummaryPanel.java new file mode 100644 index 0000000..fe8a1c0 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/SummaryPanel.java @@ -0,0 +1,555 @@ + +package com.endlessloopsoftware.ego.client; + +/** + *

Copyright: Copyright (c) 2002 - 2004

+ *

Company: Endless Loop Software

+ * @author Peter Schoaff + * + * $Id: SummaryPanel.java,v 1.1 2005/08/02 19:36:00 samag Exp $ + */ +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.io.File; +import java.io.PrintWriter; +import java.text.DecimalFormat; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.ProgressMonitor; + +import com.endlessloopsoftware.ego.Shared; +import com.endlessloopsoftware.ego.client.statistics.StatisticsArrayPanel; +import com.endlessloopsoftware.ego.client.statistics.models.StatTableModel; +import com.endlessloopsoftware.elsutils.SwingWorker; +import com.endlessloopsoftware.elsutils.files.DirList; +import com.endlessloopsoftware.elsutils.files.FileHelpers; + +import electric.xml.Document; +import electric.xml.Element; + +public class SummaryPanel extends JPanel +{ + private final JButton _finishedButton = new JButton("Finished"); + private JPanel _summaryPanel; + + private StatRecord[] _stats = new StatRecord[0]; + private int _recordCount = 0; + + + public SummaryPanel(StatRecord[] stats) + { + setLayout(new GridBagLayout()); + + /* Get data to display */ + _stats = stats; + _recordCount = stats.length; + + /* Load table */ + _summaryPanel = new StatisticsArrayPanel(new SummaryModel(this)); + + add(_summaryPanel, + new GridBagConstraints( 0, 0, 1, 1, 1.0, 1.0, + GridBagConstraints.CENTER, GridBagConstraints.BOTH, + new Insets(0, 0, 0, 0), 0, 0)); + add(_finishedButton, + new GridBagConstraints( 0, 1, 1, 1, 0.0, 0.0, + GridBagConstraints.EAST, GridBagConstraints.NONE, + new Insets(5, 5, 5, 5), 0, 0)); + + _finishedButton.addActionListener(new java.awt.event.ActionListener() + { + public void actionPerformed(ActionEvent e) + { + finishedButton_actionPerformed(e); + } + }); + } + + public SummaryPanel(ProgressMonitor progress) + { + setLayout(new GridBagLayout()); + + /* Get data to display */ + loadInterviewArray(progress); + + /* Load table */ + _summaryPanel = new StatisticsArrayPanel(new SummaryModel(this)); + + add(_summaryPanel, + new GridBagConstraints( 0, 0, 1, 1, 1.0, 1.0, + GridBagConstraints.CENTER, GridBagConstraints.BOTH, + new Insets(0, 0, 0, 0), 0, 0)); + add(_finishedButton, + new GridBagConstraints( 0, 1, 1, 1, 0.0, 0.0, + GridBagConstraints.EAST, GridBagConstraints.NONE, + new Insets(5, 5, 5, 5), 0, 0)); + + _finishedButton.addActionListener(new java.awt.event.ActionListener() + { + public void actionPerformed(ActionEvent e) + { + finishedButton_actionPerformed(e); + } + }); + } + + static void gotoPanel(StatRecord[] stats) + { + // Build Screen + EgoClient.frame.setVisible(false); + Shared.setWaitCursor(EgoClient.frame, true); + EgoClient.frame.setContentPane(new SummaryPanel(stats)); + EgoClient.frame.createMenuBar(EgoClient.VIEW_SUMMARY); + EgoClient.frame.pack(); + // EgoClient.frame.setSize(640, 530); + EgoClient.frame.setExtendedState(EgoClient.frame.getExtendedState()|JFrame.MAXIMIZED_BOTH); + Shared.setWaitCursor(EgoClient.frame, false); + EgoClient.frame.setVisible(true); + } + + static void gotoPanel() + { + final ProgressMonitor progressMonitor = new ProgressMonitor(EgoClient.frame, "Calculating Statistics", "", 0, 100); + final SwingWorker worker = new SwingWorker() + { + public Object construct() + { + // Build Screen + EgoClient.frame.setVisible(false); + Shared.setWaitCursor(EgoClient.frame, true); + EgoClient.frame.setContentPane(new SummaryPanel(progressMonitor)); + EgoClient.frame.createMenuBar(EgoClient.VIEW_SUMMARY); + EgoClient.frame.pack(); + //EgoClient.frame.setSize(640, 530); + EgoClient.frame.setExtendedState(EgoClient.frame.getExtendedState()|JFrame.MAXIMIZED_BOTH); + return EgoClient.frame; + } + + public void finished() + { + Shared.setWaitCursor(EgoClient.frame, false); + EgoClient.frame.setVisible(true); + + if (progressMonitor.isCanceled()) + { + SourceSelectPanel.gotoPanel(false); + } + progressMonitor.close(); + } + }; + + progressMonitor.setProgress(0); + progressMonitor.setMillisToDecideToPopup(0); + progressMonitor.setMillisToPopup(0); + + worker.start(); + } + + private void finishedButton_actionPerformed(ActionEvent e) + { + SourceSelectPanel.gotoPanel(false); + } + + private void loadInterviewArray(ProgressMonitor progress) + { + File intPath = new File(EgoClient.storage.getPackageFile().getParent(), "/Interviews/"); + File istPath = new File(EgoClient.storage.getPackageFile().getParent(), "/Statistics/"); + String[] intFiles = DirList.getDirList(intPath, "int"); + Set istFileSet = new HashSet(); + int i = 0, p = 0; + + istPath.mkdir(); + + progress.setMinimum(0); + progress.setMaximum(intFiles.length * 2); + + for (i = 0; (i < intFiles.length) && !progress.isCanceled(); i++) + { + progress.setProgress(++p); + + String thisIntFileName = intFiles[i]; + File intFile = new File(intPath, thisIntFileName); + File thisIstFile, thisMatrixFile, thisWeightedMatrixFile; + + // Check that this has the correct Study Id + try + { + String istPathString = istPath.getCanonicalPath(); + thisIstFile = EgoStore.int2ist(istPathString, intFiles[i]); + thisMatrixFile = EgoStore.int2matrix(istPathString, intFiles[i]); + thisWeightedMatrixFile = EgoStore.int2weightedmatrix(istPathString, intFiles[i]); + + if (!(thisIstFile.exists() && thisMatrixFile.exists() && thisWeightedMatrixFile.exists())) + { + Document document = new Document(intFile); + Element root = document.getRoot(); + long id = Long.parseLong(root.getAttribute("StudyId")); + + if (id == EgoClient.study.getStudyId()) + { + EgoClient.storage.generateStatisticsFile(intFile); + istFileSet.add(thisIstFile); + } + } + else + { + // IST files exists, check for compliance + Document document = new Document(thisIstFile); + Element root = document.getRoot(); + long id = Long.parseLong(root.getAttribute("StudyId")); + String creator = root.getAttribute("Creator"); + + if (id == EgoClient.study.getStudyId()) + { + //Commented this out because it would not create a new IST file when new measures are added + if (creator.equals(Shared.version)) + { + istFileSet.add(thisIstFile); + } + else + { + EgoClient.storage.generateStatisticsFile(intFile); + istFileSet.add(thisIstFile); + } + } + } + } + catch (Exception ignored) + { + } + } + + _stats = new StatRecord[istFileSet.size()]; + + progress.setMaximum(intFiles.length + istFileSet.size()); + for (Iterator it = istFileSet.iterator(); it.hasNext() && !progress.isCanceled();) + { + progress.setProgress(++p); + + try + { + Document document = new Document((File) it.next()); + Element root = document.getRoot(); + + StatRecord rec = new StatRecord(root); + + if (rec != null) + { + _stats[_recordCount++] = rec; + } + } + catch (Exception ignored) + { + ignored.printStackTrace(); + } + } + } + + public void writeStudySummary(PrintWriter w) + { + Iterator it; + StatRecord stat = _stats[0]; + DecimalFormat percentFormatter = new DecimalFormat("#.##"); + + if (_stats.length == 0) + { + return; + } + + /******* + * Column Headers + */ + w.print("Respondant_Name"); + it = stat.egoAnswers.iterator(); + while (it.hasNext()) + { + w.print(", " + FileHelpers.formatForCSV(((StatRecord.EgoAnswer) it.next()).title)); + } + + it = stat.alterAnswers.iterator(); + while (it.hasNext()) + { + StatRecord.AlterAnswer answer = (StatRecord.AlterAnswer) it.next(); + String title = FileHelpers.formatForCSV(answer.title); + + if (answer.selections.length == 1) + { + w.print(", " + title + "_mn"); + } + else + { + + //Code commented and changed by sonam 08/24/07 + /*for (int i = 0; i < answer.selections.length; i++) + { + w.print(", " + title + i + "N"); + w.print(", " + title + i + "P"); + }*/ + + for (int i = 0; i < answer.selections.length; i++) + { + + w.print(", " + title + "|Answer:" + answer.selections[i] + + "|Value:" + answer.AnswerIndex[i] + "|Count"); + w.print(", " + title + "|Answer:" + answer.selections[i] + + "|Value:" + answer.AnswerIndex[i] + "|Percentage"); + + } + //end of code modify + } + } + /*w.println( + ", Max_Deg_Name, Max_Deg_Value, Max_Close_Name, Max_Close_Value" + + ", Max_Between_Name, Max_Between_Value, #_Cliques, #_Components");*/ + w.println( + ", Max_Deg_Name, Max_Deg_Value, Max_Close_Name, Max_Close_Value" + + ", Max_Between_Name, Max_Between_Value, N_Cliques, N_Components, Degree_Mean" + + ", Closeness_Mean, Between_Mean, DegreeNC, ClosenessNC, BetweenNC" + + ", N_Isolates, N_Dyads"); + + /******* + * Data Lines + */ + for (int i = 0; i < _recordCount; i++) + { + stat = _stats[i]; + + w.print(FileHelpers.formatForCSV(stat.name)); + + it = stat.egoAnswers.iterator(); + while (it.hasNext()) + { + w.print(", " + ((StatRecord.EgoAnswer) it.next()).index); + } + + it = stat.alterAnswers.iterator(); + while (it.hasNext()) + { + StatRecord.AlterAnswer answer = (StatRecord.AlterAnswer) it.next(); + + if (answer.selections.length == 1) + { + if ((answer.count == 0) || (answer.totals[0] == 0)) + { + w.print(", " + 0); + } + else + { + w.print(", " + ((float) answer.totals[0] / answer.count)); + } + } + else + { + for (int j = 0; j < answer.selections.length; j++) + { + w.print(", " + answer.totals[j]); + + if ((answer.count == 0) || (answer.totals[j] == 0)) + { + w.print(", " + 0); + } + else + { + w.print(", " + percentFormatter.format((double) answer.totals[j] / answer.count)); + } + } + } + } + + w.println( + ", " + + FileHelpers.formatForCSV(stat.degreeName) + + ", " + + stat.degreeValue + + ", " + + FileHelpers.formatForCSV(stat.closenessName) + + ", " + + stat.closenessValue + + ", " + + FileHelpers.formatForCSV(stat.betweenName) + + ", " + + stat.betweenValue + + ", " + + stat.numCliques + + ", " + + stat.numComponents + + ", " + + stat.degreeMean + + ", " + + (stat.closenessMean.floatValue()== -1 ? ".":stat.closenessMean.toString()) + + ", " + + stat.betweenMean + + ", " + + stat.degreeNC + + ", " + + stat.closenessNC + + ", " + + stat.betweenNC + + "," + + stat.numIsolates + + "," + + stat.numDyads); + } + } + + class SummaryModel extends StatTableModel + { + private final SummaryPanel summaryPanel; + SummaryModel(SummaryPanel parent) + { + summaryPanel = parent; + } + + public int getColumnCount() + { + if (summaryPanel._recordCount > 0) + { + return 17; + } + else + { + return 1; + } + } + + public Object getValueAt(int rowIndex, int columnIndex) + { + if (rowIndex < summaryPanel._recordCount) + { + try + { + switch (columnIndex) + { + case 0 : + return (summaryPanel._stats[rowIndex].name); /* Name */ + case 1 : + return (summaryPanel._stats[rowIndex].degreeName); /* Max Degree Name */ + case 2 : + return (summaryPanel._stats[rowIndex].degreeValue); + case 3 : + return (summaryPanel._stats[rowIndex].closenessName); /* Max Closeness Name */ + case 4 : + return (summaryPanel._stats[rowIndex].closenessValue); + case 5 : + return (summaryPanel._stats[rowIndex].betweenName); /* Max Betweenness Name */ + case 6 : + return (summaryPanel._stats[rowIndex].betweenValue); + case 7 : + return (summaryPanel._stats[rowIndex].numCliques); /* # Cliques */ + case 8 : + return (summaryPanel._stats[rowIndex].numComponents); /* # Components */ + case 9 : + return (summaryPanel._stats[rowIndex].degreeMean); + case 10 : + return (summaryPanel._stats[rowIndex].closenessMean); + case 11 : + return (summaryPanel._stats[rowIndex].betweenMean); + case 12 : + return (summaryPanel._stats[rowIndex].degreeNC); + case 13 : + return (summaryPanel._stats[rowIndex].closenessNC); + case 14 : + return (summaryPanel._stats[rowIndex].betweenNC); + case 15 : + return (summaryPanel._stats[rowIndex].numIsolates); /* Components size 1 */ + case 16 : + return (summaryPanel._stats[rowIndex].numDyads); /* Components size 2*/ + default : + return (null); + } + } + catch (Exception ex) + { + ex.printStackTrace(); + return (null); + } + } + else + { + return (null); + } + } + + public int getRowCount() + { + if (summaryPanel._recordCount > 0) + { + return summaryPanel._recordCount; + } + else + { + return 0; + } + } + + public String getColumnName(int column) + { + if (summaryPanel._recordCount > 0) + { + switch (column) + { + case 0 : + return ("Name"); + case 1 : + return ("Degree Max"); + case 3 : + return ("Closeness Max"); + case 5 : + return ("Betweenness Max"); + case 7 : + return ("# Cliques"); + case 8 : + return ("# Components"); + case 9 : + return ("Degree Mean"); + case 10: + return ("Closeness Mean"); + case 11: + return ("Betweenness Mean"); + case 12: + return ("Degree NC"); + case 13: + return ("Closeness NC"); + case 14: + return ("Betweenness NC"); + case 15: + return ("# Isolates"); + case 16: + return ("# Dyads"); + default : + return (null); + } + } + else + { + return ("No matching interviews found"); + } + } + + public int getResizeMode() + { + return JTable.AUTO_RESIZE_OFF; + } + } + +} + + +/** + * $Log: SummaryPanel.java,v $ + * Revision 1.1 2005/08/02 19:36:00 samag + * Initial checkin + * + * Revision 1.11 2004/04/08 15:06:07 admin + * EgoClient now creates study summaries from Server + * EgoAuthor now sets active study on server + * + */ \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/TestPanel.java b/src/com/endlessloopsoftware/ego/client/TestPanel.java new file mode 100644 index 0000000..b220969 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/TestPanel.java @@ -0,0 +1,29 @@ +package com.endlessloopsoftware.ego.client; + +import com.cim.dlgedit.loader.DialogResource; + +import java.awt.GridBagLayout; +import java.awt.GridLayout; + +import javax.swing.JPanel; +import javax.swing.JButton; + +public class TestPanel +extends JPanel{ + private final GridBagLayout gblayout2 = new GridBagLayout(); + private JButton JB1; + public TestPanel(){ + try{ + JPanel panel = DialogResource.load("com/endlessloopsoftware/ego/client/localSelect.gui_xml"); + JB1 = (JButton)DialogResource.getComponentByName(panel,"Test Button"); + jbInit(); + this.setLayout(new GridLayout()); + this.add(panel); + } + catch(Exception e){} + } + + private void jbInit() throws Exception{ + + } +} \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/ViewInterviewPanel.java b/src/com/endlessloopsoftware/ego/client/ViewInterviewPanel.java new file mode 100644 index 0000000..be26e1d --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/ViewInterviewPanel.java @@ -0,0 +1,94 @@ +/** + *

Copyright: Copyright (c) 2002 - 2004

+ *

Company: Endless Loop Software

+ * @author Peter Schoaff + * + * $Id: ViewInterviewPanel.java,v 1.1 2005/08/02 19:36:00 samag Exp $ + */ +package com.endlessloopsoftware.ego.client; + +import javax.swing.JFrame; +import javax.swing.JTabbedPane; +import javax.swing.ProgressMonitor; + +import com.endlessloopsoftware.ego.Shared; +import com.endlessloopsoftware.ego.client.statistics.StatisticsFrame; +import com.endlessloopsoftware.elsutils.SwingWorker; +import com.endlessloopsoftware.ego.client.graph.*; + +public class ViewInterviewPanel + extends JTabbedPane +{ + public ViewInterviewPanel(ProgressMonitor progress) + { + super(); + progress.setProgress(10); + this.addTab("Interview", new ClientQuestionPanel()); + progress.setProgress(15); + this.addTab("Statistics", new StatisticsFrame()); + this.addTab("Graph", new GraphPanel()); + progress.setProgress(70); + } + + static void gotoPanel() + { + final ProgressMonitor progressMonitor = new ProgressMonitor(EgoClient.frame, "Calculating Statistics", "", 0, 100); + final SwingWorker worker = new SwingWorker() + { + public Object construct() + { + // Build Screen + EgoClient.frame.setVisible(false); + Shared.setWaitCursor(EgoClient.frame, true); + progressMonitor.setProgress(5); + EgoClient.frame.setContentPane(new ViewInterviewPanel(progressMonitor)); + progressMonitor.setProgress(75); + EgoClient.frame.createMenuBar(EgoClient.VIEW_INTERVIEW); + EgoClient.frame.pack(); + // EgoClient.frame.setSize(640, 530); + EgoClient.frame.setExtendedState(EgoClient.frame.getExtendedState()|JFrame.MAXIMIZED_BOTH); + + return EgoClient.frame; + } + + public void finished() + { + Shared.setWaitCursor(EgoClient.frame, false); + progressMonitor.close(); + EgoClient.frame.setVisible(true); + } + }; + + progressMonitor.setProgress(0); + progressMonitor.setMillisToDecideToPopup(0); + progressMonitor.setMillisToPopup(0); + + worker.start(); + } +} + + +/** + * $Log: ViewInterviewPanel.java,v $ + * Revision 1.1 2005/08/02 19:36:00 samag + * Initial checkin + * + * Revision 1.5 2004/03/29 00:35:10 admin + * Downloading Interviews + * Fixing some bugs creating Interviews from Data Objects + * + * Revision 1.4 2004/03/28 17:31:32 admin + * More error handling when uploading study to server + * Server URL selection dialog for upload + * + * Revision 1.3 2004/03/21 15:38:08 admin + * Using progress bar while bringing up Summary Panel + * + * Revision 1.2 2004/03/21 15:17:42 admin + * Using progress Bar while bringing up question panel + * + * Revision 1.1 2004/03/19 20:28:45 admin + * Converted statistics frome to a panel. Incorporated in a tabbed panel + * as part of main frame. + * + */ \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/WorkingDialog.java b/src/com/endlessloopsoftware/ego/client/WorkingDialog.java new file mode 100644 index 0000000..10447fe --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/WorkingDialog.java @@ -0,0 +1,53 @@ +package com.endlessloopsoftware.ego.client; + +import java.awt.BorderLayout; + +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; + +/** + *

Title: Egocentric Networks Client Program

+ *

Description: Subject Interview Client

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter Schoaff + * @version 1.0 + */ + +public class WorkingDialog extends JDialog +{ + private JPanel panel1 = new JPanel(); + private BorderLayout borderLayout1 = new BorderLayout(); + private JLabel jLabel1 = new JLabel(); + + public WorkingDialog(JFrame frame, String title, boolean modal) + { + super(frame, title, modal); + try + { + jbInit(); + pack(); + } + catch(Exception ex) + { + ex.printStackTrace(); + } + } + + public WorkingDialog() + { + this(null, "", false); + } + + private void jbInit() throws Exception + { + panel1.setLayout(borderLayout1); + jLabel1.setFont(new java.awt.Font("Dialog", 1, 16)); + jLabel1.setToolTipText(""); + jLabel1.setText("Working..."); + getContentPane().add(panel1); + panel1.add(jLabel1, BorderLayout.CENTER); + } +} \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/graph/ELSFRLayout.java b/src/com/endlessloopsoftware/ego/client/graph/ELSFRLayout.java new file mode 100644 index 0000000..04283e9 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/ELSFRLayout.java @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2003, the JUNG Project and the Regents of the University of + * California All rights reserved. + * + * This software is open-source under the BSD license; see either "license.txt" + * or http://jung.sourceforge.net/license.txt for a description. + */ +package com.endlessloopsoftware.ego.client.graph; + +import java.util.Iterator; + +import cern.colt.matrix.DoubleMatrix1D; +import cern.colt.matrix.impl.DenseDoubleMatrix1D; +import edu.uci.ics.jung.exceptions.FatalException; +//import edu.uci.ics.jung.graph.ArchetypeVertex; +import edu.uci.ics.jung.graph.Edge; +import edu.uci.ics.jung.graph.Graph; +import edu.uci.ics.jung.graph.Vertex; +import edu.uci.ics.jung.utils.Pair; +import edu.uci.ics.jung.utils.UserData; +import edu.uci.ics.jung.visualization.AbstractLayout; +import edu.uci.ics.jung.visualization.Coordinates; +import edu.uci.ics.jung.visualization.LayoutMutable; + +/** + * Implements the Fruchterman-Reingold algorithm for node layout. + * + * @author Scott White, Yan-Biao Boey, Danyel Fisher + */ +public class ELSFRLayout + extends AbstractLayout + implements LayoutMutable +{ + + private static final Object FR_KEY = "edu.uci.ics.jung.FR_Visualization_Key"; + private double forceConstant; + private double temperature; + private int currentIteration; + private String status = null; + private int mMaxIterations = 700; + + public ELSFRLayout(Graph g) + { + super(g); + // currentIteration = 0; + } + + /* + * new function for handling updates and changes to the graph + */ + public void update() + { + for (Iterator iter = getGraph().getVertices().iterator(); iter.hasNext();) + { + Vertex v = (Vertex) iter.next(); + Coordinates coord = (Coordinates) v.getUserDatum(getBaseKey()); + if (coord == null) + { + coord = new Coordinates(); + v.addUserDatum(getBaseKey(), coord, UserData.REMOVE); + initializeLocation(v, coord, getCurrentSize()); + initialize_local_vertex(v); + } + } + } + + /** + * Returns the current temperature and number of iterations elapsed, as a + * string. + */ + public String getStatus() + { + return status; + } + + public void forceMove(Vertex picked, int x, int y) + { + super.forceMove(picked, x, y); + } + + protected void initialize_local() + { + currentIteration = 0; + temperature = getCurrentSize().getWidth() / 10; + forceConstant = 0.75 * Math.sqrt(getCurrentSize().getHeight() * getCurrentSize().getWidth() + / getVisibleGraph().numVertices()); + } + + private Object key = null; + + private double EPSILON = 0.000001D; + + /** + * Returns a visualization-specific key (that is, specific both to this + * instance and AbstractLayout) that can be used to access + * UserData related to the AbstractLayout. + */ + public Object getKey() + { + if (key == null) + key = new Pair(this, FR_KEY); + return key; + } + + protected void initialize_local_vertex(Vertex v) + { + if (v.getUserDatum(getKey()) == null) + { + v.addUserDatum(getKey(), new FRVertexData(), UserData.REMOVE); + } + } + + /** + * Moves the iteration forward one notch, calculation attraction and + * repulsion between vertices and edges and cooling the temperature. + */ + public void advancePositions() + { + currentIteration++; + status = "VV: " + getVisibleVertices().size() + " IT: " + currentIteration + " temp: " + temperature; + /** + * Calculate repulsion + */ + for (Iterator iter = getVisibleVertices().iterator(); iter.hasNext();) + { + Vertex v1 = (Vertex) iter.next(); + if (dontMove(v1)) + continue; + calcRepulsion(v1); + } + + /** + * Calculate attraction + */ + for (Iterator iter = getVisibleEdges().iterator(); iter.hasNext();) + { + Edge e = (Edge) iter.next(); + + calcAttraction(e); + } + + // double cumulativeChange = 0; + + for (Iterator iter = getVisibleVertices().iterator(); iter.hasNext();) + { + Vertex v = (Vertex) iter.next(); + if (dontMove(v)) + continue; + calcPositions(v); + } + + cool(); + } + + public void calcPositions(Vertex v) + { + FRVertexData fvd = getFRData(v); + Coordinates xyd = super.getCoordinates(v); + double deltaLength = Math.max(EPSILON, Math.sqrt(fvd.disp.zDotProduct(fvd.disp))); + + double newXDisp = fvd.getXDisp() / deltaLength * Math.min(deltaLength, temperature); + + if (Double.isNaN(newXDisp)) { throw new FatalException("Unexpected mathematical result"); } + + double newYDisp = fvd.getYDisp() / deltaLength * Math.min(deltaLength, temperature); + xyd.addX(newXDisp); + xyd.addY(newYDisp); + + double borderWidth = getCurrentSize().getWidth() / 50.0; + double maxborder = getCurrentSize().getWidth() / 8; + borderWidth = Math.max(borderWidth, 30); + borderWidth = Math.min(borderWidth, maxborder); + + double newXPos = xyd.getX(); + if (newXPos < borderWidth) + { + newXPos = borderWidth + Math.random() * borderWidth * 2.0; + } + else if (newXPos > (getCurrentSize().getWidth() - borderWidth)) + { + newXPos = getCurrentSize().getWidth() - borderWidth - Math.random() * borderWidth * 2.0; + } + //double newXPos = Math.min(getCurrentSize().getWidth() - 20.0, + // Math.max(20.0, xyd.getX())); + + double newYPos = xyd.getY(); + if (newYPos < borderWidth) + { + newYPos = borderWidth + Math.random() * borderWidth * 2.0; + } + else if (newYPos > (getCurrentSize().getHeight() - borderWidth)) + { + newYPos = getCurrentSize().getHeight() - borderWidth - Math.random() * borderWidth * 2.0; + } + //double newYPos = Math.min(getCurrentSize().getHeight() - 20.0, + // Math.max(20.0, xyd.getY())); + + xyd.setX(newXPos); + xyd.setY(newYPos); + } + + public void calcAttraction(Edge e) + { + Vertex v1 = (Vertex) e.getIncidentVertices().iterator().next(); + Vertex v2 = e.getOpposite(v1); + + double xDelta = getX(v1) - getX(v2); + double yDelta = getY(v1) - getY(v2); + + double deltaLength = Math.max(EPSILON, Math.sqrt((xDelta * xDelta) + (yDelta * yDelta))); + + double force = (deltaLength * deltaLength) / forceConstant; + + if (Double.isNaN(force)) { throw new FatalException("Unexpected mathematical result"); } + + FRVertexData fvd1 = getFRData(v1); + FRVertexData fvd2 = getFRData(v2); + + fvd1.decrementDisp((xDelta / deltaLength) * force, (yDelta / deltaLength) * force); + fvd2.incrementDisp((xDelta / deltaLength) * force, (yDelta / deltaLength) * force); + } + + public void calcRepulsion(Vertex v1) + { + FRVertexData fvd1 = getFRData(v1); + fvd1.setDisp(0, 0); + + for (Iterator iter2 = getVisibleVertices().iterator(); iter2.hasNext();) + { + Vertex v2 = (Vertex) iter2.next(); + if (dontMove(v2)) + continue; + if (v1 != v2) + { + double xDelta = getX(v1) - getX(v2); + double yDelta = getY(v1) - getY(v2); + + double deltaLength = Math.max(EPSILON, Math.sqrt((xDelta * xDelta) + (yDelta * yDelta))); + + double force = (forceConstant * forceConstant) / deltaLength; + + if (Double.isNaN(force)) { throw new FatalException("Unexpected mathematical result"); } + + fvd1.incrementDisp((xDelta / deltaLength) * force, (yDelta / deltaLength) * force); + } + } + } + + private void cool() + { + temperature *= (1.0 - currentIteration / (double) mMaxIterations); + } + + public void setMaxIterations(int maxIterations) + { + mMaxIterations = maxIterations; + } + + public FRVertexData getFRData(Vertex v) + { + return (FRVertexData) (v.getUserDatum(getKey())); + } + + /** + * This one is an incremental visualization. + */ + public boolean isIncremental() + { + return true; + } + + /** + * Returns true once the current iteration has passed the maximum count, MAX_ITERATIONS. + */ + public boolean incrementsAreDone() + { + if (currentIteration > mMaxIterations) { return true; } + return false; + } + + public static class FRVertexData + { + private DoubleMatrix1D disp; + + public FRVertexData() + { + initialize(); + } + + public void initialize() + { + disp = new DenseDoubleMatrix1D(2); + } + + public double getXDisp() + { + return disp.get(0); + } + + public double getYDisp() + { + return disp.get(1); + } + + public void setDisp(double x, double y) + { + disp.set(0, x); + disp.set(1, y); + } + + public void incrementDisp(double x, double y) + { + disp.set(0, disp.get(0) + x); + disp.set(1, disp.get(1) + y); + } + + public void decrementDisp(double x, double y) + { + disp.set(0, disp.get(0) - x); + disp.set(1, disp.get(1) - y); + } + } +} \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/graph/ELSRenderer.java b/src/com/endlessloopsoftware/ego/client/graph/ELSRenderer.java new file mode 100644 index 0000000..c85c386 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/ELSRenderer.java @@ -0,0 +1,181 @@ +/* +* Copyright (c) 2003, the JUNG Project and the Regents of the University +* of California +* All rights reserved. +* +* This software is open-source under the BSD license; see either +* "license.txt" or +* http://jung.sourceforge.net/license.txt for a description. +*/ +package com.endlessloopsoftware.ego.client.graph; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics; + +import edu.uci.ics.jung.graph.Edge; +import edu.uci.ics.jung.graph.Graph; +import edu.uci.ics.jung.graph.Vertex; +import edu.uci.ics.jung.graph.decorators.StringLabeller; +import edu.uci.ics.jung.visualization.AbstractRenderer; + +/** + * @author Scott White + */ +public class ELSRenderer extends AbstractRenderer +{ + private String mSizeKey; + + private GradientPaint paint = null; + private int mDefaultNodeSize; + private double maxRank = -1; + private double minRank = -1; + + public ELSRenderer() + { + mDefaultNodeSize = 8; + maxRank = 0; + } + + public void paintEdge(Graphics g, Edge e, int x1, int y1, int x2, int y2) + { + Color c = g.getColor(); + g.setColor(Color.LIGHT_GRAY); + g.drawLine(x1, y1, x2, y2); + g.setColor(c); + } + + public void paintVertex(Graphics g, Vertex v, int x, int y) + { + + String label = null; + if (getLabel() != null) + { + // label = (String) v.getUserDatum(getLabel()); + label = StringLabeller.getLabeller((Graph) v.getGraph()).getLabel(v); + } + + if (label == null) + { + label = v.toString(); + } + + if (label.length() > 15) + { + label = label.substring(0, 14); + } + + int nodeSize = mDefaultNodeSize; + if (mSizeKey != null) + { + // for (Iterator it = v.getUserDatumKeyIterator(); it.hasNext();) + // System.out.println(it.next()); + + try + { + Number decoratedNodeSize = (Number) v.getUserDatum(mSizeKey); + int red = 0; + + if (decoratedNodeSize.doubleValue() > 0) + { + // red = (int) Math.ceil((double) 255 * (Math.log(decoratedNodeSize.doubleValue()) / + // Math.log(getMaxDegreeRank()))); + red = (int) Math.ceil((double) 255 * (decoratedNodeSize.doubleValue() / + getMaxDegreeRank())); + } + + // System.out.println(decoratedNodeSize.doubleValue()); + // System.out.println(Math.log(decoratedNodeSize.doubleValue())); + // System.out.println(getMaxDegreeRank()); + // System.out.println(Math.log(getMaxDegreeRank())); + // System.out.println(red); + + Color c = new Color(255, 255 - red, 64 - (red / 4)); + g.setColor(c); + } + catch (Exception e) + { + e.printStackTrace(); + System.exit(-1); + } + } + + int labelSize = g.getFontMetrics().stringWidth(label); + nodeSize = Math.max(nodeSize, 10); + nodeSize = Math.min(nodeSize, 150); + + g.fillOval(x - nodeSize / 2, y - nodeSize / 2, nodeSize, nodeSize); + g.setColor(Color.GRAY); + g.drawOval(x - nodeSize / 2, y - nodeSize / 2, nodeSize, nodeSize); + g.setColor(Color.BLACK); + Font font = new Font("Arial", Font.PLAIN, 12); + Font f = g.getFont(); + g.setFont(font); + if (nodeSize > labelSize) + { + g.drawString(label, x - labelSize / 2, y + 4); + } + else + { + g.drawString(label, x - labelSize / 2 + 20, y + 15); + + } + g.setFont(f); + } + + public String getSizeKey() + { + return mSizeKey; + } + + public void setSizeKey(String decorationKey) + { + this.mSizeKey = decorationKey; + } + + String mLabel; + + public String getLabel() + { + return mLabel; + } + + public void setLabel(String label) + { + this.mLabel = label; + } + + /** + * @return + */ + public double getMaxDegreeRank() + { + return maxRank; + } + + /** + * @param i + */ + public void setMaxDegreeRank(double d) + { + maxRank = d; + } + + /** + * @return + */ + public double getMinRank() + { + return minRank; + } + + /** + * @param d + */ + public void setMinRank(double d) + { + minRank = d; + } + +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/EdgePanel.java b/src/com/endlessloopsoftware/ego/client/graph/EdgePanel.java new file mode 100644 index 0000000..e13573b --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/EdgePanel.java @@ -0,0 +1,400 @@ +package com.endlessloopsoftware.ego.client.graph; + +import java.awt.Color; +import java.awt.Dimension; +import java.util.*; +import java.awt.event.ItemEvent; +import javax.swing.table.*; + +import org.egonet.util.table.*; + +import javax.swing.GroupLayout; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableModel; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.*; + +import com.endlessloopsoftware.ego.Question; +import com.endlessloopsoftware.ego.QuestionList; +import com.endlessloopsoftware.ego.Study; +import com.endlessloopsoftware.ego.client.EgoClient; +import com.endlessloopsoftware.ego.client.graph.GraphQuestion; +import org.egonet.util.listbuilder.Selection; +import edu.uci.ics.jung.utils.Pair; + +public class EdgePanel extends JPanel implements TableModelListener { + + private GroupLayout layout; + + GraphRenderer graphRenderer; + + List graphQuestions = new ArrayList(); + + String[] sizes = { "1", "2", "3", "4", "5" }; + + // Components used + JLabel questionLabel; + + JComboBox questionCombo; + + JLabel selectionLabel; + + // For table entries + + JTable table; + + JButton colorButton; + + JComboBox sizeCombo; + + JComboBox shapeCombo; + + ColorChooserEditor colorChooser; + + List selectionList = new ArrayList(); + + public EdgePanel(GraphRenderer gr) { + + layout = new GroupLayout(this); + this.setLayout(layout); + + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + + this.graphRenderer = gr; + this.graphQuestions = graphRenderer.getGQuestions(); + + createComponents(); + } + + /** + * implemented method for TableModelListener + */ + public void tableChanged(TableModelEvent e) { + int row = e.getFirstRow(); + int column = e.getColumn(); + TableModel model = (TableModel) e.getSource(); + updateMapEntry(model, row, column); + } + + /** + * Updates map with newly selected properties + * + * @param int + * row: row at which a property was changed in the table int + * column: corresponds to which property was chnged TableModel + * tableModel: to get table properties + */ + private void updateMapEntry(TableModel tableModel, int row, int column) { + // Get property settings in modified row + String sizeStr = (String) tableModel.getValueAt(row, 4); + int size = sizeStr != null ? Integer.parseInt(sizeStr) : -1; + EdgeProperty.EdgeShape shape = (EdgeProperty.EdgeShape) tableModel + .getValueAt(row, 3); + Color color = (Color) tableModel.getValueAt(row, 2); + + // create a graph question with the question in question combo and + // selection in modified row of the table + Question question = (Question) questionCombo.getSelectedItem(); + int category = Question.ALTER_PAIR_QUESTION; + Selection selection = (Selection) tableModel.getValueAt(row, 1); + GraphQuestion gq = new GraphQuestion(question, selection, category); + + // look for entry in Map + EdgeProperty currentProperty = (EdgeProperty) GraphRenderer.propertyMap + .get(gq); + + // add/update entry only if selected + if (tableModel.getValueAt(row, 0) == Boolean.TRUE) { + // if entry not found + if (currentProperty == null) { + currentProperty = new EdgeProperty(); + currentProperty.setShape(shape); + currentProperty.setSize(size); + currentProperty.setColor(color); + System.out.println("Entry will be added anew: " + + currentProperty); + } else { + switch (column) { + case 2: // property modified is Color: Update Color only + currentProperty.setColor(color); + break; + case 3: // propery modified is Shape : Update Shape only + currentProperty.setShape(shape); + break; + case 4: // property modified is size : Update size only + currentProperty.setSize(size); + break; + default: + System.out.println("Invalid update attempted"); + break; + } + System.out.println("Re-added entry to map: " + currentProperty); + } + GraphRenderer.propertyMap.put(gq, currentProperty); + + } else { + GraphRenderer.propertyMap.remove(gq); + } + + // Add alter list for Graph Question also as a part of the + // datastructure + GraphData graphData = new GraphData(); + List alterList = graphData.getAlterNumbers(gq); + gq.setAlterList(alterList); + // Get list pairs of alters for the GraphQuestion and create + // corresponding vertex pair map + + displayPropertyMap(); + + updateEdgeMap(); + + } + + /** + * updates the GraphRenderer's edge Map + * + */ + private void updateEdgeMap() { + GraphData graphData = new GraphData(); + GraphRenderer.vertexPair.removeAll(GraphRenderer.vertexPair); + GraphRenderer.edgePropertyList.clear(); + + for (GraphQuestion gq : GraphRenderer.propertyMap.keySet()) { + if (gq.getCategory() == Question.ALTER_PAIR_QUESTION) { + List vPair = graphData.getAlterPairs(gq); + EdgeProperty edgeProperty = (EdgeProperty) GraphRenderer.propertyMap + .get(gq); + for (Pair pair : vPair) { + GraphRenderer.vertexPair.add(pair); + GraphRenderer.edgePropertyList.add(edgeProperty); + } + } + + } + graphRenderer.createNodeEdge(); + graphRenderer.getVv().repaint(); + } + + /** + * Displays contents of the property Map Used for Tesing the process of + * adding values to Map + * + */ + private void displayPropertyMap() { + String question = ""; + String selection = ""; + String propValues = ""; + NodeProperty np; + EdgeProperty ep; + System.out + .println("---------------MAP CONTENTS ----------------------------"); + for (Map.Entry entry : GraphRenderer.propertyMap.entrySet()) { + question = entry.getKey().toString(); + selection = entry.getKey().getSelection().getString(); + if (entry.getKey().getCategory() == Question.ALTER_QUESTION + || entry.getKey().getCategory() == 0) { + np = (NodeProperty) entry.getValue(); + if (np != null) { + propValues = np.toString(); + } + } else { + ep = (EdgeProperty) entry.getValue(); + if (ep != null) { + propValues = ep.toString(); + } + } + System.out.print("\n" + question + " | " + propValues + " | "); + for (int alter : entry.getKey().getAlterList()) { + System.out.print(+alter + ","); + } + } + System.out + .println("\n-------------------------------------------------------"); + } + + /** + * Creates a combo box each for Alterquestions and possible selections for + * the question. It also creates other options such as color, size, shape + */ + private void createComponents() { + + // create question label and combo pair: populated with + // alter/alterpair Qs + questionLabel = new JLabel("Question: "); + questionLabel.setOpaque(true); + + List qList = new ArrayList(); + Study study = EgoClient.interview.getStudy(); + QuestionList questionList = study.getQuestions(); + + Map questionMap = questionList.getQuestionMap(); + for (Long key : questionMap.keySet()) { + Question currentQuestion = questionMap.get(key); + int questionType = currentQuestion.questionType; + if (questionType == Question.ALTER_PAIR_QUESTION) { + // populate the list box with only questions that have choices + // as answers + if (currentQuestion.selections.length >= 1) + qList.add(currentQuestion); + } + } + + questionCombo = new JComboBox(qList.toArray()); + questionCombo.setPreferredSize(new Dimension(20, 20)); + questionCombo.setMaximumSize(new Dimension(20, 30)); + + questionCombo.setAutoscrolls(true); + questionCombo.setSelectedIndex(0); + + questionCombo.addItemListener(new java.awt.event.ItemListener() { + public void itemStateChanged(ItemEvent e) { + + if (e.getStateChange() == ItemEvent.SELECTED) { + createTable(); + drawPanel(); + } + } + }); + + selectionLabel = new JLabel("Answers: "); + selectionLabel.setOpaque(true); + + sizeCombo = new JComboBox(sizes); + sizeCombo.setPreferredSize(new Dimension(20, 20)); + sizeCombo.setMaximumSize(new Dimension(20, 30)); + sizeCombo.setSelectedIndex(0); + + shapeCombo = new JComboBox(EdgeProperty.EdgeShape.values()); + shapeCombo.setPreferredSize(new Dimension(20, 20)); + shapeCombo.setMaximumSize(new Dimension(20, 30)); + shapeCombo.setSelectedIndex(0); + + createTable(); + drawPanel(); + } + + /** + * Creates table and sets the following properties: 1) The initial values of + * the controls based on entries in map 2) Sets table model to instance of + * NodePanel 3) Sets editors for the editable controls 3) Sets renderers for + * each cell + */ + private void createTable() { + Question question = (Question) questionCombo.getSelectedItem(); + int category = Question.ALTER_PAIR_QUESTION; + int noOfRows = question.selections.length; + /* change the list of selections based on the selected question */ + if (!selectionList.isEmpty()) { + selectionList.removeAll(selectionList); + } + for (Selection selection : question.selections) { + selectionList.add(selection); + } + + // populate controls values in map or with default settings of no entry + // exists + Object[][] tableData = new Object[noOfRows][5]; + for (int i = 0; i < noOfRows; i++) { + Selection selection = selectionList.get(i); + GraphQuestion gq = new GraphQuestion(question, selection, category); + + // Test code + GraphData gd = new GraphData(); + gd.getAlterPairs(gq); + // end of test code + + EdgeProperty currentProperty = (EdgeProperty) GraphRenderer.propertyMap + .get(gq); + + if (currentProperty != null) { + tableData[i][0] = Boolean.TRUE; + tableData[i][1] = selection; + tableData[i][2] = currentProperty.getColor(); + tableData[i][3] = currentProperty.getShape(); + tableData[i][4] = (new Integer(currentProperty.getSize())) + .toString(); + + } else { + tableData[i][0] = Boolean.FALSE; + tableData[i][1] = selection; + tableData[i][2] = Color.BLACK; + tableData[i][3] = EdgeProperty.EdgeShape.Line; + tableData[i][4] = new String("1"); + + } + } + + table = new JTable(new TableModelBoolean(tableData)); + table.getModel().addTableModelListener(this); + table.setRowHeight(25); + table.setBorder(BorderFactory.createCompoundBorder(BorderFactory + .createLineBorder(Color.black), getBorder())); + TableColumnModel columnModel = table.getColumnModel(); + + // Size Column - set renderer and editor + DefaultCellEditor sizeEditor = new DefaultCellEditor(sizeCombo); + columnModel.getColumn(4).setCellEditor(sizeEditor); + columnModel.getColumn(4).setCellRenderer( + new TableComboBoxRenderer(sizes)); + + // Shape column - set renderer and editor + DefaultCellEditor shapeEditor = new DefaultCellEditor(shapeCombo); + columnModel.getColumn(3).setCellEditor(shapeEditor); + columnModel.getColumn(3).setCellRenderer( + new TableComboBoxRenderer(EdgeProperty.EdgeShape.values())); + + // Color column - Set editor and renderer + TableCellEditor colorEditor = new ColorEditor(); + columnModel.getColumn(2).setCellEditor(colorEditor); + ColorRenderer colorButtonRenderer = new ColorRenderer(true); + columnModel.getColumn(2).setCellRenderer(colorButtonRenderer); + + // selection column - not editable, set renderer only + LabelRenderer selectionRenderer = new LabelRenderer(); + columnModel.getColumn(1).setCellRenderer(selectionRenderer); + + columnModel.getColumn(0).setPreferredWidth(30); + columnModel.getColumn(1).setPreferredWidth(250); + columnModel.getColumn(2).setPreferredWidth(30); + columnModel.getColumn(3).setPreferredWidth(100); + columnModel.getColumn(4).setPreferredWidth(50); + } + + /** + * Fits the components belonging to this panel using Group Layout + * + */ + private void drawPanel() { + this.removeAll(); + + GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup(); + hGroup.addGroup(layout.createParallelGroup() + .addComponent(questionLabel).addComponent(questionCombo) + .addComponent(selectionLabel).addComponent( + table.getTableHeader()).addComponent(table)); + layout.setHorizontalGroup(hGroup); + + GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup(); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(questionLabel)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(questionCombo)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(selectionLabel)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent( + table.getTableHeader())); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(table)); + layout.setVerticalGroup(vGroup); + + } + +} \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/graph/EdgeProperty.java b/src/com/endlessloopsoftware/ego/client/graph/EdgeProperty.java new file mode 100644 index 0000000..75926f7 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/EdgeProperty.java @@ -0,0 +1,33 @@ +package com.endlessloopsoftware.ego.client.graph; + +import java.awt.*; + +public class EdgeProperty extends GraphProperty{ + + public static enum EdgeShape {Line, QuadCurve, CubicCurve} + EdgeShape shape; + + public EdgeProperty() + { + this.size= 1; + this.shape = EdgeShape.Line; + this.color = Color.BLACK; + } + + public EdgeShape getShape() { + return this.shape; + } + + public void setShape(EdgeShape shape) { + this.shape = shape; + } + + public String toString() + { + String str; + str = this.shape.toString() + " " + this.color.toString() + " " + this.size; + return str; + } + + +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/EllipseVertexShapeFunction.java b/src/com/endlessloopsoftware/ego/client/graph/EllipseVertexShapeFunction.java new file mode 100644 index 0000000..96dd5e2 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/EllipseVertexShapeFunction.java @@ -0,0 +1,35 @@ +package com.endlessloopsoftware.ego.client.graph; + +import java.awt.Shape; +import edu.uci.ics.jung.graph.Vertex; +import edu.uci.ics.jung.graph.decorators.*; + +public class EllipseVertexShapeFunction extends AbstractVertexShapeFunction +{ + public EllipseVertexShapeFunction() + { + this.setSizeFunction(new ConstantVertexSizeFunction(15)); + } + public EllipseVertexShapeFunction(VertexSizeFunction vsf, VertexAspectRatioFunction varf) + { + super(vsf, varf); + } + public Shape getShape(Vertex v, int size) + { + this.setSizeFunction(new ConstantVertexSizeFunction(size)); + return getShape(v); + } + public Shape getShape(Vertex v) + { + return factory.getEllipse(v); + } + public Shape getShape(Vertex v, NodeProperty.NodeShape type, int size){ + this.setSizeFunction(new ConstantVertexSizeFunction(size)); + if(type == NodeProperty.NodeShape.Star) { + return factory.getRegularStar(v, 5); + } + else { + return factory.getRoundRectangle(v); + } + } +} \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/graph/GraphData.java b/src/com/endlessloopsoftware/ego/client/graph/GraphData.java new file mode 100644 index 0000000..14a010b --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/GraphData.java @@ -0,0 +1,404 @@ +package com.endlessloopsoftware.ego.client.graph; + +import com.endlessloopsoftware.ego.*; +import org.egonet.util.listbuilder.Selection; +import com.endlessloopsoftware.ego.client.Interview; +import com.endlessloopsoftware.ego.client.EgoClient; +import com.endlessloopsoftware.egonet.Shared; +import java.util.*; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.File; + +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.Font; +import java.awt.Point; +import java.awt.Color; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.util.Set; +import java.awt.Dimension; +import java.awt.Rectangle; +import edu.uci.ics.jung.graph.ArchetypeEdge; +import edu.uci.ics.jung.graph.Edge; +import edu.uci.ics.jung.graph.Graph; +import edu.uci.ics.jung.graph.Vertex; +import edu.uci.ics.jung.graph.decorators.EdgeStringer; +import edu.uci.ics.jung.graph.decorators.StringLabeller; +import edu.uci.ics.jung.graph.decorators.VertexFontFunction; +import edu.uci.ics.jung.visualization.*; +import edu.uci.ics.jung.visualization.contrib.CircleLayout; +import edu.uci.ics.jung.visualization.contrib.KKLayout; +import edu.uci.ics.jung.visualization.transform.MutableTransformer; + +import javax.imageio.ImageIO; + +import edu.uci.ics.jung.utils.Pair; + +public class GraphData { + + private static final double OFFSET = 25.0d; + + private String[] completeAlterNameList; + + private Interview interview; + + public static int[][] adjacencyMatrix = new int[EgoClient.interview + .getStudy().getNumAlters()][EgoClient.interview.getStudy() + .getNumAlters()]; + + /** + * List to contain list of all alter Questions in interview + */ + private static List interviewAlterQuestionList; + + /** + * List to contain list of all alter pair Questions in interview + */ + private List interviewAlterPairQuestionList; + + public GraphData() { + interview = EgoClient.interview; + completeAlterNameList = EgoClient.interview.getStats().alterList; + adjacencyMatrix = interview.getStats().adjacencyMatrix; + interviewAlterQuestionList = new ArrayList(); + interviewAlterPairQuestionList = new ArrayList(); + } + + /** + * Generates list of alter pairs about whom the graph question is. + * + * @param graphQuestion + * @return Pair AlterPairList + */ + public List getAlterPairs(GraphQuestion graphQuestion) { + List alterPairList = new ArrayList(); + List alterNumbers = new ArrayList(); + alterNumbers = getAlterNumbers(graphQuestion); + for (int i = 0; i < alterNumbers.size(); i = i + 2) { + Pair alterPair = new Pair(alterNumbers.get(i), alterNumbers + .get(i + 1)); + alterPairList.add(alterPair); + // System.out.println(alterPair); + } + return alterPairList; + } + + /** + * Populates alter-question list and alter-pair question list for the chosen + * interview + */ + private void populateQuestionLists() { + Question question; + + interview.rewind(); + interviewAlterQuestionList.removeAll(interviewAlterQuestionList); + interviewAlterPairQuestionList + .removeAll(interviewAlterPairQuestionList); + while (interview.hasNext()) { + question = interview.next(); + if (question.questionType == Question.ALTER_QUESTION) { + interviewAlterQuestionList.add(question); + } else if (question.questionType == Question.ALTER_PAIR_QUESTION) { + interviewAlterPairQuestionList.add(question); + } + } + } + + /** + * Given a graph question, this method returns the list of alters for whom + * the question answer pair was provided. + * + * @param graphQuestion + * @param category: + * ALTER_QUESTION or ALTER_PAIR_QUESTION + * @return + */ + public List getAlterNumbers(GraphQuestion graphQuestion) { + + List alterNumbers = new ArrayList(); + Long QID = graphQuestion.getQuestion().UniqueId; + Iterator questionIterator; + Question interviewQuestion; + + populateQuestionLists(); + + if (graphQuestion.getCategory() == Question.ALTER_QUESTION) { + questionIterator = interviewAlterQuestionList.iterator(); + } else { // if category is ALTER_PAIR_QUESTION + questionIterator = interviewAlterPairQuestionList.iterator(); + } + + while (questionIterator.hasNext()) { + + interviewQuestion = (Question) questionIterator.next(); + if (interviewQuestion.answer.answered == false) { + System.out.println(interviewQuestion.UniqueId + " | " + + interviewQuestion.text + " | Unanswered"); + continue; + } + + if (interviewQuestion.UniqueId == QID) { + if ((interviewQuestion.answer.string.trim()) + .equals(graphQuestion.getSelection().getString().trim())) { + int[] alterNumArray = interviewQuestion.answer.getAlters(); + for (int alterNum : alterNumArray) { + alterNumbers.add(alterNum); + // System.out.println(alterNum); + } + } + } + } + return alterNumbers; + } + + public List getAlterNumbers(Question selectedQuestion, Selection selection) { + List alterNumbers = new ArrayList(); + Long QID = selectedQuestion.UniqueId; + Iterator questionIterator; + Question interviewQuestion; + populateQuestionLists(); + questionIterator = interviewAlterQuestionList.iterator(); + while (questionIterator.hasNext()) { + interviewQuestion = (Question) questionIterator.next(); + if (interviewQuestion.answer.answered == false) { + System.out.println(interviewQuestion.UniqueId + " | " + + interviewQuestion.text + " | Unanswered"); + continue; + } + if (interviewQuestion.UniqueId == QID) { + if ((interviewQuestion.answer.string.trim()) + .equals(selection.getString().trim())) { + int[] alterNumArray = interviewQuestion.answer.getAlters(); + for (int alterNum : alterNumArray) { + alterNumbers.add(alterNum); + // System.out.println(alterNum); + } + } + } + } + return alterNumbers; + } + /** + * Given a graph question, this method returns the list of alters for whom + * the question answer pair was provided. + * + * @param graphQuestion + * @param category: + * ALTER_QUESTION or ALTER_PAIR_QUESTION + * @return + */ + public List getAlterNames(GraphQuestion graphQuestion) { + + List alterNames = new ArrayList(); + Long QID = graphQuestion.getQuestion().UniqueId; + Iterator questionIterator; + Question interviewQuestion; + + populateQuestionLists(); + + if (graphQuestion.getCategory() == Question.ALTER_QUESTION) { + questionIterator = interviewAlterQuestionList.iterator(); + } else { // if category is ALTER_PAIR_QUESTION + questionIterator = interviewAlterPairQuestionList.iterator(); + } + while (questionIterator.hasNext()) { + + interviewQuestion = (Question) questionIterator.next(); + if (interviewQuestion.answer.answered == false) { + System.out.println(interviewQuestion.UniqueId + " | " + + interviewQuestion.text + " | Unanswered"); + continue; + } + if (interviewQuestion.UniqueId == QID) { + if (interviewQuestion.answer.string.trim().equals( + graphQuestion.getSelection().getString().trim())) { + int[] alterNumArray = interviewQuestion.answer.getAlters(); + for (int alterNum : alterNumArray) { + alterNames.add(completeAlterNameList[alterNum]); + } + } + } + } + + return alterNames; + } + + public static void generateAdjacencyMatrix(Question question, + Selection selection, boolean weighted) { + Study study = EgoClient.interview.getStudy(); + if (study.getUIType().equals(Shared.TRADITIONAL_QUESTIONS)) { + for (Iterator it = EgoClient.interview.getAnswerSubset( + question.UniqueId).iterator(); it.hasNext();) { + Answer a = (Answer) it.next(); + if (weighted) { + if ((adjacencyMatrix[a.getAlters()[0]][a.getAlters()[1]] == 0) + && (adjacencyMatrix[a.getAlters()[1]][a.getAlters()[0]] == 0) + && (a.value == selection.getValue())) { + adjacencyMatrix[a.getAlters()[0]][a.getAlters()[1]] = (a.adjacent) ? selection + .getValue() + : 0; + adjacencyMatrix[a.getAlters()[1]][a.getAlters()[0]] = (a.adjacent) ? selection + .getValue() + : 0; + System.out + .println("Updating weighted adjacency matrix"); + } + } else { + if ((adjacencyMatrix[a.getAlters()[0]][a.getAlters()[1]] == 0) + && (adjacencyMatrix[a.getAlters()[1]][a.getAlters()[0]] == 0) + && (a.value == selection.getValue())) { + adjacencyMatrix[a.getAlters()[0]][a.getAlters()[1]] = (a.adjacent) ? 1 + : 0; + adjacencyMatrix[a.getAlters()[1]][a.getAlters()[0]] = (a.adjacent) ? 1 + : 0; + System.out + .println("Updating weighted adjacency matrix"); + } + + } + } + } + } + + public static void writeJPEGImage(File imageFile) + { + int width = GraphRenderer.getVv().getWidth(); + int height = GraphRenderer.getVv().getHeight(); + + BufferedImage bi = new BufferedImage(width, height, + BufferedImage.TYPE_INT_RGB); + Graphics2D graphics = bi.createGraphics(); + GraphRenderer.getVv().paint(graphics); + + graphics.dispose(); + + try { + ImageIO.write(bi, "jpeg", imageFile); + } catch (Exception e) { + e.printStackTrace(); + } + /* + Color bg = GraphRenderer.getVv().getBackground(); + Rectangle rect = calculateGraphRect(); + Dimension size = rect.getSize(); + size.setSize(size.width + OFFSET * 2, size.height + OFFSET * 2); + BufferedImage bi = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_BGR); + Graphics2D graphics = bi.createGraphics(); + graphics.setColor(bg); + graphics.fillRect(0, 0, size.width, size.height); + Dimension visibleSize = GraphRenderer.getVv().getSize(); + + // Hide the visualization viewer, resize it to entire graph size and move the graph to + // upper most left corner of the viewr. + GraphRenderer.getVv().setVisible(false); + GraphRenderer.getVv().setSize(size); + GraphRenderer.getVv().getViewTransformer().translate(OFFSET - rect.getX(), OFFSET - rect.getY()); + + GraphRenderer.getVv().paint(graphics); + + // Return the previous size and location and redisplay the graph + GraphRenderer.getVv().getViewTransformer().translate(rect.getX() - OFFSET, rect.getY() - OFFSET); + GraphRenderer.getVv().setSize(visibleSize); + GraphRenderer.getVv().setVisible(true); + + try { + ImageIO.write(bi, "jpeg", imageFile); + } catch (IOException e) { + e.printStackTrace(); + } + */ + } + + public static StringLabeller getStringLabeller(Graph graph) { + return StringLabeller.getLabeller(graph); + } + + public static Rectangle calculateGraphRect() { + double x; + double y; + double minX = Double.MAX_VALUE; + double minY = Double.MAX_VALUE; + double maxX = Double.MIN_VALUE; + double maxY = Double.MIN_VALUE; + + double labeloffset = OFFSET * 2.0d; + + Point2D location; + + Layout layout = GraphRenderer.getVv().getGraphLayout(); + MutableTransformer layoutTransformer = GraphRenderer.getVv().getLayoutTransformer(); + Graph graph = layout.getGraph(); + StringLabeller labeller = getStringLabeller(GraphRenderer.getGraph()); + Set vertices = graph.getVertices(); + Vertex mostRightVertex = vertices.iterator().next(); + + // Find the upper most left in lower most right vertices + for (Vertex v : vertices) { + // Transform from graph layout coordinates to graphics2d coordinates + location = layoutTransformer.transform(layout.getLocation(v)); + x = location.getX(); + y = location.getY(); + if (x < minX) { + minX = x; + } + if (x > maxX) { + maxX = x; + mostRightVertex = v; + } + if (y < minY) { + minY = y; + } + if (y > maxY) { + maxY = y; + } + } + + // Calculate the width of the right most vetrex label + String label = labeller.getLabel(mostRightVertex); + if (label == null) { + label = ""; + } + if (GraphRenderer.getVv().getRenderer() instanceof PluggableRenderer) { + VertexFontFunction vertexFontFunction = + ((PluggableRenderer) GraphRenderer.getVv().getRenderer()).getVertexFontFunction(); + Font font = vertexFontFunction.getFont(mostRightVertex); + Rectangle2D labelBounds = font.getStringBounds(label, ((Graphics2D) GraphRenderer.getVv().getGraphics()).getFontRenderContext()); + labeloffset += labelBounds.getWidth(); + } else { + Font font = GraphRenderer.getVv().getFont(); + Rectangle2D labelBounds = font.getStringBounds(label, ((Graphics2D) GraphRenderer.getVv().getGraphics()).getFontRenderContext()); + labeloffset += labelBounds.getWidth(); + } + + final Dimension actual = new Dimension((int) (maxX - minX) + (int) labeloffset, (int) (maxY - minY) + (int) OFFSET * 2); + return new Rectangle(new Point((int) minX, (int) minY), actual); + } + public String[] getCompleteAlterNameList() { + return completeAlterNameList; + } + + public Interview getInterview() { + return interview; + } + + public List getInterviewAlterPairQuestionList() { + return interviewAlterPairQuestionList; + } + + public List getInterviewAlterQuestionList() { + return interviewAlterQuestionList; + } + + public int[][] getAdjacencyMatrix() { + return adjacencyMatrix; + } + + public void setAdjacencyMatrix(int[][] adjacencyMatrix) { + this.adjacencyMatrix = adjacencyMatrix; + } + +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/GraphPanel.java b/src/com/endlessloopsoftware/ego/client/graph/GraphPanel.java new file mode 100644 index 0000000..43e7657 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/GraphPanel.java @@ -0,0 +1,136 @@ +package com.endlessloopsoftware.ego.client.graph; + +import java.awt.*; +import javax.swing.*; + +import com.endlessloopsoftware.ego.client.graph.GraphTabPanel; +import edu.uci.ics.jung.visualization.GraphZoomScrollPane; +import com.endlessloopsoftware.ego.client.statistics.StatisticsFrame; + +public class GraphPanel extends javax.swing.JPanel { + private JSplitPane mainSplitPane; + + private JSplitPane rightSplitPane; + + // private JPanel mainPanel; + + private JPanel leftPanel; + + private JPanel topRightPanel; + + GraphRenderer graphRenderer; + + public GraphPanel() { + init(); + } + + /** + * calls createRenderer(Statistics Frame) calls methods to create split + * panes + * + * @param frame + */ + public void init() { + GraphRenderer.propertyMap.clear(); + GraphRenderer.edgeMap.clear(); + GraphRenderer.edgeLabelMap.clear(); + GraphRenderer.edgePropertyList.clear(); + + graphRenderer = new GraphRenderer(); + graphRenderer.createNodeEdge(); + // split the window into 3 and populate with appropriate panels + createSplitPanel(); + this.setLayout(new BorderLayout()); + this.add(mainSplitPane, BorderLayout.CENTER); + } + + /* + * Creating and populating separate panels for left pane, top right pane and + * bottom right pane + */ + + /** + * Mehtod to create left panel and add graph to it + */ + private void createLeftPanel() { + leftPanel = new JPanel(); + JComponent gzsp = graphRenderer.createGraph(); + leftPanel.setLayout(new BorderLayout()); + leftPanel.setPreferredSize(new Dimension(600, 600)); + leftPanel.add(gzsp, BorderLayout.CENTER); + } + + /** + * Create Top right panel with graph, node and edge tabs + * + */ + private void createTopRightPanel() { + topRightPanel = new JPanel(new BorderLayout()); + + JTabbedPane tabs = new JTabbedPane(); + + tabs.add("Graph", new GraphTabPanel(graphRenderer)); + // tabs.add("Node", new NodePanel(graphRenderer)); + tabs.add("Edge", new EdgePanel(graphRenderer)); + tabs.add("Node Label", new NodeLabelPanel(graphRenderer)); + tabs.add("Node Color", new NodeColorPanel(graphRenderer)); + tabs.add("Node Shape", new NodeShapePanel(graphRenderer)); + tabs.add("Node Size", new NodeSizePanel(graphRenderer)); + tabs.add("Structural Measures", new StructuralMeasuresPanel(graphRenderer)); + + + topRightPanel.add(tabs); + } + + /** + * Create panels for individual parts of the split window Create split panes + * to divide total window into three set properties of set property to male + * split pane contractible Split panes are populated with individual panels + * for the split window + */ + private void createSplitPanel() { + createLeftPanel(); + createTopRightPanel(); + + // Topright: 3 tabbed window; Bottom right: satellite view; left: graph + // display + JScrollPane topRightScrollPanel = new JScrollPane(topRightPanel); + + JComponent satellitePane = graphRenderer.createSatellitePane(); + satellitePane.setMinimumSize(satellitePane.getPreferredSize()); + + rightSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, topRightScrollPanel, satellitePane); + + rightSplitPane.setResizeWeight(0.75); + rightSplitPane.setContinuousLayout(true); + + //with staellite pane +// mainSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, leftPanel, +// rightSplitPane); + + //without satellite pane + mainSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, leftPanel, + topRightScrollPanel); + + mainSplitPane.setResizeWeight(0.8); + mainSplitPane.setContinuousLayout(true); + mainSplitPane.setOneTouchExpandable(true); + } + + public static void main(String[] args) { + + JFrame frame = new JFrame(); + GraphPanel gf = new GraphPanel(); + + frame.add(gf); + + frame.setTitle("Ego-centric network Graph"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(1200, 800); + frame.setLocationRelativeTo(null); + frame.pack(); + frame.setVisible(true); + + } + +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/GraphProperty.java b/src/com/endlessloopsoftware/ego/client/graph/GraphProperty.java new file mode 100644 index 0000000..de0e6ed --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/GraphProperty.java @@ -0,0 +1,50 @@ +package com.endlessloopsoftware.ego.client.graph; + +import java.awt.*; + + +public class GraphProperty { + +// public static final int SHAPE = 1; +// public static final int SIZE = 2; +// public static final int COLOR = 3; +// public static final int ALL = 4; + + Color color; + int size; + String label; + boolean defaultSetting = true; + + public GraphProperty() + { + color = Color.RED; + size = 1; + label = ""; + } + + public boolean isDefaultSetting() { + return this.defaultSetting; + } + public void setDefaultSetting(boolean defaultSetting) { + this.defaultSetting = defaultSetting; + } + public Color getColor() { + return this.color; + } + public void setColor(Color color) { + this.color = color; + } + public String getLabel() { + return label; + } + public void setLabel(String label) { + this.label = label; + } + public int getSize() { + return size; + } + public void setSize(int size) { + this.size = size; + } + +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/GraphQuestion.java b/src/com/endlessloopsoftware/ego/client/graph/GraphQuestion.java new file mode 100644 index 0000000..5d27f42 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/GraphQuestion.java @@ -0,0 +1,92 @@ +package com.endlessloopsoftware.ego.client.graph; + +import java.util.*; + +import com.endlessloopsoftware.ego.*; +import org.egonet.util.listbuilder.Selection; + +public class GraphQuestion { + + // public static enum QuestionCategory { EGO, ALTER, ALTERPAIR } + private Question question; + + private Selection selection; + + //used only for ALTER QUESTIONS + private List alterList = new ArrayList(); + /** + * EGO_QUESTION = 1; ALTER_PROMPT = 2; ALTER_QUESTION = 3; + * ALTER_PAIR_QUESTION = 4; + */ + private int category; + + private boolean showLabel = false; + + public boolean isShowLabel() { + return showLabel; + } + + public void setShowLabel(boolean showLabel) { + this.showLabel = showLabel; + } + + public GraphQuestion(Question question, Selection selection, int category) { + this.question = question; + this.category = category; + this.selection = selection; + } + + public Selection getSelection() { + return selection; + } + + public void setSelection(Selection selection) { + this.selection = selection; + } + + public int getCategory() { + return category; + } + + public void setCategory(int category) { + this.category = category; + } + + public Question getQuestion() { + return question; + } + + public void setQuestion(Question question) { + this.question = question; + } + + public String toString() { + return (this.question.title + " " + this.selection.getString()); + } + + public boolean equals(Object obj) { + if(!(obj instanceof GraphQuestion)) + return false; + + GraphQuestion gq = (GraphQuestion)obj; + return (this.question.equals(gq.question) + && this.selection.equals(gq.selection) + && this.category == gq.category); + } + + public int hashCode() + { + return this.question.hashCode() + this.selection.hashCode() + category; + } + + public List getAlterList() { + return alterList; + } + + public void setAlterList(List alterList) { + this.alterList = alterList; + } + + + +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/GraphRenderer.java b/src/com/endlessloopsoftware/ego/client/graph/GraphRenderer.java new file mode 100644 index 0000000..09ab938 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/GraphRenderer.java @@ -0,0 +1,761 @@ +package com.endlessloopsoftware.ego.client.graph; + +import java.lang.reflect.Constructor; +import java.util.*; +import java.util.List; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; + +import javax.swing.*; + +import edu.uci.ics.jung.visualization.PluggableRenderer; +import com.endlessloopsoftware.ego.*; +import org.egonet.util.listbuilder.Selection; +import com.endlessloopsoftware.ego.client.EgoClient; +import com.endlessloopsoftware.ego.QuestionList; +import com.endlessloopsoftware.ego.Question; +import com.endlessloopsoftware.ego.client.statistics.Statistics; + +import edu.uci.ics.jung.graph.ArchetypeVertex; +import edu.uci.ics.jung.graph.Edge; +import edu.uci.ics.jung.graph.Graph; +import edu.uci.ics.jung.graph.Vertex; +import edu.uci.ics.jung.utils.Pair; +import edu.uci.ics.jung.graph.decorators.ConstantEdgePaintFunction; +import edu.uci.ics.jung.graph.decorators.ConstantEdgeStrokeFunction; +import edu.uci.ics.jung.graph.decorators.ConstantVertexPaintFunction; +import edu.uci.ics.jung.graph.decorators.ToolTipFunction; +import edu.uci.ics.jung.graph.decorators.EdgeShape; +import edu.uci.ics.jung.graph.decorators.VertexStringer; +import edu.uci.ics.jung.graph.decorators.VertexPaintFunction; +import edu.uci.ics.jung.graph.decorators.EdgeStrokeFunction; +import edu.uci.ics.jung.visualization.FRLayout; +import edu.uci.ics.jung.visualization.Layout; +import edu.uci.ics.jung.graph.decorators.EdgeStringer; +import edu.uci.ics.jung.visualization.ShapePickSupport; +import edu.uci.ics.jung.graph.decorators.PickableEdgePaintFunction; +import edu.uci.ics.jung.graph.decorators.EdgePaintFunction; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; +import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; +import edu.uci.ics.jung.visualization.control.ScalingControl; +import edu.uci.ics.jung.visualization.DefaultVisualizationModel; +import edu.uci.ics.jung.visualization.GraphZoomScrollPane; +import edu.uci.ics.jung.visualization.VisualizationModel; +import edu.uci.ics.jung.visualization.control.SatelliteVisualizationViewer; +import edu.uci.ics.jung.graph.ArchetypeEdge; +import edu.uci.ics.jung.graph.decorators.EdgeShapeFunction; +import edu.uci.ics.jung.graph.Vertex; +import edu.uci.ics.jung.graph.decorators.StringLabeller; +import edu.uci.ics.jung.graph.decorators.StringLabeller.UniqueLabelException; +import edu.uci.ics.jung.graph.impl.SparseVertex; +import edu.uci.ics.jung.graph.impl.UndirectedSparseEdge; +import edu.uci.ics.jung.graph.impl.UndirectedSparseGraph; +import edu.uci.ics.jung.graph.decorators.VertexShapeFunction; + +public class GraphRenderer extends PluggableRenderer implements + VertexShapeFunction, VertexPaintFunction, EdgeShapeFunction, + EdgePaintFunction, EdgeStringer, VertexStringer, EdgeStrokeFunction, + ToolTipFunction { + + public static java.util.List gQuestions = java.util.Collections + .synchronizedList(new ArrayList()); + + /** + * Contains a map of GraphQuestion --> GraphProperty. GraphProperty may be + * any of its two subclasses - NodeProperty or EdgeProperty. This map is + * used to find out the list of alters matching a QA pair and their + * respective properties in the graph This map will be used to poplulate the + * vertexMap + */ + public static Map propertyMap = java.util.Collections + .synchronizedMap(new HashMap()); + + public static List edgePropertyList = java.util.Collections + .synchronizedList(new ArrayList()); + + public static Map edgeMap = java.util.Collections + .synchronizedMap(new HashMap()); + + public static Map edgeLabelMap = java.util.Collections + .synchronizedMap(new HashMap()); + + public static List vertexPair = java.util.Collections + .synchronizedList(new ArrayList()); + + private GraphSettings graphSettings; + + private static VisualizationViewer visualizationViewer; + + private GraphZoomScrollPane visualizationViewerScrollPane; + + private SatelliteVisualizationViewer satelliteVisualizationViewer; + + private GraphZoomScrollPane satelliteVisualizationViewerScrollPane; + + private DefaultModalGraphMouse graphMouse; + + private VisualizationModel visualizationModel; + + private StringLabeller undirectedLabeler; + + private Vertex[] _vertexArray = null; + + private List edgeList = null; + + private int[][] adjacencyMatrix; + + private String[] alterList; + + private Statistics stats; + + private GraphData graphData; + + private static Graph graph; + + public static boolean showEdgeWeights = false; + + public GraphRenderer() { + graph = new UndirectedSparseGraph(); + stats = EgoClient.interview.getStats(); + alterList = stats.alterList; + _vertexArray = new Vertex[alterList.length]; + + for (int i = 0; i < alterList.length; ++i) { + _vertexArray[i] = new SparseVertex(); + graph.addVertex(_vertexArray[i]); + } + + graphData = new GraphData(); + graphSettings = new GraphSettings(this); + adjacencyMatrix = graphData.getAdjacencyMatrix(); + this.setVertexShapeFunction(this); + this.setVertexPaintFunction(this); + this.setEdgeShapeFunction(this); + this.setEdgePaintFunction(this); + this.setEdgeStrokeFunction(this); + EdgeStringer stringer = new EdgeStringer() { + public String getLabel(ArchetypeEdge e) { + return ""; + } + }; + this.setEdgeStringer(stringer); + this.setVertexStringer(this); + } + + /** + * Redraws the graph with the provided layout + * + * @param Class + * layout + */ + public void changeLayout(Class layout) { + try { + Constructor constructor = layout + .getConstructor(new Class[] { Graph.class }); + Object o = constructor.newInstance(graph); + Layout l = (Layout) o; + + if (l instanceof FRLayout) { + FRLayout frLayout = (FRLayout) l; + frLayout.setMaxIterations(1000); + } + + // TODO: change required with spring layout not FR layout + if (l instanceof SpringLayout) { + SpringLayout springLayout = (SpringLayout) l; + // springLayout.setMaxIterations(1000); + } + visualizationViewer.stop(); + visualizationViewer.setGraphLayout(l, false); + visualizationViewer.restart(); + + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + /** + * create the main viewable Graph by for display on a panel use the JUNG + * classes + * + * @return + */ + public JComponent createGraph() { + ToolTipManager.sharedInstance().setDismissDelay(10000); + // setVertexStringer(undirectedLabeler); + graphMouse = new DefaultModalGraphMouse(); + + // create the model that drives layouts and view updates + visualizationModel = new DefaultVisualizationModel(new FRLayout(graph)); + + // create the regular viewer and scroller + visualizationViewer = new VisualizationViewer(visualizationModel, this); + visualizationViewer.setPickSupport(new ShapePickSupport()); + visualizationViewer.setToolTipFunction(this); + visualizationViewer.setGraphMouse(graphMouse); + visualizationViewer.setBackground(Color.WHITE); + + visualizationViewerScrollPane = new GraphZoomScrollPane( + visualizationViewer); + final ScalingControl scaler = new CrossoverScalingControl(); + + JButton plus = new JButton("+"); + plus.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(visualizationViewer, 1.1f, visualizationViewer + .getCenter()); + } + }); + // plus.setMaximumSize(new Dimension(20,20)); + JButton minus = new JButton("-"); + minus.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + scaler.scale(visualizationViewer, 1 / 1.1f, visualizationViewer + .getCenter()); + } + }); + // minus.setMaximumSize(new Dimension(20,20)); + // visualizationViewerScrollPane.add(plus); + // visualizationViewerScrollPane.add(minus); + + // create the sat viewer and scroller + satelliteVisualizationViewer = new SatelliteVisualizationViewer( + visualizationViewer, visualizationModel, + new PluggableRenderer()); // TODO: fix renderer and change + // back to this + satelliteVisualizationViewer.setPreferredSize(new Dimension(150, 150)); + satelliteVisualizationViewer.setToolTipFunction(this); + satelliteVisualizationViewer.setBackground(visualizationViewer + .getBackground()); + + satelliteVisualizationViewerScrollPane = new GraphZoomScrollPane( + satelliteVisualizationViewer); + + return visualizationViewerScrollPane; + } + + /** + * creates the nodes for every alter creates edges for entries in adjacency + * matrix + */ + public void createNodeEdge() { + + graph.removeAllEdges(); + // graph.removeAllVertices(); + + edgeMap.clear(); + edgeList = new ArrayList(); + + int counter = 0; + + if (!vertexPair.isEmpty()) { + for (Pair alterPair : vertexPair) { + UndirectedSparseEdge edge = new UndirectedSparseEdge( + _vertexArray[(Integer) alterPair.getFirst()], + _vertexArray[(Integer) alterPair.getSecond()]); + edgeMap.put(edge, edgePropertyList.get(counter)); + + edgeLabelMap.put(edge, + ((Integer) stats.proximityMatrix[(Integer) alterPair + .getFirst()][(Integer) alterPair.getSecond()]) + .toString()); + + counter++; + graph.addEdge(edge); + + } + return; + } + for (int i = 0; i < adjacencyMatrix.length; ++i) { + for (int j = i + 1; j < adjacencyMatrix[i].length; ++j) { + if (adjacencyMatrix[i][j] > 0) { + UndirectedSparseEdge edge = new UndirectedSparseEdge( + _vertexArray[i], _vertexArray[j]); + graph.addEdge(edge); + + edgeLabelMap.put(edge, + ((Integer) stats.proximityMatrix[i][j]).toString()); + + } + } + } + } + + /* + * (non-Javadoc) + * + * @see edu.uci.ics.jung.graph.decorators.VertexStringer#getLabel(edu.uci.ics.jung.graph.ArchetypeVertex) + */ + public String getLabel(ArchetypeVertex v) { + /* + * String label = null; for (int i = 0; i < _vertexArray.length; i++) { + * if (_vertexArray[i].equals(v)) { label = alterList[i]; } } + */ + // return vertexLabelMap.get(v); + return graphSettings.getNodeLabel(v); + // return label; + } + + /** + * Creates the small thumdnail viewer for the main graph. You MUST call + * createGraph first. + * + * @return + */ + public JComponent createSatellitePane() { + return satelliteVisualizationViewerScrollPane; + } + + /** + * Displays the edges of graph used to draw the edge + */ + public void drawEdgeLabels() { + this.setEdgeStringer(this); + this.setEdgePaintFunction(new PickableEdgePaintFunction(this, + Color.black, Color.cyan)); + visualizationViewer.repaint(); + } + + /** + * Displays the labels of nodes + */ + public void drawNodeLabels() { + + this.setVertexStringer(this); + visualizationViewer.repaint(); + } + + public Vertex[] get_vertexArray() { + return _vertexArray; + } + + /** + * Implemented for VertexPaintFunction Returns the color of the outline of + * the vertex Draw paint color is defaulted BLACK + */ + public Paint getDrawPaint(Vertex v) { + Color fillColor = graphSettings.getNodeColor(v); + ConstantVertexPaintFunction cvpf = new ConstantVertexPaintFunction( + Color.BLACK, fillColor); + return cvpf.getDrawPaint(v); + } + + /** + * (non-Javadoc) + * + * @see edu.uci.ics.jung.graph.decorators.EdgePaintFunction#getDrawPaint(edu.uci.ics.jung.graph.Edge) + */ + public Paint getDrawPaint(Edge e) { + EdgeProperty edgeProperty = edgeMap.get(e); + Paint returnPaint = null; + ConstantEdgePaintFunction edgePaintFunction; + if (edgeProperty != null) { + // System.out.println("Edge color from map"); + edgePaintFunction = new ConstantEdgePaintFunction(edgeProperty + .getColor(), null); + returnPaint = edgePaintFunction.getDrawPaint(e); + return returnPaint; + } + edgePaintFunction = new ConstantEdgePaintFunction(Color.BLACK, null); + returnPaint = edgePaintFunction.getDrawPaint(e); + return returnPaint; + } + + /* + * (non-Javadoc) + * + * @see edu.uci.ics.jung.graph.decorators.EdgePaintFunction#getFillPaint(edu.uci.ics.jung.graph.Edge) + */ + public Paint getFillPaint(Edge e) { + EdgeProperty edgeProperty = edgeMap.get(e); + Paint returnPaint = null; + ConstantEdgePaintFunction edgePaintFunction; + if (edgeProperty != null) { + edgePaintFunction = new ConstantEdgePaintFunction(edgeProperty + .getColor(), null); + returnPaint = edgePaintFunction.getFillPaint(e); + return returnPaint; + } + edgePaintFunction = new ConstantEdgePaintFunction(Color.BLACK, null); + returnPaint = edgePaintFunction.getFillPaint(e); + return returnPaint; + } + + /* + * (non-Javadoc) + * + * @see edu.uci.ics.jung.graph.decorators.EdgeStrokeFunction#getStroke(edu.uci.ics.jung.graph.Edge) + */ + public Stroke getStroke(Edge e) { + EdgeProperty edgeProperty = edgeMap.get(e); + ConstantEdgeStrokeFunction edgeStrokeFunction; + if (edgeProperty != null) { + edgeStrokeFunction = new ConstantEdgeStrokeFunction( + (float) edgeProperty.getSize()); + return edgeStrokeFunction.getStroke(e); + } + edgeStrokeFunction = new ConstantEdgeStrokeFunction(1.0f); + return edgeStrokeFunction.getStroke(e); + } + + /** + * Implemented for VertexPaintFunction Returns the color with which the + * vertex needs to be filled The color is determined by map entry + */ + public Paint getFillPaint(Vertex v) { + Color fillColor = graphSettings.getNodeColor(v); + ConstantVertexPaintFunction cvpf = new ConstantVertexPaintFunction( + Color.BLACK, fillColor); + + return cvpf.getFillPaint(v); + } + + public java.util.List getGQuestions() { + return gQuestions; + } + + public static Graph getGraph() { + return graph; + } + + public DefaultModalGraphMouse getGraphMouse() { + return graphMouse; + } + + public JComponent getGzsp() { + return visualizationViewerScrollPane; + } + + /** + * Implementes for EdgeStringer Retruns the edgeLabel for a given edge + */ + public String getLabel(ArchetypeEdge e) { + + return edgeLabelMap.get((UndirectedSparseEdge) e); + } + + /** + * Implemented for VertexShapeFunction Returns shape of vertex by looking + * for an entry in map + */ + public Shape getShape(Vertex v) { + NodeProperty.NodeShape shape = graphSettings.getNodeShape(v); + int size = graphSettings.getNodeSize(v); + EllipseVertexShapeFunction basicCircle = new EllipseVertexShapeFunction(); + Shape returnShape = basicCircle.getShape(v, 10 + (5 * size)); + switch (shape) { + case Circle: + EllipseVertexShapeFunction circle = new EllipseVertexShapeFunction(); + returnShape = circle.getShape(v, 10 + (5 * size)); + break; + case Square: + PolygonVertexShapeFunction square = new PolygonVertexShapeFunction(); + returnShape = square.getShape(v, 10 + (5 * size), 4); + break; + case Pentagon: + PolygonVertexShapeFunction pentagon = new PolygonVertexShapeFunction(); + returnShape = pentagon.getShape(v, 10 + (5 * size), 5); + break; + case Hexagon: + PolygonVertexShapeFunction hexagon = new PolygonVertexShapeFunction(); + returnShape = hexagon.getShape(v, 10 + (5 * size), 6); + break; + case Triangle: + PolygonVertexShapeFunction triangle = new PolygonVertexShapeFunction(); + returnShape = triangle.getShape(v, 10 + (5 * size), 3); + break; + case Star: + EllipseVertexShapeFunction star = new EllipseVertexShapeFunction(); + returnShape = star.getShape(v, NodeProperty.NodeShape.Star, + 10 + (5 * size)); + break; + case RoundedRectangle: + EllipseVertexShapeFunction roundRect = new EllipseVertexShapeFunction(); + returnShape = roundRect.getShape(v, + NodeProperty.NodeShape.RoundedRectangle, 10 + (5 * size)); + break; + } + return returnShape; + } + + public VisualizationModel getVisualizationModel() { + return visualizationModel; + } + + public static VisualizationViewer getVv() { + return visualizationViewer; + } + + /** + * Hides edge labels + */ + public void hideEdgeLabels() { + EdgeStringer stringer = new EdgeStringer() { + public String getLabel(ArchetypeEdge e) { + return ""; + } + }; + this.setEdgeStringer(stringer); + /* + * this.setEdgePaintFunction(new PickableEdgePaintFunction(this, + * Color.black, Color.black)); + */ + visualizationViewer.repaint(); + + } + + /** + * Hides the labels of nodes + */ + public void hideNodeLabels() { + VertexStringer vertexStringer = new VertexStringer() { + public String getLabel(ArchetypeVertex v) { + return null; + } + }; + this.setVertexStringer(vertexStringer); + visualizationViewer.repaint(); + } + + /* + * (non-Javadoc) + * + * @see edu.uci.ics.jung.graph.decorators.EdgeShapeFunction#getShape(edu.uci.ics.jung.graph.Edge) + */ + public Shape getShape(Edge e) { + + EdgeProperty edgeProperty = edgeMap.get(e); + Shape returnShape = null; + EdgeShapeFunction edgeShapeFunction; + if (edgeProperty != null) { + switch (edgeProperty.getShape()) { + case Line: + edgeShapeFunction = new EdgeShape.Line(); + returnShape = edgeShapeFunction.getShape(e); + break; + case QuadCurve: + edgeShapeFunction = new EdgeShape.QuadCurve(); + returnShape = edgeShapeFunction.getShape(e); + break; + case CubicCurve: + edgeShapeFunction = new EdgeShape.CubicCurve(); + returnShape = edgeShapeFunction.getShape(e); + break; + } + return returnShape; + } + edgeShapeFunction = new EdgeShape.Line(); + returnShape = edgeShapeFunction.getShape(e); + return returnShape; + } + + /** + * Resizes edges by adjusting thickness + */ + public void reSizeEdges(float strokeWeight) { + this + .setEdgeStrokeFunction(new ConstantEdgeStrokeFunction( + strokeWeight)); + visualizationViewer.repaint(); + + } + + public List getEdgeList() { + return edgeList; + } + + public void setEdgeList(List edgeList) { + this.edgeList = edgeList; + } + + public void updateGraphSettings() { + Iterator iterator = graphSettings.getQAsettingsIterator(); + while (iterator.hasNext()) { + GraphSettingsEntry entry = (GraphSettingsEntry) iterator.next(); + GraphQuestion graphQuestion = entry.getGraphQuestion(); + if (graphQuestion.getCategory() == Question.ALTER_QUESTION) { + NodeProperty nodeProperty = entry.getNodeProperty(); + NodeProperty.Property prop = nodeProperty.getProperty(); + Question question = graphQuestion.getQuestion(); + Selection selection = graphQuestion.getSelection(); + GraphData graphData = new GraphData(); + List alterList = graphData.getAlterNumbers(question, + selection); + + switch (prop) { + case Color: + for (int alter : alterList) { + graphSettings.setNodeColor(_vertexArray[alter], + nodeProperty.getColor()); + } + break; + case Shape: + for (int alter : alterList) { + graphSettings.setNodeShape(_vertexArray[alter], + nodeProperty.getShape()); + } + break; + case Size: + for (int alter : alterList) { + graphSettings.setNodeSize(_vertexArray[alter], + nodeProperty.getSize()); + } + break; + case Label: + for (int alter : alterList) { + graphSettings.setNodeLabel(_vertexArray[alter], + nodeProperty.getLabel()); + } + break; + } + } else if (graphQuestion.getCategory() == Question.CATEGORICAL) // structural + // measure + { + NodeProperty nodeProperty = entry.getNodeProperty(); + NodeProperty.Property prop = nodeProperty.getProperty(); + if (graphQuestion.getSelection().getString() == "DegreeCentrality") { + switch (prop) { + case Color: + applyDegreeCentrality(NodeProperty.Property.Color); + break; + case Size: + applyDegreeCentrality(NodeProperty.Property.Size); + break; + } + } else { // Degree centrality + switch (prop) { + case Color: + applyBetweennessCentrality(NodeProperty.Property.Color); + break; + case Size: + applyBetweennessCentrality(NodeProperty.Property.Size); + break; + } + } + } + } + visualizationViewer.repaint(); + } + + private float max(float[] array) { + float max = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] > max) { + max = array[i]; + } + } + return max; + } + + private void applyDegreeCentrality(NodeProperty.Property property) { + + float[] degreeCentrality = new float[EgoClient.interview.getNumAlters()]; + float[] scaledDegreeCentrality = new float[EgoClient.interview + .getNumAlters()]; + for (int i = 0; i < EgoClient.interview.getNumAlters(); i++) { + degreeCentrality[i] = new Float( + EgoClient.interview.getStats().degreeArray[i] + / ((float) (EgoClient.interview.getStats().proximityMatrix.length - 1))); + } + // scale the values + float maximum = max(degreeCentrality); + for (int i = 0; i < degreeCentrality.length; i++) { + scaledDegreeCentrality[i] = (1 / maximum) * degreeCentrality[i]; + } + + for (int i = 0; i < scaledDegreeCentrality.length; i++) { + float grayPercentage = 1 - scaledDegreeCentrality[i]; + if (property == NodeProperty.Property.Color) { + Color nodeColor = new Color(grayPercentage, grayPercentage, + grayPercentage); + graphSettings.setNodeColor(_vertexArray[i], nodeColor); + } else if (property == NodeProperty.Property.Size) { + int size = Math.round(1 + 2 * scaledDegreeCentrality[i]); + graphSettings.setNodeSize(_vertexArray[i], size); + } + } + } + + private void applyBetweennessCentrality(NodeProperty.Property property) { + + float[] betweennessCentrality = new float[EgoClient.interview + .getNumAlters()]; + float[] scaledBetweennessCentrality = new float[EgoClient.interview + .getNumAlters()]; + for (int i = 0; i < EgoClient.interview.getNumAlters(); i++) { + double big = EgoClient.interview.getStats().proximityMatrix.length - 1; + big *= big; + betweennessCentrality[i] = new Float( + EgoClient.interview.getStats().betweennessArray[i] / big); + } + // scale the values + float maximum = max(betweennessCentrality); + for (int i = 0; i < betweennessCentrality.length; i++) { + scaledBetweennessCentrality[i] = (1 / maximum) + * betweennessCentrality[i]; + } + + for (int i = 0; i < scaledBetweennessCentrality.length; i++) { + float grayPercentage = 1 - scaledBetweennessCentrality[i]; + if (property == NodeProperty.Property.Color) { + Color nodeColor = new Color(grayPercentage, grayPercentage, + grayPercentage); + graphSettings.setNodeColor(_vertexArray[i], nodeColor); + } else if (property == NodeProperty.Property.Size) { + int size = Math.round(1 + 2 * scaledBetweennessCentrality[i]); + graphSettings.setNodeSize(_vertexArray[i], size); + } + } + } + + public void updateGraphSettings(Object updateValue, int nodeIndex, + int updateParam) { + // update param + // 1: Label + // 2: Color + // 3: Shape + // 4: Size + switch (updateParam) { + case 1: + graphSettings.setNodeLabel(_vertexArray[nodeIndex], + (String) updateValue); + break; + case 2: + graphSettings.setNodeColor(_vertexArray[nodeIndex], + (Color) updateValue); + break; + case 3: + graphSettings.setNodeShape(_vertexArray[nodeIndex], + (NodeProperty.NodeShape) updateValue); + break; + case 4: + graphSettings.setNodeSize(_vertexArray[nodeIndex], Integer + .parseInt((String) updateValue)); + break; + + } + } + + public void addQAsettings(GraphQuestion graphQuestion, + NodeProperty nodeProperty) { + graphSettings.addQAsetting(graphQuestion, nodeProperty); + } + + public String getToolTipText(Vertex v) { + String text = graphSettings.getNodeToolTipText(v); + // System.out.println(text); + return text; + } + + public String getToolTipText(Edge e) { + return e.toString(); + } + + public String getToolTipText(MouseEvent event) { + return ((JComponent) event.getSource()).getToolTipText(); + } + +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/GraphSettings.java b/src/com/endlessloopsoftware/ego/client/graph/GraphSettings.java new file mode 100644 index 0000000..f842688 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/GraphSettings.java @@ -0,0 +1,139 @@ +package com.endlessloopsoftware.ego.client.graph; + +import edu.uci.ics.jung.graph.ArchetypeVertex; +import java.util.*; +import java.util.List; +import java.awt.*; +import java.io.*; + +import com.endlessloopsoftware.ego.Question; +import com.endlessloopsoftware.ego.Answer; +import org.egonet.util.listbuilder.Selection; + +import com.endlessloopsoftware.ego.client.*; +import com.endlessloopsoftware.ego.client.graph.NodeProperty.NodeShape; + +public class GraphSettings { + + private Map propertySettingsMap = Collections + .synchronizedMap(new HashMap()); + + private java.util.List QAsettings = Collections + .synchronizedList(new ArrayList()); + + GraphRenderer renderer; + + public GraphSettings(GraphRenderer renderer) { + this.renderer = renderer; + init(); + } + + private void init() { + int noOfAlters = EgoClient.interview.getNumAlters(); + for (int i = 0; i < noOfAlters; i++) { + String alterName = EgoClient.interview.getAlterList()[i]; + Color color = Color.RED; + int size = 1; + NodeShape shape = NodeShape.Circle; + NodeProperty nodeProperty = new NodeProperty(alterName, color, + shape, size); + String toolTipText = getAlterInfo(i); + nodeProperty.setToolTipText(toolTipText); + propertySettingsMap + .put(renderer.get_vertexArray()[i], nodeProperty); + + } + } + + public int getNodeSize(ArchetypeVertex node) { + NodeProperty nodeProperty = propertySettingsMap.get(node); + return nodeProperty.getSize(); + } + + public void setNodeSize(ArchetypeVertex node, int nodeSize) { + propertySettingsMap.get(node).setSize(nodeSize); + } + + public NodeShape getNodeShape(ArchetypeVertex node) { + NodeProperty nodeProperty = propertySettingsMap.get(node); + return nodeProperty.getShape(); + } + + public void setNodeShape(ArchetypeVertex node, NodeShape nodeShape) { + propertySettingsMap.get(node).setShape(nodeShape); + } + + public Color getNodeColor(ArchetypeVertex node) { + NodeProperty nodeProperty = propertySettingsMap.get(node); + return nodeProperty.getColor(); + } + + public void setNodeColor(ArchetypeVertex node, Color nodeColor) { + propertySettingsMap.get(node).setColor(nodeColor); + } + + public String getNodeLabel(ArchetypeVertex node) { + NodeProperty nodeProperty = propertySettingsMap.get(node); + return nodeProperty.getLabel(); + } + + public void setNodeLabel(ArchetypeVertex node, String nodeLabel) { + propertySettingsMap.get(node).setLabel(nodeLabel); + } + + public String getNodeToolTipText(ArchetypeVertex node) { + NodeProperty nodeProperty = propertySettingsMap.get(node); + return nodeProperty.getToolTipText(); + } + + public void addQAsetting(GraphQuestion graphQuestion, + NodeProperty nodeProperty) { + GraphSettingsEntry entry = new GraphSettingsEntry(graphQuestion, + nodeProperty); + QAsettings.add(entry); + displaySettings(); + } + + private void displaySettings() { + + System.out + .println("---------------QA SETTINGS CONTENTS ----------------------------"); + int size = QAsettings.size(); + for (int i = 0; i < size; i++) { + GraphSettingsEntry entry = QAsettings.get(i); + System.out.println(entry.toString()); + } + System.out + .println("\n-------------------------------------------------------"); + } + + public Iterator getQAsettingsIterator() { + return QAsettings.iterator(); + } + + private String getAlterInfo(int alterIndex) { + String[] alterToolTip = new String[EgoClient.interview.getNumAlters()]; + for (int i = 0; i < alterToolTip.length; i++) { + alterToolTip[i] = "" + EgoClient.interview.getAlterList()[i] + "
"; + } + Answer[] answers = EgoClient.interview.get_answers(); + for (Answer answer : answers) { + String questionTitle = ""; + String answerString = ""; + Question question = EgoClient.study.getQuestion(answer.questionId); + if (question.questionType == Question.ALTER_QUESTION) { + // System.out.println(answer.getString()); + questionTitle = question.title; + answerString = answer.string; + int[] alters = answer.getAlters(); + for (int alter : alters) { + alterToolTip[alter] += questionTitle + " : " + answerString + + "
"; + } + } + + } + return alterToolTip[alterIndex]; + + } +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/GraphSettingsEntry.java b/src/com/endlessloopsoftware/ego/client/graph/GraphSettingsEntry.java new file mode 100644 index 0000000..ef20a8d --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/GraphSettingsEntry.java @@ -0,0 +1,26 @@ +package com.endlessloopsoftware.ego.client.graph; + +public class GraphSettingsEntry { + + GraphQuestion graphQuestion = null; + NodeProperty nodeProperty = null; + + public GraphSettingsEntry (GraphQuestion gq, NodeProperty np) { + this.graphQuestion = gq; + this.nodeProperty = np; + } + + public GraphQuestion getGraphQuestion() { + return graphQuestion; + } + + public NodeProperty getNodeProperty() { + return nodeProperty; + } + + public String toString() { + String string = ""; + string = graphQuestion.toString() + "||" + nodeProperty.toString(); + return string; + } +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/GraphTabPanel.java b/src/com/endlessloopsoftware/ego/client/graph/GraphTabPanel.java new file mode 100644 index 0000000..47de9a2 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/GraphTabPanel.java @@ -0,0 +1,203 @@ +package com.endlessloopsoftware.ego.client.graph; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import javax.swing.*; + +import java.util.*; + +import edu.uci.ics.jung.graph.Graph; +import edu.uci.ics.jung.visualization.FRLayout; +import edu.uci.ics.jung.visualization.ISOMLayout; +import edu.uci.ics.jung.visualization.contrib.KKLayout; +import edu.uci.ics.jung.visualization.SpringLayout; +import edu.uci.ics.jung.visualization.VisualizationViewer; +import edu.uci.ics.jung.visualization.contrib.CircleLayout; +import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; + +public class GraphTabPanel extends JPanel { + private List layoutOptions; + private DefaultModalGraphMouse graphMouse; + + private VisualizationViewer vv; + + private GraphRenderer graphRenderer; + + private GroupLayout layout; + + private Graph g; + public GraphTabPanel(Graph g, VisualizationViewer vv, + DefaultModalGraphMouse graphMouse) { + + this.g= g; + this.graphMouse = graphMouse; + this.vv = vv; + createComponents(); + } + public GraphTabPanel(GraphRenderer gr) + { + this.graphRenderer = gr; + this.g = gr.getGraph(); + this.graphMouse = gr.getGraphMouse(); + this.vv= gr.getVv(); + createComponents(); + } + + private void createComponents() { + + layout = new GroupLayout(this); + this.setLayout(layout); + + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + + // label combo pair for choosing different layouts + JLabel layoutLabel = new JLabel("Choose Layout"); + layoutLabel.setOpaque(true); + + //add all possible layouts to the layout combo + layoutOptions = new ArrayList(); + layoutOptions.add(KKLayout.class); + layoutOptions.add(FRLayout.class); + layoutOptions.add(CircleLayout.class); + //layoutOptions.add(SpringLayout.class); + layoutOptions.add(ISOMLayout.class); + Class[] layoutList = (Class[]) layoutOptions.toArray(new Class[0]); + + JComboBox layoutCombo = new JComboBox(layoutList); + + //use a renderer to shorten the layout name presentation + layoutCombo.setRenderer(new DefaultListCellRenderer() { + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + String valueString = value.toString(); + valueString = valueString.substring(valueString.lastIndexOf('.')+1); + return super.getListCellRendererComponent(list, valueString, index, isSelected, cellHasFocus); + } + }); + + layoutCombo.setPreferredSize(new Dimension(20,20)); + layoutCombo.setMaximumSize(new Dimension(20,30)); + layoutCombo.setSelectedItem(FRLayout.class); + layoutCombo.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + //call renderer to render the graph with selected layout + JComboBox cb = (JComboBox) e.getSource(); + Class layoutC = (Class) cb.getSelectedItem(); + Class lay = layoutC; + graphRenderer.changeLayout(lay); + } + }); + + // label combo pair to choose mode of operation + JLabel modeLabel = new JLabel("Choose Mode"); + modeLabel.setOpaque(true); + + JComboBox modeCombo = graphMouse.getModeComboBox(); + modeCombo.setPreferredSize(new Dimension(20,20)); + modeCombo.setMaximumSize(new Dimension(20,30)); + modeCombo.addItemListener(((DefaultModalGraphMouse) vv.getGraphMouse()) + .getModeListener()); + + // add label button pair for changing background color + JLabel bgcolorLabel = new JLabel("BackGround"); + bgcolorLabel.setOpaque(true); + + JButton bgcolorButton = new JButton(); + bgcolorButton.setText(" "); + bgcolorButton.setBackground(vv.getBackground()); + Color chosenColor; + bgcolorButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + // code displaying a color box/palette to choose from + Color c = JColorChooser.showDialog(null, "Choose color", vv + .getBackground()); + if (c != null) { + vv.setBackground(c); + JButton colorButton = (JButton) e.getSource(); + colorButton.setBackground(c); + } + + } + }); + + // create check boxes for showing labels and Edge weights + JCheckBox showLabelChkBox = new JCheckBox("Show Node Labels"); + showLabelChkBox.setSelected(true); + showLabelChkBox.addItemListener(new ItemListener(){ + public void itemStateChanged(ItemEvent e) { + if(e.getStateChange() == ItemEvent.SELECTED) + graphRenderer.drawNodeLabels(); + else + graphRenderer.hideNodeLabels(); + + } + }); + + JCheckBox showWeightChkBox = new JCheckBox("Show Edge Weights"); + showWeightChkBox.setSelected(false); + showWeightChkBox.addItemListener(new ItemListener(){ + public void itemStateChanged(ItemEvent e) { + if(e.getStateChange() == ItemEvent.SELECTED) + { + GraphRenderer.showEdgeWeights= true; + graphRenderer.drawEdgeLabels(); + + } + else + graphRenderer.hideEdgeLabels(); + + } + }); + + + + // disply in the panel using GroupLayout + GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup(); + + // The sequential group in turn contains two parallel groups. + // One parallel group contains the labels, the other the text fields. + // Putting the labels in a parallel group along the horizontal axis + // positions them at the same x location. + // + // Variable indentation is used to reinforce the level of grouping. + hGroup.addGroup(layout.createParallelGroup().addComponent(layoutLabel) + .addComponent(modeLabel).addComponent(bgcolorLabel) + .addComponent(showLabelChkBox).addComponent(showWeightChkBox)); + + hGroup.addGroup(layout.createParallelGroup().addComponent(layoutCombo) + .addComponent(modeCombo).addComponent(bgcolorButton)); + + layout.setHorizontalGroup(hGroup); + + // Create a sequential group for the vertical axis. + GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup(); + + // The sequential group contains two parallel groups that align + // the contents along the baseline. The first parallel group contains + // the first label and text field, and the second parallel group + // contains + // the second label and text field. By using a sequential group + // the labels and text fields are positioned vertically after one + // another. + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(layoutLabel) + .addComponent(layoutCombo)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(modeLabel) + .addComponent(modeCombo)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(bgcolorLabel) + .addComponent(bgcolorButton)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(showLabelChkBox)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(showWeightChkBox)); + layout.setVerticalGroup(vGroup); + } + +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/ImageLabel.java b/src/com/endlessloopsoftware/ego/client/graph/ImageLabel.java new file mode 100644 index 0000000..98501fb --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/ImageLabel.java @@ -0,0 +1,544 @@ +package com.endlessloopsoftware.ego.client.graph; +// +import java.awt.Canvas; +//import java.net.*; +// +//// This appears in Core Web Programming from +//// Prentice Hall Publishers, and may be freely used +//// or adapted. 1997 Marty Hall, hall@apl.jhu.edu. +// +////====================================================== +///** +// * A class for displaying images. It places the Image into a canvas so that it can moved around by layout managers, will get repainted automatically, etc. No +// * mouseXXX or action events are defined, so it is most similar to the Label Component. +// *

+// * By default, with FlowLayout the ImageLabel takes its minimum size (just enclosing the image). The default with BorderLayout is to expand to fill the region +// * in width (North/South), height (East/West) or both (Center). This is the same behavior as with the builtin Label class. If you give an explicit resize or +// * reshape call before adding the ImageLabel to the Container, this size will override the defaults. +// *

+// * Here is an example of its use: +// *

+// * +// *

+// * 
+// * public class ShowImages extends Applet { private ImageLabel image1, image2;
+// * 
+// * public void init() { image1 = new ImageLabel(getCodeBase(), "some-image.gif"); image2 = new ImageLabel(getCodeBase(), "other-image.jpg"); add(image1);
+// * add(image2); } }
+// * 
+// * 
+// * +// * @author Marty Hall (hall@apl.jhu.edu) +// * @see Icon +// * @see ImageButton +// * @version 1.0 (1997) +// */ +// +public class ImageLabel extends Canvas +{ +// //---------------------------------------------------- +// // Instance variables. +// +// // The actual Image drawn on the canvas. +// private Image image; +// +// // A String corresponding to the URL of the image +// // you will get if you call the constructor with +// // no arguments. +// private static String defaultImageString = "http://www.endlessloopsoftware.com/eLoop_web.png"; +// +// // The URL of the image. But sometimes we will use +// // an existing image object (e.g. made by +// // createImage) for which this info will not be +// // available, so a default string is used here. +// private String imageString = ""; +// +// // Turn this on to get verbose debugging messages. +// private boolean debug = false; +// +// /** Amount of extra space around the image. */ +// private int border = 0; +// +// /** +// * If there is a non-zero border, what color should it be? Default is to use the background color of the Container. +// */ +// private Color borderColor = null; +// +// // Width and height of the Canvas. This is the +// // width/height of the image plus twice the border. +// private int width, height; +// +// /** +// * Determines if it will be sized automatically. If the user issues a resize() or reshape() call before adding the label to the Container, or if the +// * LayoutManager resizes before drawing (as with BorderLayout), then those sizes override the default, which is to make the label the same size as the image +// * it holds (after reserving space for the border, if any). This flag notes this, so subclasses that override ImageLabel need to check this flag, and if it +// * is true, and they draw modified image, then they need to draw them based on the width height variables, not just blindly drawing them full size. +// */ +// private boolean explicitSize = false; +// private int explicitWidth = 0, explicitHeight = 0; +// +// // The MediaTracker that can tell if image has been +// // loaded before trying to paint it or resize +// // based on its size. +// private MediaTracker tracker; +// +// // Used by MediaTracker to be sure image is loaded +// // before paint & resize, since you can't find out +// // the size until it is done loading. +// private static int lastTrackerID = 0; +// private int currentTrackerID; +// private boolean doneLoading = false; +// +// private Container parentContainer; +// +// //---------------------------------------------------- +// /** +// * Create an ImageLabel with the default image. +// * +// * @see #getDefaultImageString +// * @see #setDefaultImageString +// */ +// // Remember that the funny "this()" syntax calls +// // constructor of same class +// public ImageLabel() +// { +// this(defaultImageString); +// } +// +// /** +// * Create an ImageLabel using the image at URL specified by the string. +// * +// * @param imageURLString +// * A String specifying the URL of the image. +// */ +// public ImageLabel(String imageURLString) +// { +// this(makeURL(imageURLString)); +// } +// +// /** +// * Create an ImageLabel using the image at URL specified. +// * +// * @param imageURL +// * The URL of the image. +// */ +// public ImageLabel(URL imageURL) +// { +// this(loadImage(imageURL)); +// imageString = imageURL.toExternalForm(); +// } +// +// /** +// * Create an ImageLabel using the image in the file in the specified directory. +// * +// * @param imageDirectory +// * Directory containing image +// * @param file +// * Filename of image +// */ +// public ImageLabel(URL imageDirectory, String file) +// { +// this(makeURL(imageDirectory, file)); +// imageString = file; +// } +// +// /** +// * Create an ImageLabel using the image specified. The other constructors eventually call this one, but you may want to call it directly if you already have +// * an image (e.g. created via createImage). +// * +// * @param image +// * The image +// */ +// public ImageLabel(Image image) +// { +// this.image = image; +// tracker = new MediaTracker(this); +// currentTrackerID = lastTrackerID++; +// tracker.addImage(image, currentTrackerID); +// } +// +// //---------------------------------------------------- +// /** +// * Makes sure that the Image associated with the Canvas is done loading before returning, since loadImage spins off a separate thread to do the loading. +// * Once you get around to drawing the image, this will make sure it is loaded, waiting if not. The user does not need to call this at all, but if several +// * ImageLabels are used in the same Container, this can cause several repeated layouts, so users might want to explicitly call this themselves before adding +// * the ImageLabel to the Container. Another alternative is to start asynchronous loading by calling prepareImage on the ImageLabel's image (see getImage). +// * +// * @param doLayout +// * Determines if the Container should be re-laid out after you are finished waiting. This should be true when called from user functions, +// * but is set to false when called from preferredSize to avoid an infinite loop. This is needed when using BorderLayout, which calls preferredSize +// * before calling paint. +// */ +// public void waitForImage(boolean doLayout) +// { +// if (!doneLoading) +// { +// debug("[waitForImage] - Resizing and waiting for " + imageString); +// try +// { +// tracker.waitForID(currentTrackerID); +// } +// catch (InterruptedException ie) +// { +// } +// catch (Exception e) +// { +// System.out.println("Error loading " + imageString + ": " + e.getMessage()); +// e.printStackTrace(); +// } +// if (tracker.isErrorID(0)) +// new Throwable("Error loading image " + imageString).printStackTrace(); +// doneLoading = true; +// if (explicitWidth != 0) +// width = explicitWidth; +// else +// width = image.getWidth(this) + 2 * border; +// if (explicitHeight != 0) +// height = explicitHeight; +// else +// height = image.getHeight(this) + 2 * border; +// resize(width, height); +// debug("[waitForImage] - " + imageString + " is " + width + "x" + height + "."); +// +// // If no parent, you are OK, since it will have +// // been resized before being added. But if +// // parent exists, you have already been added, +// // and the change in size requires re-layout. +// if (((parentContainer = getParent()) != null) && doLayout) +// { +// setBackground(parentContainer.getBackground()); +// parentContainer.layout(); +// } +// } +// } +// +// //---------------------------------------------------- +// /** +// * Moves the image so that it is centered at the specified location, as opposed to the move method of Component which places the top left corner at +// * the specified location. +// *

+// * Note: The effects of this could be undone by the LayoutManager of the parent Container, if it is using one. So this is normally only used in +// * conjunction with a null LayoutManager. +// * +// * @param x +// * The X coord of center of the image (in parent's coordinate system) +// * @param y +// * The Y coord of center of the image (in parent's coordinate system) +// * @see java.awt.Component#move +// */ +// +// public void centerAt(int x, int y) +// { +// debug("Placing center of " + imageString + " at (" + x + "," + y + ")"); +// move(x - width / 2, y - height / 2); +// } +// +// //---------------------------------------------------- +// /** +// * Determines if the x and y (in the ImageLabel's own coordinate system) is inside the ImageLabel. Put here because Netscape 2.02 has a bug in +// * which it doesn't process inside() and locate() tests correctly. +// */ +// public synchronized boolean inside(int x, int y) +// { +// return ((x >= 0) && (x <= width) && (y >= 0) && (y <= height)); +// } +// +// //---------------------------------------------------- +// /** +// * Draws the image. If you override this in a subclass, be sure to call super.paint. +// */ +// public void paint(Graphics g) +// { +// if (!doneLoading) +// waitForImage(true); +// else +// { +// if (explicitSize) +// g.drawImage(image, border, border, width - 2 * border, height - 2 * border, this); +// else +// g.drawImage(image, border, border, this); +// drawRect(g, 0, 0, width - 1, height - 1, border, borderColor); +// } +// } +// +// //---------------------------------------------------- +// /** +// * Used by layout managers to calculate the usual size allocated for the Component. Since some layout managers (e.g. BorderLayout) may call this before +// * paint is called, you need to make sure that the image is done loading, which will force a resize, which determines the values returned. +// */ +// public Dimension preferredSize() +// { +// if (!doneLoading) +// waitForImage(false); +// return (super.preferredSize()); +// } +// +// //---------------------------------------------------- +// /** +// * Used by layout managers to calculate the smallest size allocated for the Component. Since some layout managers (e.g. BorderLayout) may call this before +// * paint is called, you need to make sure that the image is done loading, which will force a resize, which determines the values returned. +// */ +// public Dimension minimumSize() +// { +// if (!doneLoading) +// waitForImage(false); +// return (super.minimumSize()); +// } +// +// //---------------------------------------------------- +// // LayoutManagers (such as BorderLayout) might call +// // resize or reshape with only 1 dimension of +// // width/height non-zero. In such a case, you still +// // want the other dimension to come from the image +// // itself. +// +// /** +// * Resizes the ImageLabel. If you don't resize the label explicitly, then what happens depends on the layout manager. With FlowLayout, as with FlowLayout +// * for Labels, the ImageLabel takes its minimum size, just enclosing the image. With BorderLayout, as with BorderLayout for Labels, the ImageLabel is +// * expanded to fill the section. Stretching GIF/JPG files does not always result in clear looking images. So just as with builtin Labels and Buttons, +// * don't use FlowLayout if you don't want the Buttons to get resized. If you don't use any LayoutManager, then the ImageLabel will also just fit the +// * image. +// *

+// * Note that if you resize explicitly, you must do it before the ImageLabel is added to the Container. In such a case, the explicit size overrides +// * the image dimensions. +// * +// * @see #reshape +// */ +// public void resize(int width, int height) +// { +// if (!doneLoading) +// { +// explicitSize = true; +// if (width > 0) +// explicitWidth = width; +// if (height > 0) +// explicitHeight = height; +// } +// super.resize(width, height); +// } +// +// /** +// * Resizes the ImageLabel. If you don't resize the label explicitly, then what happens depends on the layout manager. With FlowLayout, as with FlowLayout +// * for Labels, the ImageLabel takes its minimum size, just enclosing the image. With BorderLayout, as with BorderLayout for Labels, the ImageLabel is +// * expanded to fill the section. Stretching GIF/JPG files does not always result in clear looking images. So just as with builtin Labels and Buttons, +// * don't use FlowLayout if you don't want the Buttons to get resized. If you don't use any LayoutManager, then the ImageLabel will also just fit the +// * image. +// *

+// * Note that if you resize explicitly, you must do it before the ImageLabel is added to the Container. In such a case, the explicit size overrides +// * the image dimensions. +// * +// * @see #resize +// */ +// public void reshape(int x, int y, int width, int height) +// { +// if (!doneLoading) +// { +// explicitSize = true; +// if (width > 0) +// explicitWidth = width; +// if (height > 0) +// explicitHeight = height; +// } +// super.reshape(x, y, width, height); +// } +// +// //---------------------------------------------------- +// // You can't just set the background color to +// // the borderColor and skip drawing the border, +// // since it messes up transparent gifs. You +// // need the background color to be the same as +// // the container. +// +// /** +// * Draws a rectangle with the specified OUTSIDE left, top, width, and height. Used to draw the border. +// */ +// protected void drawRect( +// Graphics g, +// int left, +// int top, +// int width, +// int height, +// int lineThickness, +// Color rectangleColor) +// { +// g.setColor(rectangleColor); +// for (int i = 0; i < lineThickness; i++) +// { +// g.drawRect(left, top, width, height); +// if (i < lineThickness - 1) +// { // Skip last iteration +// left = left + 1; +// top = top + 1; +// width = width - 2; +// height = height - 2; +// } +// } +// } +// +// //---------------------------------------------------- +// /** +// * Calls System.out.println if the debug variable is true; does nothing otherwise. +// * +// * @param message +// * The String to be printed. +// */ +// protected void debug(String message) +// { +// if (debug) +// System.out.println(message); +// } +// +// //---------------------------------------------------- +// // Creates the URL with some error checking. +// +// private static URL makeURL(String s) +// { +// URL u = null; +// try +// { +// u = new URL(s); +// } +// catch (MalformedURLException mue) +// { +// System.out.println("Bad URL " + s + ": " + mue); +// mue.printStackTrace(); +// } +// return (u); +// } +// +// private static URL makeURL(URL directory, String file) +// { +// URL u = null; +// try +// { +// u = new URL(directory, file); +// } +// catch (MalformedURLException mue) +// { +// System.out.println("Bad URL " + directory.toExternalForm() + ", " + file + ": " + mue); +// mue.printStackTrace(); +// } +// return (u); +// } +// +// //---------------------------------------------------- +// // Loads the image. Needs to be static since it is +// // called by the constructor. +// +// private static Image loadImage(URL url) +// { +// return (Toolkit.getDefaultToolkit().getImage(url)); +// } +// +// //---------------------------------------------------- +// /** The Image associated with the ImageLabel. */ +// +// public Image getImage() +// { +// return (image); +// } +// +// //---------------------------------------------------- +// /** Gets the border width. */ +// +// public int getBorder() +// { +// return (border); +// } +// +// /** Sets the border thickness. */ +// +// public void setBorder(int border) +// { +// this.border = border; +// } +// +// //---------------------------------------------------- +// /** Gets the border color. */ +// +// public Color getBorderColor() +// { +// return (borderColor); +// } +// +// /** Sets the border color. */ +// +// public void setBorderColor(Color borderColor) +// { +// this.borderColor = borderColor; +// } +// +// //---------------------------------------------------- +// // You could just call size().width and size().height, +// // but since we've overridden resize to record +// // this, we might as well use it. +// +// /** Gets the width (image width plus twice border). */ +// +// public int getWidth() +// { +// return (width); +// } +// +// /** Gets the height (image height plus 2x border). */ +// +// public int getHeight() +// { +// return (height); +// } +// +// //---------------------------------------------------- +// /** +// * Has the ImageLabel been given an explicit size? This is used to decide if the image should be stretched or not. This will be true if you call resize or +// * reshape on the ImageLabel before adding it to a Container. It will be false otherwise. +// */ +// protected boolean hasExplicitSize() +// { +// return (explicitSize); +// } +// +// //---------------------------------------------------- +// /** +// * Returns the string representing the URL that will be used if none is supplied in the constructor. +// */ +// public static String getDefaultImageString() +// { +// return (defaultImageString); +// } +// +// /** +// * Sets the string representing the URL that will be used if none is supplied in the constructor. Note that this is static, so is shared by all ImageLabels. +// * Using this might be convenient in testing, but "real" applications should avoid it. +// */ +// public static void setDefaultImageString(String file) +// { +// defaultImageString = file; +// } +// +// //---------------------------------------------------- +// /** +// * Returns the string representing the URL of image. +// */ +// protected String getImageString() +// { +// return (imageString); +// } +// +// //---------------------------------------------------- +// /** Is the debugging flag set? */ +// +// public boolean isDebugging() +// { +// return (debug); +// } +// +// /** +// * Set the debugging flag. Verbose messages will be printed to System.out if this is true. +// */ +// public void setIsDebugging(boolean debug) +// { +// this.debug = debug; +// } +// + //---------------------------------------------------- +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/NodeColorPanel.java b/src/com/endlessloopsoftware/ego/client/graph/NodeColorPanel.java new file mode 100644 index 0000000..280a66c --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/NodeColorPanel.java @@ -0,0 +1,195 @@ +package com.endlessloopsoftware.ego.client.graph; + +import javax.swing.*; +import javax.swing.table.*; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.util.*; +import java.awt.event.*; + +import org.egonet.util.listbuilder.Selection; +import org.egonet.util.table.*; + +import com.endlessloopsoftware.ego.*; +import com.endlessloopsoftware.ego.client.EgoClient; +import com.endlessloopsoftware.ego.client.graph.NodeProperty.NodeShape; + +public class NodeColorPanel extends JPanel { + + private JLabel questionLabel; + + private JComboBox questionCombo; + + ColorChooserEditor colorChooser; + + private PropertyTableModel tableModel; + + private JTable table; + + private GroupLayout layout; + + private GraphRenderer graphRenderer; + + private GraphData graphData; + + private JButton applyButton; + + List selectionList = new ArrayList(); + + public NodeColorPanel(GraphRenderer renderer) { + this.graphRenderer = renderer; + layout = new GroupLayout(this); + this.setLayout(layout); + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + graphData = new GraphData(); + createComponents(); + } + + private void createComponents() { + + questionLabel = new JLabel("Choose question to shape"); + questionLabel.setVisible(true); + + // create questionCombo + List qList = new ArrayList(); + Study study = EgoClient.interview.getStudy(); + QuestionList questionList = study.getQuestions(); + Map questionMap = questionList.getQuestionMap(); + for (Long key : questionMap.keySet()) { + Question currentQuestion = questionMap.get(key); + int questionType = currentQuestion.questionType; + if (questionType == Question.ALTER_QUESTION) { + // populate the list box with only questions that have choices + // as answers + if (currentQuestion.selections.length >= 1) + qList.add(currentQuestion); + } + } + questionCombo = new JComboBox(qList.toArray()); + questionCombo.setVisible(true); + questionCombo.setEnabled(true); + questionCombo.setPreferredSize(new Dimension(20, 20)); + questionCombo.setMaximumSize(new Dimension(20, 30)); + questionCombo.setAutoscrolls(true); + + questionCombo.addItemListener(new java.awt.event.ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + createTable(); + drawPanel(); + } + } + }); + + // create apply button + applyButton = new JButton("Apply Colors"); + applyButton.setVisible(true); + applyButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateNodeColor(); + } + }); + + createTable(); + drawPanel(); + + } + + private void createTable() { + Question question = (Question) questionCombo.getSelectedItem(); + int category = Question.ALTER_QUESTION; + int noOfRows = question.selections.length; + Object[][] rowData = new Object[noOfRows][2]; + /* change the list of selections based on the selected question */ + if (!selectionList.isEmpty()) { + selectionList.removeAll(selectionList); + } + for (Selection selection : question.selections) { + selectionList.add(selection); + } + // populate the responses + for (int i = 0; i < noOfRows; i++) { + rowData[i][0] = selectionList.get(i); + String str = ((Selection) rowData[i][0]).getString(); + } + // populate the colors + int noOfColors = question.selections.length; + Random rand = new Random(); + for (int i = 0; i < noOfColors; i++) { + int red = rand.nextInt(255); + int green = rand.nextInt(255); + int blue = rand.nextInt(255); + Color color = new Color(red, green, blue); + rowData[i][1] = color; + } + + + table = new JTable(new PropertyTableModel(rowData)); + table.setPreferredScrollableViewportSize(table.getPreferredSize()); + table.setRowHeight(25); + table.setVisible(true); + table.setBorder(BorderFactory.createCompoundBorder(BorderFactory + .createLineBorder(Color.black), getBorder())); + + TableColumnModel columnModel = table.getColumnModel(); + LabelRenderer selectionRenderer = new LabelRenderer(); + columnModel.getColumn(0).setCellRenderer(selectionRenderer); + + TableCellEditor colorEditor = new ColorEditor(); + columnModel.getColumn(1).setCellEditor(colorEditor); + ColorRenderer colorButtonRenderer = new ColorRenderer(true); + columnModel.getColumn(1).setCellRenderer(colorButtonRenderer); + + columnModel.getColumn(0).setMaxWidth(200); + columnModel.getColumn(1).setMaxWidth(150); + } + + private void updateNodeColor() { + + int noOfAlters = EgoClient.interview.getNumAlters(); + Question question = (Question) questionCombo.getSelectedItem(); + for (int i = 0; i < question.selections.length; i++) { + Selection selection = question.selections[i]; + + GraphQuestion graphQuestion = new GraphQuestion(question, selection, Question.ALTER_QUESTION); + NodeProperty nodeProperty = new NodeProperty(); + nodeProperty.setColor((Color)table.getValueAt(i, 1)); + nodeProperty.setProperty(NodeProperty.Property.Color); + graphRenderer.addQAsettings(graphQuestion, nodeProperty); + graphRenderer.updateGraphSettings(); + } +// graphRenderer.getVv().repaint(); + } + + private void drawPanel() { + this.removeAll(); + + GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup(); + hGroup.addGroup(layout.createParallelGroup() + .addComponent(questionLabel).addComponent(questionCombo) + .addComponent(table.getTableHeader()).addComponent(table) + .addComponent(applyButton)); + + layout.setHorizontalGroup(hGroup); + + GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup(); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(questionLabel)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(questionCombo)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent( + table.getTableHeader())); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(table)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(applyButton)); + + layout.setVerticalGroup(vGroup); + } +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/NodeLabelPanel.java b/src/com/endlessloopsoftware/ego/client/graph/NodeLabelPanel.java new file mode 100644 index 0000000..9744a30 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/NodeLabelPanel.java @@ -0,0 +1,224 @@ +package com.endlessloopsoftware.ego.client.graph; + +import javax.swing.*; +import javax.swing.table.TableColumnModel; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.ItemEvent; +import java.util.*; +import java.awt.event.*; + +import org.egonet.util.listbuilder.Selection; +import org.egonet.util.table.LabelTableModel; +import org.egonet.util.table.LabelRenderer; +import com.endlessloopsoftware.ego.*; +import com.endlessloopsoftware.ego.client.EgoClient; +import com.endlessloopsoftware.ego.client.graph.NodeProperty.NodeShape; + +public class NodeLabelPanel extends JPanel { + + private JComboBox questionCombo; + + private JLabel questionLabel; + + private JRadioButton questionRadio; + + private JRadioButton alterNamesRadio; + + private JButton applyButton; + + private LabelTableModel tableModel; + + private JTable table; + + private GroupLayout layout; + + private GraphRenderer graphRenderer; + + private GraphData graphData; + + List selectionList = new ArrayList(); + + public NodeLabelPanel(GraphRenderer renderer) { + this.graphRenderer = renderer; + layout = new GroupLayout(this); + this.setLayout(layout); + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + graphData = new GraphData(); + createComponents(); + } + + private void createComponents() { + +// questionLabel = new JLabel("Label by question"); +// questionLabel.setVisible(true); + + questionRadio = new JRadioButton("Label by question"); + alterNamesRadio = new JRadioButton("Label by alter names"); + ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add(questionRadio); + buttonGroup.add(alterNamesRadio); + alterNamesRadio.setSelected(true); + questionRadio.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent e) { + if (questionRadio.isSelected() == true) { + questionCombo.setEnabled(true); + if(applyButton != null) + applyButton.setEnabled(true); + } + } + }); + + alterNamesRadio.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent e) { + if (alterNamesRadio.isSelected() == true) { + //table.setEnabled(false); + questionCombo.setEnabled(false); + if(applyButton!= null ) + applyButton.setEnabled(false); + updateNodeLabels(true); + } + } + }); + + // create questionCombo + List qList = new ArrayList(); + Study study = EgoClient.interview.getStudy(); + QuestionList questionList = study.getQuestions(); + Map questionMap = questionList.getQuestionMap(); + for (Long key : questionMap.keySet()) { + Question currentQuestion = questionMap.get(key); + int questionType = currentQuestion.questionType; + if (questionType == Question.ALTER_QUESTION) { + // populate the list box with only questions that have choices + // as answers + if (currentQuestion.selections.length >= 1) + qList.add(currentQuestion); + } + } + questionCombo = new JComboBox(qList.toArray()); + questionCombo.setVisible(true); + questionCombo.setEnabled(false); + questionCombo.setPreferredSize(new Dimension(20, 20)); + questionCombo.setMaximumSize(new Dimension(20, 30)); + questionCombo.setAutoscrolls(true); + + questionCombo.addItemListener(new java.awt.event.ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + createTable(); + drawPanel(); + } + } + }); + + // show only questin combo and label + GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup(); + hGroup.addGroup(layout.createParallelGroup() + .addComponent(questionRadio).addComponent(questionCombo) + .addComponent(alterNamesRadio)); + + layout.setHorizontalGroup(hGroup); + + GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup(); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(questionRadio)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(questionCombo)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(alterNamesRadio)); + layout.setVerticalGroup(vGroup); + } + + private void createTable() { + Question question = (Question) questionCombo.getSelectedItem(); + int category = Question.ALTER_QUESTION; + int noOfRows = question.selections.length; + Selection[] rowData = new Selection[noOfRows]; + /* change the list of selections based on the selected question */ + if (!selectionList.isEmpty()) { + selectionList.removeAll(selectionList); + } + for (Selection selection : question.selections) { + selectionList.add(selection); + } + for (int i = 0; i < noOfRows; i++) { + rowData[i] = selectionList.get(i); + } + table = new JTable(new LabelTableModel(rowData)); + table.setPreferredScrollableViewportSize(table.getPreferredSize()); + table.setRowHeight(25); + table.setVisible(true); + table.setBorder(BorderFactory.createCompoundBorder(BorderFactory + .createLineBorder(Color.black), getBorder())); + + TableColumnModel columnModel = table.getColumnModel(); + + LabelRenderer selectionRenderer = new LabelRenderer(); + columnModel.getColumn(0).setCellRenderer(selectionRenderer); + columnModel.getColumn(0).setMaxWidth(200); + + // create apply button + applyButton = new JButton("Apply Label"); + applyButton.setVisible(true); + applyButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateNodeLabels(false); + } + }); + } + + private void updateNodeLabels(boolean defaultNames) { + if (defaultNames) { + int noOfAlters = EgoClient.interview.getNumAlters(); + for (int i = 0; i < noOfAlters; i++) { + String alterName = EgoClient.interview.getAlterList()[i]; + graphRenderer.updateGraphSettings(alterName, i, 1); + } + } else { + Question question = (Question) questionCombo.getSelectedItem(); + for (Selection selection : question.selections) { + List alterList = graphData.getAlterNumbers(question, + selection); + + GraphQuestion graphQuestion = new GraphQuestion(question, selection, Question.ALTER_QUESTION); + NodeProperty nodeProperty = new NodeProperty(); + nodeProperty.setLabel(selection.getString()); + nodeProperty.setProperty(NodeProperty.Property.Label); + graphRenderer.addQAsettings(graphQuestion, nodeProperty); + graphRenderer.updateGraphSettings(); + } + } + graphRenderer.getVv().repaint(); + } + + private void drawPanel() { + this.removeAll(); + GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup(); + hGroup.addGroup(layout.createParallelGroup() + .addComponent(questionRadio).addComponent(questionCombo) + .addComponent(table.getTableHeader()).addComponent(table) + .addComponent(applyButton).addComponent(alterNamesRadio)); + + layout.setHorizontalGroup(hGroup); + + GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup(); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(questionRadio)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(questionCombo)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent( + table.getTableHeader())); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(table)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(applyButton)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(alterNamesRadio)); + + layout.setVerticalGroup(vGroup); + } +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/NodeProperty.java b/src/com/endlessloopsoftware/ego/client/graph/NodeProperty.java new file mode 100644 index 0000000..35ad47a --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/NodeProperty.java @@ -0,0 +1,86 @@ +package com.endlessloopsoftware.ego.client.graph; + +import java.awt.*; + +public class NodeProperty extends GraphProperty { + + public static enum NodeShape { + Circle, Square, Pentagon, Hexagon, Triangle, Star, RoundedRectangle + } + + public static enum Property { + Color, Shape, Size, Label + } + public NodeProperty(String label, Color color, NodeShape shape, int size) { + this.label = label; + this.color = color; + this.shape = shape; + this.size = size; + } + + private NodeShape shape; + + private String label; + + private boolean showLabel = false; + + private Property property = null; + + private String toolTipText = ""; + + public String getToolTipText() { + return toolTipText; + } + + public void setToolTipText(String toolTipText) { + this.toolTipText = toolTipText; + } + + public Property getProperty() { + return property; + } + + public void setProperty(Property property) { + this.property = property; + } + + public NodeProperty() { + super(); + this.shape = NodeShape.Circle; + } + + public NodeShape getShape() { + return this.shape; + } + + public void setShape(NodeShape shape) { + this.shape = shape; + // System.out.println("Shape updated to "+ this.shape.toString()); + } + + public String toString() { + String str; + str = this.shape.toString() + " " + this.color.toString() + " " + + this.size + " " + this.label + " " + this.property; + return str; + } + + public boolean isShowLabel() { + return showLabel; + } + + public void setShowLabel(boolean showLabel) { + this.showLabel = showLabel; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + + +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/NodeShapePanel.java b/src/com/endlessloopsoftware/ego/client/graph/NodeShapePanel.java new file mode 100644 index 0000000..11ebe7c --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/NodeShapePanel.java @@ -0,0 +1,204 @@ +package com.endlessloopsoftware.ego.client.graph; + +import javax.swing.*; +import javax.swing.table.TableColumnModel; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.util.*; +import java.awt.event.*; + +import org.egonet.util.listbuilder.Selection; +import org.egonet.util.table.*; + +import com.endlessloopsoftware.ego.*; +import com.endlessloopsoftware.ego.client.EgoClient; +import com.endlessloopsoftware.ego.client.graph.NodeProperty.NodeShape; + +public class NodeShapePanel extends JPanel { + + private JLabel questionLabel; + + private JComboBox questionCombo; + + private JComboBox shapeCombo; + + private PropertyTableModel tableModel; + + private JTable table; + + private GroupLayout layout; + + private GraphRenderer graphRenderer; + + private GraphData graphData; + + private JButton applyButton; + + List selectionList = new ArrayList(); + + public NodeShapePanel(GraphRenderer renderer) { + this.graphRenderer = renderer; + layout = new GroupLayout(this); + this.setLayout(layout); + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + graphData = new GraphData(); + createComponents(); + } + + private void createComponents() { + + questionLabel = new JLabel("Choose question to shape"); + questionLabel.setVisible(true); + + // create questionCombo + List qList = new ArrayList(); + Study study = EgoClient.interview.getStudy(); + QuestionList questionList = study.getQuestions(); + Map questionMap = questionList.getQuestionMap(); + for (Long key : questionMap.keySet()) { + Question currentQuestion = questionMap.get(key); + int questionType = currentQuestion.questionType; + if (questionType == Question.ALTER_QUESTION) { + // populate the list box with only questions that have choices + // as answers + if (currentQuestion.selections.length >= 1) + qList.add(currentQuestion); + } + } + questionCombo = new JComboBox(qList.toArray()); + questionCombo.setVisible(true); + questionCombo.setEnabled(true); + questionCombo.setPreferredSize(new Dimension(20, 20)); + questionCombo.setMaximumSize(new Dimension(20, 30)); + questionCombo.setAutoscrolls(true); + + questionCombo.addItemListener(new java.awt.event.ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + createTable(); + drawPanel(); + } + } + }); + + // create shape combo for table cell + shapeCombo = new JComboBox(NodeProperty.NodeShape.values()); + shapeCombo.setPreferredSize(new Dimension(20, 20)); + shapeCombo.setMaximumSize(new Dimension(20, 30)); + // shapeCombo.setSelectedIndex(0); + + // create apply button + applyButton = new JButton("Apply Shape"); + applyButton.setVisible(true); + applyButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateNodeShape(); + } + }); + + createTable(); + drawPanel(); + + } + + private void createTable() { + Question question = (Question) questionCombo.getSelectedItem(); + int category = Question.ALTER_QUESTION; + int noOfRows = question.selections.length; + Object[][] rowData = new Object[noOfRows][2]; + /* change the list of selections based on the selected question */ + if (!selectionList.isEmpty()) { + selectionList.removeAll(selectionList); + } + for (Selection selection : question.selections) { + selectionList.add(selection); + } + // populate the responses + for (int i = 0; i < noOfRows; i++) { + rowData[i][0] = selectionList.get(i); + String str = ((Selection) rowData[i][0]).getString(); + } + // populate the shapes + int maxNoOfShapes = NodeProperty.NodeShape.values().length; + for (int i = 0; i < noOfRows; i++) { + if (i < maxNoOfShapes) { + NodeProperty.NodeShape shape = NodeProperty.NodeShape.values()[i]; + rowData[i][1] = shape; + } else { + NodeProperty.NodeShape shape = NodeProperty.NodeShape.values()[i + - maxNoOfShapes]; + rowData[i][1] = shape; + } + } + + table = new JTable(new PropertyTableModel(rowData)); + table.setPreferredScrollableViewportSize(table.getPreferredSize()); + table.setRowHeight(25); + table.setVisible(true); + table.setBorder(BorderFactory.createCompoundBorder(BorderFactory + .createLineBorder(Color.black), getBorder())); + + TableColumnModel columnModel = table.getColumnModel(); + LabelRenderer selectionRenderer = new LabelRenderer(); + columnModel.getColumn(0).setCellRenderer(selectionRenderer); + + DefaultCellEditor shapeEditor = new DefaultCellEditor(shapeCombo); + columnModel.getColumn(1).setCellEditor(shapeEditor); + columnModel.getColumn(1).setCellRenderer( + new TableComboBoxRenderer(NodeProperty.NodeShape.values())); + + columnModel.getColumn(0).setMaxWidth(200); + columnModel.getColumn(1).setMaxWidth(150); + } + + private void updateNodeShape() { + + int noOfAlters = EgoClient.interview.getNumAlters(); + Question question = (Question) questionCombo.getSelectedItem(); + for (int i = 0; i < question.selections.length; i++) { + Selection selection = question.selections[i]; + List alterList = graphData.getAlterNumbers(question, + selection); + + GraphQuestion graphQuestion = new GraphQuestion(question, selection, Question.ALTER_QUESTION); + NodeProperty nodeProperty = new NodeProperty(); + nodeProperty.setShape((NodeProperty.NodeShape)table.getValueAt(i, 1)); + nodeProperty.setProperty(NodeProperty.Property.Shape); + graphRenderer.addQAsettings(graphQuestion, nodeProperty); + graphRenderer.updateGraphSettings(); + } +// graphRenderer.getVv().repaint(); + } + + private void drawPanel() { + this.removeAll(); + + GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup(); + hGroup.addGroup(layout.createParallelGroup() + .addComponent(questionLabel).addComponent(questionCombo) + .addComponent(table.getTableHeader()).addComponent(table) + .addComponent(applyButton)); + + layout.setHorizontalGroup(hGroup); + + GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup(); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(questionLabel)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(questionCombo)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent( + table.getTableHeader())); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(table)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(applyButton)); + + layout.setVerticalGroup(vGroup); + } +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/NodeSizePanel.java b/src/com/endlessloopsoftware/ego/client/graph/NodeSizePanel.java new file mode 100644 index 0000000..95f033b --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/NodeSizePanel.java @@ -0,0 +1,205 @@ +package com.endlessloopsoftware.ego.client.graph; + +import javax.swing.*; +import javax.swing.table.TableColumnModel; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.util.*; +import java.awt.event.*; + +import org.egonet.util.listbuilder.Selection; +import org.egonet.util.table.*; + +import com.endlessloopsoftware.ego.*; +import com.endlessloopsoftware.ego.client.EgoClient; +import com.endlessloopsoftware.ego.client.graph.NodeProperty.NodeShape; + +public class NodeSizePanel extends JPanel { + + private JLabel questionLabel; + + private JComboBox questionCombo; + + private JComboBox sizeCombo; + + private PropertyTableModel tableModel; + + private JTable table; + + private GroupLayout layout; + + private GraphRenderer graphRenderer; + + private GraphData graphData; + + private JButton applyButton; + + String[] sizes = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" }; + + List selectionList = new ArrayList(); + + public NodeSizePanel(GraphRenderer renderer) { + this.graphRenderer = renderer; + layout = new GroupLayout(this); + this.setLayout(layout); + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + graphData = new GraphData(); + createComponents(); + } + + private void createComponents() { + + questionLabel = new JLabel("Choose question to shape"); + questionLabel.setVisible(true); + + // create questionCombo + List qList = new ArrayList(); + Study study = EgoClient.interview.getStudy(); + QuestionList questionList = study.getQuestions(); + Map questionMap = questionList.getQuestionMap(); + for (Long key : questionMap.keySet()) { + Question currentQuestion = questionMap.get(key); + int questionType = currentQuestion.questionType; + if (questionType == Question.ALTER_QUESTION) { + // populate the list box with only questions that have choices + // as answers + if (currentQuestion.selections.length >= 1) + qList.add(currentQuestion); + } + } + questionCombo = new JComboBox(qList.toArray()); + questionCombo.setVisible(true); + questionCombo.setEnabled(true); + questionCombo.setPreferredSize(new Dimension(20, 20)); + questionCombo.setMaximumSize(new Dimension(20, 30)); + questionCombo.setAutoscrolls(true); + + questionCombo.addItemListener(new java.awt.event.ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + createTable(); + drawPanel(); + } + } + }); + + // create size combo for table cell + sizeCombo = new JComboBox(sizes); + sizeCombo.setPreferredSize(new Dimension(20, 20)); + sizeCombo.setMaximumSize(new Dimension(20, 30)); + sizeCombo.setSelectedIndex(0); + + // create apply button + applyButton = new JButton("Apply Size"); + applyButton.setVisible(true); + applyButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateNodeSize(); + } + }); + + createTable(); + drawPanel(); + + } + + private void createTable() { + Question question = (Question) questionCombo.getSelectedItem(); + int category = Question.ALTER_QUESTION; + int noOfRows = question.selections.length; + Object[][] rowData = new Object[noOfRows][2]; + /* change the list of selections based on the selected question */ + if (!selectionList.isEmpty()) { + selectionList.removeAll(selectionList); + } + for (Selection selection : question.selections) { + selectionList.add(selection); + } + // populate the responses + for (int i = 0; i < noOfRows; i++) { + rowData[i][0] = selectionList.get(i); + String str = ((Selection) rowData[i][0]).getString(); + } + // populate the shapes + int noOfSizes = question.selections.length; + for (int i = 1; i <= noOfSizes; i++) { + if (i < sizes.length) { + String num = (new Integer(i)).toString(); + rowData[i - 1][1] = num; + } else { + String num = (new Integer(i - sizes.length)).toString(); + rowData[i - 1][1] = num; + } + } + + table = new JTable(new PropertyTableModel(rowData)); + table.setPreferredScrollableViewportSize(table.getPreferredSize()); + table.setRowHeight(25); + table.setVisible(true); + table.setBorder(BorderFactory.createCompoundBorder(BorderFactory + .createLineBorder(Color.black), getBorder())); + + TableColumnModel columnModel = table.getColumnModel(); + LabelRenderer selectionRenderer = new LabelRenderer(); + columnModel.getColumn(0).setCellRenderer(selectionRenderer); + + DefaultCellEditor sizeEditor = new DefaultCellEditor(sizeCombo); + columnModel.getColumn(1).setCellEditor(sizeEditor); + columnModel.getColumn(1).setCellRenderer( + new TableComboBoxRenderer(sizes)); + + columnModel.getColumn(0).setMaxWidth(200); + columnModel.getColumn(1).setMaxWidth(80); + } + + private void updateNodeSize() { + Question question = (Question) questionCombo.getSelectedItem(); + for (int i = 0; i < question.selections.length; i++) { + Selection selection = question.selections[i]; + List alterList = graphData.getAlterNumbers(question, + selection); + + GraphQuestion graphQuestion = new GraphQuestion(question, + selection, Question.ALTER_QUESTION); + NodeProperty nodeProperty = new NodeProperty(); + nodeProperty.setSize(Integer.parseInt((String) table.getValueAt(i, + 1))); + nodeProperty.setProperty(NodeProperty.Property.Size); + graphRenderer.addQAsettings(graphQuestion, nodeProperty); + graphRenderer.updateGraphSettings(); + } + // graphRenderer.getVv().repaint(); + } + + private void drawPanel() { + this.removeAll(); + + GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup(); + hGroup.addGroup(layout.createParallelGroup() + .addComponent(questionLabel).addComponent(questionCombo) + .addComponent(table.getTableHeader()).addComponent(table) + .addComponent(applyButton)); + + layout.setHorizontalGroup(hGroup); + + GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup(); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(questionLabel)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(questionCombo)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent( + table.getTableHeader())); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(table)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(applyButton)); + + layout.setVerticalGroup(vGroup); + } +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/PolygonVertexShapeFunction.java b/src/com/endlessloopsoftware/ego/client/graph/PolygonVertexShapeFunction.java new file mode 100644 index 0000000..715c93b --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/PolygonVertexShapeFunction.java @@ -0,0 +1,26 @@ +package com.endlessloopsoftware.ego.client.graph; + +import java.awt.Shape; +import edu.uci.ics.jung.graph.Vertex; +import edu.uci.ics.jung.graph.decorators.*; + +public class PolygonVertexShapeFunction extends AbstractVertexShapeFunction +{ + private int numberOfEdges; + public PolygonVertexShapeFunction() + { + this.setSizeFunction(new ConstantVertexSizeFunction(15)); + } + + public Shape getShape(Vertex v, int size, int numberOfEdges) + { + this.numberOfEdges= numberOfEdges; + this.setSizeFunction(new ConstantVertexSizeFunction(size)); + return getShape(v); + } + + public Shape getShape(Vertex v) + { + return factory.getRegularPolygon(v,numberOfEdges); + } +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/StructuralMeasuresPanel.java b/src/com/endlessloopsoftware/ego/client/graph/StructuralMeasuresPanel.java new file mode 100644 index 0000000..dd6c9d7 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/StructuralMeasuresPanel.java @@ -0,0 +1,135 @@ +package com.endlessloopsoftware.ego.client.graph; + +import java.awt.event.*; +import javax.swing.*; + +import org.egonet.util.listbuilder.Selection; + +import com.endlessloopsoftware.ego.Question; + +import java.awt.*; + +public class StructuralMeasuresPanel extends JPanel { + + private JButton applySizeButton; + + private JButton applyColorButton; + + private JComboBox structuralCombo_1; + + private JComboBox structuralCombo_2; + + private JLabel sizeLabel; + + private JLabel colorLabel; + + private GroupLayout layout; + + private GraphRenderer graphRenderer; + + private GraphData graphData; + + public static enum StructuralMeasures { + DegreeCentrality, BetweennessCentrality + }; + + public StructuralMeasuresPanel(GraphRenderer renderer) { + this.graphRenderer = renderer; + layout = new GroupLayout(this); + this.setLayout(layout); + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + graphData = new GraphData(); + createComponents(); + } + + private void createComponents() { + sizeLabel = new JLabel("Size nodes based on : "); + colorLabel = new JLabel("Color nodes based on : "); + structuralCombo_1 = new JComboBox(StructuralMeasures.values()); + structuralCombo_1.setMaximumSize(new Dimension(50, 100)); + structuralCombo_2 = new JComboBox(StructuralMeasures.values()); + structuralCombo_2.setMaximumSize(new Dimension(50, 100)); + applySizeButton = new JButton("Apply Size"); + applyColorButton = new JButton("Apply Color"); + + applySizeButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + StructuralMeasures measure = (StructuralMeasures) structuralCombo_1 + .getSelectedItem(); + NodeProperty.Property param = NodeProperty.Property.Size; + System.out.println("Size by : " + measure.toString()); + addStructuralElement(measure, param); + graphRenderer.updateGraphSettings(); + } + }); + applyColorButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + StructuralMeasures measure = (StructuralMeasures) structuralCombo_2 + .getSelectedItem(); + NodeProperty.Property param = NodeProperty.Property.Color; + addStructuralElement(measure, param); + graphRenderer.updateGraphSettings(); + System.out.println("Color by : " + measure.toString()); + + } + }); + + drawPanel(); + } + + private void addStructuralElement(StructuralMeasures measure, + NodeProperty.Property property) { + + Question question = new Question("Structural_Properties"); + Selection selection = null; + switch (measure) { + case BetweennessCentrality: + selection = new Selection("BetweennessCentrality", 0, 0, false); + break; + case DegreeCentrality: + selection = new Selection("DegreeCentrality", 0, 0, false); + break; + } + // the 3rd argument to constructor is 0 to say that it is a structural + // question + GraphQuestion gq = new GraphQuestion(question, selection, 0); + NodeProperty nodeProperty = new NodeProperty(); + nodeProperty.setColor(Color.BLACK); + nodeProperty.setSize(15); + nodeProperty.setShape(NodeProperty.NodeShape.Circle); + nodeProperty.setProperty(property); + graphRenderer.addQAsettings(gq, nodeProperty); + } + + private void drawPanel() { + this.removeAll(); + + GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup(); + hGroup.addGroup(layout.createParallelGroup().addComponent(sizeLabel) + .addGap(30).addComponent(applySizeButton).addGap(30) + .addComponent(colorLabel).addGap(30).addComponent( + applyColorButton)); + hGroup.addGroup(layout.createParallelGroup().addComponent( + structuralCombo_1).addComponent(structuralCombo_2)); + layout.setHorizontalGroup(hGroup); + GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup(); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(sizeLabel) + .addComponent(structuralCombo_1)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addGap(30)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(applySizeButton)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addGap(30)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(colorLabel) + .addComponent(structuralCombo_2)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addGap(30)); + vGroup.addGroup(layout.createParallelGroup( + GroupLayout.Alignment.BASELINE).addComponent(applyColorButton)); + layout.setVerticalGroup(vGroup); + } +} diff --git a/src/com/endlessloopsoftware/ego/client/graph/VertexToolTipFunction.java b/src/com/endlessloopsoftware/ego/client/graph/VertexToolTipFunction.java new file mode 100644 index 0000000..b9b1bb7 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/graph/VertexToolTipFunction.java @@ -0,0 +1,32 @@ +package com.endlessloopsoftware.ego.client.graph; + +import edu.uci.ics.jung.graph.decorators.ToolTipFunctionAdapter; +import java.awt.event.MouseEvent; +import javax.swing.JComponent; +import edu.uci.ics.jung.graph.Edge; +import edu.uci.ics.jung.graph.Vertex; + +public class VertexToolTipFunction extends ToolTipFunctionAdapter { + /** + * @param v + * the Vertex + * @return toString on the passed Vertex + */ + public String getToolTipText(Vertex v) { + + return v.toString(); + } + + /** + * @param e + * the Edge + * @return toString on the passed Edge + */ + public String getToolTipText(Edge e) { + return e.toString(); + } + + public String getToolTipText(MouseEvent e) { + return ((JComponent) e.getSource()).getToolTipText(); + } +} diff --git a/src/com/endlessloopsoftware/ego/client/localSelect.gui_xml b/src/com/endlessloopsoftware/ego/client/localSelect.gui_xml new file mode 100644 index 0000000..060b9d6 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/localSelect.gui_xml @@ -0,0 +1,260 @@ + + + + + + + 0 + + + Title + + + Egocentric Network Study + + + + + + + 0 + + + StudyName + + + New Study + + + + + + Select Study + + SelectStudy + + + + + + View Interview + + ViewInterview + + + + + + Summary Statistics + + SummaryStatistics + + + + + + Start Interview + + StartInterview + + + + + + Gradient Statistics + + GradientStatistics + + + + + + 525 + 246 + + + + + 525 + 246 + + + + + + + + + + + West + + + West + 0 + + + + + East + + + East + 0 + + + + + North + + + South + 30 + + + + + + + + West + + + + + + + + + 10 + 8 + 50 + + + + + North + + + North + 0 + + + + + Width + + + 10 + + + + + + + + West + + + West + 0 + + + + + North + + + South + 30 + + + + + + + + East + + + East + 0 + + + + + North + + + North + 0 + + + + + + + + West + + + 10 + Width + 0 + + + + + East + + + 90 + Width + 0 + + + + + North + + + South + 30 + + + + + + + + West + + + 5 + Width + 0 + + + + + East + + + 95 + Width + 0 + + + + + North + + + North + 18 + + + + + + diff --git a/src/com/endlessloopsoftware/ego/client/statistics/AlterStats.java b/src/com/endlessloopsoftware/ego/client/statistics/AlterStats.java new file mode 100644 index 0000000..02ce8c3 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/statistics/AlterStats.java @@ -0,0 +1,41 @@ +package com.endlessloopsoftware.ego.client.statistics; + +import java.util.Map; +import java.util.HashMap; + +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2003

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: AlterStats.java,v 1.1 2005/08/02 19:36:05 samag Exp $ + * + * $Log: AlterStats.java,v $ + * Revision 1.1 2005/08/02 19:36:05 samag + * Initial checkin + * + * Revision 1.2 2004/02/26 21:19:17 admin + * adding jardescs + * + * Revision 1.1 2003/12/08 15:57:50 admin + * Modified to generate matrix files on survey completion or summarization + * Extracted statistics models + * + */ + +public class AlterStats +{ + public String qTitle; + public int answerType; + public Long questionId; + public int answerCount; + public String[] answerText; + + // Map + //public Map answerTotals = new HashMap(0); + public int answerTotals[]; +} + diff --git a/src/com/endlessloopsoftware/ego/client/statistics/Statistics.java b/src/com/endlessloopsoftware/ego/client/statistics/Statistics.java new file mode 100644 index 0000000..1c42691 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/statistics/Statistics.java @@ -0,0 +1,1089 @@ +package com.endlessloopsoftware.ego.client.statistics; + +/** + *

Copyright: Copyright (c) 2002 - 2004, Endless Loop Software, Inc.

+ *

Company: Endless Loop Software, Inc.

+ * @author Peter Schoaff + * + * $Id: Statistics.java,v 1.1 2005/08/02 19:36:06 samag Exp $ + */ + +import java.io.PrintWriter; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.Stack; + +import org.egonet.exceptions.MissingPairException; + +import com.endlessloopsoftware.ego.Answer; +import com.endlessloopsoftware.ego.Question; +import com.endlessloopsoftware.ego.Study; +import com.endlessloopsoftware.ego.client.Interview; +import com.endlessloopsoftware.elsutils.files.FileHelpers; + +import electric.xml.Element; + +public class Statistics +{ + private final Study _study; + private final Interview _interview; + + public int[][] adjacencyMatrix = new int[0][]; + public int[][] weightedAdjacencyMatrix = new int[0][]; + public int[][] proximityMatrix = new int[0][]; + public float[] betweennessArray = new float[0]; + public float[] closenessArray = new float[0]; + public int[] farnessArray = new int[0]; + public int[] degreeArray = new int[0]; + public Set cliqueSet = new HashSet(); + public Set allSet = new HashSet(); + public Set componentSet = new HashSet(); + public AlterStats[] alterStatArray = new AlterStats[0]; + public Integer[][] alterSummary = new Integer[0][]; + + public String[] alterList = new String[0]; + + public int isolates; + public int dyads; + + public int mostCentralDegreeAlterValue; + public float meanCentralDegreeValue; + public int mostCentralDegreeAlterIndex; + public String mostCentralDegreeAlterName; + public float degreeNC; + + public float mostCentralBetweenAlterValue; + public float meanCentralBetweenAlterValue; + public int mostCentralBetweenAlterIndex; + public String mostCentralBetweenAlterName; + public float betweenNC; + + public float mostCentralClosenessAlterValue; + public float meanCentralClosenessValue; + public int mostCentralClosenessAlterIndex; + public String mostCentralClosenessAlterName; + public float closenessNC; + + public static final int MINIMUM_CLIQUE = 3; + + /* Instantiable class */ + private Statistics(Interview interview) + { + _interview = interview; + _study = interview.getStudy(); + } + + public static Statistics generateInterviewStatistics(Interview interview, Question q) + { + Statistics stats = new Statistics(interview); + + int numAlters = stats.getInterview().getNumAlters(); + //int numAlters = 2; + int alterindex; + float maxfloat, meanfloat; + int maxint, meanint; + float sizefloat; + + try + { + if (stats.getInterview().getAnswerSubset(q.UniqueId).size() == 0) + { + stats.adjacencyMatrix = new int[0][]; + stats.weightedAdjacencyMatrix = new int[0][]; + stats.proximityMatrix = new int[0][]; + stats.betweennessArray = new float[0]; + stats.closenessArray = new float[0]; + stats.farnessArray = new int[0]; + stats.allSet = new HashSet(0); + stats.cliqueSet = new HashSet(0); + stats.componentSet = new HashSet(0); + stats.degreeArray = new int[0]; + stats.alterStatArray = new AlterStats[0]; + stats.alterSummary = new Integer[0][]; + } + else + { + stats.adjacencyMatrix = interview.generateAdjacencyMatrix(q, false); + stats.weightedAdjacencyMatrix = interview.generateAdjacencyMatrix(q, true); + stats.alterList = interview.getAlterList(); + stats.allSet = new HashSet(0); + stats.cliqueSet = new HashSet(0); + stats.componentSet = new HashSet(0); + + stats.allSet = stats.identifyAllConnections(); + stats.cliqueSet = stats.identifyCliques(); + stats.componentSet = stats.identifyComponents(); + stats.isolates = stats.countComponentSize(1); + stats.dyads = stats.countComponentSize(2); + + stats.clearIdentity(stats.adjacencyMatrix); + stats.clearIdentity(stats.weightedAdjacencyMatrix); + + stats.proximityMatrix = stats.generateProximityMatrix(); + stats.betweennessArray = stats.generateBetweennessArray(); + stats.degreeArray = stats.generateDegreeArray(); + + stats.generateAlterStatArray(); + + /* find most between alter */ + maxfloat = -1; + alterindex = -1; + float tot = 0; + for (int i = 0; i < numAlters; ++i) + { + stats.betweennessArray[i] = (float)(Math.rint(1000*(stats.betweennessArray[i]/2)))/1000; + tot = tot + stats.betweennessArray[i]; + if (stats.betweennessArray[i] > maxfloat) + { + alterindex = i; + maxfloat = stats.betweennessArray[i]; + } + } + + stats.mostCentralBetweenAlterValue = maxfloat; + stats.mostCentralBetweenAlterIndex = alterindex; + stats.mostCentralBetweenAlterName = stats.alterList[alterindex]; + stats.meanCentralBetweenAlterValue = (float)(Math.rint(1000*tot/numAlters))/1000; + + //compute betweenness network centralization + meanfloat = 0; + for (int i = 0; i < numAlters; ++i) + { + meanfloat = meanfloat + (maxfloat - stats.betweennessArray[i]); + } + + stats.betweenNC = (float)(Math.rint(1000*((2*meanfloat*100)/((numAlters-1)*(numAlters-1)*(numAlters-2)))))/1000; + + //System.out.println("Mean betweenness value is "+ stats.meanCentralBetweenAlterValue); + /* find most central degree alter */ + maxint = -1; + for (int i = 0; i < numAlters; ++i) + { + if (stats.degreeArray[i] > maxint) + { + alterindex = i; + maxint = stats.degreeArray[i]; + } + } + + meanfloat = 0; + for (int i = 0; i < numAlters; ++i) + { + meanfloat = meanfloat + stats.degreeArray[i]; + } + + //System.out.println("Mean degree centrality is "+ meanint/size); + stats.mostCentralDegreeAlterValue = maxint; + + //System.out.println("Most central degree value is "+ maxint); + stats.mostCentralDegreeAlterIndex = alterindex; + stats.mostCentralDegreeAlterName = stats.alterList[alterindex]; + stats.meanCentralDegreeValue = (float)(Math.rint(1000*meanfloat/numAlters))/1000; + + //compute network centralization + meanfloat = 0; + for(int i = 0; i < numAlters; ++i){ + meanfloat = meanfloat + (maxint-stats.degreeArray[i]); + } + stats.degreeNC = (float)(Math.rint(1000*((meanfloat*100)/((numAlters*numAlters)-(3*numAlters)+2))))/1000; + //System.out.println("summation is " + meanfloat + "NC is " + stats.degreeNC); + + /* find most central closeness alter */ + stats.closenessArray = new float[stats.alterList.length]; + stats.farnessArray = new int[stats.alterList.length]; + maxint = -1; + maxfloat = -1; + + int count = 0; + boolean conn = true; + for (int i = 0; i < numAlters; ++i) + { + int total = 0; + count = 0; + for (int j = 0; j < numAlters; ++j) + { + //For farness add maximum value if unconnected + if (stats.proximityMatrix[i][j] == 0) + { + total += numAlters; + ++count; + } + else + { + total += stats.proximityMatrix[i][j]; + } + } + + if (count > 1) + { + conn = false; + count = 0; + } + + //Subtract the maximum value added for ego + total = total - numAlters; + stats.farnessArray[i] = total; //(total == 0) ? 0 : ((float) 1 / + // total); + if (stats.farnessArray[i] > 0) + { + sizefloat = (float) numAlters; + stats.closenessArray[i] = (float) (Math.rint(1000 * ((100 * (sizefloat - 1)) / stats.farnessArray[i]))) / 1000; + if (stats.closenessArray[i] > maxfloat) + { + alterindex = i; + maxfloat = stats.closenessArray[i]; + } + } + else + { + stats.closenessArray[i] = 0; + } + } + + meanfloat = 0; + for (int i = 0; i < numAlters; ++i) + { + meanfloat = meanfloat + stats.closenessArray[i]; + } + //System.out.println("Mean closeness centrality is "+ meanfloat/size); + + stats.mostCentralClosenessAlterValue = maxfloat; + stats.mostCentralClosenessAlterIndex = alterindex; + stats.mostCentralClosenessAlterName = stats.alterList[alterindex]; + stats.meanCentralClosenessValue = (float)(Math.rint(1000*meanfloat/numAlters))/1000; + + //compute network centralization + if (conn) + { + meanfloat = 0; + for (int i = 0; i < numAlters; ++i) + { + meanfloat = meanfloat + (maxfloat - stats.closenessArray[i]); + } + + stats.closenessNC = (float) (Math.rint(1000 * (meanfloat / (((numAlters * numAlters) - (3 * numAlters) + 2) / ((2 * numAlters) - 3))))) / 1000; + } + else + { + //System.out.println("Unconnected nw"); + stats.closenessNC = -1; + } + } + } + catch (MissingPairException ex) + { + ex.printStackTrace(); + } + + /********* + * test code + */ + /* + long l = System.currentTimeMillis(); + int[][] testmatrix = new int[80][80]; + for (int i = 0; i < 80; ++i) + { + for (int j = i; j < 80; ++j) + { + int v = (int)(Math.random() * 2); + testmatrix[i][j] = v; + testmatrix[j][i] = v; + } + + testmatrix[i][i] = 1; + } + generateProximityMatrix(testmatrix); + generateBetweennessArray(testmatrix); + Set testCliqueSet = identifyCliques(testmatrix); + identifyComponents(testCliqueSet); + generateDegreeArray(testmatrix); + l = System.currentTimeMillis() - l; + System.out.println(l); + */ + + return stats; + } + + private void clearIdentity(int[][] matrix) + { + for (int i = 0; i < this.adjacencyMatrix.length; ++i) + { + matrix[i][i] = 0; + } + } + + private void setIdentity(int[][] matrix) + { + for (int i = 0; i < this.adjacencyMatrix.length; ++i) + { + matrix[i][i] = 1; + } + } + + /******** + * For a non-directional adjacency graph represented by the parameter matrix, determines + * shortest path length between each pair of alters + * @param adjacencyMatrix representing non-directed graph of alters + * @return Matrix of shortest path lengths + */ + private int[][] generateProximityMatrix() + { + int size = this.adjacencyMatrix.length; + int[][] outMatrix = new int[size][]; + int s, i, j, k; + + /* Clear */ + for (i = 0; i < size; ++i) + { + outMatrix[i] = (int[]) this.adjacencyMatrix[i].clone(); + outMatrix[i][i] = 0; + } + + for (i = 0; i < size; ++i) + { + for (j = 0; j < size; ++j) + { + if (outMatrix[j][i] > 0) + { + for (k = 0; k < size; ++k) + { + if (outMatrix[i][k] > 0) + { + s = outMatrix[j][i] + outMatrix[i][k]; + if ((outMatrix[j][k] == 0) || (s < outMatrix[j][k])) + { + outMatrix[j][k] = s; + } + } + } + } + } + } + + /* Clear */ + for (i = 0; i < size; ++i) + { + outMatrix[i][i] = 0; + } + + return (outMatrix); + } + + /******** + * For a non-directional adjacency graph represented by the parameter matrix, + * the Degree Centrality value for each alter. This is simply the number of + * people the alter knows + * @param adjacencyMatrix representing non-directed graph of alters + * @return array of C(D) for each alter + */ + private int[] generateDegreeArray() + { + int[] d = new int[this.adjacencyMatrix.length]; + + for (int i = 0; i < this.adjacencyMatrix.length; ++i) + { + for (int j = 0; j < this.adjacencyMatrix.length; ++j) + { + if (this.adjacencyMatrix[i][j] > 0) + { + d[i]++; + } + } + } + + return d; + } + + /******** + * For a non-directional adjacency graph represented by the parameter matrix, + * the "Betweenness" value for each alter. The betweenness is the percentage of + * all shortests paths which pass through the alter. + * Based on an algorithm by Ulrik Brandes (2001) + * @return array of C(B) for each alter + */ + private float[] generateBetweennessArray() + { + int size = this.adjacencyMatrix.length; + float[] Cb = new float[size]; + int s; + + for (s = 0; s < size; ++s) + { + Stack S = new Stack(); + java.util.List[] P = new java.util.List[size]; + LinkedList Q = new LinkedList(); + int[] spaths = new int[size]; + int[] distance = new int[size]; + + for (int w = 0; w < size; ++w) + { + P[w] = new LinkedList(); + distance[w] = -1; + } + + spaths[s] = 1; + distance[s] = 0; + Q.addLast(new Integer(s)); + + while (!Q.isEmpty()) + { + Integer V = (Integer) Q.removeFirst(); + int v = V.intValue(); + + S.push(V); + + for (int w = 0; w < size; ++w) + { + if ((w != v) && (adjacencyMatrix[w][v] > 0)) + { + // w found for first time? + if (distance[w] < 0) + { + Q.addLast(new Integer(w)); + distance[w] = distance[v] + 1; + } + + // shortest path to w via v? + if (distance[w] == (distance[v] + 1)) + { + spaths[w] += spaths[v]; + P[w].add(new Integer(v)); + } + } + } + } + + // S returns vertices in order of non-increasing distance from s + float[] dependency = new float[size]; + while (!S.empty()) + { + int w = ((Integer) S.pop()).intValue(); + + Iterator it = P[w].iterator(); + while (it.hasNext()) + { + int v = ((Integer) it.next()).intValue(); + dependency[v] += (spaths[v] + (spaths[v] * dependency[w])) / spaths[w]; + } + + if (w != s) + { + Cb[w] += dependency[w]; + } + } + } + + return (Cb); + } + + /******** + * For a non-directional adjacency graph represented by the parameter matrix, + * counts the number of cliques in the graph + * Based on Bron Kerbosch [73] + * @return Set of Sets. Each Set represents one clique + */ + private Set identifyCliques() + { + int[][] matrix = (int[][]) adjacencyMatrix.clone(); + int size = matrix.length; + int[] all = new int[size]; + Stack compsub = new Stack(); + Set cliqueSet = new HashSet(); + + for (int c = 0; c < size; ++c) + { + all[c] = c; + } + + // Set identity + for (int i = 0; i < size; ++i) + matrix[i][i] = 1; + + extendVersion2(matrix, compsub, cliqueSet, all, 0, size, 0); + + return (cliqueSet); + } + + /******** + * For a non-directional adjacency graph represented by the parameter matrix, + * counts the number of all fully connected groups in the graph + * * @return Set of Sets. Each Set represents one fully connected graph + */ + private Set identifyAllConnections() + { + int[][] matrix = (int[][]) adjacencyMatrix.clone(); + int size = matrix.length; + int[] all = new int[size]; + Stack compsub = new Stack(); + Set allSet = new HashSet(); + + for (int c = 0; c < size; ++c) + { + all[c] = c; + } + + // Set identity + for (int i = 0; i < size; ++i) + matrix[i][i] = 1; + + extendVersion2(matrix, compsub, allSet, all, 0, size, 1); + + return (allSet); + } + + //Returns the number of components of size n in the network + private int countComponentSize(int n) + { + int count = 0; + Iterator it = this.componentSet.iterator(); + + while (it.hasNext()) + { + Set s = (Set) it.next(); + if (s.size() == n) + { + ++count; + } + } + return count; + } + + private void extendVersion2(int[][] pMatrix, Stack compsub, Set cliqueSet, int[] old, int ne, int ce, + int cliqueOrAll) + { + int[] newarray = new int[ce]; + int nod, fixp = 0; + int db = -1; + int newne, newce, i, j, count, pos = 0, p, s = 0, sel, minnod; + + minnod = ce; + nod = 0; + + // Determine each counter value and look for minimum + for (i = 0; (i < ce) && (minnod != 0); ++i) + { + p = old[i]; + count = 0; + + // Count Disconnections + for (j = ne; (j < ce) && (count < minnod); ++j) + { + if (pMatrix[p][old[j]] == 0) + { + ++count; + + // Save position of potential candidate + pos = j; + } + } + + // Test new minimum + if (count < minnod) + { + fixp = p; + minnod = count; + + if (i < ne) + { + s = pos; + db = 0; + } + else + { + s = i; + db = 1; + nod = 1; + } + } + } + + /*** + * If fixed point initially chosen from candidates then number of + * disconnections will be preincreased by one + */ + for (nod += minnod; nod >= 1; --nod) + { + /* Interchange */ + p = old[s]; + old[s] = old[ne]; + old[ne] = p; + sel = p; + + /* Fill new set "not" */ + newne = 0; + for (i = 0; i < ne; ++i) + { + if (pMatrix[sel][old[i]] > 0) + { + newarray[newne++] = old[i]; + } + } + + newce = newne; + + /* Fill new set "cand" */ + for (i = ne + 1; i < ce; ++i) + { + if (pMatrix[sel][old[i]] > 0) + { + newarray[newce++] = old[i]; + } + } + + /* Add to compsub */ + compsub.push(new Integer(sel)); + + if (newce == 0) + { + if (cliqueOrAll == 1) + { + /* Add if all the disconnections are needed */ + /* Return the fully connected graph */ + if (compsub.size() >= 1) + { + cliqueSet.add(compsub.clone()); + } + } + else + { + /* Add if only the cliques are needed */ + /* Return Clique */ + if (compsub.size() >= MINIMUM_CLIQUE) + { + cliqueSet.add(compsub.clone()); + } + } + } + else if (newne < newce) + { + extendVersion2(pMatrix, compsub, cliqueSet, newarray, newne, newce, cliqueOrAll); + } + + /* Remove from compsub */ + compsub.pop(); + ne++; + + if (nod > 1) + { + for (s = ne; pMatrix[fixp][old[s]] > 0; ++s); + } + } + } + + /******** + * For a non-directional adjacency graph represented by the parameter matrix, + * identifies all unconnected components of the graph + * @param cliqueSet Set of stacks returned by identifyCliques + * @return Set of Sets. Each Set represents one component + */ + private Set identifyComponents() + { + Set s = new HashSet(); + LinkedList list = new LinkedList(); + Iterator it; + boolean merged; + + /* clone stacks so this is non-destructive */ + + for (it = this.allSet.iterator(); it.hasNext();) + { + list.add(new HashSet((Stack) it.next())); + } + + while (list.size() > 0) + { + merged = false; + it = list.iterator(); + Set component = (Set) it.next(); + + while (it.hasNext()) + { + Set intersection = new HashSet(component); + Set compareClique = (Set) it.next(); + + intersection.retainAll(compareClique); + + if (intersection.size() > 0) + { + component.addAll(compareClique); + it.remove(); + merged = true; + } + } + + if (merged == false) + { + s.add(component); + list.remove(0); + } + } + + return s; + } + + /******** + * Calculates compositional measures of alter questions + * Generates percentage summary for categorical, and average for numerical + */ + private void generateAlterStatArray() + { + List qList = _study.getQuestionOrder(Question.ALTER_QUESTION); + Iterator qIt = qList.iterator(); + int index = 0; + + alterSummary = new Integer[alterList.length][]; + + /* Count qList Questions which are categorical or numerical */ + while (qIt.hasNext()) + { + Question q = _study.getQuestions().getQuestion((Long) qIt.next()); + + if ((q.answerType == Question.CATEGORICAL) || (q.answerType == Question.NUMERICAL)) + index++; + } + + alterStatArray = new AlterStats[index]; + for (int i = 0; i < index; ++i) + { + alterStatArray[i] = new AlterStats(); + } + + for (int i = 0; i < alterList.length; ++i) + { + alterSummary[i] = new Integer[index]; + } + + index = 0; + qIt = qList.iterator(); + while (qIt.hasNext()) + { + Long qId = (Long) qIt.next(); + Question q = _study.getQuestions().getQuestion(qId); + + if ((q.answerType == Question.CATEGORICAL) || (q.answerType == Question.NUMERICAL)) + { + Set answerSet = _interview.getAnswerSubset(qId); + Iterator aIt = answerSet.iterator(); + + alterStatArray[index].questionId = qId; + alterStatArray[index].qTitle = q.title; + alterStatArray[index].answerType = q.answerType; + + if (q.answerType == Question.NUMERICAL) + { + alterStatArray[index].answerText = new String[] { "Mean" }; + alterStatArray[index].answerTotals = new int[1]; + } + else if (q.answerType == Question.CATEGORICAL) + { + alterStatArray[index].answerTotals = new int[q.selections.length]; + alterStatArray[index].answerText = new String[q.selections.length]; + + for (int i = 0; i < q.selections.length; ++i) + { + // alterStatArray[index].answerText[i] = q.selections[q.selections.length - (i + 1)].getString(); + alterStatArray[index].answerText[i] = q.selections[i].getString(); + } + } + + while (aIt.hasNext()) + { + try { + Answer a = (Answer) aIt.next(); + + if (a.answered) + { + alterSummary[a.getAlters()[0]][index] = new Integer(a.value); + + if (q.answerType == Question.NUMERICAL) + { + if (a.value != -1) + { + alterStatArray[index].answerTotals[0] += a.value; + alterStatArray[index].answerCount++; + } + } + else if (q.answerType == Question.CATEGORICAL) + { + alterStatArray[index].answerTotals[a.index] += 1; + alterStatArray[index].answerCount++; + } + } + else + { + alterSummary[a.getAlters()[0]][index] = new Integer(-1); + } + + } catch (Exception ex) { + ex.printStackTrace(System.err); + } + + } + + index++; + } + } + } + + /******** + * Add interview information to an xml structure for output to a file + * @param e XML Element, parent of interview tree + */ + public void writeStructuralStatistics(Element e) + { + String[] egoName = _interview.getName(); + Element egoNameElem = e.addElement("EgoName"); + egoNameElem.addElement("First").setString(egoName[0]); + egoNameElem.addElement("Last").setString(egoName[1]); + + e.addElement("DegreeValue").setInt(mostCentralDegreeAlterValue); + e.addElement("DegreeName").setString(mostCentralDegreeAlterName); + e.addElement("DegreeMean").setFloat(meanCentralDegreeValue); + e.addElement("DegreeNC").setFloat(degreeNC); + + e.addElement("BetweenValue").setFloat(mostCentralBetweenAlterValue); + e.addElement("BetweenName").setString(mostCentralBetweenAlterName); + e.addElement("BetweenMean").setFloat(meanCentralBetweenAlterValue); + e.addElement("BetweenNC").setFloat(betweenNC); + + e.addElement("ClosenessValue").setFloat(mostCentralClosenessAlterValue); + e.addElement("ClosenessName").setString(mostCentralClosenessAlterName); + e.addElement("ClosenessMean").setFloat(meanCentralClosenessValue); + e.addElement("ClosenessNC").setFloat(closenessNC); + + e.addElement("NumCliques").setInt(cliqueSet.size()); + e.addElement("NumComponents").setInt(componentSet.size()-isolates-dyads); + e.addElement("NumIsolates").setInt(isolates); + e.addElement("NumDyads").setInt(dyads); + } + + /******** + * Add interview information to an xml structure for output to a file + * @param e XML Element, parent of interview tree + */ + public void writeCompositionalStatistics(Element e) + { + Element aqList = e.addElement("AlterQuestionSummaries"); + + for (int i = 0; i < alterStatArray.length; ++i) + { + Element aqElement = aqList.addElement("AlterQuestionSummary"); + + aqElement.addElement("Title").setString(alterStatArray[i].qTitle); + aqElement.addElement("Count").setInt(alterStatArray[i].answerCount); + + Element aList = aqElement.addElement("Answers"); + for (int j = 0; j < alterStatArray[i].answerText.length; ++j) + { + Element aElement = aList.addElement("Answer"); + aElement.addElement("Text").setString(alterStatArray[i].answerText[j]); + aElement.addElement("Total").setInt(alterStatArray[i].answerTotals[j]); + aElement.addElement("AnswerIndex").setInt(j); + } + } + } + + /******** + * Write proximity array for current question to a printwriter + * @param name Name of Ego + * @param w PrintWriter + * @param weighted use weighted proximity array + */ + public void writeAdjacencyArray(String name, PrintWriter w, boolean weighted) + { + // Write column names + w.print(FileHelpers.formatForCSV(name)); + + for (int i = 0; i < alterList.length; ++i) + { + w.print(", " + FileHelpers.formatForCSV(alterList[i])); + } + w.println(); + + for (int i = 0; i < alterList.length; ++i) + { + w.print(FileHelpers.formatForCSV(alterList[i])); + if (weighted) + { + for (int j = 0; j < alterList.length; ++j) + { + w.print(", " + weightedAdjacencyMatrix[i][j]); + } + } + else + { + for (int j = 0; j < alterList.length; ++j) + { + w.print(", " + adjacencyMatrix[i][j]); + } + } + w.println(); + } + } + + /******** + * Write alters answer summary to a printwriter + * @param w PrintWriter + */ + public void writeAlterArray(PrintWriter w) + { + // Write column names + w.write("Alter_Name"); + for (int i = 0; i < alterStatArray.length; ++i) + { + w.write(", " + FileHelpers.formatForCSV(alterStatArray[i].qTitle)); + } + w.println(", Degree, Closeness, Betweenness"); + + for (int i = 0; i < alterList.length; ++i) + { + w.print(FileHelpers.formatForCSV(alterList[i])); + + for (int j = 0; j < alterSummary[0].length; ++j) + { + try + { + w.print(", " + alterSummary[i][j]); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + w.println(", " + degreeArray[i] + ", " + closenessArray[i] + ", " + betweennessArray[i]); + } + } + + /******** + * Calculates compositional measures of alter questions + * Generates percentage summary for categorical, and average for numerical + */ + public void writeTextAnswers(PrintWriter w) + { + List qList; + Iterator qIt; + int parsePtr; + String s; + + qList = _interview.getEgoAnswers(); + qIt = qList.iterator(); + while (qIt.hasNext()) + { + Answer answer = (Answer) qIt.next(); + Question q = _study.getQuestions().getQuestion(answer.questionId); + + if (q.answerType == Question.TEXT) + { + if (answer.answered) + { + w.println("Ego Question: " + q.title); + w.println("Text: " + q.text); + w.println(answer.string); + } + } + } + + qList = _study.getQuestionOrder(Question.ALTER_QUESTION); + qIt = qList.iterator(); + while (qIt.hasNext()) + { + Long qId = (Long) qIt.next(); + Question q = _study.getQuestions().getQuestion(qId); + + if (q.answerType == Question.TEXT) + { + w.println("Alter Question: " + q.title); + w.println("Text: " + q.text); + Set answerSet = _interview.getAnswerSubset(qId); + Iterator aIt = answerSet.iterator(); + + while (aIt.hasNext()) + { + Answer a = (Answer) aIt.next(); + + if (a.answered) + { + w.println(alterList[a.getAlters()[0]] + ": " + a.string); + } + } + } + } + } + + /** + * @return Returns the interview. + */ + public Interview getInterview() + { + return _interview; + } + + /** + * @return Returns the study. + */ + public Study getStudy() + { + return _study; + } +} + +/** + * $Log: Statistics.java,v $ + * Revision 1.1 2005/08/02 19:36:06 samag + * Initial checkin + * + * Revision 1.14 2004/04/08 15:06:07 admin + * EgoClient now creates study summaries from Server + * EgoAuthor now sets active study on server + * + * Revision 1.13 2004/04/06 20:29:22 admin + * First pass as supporting interactive applet linking interviews + * + * Revision 1.12 2004/04/06 15:46:11 admin + * cvs tags in headers + * + * revision 1.11 + * Moving matrix generation into interview to support Applet Linking UI. + * An interview generated with applet linking will have no meaningful alter pair + * questions. The adjacency matrix will be returned in an Athenian manner from + * the server. + * + * revision 1.10 + * Work to integrate with Applet Linking UI + * + * revision 1.9 + * Fixed the components: It was looking at cliques before.Now it will look + * at all the different components in the graph. + * Added display of Components and Cliques + * + * revision 1.8 + * Adding client library + * cleaning up code + * + * revision 1.7 + * Added some structural measures + * + * revision 1.6 + * Version 2.0 beta 3 + * + * revision 1.5 + * Making sure all libraries are available + * + * revision 1.4 + * Fixing bug reading in adjacency selections + * Clearing identity diagonal of Weighted Adjacency Matrix + * + * revision 1.3 + * Modified to generate matrix files on survey completion or summarization + * Extracted statistics models + * + * revision 1.2 + * Extracting Study + * + * revision 1.1 + * Merging EgoNet and EgoClient projects so that they can share some + * common classes more easily. + */ \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/statistics/StatisticsArrayPanel.java b/src/com/endlessloopsoftware/ego/client/statistics/StatisticsArrayPanel.java new file mode 100644 index 0000000..12ea45e --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/statistics/StatisticsArrayPanel.java @@ -0,0 +1,72 @@ +package com.endlessloopsoftware.ego.client.statistics; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; + +import com.endlessloopsoftware.ego.client.statistics.models.StatTableModel; + +/** + *

Title: Egocentric Networks Client Program

+ *

Description: Subject Interview Client

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter Schoaff + * @version 1.0 + */ + +public class StatisticsArrayPanel extends JPanel +{ + StatTableModel data; + private JTable dataTable; + private JScrollPane dataScroll = new JScrollPane(); + private GridBagLayout gridBagLayout1 = new GridBagLayout(); + + public StatisticsArrayPanel(StatTableModel data) + { + this.data = data; + + try + { + jbInit(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + private void jbInit() throws Exception + { + dataTable = new JTable(data); + + this.setLayout(gridBagLayout1); + + dataTable.setAutoResizeMode(data.getResizeMode()); + dataTable.setRowHeight(16); + this.add( + dataScroll, + new GridBagConstraints( + 0, + 0, + 1, + 1, + 1.0, + 1.0, + GridBagConstraints.CENTER, + GridBagConstraints.BOTH, + new Insets(5, 5, 5, 5), + 0, + 0)); + dataScroll.getViewport().add(dataTable, null); + } + + public StatTableModel getTableModel() + { + return data; + } +} \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/statistics/StatisticsFrame.java b/src/com/endlessloopsoftware/ego/client/statistics/StatisticsFrame.java new file mode 100644 index 0000000..b77caae --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/statistics/StatisticsFrame.java @@ -0,0 +1,325 @@ +package com.endlessloopsoftware.ego.client.statistics; + +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2002

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: StatisticsFrame.java,v 1.1 2005/08/02 19:36:05 samag Exp $ + * + */ + +import java.awt.Container; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.io.PrintWriter; +import java.util.Iterator; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; + +import com.endlessloopsoftware.ego.Question; +import com.endlessloopsoftware.ego.client.EgoClient; +import com.endlessloopsoftware.ego.client.SourceSelectPanel; +import com.endlessloopsoftware.ego.client.statistics.models.BetweennessModel; +import com.endlessloopsoftware.ego.client.statistics.models.CliqueModel; +import com.endlessloopsoftware.ego.client.statistics.models.ClosenessModel; +import com.endlessloopsoftware.ego.client.statistics.models.CompositionalStatsModel; +import com.endlessloopsoftware.ego.client.statistics.models.DegreeModel; +import com.endlessloopsoftware.ego.client.statistics.models.InterviewSummaryModel; +import com.endlessloopsoftware.ego.client.statistics.models.QSummaryModel; +import com.endlessloopsoftware.elsutils.files.FileCreateException; +import com.endlessloopsoftware.ego.client.graph.GraphPanel; + + + +public class StatisticsFrame extends JPanel { + private Statistics stats = null; + + private JTabbedPane tabs = new JTabbedPane(); + + private JPanel summaryPanel = null; + + private JPanel dcPanel = null; + + private JPanel ccPanel = null; + + private JPanel bcPanel = null; + + private JPanel cliquePanel = null; + + private JPanel componentPanel = null; + + private JPanel qSummaryPanel = null; + +// private JPanel graphPanel = new GraphPanel(); + + //private JComboBox alterQuestionMenu = new JComboBox(); + + public StatisticsFrame() { + try { + jbInit(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void jbInit() throws Exception { + boolean studyStatable = false; + + /*********************************************************************** + * Fill in alter pair question selection menu + **********************************************************************/ + //String s = alterQuestionMenu.getActionCommand(); + //alterQuestionMenu.setActionCommand("Initialization"); + + Iterator questions = EgoClient.study.getQuestionOrder( + Question.ALTER_PAIR_QUESTION).iterator(); + while (questions.hasNext()) { + Question q = EgoClient.study.getQuestion((Long) questions.next()); + + if (q.statable) { + //alterQuestionMenu.addItem(q); + studyStatable = true; + stats = EgoClient.interview.generateStatistics(q); + + // Use stats to initialize panels + summaryPanel = new StatisticsArrayPanel(new InterviewSummaryModel( + stats)); + dcPanel = new StatisticsArrayPanel(new DegreeModel(stats)); + ccPanel = new StatisticsArrayPanel(new ClosenessModel(stats)); + bcPanel = new StatisticsArrayPanel(new BetweennessModel(stats)); + cliquePanel = new StatisticsArrayPanel(new CliqueModel(stats)); + componentPanel = new StatisticsArrayPanel( + new CompositionalStatsModel(stats)); + qSummaryPanel = new StatisticsArrayPanel(new QSummaryModel(stats)); + + /******************************************************************* + * Create UI + ******************************************************************/ + // setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + // Container panel = this.getContentPane(); + Container panel = this; + + panel.setLayout(new GridBagLayout()); + tabs.addTab("Structural Measures", summaryPanel); + tabs.addTab("Compositional Summary", qSummaryPanel); + tabs.addTab("Degree Centrality", dcPanel); + tabs.addTab("Closeness Centrality", ccPanel); + tabs.addTab("Betweenness Centrality", bcPanel); + tabs.addTab("Cliques", cliquePanel); + tabs.addTab("Components", componentPanel); + // tabs.addTab("Graph", graphPanel); + + /******************************************************************* + * Layout + ******************************************************************/ + this.add(tabs); + panel.add(tabs, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.9, + GridBagConstraints.CENTER, GridBagConstraints.BOTH, + new Insets(0, 0, 0, 0), 0, 0)); + /*panel.add(alterQuestionMenu, new GridBagConstraints(0, 1, 1, 1, + 0.2, 0.1, GridBagConstraints.SOUTHEAST, + GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 6, 6)); + */ + + /******************************************************************* + * Event Handlers + ******************************************************************/ + /*alterQuestionMenu + .addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + alterQuestionMenu_actionPerformed(e); + } + });*/ + + EgoClient.frame.saveAlterSummary + .addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + saveAlterSummary_actionPerformed(e); + } + }); + + EgoClient.frame.saveTextSummary + .addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + saveTextSummary_actionPerformed(e); + } + }); + + EgoClient.frame.saveWeightedAdjacencyMatrix + .addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + saveWeightedAdjacencyMatrix_actionPerformed(e); + } + }); + + EgoClient.frame.saveAdjacencyMatrix + .addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + saveAdjacencyMatrix_actionPerformed(e); + } + }); + + EgoClient.frame.saveInterviewStatistics + .addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + saveInterviewStatistics_actionPerformed(e); + } + }); + + updateAll(); + } + } + //alterQuestionMenu.setActionCommand(s); + + /*********************************************************************** + * Check that there is at least one statable question, if not abort this + */ + if (!studyStatable) { + /******************************************************************* + * No Statable Questions + */ + this.setLayout(new GridLayout()); + this + .add(new JLabel( + "No questions with adjacent and non-adjacent selections found.")); + } + + EgoClient.frame.close + .addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + close_actionPerformed(e); + } + }); + } + + void updateAll() { + for (int i = 0; i < tabs.getTabCount(); i++) { + JComponent component = (JComponent) tabs.getComponentAt(i); + + if (component instanceof StatisticsArrayPanel) { + ((StatisticsArrayPanel) component).getTableModel().setStats( + stats); + ((StatisticsArrayPanel) component).getTableModel().update(); + } +// else if (component instanceof GraphPanel) { +// ((GraphPanel) component).init(this); +// } + } + } + + //TODO: We need to do alter pair stats when a question is selected, since we got rid of the drop down list. + /*void alterQuestionMenu_actionPerformed(ActionEvent e) { + if (!e.getActionCommand().equals("Initialization")) { + stats = EgoClient.interview + .generateStatistics((Question) alterQuestionMenu + .getSelectedItem()); + + updateAll(); + } + }*/ + + void saveAlterSummary_actionPerformed(ActionEvent e) { + String[] name = EgoClient.interview.getName(); + String filename = name[0] + "_" + name[1] + "_Alter_Summary"; + PrintWriter w = EgoClient.storage.newStatisticsPrintWriter( + "Alter Summary", "csv", filename); + + if (w != null) { + try { + stats.writeAlterArray(w); + } finally { + w.close(); + } + } + } + + void saveTextSummary_actionPerformed(ActionEvent e) { + String[] name = EgoClient.interview.getName(); + String filename = name[0] + "_" + name[1] + "_Text_Summary"; + PrintWriter w = EgoClient.storage.newStatisticsPrintWriter( + "Text Summary", "txt", filename); + + if (w != null) { + try { + stats.writeTextAnswers(w); + } finally { + w.close(); + } + } + } + + void saveAdjacencyMatrix_actionPerformed(ActionEvent e) { + String[] name = EgoClient.interview.getName(); + String filename = name[0] + "_" + name[1] + "_Adjacency_Matrix"; + PrintWriter w = EgoClient.storage.newStatisticsPrintWriter( + "Adjaceny Matrix", "csv", filename); + + if (w != null) { + try { + stats.writeAdjacencyArray(name[0] + " " + name[1], w, false); + } finally { + w.close(); + } + } + } + + void saveWeightedAdjacencyMatrix_actionPerformed(ActionEvent e) { + String[] name = EgoClient.interview.getName(); + String filename = name[0] + "_" + name[1] + + "_Weighted_Adjacency_Matrix"; + PrintWriter w = EgoClient.storage.newStatisticsPrintWriter( + "Adjaceny Matrix", "csv", filename); + + if (w != null) { + try { + stats.writeAdjacencyArray(name[0] + " " + name[1], w, true); + } finally { + w.close(); + } + } + } + + void close_actionPerformed(ActionEvent e) { + //System.out.println("Return"); + SourceSelectPanel.gotoPanel(false); + } + + void saveInterviewStatistics_actionPerformed(ActionEvent e) { + /*********************************************************************** + * Generate statistics for the first statable question + */ + Question q = EgoClient.study.getFirstStatableQuestion(); + + try { + if (q != null) { + EgoClient.storage.writeStatisticsFiles(stats, + EgoClient.interview.getName()); + } + } catch (FileCreateException ex) { + } + } +} + +/** + * $Log: StatisticsFrame.java,v $ Revision 1.1 2005/08/02 19:36:05 samag Initial + * checkin + * + * Revision 1.9 2004/04/11 00:24:48 admin Fixing headers + * + * Revision 1.8 2004/04/06 20:29:22 admin First pass as supporting interactive + * applet linking interviews + * + * Revision 1.7 2004/04/01 15:11:16 admin Completing Original UI work + * + */ + diff --git a/src/com/endlessloopsoftware/ego/client/statistics/models/BetweennessModel.java b/src/com/endlessloopsoftware/ego/client/statistics/models/BetweennessModel.java new file mode 100644 index 0000000..19e2d33 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/statistics/models/BetweennessModel.java @@ -0,0 +1,92 @@ +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2003

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: BetweennessModel.java,v 1.1 2005/08/02 19:36:11 samag Exp $ + * + * $Log: BetweennessModel.java,v $ + * Revision 1.1 2005/08/02 19:36:11 samag + * Initial checkin + * + * Revision 1.2 2004/02/26 21:19:18 admin + * adding jardescs + * + * Revision 1.1 2003/12/08 15:57:51 admin + * Modified to generate matrix files on survey completion or summarization + * Extracted statistics models + * + */ +package com.endlessloopsoftware.ego.client.statistics.models; + +import javax.swing.JTable; + +import com.endlessloopsoftware.ego.client.statistics.Statistics; + +public class BetweennessModel extends StatTableModel +{ + public BetweennessModel(Statistics stats) + { + super(stats); + } + + public int getColumnCount() + { + return (3); + } + + public int getRowCount() + { + return (stats.degreeArray.length); + } + + public Object getValueAt(int rowIndex, int columnIndex) + { + try + { + if (columnIndex == 0) + { + return (stats.alterList[rowIndex]); + } + else if (columnIndex == 1) + { + return (new Float(stats.betweennessArray[rowIndex])); + } + else + { + double big = stats.proximityMatrix.length - 1; + big *= big; + return (new Float(stats.betweennessArray[rowIndex] / big)); + } + } + catch (Exception ex) + { + ex.printStackTrace(); + return null; + } + } + + public String getColumnName(int column) + { + if (column == 0) + { + return ("Alters"); + } + else if (column == 1) + { + return ("Raw"); + } + else + { + return ("Normalized"); + } + } + + public int getResizeMode() + { + return JTable.AUTO_RESIZE_ALL_COLUMNS; + } +} \ No newline at end of file diff --git a/src/com/endlessloopsoftware/ego/client/statistics/models/CliqueModel.java b/src/com/endlessloopsoftware/ego/client/statistics/models/CliqueModel.java new file mode 100644 index 0000000..a133104 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/statistics/models/CliqueModel.java @@ -0,0 +1,111 @@ +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2003

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: CliqueModel.java,v 1.1 2005/08/02 19:36:11 samag Exp $ + * + * $Log: CliqueModel.java,v $ + * Revision 1.1 2005/08/02 19:36:11 samag + * Initial checkin + * + * Revision 1.2 2004/02/26 21:19:18 admin + * adding jardescs + * + * Revision 1.1 2003/12/08 15:57:51 admin + * Modified to generate matrix files on survey completion or summarization + * Extracted statistics models + * + */ +package com.endlessloopsoftware.ego.client.statistics.models; + +import java.util.Iterator; +import java.util.Stack; + +import javax.swing.JTable; + +import com.endlessloopsoftware.ego.client.statistics.Statistics; + +public class CliqueModel extends StatTableModel +{ + private Stack[] cliqueArray; + private int cliqueDepth; + + public CliqueModel(Statistics stats) + { + super(stats); + initModel(); + } + + private void initModel() + { + cliqueArray = new Stack[stats.cliqueSet.size()]; + stats.cliqueSet.toArray(cliqueArray); + + /* Determine deepest clique */ + Iterator it = stats.cliqueSet.iterator(); + int maxCount = 0; + + while (it.hasNext()) + { + Stack s = (Stack) it.next(); + + if (s.size() > maxCount) + { + maxCount = s.size(); + } + } + + cliqueDepth = maxCount; + } + + public void update() + { + initModel(); + this.fireTableStructureChanged(); + fireTableDataChanged(); + } + + public int getColumnCount() + { + return (cliqueArray.length); + } + + public int getRowCount() + { + return (cliqueDepth); + } + + public Object getValueAt(int rowIndex, int columnIndex) + { + if (rowIndex < cliqueArray[columnIndex].size()) + { + try + { + return (stats.alterList[((Integer) cliqueArray[columnIndex].get(rowIndex)).intValue()]); + } + catch (Exception ex) + { + ex.printStackTrace(); + return null; + } + } + else + { + return null; + } + } + + public String getColumnName(int column) + { + return ("Clique " + (column + 1)); + } + + public int getResizeMode() + { + return JTable.AUTO_RESIZE_OFF; + } +} diff --git a/src/com/endlessloopsoftware/ego/client/statistics/models/ClosenessModel.java b/src/com/endlessloopsoftware/ego/client/statistics/models/ClosenessModel.java new file mode 100644 index 0000000..0d5111d --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/statistics/models/ClosenessModel.java @@ -0,0 +1,102 @@ +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2003

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: ClosenessModel.java,v 1.1 2005/08/02 19:36:11 samag Exp $ + * + * $Log: ClosenessModel.java,v $ + * Revision 1.1 2005/08/02 19:36:11 samag + * Initial checkin + * + * Revision 1.3 2004/04/11 00:24:49 admin + * Fixing headers + * + * Revision 1.2 2004/03/10 14:32:39 admin + * Adding client library + * cleaning up code + * + * Revision 1.1 2003/12/08 15:57:51 admin + * Modified to generate matrix files on survey completion or summarization + * Extracted statistics models + * + */ +package com.endlessloopsoftware.ego.client.statistics.models; + +import javax.swing.JTable; + +import com.endlessloopsoftware.ego.client.statistics.Statistics; + +public class ClosenessModel extends StatTableModel +{ + public ClosenessModel(Statistics stats) + { + super(stats); + } + + public int getColumnCount() + { + return (stats.proximityMatrix.length + 3); + } + + public int getRowCount() + { + return (stats.proximityMatrix.length); + } + + public Object getValueAt(int rowIndex, int columnIndex) + { + try + { + if (columnIndex == 0) + { + return (stats.alterList[rowIndex]); + } + else if (columnIndex == (getColumnCount() - 2)) + { + return (new Integer(stats.farnessArray[rowIndex])); + } + else if (columnIndex == (getColumnCount() - 1)) + { + return (new Float(stats.closenessArray[rowIndex])); + } + else + { + return (new Integer(stats.proximityMatrix[rowIndex][columnIndex - 1])); + } + } + catch (Exception ex) + { + ex.printStackTrace(); + return null; + } + } + + public String getColumnName(int column) + { + if (column == 0) + { + return (" "); + } + else if (column == (getColumnCount() - 2)) + { + return ("Farness"); + } + else if (column == (getColumnCount() - 1)) + { + return ("nCloseness"); + } + else + { + return stats.alterList[column - 1]; + } + } + + public int getResizeMode() + { + return JTable.AUTO_RESIZE_OFF; + } +} diff --git a/src/com/endlessloopsoftware/ego/client/statistics/models/CompositionalStatsModel.java b/src/com/endlessloopsoftware/ego/client/statistics/models/CompositionalStatsModel.java new file mode 100644 index 0000000..62a303d --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/statistics/models/CompositionalStatsModel.java @@ -0,0 +1,108 @@ +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2003

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: CompositionalStatsModel.java,v 1.1 2005/08/02 19:36:12 samag Exp $ + * + * $Log: CompositionalStatsModel.java,v $ + * Revision 1.1 2005/08/02 19:36:12 samag + * Initial checkin + * + * Revision 1.2 2004/04/11 00:24:49 admin + * Fixing headers + * + * Revision 1.1 2003/12/08 15:57:51 admin + * Modified to generate matrix files on survey completion or summarization + * Extracted statistics models + * + */ +package com.endlessloopsoftware.ego.client.statistics.models; + +import java.util.Iterator; +import java.util.Set; + +import javax.swing.JTable; + +import com.endlessloopsoftware.ego.client.statistics.Statistics; + +public class CompositionalStatsModel extends StatTableModel +{ + private Integer[][] componentArray; + private int componentDepth; + + public CompositionalStatsModel(Statistics stats) + { + super(stats); + initModel(); + } + + private void initModel() + { + Iterator it = stats.componentSet.iterator(); + int index = 0; + int maxCount = 0; + + componentArray = new Integer[stats.componentSet.size()][]; + + while (it.hasNext()) + { + String[] names; + Set s = (Set) it.next(); + + componentArray[index] = new Integer[s.size()]; + s.toArray(componentArray[index]); + + if (s.size() > maxCount) + { + maxCount = s.size(); + } + + index++; + } + + componentDepth = maxCount; + } + + public void update() + { + initModel(); + this.fireTableStructureChanged(); + fireTableDataChanged(); + } + + public int getColumnCount() + { + return (componentArray.length); + } + + public int getRowCount() + { + return (componentDepth); + } + + public Object getValueAt(int rowIndex, int columnIndex) + { + if (rowIndex < componentArray[columnIndex].length) + { + return (stats.alterList[componentArray[columnIndex][rowIndex].intValue()]); + } + else + { + return null; + } + } + + public String getColumnName(int column) + { + return ("Component " + (column + 1)); + } + + public int getResizeMode() + { + return JTable.AUTO_RESIZE_OFF; + } +} diff --git a/src/com/endlessloopsoftware/ego/client/statistics/models/DegreeModel.java b/src/com/endlessloopsoftware/ego/client/statistics/models/DegreeModel.java new file mode 100644 index 0000000..6368c60 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/statistics/models/DegreeModel.java @@ -0,0 +1,90 @@ +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2003

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: DegreeModel.java,v 1.1 2005/08/02 19:36:12 samag Exp $ + * + * $Log: DegreeModel.java,v $ + * Revision 1.1 2005/08/02 19:36:12 samag + * Initial checkin + * + * Revision 1.2 2004/04/11 00:24:49 admin + * Fixing headers + * + * Revision 1.1 2003/12/08 15:57:51 admin + * Modified to generate matrix files on survey completion or summarization + * Extracted statistics models + * + */ +package com.endlessloopsoftware.ego.client.statistics.models; + +import javax.swing.JTable; + +import com.endlessloopsoftware.ego.client.statistics.Statistics; + +public class DegreeModel extends StatTableModel +{ + public DegreeModel(Statistics stats) + { + super(stats); + } + + public int getColumnCount() + { + return (3); + } + + public int getRowCount() + { + return (stats.degreeArray.length); + } + + public Object getValueAt(int rowIndex, int columnIndex) + { + try + { + if (columnIndex == 0) + { + return (stats.alterList[rowIndex]); + } + else if (columnIndex == 1) + { + return (new Integer(stats.degreeArray[rowIndex])); + } + else + { + return (new Float(stats.degreeArray[rowIndex] / ((float) (stats.proximityMatrix.length - 1)))); + } + } + catch (Exception ex) + { + ex.printStackTrace(); + return null; + } + } + + public String getColumnName(int column) + { + if (column == 0) + { + return ("Alter"); + } + else if (column == 1) + { + return ("Raw"); + } + else + { + return ("Normalized"); + } + } + + public int getResizeMode() + { + return JTable.AUTO_RESIZE_ALL_COLUMNS; + } +} diff --git a/src/com/endlessloopsoftware/ego/client/statistics/models/InterviewSummaryModel.java b/src/com/endlessloopsoftware/ego/client/statistics/models/InterviewSummaryModel.java new file mode 100644 index 0000000..e675086 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/statistics/models/InterviewSummaryModel.java @@ -0,0 +1,135 @@ +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2003

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * + * $Id: InterviewSummaryModel.java,v 1.1 2005/08/02 19:36:12 samag Exp $ + * + * $Log: InterviewSummaryModel.java,v $ + * Revision 1.1 2005/08/02 19:36:12 samag + * Initial checkin + * + * Revision 1.3 2004/04/11 00:24:49 admin + * Fixing headers + * + * Revision 1.2 2004/03/10 14:32:40 admin + * Adding client library + * cleaning up code + * + * Revision 1.1 2003/12/08 15:57:51 admin + * Modified to generate matrix files on survey completion or summarization + * Extracted statistics models + * + */ +package com.endlessloopsoftware.ego.client.statistics.models; + +import javax.swing.JTable; + +import com.endlessloopsoftware.ego.client.statistics.Statistics; + +public class InterviewSummaryModel extends StatTableModel +{ + public InterviewSummaryModel(Statistics stats) + { + super(stats); + } + + public int getColumnCount() + { + if (stats.adjacencyMatrix.length > 0) + { + return 3; + } + else + { + return 1; + } + } + + public Object getValueAt(int rowIndex, int columnIndex) + { + switch (columnIndex) + { + case 0 : + switch (rowIndex) + { + case 0 : + return ("Degree Centrality Maximum"); + case 1 : + return ("Closeness Centrality Minimum"); + case 2 : + return ("Betweenness Centrality Maximum"); + case 3 : + return ("Number of Cliques"); + case 4 : + return ("Number of Components"); + default : + return (null); + } + + case 1 : + switch (rowIndex) + { + case 0 : + return (stats.mostCentralDegreeAlterName); + case 1 : + return (stats.mostCentralClosenessAlterName); + case 2 : + return (stats.mostCentralBetweenAlterName); + case 3 : + return (new Integer(stats.cliqueSet.size())); + case 4 : + return (new Integer(stats.componentSet.size())); + default : + return (null); + } + + case 2 : + switch (rowIndex) + { + case 0 : + return (new Integer(stats.mostCentralDegreeAlterValue)); + case 1 : + return (new Float(stats.mostCentralClosenessAlterValue)); + case 2 : + return (new Float(stats.mostCentralBetweenAlterValue)); + default : + return (null); + } + + default : + return (null); + } + } + + public int getRowCount() + { + if (stats.adjacencyMatrix.length > 0) + { + return 5; + } + else + { + return 0; + } + } + + public String getColumnName(int column) + { + if (stats.adjacencyMatrix.length > 0) + { + return null; + } + else + { + return ("No Structural Measures question specified in study"); + } + } + + public int getResizeMode() + { + return JTable.AUTO_RESIZE_ALL_COLUMNS; + } +} diff --git a/src/com/endlessloopsoftware/ego/client/statistics/models/QSummaryModel.java b/src/com/endlessloopsoftware/ego/client/statistics/models/QSummaryModel.java new file mode 100644 index 0000000..e2d5d9e --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/statistics/models/QSummaryModel.java @@ -0,0 +1,140 @@ +/** + *

Title: Egocentric Network Researcher

+ *

Description: Configuration Utilities for an Egocentric network study

+ *

Copyright: Copyright (c) 2003

+ *

Company: Endless Loop Software

+ * @author Peter C. Schoaff + * @version 1.0 + * + * $Id: QSummaryModel.java,v 1.1 2005/08/02 19:36:12 samag Exp $ + * + * $Log: QSummaryModel.java,v $ + * Revision 1.1 2005/08/02 19:36:12 samag + * Initial checkin + * + * Revision 1.2 2004/04/11 00:24:49 admin + * Fixing headers + * + * Revision 1.1 2003/12/08 15:57:51 admin + * Modified to generate matrix files on survey completion or summarization + * Extracted statistics models + * + */ +package com.endlessloopsoftware.ego.client.statistics.models; + +import javax.swing.JTable; + +import com.endlessloopsoftware.ego.Question; +import com.endlessloopsoftware.ego.client.statistics.Statistics; + +public class QSummaryModel extends StatTableModel +{ + private int answerDepth; + + public QSummaryModel(Statistics stats) + { + super(stats); + initModel(); + } + + private void initModel() + { + int maxCount = 0; + for (int i = 0; i < stats.alterStatArray.length; i++) + { + if (stats.alterStatArray[i].answerTotals.length > maxCount) + { + maxCount = stats.alterStatArray[i].answerTotals.length; + } + } + + answerDepth = maxCount; + } + + public void update() + { + initModel(); + this.fireTableStructureChanged(); + fireTableDataChanged(); + } + + public int getColumnCount() + { + return (answerDepth + 1); + } + + public int getRowCount() + { + return (stats.alterStatArray.length); + } + + public Object getValueAt(int rowIndex, int columnIndex) + { + if (columnIndex == 0) + { + return (stats.alterStatArray[rowIndex].qTitle); + } + else if (columnIndex <= stats.alterStatArray[rowIndex].answerTotals.length) + { + if ((stats.alterStatArray[rowIndex].answerType == Question.NUMERICAL) && (columnIndex == 1)) + { + return ( + "Average: " + + stats.alterStatArray[rowIndex].answerTotals[0] / stats.alterStatArray[rowIndex].answerCount); + } + else if (stats.alterStatArray[rowIndex].answerType == Question.CATEGORICAL) + { + String s = null; + try + { + if (stats.alterStatArray[rowIndex].answerCount == 0) + { + s = stats.alterStatArray[rowIndex].answerText[columnIndex - 1] + ": n/a"; + } + else + { + s = + stats.alterStatArray[rowIndex].answerText[columnIndex + - 1] + + ": " + + ((stats.alterStatArray[rowIndex].answerTotals[columnIndex - 1] * 100) + / stats.alterStatArray[rowIndex].answerCount) + + "%"; + } + } + catch (Exception ex) + { + s = "Statistics Generation Error"; + System.err.println("Error in StatTableModel::getValueAt; " + ex); + } + + return s; + } + else + { + return null; + } + } + else + { + return null; + } + } + + public String getColumnName(int column) + { + if (column == 0) + { + return ("Question"); + } + else + { + return ("Answer " + column); + } + } + + public int getResizeMode() + { + return JTable.AUTO_RESIZE_OFF; + } +} diff --git a/src/com/endlessloopsoftware/ego/client/statistics/models/StatTableModel.java b/src/com/endlessloopsoftware/ego/client/statistics/models/StatTableModel.java new file mode 100644 index 0000000..2b1ccb4 --- /dev/null +++ b/src/com/endlessloopsoftware/ego/client/statistics/models/StatTableModel.java @@ -0,0 +1,49 @@ +package com.endlessloopsoftware.ego.client.statistics.models; + +/** + *

Endless Loop Software Abstract Statistics Table Model

+ *

Copyright: Copyright (c) 2003

+ *

Company: Endless Loop Software, Inc.

+ * + * @author Peter C. Schoaff + * @version $Revision: 1.1 $ + * + * $Date: 2005/08/02 19:36:11 $ + * $Id: StatTableModel.java,v 1.1 2005/08/02 19:36:11 samag Exp $ + */ + +import javax.swing.JTable; +import javax.swing.table.AbstractTableModel; + +import com.endlessloopsoftware.ego.client.statistics.Statistics; + +public abstract class StatTableModel extends AbstractTableModel +{ + public Statistics stats; + + public StatTableModel(Statistics stats) + { + this.stats = stats; + } + + public StatTableModel() {} + + public int getResizeMode() + { + return JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS; + } + + public void update() + { + this.fireTableStructureChanged(); + fireTableDataChanged(); + } + + /** + * @param stats The stats to set. + */ + public void setStats(Statistics stats) + { + this.stats = stats; + } +} diff --git a/src/org/egonet/exceptions/CorruptedInterviewException.java b/src/org/egonet/exceptions/CorruptedInterviewException.java new file mode 100644 index 0000000..67d263e --- /dev/null +++ b/src/org/egonet/exceptions/CorruptedInterviewException.java @@ -0,0 +1,5 @@ +package org.egonet.exceptions; + +public class CorruptedInterviewException extends org.egonet.exceptions.EgonetException +{ +} diff --git a/src/org/egonet/exceptions/DuplicateQuestionException.java b/src/org/egonet/exceptions/DuplicateQuestionException.java new file mode 100644 index 0000000..123b261 --- /dev/null +++ b/src/org/egonet/exceptions/DuplicateQuestionException.java @@ -0,0 +1,5 @@ +package org.egonet.exceptions; + +public class DuplicateQuestionException extends org.egonet.exceptions.EgonetException +{ +} diff --git a/src/org/egonet/exceptions/EgonetException.java b/src/org/egonet/exceptions/EgonetException.java new file mode 100644 index 0000000..e74566c --- /dev/null +++ b/src/org/egonet/exceptions/EgonetException.java @@ -0,0 +1,24 @@ +package org.egonet.exceptions; + +public class EgonetException extends Exception +{ + public EgonetException() + { + super(); + } + + public EgonetException(String s) + { + super(s); + } + + public EgonetException(Throwable t) + { + super(t); + } + + public EgonetException(String s, Throwable t) + { + super(s,t); + } +} diff --git a/src/org/egonet/exceptions/FileMismatchException.java b/src/org/egonet/exceptions/FileMismatchException.java new file mode 100644 index 0000000..1bf6b91 --- /dev/null +++ b/src/org/egonet/exceptions/FileMismatchException.java @@ -0,0 +1,5 @@ +package org.egonet.exceptions; + +public class FileMismatchException extends org.egonet.exceptions.EgonetException +{ +} diff --git a/src/org/egonet/exceptions/MalformedQuestionException.java b/src/org/egonet/exceptions/MalformedQuestionException.java new file mode 100644 index 0000000..e6c4802 --- /dev/null +++ b/src/org/egonet/exceptions/MalformedQuestionException.java @@ -0,0 +1,5 @@ +package org.egonet.exceptions; + +public class MalformedQuestionException extends org.egonet.exceptions.EgonetException +{ +} diff --git a/src/org/egonet/exceptions/MissingPairException.java b/src/org/egonet/exceptions/MissingPairException.java new file mode 100644 index 0000000..537e434 --- /dev/null +++ b/src/org/egonet/exceptions/MissingPairException.java @@ -0,0 +1,5 @@ +package org.egonet.exceptions; + +public class MissingPairException extends org.egonet.exceptions.EgonetException +{ +} \ No newline at end of file diff --git a/src/org/egonet/util/listbuilder/ListBuilder.java b/src/org/egonet/util/listbuilder/ListBuilder.java new file mode 100644 index 0000000..a2cf882 --- /dev/null +++ b/src/org/egonet/util/listbuilder/ListBuilder.java @@ -0,0 +1,737 @@ +package org.egonet.util.listbuilder; + +import javax.swing.*; +import javax.swing.event.*; + +import java.awt.event.*; + +import com.jgoodies.forms.layout.*; +import com.jgoodies.forms.builder.*; + +import java.util.Observable; +import java.util.Observer; +import java.util.Iterator; +import java.util.Map; + +public class ListBuilder extends JPanel implements Observer +{ + /** + * Contains a list of Selection items that can be observed as the contents of + * the list change. + */ + public ObservableList elementList; + + /** + * When true, a form is displayed where entries in the list may be added or + * deleted. When false, only the list of options is displayed. + */ + private boolean editable = true; + + /** + * When namelist is set to true, you get a "First Name" and a "Last Name" + * field to fill in. Normally, you just get a "Name" field. + */ + private boolean nameList = false; + + /** + * If adjacent is active, the selected item of the list may be toggled to be + * adjacent via a 2-state button. Toggled entries in the list, selected or + * not, will be highlighted with a different color. Adjacent "Selection" + * items will have their Adjacent field set to true IFF they have been + * selected by the 2-state button being toggled ON. + */ + private boolean adjacencyActive = false; + + /** + * If preset lists are active, you may choose a pre-set list of options + * (States, Gender, Yes/No, i.e. NON-custom). When false, custom is the only + * option and no list is shown. + */ + private boolean presetListsActive = false; + + /** + * When true, users will be able to directly set Selection values in the same + * manner they can enter string values. + */ + private boolean letUserPickValues = false; + + /** + * When max size is selected, users will not be able to add more items once + * the number of items has reached max size. The delete button will still be + * available, however, so they may delete items and then they will again be + * allowed to add items up to the max size of the list. + */ + private int maxSize = 10; + + private String elementName = ""; + + private String title = ""; + + private String description = ""; + + private JList jList = null;; + + private JScrollPane jScrollPane = null; + + private JPanel panelTopHalf = null; + + private JPanel panelTopRight = null; + + private JPanel panelButtons = null; + + private JButton buttonAdjacency = null; + + private JButton buttonDelete = null; + + private JTextArea labelDescription = null; + + private JTextField firstName, lastName, itemName, value; + + private CellConstraints constraints = new CellConstraints(); + + private static final Map presets = ListBuilderPresets.getPresets(); + + private static final String CHOOSE_PRESET_INSTRUCTION = "Choose from preset options"; + + public ListBuilder() + { + super(); + elementList = new ObservableList(); + build(); + addListObserver(this); + } + + public ListBuilder(boolean isPresetValuesActive) + { + super(); + elementList = new ObservableList(); + build(); + addListObserver(this); + } + + public ListBuilder(Selection[] selections) + { + super(); + elementList = new ObservableList(selections); + build(); + addListObserver(this); + } + + public void addListObserver(Observer ob) + { + elementList.addObserver(ob); + } + + /** + * item has been updated. update our JList. + */ + public void update(Observable o, Object arg) + { + jList.setListData(elementList.toArray()); + jList.revalidate(); + jList.repaint(); + } + + private void build() + { + // purge anything old + removeAll(); + +// System.out.println("Building List builder..."); + jList = new JList(); + jList.setCellRenderer(new SelectionListCellRenderer()); + jList.setListData(elementList.toArray()); + jScrollPane = new JScrollPane(jList); + + + // is NOT editable, we only use the main panel i.e. "this" + if (!editable) + { + FormLayout layout = new FormLayout("2dlu, fill:min(pref;200dlu):grow, 2dlu", + "2dlu, fill:pref:grow, 2dlu"); + setLayout(layout); + add(jScrollPane, constraints.xy(2, 2)); + return; + } + + // is editable, so we start using subpanels for layouts + FormLayout mainLayout = new FormLayout("2dlu, fill:min(pref;300dlu):grow, 2dlu", + "2dlu, fill:pref:grow, 2dlu, fill:max(pref;2dlu), 2dlu"); + setLayout(mainLayout); + + // combine top and bottom panels + add(buildTop(), constraints.xy(2, 2)); + add(buildBottom(), constraints.xy(2, 4)); + + // when the list selection is changed + jList.addListSelectionListener(new ListSelectionListener() + { + public void valueChanged(ListSelectionEvent e) + { + if (e.getValueIsAdjusting()) + { + clearTextFields(); + return; + } + + JList list = (JList) e.getSource(); + Selection selection = (Selection) list.getSelectedValue(); + if (selection == null) + return; + + // if this is a name list, attempt to parse out the pieces, + // otherwise leave them empty + String firstStr = ""; + String lastStr = ""; + if (isNameList()) + { + String[] parts = selection.getString().split(", "); + if (parts.length != 2) + { + // TODO: warn person + return; + } else + { + lastStr = parts[0]; + firstStr = parts[1]; + } + } + + setTextFields(firstStr, lastStr, selection.getString(), "" + selection.getValue()); + } + }); + } + + private JComponent buildTop() + { +// System.out.println("Building Top...."); + // top half panel + panelTopHalf = new JPanel(); + FormLayout topHalfLayout = new FormLayout( + "2dlu, fill:145dlu:grow, 2dlu, fill:145dlu:grow, 2dlu", + "2dlu, fill:min(pref;150dlu):grow, 2dlu"); + panelTopHalf.setLayout(topHalfLayout); + panelTopHalf.add(jScrollPane, constraints.xy(2, 2)); + panelTopHalf.add(buildTopRight(), constraints.xy(4, 2)); + return panelTopHalf; + } + + private JComponent buildTopRight() + { +// System.out.println("Building Top Right....:"+ isPresetListsActive()); + panelTopRight = new JPanel(); + FormLayout panelTopRightLayout = new FormLayout("2dlu, fill:75dlu:grow, 2dlu", + "2dlu, fill:20dlu:grow, 2dlu, fill:pref:grow, 2dlu, pref:grow, 2dlu, fill:pref:grow, 2dlu"); + panelTopRight.setLayout(panelTopRightLayout); + + JLabel labelTitle = new JLabel(title); + panelTopRight.add(labelTitle, constraints.xy(2, 2)); + + labelDescription = new JTextArea(description); + labelDescription.setEditable(false); + labelDescription.setLineWrap(true); + labelDescription.setWrapStyleWord(true); + JScrollPane sp = new JScrollPane(labelDescription); + panelTopRight.add(sp, constraints.xy(2, 4)); + + if (isPresetListsActive()) + { + // System.out.println("Presets are supposed to be active"); + final JComboBox comboPresets = new JComboBox(); + comboPresets.addItem(CHOOSE_PRESET_INSTRUCTION); + for (String presetName : presets.keySet()) + comboPresets.addItem(presetName); + panelTopRight.add(comboPresets, constraints.xy(2, 6)); + comboPresets.addItemListener(new ItemListener() + { + public void itemStateChanged(ItemEvent e) + { + if (e.getStateChange() == ItemEvent.SELECTED) + { + Object item = e.getItem(); + Selection[] options = presets.get(item); + if (options != null) + { + setListSelections(options); + comboPresets.setSelectedIndex(0); + clearTextFields(); + } + } + } + }); + } + + panelTopRight.add(buildInputPanel(), constraints.xy(2, 8)); + + return panelTopRight; + } + + private JComponent buildInputPanel() + { + final String colspec = "2dlu, fill:pref:grow, 2dlu"; + + FormLayout inputLayout = new FormLayout(colspec); + DefaultFormBuilder formBuilder = new DefaultFormBuilder(inputLayout); + + formBuilder.append(""); + formBuilder.nextRow(); + formBuilder.setLeadingColumnOffset(1); + + firstName = new JTextField(); + lastName = new JTextField(); + itemName = new JTextField(); + value = new JTextField(); + + firstName.addKeyListener(new KeyListener() + { + public void keyTyped(KeyEvent keyEvent) + { + } + + public void keyPressed(KeyEvent keyEvent) + { + } + + public void keyReleased(KeyEvent keyEvent) + { + saveDataForSelectionOfList(keyEvent); + if (KeyEvent.getKeyText(keyEvent.getKeyCode()).equals("Enter")) + lastName.grabFocus(); + } + }); + + lastName.addKeyListener(new KeyListener() + { + public void keyTyped(KeyEvent keyEvent) + { + } + + public void keyPressed(KeyEvent keyEvent) + { + } + + public void keyReleased(KeyEvent keyEvent) + { + saveDataForSelectionOfList(keyEvent); + if (KeyEvent.getKeyText(keyEvent.getKeyCode()).equals("Enter") && isLetUserPickValues()) + value.grabFocus(); + else if(KeyEvent.getKeyText(keyEvent.getKeyCode()).equals("Enter")) + firstName.grabFocus(); + } + }); + + itemName.addKeyListener(new KeyListener() + { + public void keyTyped(KeyEvent keyEvent) + { + } + + public void keyPressed(KeyEvent keyEvent) + { + } + + public void keyReleased(KeyEvent keyEvent) + { + saveDataForSelectionOfList(keyEvent); + if (KeyEvent.getKeyText(keyEvent.getKeyCode()).equals("Enter") && isLetUserPickValues()) + value.grabFocus(); + } + }); + + value.addKeyListener(new KeyListener() + { + public void keyTyped(KeyEvent keyEvent) + { + } + + public void keyPressed(KeyEvent keyEvent) + { + } + + public void keyReleased(KeyEvent keyEvent) + { + saveDataForSelectionOfList(keyEvent); + + // if you typed on value, blank the selection + if (KeyEvent.getKeyText(keyEvent.getKeyCode()).equals("Enter")) + { + if (isNameList()) + firstName.grabFocus(); + else + itemName.grabFocus(); + } + } + }); + + // couple different configurations here + if (nameList) + { + formBuilder.append("First Name: ", firstName, true); + formBuilder.append("Last Name: ", lastName, true); + } else + { + formBuilder.append("Item Name: ", itemName, true); + } + + if (letUserPickValues) + formBuilder.append("Value: ", value, true); + + return formBuilder.getPanel(); + } + + /** + * Called when the text fields have keys typed in them. This method does not + * handle FOCUS at all -- it only stores data. If you'd like the selected + * item to change, or the focus to move, upon saving data, do it outside of + * this method in your handler. + * + * @param keyEvent + */ + private void saveDataForSelectionOfList(KeyEvent keyEvent) + { + Object selectionObject = jList.getSelectedValue(); + boolean itemSelectedFromList = selectionObject != null + && selectionObject instanceof Selection; + boolean enterPressed = KeyEvent.getKeyText(keyEvent.getKeyCode()).equals("Enter"); + boolean shouldBlank = (keyEvent.getSource() == value) + || (keyEvent.getSource() == lastName && isNameList() && !isLetUserPickValues()) + || (keyEvent.getSource() == itemName && !isNameList() && !isLetUserPickValues()); + + if (itemSelectedFromList) + { + Selection selection = (Selection) selectionObject; + + // OLD item + try + { + convertTextFieldsToSelection(selection); + } catch (Exception ex) + { + JOptionPane.showMessageDialog(this, "Could not parse your integer value", + "Value problem", JOptionPane.ERROR_MESSAGE); + value.setText(selection.getValue() + ""); + return; + } + + if (enterPressed && shouldBlank) + { + // someone HAS pressed enter + jList.clearSelection(); + clearTextFields(); + } + jList.updateUI(); + } else if (!itemSelectedFromList) + { + // NEW item -- don't do anything until they hit enter on the LAST field + if (enterPressed && shouldBlank) + { + Selection selection = new Selection(); + try + { + convertTextFieldsToSelection(selection); + } catch (Exception ex) + { + JOptionPane.showMessageDialog(this, "Could not parse your integer value", + "Value problem", JOptionPane.ERROR_MESSAGE); + return; + } + elementList.add(selection); + + // someone HAS pressed enter + jList.clearSelection(); + clearTextFields(); + } + } + } + + private void convertTextFieldsToSelection(Selection selection) throws NumberFormatException + { + if (isLetUserPickValues() && !value.getText().equals("") && !value.getText().equals("-")) + { + int intVal = Integer.parseInt(value.getText()); + selection.setValue(intVal); + } + + if (isNameList()) + { + //selection.setString(lastName.getText() + ", " + firstName.getText()); + selection.setString(firstName.getText() + " " + lastName.getText()); + } else + { + selection.setString(itemName.getText()); + } + } + + private void clearTextFields() + { + setTextFields("", "", "", ""); + } + + private void setTextFields(String first, String last, String item, String values) + { + firstName.setText(first); + lastName.setText(last); + itemName.setText(item); + value.setText(values); + } + + private JComponent buildBottom() + { + // button panel + panelButtons = new JPanel(); + + buttonDelete = new JButton("Delete selected item"); + buttonDelete.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent actionEvent) + { + Object[] selections = jList.getSelectedValues(); + for (Object o : selections) + elementList.remove(o); + jList.clearSelection(); + } + }); + panelButtons.add(buttonDelete); + + if (isAdjacencyActive()) + { + buttonAdjacency = new JButton("Mark selected item adjacent"); + buttonAdjacency.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent actionEvent) + { + Object[] selections = jList.getSelectedValues(); + for (Object o : selections) + { + if (o instanceof Selection) + { + ((Selection) o).setAdjacent(!((Selection) o).isAdjacent()); + } + } + jList.revalidate(); + jList.repaint(); + } + }); + panelButtons.add(buttonAdjacency); + } + + return panelButtons; + } + + public static void main(String[] args) + { + ListBuilder listBuilder = new ListBuilder(); + + listBuilder.setName("name field"); + listBuilder.setTitle("title field"); + + String s = ""; + for (int i = 0; i < 20; i++) + s += "Lorem ipsum dolor. "; + + listBuilder.setDescription(s); + + listBuilder.setEditable(true); + listBuilder.setLetUserPickValues(true); + listBuilder.setNameList(false); + listBuilder.setAdjacencyActive(true); + + JFrame frame = new JFrame(); + frame.add(listBuilder); + + frame.setSize(400, 500); + frame.pack(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setVisible(true); + } + + public boolean isEditable() + { + return editable; + } + + public void setEditable(boolean editable) + { + this.editable = editable; + build(); + } + + public ObservableList getElementList() + { + return elementList; + } + + public void setElementList(ObservableList elementList) + { + this.elementList = elementList; + } + + public String getElementName() + { + return elementName; + } + + public void setElementName(String elementName) + { + this.elementName = elementName; + } + + public int getMaxListSize() + { + return maxSize; + } + + public void setMaxListSize(int maxSize) + { + setMaxSize(maxSize); + } + + public int getMaxSize() + { + return maxSize; + } + + public void setMaxSize(int maxSize) + { + this.maxSize = maxSize; + build(); + } + + public Selection[] getSelections() + { + int ct = 0; + for (Object o : elementList.toArray()) + if (o.getClass().equals(Selection.class)) + ct++; + + Selection[] arr = new Selection[ct]; + int i = 0; + for (Object o : elementList.toArray()) + { + if (o.getClass().equals(Selection.class)) + { + arr[i] = (Selection) o; + i++; + } + } + + return arr; + } + + public void setSelections(Selection[] selections) + { + elementList.removeAll(); + elementList.addAll(selections); + } + + public Selection[] getListSelections() + { + return getSelections(); + } + + public void setListSelections(Selection[] selections) + { + setSelections(selections); + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public String getDescription() + { + return description; + } + + public void setDescription(String description) + { + this.description = description; + } + + public boolean isNameList() + { + return nameList; + } + + public void setNameList(boolean nameList) + { + this.nameList = nameList; + build(); + } + + public boolean isAdjacencyActive() + { + return adjacencyActive; + } + + public void setAdjacencyActive(boolean adjacencyActive) + { + this.adjacencyActive = adjacencyActive; + build(); + } + + public boolean isPresetListsActive() + { + return presetListsActive; + } + + public void setPresetListsActive(boolean presetListsActive) + { + this.presetListsActive = presetListsActive; + System.out.println("Set presets on"); + build(); + System.out.println("Completed new build"); + } + + public String[] getListStrings() + { + int len = elementList.size(); + String[] listStrings = new String[len]; + int i = 0; + for (Iterator iterator = elementList.iterator(); iterator.hasNext();) + { + Object element = iterator.next(); + if (!(element instanceof Selection)) + element = new Selection(); + + Selection selection = (Selection) element; + listStrings[i++] = selection.getString(); + } + + return listStrings; + } + + public void setListStrings(String[] listStrings) + { + elementList.removeAll(); + for (int i = 0; i < listStrings.length; i++) + { + elementList.add(new Selection(listStrings[i], i, i, false)); + } + } + + public boolean isLetUserPickValues() + { + return letUserPickValues; + } + + public void setLetUserPickValues(boolean letUserPickValues) + { + this.letUserPickValues = letUserPickValues; + build(); + } + + public void requestFocusOnFirstVisibleComponent() + { + if(this.isNameList()) + itemName.requestFocusInWindow(); + else + firstName.requestFocusInWindow(); + } +} diff --git a/src/org/egonet/util/listbuilder/ListBuilderPresets.java b/src/org/egonet/util/listbuilder/ListBuilderPresets.java new file mode 100644 index 0000000..141271c --- /dev/null +++ b/src/org/egonet/util/listbuilder/ListBuilderPresets.java @@ -0,0 +1,52 @@ +package org.egonet.util.listbuilder; + +import java.util.Map; +import java.util.HashMap; + +public class ListBuilderPresets +{ + public static Map getPresets() + { + Map ret = new HashMap(); + ret.put("Yes/No", YES_NO); + ret.put("Gender", GENDER); + ret.put("States", STATES); + + return ret; + } + + public static final Selection[] YES_NO = + { new Selection("Yes", 1, 0, false), new Selection("No", 0, 1, false), }; + + public static final Selection[] GENDER = + { new Selection("Male", 1, 0, false), new Selection("Female", 0, 1, false), }; + + public static final Selection[] STATES = + { new Selection("Alabama", 49, 0, false), new Selection("Alaska", 48, 1, false), + new Selection("Arizona", 47, 2, false), new Selection("Arkansas", 46, 3, false), + new Selection("California", 45, 4, false), new Selection("Colorado", 44, 5, false), + new Selection("Connecticut", 43, 6, false), new Selection("Delaware", 42, 7, false), + new Selection("Florida", 41, 8, false), new Selection("Georgia", 40, 9, false), + new Selection("Hawaii", 39, 10, false), new Selection("Idaho", 38, 11, false), + new Selection("Illinois", 37, 12, false), new Selection("Indiana", 36, 13, false), + new Selection("Iowa", 35, 14, false), new Selection("Kansas", 34, 15, false), + new Selection("Kentucky", 33, 16, false), new Selection("Louisiana", 32, 17, false), + new Selection("Maine", 31, 18, false), new Selection("Maryland", 30, 19, false), + new Selection("Massachusetts", 29, 20, false), new Selection("Michigan", 28, 21, false), + new Selection("Minnesota", 27, 22, false), new Selection("Mississippi", 26, 23, false), + new Selection("Missouri", 25, 24, false), new Selection("Montana", 24, 25, false), + new Selection("Nebraska", 23, 26, false), new Selection("Nevada", 22, 27, false), + new Selection("New Hampshire", 21, 28, false), new Selection("New Jersey", 20, 29, false), + new Selection("New Mexico", 19, 30, false), new Selection("New York", 18, 31, false), + new Selection("North Carolina", 17, 32, false), + new Selection("North Dakota", 16, 33, false), new Selection("Ohio", 15, 34, false), + new Selection("Oklahoma", 14, 35, false), new Selection("Oregon", 13, 36, false), + new Selection("Pennsylvania", 12, 37, false), + new Selection("Rhode Island", 11, 38, false), + new Selection("South Carolina", 10, 39, false), + new Selection("South Dakota", 9, 40, false), new Selection("Tennessee", 8, 41, false), + new Selection("Texas", 7, 42, false), new Selection("Utah", 6, 43, false), + new Selection("Vermont", 5, 44, false), new Selection("Virginia", 4, 45, false), + new Selection("Washington", 3, 46, false), new Selection("West Virginia", 2, 47, false), + new Selection("Wisconsin", 1, 48, false), new Selection("Wyoming", 0, 49, false), }; +} diff --git a/src/org/egonet/util/listbuilder/ObservableList.java b/src/org/egonet/util/listbuilder/ObservableList.java new file mode 100644 index 0000000..9cd8cc4 --- /dev/null +++ b/src/org/egonet/util/listbuilder/ObservableList.java @@ -0,0 +1,77 @@ +package org.egonet.util.listbuilder; + +import java.util.Arrays; +import java.util.Observable; +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Collections; + +public class ObservableList extends Observable +{ + + private List list; + + public ObservableList() + { + list = Collections.synchronizedList(new ArrayList()); + } + + public ObservableList(T [] array) + { + list = Collections.synchronizedList(new ArrayList(Arrays.asList(array))); + } + + public void add(T o) + { + list.add(o); + super.setChanged(); super.notifyObservers(this); + } + + public void remove(Object o) + { + list.remove(o); + super.setChanged(); super.notifyObservers(this); + } + + public void addAll(T [] o) + { + list.addAll(java.util.Arrays.asList(o)); + super.setChanged(); super.notifyObservers(this); + } + + public Iterator iterator() + { + return list.iterator(); + } + + public T remove(int i) + { + System.out.println("Removing element " + i); + T obj = list.remove(i); + super.setChanged(); super.notifyObservers(this); + + return obj; + } + + public void removeAll() + { + list.clear(); + super.setChanged(); super.notifyObservers(this); + } + + public int size() + { + return list.size(); + } + + public Object[] toArray() + { + return list.toArray(); + } + + public T get(int index) + { + return list.get(index); + } +} diff --git a/src/org/egonet/util/listbuilder/OldListTester.java b/src/org/egonet/util/listbuilder/OldListTester.java new file mode 100644 index 0000000..9265706 --- /dev/null +++ b/src/org/egonet/util/listbuilder/OldListTester.java @@ -0,0 +1,70 @@ +package org.egonet.util.listbuilder; + +import com.endlessloopsoftware.elsutils.listbuilder.ListBuilder; +import com.endlessloopsoftware.elsutils.listbuilder.ObservableList; +import com.endlessloopsoftware.elsutils.listbuilder.Selection; +import javax.swing.JFrame; +import java.util.Observable; +import java.util.Observer; +import java.util.Iterator; + +public class OldListTester implements Observer +{ + + /** + * @param args + */ + public static void main(String[] args) + { + System.out.println("Before"); + new OldListTester(); + System.out.println("After"); + } + + private ListBuilder listBuilder; + public OldListTester() + { + super(); + this.listBuilder = new ListBuilder(); + this.listBuilder.addListObserver(this); + this.listBuilder.setName("name field"); + this.listBuilder.setTitle("title field"); + + String s = ""; + for(int i = 0; i < 10; i++) + s+= "Lorem ipsum dolor. "; + + this.listBuilder.setDescription(s); + this.listBuilder.setEditable(false); + + + JFrame frame = new JFrame(); + frame.add(listBuilder); + frame.setSize(400,500); + frame.setVisible(true); + } + + public void update(Observable o, Object arg) + { + if(arg.getClass().equals(ObservableList.class)) + { + ObservableList lb = (ObservableList)arg; + System.out.println("o=" + o +", arg=" + arg ); + for(Iterator iterator = lb.iterator();iterator.hasNext();) + { + Object nextObject = iterator.next(); + if(nextObject.getClass().equals(Selection.class)) + { + Selection selection = (Selection)nextObject; + //System.out.println("Selection: string=\"" + selection.string +"\", adjacent = " + selection.adjacent + ", index = " + selection.index + ", value = " + selection.value); + System.out.println("new Selection(\""+selection.string+"\", "+selection.value+", "+selection.index+", " + selection.adjacent + "),"); + } else { + System.out.println("Class = "+nextObject.getClass()+", value = " + nextObject); + } + } + } else { + System.out.println("o=" + o +", arg=" + arg); + } + } + +} diff --git a/src/org/egonet/util/listbuilder/Selection.java b/src/org/egonet/util/listbuilder/Selection.java new file mode 100644 index 0000000..f3cc46e --- /dev/null +++ b/src/org/egonet/util/listbuilder/Selection.java @@ -0,0 +1,70 @@ +package org.egonet.util.listbuilder; + +public class Selection +{ + private String string; + private int index; + private int value; + private boolean adjacent; + + public Selection() + { + this.string = ""; + this.value = -1; + this.index = -1; + this.adjacent = false; + } + + public Selection(String string, int value, int index, boolean adjacent) + { + this.string = string; + this.value = value; + this.index = index; + this.adjacent = adjacent; + } + + public String toString() + { + return "[Selection \"" + string + "\", index = "+index+", value = "+value+", adjacent = "+adjacent+"]"; + } + + public boolean isAdjacent() + { + return adjacent; + } + + public void setAdjacent(boolean adjacent) + { + this.adjacent = adjacent; + } + + public int getIndex() + { + return index; + } + + public void setIndex(int index) + { + this.index = index; + } + + public String getString() + { + return string; + } + + public void setString(String string) + { + this.string = string; + } + + public int getValue() + { + return value; + } + + public void setValue(int value) + { + this.value = value; + } +} diff --git a/src/org/egonet/util/listbuilder/SelectionListCellRenderer.java b/src/org/egonet/util/listbuilder/SelectionListCellRenderer.java new file mode 100644 index 0000000..6e9f9a5 --- /dev/null +++ b/src/org/egonet/util/listbuilder/SelectionListCellRenderer.java @@ -0,0 +1,35 @@ +package org.egonet.util.listbuilder; + +import java.awt.Component; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JList; +import javax.swing.ListCellRenderer; + +public class SelectionListCellRenderer implements ListCellRenderer +{ + protected final DefaultListCellRenderer renderer = new DefaultListCellRenderer(); + + public Component getListCellRendererComponent( + JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) + { + if(value.getClass().equals(Selection.class)) + { + Selection selection = (Selection)value; + String strValue = selection.getString() + " (value = " + selection.getValue()+")"; + + Component listCellRendererComponent = renderer.getListCellRendererComponent(list, strValue, index, isSelected, cellHasFocus); + if(selection.isAdjacent()) + listCellRendererComponent.setForeground(java.awt.Color.RED); + return listCellRendererComponent; + } + else + { + return renderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + } + } +} diff --git a/src/org/egonet/util/table/ColorChooserEditor.java b/src/org/egonet/util/table/ColorChooserEditor.java new file mode 100644 index 0000000..faa4c3c --- /dev/null +++ b/src/org/egonet/util/table/ColorChooserEditor.java @@ -0,0 +1,48 @@ +package org.egonet.util.table; + +import java.awt.Color; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.AbstractCellEditor; +import javax.swing.JButton; +import javax.swing.JColorChooser; +import javax.swing.JTable; +import javax.swing.table.TableCellEditor; + +public class ColorChooserEditor extends AbstractCellEditor implements TableCellEditor { + + private JButton delegate = new JButton(); + + Color savedColor; + + public ColorChooserEditor() { + + ActionListener actionListener = new ActionListener() { + public void actionPerformed(ActionEvent actionEvent) { + Color color = JColorChooser.showDialog(delegate, + "Color Chooser", savedColor); + ColorChooserEditor.this.changeColor(color); + } + }; + + delegate.addActionListener(actionListener); + } + + public Object getCellEditorValue() { + return savedColor; + } + + private void changeColor(Color color) { + if (color != null) { + savedColor = color; + delegate.setIcon(new DiamondIcon(color)); + } + } + + public Component getTableCellEditorComponent(JTable table, Object value, + boolean isSelected, int row, int column) { + changeColor((Color) value); + return delegate; + } + } diff --git a/src/org/egonet/util/table/ColorEditor.java b/src/org/egonet/util/table/ColorEditor.java new file mode 100644 index 0000000..6023196 --- /dev/null +++ b/src/org/egonet/util/table/ColorEditor.java @@ -0,0 +1,73 @@ +package org.egonet.util.table; + +import javax.swing.JColorChooser; +import javax.swing.AbstractCellEditor; +import javax.swing.table.TableCellEditor; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JTable; +import java.awt.Component; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class ColorEditor extends AbstractCellEditor implements TableCellEditor, + ActionListener { + Color currentColor; + + JButton button; + + JColorChooser colorChooser; + + JDialog dialog; + + protected static final String EDIT = "edit"; + + public ColorEditor() { + // Set up the editor (from the table's point of view), + // which is a button. + // This button brings up the color chooser dialog, + // which is the editor from the user's point of view. + button = new JButton(); + button.setActionCommand(EDIT); + button.addActionListener(this); + button.setBorderPainted(false); + + // Set up the dialog that the button brings up. + colorChooser = new JColorChooser(); + dialog = JColorChooser.createDialog(button, "Pick a Color", true, // modal + colorChooser, this, // OK button handler + null); // no CANCEL button handler + } + + /** + * Handles events from the editor button and from the dialog's OK button. + */ + public void actionPerformed(ActionEvent e) { + if (EDIT.equals(e.getActionCommand())) { + // The user has clicked the cell, so + // bring up the dialog. + button.setBackground(currentColor); + colorChooser.setColor(currentColor); + dialog.setVisible(true); + + // Make the renderer reappear. + fireEditingStopped(); + + } else { // User pressed dialog's "OK" button. + currentColor = colorChooser.getColor(); + } + } + + // Implement the one CellEditor method that AbstractCellEditor doesn't. + public Object getCellEditorValue() { + return currentColor; + } + + // Implement the one method defined by TableCellEditor. + public Component getTableCellEditorComponent(JTable table, Object value, + boolean isSelected, int row, int column) { + currentColor = (Color) value; + return button; + } +} diff --git a/src/org/egonet/util/table/ColorRenderer.java b/src/org/egonet/util/table/ColorRenderer.java new file mode 100644 index 0000000..96820cd --- /dev/null +++ b/src/org/egonet/util/table/ColorRenderer.java @@ -0,0 +1,50 @@ +package org.egonet.util.table; + +import javax.swing.*; +import javax.swing.table.*; +import javax.swing.border.*; +import java.awt.Component; +import java.awt.Color; + +/** + * Renderer on color cell with label - not used now + * @author sonam + * + */ +public class ColorRenderer extends JLabel implements TableCellRenderer { + Border unselectedBorder = null; + + Border selectedBorder = null; + + boolean isBordered = true; + + public ColorRenderer(boolean isBordered) { + this.isBordered = isBordered; + setOpaque(true); // MUST do this for background to show up. + } + + public Component getTableCellRendererComponent(JTable table, Object color, + boolean isSelected, boolean hasFocus, int row, int column) { + Color newColor = (Color) color; + setBackground(newColor); + if (isBordered) { + if (isSelected) { + if (selectedBorder == null) { + selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, + 5, table.getSelectionBackground()); + } + setBorder(selectedBorder); + } else { + if (unselectedBorder == null) { + unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, + 5, table.getBackground()); + } + setBorder(unselectedBorder); + } + } + + setToolTipText("RGB value: " + newColor.getRed() + ", " + + newColor.getGreen() + ", " + newColor.getBlue()); + return this; + } +} diff --git a/src/org/egonet/util/table/ComboTableCellRenderer.java b/src/org/egonet/util/table/ComboTableCellRenderer.java new file mode 100644 index 0000000..88c0330 --- /dev/null +++ b/src/org/egonet/util/table/ComboTableCellRenderer.java @@ -0,0 +1,47 @@ +package org.egonet.util.table; + +import java.awt.Color; +import java.awt.Component; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JTable; +import javax.swing.ListCellRenderer; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellRenderer; + +public class ComboTableCellRenderer implements ListCellRenderer, TableCellRenderer { + DefaultListCellRenderer listRenderer = new DefaultListCellRenderer(); + + DefaultTableCellRenderer tableRenderer = new DefaultTableCellRenderer(); + + private void configureRenderer(JLabel renderer, Object value) { + if ((value != null) && (value instanceof Color)) { + renderer.setIcon(new DiamondIcon((Color) value)); + renderer.setText(""); + } else { + renderer.setIcon(null); + renderer.setText((String) value); + } + } + + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) { + listRenderer = (DefaultListCellRenderer) listRenderer + .getListCellRendererComponent(list, value, index, isSelected, + cellHasFocus); + configureRenderer(listRenderer, value); + return listRenderer; + } + + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + tableRenderer = (DefaultTableCellRenderer) tableRenderer + .getTableCellRendererComponent(table, value, isSelected, + hasFocus, row, column); + configureRenderer(tableRenderer, value); + return tableRenderer; + } + } + diff --git a/src/org/egonet/util/table/DiamondIcon.java b/src/org/egonet/util/table/DiamondIcon.java new file mode 100644 index 0000000..17acaef --- /dev/null +++ b/src/org/egonet/util/table/DiamondIcon.java @@ -0,0 +1,75 @@ +package org.egonet.util.table; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Polygon; + +import javax.swing.Icon; + +/** + * Icon to display on color cell of table + * @author sonam + * + */ +class DiamondIcon implements Icon { + private Color color; + + private boolean selected; + + private int width; + + private int height; + + private Polygon poly; + + private static final int DEFAULT_WIDTH = 10; + + private static final int DEFAULT_HEIGHT = 10; + + public DiamondIcon(Color color) { + this(color, true, DEFAULT_WIDTH, DEFAULT_HEIGHT); + } + + public DiamondIcon(Color color, boolean selected) { + this(color, selected, DEFAULT_WIDTH, DEFAULT_HEIGHT); + } + + public DiamondIcon(Color color, boolean selected, int width, int height) { + this.color = color; + this.selected = selected; + this.width = width; + this.height = height; + initPolygon(); + } + + private void initPolygon() { + poly = new Polygon(); + int halfWidth = width / 2; + int halfHeight = height / 2; + poly.addPoint(0, halfHeight); + poly.addPoint(halfWidth, 0); + poly.addPoint(width, halfHeight); + poly.addPoint(halfWidth, height); + } + + public int getIconHeight() { + return height; + } + + public int getIconWidth() { + return width; + } + + public void paintIcon(Component c, Graphics g, int x, int y) { + g.setColor(color); + g.translate(x, y); + if (selected) { + g.fillPolygon(poly); + } else { + g.drawPolygon(poly); + } + g.translate(-x, -y); + } + } + diff --git a/src/org/egonet/util/table/EgonetTableModel.java b/src/org/egonet/util/table/EgonetTableModel.java new file mode 100644 index 0000000..eb5d2a6 --- /dev/null +++ b/src/org/egonet/util/table/EgonetTableModel.java @@ -0,0 +1,53 @@ +package org.egonet.util.table; + +import javax.swing.table.AbstractTableModel; + +/** + * Custom table model for Graph Data + * @author sonam + * + */ +public class EgonetTableModel extends AbstractTableModel { + + private String[] columnNames = { "Choice", "Color", "Shape", "Size" };; + + private Object[][] rowData; + + public EgonetTableModel(Object[][] data) { + super(); + this.rowData = new Object[data.length][data[0].length]; + this.rowData = data; + } + + public EgonetTableModel() { + super(); + } + + public int getColumnCount() { + return columnNames.length; + } + + public int getRowCount() { + return rowData.length; + } + + public Object getValueAt(int rowIndex, int columnIndex) { + return rowData[rowIndex][columnIndex]; + } + + public Class getColumnClass(int c) { + return getValueAt(0, c).getClass(); + } + + public boolean isCellEditable(int row, int col) { + return !(col == 0); + } + + public void setValueAt(Object value, int row, int col) { + rowData[row][col] = value; + fireTableCellUpdated(row, col); + } + + public String getColumnName(int col) + { return columnNames[col]; } +} diff --git a/src/org/egonet/util/table/LabelRenderer.java b/src/org/egonet/util/table/LabelRenderer.java new file mode 100644 index 0000000..19eaa68 --- /dev/null +++ b/src/org/egonet/util/table/LabelRenderer.java @@ -0,0 +1,30 @@ +package org.egonet.util.table; + +import javax.swing.*; +import javax.swing.table.*; +import java.awt.Component; + +import org.egonet.util.listbuilder.Selection; + +/** + * To Renderer the cell displaying selection + * @author sonam + * + */ +public class LabelRenderer extends DefaultTableCellRenderer { + + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + + if(value instanceof Selection) + { + Selection s = (Selection) value; + value = s.getString(); + //} else { + // System.out.println("Asked to render a non-selection of type "+value.getClass()+": " + value); + } + + setHorizontalAlignment(CENTER); + return super.getTableCellRendererComponent(table,value,isSelected,hasFocus,row,column); + } +} diff --git a/src/org/egonet/util/table/LabelTableModel.java b/src/org/egonet/util/table/LabelTableModel.java new file mode 100644 index 0000000..6cd262e --- /dev/null +++ b/src/org/egonet/util/table/LabelTableModel.java @@ -0,0 +1,43 @@ +package org.egonet.util.table; +import org.egonet.util.listbuilder.Selection; +import javax.swing.table.AbstractTableModel; + +public class LabelTableModel extends AbstractTableModel { + private String[] columnNames = { "Responses" }; + + private Selection[] rowData; + + public LabelTableModel(Selection[] data) { + super(); + this.rowData = new Selection[data.length]; + this.rowData = data; + } + + public LabelTableModel() { + super(); + } + + public int getColumnCount() { + return columnNames.length; + } + + public int getRowCount() { + return rowData.length; + } + + public Selection getValueAt(int rowIndex, int columnIndex) { + return rowData[rowIndex]; + } + + public void setValueAt(Selection selection, int rowIndex) { + rowData[rowIndex] = selection; + } + + public Class getColumnClass(int c) { + return getValueAt(0, c).getClass(); + } + + public String getColumnName(int col) { + return columnNames[col]; + } +} diff --git a/src/org/egonet/util/table/PropertyTableModel.java b/src/org/egonet/util/table/PropertyTableModel.java new file mode 100644 index 0000000..a0a32b9 --- /dev/null +++ b/src/org/egonet/util/table/PropertyTableModel.java @@ -0,0 +1,50 @@ +package org.egonet.util.table; + +import org.egonet.util.listbuilder.Selection; +import javax.swing.table.AbstractTableModel; + +public class PropertyTableModel extends AbstractTableModel{ + private String[] columnNames = { "Responses", "Property"}; + + private Object[][] rowData; + + public PropertyTableModel(Object[][] data) { + super(); + this.rowData = new Selection[data.length][data[0].length]; + this.rowData = data; + } + + public PropertyTableModel() { + super(); + } + + public int getColumnCount() { + return columnNames.length; + } + + public int getRowCount() { + return rowData.length; + } + + public Object getValueAt(int rowIndex, int columnIndex) { + return rowData[rowIndex][columnIndex]; + } + + public void setValueAt(Object value, int rowIndex, int colIndex) { + rowData[rowIndex][colIndex] = value; + fireTableCellUpdated(rowIndex, colIndex); + } + + public Class getColumnClass(int c) { + return getValueAt(0, c).getClass(); + } + + public String getColumnName(int col) { + return columnNames[col]; + } + + public boolean isCellEditable(int row, int col) { + return !(col == 0); + } + +} diff --git a/src/org/egonet/util/table/SelectionTableModel.java b/src/org/egonet/util/table/SelectionTableModel.java new file mode 100644 index 0000000..7917cc4 --- /dev/null +++ b/src/org/egonet/util/table/SelectionTableModel.java @@ -0,0 +1,55 @@ +package org.egonet.util.table; + +import javax.swing.table.AbstractTableModel; + +/** + * Custom table model for Graph Data + * + * @author sonam + * + */ +public class SelectionTableModel extends AbstractTableModel { + + private String[] columnNames = { "ShowLabel", "Choice", "Color", "Shape", "Size" }; + + private Object[][] rowData; + + public SelectionTableModel(Object[][] data) { + super(); + this.rowData = new Object[data.length][data[0].length]; + this.rowData = data; + } + + public SelectionTableModel() { + super(); + } + + public int getColumnCount() { + return columnNames.length; + } + + public int getRowCount() { + return rowData.length; + } + + public Object getValueAt(int rowIndex, int columnIndex) { + return rowData[rowIndex][columnIndex]; + } + + public Class getColumnClass(int c) { + return getValueAt(0, c).getClass(); + } + + public boolean isCellEditable(int row, int col) { + return !(col == 1); + } + + public void setValueAt(Object value, int row, int col) { + rowData[row][col] = value; + fireTableCellUpdated(row, col); + } + + public String getColumnName(int col) { + return columnNames[col]; + } +} diff --git a/src/org/egonet/util/table/TableComboBoxRenderer.java b/src/org/egonet/util/table/TableComboBoxRenderer.java new file mode 100644 index 0000000..4ef57ba --- /dev/null +++ b/src/org/egonet/util/table/TableComboBoxRenderer.java @@ -0,0 +1,33 @@ +package org.egonet.util.table; + +import javax.swing.table.*; +import javax.swing.JTable; +import javax.swing.JComboBox; +import java.awt.Component; + +/** + * Renderer for combobox - used for shape and size + * @author sonam + * + */ +public class TableComboBoxRenderer extends JComboBox implements + TableCellRenderer { + public TableComboBoxRenderer(Object[] items) { + super(items); + } + + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + if (isSelected) { + setForeground(table.getSelectionForeground()); + super.setBackground(table.getSelectionBackground()); + } else { + setForeground(table.getForeground()); + setBackground(table.getBackground()); + } + + // Select the current value + setSelectedItem(value); + return this; + } +} diff --git a/src/org/egonet/util/table/TableModelBoolean.java b/src/org/egonet/util/table/TableModelBoolean.java new file mode 100644 index 0000000..ac44f5a --- /dev/null +++ b/src/org/egonet/util/table/TableModelBoolean.java @@ -0,0 +1,53 @@ +package org.egonet.util.table; + +import javax.swing.table.AbstractTableModel; + +/** + * Custom table model for Graph Data + * @author sonam + * + */ +public class TableModelBoolean extends AbstractTableModel { + + private String[] columnNames = { "ShowEdges", "Choice", "Color", "Shape", "Size" }; + + private Object[][] rowData; + + public TableModelBoolean(Object[][] data) { + super(); + this.rowData = new Object[data.length][data[0].length]; + this.rowData = data; + } + + public TableModelBoolean() { + super(); + } + + public int getColumnCount() { + return columnNames.length; + } + + public int getRowCount() { + return rowData.length; + } + + public Object getValueAt(int rowIndex, int columnIndex) { + return rowData[rowIndex][columnIndex]; + } + + public Class getColumnClass(int c) { + return getValueAt(0, c).getClass(); + } + + public boolean isCellEditable(int row, int col) { + return !(col == 1); + } + + public void setValueAt(Object value, int row, int col) { + rowData[row][col] = value; + fireTableCellUpdated(row, col); + } + + public String getColumnName(int col) + { return columnNames[col]; } +}