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