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    }