]> source.dussan.org Git - vaadin-framework.git/commitdiff
Added new component to read and manipulate URI fragment. (#34)
authorMatti Tahvonen <matti.tahvonen@itmill.com>
Thu, 13 Nov 2008 09:46:16 +0000 (09:46 +0000)
committerMatti Tahvonen <matti.tahvonen@itmill.com>
Thu, 13 Nov 2008 09:46:16 +0000 (09:46 +0000)
svn changeset:5883/svn branch:trunk

src/com/itmill/toolkit/terminal/gwt/client/ui/IUriFragmentUtility.java [new file with mode: 0644]
src/com/itmill/toolkit/tests/tickets/Ticket34.java [new file with mode: 0644]
src/com/itmill/toolkit/ui/UriFragmentUtility.java [new file with mode: 0644]

diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/IUriFragmentUtility.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/IUriFragmentUtility.java
new file mode 100644 (file)
index 0000000..c659b40
--- /dev/null
@@ -0,0 +1,66 @@
+package com.itmill.toolkit.terminal.gwt.client.ui;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.user.client.History;
+import com.google.gwt.user.client.HistoryListener;
+import com.google.gwt.user.client.ui.Widget;
+import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
+import com.itmill.toolkit.terminal.gwt.client.BrowserInfo;
+import com.itmill.toolkit.terminal.gwt.client.Paintable;
+import com.itmill.toolkit.terminal.gwt.client.UIDL;
+
+/**
+ * Client side implementation for UriFragmentUtility. Uses GWT's History object
+ * as an implementation.
+ * 
+ */
+public class IUriFragmentUtility extends Widget implements Paintable,
+        HistoryListener {
+
+    private String fragment;
+    private ApplicationConnection client;
+    private String paintableId;
+    private boolean immediate;
+
+    public IUriFragmentUtility() {
+        setElement(Document.get().createDivElement());
+        if (BrowserInfo.get().isIE6()) {
+            getElement().getStyle().setProperty("overflow", "hidden");
+            getElement().getStyle().setProperty("height", "0");
+        }
+        History.addHistoryListener(this);
+    }
+
+    public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+        if (client.updateComponent(this, uidl, false)) {
+            return;
+        }
+        String uidlFragment = uidl.getStringVariable("fragment");
+        immediate = uidl.getBooleanAttribute("immediate");
+        if (this.client == null) {
+            // initial paint has some special logic
+            this.client = client;
+            paintableId = uidl.getId();
+            if ("".equals(uidlFragment)) {
+                // empty fragment, send initial fragment to server
+                History.fireCurrentHistoryState();
+            } else {
+                // fragment initially exists server side, set that
+                History.newItem(uidlFragment, false);
+            }
+        } else {
+            if (uidlFragment != null && !uidlFragment.equals(fragment)) {
+                // normal fragment change from server, add new history item
+                History.newItem(uidlFragment, false);
+            }
+        }
+    }
+
+    public void onHistoryChanged(String historyToken) {
+        fragment = historyToken;
+        if (client != null) {
+            client.updateVariable(paintableId, "fragment", fragment, immediate);
+        }
+    }
+
+}
diff --git a/src/com/itmill/toolkit/tests/tickets/Ticket34.java b/src/com/itmill/toolkit/tests/tickets/Ticket34.java
new file mode 100644 (file)
index 0000000..9568c6d
--- /dev/null
@@ -0,0 +1,110 @@
+package com.itmill.toolkit.tests.tickets;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.itmill.toolkit.Application;
+import com.itmill.toolkit.ui.Button;
+import com.itmill.toolkit.ui.Component;
+import com.itmill.toolkit.ui.Label;
+import com.itmill.toolkit.ui.OrderedLayout;
+import com.itmill.toolkit.ui.Panel;
+import com.itmill.toolkit.ui.TextField;
+import com.itmill.toolkit.ui.UriFragmentUtility;
+import com.itmill.toolkit.ui.Window;
+import com.itmill.toolkit.ui.Button.ClickEvent;
+import com.itmill.toolkit.ui.UriFragmentUtility.FragmentChangedEvent;
+
+public class Ticket34 extends Application {
+
+    private Map<String, Component> views = new HashMap<String, Component>();
+    private OrderedLayout mainLayout;
+    private Component currentView;
+    private UriFragmentUtility reader;
+
+    public void init() {
+
+        buildViews(new String[] { "main", "view2", "view3" });
+
+        reader = new UriFragmentUtility();
+        reader.addListener(new UriFragmentUtility.FragmentChangedListener() {
+
+            public void fragmentChanged(FragmentChangedEvent event) {
+                getMainWindow().showNotification(
+                        "Fragment now: "
+                                + event.getUriFragmentUtility().getFragment());
+                // try to change to view mapped by fragment string
+                setView(event.getUriFragmentUtility().getFragment());
+            }
+        });
+
+        mainLayout = new OrderedLayout();
+        mainLayout.setSizeFull();
+        final Window mainWin = new Window(
+                "Test app for URI fragment management/reading", mainLayout);
+        setMainWindow(mainWin);
+
+        // UriFragmentReader is 0px size by default, so it will not render
+        // anything on screen
+        mainLayout.addComponent(reader);
+
+        setView("main");
+
+    }
+
+    private void setView(String string) {
+        Component component = views.get(string);
+        if (component == null) {
+            getMainWindow().showNotification(
+                    "View called " + string + " not found!");
+        } else if (component != currentView) {
+            if (currentView != null) {
+                mainLayout.replaceComponent(currentView, component);
+            } else {
+                mainLayout.addComponent(component);
+            }
+            // give all extra space for view
+            mainLayout.setExpandRatio(component, 1);
+            currentView = component;
+        }
+    }
+
+    private void buildViews(String[] strings) {
+        for (String string : strings) {
+            Panel p = new Panel(string);
+            p.setSizeFull();
+            ((OrderedLayout) p.getLayout()).setSpacing(true);
+            p.addComponent(new Label("This is a simple test case for "
+                    + "UriFragmentReader that can be used for"
+                    + " adding linking, back/forward button "
+                    + "and history support for ajax application. "));
+            StringBuffer sb = new StringBuffer();
+            sb.append("Available views : ");
+            for (String key : strings) {
+                sb.append(key);
+                sb.append(" ");
+            }
+            sb.append("Application will change to them from uri "
+                    + "fragment or server initiated via textfield below.");
+            p.addComponent(new Label(sb.toString()));
+
+            final TextField tf = new TextField(
+                    "Type view name (will change to that "
+                            + "view and change the uri fragment)");
+            p.addComponent(tf);
+            Button b = new Button("Go!");
+            p.addComponent(b);
+            b.addListener(new Button.ClickListener() {
+
+                public void buttonClick(ClickEvent event) {
+                    String viewName = tf.getValue().toString();
+                    // fragmentChangedListener will change the view if possible
+                    reader.setFragment(viewName);
+                }
+            });
+
+            views.put(string, p);
+        }
+    }
+
+}
diff --git a/src/com/itmill/toolkit/ui/UriFragmentUtility.java b/src/com/itmill/toolkit/ui/UriFragmentUtility.java
new file mode 100644 (file)
index 0000000..633d8da
--- /dev/null
@@ -0,0 +1,137 @@
+package com.itmill.toolkit.ui;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import com.itmill.toolkit.service.ApplicationContext.TransactionListener;
+import com.itmill.toolkit.terminal.PaintException;
+import com.itmill.toolkit.terminal.PaintTarget;
+
+/**
+ * Experimental web browser dependent component for URI fragment (part after
+ * hash mark "#") reading and writing.
+ * 
+ * Component can be used to workaround common ajax web applications pitfalls:
+ * bookmarking a program state and back button.
+ * 
+ */
+public class UriFragmentUtility extends AbstractComponent {
+
+    /**
+     * Listener that listens changes in URI fragment.
+     */
+    public interface FragmentChangedListener {
+
+        public void fragmentChanged(FragmentChangedEvent source);
+
+    }
+
+    /**
+     * Event fired when uri fragment changes.
+     */
+    public class FragmentChangedEvent extends Component.Event {
+
+        /**
+         * Serial generated by eclipse
+         */
+        private static final long serialVersionUID = -4142140007700263197L;
+
+        /**
+         * Creates a new instance of UriFragmentReader change event.
+         * 
+         * @param source
+         *            the Source of the event.
+         */
+        public FragmentChangedEvent(Component source) {
+            super(source);
+        }
+
+        /**
+         * Gets the UriFragmentReader where the event occurred.
+         * 
+         * @return the Source of the event.
+         */
+        public UriFragmentUtility getUriFragmentUtility() {
+            return (UriFragmentUtility) getSource();
+        }
+    }
+
+    private static final Method FRAGMENT_CHANGED_METHOD;
+
+    static {
+        try {
+            FRAGMENT_CHANGED_METHOD = FragmentChangedListener.class
+                    .getDeclaredMethod("fragmentChanged",
+                            new Class[] { FragmentChangedEvent.class });
+        } catch (final java.lang.NoSuchMethodException e) {
+            // This should never happen
+            throw new java.lang.RuntimeException(
+                    "Internal error finding methods in FragmentChangedListener");
+        }
+    }
+
+    public void addListener(FragmentChangedListener listener) {
+        addListener(FragmentChangedEvent.class, listener,
+                FRAGMENT_CHANGED_METHOD);
+    }
+
+    public void removeListener(FragmentChangedListener listener) {
+        removeListener(FragmentChangedEvent.class, listener,
+                FRAGMENT_CHANGED_METHOD);
+    }
+
+    private String fragment;
+
+    public UriFragmentUtility() {
+        // immediate by default
+        setImmediate(true);
+    }
+
+    @Override
+    public String getTag() {
+        return "urifragment";
+    }
+
+    @Override
+    public void paintContent(PaintTarget target) throws PaintException {
+        super.paintContent(target);
+        target.addVariable(this, "fragment", fragment);
+    }
+
+    @Override
+    public void changeVariables(Object source, Map variables) {
+        super.changeVariables(source, variables);
+        fragment = (String) variables.get("fragment");
+        fireEvent(new FragmentChangedEvent(this));
+    }
+
+    /**
+     * Gets currently set URI fragment.
+     * 
+     * Note that initial URI fragment that user used to enter the application
+     * will be read after application init. If you absolutely need that you must
+     * hook to {@link TransactionListener}
+     * 
+     * To listen changes in fragment, hook a {@link FragmentChangedListener}.
+     * 
+     * @return the current fragment in browser uri or null if not known
+     */
+    public String getFragment() {
+        return fragment;
+    }
+
+    /**
+     * Sets URI fragment.
+     * 
+     * @param newFragment
+     */
+    public void setFragment(String newFragment) {
+        if ((newFragment == null && fragment != null)
+                || (newFragment != null && !newFragment.equals(fragment))) {
+            fragment = newFragment;
+            fireEvent(new FragmentChangedEvent(this));
+            requestRepaint();
+        }
+    }
+
+}