/******************************************************************************* * CogTool Copyright Notice and Distribution Terms * CogTool 1.3, Copyright (c) 2005-2013 Carnegie Mellon University * This software is distributed under the terms of the FSF Lesser * Gnu Public License (see LGPL.txt). * * CogTool is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * CogTool is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with CogTool; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * CogTool makes use of several third-party components, with the * following notices: * * Eclipse SWT version 3.448 * Eclipse GEF Draw2D version 3.2.1 * * Unless otherwise indicated, all Content made available by the Eclipse * Foundation is provided to you under the terms and conditions of the Eclipse * Public License Version 1.0 ("EPL"). A copy of the EPL is provided with this * Content and is also available at http://www.eclipse.org/legal/epl-v10.html. * * CLISP version 2.38 * * Copyright (c) Sam Steingold, Bruno Haible 2001-2006 * This software is distributed under the terms of the FSF Gnu Public License. * See COPYRIGHT file in clisp installation folder for more information. * * ACT-R 6.0 * * Copyright (c) 1998-2007 Dan Bothell, Mike Byrne, Christian Lebiere & * John R Anderson. * This software is distributed under the terms of the FSF Lesser * Gnu Public License (see LGPL.txt). * * Apache Jakarta Commons-Lang 2.1 * * This product contains software developed by the Apache Software Foundation * (http://www.apache.org/) * * jopt-simple version 1.0 * * Copyright (c) 2004-2013 Paul R. Holser, Jr. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * Mozilla XULRunner 1.9.0.5 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/. * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * The J2SE(TM) Java Runtime Environment version 5.0 * * Copyright 2009 Sun Microsystems, Inc., 4150 * Network Circle, Santa Clara, California 95054, U.S.A. All * rights reserved. U.S. * See the LICENSE file in the jre folder for more information. ******************************************************************************/ package edu.cmu.cs.hcii.cogtool.ui; import java.util.Arrays; import java.util.EventObject; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.custom.TableEditor; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; import edu.cmu.cs.hcii.cogtool.CogTool; import edu.cmu.cs.hcii.cogtool.CogToolLID; import edu.cmu.cs.hcii.cogtool.model.Design; import edu.cmu.cs.hcii.cogtool.model.GensimLSASimilarity; import edu.cmu.cs.hcii.cogtool.model.ISimilarityDictionary; import edu.cmu.cs.hcii.cogtool.model.ISimilarityDictionary.DictEntry; import edu.cmu.cs.hcii.cogtool.model.ISimilarityDictionary.EntryChange; import edu.cmu.cs.hcii.cogtool.model.ISitedTermSimilarity; import edu.cmu.cs.hcii.cogtool.model.ITermSimilarity; import edu.cmu.cs.hcii.cogtool.model.LSASimilarity; import edu.cmu.cs.hcii.cogtool.model.Project; import edu.cmu.cs.hcii.cogtool.uimodel.DictionaryEditorUIModel; import edu.cmu.cs.hcii.cogtool.util.AlertHandler; import edu.cmu.cs.hcii.cogtool.util.DoubleEntry; import edu.cmu.cs.hcii.cogtool.util.L10N; import edu.cmu.cs.hcii.cogtool.util.ListenerIdentifier; import edu.cmu.cs.hcii.cogtool.util.ListenerIdentifierMap; import edu.cmu.cs.hcii.cogtool.util.MenuUtil; import edu.cmu.cs.hcii.cogtool.util.NameChangeAlert; import edu.cmu.cs.hcii.cogtool.util.OSUtils; import edu.cmu.cs.hcii.cogtool.util.UndoManager; import edu.cmu.cs.hcii.cogtool.view.DictionaryEditorView; import edu.cmu.cs.hcii.cogtool.view.View; public class DictionaryEditorUI extends DefaultUI { /** * Parameters for setting either the goal or search string for an * existing dictionary entry */ public static class SetStringParms { public String string; public int rowIndex; public SetStringParms(String s, int index) { string = s; rowIndex = index; } } public static class AddStringParms { public String newString; public boolean isGoal; public AddStringParms(String s, boolean goal) { super(); newString = s; isGoal = goal; } } public static class SetAlgorithmParms { public ITermSimilarity algorithm; public int[] rowIndices; public SetAlgorithmParms(ITermSimilarity alg, int[] indices) { algorithm = alg; rowIndices = indices; } } public static class SetSimilarityParms { public double similarity; public int rowIndex; public SetSimilarityParms(double simil, int index) { similarity = simil; rowIndex = index; } } public static class ComputeSimilarityParms { public String goalString; public String searchString; public ITermSimilarity algorithm; public int rowIndex; public ComputeSimilarityParms(String goal, String search, ITermSimilarity alg, int index) { goalString = goal; searchString = search; algorithm = alg; rowIndex = index; } } protected ISimilarityDictionary dictionary; protected Design design; protected DictionaryEditorView view; protected TableEditor editor; protected DictionaryEditorUIModel uiModel; protected DelayedRepaint delayedRepainting; protected Interaction interaction; protected int modifiedRow = -1; protected static final String DICT_PREFIX = L10N.get("WT.DictPrefix", "Dictionary"); protected Combo spaceCombo; protected SelectionListener algListener = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent evt) { Combo c = (Combo) evt.widget; TableItem row = (TableItem) c.getData(); int index = c.getSelectionIndex(); String algString = c.getItem(index); boolean isLSA = (index == DictionaryEditorUIModel.LSA_INDEX); boolean isGENSIM = (index == DictionaryEditorUIModel.GENSIM_LSA_INDEX); view.setURLEnabled((index == DictionaryEditorUIModel.GOOGLE_WORD_INDEX) || (index == DictionaryEditorUIModel.GOOGLE_PHRASE_INDEX) || isLSA); view.setURLEnabled(isLSA || isGENSIM); //Where should these get updated versus just using what is already there?? //It appears that the URL is just used from the previous rather than being set here if (isGENSIM) { //view.setItems(GensimLSASimilarity.KNOWN_SPACES); view.setSpace(GensimLSASimilarity.DEFAULT_SPACE); view.setURL(GensimLSASimilarity.GENSIM_SIMPLE_LSA_URL); } else { //LSA //view.setItems(LSASimilarity.KNOWN_SPACES); view.setSpace(LSASimilarity.DEFAULT_SPACE); view.setURL(LSASimilarity.DEFAULT_LSA_URL); } Table dictTable = view.getDictTable(); modifiedRow = dictTable.indexOf(row); dictTable.setSelection(modifiedRow); ITermSimilarity alg = getAlgorithm(algString); DictionaryEditorUI.SetAlgorithmParms parms = new DictionaryEditorUI.SetAlgorithmParms(alg, dictTable.getSelectionIndices()); performAction(DictionaryEditorLID.SetAlgorithm, parms); int manualIndex = DictionaryEditorUIModel.MANUAL_INDEX; if ((index == manualIndex) && (dictTable.getSelectionCount() == 1)) { initiateEdit(row, DictionaryEditorUIModel.SIMIL_COL); } } }; public DictionaryEditorUI(ISimilarityDictionary dict, Design d, PendingDictEntry pendingEntry, Project project, UndoManager undoMgr) { super(project, "", buildLeadItems(project), undoMgr); dictionary = dict; design = d; view = new DictionaryEditorView(lIDMap, this, menuData, getWindowLocation()); updateTitle(); final Table dictTable = view.getDictTable(); dictTable.addMouseListener(new MouseAdapter() { @Override public void mouseDoubleClick(MouseEvent evt) { TableItem row = dictTable.getItem(new Point(evt.x, evt.y)); if (row != null) { int column = evt.x / DictionaryEditorView.COL_WIDTH; if (column <= DictionaryEditorUIModel.SIMIL_COL) { initiateEdit(row, column); } } } }); dictTable.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent evt) { updateView(); setViewEnabledState(ListenerIdentifierMap.NORMAL); } }); dictTable.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent evt) { if ((evt.keyCode == SWT.DEL) || (evt.keyCode == SWT.BS)) { int[] indices = dictTable.getSelectionIndices(); Arrays.sort(indices); performAction(DictionaryEditorLID.Delete, indices); } } }); editor = new TableEditor(dictTable); editor.grabHorizontal = true; interaction = new DictionaryEditorInteraction(view); uiModel = new DictionaryEditorUIModel(dictionary, pendingEntry, view.getDictTable(), algListener, this.project); delayedRepainting = new DelayedRepaint() { @Override protected void performRepaint() { updateView(); } @Override public void doWork() { super.doWork(); undoMgrViewHandler.resetView(undoManager); // Update the enabled items selection state. setViewEnabledState(ListenerIdentifierMap.NORMAL); if (modifiedRow >= 0) { uiModel.updateRow(modifiedRow); modifiedRow = -1; } view.getDictTable().update(); } }; CogTool.repaintPhase.addDelayedWork(delayedRepainting); AlertHandler handler = new AlertHandler() { public void handleAlert(EventObject alert) { EntryChange change = (EntryChange) alert; DictEntry entry = dictionary.getEntry(change.rowIndex); if (entry.algorithm instanceof ISitedTermSimilarity) { String newSite = ((ISitedTermSimilarity) entry.algorithm).getContextSite(); if (newSite != null) { view.setURL(newSite); } } } }; dictionary.addHandler(this, EntryChange.class, handler); design.addHandler(this, NameChangeAlert.class, renameHandler); this.project.addHandler(this, Project.DesignChange.class, new AlertHandler() { public void handleAlert(EventObject alert) { Project.DesignChange chg = (Project.DesignChange) alert; if ((! chg.isAdd) && (chg.element == design)) { closeOpenController(); } } }); setInitiallyEnabled(true); } protected void updateView() { Table dictTable = view.getDictTable(); int selectionCount = 0; try { selectionCount = dictTable.getSelectionCount(); } catch (SWTException e) { return; } if (selectionCount == 1) { int index = dictTable.getSelectionIndex(); DictEntry entry = dictionary.getEntry(index); if (entry != null) { String url = null; String space = null; if (entry.algorithm instanceof ISitedTermSimilarity) { url = ((ISitedTermSimilarity) entry.algorithm).getContextSite(); view.setURLEnabled(true); view.setSpaceEnabled(false); } else if (entry.algorithm instanceof LSASimilarity) { LSASimilarity lsa = (LSASimilarity) entry.algorithm; space = lsa.getSpace(); url = lsa.getURL(); if (url == null) { url = LSASimilarity.DEFAULT_LSA_URL; } view.setURLEnabled(true); view.setSpaceEnabled(true); } else if (entry.algorithm instanceof GensimLSASimilarity) { GensimLSASimilarity lsa = (GensimLSASimilarity) entry.algorithm; space = lsa.getSpace(); url = lsa.getURL(); if (url == null) { url = GensimLSASimilarity.DEFAULT_LSA_URL; } view.setURLEnabled(true); view.setSpaceEnabled(true); } else { view.setURLEnabled(false); view.setSpaceEnabled(false); } view.setURL(url); view.setSpace(space); } else { TableItem row = dictTable.getItem(index); Combo c = (Combo) row.getData(); int seln = c.getSelectionIndex(); boolean isLSA = (seln == DictionaryEditorUIModel.LSA_INDEX); view.setURLEnabled((seln == DictionaryEditorUIModel.GOOGLE_WORD_INDEX) || (seln == DictionaryEditorUIModel.GOOGLE_PHRASE_INDEX) || isLSA); view.setSpaceEnabled(isLSA); } } else { // disable if 0 or more than 1 are selected view.setURL(null); view.setSpace(null); view.setURLEnabled(false); view.setSpaceEnabled(false); } } @Override protected String buildWindowMenuLabel() { return ""; } @Override protected Object getModelObject() { return dictionary; } @Override public View getView() { return view; } @Override public Interaction getStandardInteraction() { return interaction; } public double parseDouble(String s) { if (DictionaryEditorUIModel.UNRELATED.equals(s)) { return ITermSimilarity.UNKNOWN; } return Double.parseDouble(s); } public void initiateEdit(final TableItem row, int column) { Table dictTable = view.getDictTable(); editor.horizontalAlignment = SWT.LEFT; if (column < 3) { Text actualEditor; if (column == DictionaryEditorUIModel.SIMIL_COL) { actualEditor = new DoubleEntry(dictTable, SWT.NONE) { @Override public boolean confirm(int focusRule) { return commitEdit(); } @Override public void cancel() { cleanupEditor(); } }; } else { actualEditor = new View.PerformActionText(dictTable, SWT.SINGLE | SWT.LEFT) { @Override protected boolean doChangeAction() { return commitEdit(); } @Override public void cancel() { cleanupEditor(); } }; } String rowText = row.getText(column); if (DictionaryEditorUIModel.UNRELATED.equals(rowText)) { rowText = ""; } actualEditor.setText(rowText); actualEditor.setFont(dictTable.getFont()); actualEditor.setFocus(); actualEditor.selectAll(); Object data = new Integer(column); actualEditor.setData(data); editor.setEditor(actualEditor, row, column); } dictTable.setSelection(row); } @Override public Object getParameters(ListenerIdentifier originalLID, ListenerIdentifier transmutedLID, boolean isContextSelection) { // Check the general LID, if it is to restore the parent, then // return the super's get parameters Object parameters = super.getParameters(originalLID, transmutedLID, isContextSelection); if (parameters != UNSET) { return parameters; } Table dictTable = view.getDictTable(); int rowIndex = dictTable.getSelectionIndex(); if (transmutedLID == DictionaryEditorLID.Delete) { int[] indices = dictTable.getSelectionIndices(); Arrays.sort(indices); return indices; } if ((transmutedLID == DictionaryEditorLID.ComputeSimilarity) || (transmutedLID == DictionaryEditorLID.CreateNewEntry)) { if ((0 <= rowIndex) && (rowIndex < dictTable.getItemCount())) { TableItem row = dictTable.getItem(rowIndex); Combo c = (Combo) row.getData(); int ind = c.getSelectionIndex(); return new DictionaryEditorUI.ComputeSimilarityParms(uiModel.getText(rowIndex, DictionaryEditorUIModel.GOAL_COL), uiModel.getText(rowIndex, DictionaryEditorUIModel.SEARCH_COL), getAlgorithm(c.getItem(ind)), rowIndex); } } if (transmutedLID == DictionaryEditorLID.SetAlgorithm) { if ((0 <= rowIndex) && (rowIndex < dictTable.getItemCount())) { TableItem row = dictTable.getItem(rowIndex); Combo c = (Combo) row.getData(); int ind = c.getSelectionIndex(); return new DictionaryEditorUI.SetAlgorithmParms(getAlgorithm(c.getItem(ind)), dictTable.getSelectionIndices()); } } return null; } protected ITermSimilarity getAlgorithm(String algString) { return DictionaryEditorUIModel.getAlgorithm(algString, view.getURL(), view.getSpace()); } @Override protected void updateTitle() { String designName = design.getName(); view.setWindowTitle(modificationFlag + DICT_PREFIX + ": " + project.getName() + " > " + designName + ((OSUtils.MACOSX) ? "" : UI.WINDOW_TITLE)); view.updateTitle(designName); } /** * Removes stale Text control and selection listener. */ protected void cleanupEditor() { // Remove and dispose the text box Control oldEditor = editor.getEditor(); if (oldEditor != null) { oldEditor.dispose(); editor.setEditor(null); } } /** * Sets the "always-enabled" widgets; * call this at the end of the subclass constructor! * * @author mlh */ @Override protected void setInitiallyEnabled(boolean forConstruction) { super.setInitiallyEnabled(forConstruction); setEnabled(ProjectLID.ExportDictToCSV, ListenerIdentifierMap.ALL, MenuUtil.ENABLED); setEnabled(ProjectLID.ImportDict, ListenerIdentifierMap.ALL, MenuUtil.ENABLED); setEnabled(ProjectLID.SelectAll, ListenerIdentifierMap.ALL, MenuUtil.ENABLED); setViewEnabledState(ListenerIdentifierMap.NORMAL); } @Override public void cleanup(boolean okToContinue, boolean menuHidden) { super.cleanup(okToContinue, menuHidden); setViewEnabledState(ListenerIdentifierMap.NORMAL); updateView(); } public void setLIDEnabledState() { setViewEnabledState(ListenerIdentifierMap.NORMAL); } protected void setViewEnabledState(Boolean context) { try { Table dictTable = view.getDictTable(); int selectionCount = dictTable.getSelectionCount(); boolean pendingSelected = dictTable.isSelected(dictTable.getItemCount() - 1); boolean enabled = ! pendingSelected && (selectionCount >= 1); setEnabled(CogToolLID.Delete, context, enabled); enabled = selectionCount == 1; setEnabled(DictionaryEditorLID.SetSimilarity, context, enabled); setEnabled(DictionaryEditorLID.SetGoalString, context, enabled); setEnabled(DictionaryEditorLID.SetSearchString, context, enabled); } catch (SWTException e) { // ignore } } protected boolean commitEdit() { Text actualEditor = (Text) editor.getEditor(); if (actualEditor != null) { Table dictTable = view.getDictTable(); modifiedRow = dictTable.getSelectionIndex(); if (modifiedRow < 0) { cleanupEditor(); return true; } Integer editCol = (Integer) actualEditor.getData(); int col = editCol.intValue(); String value = actualEditor.getText(); Object editParms; if (! ("".equals(value))) { DictEntry entry = dictionary.getEntry(modifiedRow); if (col == DictionaryEditorUIModel.SIMIL_COL) { // check this first because we set this regardless of // whether there's an entry for this row already double simil = parseDouble(value); editParms = new DictionaryEditorUI.SetSimilarityParms(simil, modifiedRow); performAction(DictionaryEditorLID.SetSimilarity, editParms); } else if (entry == null) { addTerm(col, value, modifiedRow); } else if (col == DictionaryEditorUIModel.GOAL_COL) { editParms = new DictionaryEditorUI.SetStringParms(value, modifiedRow); performAction(DictionaryEditorLID.SetGoalString, editParms); } else if (col == DictionaryEditorUIModel.SEARCH_COL) { editParms = new DictionaryEditorUI.SetStringParms(value, modifiedRow); performAction(DictionaryEditorLID.SetSearchString, editParms); } } else { // text is empty, set similarity to UNKNOWN if applicable if (col == DictionaryEditorUIModel.SIMIL_COL) { double simil = ITermSimilarity.UNKNOWN; editParms = new DictionaryEditorUI.SetSimilarityParms(simil, modifiedRow); performAction(DictionaryEditorLID.SetSimilarity, editParms); } } } cleanupEditor(); return true; } protected void addTerm(int column, String newText, int row) { String otherText = uiModel.getText(row, (column + 1) % 2); boolean createEntry = ! ("".equals(otherText)); if (createEntry) { // both strings are filled in, so add an entry to the dictionary uiModel.setText(row, column, newText); if (! performAction(DictionaryEditorLID.CreateNewEntry)) { // Adding the new entry failed, so undo the change to the // pending entry uiModel.setText(row, column, ""); } } else { // only one string is not empty, so don't save anything to the model performAction(DictionaryEditorLID.StartNewEntry, new DictionaryEditorUI.AddStringParms(newText, column == DictionaryEditorUIModel.GOAL_COL)); } } @Override protected void updateWindowMenus() { // do nothing } @Override public void dispose() { if (editor != null) { editor.dispose(); } uiModel.dispose(); CogTool.repaintPhase.removeDelayedWork(delayedRepainting); dictionary.removeAllHandlers(this); design.removeAllHandlers(this); project.removeAllHandlers(this); super.dispose(); } public void selectAll() { Table dictTable = view.getDictTable(); int size = dictTable.getItemCount(); // Don't select the pending entry row dictTable.setSelection(0, size - 2); } }