From f1713c3634c7ff70ebc9cd7400a5a0732394b104 Mon Sep 17 00:00:00 2001 From: krasimir Date: Wed, 29 Sep 2010 13:17:48 +0000 Subject: [PATCH] initial import of the new GF editor. Note: the FridgeApp is temporary broken. It will be fixed with the next patch --- .../ui/gwt/EditorApp.gwt.xml | 6 +- .../ui/gwt/client/BrowsePanel.java | 267 +++++++++ .../ui/gwt/client/EditorApp.java | 460 +++++++++++++++ .../ui/gwt/client/FridgeBagPanel.java | 104 +--- .../ui/gwt/client/LinearizationsPanel.java | 150 +++++ .../ui/gwt/client/MagnetSearchBox.java | 49 ++ .../ui/gwt/client/PGF.java | 62 +- .../ui/gwt/client/PGFWrapper.java | 10 +- .../ui/gwt/client/QueryPanel.java | 109 ++++ .../ui/gwt/client/TextInputPanel.java | 547 ++++++++++++++++++ .../ui/gwt/client/selection/Selection.java | 66 +++ .../client/selection/SelectionEndPoint.java | 69 +++ .../InternetExplorerSelectionSupport.java | 248 ++++++++ .../selection/support/SelectionSupport.java | 69 +++ .../ui/gwt/public/Editor.css | 253 ++++++++ .../ui/gwt/public/background.png | Bin 0 -> 760 bytes .../ui/gwt/public/new.png | Bin 0 -> 230 bytes .../ui/gwt/public/textinput-buttons.png | Bin 0 -> 518 bytes 18 files changed, 2353 insertions(+), 116 deletions(-) create mode 100644 src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/BrowsePanel.java create mode 100644 src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/EditorApp.java create mode 100644 src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/LinearizationsPanel.java create mode 100644 src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/MagnetSearchBox.java create mode 100644 src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/QueryPanel.java create mode 100644 src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/TextInputPanel.java create mode 100644 src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/Selection.java create mode 100644 src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/SelectionEndPoint.java create mode 100644 src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/support/InternetExplorerSelectionSupport.java create mode 100644 src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/support/SelectionSupport.java create mode 100644 src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/Editor.css create mode 100644 src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/background.png create mode 100644 src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/new.png create mode 100644 src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/textinput-buttons.png diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/EditorApp.gwt.xml b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/EditorApp.gwt.xml index 0fa154520..a7b24c1af 100644 --- a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/EditorApp.gwt.xml +++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/EditorApp.gwt.xml @@ -13,8 +13,10 @@ - - + + + + diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/BrowsePanel.java b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/BrowsePanel.java new file mode 100644 index 000000000..ea7e0aca8 --- /dev/null +++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/BrowsePanel.java @@ -0,0 +1,267 @@ +package org.grammaticalframework.ui.gwt.client; + +import java.util.*; +import com.google.gwt.user.client.History; +import com.google.gwt.user.client.HistoryListener; +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.DeferredCommand; +import com.google.gwt.user.client.ui.*; +import com.google.gwt.http.client.*; +import com.google.gwt.xml.client.*; +import com.google.gwt.event.logical.shared.*; + +public class BrowsePanel extends Composite { + + private PGFWrapper pgf; + private HTML sourceView; + private SuggestBox searchBox; + private CompletionOracle oracle; + private List identifiers = null; + + public BrowsePanel(PGFWrapper pgf) { + this.pgf = pgf; + + oracle = new CompletionOracle(); + + HorizontalPanel browsePanel = new HorizontalPanel(); + VerticalPanel vPanel = new VerticalPanel(); + vPanel.add(createSearchPanel(oracle)); + vPanel.add(createTreeView()); + browsePanel.add(vPanel); + browsePanel.add(createSourcePanel()); + browsePanel.setCellWidth(sourceView,"100%"); + + initWidget(browsePanel); + setStylePrimaryName("my-BrowsePanel"); + + pgf.addSettingsListener(new MySettingsListener(pgf)); + } + + public native void onActivate() /*-{ + $doc.browsePanel = this; + $doc.callBrowse = @org.grammaticalframework.ui.gwt.client.BrowsePanel::callBrowse(Lorg/grammaticalframework/ui/gwt/client/BrowsePanel;Ljava/lang/String;); + }-*/; + + protected Widget createSearchPanel(CompletionOracle oracle) { + searchBox = new SuggestBox(oracle); + searchBox.setLimit(10); + searchBox.addKeyboardListener(new KeyboardListenerAdapter() { + public void onKeyUp (Widget sender, char keyCode, int modifiers) { + if (keyCode == KEY_ENTER) { + callBrowse(BrowsePanel.this,searchBox.getText()); + } + } + }); + + DecoratorPanel decorator = new DecoratorPanel(); + VerticalPanel vPanel = new VerticalPanel(); + vPanel.add(new Label("Search")); + vPanel.add(searchBox); + decorator.add(vPanel); + return decorator; + } + + private static void callBrowse(BrowsePanel panel, String id) { + panel.browse(id); + History.newItem("browse:"+id); + } + + public void browse(final String id) { + if (id == null || id.equals("")) { + sourceView.setHTML(""); + return; + } + + pgf.browse(id, "javascript:document.callBrowse(document.browsePanel,'$ID')", + "my-identifierLink", + new RequestCallback() { + public void onResponseReceived(Request request, Response response) { + sourceView.setHTML(response.getText()); + } + + public void onError(Request request, java.lang.Throwable exception) { + // errorHandler.onError(e); + } + }); + } + + protected Widget createTreeView() { + hierarchyTree = new Tree(); + hierarchyTree.addSelectionHandler(new SelectionHandler() { + public void onSelection(SelectionEvent event) { + TreeItem item = event.getSelectedItem(); + callBrowse(BrowsePanel.this,item.getText()); + } + }); + return hierarchyTree; + } + + protected Widget createSourcePanel() { + sourceView = new HTML(); + sourceView.setStylePrimaryName("source"); + return sourceView; + } + + protected class CompletionOracle extends SuggestOracle { + + public CompletionOracle() { + } + + public void requestSuggestions(SuggestOracle.Request request, SuggestOracle.Callback callback) { + List list = new ArrayList(); + + int index = Collections.binarySearch(identifiers, request.getQuery()); + index = (index >= 0) ? index : -(index+1); + + for (; index < identifiers.size(); index++) { + String id = identifiers.get(index); + + if (id.startsWith(request.getQuery())) { + list.add(new CompletionSuggestion(id)); + } + else + break; + + if (list.size() > request.getLimit()) + break; + } + + callback.onSuggestionsReady(request, new SuggestOracle.Response(list)); + } + } + + protected static class CompletionSuggestion implements SuggestOracle.Suggestion { + private String string; + + public CompletionSuggestion(String string) { + this.string = string; + } + + public String getDisplayString() { + return string; + } + + public String getReplacementString() { + return string; + } + } + + Tree hierarchyTree = null; + + protected void reloadHierarchyTree() { + hierarchyTree.clear(); + + final String url = pgf.getGrammarURL()+".xml"; + RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(url)); + try + { + builder.sendRequest(null, new RequestCallback() { + public void onResponseReceived(Request request, Response response) + { + if (200 == response.getStatusCode()) + { + try + { + Document browseDoc = XMLParser.parse(response.getText()); + + TreeLoader loader = new TreeLoader(); + + Element element = browseDoc.getDocumentElement(); + NodeList children = element.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node node = children.item(i); + if (node instanceof Element) { + Element childElement = (Element) node; + TreeItem childItem = hierarchyTree.addItem(childElement.getAttribute("name")); + loader.push(childElement, childItem); + } + } + + loader.execute(); + } + catch (DOMException e) + { + } + } + else + { + } + } + + public void onError(Request request, Throwable e) + { + } + }); + } + catch (RequestException e) + { + } + } + + private class TreeLoader implements Command { + private int count = 0; + private ArrayList elements = new ArrayList(); + private ArrayList items = new ArrayList(); + + public void execute() { + for (int n = 0; n < 100; n++) { + if (count <= 0) + return; + + int index = --count; + Element element = (Element) elements.remove(index); + TreeItem item = (TreeItem) items.remove(index); + + NodeList children = element.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node node = children.item(i); + if (node instanceof Element) { + Element childElement = (Element) node; + TreeItem childItem = item.addItem(childElement.getAttribute("name")); + push(childElement, childItem); + } + } + } + DeferredCommand.addCommand(this); + } + + public final void push(Element element, TreeItem item) { + elements.add(element); + items.add(item); + count++; + } + } + + protected class MySettingsListener implements PGFWrapper.SettingsListener { + + private PGFWrapper pgf; + + public MySettingsListener(PGFWrapper pgf) { + this.pgf = pgf; + } + + public void onAvailableGrammarsChanged() { } + public void onSelectedGrammarChanged() + { + List ids = new ArrayList(); + + for (int i = 0; i < pgf.getCategories().length(); i++) { + ids.add(pgf.getCategories().get(i)); + } + for (int i = 0; i < pgf.getFunctions().length(); i++) { + ids.add(pgf.getFunctions().get(i)); + } + + Collections.sort(ids); + + identifiers = ids; + sourceView.setText(""); + searchBox.setText(""); + reloadHierarchyTree(); + } + public void onInputLanguageChanged() { } + public void onOutputLanguageChanged() { } + public void onStartCategoryChanged() { } + public void onSettingsError(String msg, Throwable e) { } + } +} diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/EditorApp.java b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/EditorApp.java new file mode 100644 index 000000000..107e2c6cd --- /dev/null +++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/EditorApp.java @@ -0,0 +1,460 @@ +package org.grammaticalframework.ui.gwt.client; + +import java.util.*; + +import com.google.gwt.core.client.*; +import com.google.gwt.user.client.*; +import com.google.gwt.user.client.ui.*; +import com.google.gwt.event.dom.client.*; +import com.google.gwt.event.logical.shared.*; +import com.google.gwt.event.shared.*; + +public class EditorApp implements EntryPoint { + + protected static final String pgfBaseURL = "/grammars"; + + protected PGFWrapper pgf; + + protected VerticalPanel outputPanel; + protected Widget translatePanel; + protected BrowsePanel browsePanel; + protected QueryPanel queryPanel; + protected StatusPopup statusPopup; + protected TextInputPanel textPanel; + protected FridgeBagPanel bagPanel; + protected MagnetFactory magnetFactory; + + private JSONRequest completeRequest = null; + private JSONRequest translateRequest = null; + + private int maxMagnets = 100; + private static final int LIMIT_SCALE_FACTOR = 4; + + private String cachedPrefix = null; + private List cachedMagnets = Collections.emptyList(); + + // + // Update + // + protected void update() { + clearMagnetCache(); + updateBag(""); + updateTranslation(); + } + + protected void clearMagnetCache() { + cachedPrefix = null; + cachedMagnets = Collections.emptyList(); + } + + protected void updateTranslation() { + if (translateRequest != null) { + translateRequest.cancel(); + } + + outputPanel.clear(); + outputPanel.addStyleDependentName("working"); + translateRequest = pgf.translate(textPanel.getText(), + new PGF.TranslateCallback() { + public void onResult (PGF.Translations translations) { + translateRequest = null; + + outputPanel.clear(); + outputPanel.removeStyleDependentName("working"); + for (PGF.TranslationResult tr : translations.iterable()) { + textPanel.renderBracketedString(tr.getBracketedString()); + + if (tr.getTranslations() != null) + for (PGF.Translation t : tr.getTranslations().iterable()) { + LinearizationsPanel lin = new LinearizationsPanel(pgf, t.getTree(), t.getLinearizations()); + lin.setWidth("100%"); + outputPanel.add(lin); + } + + if (tr.getTypeErrors() != null && tr.getTypeErrors().length > 0) { + for (PGF.TcError error : tr.getTypeErrors()) { + VerticalPanel panel = new VerticalPanel(); + panel.addStyleName("my-typeError"); + Label errLabel = new Label("Type Error"); + errLabel.addStyleName("title"); + panel.add(errLabel); + panel.add(createErrorMsg(error)); + outputPanel.add(panel); + } + textPanel.showError(tr.getTypeErrors()[0].getFId()); + } + } + } + public void onError (Throwable e) { + translateRequest = null; + + showError("Translation failed", e); + } + }); + } + + private class Callback { + private String prefix; + + public Callback(String prefix) { + this.prefix = prefix; + } + + public void onResult(List magnets) { + bagPanel.fill(magnets); + + if (magnets.size() == 0) { + if (prefix.isEmpty()) { + textPanel.hideSearchBox(); + textPanel.setFocus(true); + } + else + textPanel.showSearchError(); + } else { + textPanel.clearSearchError(); + } + } + } + + public void updateBag(String prefix) { + Callback callback = new Callback(prefix); + List magnets = filterCachedMagnets(prefix); + if (magnets != null) + callback.onResult(magnets); + else + retrieveMagnets(prefix, callback); + } + + public List filterCachedMagnets(final String prefix) { + if (prefix.length() > 0 && cachedPrefix != null && prefix.startsWith(cachedPrefix)) { + // If the prefix had no completions, there is no way that the current input will. + if (cachedMagnets.isEmpty()) { + return Collections.emptyList(); + } + + List magnets = new ArrayList(); + for (Magnet magnet : cachedMagnets) { + if (magnet.getWord().startsWith(prefix)) { + magnets.add(magnet); + if (magnets.size() >= maxMagnets) + return magnets; + } + } + } + return null; + } + + public void retrieveMagnets(final String prefix, final Callback callback) { + final String query = textPanel.getText() + " " + prefix; + + if (completeRequest != null) { + completeRequest.cancel(); + } + + bagPanel.clear(); + completeRequest = pgf.complete(query, LIMIT_SCALE_FACTOR * maxMagnets, + new PGF.CompleteCallback() { + public void onResult(PGF.Completions completions) { + completeRequest = null; + + cachedPrefix = query; + cachedMagnets = new ArrayList(); + + for (PGF.Completion completion : completions.iterable()) { + textPanel.renderBracketedString(completion.getBracketedString()); + if (completion.getCompletions() != null) { + if (completion.getText() != prefix) + textPanel.setSearchTerm(completion.getText()); + + for (String word : completion.getCompletions()) { + Magnet magnet = magnetFactory.createMagnet(word, completion.getFrom()); + cachedMagnets.add(magnet); + } + } else { + textPanel.setSearchTerm(completion.getText()); + } + } + + List magnets = new ArrayList(); + for (Magnet magnet : cachedMagnets) { + magnets.add(magnet); + if (magnets.size() >= maxMagnets) + break; + } + callback.onResult(magnets); + } + + public void onError(Throwable e) { + completeRequest = null; + + showError("Getting completions failed", e); + } + }); + } + + + // + // Status stuff + // + + protected void setStatus(String msg) { + statusPopup.setStatus(msg); + } + + protected void showError(String msg, Throwable e) { + statusPopup.showError(msg, e); + } + + protected void clearStatus() { + statusPopup.clearStatus(); + } + + // + // GUI + // + + protected Widget createUI() { + translatePanel = createTranslatePanel(); + browsePanel = createBrowsePanel(); + queryPanel = createQueryPanel(); + + VerticalPanel vPanel = new VerticalPanel(); + vPanel.setWidth("100%"); + vPanel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER); + + HorizontalPanel hPanel = new HorizontalPanel(); + hPanel.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE); + hPanel.setStylePrimaryName("my-HeaderPanel"); + + Widget linksPanel = createLinksPanel(vPanel); + hPanel.add(linksPanel); + hPanel.setCellHorizontalAlignment(linksPanel,HorizontalPanel.ALIGN_LEFT); + + Widget settingsPanel = createSettingsPanel(); + hPanel.add(settingsPanel); + hPanel.setCellHorizontalAlignment(settingsPanel,HorizontalPanel.ALIGN_RIGHT); + + vPanel.add(hPanel); + vPanel.add(translatePanel); + + History.newItem("translate"); + History.addHistoryListener(new MyHistoryListener(vPanel)); + + return vPanel; + } + + protected Widget createSettingsPanel () { + return new SettingsPanel(pgf); + } + + protected Widget createTranslatePanel() { + textPanel = new TextInputPanel(); + textPanel.addValueChangeHandler(new ValueChangeHandler() { + public void onValueChange(ValueChangeEvent event) { + update(); + } + }); + textPanel.addSelectionHandler(new SelectionHandler() { + public void onSelection(SelectionEvent event) { + String prefix = event.getSelectedItem(); + char lastChar = prefix.charAt(prefix.length()-1); + + Iterator iter = bagPanel.iterator(); + if ((Character.isSpace(lastChar) || lastChar == 160) && iter.hasNext()) { + Magnet magnet = iter.next(); + textPanel.setSearchTerm(""); + textPanel.addMagnet(magnet); + } + else + updateBag(prefix); + } + }); + + final ClickListener magnetClickListener = new ClickListener () { + public void onClick(Widget widget) { + Magnet magnet = (Magnet)widget; + textPanel.hideSearchBox(); + textPanel.addMagnet(magnet); + textPanel.setFocus(true); + } + }; + magnetFactory = new MagnetFactory(magnetClickListener); + + bagPanel = new FridgeBagPanel(); + + outputPanel = new VerticalPanel(); + outputPanel.addStyleName("my-translations"); + + final DockPanel translatePanel = new DockPanel(); + translatePanel.setStyleName("my-TranslatePanel"); + translatePanel.add(textPanel, DockPanel.NORTH); + translatePanel.add(bagPanel, DockPanel.CENTER); + translatePanel.add(outputPanel, DockPanel.EAST); + + translatePanel.setCellHeight(bagPanel, "100%"); + translatePanel.setCellWidth(bagPanel, "70%"); + translatePanel.setCellHeight(outputPanel, "100%"); + translatePanel.setCellWidth(outputPanel, "30%"); + translatePanel.setCellVerticalAlignment(bagPanel, HasVerticalAlignment.ALIGN_TOP); + translatePanel.setCellHorizontalAlignment(outputPanel, HasHorizontalAlignment.ALIGN_RIGHT); + + Window.addWindowResizeListener(new WindowResizeListener() { + public void onWindowResized(int w, int h) { + translatePanel.setPixelSize(w-20, h-50); + } + }); + int w = Window.getClientWidth(); + int h = Window.getClientHeight(); + translatePanel.setPixelSize(w-20, h-50); + + return translatePanel; + } + + protected BrowsePanel createBrowsePanel() { + return new BrowsePanel(pgf); + } + + protected QueryPanel createQueryPanel() { + return new QueryPanel(pgf); + } + + protected Widget createLinksPanel(final Panel parent) { + HorizontalPanel linksPanel = new HorizontalPanel(); + linksPanel.setStylePrimaryName("my-LinksPanel"); + + Hyperlink translateLink = new Hyperlink("Translate", "translate"); + translateLink.addClickListener(new ClickListener() { + public void onClick(Widget sender) { + parent.remove(browsePanel); + parent.remove(queryPanel); + parent.add(translatePanel); + } + }); + linksPanel.add(translateLink); + + Hyperlink queryLink = new Hyperlink("Query", "query"); + queryLink.addClickListener(new ClickListener() { + public void onClick(Widget sender) { + parent.remove(translatePanel); + parent.remove(browsePanel); + parent.add(queryPanel); + } + }); + linksPanel.add(queryLink); + + Hyperlink browseLink = new Hyperlink("Browse", "browse"); + browseLink.addClickListener(new ClickListener() { + public void onClick(Widget sender) { + parent.remove(translatePanel); + parent.remove(queryPanel); + parent.add(browsePanel); + browsePanel.onActivate(); + } + }); + linksPanel.add(browseLink); + + return linksPanel; + } + + protected Widget createErrorMsg(final PGF.TcError error) { + HTML msgHTML = new HTML("
"+error.getMsg()+"
"); + msgHTML.addStyleName("content"); + msgHTML.addClickListener(new ClickListener() { + public void onClick(Widget sender) { + textPanel.showError(error.getFId()); + } + }); + return msgHTML; + } + + // + // History stuff + // + + protected class MyHistoryListener implements HistoryListener { + private final Panel parent; + + public MyHistoryListener(Panel parent) { + this.parent = parent; + } + + public void onHistoryChanged(String token) { + if (token.equals("translate")) { + parent.remove(queryPanel); + parent.remove(browsePanel); + parent.add(translatePanel); + } else if (token.equals("query")) { + parent.remove(translatePanel); + parent.remove(browsePanel); + parent.add(queryPanel); + } else if (token.equals("browse")) { + parent.remove(translatePanel); + parent.remove(queryPanel); + parent.add(browsePanel); + browsePanel.onActivate(); + browsePanel.browse(null); + } else if (token.startsWith("browse:")) { + browsePanel.browse(token.substring(7)); + } + } + }; + + protected void setPGFName (String pgfName) { + if (pgfName != null && !pgfName.equals(pgf.getPGFName())) { + pgf.setPGFName(pgfName); + } + } + + protected void setInputLanguage (String inputLanguage) { + if (inputLanguage != null && !inputLanguage.equals(pgf.getInputLanguage())) { + pgf.setInputLanguage(inputLanguage); + } + } + + // + // Initialization + // + + protected class MySettingsListener implements PGFWrapper.SettingsListener { + // Will only happen on load + public void onAvailableGrammarsChanged() { + if (pgf.getPGFName() == null) { + List grammars = pgf.getGrammars(); + if (!grammars.isEmpty()) { + pgf.setPGFName(grammars.get(0)); + } + } + } + public void onSelectedGrammarChanged() { + textPanel.clear(); + if (pgf.getInputLanguage() == null) { + GWT.log("Setting input language to user language: " + pgf.getUserLanguage(), null); + pgf.setInputLanguage(pgf.getUserLanguage()); + } + update(); + } + public void onInputLanguageChanged() { + update(); + } + public void onOutputLanguageChanged() { + update(); + } + public void onStartCategoryChanged() { + update(); + } + public void onSettingsError(String msg, Throwable e) { + showError(msg,e); + } + } + + public void onModuleLoad() { + statusPopup = new StatusPopup(); + + pgf = new PGFWrapper(pgfBaseURL); + RootPanel.get().add(createUI()); + pgf.addSettingsListener(new MySettingsListener()); + pgf.updateAvailableGrammars(); + + textPanel.setFocus(true); + } +} diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/FridgeBagPanel.java b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/FridgeBagPanel.java index 657f3bf13..bab14808e 100644 --- a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/FridgeBagPanel.java +++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/FridgeBagPanel.java @@ -1,108 +1,38 @@ package org.grammaticalframework.ui.gwt.client; -import java.util.LinkedHashSet; +import java.util.*; import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.ui.*; -public class FridgeBagPanel extends Composite { +public class FridgeBagPanel extends Composite implements Iterable { - private PGFWrapper pgf; + private FlowPanel mainPanel; - private MagnetFactory magnetFactory; - - private JSONRequest completeRequest = null; - - private FlowPanel prefixPanel; - - private FlowPanel mainPanel; - - private int maxMagnets = 100; - - private LinkedHashSet prefixes = new LinkedHashSet(); - - - public FridgeBagPanel (PGFWrapper pgf, MagnetFactory magnetFactory) { - this.pgf = pgf; - this.magnetFactory = magnetFactory; - prefixPanel = new FlowPanel(); - prefixPanel.setStylePrimaryName("my-PrefixPanel"); + public FridgeBagPanel () { mainPanel = new FlowPanel(); - VerticalPanel vPanel = new VerticalPanel(); - vPanel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER); - vPanel.add(prefixPanel); - vPanel.add(mainPanel); - initWidget(new ScrollPanel(vPanel)); + + initWidget(new ScrollPanel(mainPanel)); setStylePrimaryName("my-FridgeBagPanel"); addStyleDependentName("empty"); } - public void updateBag (String text) { - updateBag(text, ""); - } - - public void updateBag (final String text, String prefix) { - if (completeRequest != null) { - completeRequest.cancel(); - } - final boolean updatePrefixes = prefix.equals(""); + public void clear() { mainPanel.clear(); - addStyleDependentName("empty"); - if (updatePrefixes) { clearPrefixes(); } - int limit = updatePrefixes ? 0 : maxMagnets; - completeRequest = pgf.complete(text + " " + prefix, - limit, new PGF.CompleteCallback() { - public void onResult(PGF.Completions completions) { - for (PGF.Completion completion : completions.iterable()) { - for (String word : completion.getCompletions()) { - if (updatePrefixes) { - addPrefix(text, word.substring(0,1)); - } - if (mainPanel.getWidgetCount() < maxMagnets) { - Magnet magnet = magnetFactory.createMagnet(word, completion.getFrom()); - mainPanel.add(magnet); - removeStyleDependentName("empty"); - } else { - prefixPanel.setVisible(true); - } - } - } - } - public void onError(Throwable e) { - // FIXME: show message to user? - GWT.log("Error getting completions.", e); - } - }); } - protected void clearPrefixes () { - prefixes.clear(); - prefixPanel.clear(); - prefixPanel.setVisible(false); - } - - protected void addPrefix(final String text, final String prefix) { - if (prefixes.add(prefix)) { - Button prefixButton = new Button(prefix, new ClickListener() { - public void onClick(Widget sender) { - updateBag(text, prefix); - } - }); - prefixButton.setTitle("Show only magnets stating with '" + prefix + "'"); - prefixPanel.add(prefixButton); + public void fill(List magnets) { + for (Magnet magnet : magnets) { + mainPanel.add(magnet); } + + if (mainPanel.getWidgetCount() == 0) + addStyleDependentName("empty"); + else + removeStyleDependentName("empty"); } - - /* - public void cloneMagnet (Magnet magnet) { - int i = getWidgetIndex(magnet); - GWT.log("cloneMagnet: " + magnet.getParent(), null); - if (i != -1) { - GWT.log("cloning", null); - insert(magnetFactory.createMagnet(magnet), i); - } + public Iterator iterator() { + return (Iterator) (Iterator) mainPanel.iterator(); } - */ - } diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/LinearizationsPanel.java b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/LinearizationsPanel.java new file mode 100644 index 000000000..2a916db29 --- /dev/null +++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/LinearizationsPanel.java @@ -0,0 +1,150 @@ +package org.grammaticalframework.ui.gwt.client; + +import java.util.*; + +import com.google.gwt.core.client.*; +import com.google.gwt.user.client.*; +import com.google.gwt.user.client.ui.*; +import com.google.gwt.event.dom.client.*; +import com.google.gwt.event.logical.shared.*; +import com.google.gwt.event.shared.*; + +public class LinearizationsPanel extends Composite { + + protected PGFWrapper pgf; + + public LinearizationsPanel(PGFWrapper pgf, String tree, PGF.Linearizations lins) { + this.pgf = pgf; + + HorizontalPanel hPanel = new HorizontalPanel(); + VerticalPanel linsPanel = new VerticalPanel(); + linsPanel.addStyleName("my-translation-bar"); + hPanel.add(linsPanel); + HorizontalPanel btnPanel = new HorizontalPanel(); + btnPanel.addStyleName("my-translation-btns"); + btnPanel.setSpacing(4); + btnPanel.add(createAbsTreeButton(tree)); + btnPanel.add(createAlignButton(tree)); + hPanel.add(btnPanel); + hPanel.setCellHorizontalAlignment(btnPanel,HasHorizontalAlignment.ALIGN_RIGHT); + + for (PGF.Linearization l : lins.iterable()) { + linsPanel.add(createTranslation(l.getTo(), tree, l.getText())); + } + + initWidget(hPanel); + setStylePrimaryName("my-translation-frame"); + } + + protected Widget createAbsTreeButton(final String abstractTree) { + Image treeBtn = new Image("org.grammaticalframework.ui.gwt.EditorApp/tree-btn.png"); + treeBtn.setTitle("Displays the abstract syntax tree."); + treeBtn.addClickListener( + new ClickListener() { + public void onClick(Widget sender) { + // Create a dialog box and set the caption text + final DialogBox dialogBox = new DialogBox(); + dialogBox.setText("Abstract Syntax Tree"); + + // Create a table to layout the content + HorizontalPanel dialogContents = new HorizontalPanel(); + dialogContents.setSpacing(4); + dialogBox.setWidget(dialogContents); + + // Add an image to the dialog + + Frame image = new Frame(pgf.graphvizAbstractTree(abstractTree)); + image.addStyleName("my-treeimage"); + dialogContents.add(image); + + // Add a close button at the bottom of the dialog + Button closeButton = new Button("Close", + new ClickListener() { + public void onClick(Widget sender) { + dialogBox.hide(); + } + }); + dialogContents.add(closeButton); + + dialogBox.center(); + dialogBox.show(); + } + }); + return treeBtn; + } + + protected Widget createAlignButton(final String abstractTree) { + Image alignBtn = new Image("org.grammaticalframework.ui.gwt.EditorApp/align-btn.png"); + alignBtn.setTitle("Displays word-alignment diagram."); + alignBtn.addClickListener( + new ClickListener() { + public void onClick(Widget sender) { + // Create a dialog box and set the caption text + final DialogBox dialogBox = new DialogBox(); + dialogBox.setText("Word Alignment"); + + // Create a table to layout the content + HorizontalPanel dialogContents = new HorizontalPanel(); + dialogContents.setSpacing(4); + dialogBox.setWidget(dialogContents); + + // Add an image to the dialog + Frame image = new Frame(pgf.graphvizAlignment(abstractTree)); + image.addStyleName("my-alignmentimage"); + dialogContents.add(image); + + // Add a close button at the bottom of the dialog + Button closeButton = new Button("Close", + new ClickListener() { + public void onClick(Widget sender) { + dialogBox.hide(); + } + }); + dialogContents.add(closeButton); + + dialogBox.center(); + dialogBox.show(); + } + }); + return alignBtn; + } + + protected Widget createTranslation(final String language, final String abstractTree, String text) { + Label l = new Label(text); + l.addStyleName("my-translation"); + String lang = pgf.getLanguageCode(language); + if (lang != null) { + l.getElement().setLang(lang); + } + l.addClickListener(new ClickListener() { + public void onClick(Widget sender) { + // Create a dialog box and set the caption text + final DialogBox dialogBox = new DialogBox(); + dialogBox.setText("Parse Tree"); + + // Create a table to layout the content + HorizontalPanel dialogContents = new HorizontalPanel(); + dialogContents.setSpacing(4); + dialogBox.setWidget(dialogContents); + + // Add an image to the dialog + Frame image = new Frame(pgf.graphvizParseTree(abstractTree, language)); + image.addStyleName("my-treeimage"); + dialogContents.add(image); + + // Add a close button at the bottom of the dialog + Button closeButton = new Button("Close", + new ClickListener() { + public void onClick(Widget sender) { + dialogBox.hide(); + } + }); + dialogContents.add(closeButton); + + dialogBox.center(); + dialogBox.show(); + } + }); + return l; + } +} diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/MagnetSearchBox.java b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/MagnetSearchBox.java new file mode 100644 index 000000000..5a2a70401 --- /dev/null +++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/MagnetSearchBox.java @@ -0,0 +1,49 @@ +package org.grammaticalframework.ui.gwt.client; + +import java.util.*; + +import com.google.gwt.core.client.*; +import com.google.gwt.user.client.*; +import com.google.gwt.user.client.ui.*; +import com.google.gwt.event.dom.client.*; +import com.google.gwt.event.logical.shared.*; +import com.google.gwt.event.shared.*; +import com.google.gwt.dom.client.Node; +import com.google.gwt.dom.client.Text; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.Document; +import org.grammaticalframework.ui.gwt.client.selection.*; + +public class MagnetSearchBox extends FocusWidget { + public MagnetSearchBox() { + this(Document.get().createDivElement()); + } + + public MagnetSearchBox(Element elem) { + super(elem); + elem.setAttribute("contentEditable", "true"); + setStyleName("searchbox"); + } + + public String getText() { + return getElement().getInnerText(); + } + + public void setText(String s) { + getElement().setInnerText(s); + } + + public int getCursorPos() { + return 0; + } + + public void setCursorPos(int pos) { + Node child = getElement().getFirstChild(); + if (child instanceof Text) { + SelectionEndPoint selPoint = new SelectionEndPoint((Text) child,pos); + Selection sel = Selection.getSelection(); + sel.select(selPoint,selPoint); + } + return; + } +} diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/PGF.java b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/PGF.java index a6c5cab55..e3396cad1 100644 --- a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/PGF.java +++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/PGF.java @@ -74,17 +74,6 @@ public class PGF { public final native Linearizations getLinearizations() /*-{ return this.linearizations; }-*/; } - public static class Linearizations extends IterableJsArray { - protected Linearizations() { } - } - - public static class Linearization extends JavaScriptObject { - protected Linearization() { } - - public final native String getTo() /*-{ return this.to; }-*/; - public final native String getText() /*-{ return this.text; }-*/; - } - /* Completion */ /** @@ -152,20 +141,6 @@ public class PGF { public final native int getFId() /*-{ return this.fid; }-*/; public final native int getIndex() /*-{ return this.index; }-*/; public final native BracketedString[] getChildren() /*-{ return this.children; }-*/; - - public final String render() { - if (getToken() != null) - return getToken(); - else { - StringBuilder sbuilder = new StringBuilder(); - for (BracketedString bs : getChildren()) { - if (sbuilder.length() > 0) - sbuilder.append(' '); - sbuilder.append(bs.render()); - } - return sbuilder.toString(); - } - } } public static class TcError extends JavaScriptObject { @@ -174,6 +149,29 @@ public class PGF { public final native int getFId() /*-{ return this.fid; }-*/; public final native String getMsg() /*-{ return this.msg; }-*/; } + + + /* Linearization */ + + public JSONRequest linearize (String pgfURL, String tree, String toLang, final LinearizeCallback callback) { + List args = new ArrayList(); + args.add(new Arg("tree", tree)); + args.add(new Arg("to", toLang)); + return sendGrammarRequest(pgfURL, "linearize", args, callback); + } + + public interface LinearizeCallback extends JSONCallback { } + + public static class Linearizations extends IterableJsArray { + protected Linearizations() { } + } + + public static class Linearization extends JavaScriptObject { + protected Linearization() { } + + public final native String getTo() /*-{ return this.to; }-*/; + public final native String getText() /*-{ return this.text; }-*/; + } public String graphvizAbstractTree(String pgfURL, String abstractTree) { List args = new ArrayList(); @@ -216,8 +214,20 @@ public class PGF { return request; } + + public JSONRequest query(String pgfURL, String query, QueryCallback callback) { + List args = new ArrayList(); + args.add(new Arg("cat", query)); + return sendGrammarRequest(pgfURL, "query", args, callback); + } + + public interface QueryCallback extends JSONCallback {} - /* Common */ + public static class QueryResult extends JavaScriptObject { + protected QueryResult() { } + + public final native String[] getRows() /*-{ return this.rows; }-*/; + } public JSONRequest sendGrammarRequest(String pgfURL, String resource, List args, final JSONCallback callback) { args.add(new Arg("command", resource)); diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/PGFWrapper.java b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/PGFWrapper.java index b8087ab9b..b1dfcf278 100644 --- a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/PGFWrapper.java +++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/PGFWrapper.java @@ -138,6 +138,10 @@ public class PGFWrapper { return pgf.parse(grammarURL, input, inputLanguage, cat, callback); } + public JSONRequest linearize (String tree, final PGF.LinearizeCallback callback) { + return pgf.linearize(grammarURL, tree, outputLanguage, callback); + } + public String graphvizAbstractTree(String abstractTree) { return pgf.graphvizAbstractTree(grammarURL,abstractTree); } @@ -154,6 +158,10 @@ public class PGFWrapper { return pgf.browse(grammarURL, id, href, cssClass, callback); } + public JSONRequest query(String query, PGF.QueryCallback callback) { + return pgf.query(grammarURL, query, callback); + } + // // Settings // @@ -242,7 +250,7 @@ public class PGFWrapper { PGF.Language l = languages.get(language); return l == null ? null : l.getLanguageCode(); } - + public Collection getAllLanguages() { return languages.keySet(); } diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/QueryPanel.java b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/QueryPanel.java new file mode 100644 index 000000000..6f732dbe2 --- /dev/null +++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/QueryPanel.java @@ -0,0 +1,109 @@ +package org.grammaticalframework.ui.gwt.client; + +import java.util.*; + +import com.google.gwt.http.client.*; +import com.google.gwt.user.client.ui.*; + +public class QueryPanel extends Composite { + + private PGFWrapper pgf; + + public QueryPanel(PGFWrapper pgf) { + this.pgf = pgf; + + VerticalPanel vPanel = new VerticalPanel(); + vPanel.add(createQueryPanel()); + + initWidget(vPanel); + setStylePrimaryName("my-QueryPanel"); + + pgf.addSettingsListener(new MySettingsListener(pgf)); + } + + protected Widget createQueryPanel() { + final TextArea queryBox = new TextArea(); + queryBox.setStylePrimaryName("my-QueryBox"); + + final Grid resultGrid = new Grid(0, 1); + resultGrid.setStylePrimaryName("my-ResultGrid"); + resultGrid.setBorderWidth(3); + + Button execButton = new Button("Execute"); + execButton.addClickListener(new ClickListener() { + public void onClick(Widget sender) { + pgf.query(queryBox.getText(), new PGF.QueryCallback() { + public void onResult(PGF.QueryResult result) { + while (resultGrid.getRowCount() > 0) { + resultGrid.removeRow(resultGrid.getRowCount() - 1); + } + + ClickListener labelClickListener = new ClickListener() { + public void onClick(Widget sender) { + final Label label = (Label) sender; + pgf.linearize(label.getText(), new PGF.LinearizeCallback() { + public void onResult(PGF.Linearizations result) { + final PopupPanel popup = new PopupPanel(true); + popup.setWidget(new LinearizationsPanel(pgf, label.getText(), result)); + popup.setPopupPosition(label.getAbsoluteLeft(), + label.getAbsoluteTop()+label.getOffsetHeight()); + popup.show(); + } + + public void onError(Throwable e) { + + } + }); + } + }; + + int row = 0; + for (String tree : result.getRows()) { + Label label = new Label(tree); + label.addClickListener(labelClickListener); + resultGrid.insertRow(row); + resultGrid.setWidget(row, 0, label); + row++; + } + } + + public void onError(Throwable e) { + + } + }); + } + }); + + DecoratorPanel queryDecorator = new DecoratorPanel(); + VerticalPanel vPanel = new VerticalPanel(); + vPanel.add(new Label("Query")); + HorizontalPanel hPanel = new HorizontalPanel(); + hPanel.add(queryBox); + hPanel.add(execButton); + vPanel.add(hPanel); + queryDecorator.add(vPanel); + + VerticalPanel queryPanel = new VerticalPanel(); + queryPanel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER); + queryPanel.add(queryDecorator); + queryPanel.add(resultGrid); + + return queryPanel; + } + + protected class MySettingsListener implements PGFWrapper.SettingsListener { + + private PGFWrapper pgf; + + public MySettingsListener(PGFWrapper pgf) { + this.pgf = pgf; + } + + public void onAvailableGrammarsChanged() { } + public void onSelectedGrammarChanged() { } + public void onInputLanguageChanged() { } + public void onOutputLanguageChanged() { } + public void onStartCategoryChanged() { } + public void onSettingsError(String msg, Throwable e) { } + } +} diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/TextInputPanel.java b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/TextInputPanel.java new file mode 100644 index 000000000..cfaec4f03 --- /dev/null +++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/TextInputPanel.java @@ -0,0 +1,547 @@ +package org.grammaticalframework.ui.gwt.client; + +import java.util.*; + +import com.google.gwt.core.client.*; +import com.google.gwt.user.client.*; +import com.google.gwt.user.client.ui.*; +import com.google.gwt.event.dom.client.*; +import com.google.gwt.event.logical.shared.*; +import com.google.gwt.event.shared.*; + +public class TextInputPanel extends Composite implements Focusable, HasValueChangeHandlers, HasSelectionHandlers { + + protected FlowPanel textPanel = null; + protected FlowPanel mainPanel = null; + protected FocusPanel focusPanel = null; + protected Panel focusedPanel = null; + protected List selectedPanels = null; + protected List errorPanels = null; + protected Panel tempPanel = null; + protected Label status = null; + protected NavigationController navigationController; + protected MagnetSearchBox searchBox = null; + + private List