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 }