summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorkrasimir <krasimir@chalmers.se>2010-09-29 13:17:48 +0000
committerkrasimir <krasimir@chalmers.se>2010-09-29 13:17:48 +0000
commit3b11f81c6bdb536ac5cc7c9c729d2ff22836e894 (patch)
tree07a48bc5a34105e40c64bc7907006c7013150908 /src/ui
parent8b7d27fe4c2975fb5e8718f4b79face22733eb0b (diff)
initial import of the new GF editor. Note: the FridgeApp is temporary broken. It will be fixed with the next patch
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/EditorApp.gwt.xml6
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/BrowsePanel.java267
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/EditorApp.java460
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/FridgeBagPanel.java104
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/LinearizationsPanel.java150
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/MagnetSearchBox.java49
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/PGF.java62
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/PGFWrapper.java10
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/QueryPanel.java109
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/TextInputPanel.java547
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/Selection.java66
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/SelectionEndPoint.java69
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/support/InternetExplorerSelectionSupport.java248
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/support/SelectionSupport.java69
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/Editor.css253
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/background.pngbin0 -> 760 bytes
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/new.pngbin0 -> 230 bytes
-rw-r--r--src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/textinput-buttons.pngbin0 -> 518 bytes
18 files changed, 2353 insertions, 116 deletions
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 @@
<!-- <inherits name="com.google.gwt.user.theme.chrome.Chrome"/> -->
<!-- <inherits name="com.google.gwt.user.theme.dark.Dark"/> -->
- <!-- Other module inherits -->
-
+ <replace-with class="org.grammaticalframework.ui.gwt.client.selection.support.InternetExplorerSelectionSupport">
+ <when-type-is class="org.grammaticalframework.ui.gwt.client.selection.support.SelectionSupport"/>
+ <when-property-is name="user.agent" value="ie6"/>
+ </replace-with>
<!-- Specify the app entry point class. -->
<entry-point class="org.grammaticalframework.ui.gwt.client.EditorApp" />
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<String> 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<TreeItem>() {
+ public void onSelection(SelectionEvent<TreeItem> 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<CompletionSuggestion> 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<Element> elements = new ArrayList<Element>();
+ private ArrayList<TreeItem> items = new ArrayList<TreeItem>();
+
+ 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<String> 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<Magnet> 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<Magnet> 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<Magnet> magnets = filterCachedMagnets(prefix);
+ if (magnets != null)
+ callback.onResult(magnets);
+ else
+ retrieveMagnets(prefix, callback);
+ }
+
+ public List<Magnet> 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<Magnet> magnets = new ArrayList<Magnet>();
+ 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<Magnet>();
+
+ 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<Magnet> magnets = new ArrayList<Magnet>();
+ 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<String>() {
+ public void onValueChange(ValueChangeEvent<String> event) {
+ update();
+ }
+ });
+ textPanel.addSelectionHandler(new SelectionHandler<String>() {
+ public void onSelection(SelectionEvent<String> event) {
+ String prefix = event.getSelectedItem();
+ char lastChar = prefix.charAt(prefix.length()-1);
+
+ Iterator<Magnet> 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("<pre>"+error.getMsg()+"</pre>");
+ 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<String> 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<Magnet> {
- 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<String> prefixes = new LinkedHashSet<String>();
-
-
- 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<Magnet> magnets) {
+ for (Magnet magnet : magnets) {
+ mainPanel.add(magnet);
}
- }
-
- /*
- 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);
- }
+ if (mainPanel.getWidgetCount() == 0)
+ addStyleDependentName("empty");
+ else
+ removeStyleDependentName("empty");
}
- */
+ public Iterator<Magnet> iterator() {
+ return (Iterator<Magnet>) (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<Linearization> {
- 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<Arg> args = new ArrayList<Arg>();
+ args.add(new Arg("tree", tree));
+ args.add(new Arg("to", toLang));
+ return sendGrammarRequest(pgfURL, "linearize", args, callback);
+ }
+
+ public interface LinearizeCallback extends JSONCallback<Linearizations> { }
+
+ public static class Linearizations extends IterableJsArray<Linearization> {
+ 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<Arg> args = new ArrayList<Arg>();
@@ -216,8 +214,20 @@ public class PGF {
return request;
}
+
+ public JSONRequest query(String pgfURL, String query, QueryCallback callback) {
+ List<Arg> args = new ArrayList<Arg>();
+ args.add(new Arg("cat", query));
+ return sendGrammarRequest(pgfURL, "query", args, callback);
+ }
+
+ public interface QueryCallback extends JSONCallback<QueryResult> {}
- /* Common */
+ public static class QueryResult extends JavaScriptObject {
+ protected QueryResult() { }
+
+ public final native String[] getRows() /*-{ return this.rows; }-*/;
+ }
public <T extends JavaScriptObject> JSONRequest sendGrammarRequest(String pgfURL, String resource, List<Arg> args, final JSONCallback<T> 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<String> 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<String>, HasSelectionHandlers<String> {
+
+ protected FlowPanel textPanel = null;
+ protected FlowPanel mainPanel = null;
+ protected FocusPanel focusPanel = null;
+ protected Panel focusedPanel = null;
+ protected List<Panel> selectedPanels = null;
+ protected List<Panel> errorPanels = null;
+ protected Panel tempPanel = null;
+ protected Label status = null;
+ protected NavigationController navigationController;
+ protected MagnetSearchBox searchBox = null;
+
+ private List<Label> words = new ArrayList<Label>();
+
+ private Map<Panel, Phrase> mapPanel2Phrase = new HashMap<Panel, Phrase>();
+ private Map<Integer, Phrase> mapFId2Phrase = new HashMap<Integer, Phrase>();
+
+ private ChangeListenerCollection changeListeners = null;
+
+ public TextInputPanel() {
+ mainPanel = new FlowPanel();
+ mainPanel.setStylePrimaryName("wordspanel");
+
+ textPanel = new FlowPanel();
+ textPanel.add(mainPanel);
+ textPanel.setStylePrimaryName("wordspanel");
+
+ Label space = new Label(" ");
+ space.setStylePrimaryName("wordspace");
+ textPanel.add(space);
+
+ Panel contentPanel = new FlowPanel();
+ contentPanel.add(textPanel);
+ contentPanel.setStylePrimaryName("text");
+
+ focusPanel = new FocusPanel();
+ focusPanel.setWidget(contentPanel);
+ focusPanel.setStylePrimaryName("frame");
+
+ Widget buttons = createToolbarPanel();
+
+ VerticalPanel wrapper = new VerticalPanel();
+ wrapper.add(buttons);
+ wrapper.add(focusPanel);
+ initWidget(wrapper);
+ setStylePrimaryName("my-TextInputPanel");
+
+ navigationController = new NavigationController();
+ focusPanel.addKeyDownHandler(navigationController);
+ }
+
+ protected Widget createToolbarPanel() {
+ HorizontalPanel toolbar = new HorizontalPanel();
+ toolbar.setStylePrimaryName("toolbar");
+
+ Panel buttons = new HorizontalPanel();
+ toolbar.add(buttons);
+ toolbar.setCellHorizontalAlignment(buttons,HorizontalPanel.ALIGN_LEFT);
+ toolbar.setCellVerticalAlignment(buttons,HorizontalPanel.ALIGN_MIDDLE);
+
+ Image clearButton = new Image("org.grammaticalframework.ui.gwt.EditorApp/textinput-buttons.png",0,0,20,20);
+ clearButton.setTitle("Clears the whole text.");
+ clearButton.setStylePrimaryName("button");
+ clearButton.addClickListener(new ClickListener () {
+ public void onClick(Widget sender) {
+ clear();
+ }
+ });
+ buttons.add(clearButton);
+
+ Image deleteLastButton = new Image("org.grammaticalframework.ui.gwt.EditorApp/textinput-buttons.png",20,0,20,20);
+ deleteLastButton.setTitle("Removes the last word.");
+ deleteLastButton.setStylePrimaryName("button");
+ deleteLastButton.addClickListener(new ClickListener () {
+ public void onClick(Widget sender) {
+ deleteLast();
+ }
+ });
+ buttons.add(deleteLastButton);
+
+ status = new Label();
+ status.setTitle("The currently selected category.");
+ status.setStylePrimaryName("status");
+ toolbar.add(status);
+ toolbar.setCellHorizontalAlignment(status,HorizontalPanel.ALIGN_RIGHT);
+ toolbar.setCellVerticalAlignment(status,HorizontalPanel.ALIGN_MIDDLE);
+
+ return toolbar;
+ }
+
+ public void renderBracketedString(final PGF.BracketedString bs) {
+ words.clear();
+ mapPanel2Phrase.clear();
+ mapFId2Phrase.clear();
+ mainPanel.clear();
+ selectedPanels = null;
+ focusedPanel = null;
+ errorPanels = null;
+ tempPanel = null;
+
+ Widget widget = createWordPanels(bs);
+ mainPanel.add(widget);
+ }
+
+ private Widget createWordPanels(final PGF.BracketedString bs) {
+ if (bs.getToken() != null) {
+ Label wordLabel = new Label(bs.getToken());
+ wordLabel.setStylePrimaryName("wordlabel");
+ wordLabel.addClickListener(navigationController);
+ words.add(wordLabel);
+ return wordLabel;
+ } else {
+ FlowPanel panel = new FlowPanel();
+ panel.setStylePrimaryName("wordspanel");
+
+ Integer fid = new Integer(bs.getFId());
+ Phrase phrase = mapFId2Phrase.get(fid);
+ if (phrase == null) {
+ phrase = new Phrase();
+ phrase.cat = bs.getCat();
+ phrase.panels = new ArrayList<Panel>();
+ mapFId2Phrase.put(fid,phrase);
+ }
+ phrase.panels.add(panel);
+ mapPanel2Phrase.put(panel, phrase);
+
+ for (PGF.BracketedString child : bs.getChildren()) {
+ if (panel.getWidgetCount() > 0) {
+ Label space = new Label(" ");
+ space.setStylePrimaryName("wordspace");
+ panel.add(space);
+ }
+ panel.add(createWordPanels(child));
+ }
+ return panel;
+ }
+ }
+
+ public void clear() {
+ setSearchTerm("");
+ words.clear();
+ mapPanel2Phrase.clear();
+ mapFId2Phrase.clear();
+ mainPanel.clear();
+ selectedPanels = null;
+ focusedPanel = null;
+ errorPanels = null;
+ tempPanel = null;
+ fireValueChange();
+ }
+
+ public void addMagnet(Magnet magnet) {
+ Label wordLabel = new Label(magnet.getText());
+ wordLabel.setStylePrimaryName("wordlabel");
+ getTempPanel().add(wordLabel);
+ words.add(wordLabel);
+
+ fireValueChange();
+ }
+
+ public String deleteLast() {
+ int wordsCount = words.size();
+ if (wordsCount <= 0)
+ return null;
+ Label lastWord = words.remove(wordsCount-1);
+
+ setSearchTerm("");
+ mapPanel2Phrase.clear();
+ mapFId2Phrase.clear();
+ mainPanel.clear();
+ selectedPanels = null;
+ focusedPanel = null;
+ errorPanels = null;
+ tempPanel = null;
+ for (Label word : words) {
+ if (((FlowPanel) getTempPanel()).getWidgetCount() > 0) {
+ Label space = new Label(" ");
+ space.setStylePrimaryName("wordspace");
+ getTempPanel().add(space);
+ }
+ getTempPanel().add(word);
+ }
+ fireValueChange();
+
+ return lastWord.getText();
+ }
+
+ public void showSearchBox() {
+ if (searchBox == null) {
+ searchBox = new MagnetSearchBox();
+ SearchBoxKeyboardHandler handler = new SearchBoxKeyboardHandler();
+ searchBox.addKeyUpHandler(handler);
+ searchBox.addKeyDownHandler(handler);
+
+ textPanel.add(searchBox);
+ searchBox.setFocus(true);
+ }
+ }
+
+ public void hideSearchBox() {
+ if (searchBox != null) {
+ searchBox.removeFromParent();
+ searchBox = null;
+ }
+ }
+
+ public void setSearchTerm(String term) {
+ if (searchBox != null) {
+ searchBox.setText(term);
+ if ("".equals(term))
+ searchBox.setCursorPos(0);
+ }
+ }
+
+ public String getSearchTerm() {
+ if (searchBox != null)
+ return searchBox.getText();
+ else
+ return null;
+ }
+
+ public void showSearchError() {
+ if (searchBox != null) {
+ searchBox.addStyleDependentName("error");
+ }
+ }
+
+ public void clearSearchError() {
+ if (searchBox != null) {
+ searchBox.removeStyleDependentName("error");
+ }
+ }
+
+ public void showError(int fid) {
+ if (errorPanels != null) {
+ for (Panel panel : errorPanels) {
+ panel.removeStyleDependentName("error");
+ }
+ errorPanels = null;
+ }
+
+ Phrase phrase = mapFId2Phrase.get(fid);
+ if (phrase != null) {
+ errorPanels = phrase.panels;
+ if (errorPanels != null) {
+ for (Panel selPanel : errorPanels) {
+ selPanel.addStyleDependentName("error");
+ }
+ }
+ }
+ }
+
+ private Panel getTempPanel() {
+ if (tempPanel == null) {
+ if (mainPanel.getWidgetCount() > 0) {
+ Label space = new Label(" ");
+ space.setStylePrimaryName("wordspace");
+ mainPanel.add(space);
+ }
+
+ tempPanel = new FlowPanel();
+ tempPanel.setStylePrimaryName("wordspanel");
+ mainPanel.add(tempPanel);
+ }
+ return tempPanel;
+ }
+
+ protected void fireValueChange() {
+ DeferredCommand.addCommand(new Command() {
+ public void execute() {
+ ValueChangeEvent.fire(TextInputPanel.this, getText());
+ }
+ });
+ }
+
+ protected void fireSelection() {
+ SelectionEvent.fire(this, (searchBox == null) ? "" : searchBox.getText());
+ }
+
+ public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) {
+ return addHandler(handler, ValueChangeEvent.getType());
+ }
+
+ public HandlerRegistration addSelectionHandler(SelectionHandler<String> handler) {
+ return addHandler(handler, SelectionEvent.getType());
+ }
+
+ public String getText () {
+ StringBuilder sb = new StringBuilder();
+ for (Label word : words) {
+ if (sb.length() > 0) {
+ sb.append(' ');
+ }
+ sb.append(word.getText());
+ }
+ return sb.toString();
+ }
+
+ public int getTabIndex() {
+ return focusPanel.getTabIndex();
+ }
+
+ public void setTabIndex(int index) {
+ focusPanel.setTabIndex(index);
+ }
+
+ public void setAccessKey(char key) {
+ focusPanel.setAccessKey(key);
+ }
+
+ public void setFocus(boolean focused) {
+ focusPanel.setFocus(focused);
+ }
+
+ private class Phrase {
+ public String cat;
+ public ArrayList<Panel> panels;
+ }
+
+ private final class NavigationController implements KeyDownHandler, ClickListener {
+
+ public void onKeyDown(KeyDownEvent event) {
+ switch (event.getNativeKeyCode()) {
+ case KeyCodes.KEY_UP:
+ if (focusedPanel != null) {
+ Panel firstUp = null;
+ FlowPanel parent = (FlowPanel) focusedPanel.getParent();
+ while (parent != mainPanel) {
+ if (parent.getWidgetCount() > 1) {
+ firstUp = parent;
+ break;
+ }
+
+ parent = (FlowPanel) parent.getParent();
+ }
+
+ if (firstUp != null)
+ setFocusedPanel(firstUp);
+ event.stopPropagation();
+ }
+ break;
+ case KeyCodes.KEY_DOWN:
+ if (focusedPanel != null) {
+ Panel firstDown = null;
+ for (Widget child : focusedPanel) {
+ if (child instanceof Panel) {
+ firstDown = (Panel) child;
+ break;
+ }
+ }
+ if (firstDown != null)
+ setFocusedPanel(firstDown);
+ event.stopPropagation();
+ }
+ break;
+ case KeyCodes.KEY_LEFT:
+ if (focusedPanel != null) {
+ Panel firstLeft = null;
+ Panel parent = (Panel) focusedPanel.getParent();
+ for (Widget child : parent) {
+ if (child instanceof Panel) {
+ if (child == focusedPanel)
+ break;
+ firstLeft = (Panel) child;
+ }
+ }
+
+ if (firstLeft == null) {
+ if (parent != mainPanel)
+ firstLeft = parent;
+ } else {
+ for (;;) {
+ Panel lastChild = null;
+ for (Widget child : firstLeft) {
+ if (child instanceof Panel) {
+ lastChild = (Panel) child;
+ }
+ }
+ if (lastChild == null)
+ break;
+ firstLeft = lastChild;
+ }
+ }
+ if (firstLeft != null)
+ setFocusedPanel(firstLeft);
+ event.stopPropagation();
+ }
+ break;
+ case KeyCodes.KEY_RIGHT:
+ if (focusedPanel != null) {
+ Panel firstRight = null;
+ Panel parent = (Panel) focusedPanel.getParent();
+ Widget prev = null;
+ for (Widget child : parent) {
+ if (child instanceof Panel) {
+ if (prev == focusedPanel) {
+ firstRight = (Panel) child;
+ break;
+ }
+ prev = child;
+ }
+ }
+
+ if (firstRight == null) {
+ if (parent != mainPanel)
+ firstRight = parent;
+ } else {
+ for (;;) {
+ Panel firstChild = null;
+ for (Widget child : firstRight) {
+ if (child instanceof Panel) {
+ firstChild = (Panel) child;
+ break;
+ }
+ }
+ if (firstChild == null)
+ break;
+ firstRight = firstChild;
+ }
+ }
+ if (firstRight != null)
+ setFocusedPanel(firstRight);
+ event.stopPropagation();
+ }
+ break;
+ case KeyCodes.KEY_ENTER:
+ case KeyCodes.KEY_ESCAPE:
+ break;
+ default:
+ if (searchBox == null) {
+ showSearchBox();
+ searchBox.fireEvent(event);
+ }
+ }
+ }
+
+ public void onClick(Widget sender) {
+ FlowPanel panel = (FlowPanel) sender.getParent();
+ FlowPanel tmpPanel = panel;
+ while (tmpPanel != mainPanel) {
+ FlowPanel parent = (FlowPanel) tmpPanel.getParent();
+
+ if (tmpPanel == focusedPanel && parent != mainPanel) {
+ panel = parent;
+ break;
+ }
+
+ tmpPanel = parent;
+ }
+
+ tmpPanel = (FlowPanel) panel.getParent();
+ while (tmpPanel != mainPanel) {
+ if (tmpPanel.getWidgetCount() > 1)
+ break;
+
+ panel = tmpPanel;
+ tmpPanel = (FlowPanel) panel.getParent();
+ }
+
+ setFocusedPanel(panel);
+ }
+
+ private void setFocusedPanel(Panel panel) {
+ if (selectedPanels != null) {
+ for (Panel tmpPanel : selectedPanels) {
+ tmpPanel.removeStyleDependentName("selected");
+ }
+ selectedPanels = null;
+ }
+
+ if (focusedPanel != null) {
+ focusedPanel.removeStyleDependentName("focused");
+ focusedPanel = null;
+ }
+
+ Phrase phrase = mapPanel2Phrase.get(panel);
+ if (phrase != null) {
+ status.setText(phrase.cat);
+ selectedPanels = phrase.panels;
+ if (selectedPanels != null) {
+ for (Panel selPanel : selectedPanels) {
+ selPanel.addStyleDependentName("selected");
+ }
+ }
+
+ focusedPanel = panel;
+ focusedPanel.addStyleDependentName("focused");
+ }
+ }
+ }
+
+ private final class SearchBoxKeyboardHandler implements KeyUpHandler, KeyDownHandler {
+
+ public void onKeyDown(KeyDownEvent event) {
+ switch (event.getNativeKeyCode()) {
+ case KeyCodes.KEY_ESCAPE:
+ hideSearchBox();
+ fireSelection();
+ setFocus(true);
+ event.stopPropagation();
+ break;
+ case KeyCodes.KEY_ENTER:
+ searchBox.setText(searchBox.getText()+" ");
+ fireSelection();
+ hideSearchBox();
+ setFocus(true);
+ event.stopPropagation();
+ break;
+ case KeyCodes.KEY_BACKSPACE:
+ if ("".equals(searchBox.getText())) {
+ String word = deleteLast();
+ searchBox.setText(word);
+ searchBox.setCursorPos(word.length());
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ break;
+ }
+ }
+
+ public void onKeyUp(KeyUpEvent event) {
+ switch (event.getNativeKeyCode()) {
+ case KeyCodes.KEY_ESCAPE:
+ case KeyCodes.KEY_ENTER:
+ case KeyCodes.KEY_UP:
+ case KeyCodes.KEY_DOWN:
+ case KeyCodes.KEY_LEFT:
+ case KeyCodes.KEY_RIGHT:
+ break;
+ default:
+ fireSelection();
+ }
+ }
+ }
+}
diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/Selection.java b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/Selection.java
new file mode 100644
index 000000000..ceee8c1a7
--- /dev/null
+++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/Selection.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright Miroslav Pokorny
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.grammaticalframework.ui.gwt.client.selection;
+
+import org.grammaticalframework.ui.gwt.client.selection.support.SelectionSupport;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.ui.RootPanel;
+
+/**
+ * The Selection class is a singleton that represents any selection made by the
+ * user typically done with the mouse.
+ *
+ * @author Miroslav Pokorny (mP)
+ */
+public class Selection extends JavaScriptObject {
+
+ /**
+ * The browser aware support that takes care of browser difference nasties.
+ */
+ static private SelectionSupport support = (SelectionSupport) GWT.create(SelectionSupport.class);
+
+ static SelectionSupport getSupport() {
+ return Selection.support;
+ }
+
+ /**
+ * Returns the document Selection singleton
+ *
+ * @return The singleton instance
+ */
+ static public Selection getSelection() {
+ return Selection.support.getSelection();
+ }
+
+ protected Selection() {
+ super();
+ }
+
+ final public SelectionEndPoint getStart() {
+ return Selection.getSupport().getStart(this);
+ }
+
+ final public SelectionEndPoint getEnd() {
+ return Selection.getSupport().getEnd(this);
+ }
+
+ final public void select(final SelectionEndPoint start, final SelectionEndPoint end) {
+ Selection.getSupport().select(this, start, end);
+ }
+}
diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/SelectionEndPoint.java b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/SelectionEndPoint.java
new file mode 100644
index 000000000..d429b4b19
--- /dev/null
+++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/SelectionEndPoint.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright Miroslav Pokorny
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.grammaticalframework.ui.gwt.client.selection;
+
+import com.google.gwt.dom.client.Text;
+
+/**
+ * An end point uses a combination of a textNode and offset to mark the
+ * start/end of a selection
+ *
+ * @author Miroslav Pokorny (mP)
+ */
+public class SelectionEndPoint {
+
+ public SelectionEndPoint() {
+ super();
+ }
+
+ public SelectionEndPoint(Text text, final int offset) {
+ super();
+
+ this.setTextNode(text);
+ this.setOffset(offset);
+ }
+
+ /**
+ * The textNode containing the start/end of the selection.
+ */
+ private Text textNode;
+
+ public Text getTextNode() {
+ return textNode;
+ }
+
+ public void setTextNode(final Text textNode) {
+ this.textNode = textNode;
+ }
+
+ /**
+ * The number of characters starting from the beginning of the textNode
+ * where the selection begins/ends.
+ */
+ public int offset;
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public void setOffset(final int offset) {
+ this.offset = offset;
+ }
+
+ public String toString() {
+ return super.toString() + ", offset: " + offset + ", textNode\"" + this.textNode + "\"";
+ }
+}
diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/support/InternetExplorerSelectionSupport.java b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/support/InternetExplorerSelectionSupport.java
new file mode 100644
index 000000000..f986365c7
--- /dev/null
+++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/support/InternetExplorerSelectionSupport.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright Miroslav Pokorny
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.grammaticalframework.ui.gwt.client.selection.support;
+
+import org.grammaticalframework.ui.gwt.client.selection.Selection;
+import org.grammaticalframework.ui.gwt.client.selection.SelectionEndPoint;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.dom.client.Text;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+
+/**
+ * A specialised SelectionSupport class that is adapted to handle
+ * InternetExplorer differences from the standard implementation.
+ *
+ * @author Miroslav Pokorny (mP)
+ */
+public class InternetExplorerSelectionSupport extends SelectionSupport {
+
+ final static String PARENT_NODE = "parentNode";
+
+ @Override
+ native public Selection getSelection()/*-{
+ return $wnd.document.selection;
+ }-*/;
+
+ @Override
+ public SelectionEndPoint getStart(final Selection selection) {
+ return this.getStart0(selection);
+ }
+
+ native protected SelectionEndPoint getStart0(final Selection selection) /*-{
+ var selectionRange = selection.createRange();
+ var element = selectionRange.parentElement();
+
+ return this.@org.grammaticalframework.ui.gwt.client.selection.support.InternetExplorerSelectionSupport::getStart1(Lorg/grammaticalframework/ui/gwt/client/selection/Selection;Lcom/google/gwt/user/client/Element;)(selection,element);
+ }-*/;
+
+ native protected SelectionEndPoint getStart1(final Selection selection, final Element element)/*-{
+ var endPoint = null;
+
+ if(! selection.createRange ){
+ alert( "selection.createRange" + selection.createRange );
+ }
+
+ var selectionRange = selection.createRange();
+
+ var range = selectionRange.duplicate();
+ range.moveToElementText( element );
+ range.collapse();
+
+ // loop thru all the childNodes belonging to element.
+ var childNodes = element.childNodes;
+ for( var i = 0; i < childNodes.length; i++ ){
+ var node = childNodes[ i ];
+ var nodeType = node.nodeType;
+
+ // found an element check its child nodes...
+ if( 1 == nodeType ){
+ endPoint = this.@org.grammaticalframework.ui.gwt.client.selection.support.InternetExplorerSelectionSupport::getStart1(Lorg/grammaticalframework/ui/gwt/client/selection/Selection;Lcom/google/gwt/user/client/Element;)(selection,node);
+
+ if( null == endPoint ){
+ range.move( "character", node.innerText.toString().length );
+ continue;
+ }
+ // endPoint found stop searching....
+ break;
+ }
+
+ // found a textNode...
+ if( 3 == nodeType ){
+ var text = node.data;
+ for( var j = 0; j < text.length; j++ ){
+ // found selection start stop searching!
+ if( selectionRange.compareEndPoints( "StartToStart", range ) == 0 ){
+ endPoint = @org.grammaticalframework.ui.gwt.client.selection.SelectionEndPoint::new(Lcom/google/gwt/dom/client/Text;I)(node,j);
+ break;
+ }
+ range.move("character", 1 );
+ }
+ // did the above for loop find the start ? if so stop escape!
+ if( null != endPoint ){
+ break;
+ }
+ }
+ }
+
+ return endPoint;
+ }-*/;
+
+ @Override
+ public void select(final Selection selection, final SelectionEndPoint start, final SelectionEndPoint end) {
+ this.setStart0(selection, start.getTextNode(), start.getOffset());
+ this.setEnd0(selection, end.getTextNode(), end.getOffset());
+ }
+
+ native private void setStart0(final Selection selection, final Text textNode, final int offset)/*-{
+ var rangeOffset = offset;
+ var moveToElement = null;
+
+ // try an element before $textNode counting the number of characters one has moved backwards...
+ var node = textNode.previousSibling;
+
+ while( node ){
+ // if a textNode is try its previous sibling...
+ if( node.nodeType == 3 ){
+ rangeOffset = rangeOffset + node.data.length;
+ continue;
+ }
+
+ // found an element stop searching...
+ if( node.nodeType == 1 ){
+ moveToElement = node;
+ rangeOffset = rangeOffset + node.innerText.toString().length;
+ break;
+ }
+
+ // ignore other types...
+ node = node.previousSibling;
+ }
+
+ // if moveToElement is null use textNode's parent.
+ if( ! moveToElement ){
+ moveToElement = textNode.parentNode;
+ }
+
+ // update the start of selection range...
+ var range = selection.createRange();
+ range.moveToElementText( moveToElement );
+ range.moveStart( "character", rangeOffset );
+ range.select();
+ }-*/;
+
+ native private void setEnd0(final Selection selection, final Text textNode, final int offset)/*-{
+ var rangeOffset = offset;
+ var moveToElement = null;
+
+ // try an element before $textNode counting the number of characters one has moved backwards...
+ var node = textNode.previousSibling;
+
+ while( node ){
+ // if textNode is try its previous sibling...
+ if( node.nodeType == 3 ){
+ rangeOffset = rangeOffset + node.data.length;
+ continue;
+ }
+
+ // found an element stop searching...
+ if( node.nodeType == 1 ){
+ moveToElement = node;
+ rangeOffset = rangeOffset + node.innerText.toString().length;
+ break;
+ }
+
+ // ignore other types...
+ node = node.previousSibling;
+ }
+
+ // if moveToElement is null use textNode's parent.
+ if( ! moveToElement ){
+ moveToElement = textNode.parentNode;
+ }
+
+ // update the end of selection range...
+ var range = selection.createRange();
+ range.moveToElementText( moveToElement );
+ range.moveStart( "character", rangeOffset );
+ range.collapse();
+
+ var selectionRange = selection.createRange();
+ selectionRange.setEndPoint( "EndToStart", range );
+ selectionRange.select();
+ }-*/;
+
+ @Override
+ public SelectionEndPoint getEnd(final Selection selection) {
+ return this.getEnd0(selection);
+ }
+
+ protected native SelectionEndPoint getEnd0(final Selection selection) /*-{
+ var selectionRange = selection.createRange();
+ var element = selectionRange.parentElement();
+
+ return this.@org.grammaticalframework.ui.gwt.client.selection.support.InternetExplorerSelectionSupport::getEnd1(Lorg/grammaticalframework/ui/gwt/client/selection/Selection;Lcom/google/gwt/user/client/Element;)(selection,element);
+ }-*/;
+
+ protected native SelectionEndPoint getEnd1(final Selection selection, final Element element)/*-{
+ var endPoint = null;
+
+ var selectionRange = selection.createRange();
+
+ var range = selectionRange.duplicate();
+ range.moveToElementText( element );
+ range.collapse( true );
+
+ // loop thru all the childNodes belonging to element.
+ var childNodes = element.childNodes;
+ for( var i = 0; i < childNodes.length; i++ ){
+ var node = childNodes[ i ];
+ var nodeType = node.nodeType;
+
+ // found an element check its child nodes...
+ if( 1 == nodeType ){
+ endPoint = this.@org.grammaticalframework.ui.gwt.client.selection.support.InternetExplorerSelectionSupport::getEnd1(Lorg/grammaticalframework/ui/gwt/client/selection/Selection;Lcom/google/gwt/user/client/Element;)(selection,node);
+
+ if( null == endPoint ){
+ range.move( "character", node.innerText.toString().length );
+ continue;
+ }
+ // endPoint found stop searching....
+ break;
+ }
+
+ // found a textNode...
+ if( 3 == nodeType ){
+ var text = node.data;
+ for( var j = 0; j < text.length; j++ ){
+ // found selection end stop searching!
+ if( selectionRange.compareEndPoints( "EndToStart", range ) == 0 ){
+ endPoint = @org.grammaticalframework.ui.gwt.client.selection.SelectionEndPoint::new(Lcom/google/gwt/dom/client/Text;I)(node,j);
+ break;
+ }
+ range.move( "character", 1 );
+ }
+ // did the above for loop find the end ? if so stop escape!
+ if( null != endPoint ){
+ break;
+ }
+ }
+ }
+
+ return endPoint;
+ }-*/;
+}
diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/support/SelectionSupport.java b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/support/SelectionSupport.java
new file mode 100644
index 000000000..946c2a812
--- /dev/null
+++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/client/selection/support/SelectionSupport.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright Miroslav Pokorny
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.grammaticalframework.ui.gwt.client.selection.support;
+
+import org.grammaticalframework.ui.gwt.client.selection.Selection;
+import org.grammaticalframework.ui.gwt.client.selection.SelectionEndPoint;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.dom.client.Text;
+import com.google.gwt.user.client.Element;
+
+/**
+ * This class provides the standard implementation of
+ *
+ * @author Miroslav Pokorny (mP)
+ */
+public class SelectionSupport {
+
+ public SelectionEndPoint getStart(final Selection selection) {
+ return getStart0(selection);
+ }
+
+ native private SelectionEndPoint getStart0(final Selection selection) /*-{
+ var node = selection.anchorNode || null;
+ var offset = selection.anchorOffset;
+ return @org.grammaticalframework.ui.gwt.client.selection.SelectionEndPoint::new(Lcom/google/gwt/dom/client/Text;I)(value,j);
+ }-*/;
+
+ public SelectionEndPoint getEnd(final Selection selection) {
+ return getEnd0(selection);
+ }
+
+ native private SelectionEndPoint getEnd0(final Selection selection) /*-{
+ var node = selection.focusNode || null;
+ var offset = selection.focusOffset;
+ return @org.grammaticalframework.ui.gwt.client.selection.SelectionEndPoint::new(Lcom/google/gwt/dom/client/Text;I)(value,j);
+ }-*/;
+
+ public void select(final Selection selection, final SelectionEndPoint start, final SelectionEndPoint end) {
+ select0(selection, start.getTextNode(), start.getOffset(), end.getTextNode(), end.getOffset());
+ }
+
+ native private void select0(final Selection selection, final Text startNode, final int startOffset, final Text endNode, final int endOffset)/*-{
+ var range = startNode.ownerDocument.createRange();
+ range.setStart(startNode, startOffset);
+ range.setEnd(endNode, endOffset);
+
+ // delete all ranges then recreate...
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }-*/;
+
+ native public Selection getSelection()/*-{
+ return $wnd.getSelection();
+ }-*/;
+}
diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/Editor.css b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/Editor.css
new file mode 100644
index 000000000..2b876160e
--- /dev/null
+++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/Editor.css
@@ -0,0 +1,253 @@
+/** Add css rules here for your application. */
+
+.my-TranslatePanel {
+ padding-top: 1em;
+ padding-bottom: 1em
+}
+
+.my-TextInputPanel {
+ width: 100%;
+}
+
+.my-TextInputPanel .frame {
+ margin: 0;
+ padding: 1em;
+ background-color: #F3F3F3;
+ outline: 0;
+}
+
+.my-TextInputPanel .text {
+ padding: 1em;
+ background-color: white;
+ border-top: 1px solid #CCCCCC;
+ border-left: 1px solid #CCCCCC;
+ border-bottom: 2px solid #BBBBBB;
+ border-right: 2px solid #BBBBBB;
+ min-width: 100px;
+ min-height: 100px;
+}
+
+.my-TextInputPanel .toolbar {
+ width: 100%;
+ padding: 3px;
+ background-attachement: scroll;
+ background-color: #E5E5E5;
+ background-image: url("background.png");
+ background-position: 0px -192px;
+ background-repeat: repeat-x;
+}
+
+.my-TextInputPanel .toolbar .button {
+ float: left;
+ margin: 2px;
+}
+
+.my-TextInputPanel .toolbar .button:hover {
+ margin: 1px;
+ border: 1px solid rgb(147,194,241);
+}
+
+.my-TextInputPanel .toolbar .status {
+ padding: 1px;
+ background-color: #E5E5E5;
+ border: 1px solid #BBBBBB;
+ float: right;
+}
+
+.my-TextInputPanel .searchbox {
+ font-size: 150%;
+ padding: 2px;
+ display: inline;
+ border-bottom: 1px dashed green;
+ outline: 0;
+}
+
+.my-TextInputPanel .searchbox-error {
+ border-bottom: 1px dashed red;
+}
+
+.my-TextInputPanel .wordspanel {
+ padding: 0;
+ display: inline;
+}
+
+.my-TextInputPanel .wordspanel-selected {
+ background-color: rgb(147,194,241);
+}
+
+.my-TextInputPanel .wordspanel-focused {
+ background-color: rgb(147,194,241);
+ border: 1px solid #666;
+}
+
+.my-TextInputPanel .wordspanel-error {
+ border-bottom: 1px dashed red;
+}
+
+.my-TextInputPanel .wordlabel {
+ display: inline;
+ font-size: 150%;
+}
+
+.my-TextInputPanel .wordspace {
+ display: inline;
+ font-size: 150%;
+}
+
+.my-SettingsPanel * {
+ margin: 0 0.4em;
+}
+
+.my-LinksPanel * {
+ margin: 0 0.1em;
+}
+
+.my-HeaderPanel {
+ width: 100%;
+ margin: 0 0.1em;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ border-bottom-color: rgb(122,165,214);
+}
+
+.my-BrowsePanel {
+ width: 100%;
+ margin: 1em;
+ border-width: 5px;
+ border-color: rgb(122,165,214);
+}
+
+.my-BrowsePanel .source {
+ padding: 1em;
+}
+
+.my-BrowseFrame {
+ width: 100%;
+ height: 100%;
+ margin: 1em;
+ border-style:none;
+}
+
+.my-QueryPanel {
+ margin: 1em;
+ border-width: 5px;
+ border-color: rgb(122,165,214);
+}
+
+.my-QueryBox {
+ min-width: 630px;
+ min-height: 94px;
+}
+
+.my-ResultGrid {
+ margin: 1em;
+}
+
+.my-translations {
+ margin-top: 1em;
+}
+
+.my-translation-frame {
+ margin: 0.5em;
+ background: #D0E4F6;
+}
+
+.my-translation-bar {
+ padding-left: 25px;
+ padding-right: 25px;
+ width: 100%;
+}
+
+.my-translation {
+ margin: 0.2em;
+ font-size: 150%;
+ background-repeat: no-repeat;
+ background-position: 0% 50%;
+ cursor:pointer;
+}
+
+.my-translation-btns {
+ background: #DDDDDD;
+ cursor:pointer;
+}
+
+.my-treeimage {
+ width: 650px;
+ height: 520px;
+}
+
+.my-alignmentimage {
+ width: 450px;
+ height: 300px;
+}
+
+.my-typeError {
+ margin: 2px;
+ padding: 12px;
+ font-size: 150%;
+ font-weight: bold;
+ background: #CDFFDA;
+}
+
+.my-typeError .title {
+ background: #DDDDDD;
+}
+
+.my-typeError .content {
+ cursor:pointer;
+}
+
+.my-identifierLink:link {
+ text-decoration: none;
+ color: black;
+}
+
+.my-identifierLink:hover {
+ text-decoration: none;
+ color: black;
+ background-color: rgb(147, 194, 241);
+}
+
+.my-FridgeBagPanel {
+ padding: 0.2em;
+ margin-top: 1.5em;
+ border: 3px solid #dddddd;
+}
+
+.my-FridgeBagPanel-empty {
+ visibility: hidden;
+}
+
+.my-Magnet {
+ float: left;
+ margin: 0.3em;
+ border-width: 1px;
+ border-style: solid;
+ border-color: black;
+ padding: 0.3em;
+ color: black;
+ background-color: white;
+}
+
+.my-SyntaxTable {
+ font-size: 120%;
+}
+
+.my-SyntaxRow {
+ margin: 1px;
+}
+
+.my-SyntaxLang {
+ background: rgb(147,194,241);
+ padding: 2px;
+}
+
+.my-SyntaxLin {
+ border: 1px solid rgb(147,194,241);
+ padding-left: 8px;
+ padding-right: 8px;
+ padding-top: 2px;
+ padding-bottom: 2px;
+}
diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/background.png b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/background.png
new file mode 100644
index 000000000..4c1e4989e
--- /dev/null
+++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/background.png
Binary files differ
diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/new.png b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/new.png
new file mode 100644
index 000000000..6f13ca749
--- /dev/null
+++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/new.png
Binary files differ
diff --git a/src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/textinput-buttons.png b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/textinput-buttons.png
new file mode 100644
index 000000000..46029f34e
--- /dev/null
+++ b/src/ui/gwt/src/org/grammaticalframework/ui/gwt/public/textinput-buttons.png
Binary files differ