001 // This file is part of AceWiki.
002 // Copyright 2008-2012, AceWiki developers.
003 //
004 // AceWiki is free software: you can redistribute it and/or modify it under the terms of the GNU
005 // Lesser General Public License as published by the Free Software Foundation, either version 3 of
006 // the License, or (at your option) any later version.
007 //
008 // AceWiki is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
009 // even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
010 // Lesser General Public License for more details.
011 //
012 // You should have received a copy of the GNU Lesser General Public License along with AceWiki. If
013 // not, see http://www.gnu.org/licenses/.
014
015 package ch.uzh.ifi.attempto.acewiki.gui;
016
017 import java.util.ArrayList;
018 import java.util.List;
019
020 import nextapp.echo.app.Color;
021 import nextapp.echo.app.Component;
022 import nextapp.echo.app.Extent;
023 import nextapp.echo.app.Font;
024 import nextapp.echo.app.Grid;
025 import nextapp.echo.app.ImageReference;
026 import nextapp.echo.app.Insets;
027 import nextapp.echo.app.WindowPane;
028 import nextapp.echo.app.event.ActionEvent;
029 import nextapp.echo.app.event.ActionListener;
030 import ch.uzh.ifi.attempto.acewiki.Wiki;
031 import ch.uzh.ifi.attempto.acewiki.core.Concept;
032 import ch.uzh.ifi.attempto.acewiki.core.Individual;
033 import ch.uzh.ifi.attempto.acewiki.core.InvalidWordException;
034 import ch.uzh.ifi.attempto.acewiki.core.LanguageUtils;
035 import ch.uzh.ifi.attempto.acewiki.core.LexiconChanger;
036 import ch.uzh.ifi.attempto.acewiki.core.LexiconDetail;
037 import ch.uzh.ifi.attempto.acewiki.core.OntologyElement;
038 import ch.uzh.ifi.attempto.acewiki.core.OntologyTextElement;
039 import ch.uzh.ifi.attempto.acewiki.core.Relation;
040 import ch.uzh.ifi.attempto.acewiki.core.Sentence;
041 import ch.uzh.ifi.attempto.echocomp.CheckBox;
042 import ch.uzh.ifi.attempto.echocomp.Label;
043 import ch.uzh.ifi.attempto.echocomp.MessageWindow;
044 import ch.uzh.ifi.attempto.echocomp.TextField;
045 import ch.uzh.ifi.attempto.preditor.WordEditorForm;
046
047 /**
048 * This class describes the basic structure for forms to create and modify words (represented by
049 * ontology elements).
050 *
051 * @author Tobias Kuhn
052 */
053 public class FormPane extends WordEditorForm {
054
055 private static final long serialVersionUID = 1433983400709841847L;
056
057 private OntologyElement element;
058 private int wordNumber;
059 private Wiki wiki;
060 private LexiconChanger lexiconChanger;
061 private boolean locked;
062 private MessageWindow delConfirmWindow;
063
064 /**
065 * Creates a form pane for creating a new word.
066 *
067 * @param type The type of the new word.
068 * @param wordNumber The word number that should be used once the word is created.
069 * @param window The parent window.
070 * @param wiki The wiki object.
071 * @param actionListener The action-listener.
072 */
073 public FormPane(String type, int wordNumber, WindowPane window, Wiki wiki,
074 ActionListener actionListener) {
075 this(type, null, wordNumber, window, wiki, actionListener);
076 }
077
078 /**
079 * Creates a form pane for creating a new word.
080 *
081 * @param type The type of the new word.
082 * @param window The parent window.
083 * @param wiki The wiki object.
084 */
085 public FormPane(String type, WindowPane window, Wiki wiki) {
086 this(type, null, 0, window, wiki, wiki);
087 }
088
089 /**
090 * Creates a form pane for modifying an existing word.
091 *
092 * @param element The ontology element that represents the word to be modified.
093 * @param window The parent window.
094 * @param wiki The wiki object.
095 */
096 public FormPane(OntologyElement element, WindowPane window, Wiki wiki) {
097 this(element.getInternalType(), element, 0, window, wiki, wiki);
098 }
099
100 private FormPane(String type, OntologyElement element, int wordNumber, WindowPane window,
101 Wiki wiki, ActionListener actionListener) {
102 super(window, actionListener);
103 this.wordNumber = wordNumber;
104 this.wiki = wiki;
105 if (element != null) {
106 this.locked = true;
107 if (wiki.isReadOnly()) {
108 setButtons("Close");
109 } else {
110 setButtons("Unlock", "Close");
111 }
112 } else {
113 element = wiki.getOntology().getEngine().createOntologyElement(type);
114 this.locked = false;
115 setButtons("OK", "Cancel");
116 }
117 this.element = element;
118
119 lexiconChanger = wiki.getLanguageHandler().getLexiconChanger(type);
120 setTitle(element.getType());
121 setExplanationComponent(lexiconChanger.getDescription());
122
123 for (LexiconDetail d : lexiconChanger.getDetails(element)) {
124 Component formElement = null;
125 Object value = d.getValue();
126 boolean required = d.isRequired();
127 if (value instanceof String) {
128 TextField textField = new TextField(this);
129 textField.setText(LanguageUtils.getPrettyPrinted((String) value));
130 formElement = textField;
131 } else if (value instanceof Boolean) {
132 CheckBox checkBox = new CheckBox();
133 checkBox.setSelected((Boolean) value);
134 formElement = checkBox;
135 required = false;
136 } else {
137 throw new RuntimeException("invalid class: " + value.getClass());
138 }
139 formElement.setEnabled(!locked);
140 addRow(d.getName(), formElement, d.getDescription(), required);
141 }
142 }
143
144 private void setExplanationComponent(String text) {
145 ImageReference imageRef = null;
146 if (element instanceof Individual) {
147 imageRef = Wiki.getImage("individual.png");
148 } else if (element instanceof Concept) {
149 imageRef = Wiki.getImage("concept.png");
150 } else if (element instanceof Relation) {
151 imageRef = Wiki.getImage("relation.png");
152 }
153 Grid explanationComp = new Grid(2);
154 explanationComp.setRowHeight(0, new Extent(110));
155 explanationComp.setColumnWidth(0, new Extent(100));
156 explanationComp.setInsets(new Insets(0, 0, 10, 20));
157 if (imageRef != null) {
158 explanationComp.add(new Label(imageRef));
159 } else {
160 explanationComp.add(new Label(""));
161 }
162 Label explLabel = new Label(text, Font.ITALIC);
163 explLabel.setForeground(new Color(120, 120, 120));
164 explanationComp.add(explLabel);
165 setExplanationComponent(explanationComp);
166 }
167
168 /**
169 * Returns whether this form is locked or not.
170 *
171 * @return true if this form is locked.
172 */
173 public boolean isLocked() {
174 return locked;
175 }
176
177 private void unlock() {
178 setButtons("Delete", "Change", "Cancel");
179 for (Component c : getFormElements()) {
180 c.setEnabled(true);
181 }
182 locked = false;
183 doFocus();
184 }
185
186 private void prepareDelete() {
187 List<Sentence> references = wiki.getOntology().getReferences(element);
188 for (Sentence s : element.getArticle().getSentences()) {
189 references.remove(s);
190 }
191 if (!references.isEmpty()) {
192 wiki.log("page", "error: cannot delete article with references");
193 wiki.showWindow(new MessageWindow(
194 "Error",
195 "This word cannot be deleted, because other articles refer to it.",
196 getParentWindow(),
197 (ActionListener) null,
198 "OK"
199 ));
200 } else {
201 wiki.log("page", "delete confirmation");
202 delConfirmWindow = new MessageWindow(
203 "Delete",
204 "Do you really want to delete this word and all the content of its article?",
205 getParentWindow(),
206 this,
207 "Yes",
208 "No"
209 );
210 wiki.showWindow(delConfirmWindow);
211 }
212 }
213
214 private void delete() {
215 wiki.log("page", "delete confirmed");
216 wiki.getOntology().remove(element);
217 wiki.showStartPage();
218 }
219
220 private void saveOrShowError() {
221 try {
222 List<Object> newValues = new ArrayList<Object>();
223 for (Component c : getFormElements()) {
224 if (c instanceof TextField) {
225 newValues.add(((TextField) c).getText());
226 } else if (c instanceof CheckBox) {
227 newValues.add(((CheckBox) c).isSelected());
228 }
229 }
230 lexiconChanger.save(element, wordNumber, newValues, wiki.getOntology());
231 wiki.log("edit", element.toString());
232 if (element.getOntology() == null) {
233 wiki.getOntology().register(element);
234 }
235 wiki.removeWindow(getParentWindow());
236
237 // a text element is used to store the ontology element and the word number in one object:
238 OntologyTextElement te = new OntologyTextElement(element, wordNumber);
239 getActionListener().actionPerformed(new ActionEvent(te, ""));
240 } catch (InvalidWordException ex) {
241 wiki.log("edit", "invalid word: " + ex.getMessage());
242 wiki.showWindow(new MessageWindow("Error", ex.getMessage(), getParentWindow(), "OK"));
243 }
244 }
245
246 public void actionPerformed(ActionEvent e) {
247 String c = e.getActionCommand();
248 if (e.getSource() == delConfirmWindow) {
249 if ("Yes".equals(c)) {
250 delete();
251 wiki.removeWindow(getParentWindow());
252 }
253 } else if ("Cancel".equals(c) || "Close".equals(c)) {
254 wiki.removeWindow(getParentWindow());
255 } else if ("OK".equals(c)) {
256 saveOrShowError();
257 } else if ("Unlock".equals(c)) {
258 if (!wiki.isEditable()) {
259 wiki.showLoginWindow();
260 } else {
261 unlock();
262 }
263 } else if ("Change".equals(c)) {
264 saveOrShowError();
265 } else if ("Delete".equals(c)) {
266 prepareDelete();
267 }
268 }
269
270 }