[kune-commits] r1576 - in trunk/src/main/java: cc/kune/core/server/rpc cc/kune/wave/client org/waveprotocol/box org/waveprotocol/box/webclient org/waveprotocol/box/webclient/client org/waveprotocol/wave/client/wavepanel org/waveprotocol/wave/client/wavepanel/impl org/waveprotocol/wave/client/wavepanel/impl/toolbar

Vicente J. Ruiz Jurado vjrj_ at ourproject.org
Thu Oct 27 00:18:15 CEST 2011


Author: vjrj_
Date: 2011-10-27 00:18:14 +0200 (Thu, 27 Oct 2011)
New Revision: 1576

Added:
   trunk/src/main/java/org/waveprotocol/box/webclient/
   trunk/src/main/java/org/waveprotocol/box/webclient/client/
   trunk/src/main/java/org/waveprotocol/box/webclient/client/HistorySupport.java
   trunk/src/main/java/org/waveprotocol/wave/client/wavepanel/impl/
   trunk/src/main/java/org/waveprotocol/wave/client/wavepanel/impl/toolbar/
   trunk/src/main/java/org/waveprotocol/wave/client/wavepanel/impl/toolbar/EditToolbar.java
Modified:
   trunk/src/main/java/cc/kune/core/server/rpc/StatsRPC.java
   trunk/src/main/java/cc/kune/wave/client/WebClient.java
Log:
CLOSED - # 141: Kune client is always "connecting" in chrome/chromium 
http://kune.ourproject.org/issues/ticket/141

Modified: trunk/src/main/java/cc/kune/core/server/rpc/StatsRPC.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/rpc/StatsRPC.java	2011-10-26 07:45:49 UTC (rev 1575)
+++ trunk/src/main/java/cc/kune/core/server/rpc/StatsRPC.java	2011-10-26 22:18:14 UTC (rev 1576)
@@ -1,5 +1,6 @@
 package cc.kune.core.server.rpc;
 
+import cc.kune.core.client.errors.SessionExpiredException;
 import cc.kune.core.server.UserSessionManager;
 import cc.kune.core.server.auth.Authenticated;
 import cc.kune.core.server.mapper.Mapper;
@@ -29,8 +30,17 @@
   }
 
   @Override
+  public HomeStatsDTO getHomeStats(final String userHash) {
+    try {
+      return getHomeStatsWrapper(userHash);
+    } catch (final SessionExpiredException e) {
+      // If the session is expired just send unauth stats
+      return getHomeStats();
+    }
+  }
+
   @Authenticated
-  public HomeStatsDTO getHomeStats(final String userHash) {
+  public HomeStatsDTO getHomeStatsWrapper(final String userHash) {
     return mapper.map(statsService.getHomeStats(userSessionManager.getUser().getUserGroup()),
         HomeStatsDTO.class);
   }

Modified: trunk/src/main/java/cc/kune/wave/client/WebClient.java
===================================================================
--- trunk/src/main/java/cc/kune/wave/client/WebClient.java	2011-10-26 07:45:49 UTC (rev 1575)
+++ trunk/src/main/java/cc/kune/wave/client/WebClient.java	2011-10-26 22:18:14 UTC (rev 1576)
@@ -260,7 +260,7 @@
    * This is the entry point method.
    */
   @Inject
-  public WebClient(final EventBus eventBus, final KuneWaveProfileManager profiles, final InboxCountPresenter inboxCount, final TokenMatcher tokenMatcher) {
+  public WebClient(final EventBus eventBus, final KuneWaveProfileManager profiles, final InboxCountPresenter inboxCount, final TokenMatcher tokenMatcher, final cc.kune.core.client.state.Session session) {
     // Window.alert("webclient! " + new Date());
     this.eventBus = eventBus;
     this.profiles = profiles;
@@ -285,7 +285,7 @@
     //setupConnectionIndicator();
 
     // Done in StateManager
-    // HistorySupport.init();
+    HistorySupport.kuneInit(tokenMatcher, session);
 
     loginImpl();
 
@@ -526,5 +526,7 @@
     });
   }
 
-  private native boolean useSocketIO() /*-{ return !window.WebSocket }-*/;
+  private native boolean useSocketIO() /*-{
+		return !window.WebSocket
+  }-*/;
 }

Added: trunk/src/main/java/org/waveprotocol/box/webclient/client/HistorySupport.java
===================================================================
--- trunk/src/main/java/org/waveprotocol/box/webclient/client/HistorySupport.java	                        (rev 0)
+++ trunk/src/main/java/org/waveprotocol/box/webclient/client/HistorySupport.java	2011-10-26 22:18:14 UTC (rev 1576)
@@ -0,0 +1,85 @@
+// @formatter:off
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * 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.waveprotocol.box.webclient.client;
+
+import javax.annotation.Nullable;
+
+import org.waveprotocol.box.webclient.client.events.Log;
+import org.waveprotocol.wave.model.waveref.InvalidWaveRefException;
+import org.waveprotocol.wave.model.waveref.WaveRef;
+import org.waveprotocol.wave.util.escapers.GwtWaverefEncoder;
+
+import cc.kune.core.client.state.TokenMatcher;
+
+/**
+ * Contains the code to interface the history event mechanism with the client's
+ * event bus. At the moment, a history token encodes a wave id or wave ref.
+ */
+public class HistorySupport {
+  private static final Log LOG = Log.get(HistorySupport.class);
+  private static TokenMatcher tokenMatcher;
+  private static cc.kune.core.client.state.Session session;
+  public static void kuneInit(TokenMatcher tokenMatcher, cc.kune.core.client.state.Session session) {
+    HistorySupport.tokenMatcher = tokenMatcher;
+    HistorySupport.session = session;
+  }
+    public static void init() {
+//    History.addValueChangeHandler(new ValueChangeHandler<String>() {
+//      @Override
+//      public void onValueChange(ValueChangeEvent<String> event) {
+//        String encodedToken = event.getValue();
+//        if (encodedToken == null || encodedToken.length() == 0) {
+//          return;
+//        }
+//        WaveRef waveRef = waveRefFromHistoryToken(encodedToken);
+//        if (waveRef == null) {
+//          LOG.info("History token contains invalid path: " + encodedToken);
+//          return;
+//        }
+//        LOG.info("Changing selected wave based on history event to " + waveRef.toString());
+//        ClientEvents.get().fireEvent(new WaveSelectionEvent(waveRef));
+//      }
+//    });
+  }
+
+  /**
+   * @param encodedToken token to parse into waveref
+   * @return null if cannot parse into valid waveRef
+   */
+  @Nullable
+  public static WaveRef waveRefFromHistoryToken(String encodedToken) {
+    LOG.info("Encoded token: " + encodedToken);
+    String waveToken = encodedToken;
+    if (tokenMatcher.isGroupToken(encodedToken) || tokenMatcher.isHomeToken(encodedToken)) {
+      waveToken = session.getContentState().getWaveRef();
+      LOG.info("Kune URL: " + encodedToken + " = " + waveToken);
+    }
+    try {
+      return GwtWaverefEncoder.decodeWaveRefFromPath(waveToken);
+    } catch (InvalidWaveRefException e) {
+      return null;
+    }
+  }
+
+  public static String historyTokenFromWaveref(WaveRef ref) {
+    return GwtWaverefEncoder.encodeToUriPathSegment(ref);
+  }
+
+  private HistorySupport() {
+  }
+}

Added: trunk/src/main/java/org/waveprotocol/wave/client/wavepanel/impl/toolbar/EditToolbar.java
===================================================================
--- trunk/src/main/java/org/waveprotocol/wave/client/wavepanel/impl/toolbar/EditToolbar.java	                        (rev 0)
+++ trunk/src/main/java/org/waveprotocol/wave/client/wavepanel/impl/toolbar/EditToolbar.java	2011-10-26 22:18:14 UTC (rev 1576)
@@ -0,0 +1,610 @@
+// @formatter:off
+/**
+ * Copyright 2010 Google Inc.
+ *
+ * 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.waveprotocol.wave.client.wavepanel.impl.toolbar;
+
+import org.waveprotocol.box.webclient.client.HistorySupport;
+import org.waveprotocol.wave.client.common.util.WaveRefConstants;
+import org.waveprotocol.wave.client.doodad.link.Link;
+import org.waveprotocol.wave.client.doodad.link.Link.InvalidLinkException;
+import org.waveprotocol.wave.client.editor.Editor;
+import org.waveprotocol.wave.client.editor.EditorContext;
+import org.waveprotocol.wave.client.editor.EditorContextAdapter;
+import org.waveprotocol.wave.client.editor.content.CMutableDocument;
+import org.waveprotocol.wave.client.editor.content.ContentElement;
+import org.waveprotocol.wave.client.editor.content.ContentNode;
+import org.waveprotocol.wave.client.editor.content.misc.StyleAnnotationHandler;
+import org.waveprotocol.wave.client.editor.content.paragraph.Paragraph;
+import org.waveprotocol.wave.client.editor.content.paragraph.Paragraph.LineStyle;
+import org.waveprotocol.wave.client.editor.toolbar.ButtonUpdater;
+import org.waveprotocol.wave.client.editor.toolbar.ParagraphApplicationController;
+import org.waveprotocol.wave.client.editor.toolbar.ParagraphTraversalController;
+import org.waveprotocol.wave.client.editor.toolbar.TextSelectionController;
+import org.waveprotocol.wave.client.editor.util.EditorAnnotationUtil;
+import org.waveprotocol.wave.client.gadget.GadgetXmlUtil;
+import org.waveprotocol.wave.client.wavepanel.impl.toolbar.attachment.AttachmentPopupWidget;
+import org.waveprotocol.wave.client.wavepanel.impl.toolbar.gadget.GadgetSelectorWidget;
+import org.waveprotocol.wave.client.wavepanel.view.AttachmentPopupView;
+import org.waveprotocol.wave.client.wavepanel.view.AttachmentPopupView.Listener;
+import org.waveprotocol.wave.client.widget.popup.UniversalPopup;
+import org.waveprotocol.wave.client.widget.toolbar.SubmenuToolbarView;
+import org.waveprotocol.wave.client.widget.toolbar.ToolbarButtonViewBuilder;
+import org.waveprotocol.wave.client.widget.toolbar.ToolbarView;
+import org.waveprotocol.wave.client.widget.toolbar.ToplevelToolbarWidget;
+import org.waveprotocol.wave.client.widget.toolbar.buttons.ToolbarClickButton;
+import org.waveprotocol.wave.client.widget.toolbar.buttons.ToolbarToggleButton;
+import org.waveprotocol.wave.media.model.AttachmentIdGenerator;
+import org.waveprotocol.wave.media.model.AttachmentIdGeneratorImpl;
+import org.waveprotocol.wave.model.document.util.FocusedRange;
+import org.waveprotocol.wave.model.document.util.LineContainers;
+import org.waveprotocol.wave.model.document.util.Point;
+import org.waveprotocol.wave.model.document.util.XmlStringBuilder;
+import org.waveprotocol.wave.model.id.IdGenerator;
+import org.waveprotocol.wave.model.wave.ParticipantId;
+import org.waveprotocol.wave.model.waveref.WaveRef;
+import org.waveprotocol.wave.util.escapers.GwtWaverefEncoder;
+
+import com.google.common.base.Preconditions;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.http.client.URL;
+import com.google.gwt.user.client.History;
+import com.google.gwt.user.client.Window;
+
+/**
+ * Attaches actions that can be performed in a Wave's "edit mode" to a toolbar.
+ * <p>
+ * Also constructs an initial set of such actions.
+ *
+ * @author kalman at google.com (Benjamin Kalman)
+ */
+public class EditToolbar {
+
+  /**
+   * Container for an alignment.
+   */
+  private static final class Alignment {
+    public final String description;
+    public final String iconCss;
+    public final LineStyle style;
+    public Alignment(final String description, final String iconCss, final LineStyle style) {
+      this.description = description;
+      this.iconCss = iconCss;
+      this.style = style;
+    }
+  }
+
+  /**
+   * Handler for click buttons added with {@link EditToolbar#addClickButton}.
+   */
+  public interface ClickHandler {
+    void onClicked(EditorContext context);
+  }
+
+  /**
+   * Container for a font family.
+   */
+  private static final class FontFamily {
+    public final String description;
+    public final String style;
+    public FontFamily(final String description, final String style) {
+      this.description = description;
+      this.style = style;
+    }
+  }
+
+  private static <E> E[] asArray(final E... elements) {
+    return elements;
+  }
+  /**
+   * Attaches editor behaviour to a toolbar, adding all the edit buttons.
+   */
+  public static EditToolbar create(final ParticipantId user, final IdGenerator idGenerator) {
+    final ToplevelToolbarWidget toolbarUi = new ToplevelToolbarWidget();
+    final EditorToolbarResources.Css css = EditorToolbarResources.Loader.res.css();
+    return new EditToolbar(css, toolbarUi, user, idGenerator);
+  }
+  private final AttachmentIdGenerator attachmentIdGenerator;
+  private final EditorToolbarResources.Css css;
+
+  private final EditorContextAdapter editor = new EditorContextAdapter(null);
+  private final ToplevelToolbarWidget toolbarUi;
+
+  private final ButtonUpdater updater = new ButtonUpdater(editor);
+
+  private final ParticipantId user;
+
+  private EditToolbar(final EditorToolbarResources.Css css, final ToplevelToolbarWidget toolbarUi,
+      final ParticipantId user, final IdGenerator idGenerator) {
+    this.css = css;
+    this.toolbarUi = toolbarUi;
+    this.user = user;
+    attachmentIdGenerator = new AttachmentIdGeneratorImpl(idGenerator);
+  }
+
+  /**
+   * Adds a button to this toolbar.
+   */
+  public void addClickButton(final String icon, final ClickHandler handler) {
+    final ToolbarClickButton.Listener uiHandler = new ToolbarClickButton.Listener() {
+      @Override
+      public void onClicked() {
+        handler.onClicked(editor);
+      }
+    };
+    new ToolbarButtonViewBuilder().setIcon(icon).applyTo(toolbarUi.addClickButton(), uiHandler);
+  }
+
+  private void createAlignButtons(final ToolbarView toolbar) {
+    final SubmenuToolbarView submenu = toolbar.addSubmenu();
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.alignDrop())
+        .applyTo(submenu, null);
+    submenu.setShowDropdownArrow(false); // Icon already has dropdown arrow.
+    final ToolbarView group = submenu.addGroup();
+    for (final Alignment alignment : asArray(
+        new Alignment("Left", css.alignLeft(), Paragraph.Alignment.LEFT),
+        new Alignment("Centre", css.alignCentre(), Paragraph.Alignment.CENTER),
+        new Alignment("Right", css.alignRight(), Paragraph.Alignment.RIGHT))) {
+      final ToolbarToggleButton b = group.addToggleButton();
+      new ToolbarButtonViewBuilder()
+          .setText(alignment.description)
+          .setIcon(alignment.iconCss)
+          .applyTo(b, createParagraphApplicationController(b, alignment.style));
+    }
+  }
+
+  private void createBoldButton(final ToolbarView toolbar) {
+    final ToolbarToggleButton b = toolbar.addToggleButton();
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.bold())
+        .applyTo(b, createTextSelectionController(b, "fontWeight", "bold"));
+  }
+
+  private void createClearFormattingButton(final ToolbarView toolbar) {
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.clearFormatting())
+        .applyTo(toolbar.addClickButton(), new ToolbarClickButton.Listener() {
+          @Override public void onClicked() {
+            EditorAnnotationUtil.clearAnnotationsOverSelection(editor, asArray(
+                StyleAnnotationHandler.key("backgroundColor"),
+                StyleAnnotationHandler.key("color"),
+                StyleAnnotationHandler.key("fontFamily"),
+                StyleAnnotationHandler.key("fontSize"),
+                StyleAnnotationHandler.key("fontStyle"),
+                StyleAnnotationHandler.key("fontWeight"),
+                StyleAnnotationHandler.key("textDecoration")
+                // NOTE: add more as required.
+            ));
+            createClearHeadingsListener().onClicked();
+          }
+        });
+  }
+
+  private ToolbarClickButton.Listener createClearHeadingsListener() {
+    return new ParagraphTraversalController(editor, new ContentElement.Action() {
+        @Override public void execute(final ContentElement e) {
+          e.getMutableDoc().setElementAttribute(e, Paragraph.SUBTYPE_ATTR, null);
+        }
+      });
+  }
+
+  private void createFontFamilyButton(final ToolbarView toolbar) {
+    final SubmenuToolbarView submenu = toolbar.addSubmenu();
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.fontFamily())
+        .applyTo(submenu, null);
+    submenu.setShowDropdownArrow(false); // Icon already has dropdown arrow.
+    createFontFamilyGroup(submenu.addGroup(), new FontFamily("Default", null));
+    createFontFamilyGroup(submenu.addGroup(),
+        new FontFamily("Sans Serif", "sans-serif"),
+        new FontFamily("Serif", "serif"),
+        new FontFamily("Wide", "arial black,sans-serif"),
+        new FontFamily("Narrow", "arial narrow,sans-serif"),
+        new FontFamily("Fixed Width", "monospace"));
+    createFontFamilyGroup(submenu.addGroup(),
+        new FontFamily("Arial", "arial,helvetica,sans-serif"),
+        new FontFamily("Comic Sans MS", "comic sans ms,sans-serif"),
+        new FontFamily("Courier New", "courier new,monospace"),
+        new FontFamily("Garamond", "garamond,serif"),
+        new FontFamily("Georgia", "georgia,serif"),
+        new FontFamily("Tahoma", "tahoma,sans-serif"),
+        new FontFamily("Times New Roman", "times new roman,serif"),
+        new FontFamily("Trebuchet MS", "trebuchet ms,sans-serif"),
+        new FontFamily("Verdana", "verdana,sans-serif"));
+  }
+
+  private Element createFontFamilyElement(final FontFamily family) {
+    final Element e = Document.get().createSpanElement();
+    e.getStyle().setProperty("fontFamily", family.style);
+    e.setInnerText(family.description);
+    return e;
+  }
+
+  private void createFontFamilyGroup(final ToolbarView toolbar, final FontFamily... families) {
+    for (final FontFamily family : families) {
+      final ToolbarToggleButton b = toolbar.addToggleButton();
+      b.setVisualElement(createFontFamilyElement(family));
+      b.setListener(createTextSelectionController(b, "fontFamily", family.style));
+    }
+  }
+
+  private void createFontSizeButton(final ToolbarView toolbar) {
+    final SubmenuToolbarView submenu = toolbar.addSubmenu();
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.fontSize())
+        .applyTo(submenu, null);
+    submenu.setShowDropdownArrow(false); // Icon already has dropdown arrow.
+    // TODO(kalman): default text size option.
+    final ToolbarView group = submenu.addGroup();
+    for (final int size : asArray(8, 9, 10, 11, 12, 14, 16, 18, 21, 24, 28, 32, 36, 42, 48, 56, 64, 72)) {
+      final ToolbarToggleButton b = group.addToggleButton();
+      final double baseSize = 12.0;
+      b.setVisualElement(createFontSizeElement(baseSize, size));
+      b.setListener(createTextSelectionController(b, "fontSize", (size / baseSize) + "em"));
+    }
+  }
+
+  private Element createFontSizeElement(final double baseSize, final double size) {
+    final Element e = Document.get().createSpanElement();
+    e.getStyle().setFontSize(size / baseSize, Unit.EM);
+    e.setInnerText(((int) size) + "");
+    return e;
+  }
+
+  private void createHeadingButton(final ToolbarView toolbar) {
+    final SubmenuToolbarView submenu = toolbar.addSubmenu();
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.heading())
+        .applyTo(submenu, null);
+    submenu.setShowDropdownArrow(false); // Icon already has dropdown arrow.
+    final ToolbarClickButton defaultButton = submenu.addClickButton();
+    new ToolbarButtonViewBuilder()
+        .setText("Default")
+        .applyTo(defaultButton, createClearHeadingsListener());
+    final ToolbarView group = submenu.addGroup();
+    for (final int level : asArray(1, 2, 3, 4)) {
+      final ToolbarToggleButton b = group.addToggleButton();
+      b.setVisualElement(createHeadingElement(level));
+      b.setListener(createParagraphApplicationController(b, Paragraph.regularStyle("h" + level)));
+    }
+  }
+
+  private Element createHeadingElement(final int level) {
+    final Element e = Document.get().createElement("h" + level);
+    e.getStyle().setMarginTop(2, Unit.PX);
+    e.getStyle().setMarginBottom(2, Unit.PX);
+    e.setInnerText("Heading " + level);
+    return e;
+  }
+
+  private void createIndentButton(final ToolbarView toolbar) {
+    final ToolbarClickButton b = toolbar.addClickButton();
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.indent())
+        .applyTo(b, new ParagraphTraversalController(editor, Paragraph.INDENTER));
+  }
+
+  private void createInsertAttachmentButton(final ToolbarView toolbar, final ParticipantId user) {
+    // Find the current wave id.
+    final String encodedToken = History.getToken();
+    WaveRef waveRef = null;
+    if (encodedToken != null && !encodedToken.isEmpty()) {
+      waveRef = HistorySupport.waveRefFromHistoryToken(encodedToken);
+    }
+    // Kune workaround while we do a HistorySupport patch
+    if (waveRef == null) {
+      return;
+    }
+    Preconditions.checkState(waveRef != null);
+    final String waveRefToken = URL.encode(GwtWaverefEncoder.encodeToUriQueryString(waveRef));
+
+    new ToolbarButtonViewBuilder().setIcon(css.insertAttachment()).setTooltip("Insert attachment")
+        .applyTo(toolbar.addClickButton(), new ToolbarClickButton.Listener() {
+          @Override
+          public void onClicked() {
+            int tmpCursor = -1;
+            final FocusedRange focusedRange = editor.getSelectionHelper().getSelectionRange();
+            if (focusedRange != null) {
+              tmpCursor = focusedRange.getFocus();
+            }
+            final int cursorLoc = tmpCursor;
+            final AttachmentPopupView attachmentView = new AttachmentPopupWidget();
+            attachmentView.init(new Listener() {
+
+              @Override
+              public void onDone(final String encodedWaveRef, final String attachmentId, final String fullFileName) {
+                // Insert a file name linking to the attachment URL.
+                final int lastSlashPos = fullFileName.lastIndexOf("/");
+                final int lastBackSlashPos = fullFileName.lastIndexOf("\\");
+                String fileName = fullFileName;
+                if (lastSlashPos != -1) {
+                  fileName = fullFileName.substring(lastSlashPos + 1, fullFileName.length());
+                } else if (lastBackSlashPos != -1) {
+                  fileName = fullFileName.substring(lastBackSlashPos + 1, fullFileName.length());
+                }
+                final XmlStringBuilder xml = XmlStringBuilder.createFromXmlString(fileName);
+                int to = -1;
+                final int docSize = editor.getDocument().size();
+                if (cursorLoc != -1) {
+                  // Insert the attachment at the cursor location.
+                  final CMutableDocument doc = editor.getDocument();
+                  final Point<ContentNode> point = doc.locate(cursorLoc);
+                  doc.insertXml(point, xml);
+                } else {
+                  LineContainers.appendLine(editor.getDocument(), xml);
+                }
+                // Calculate the link length for the attachment.
+                to = cursorLoc + editor.getDocument().size() - docSize;
+                final String linkValue =
+                    GWT.getHostPageBaseURL() + "attachment/" + attachmentId + "?fileName="
+                        + fileName + "&waveRef=" + encodedWaveRef;
+                EditorAnnotationUtil.setAnnotationOverRange(editor.getDocument(),
+                    editor.getCaretAnnotations(), Link.KEY, linkValue, cursorLoc, to);
+                // Store the attachment information as annotations to allow
+                // robots detect and process them.
+                EditorAnnotationUtil.setAnnotationOverRange(editor.getDocument(),
+                    editor.getCaretAnnotations(), "attachment/id", attachmentId, cursorLoc, to);
+                EditorAnnotationUtil.setAnnotationOverRange(editor.getDocument(),
+                    editor.getCaretAnnotations(), "attachment/fileName", fileName, cursorLoc, to);
+              }
+
+              @Override
+              public void onHide() {
+              }
+
+              @Override
+              public void onShow() {
+              }
+            });
+
+            attachmentView.setAttachmentId(attachmentIdGenerator.newAttachmentId());
+            attachmentView.setWaveRef(waveRefToken);
+            attachmentView.show();
+          }
+        });
+}
+
+  private void createInsertGadgetButton(final ToolbarView toolbar, final ParticipantId user) {
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.insertGadget())
+        .applyTo(toolbar.addClickButton(), new ToolbarClickButton.Listener() {
+          @Override public void onClicked() {
+            final GadgetSelectorWidget selector = new GadgetSelectorWidget();
+            selector.addFeaturedOptions();
+            final UniversalPopup popup = selector.showInPopup();
+            selector.setListener(new GadgetSelectorWidget.Listener() {
+              @Override public void onSelect(final String url) {
+                insertGadget(url);
+                popup.hide();
+              }
+            });
+          }
+        });
+  }
+
+  private void createInsertLinkButton(final ToolbarView toolbar) {
+    // TODO (Yuri Z.) use createTextSelectionController when the full
+    // link doodad is incorporated
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.insertLink())
+        .applyTo(toolbar.addClickButton(), new ToolbarClickButton.Listener() {
+              @Override  public void onClicked() {
+                final FocusedRange range = editor.getSelectionHelper().getSelectionRange();
+                if (range == null || range.isCollapsed()) {
+                  Window.alert("Select some text to create a link.");
+                  return;
+                }
+                final String rawLinkValue =
+                    Window.prompt("Enter link: URL or Wave ID.", WaveRefConstants.WAVE_URI_PREFIX);
+                // user hit "ESC" or "cancel"
+                if (rawLinkValue == null) {
+                  return;
+                }
+                try {
+                  final String linkAnnotationValue = Link.normalizeLink(rawLinkValue);
+                  EditorAnnotationUtil.setAnnotationOverSelection(editor, Link.KEY,
+                      linkAnnotationValue);
+                } catch (final InvalidLinkException e) {
+                  Window.alert(e.getLocalizedMessage());
+                }
+              }
+            });
+  }
+
+  private void createItalicButton(final ToolbarView toolbar) {
+    final ToolbarToggleButton b = toolbar.addToggleButton();
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.italic())
+        .applyTo(b, createTextSelectionController(b, "fontStyle", "italic"));
+  }
+
+  private void createOrderedListButton(final ToolbarView toolbar) {
+    final ToolbarToggleButton b = toolbar.addToggleButton();
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.orderedlist())
+        .applyTo(b, createParagraphApplicationController(
+            b, Paragraph.listStyle(Paragraph.LIST_STYLE_DECIMAL)));
+  }
+
+  private void createOutdentButton(final ToolbarView toolbar) {
+    final ToolbarClickButton b = toolbar.addClickButton();
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.outdent())
+        .applyTo(b, new ParagraphTraversalController(editor, Paragraph.OUTDENTER));
+  }
+
+  private ToolbarToggleButton.Listener createParagraphApplicationController(final ToolbarToggleButton b,
+      final LineStyle style) {
+    return updater.add(new ParagraphApplicationController(b, editor, style));
+  }
+
+  private void createRemoveLinkButton(final ToolbarView toolbar) {
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.removeLink())
+        .applyTo(toolbar.addClickButton(), new ToolbarClickButton.Listener() {
+          @Override public void onClicked() {
+            if (editor.getSelectionHelper().getSelectionRange() != null) {
+              EditorAnnotationUtil.clearAnnotationsOverSelection(editor, Link.LINK_KEYS);
+            }
+          }
+        });
+  }
+
+  private void createStrikethroughButton(final ToolbarView toolbar) {
+    final ToolbarToggleButton b = toolbar.addToggleButton();
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.strikethrough())
+        .applyTo(b, createTextSelectionController(b, "textDecoration", "line-through"));
+  }
+
+  private void createSubscriptButton(final ToolbarView toolbar) {
+    final ToolbarToggleButton b = toolbar.addToggleButton();
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.subscript())
+        .applyTo(b, createTextSelectionController(b, "verticalAlign", "sub"));
+  }
+
+  private void createSuperscriptButton(final ToolbarView toolbar) {
+    final ToolbarToggleButton b = toolbar.addToggleButton();
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.superscript())
+        .applyTo(b, createTextSelectionController(b, "verticalAlign", "super"));
+  }
+
+  private ToolbarToggleButton.Listener createTextSelectionController(final ToolbarToggleButton b,
+      final String styleName, final String value) {
+    return updater.add(new TextSelectionController(b, editor,
+        StyleAnnotationHandler.key(styleName), value));
+  }
+
+  private void createUnderlineButton(final ToolbarView toolbar) {
+    final ToolbarToggleButton b = toolbar.addToggleButton();
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.underline())
+        .applyTo(b, createTextSelectionController(b, "textDecoration", "underline"));
+  }
+
+  private void createUnorderedListButton(final ToolbarView toolbar) {
+    final ToolbarToggleButton b = toolbar.addToggleButton();
+    new ToolbarButtonViewBuilder()
+        .setIcon(css.unorderedlist())
+        .applyTo(b, createParagraphApplicationController(b, Paragraph.listStyle(null)));
+  }
+
+  /**
+   * Stops listening to editor changes.
+   *
+   * @throws IllegalStateException if this toolbar is not currently enabled
+   * @throws IllegalArgumentException if this toolbar is currently enabled for a
+   *         different editor
+   */
+  public void disable(final Editor editor) {
+    this.editor.checkEditor(editor);
+    // The above won't throw if we're not currently enabled, but it makes sure
+    // 'editor' is the same as the current editor, if any. So if 'editor' is
+    // null, it means we aren't enabled (the wrapped editor is null too).
+    Preconditions.checkState(editor != null);
+    editor.removeUpdateListener(updater);
+    this.editor.switchEditor(null);
+  }
+
+  /**
+   * Starts listening to editor changes.
+   *
+   * @throws IllegalStateException if this toolbar is already enabled
+   * @throws IllegalArgumentException if the editor is <code>null</code>
+   */
+  public void enable(final Editor editor) {
+    this.editor.checkEditor(null);
+    Preconditions.checkArgument(editor != null);
+    this.editor.switchEditor(editor);
+    editor.addUpdateListener(updater);
+    updater.updateButtonStates();
+  }
+
+  /**
+   * @return the {@link ToplevelToolbarWidget} backing this toolbar.
+   */
+  public ToplevelToolbarWidget getWidget() {
+    return toolbarUi;
+  }
+
+  /** Constructs the initial set of actions in the toolbar. */
+  public void init() {
+    ToolbarView group = toolbarUi.addGroup();
+    createBoldButton(group);
+    createItalicButton(group);
+    createUnderlineButton(group);
+    createStrikethroughButton(group);
+
+    group = toolbarUi.addGroup();
+    createSuperscriptButton(group);
+    createSubscriptButton(group);
+
+    group = toolbarUi.addGroup();
+    createFontSizeButton(group);
+    createFontFamilyButton(group);
+    createHeadingButton(group);
+
+    group = toolbarUi.addGroup();
+    createIndentButton(group);
+    createOutdentButton(group);
+
+    group = toolbarUi.addGroup();
+    createUnorderedListButton(group);
+    createOrderedListButton(group);
+
+    group = toolbarUi.addGroup();
+    createAlignButtons(group);
+    createClearFormattingButton(group);
+
+    group = toolbarUi.addGroup();
+    createInsertLinkButton(group);
+    createRemoveLinkButton(group);
+
+    group = toolbarUi.addGroup();
+    createInsertGadgetButton(group, user);
+
+    group = toolbarUi.addGroup();
+    createInsertAttachmentButton(group, user);
+  }
+
+  private void insertGadget(final String url) {
+    int from = -1;
+    final FocusedRange focusedRange = editor.getSelectionHelper().getSelectionRange();
+    if (focusedRange != null) {
+      from = focusedRange.getFocus();
+    }
+    if (url != null && !url.isEmpty()) {
+      final XmlStringBuilder xml = GadgetXmlUtil.constructXml(url, "", user.getAddress());
+      final CMutableDocument document = editor.getDocument();
+      if (document == null) {
+        return;
+      }
+      if (from != -1) {
+        final Point<ContentNode> point = document.locate(from);
+        document.insertXml(point, xml);
+      } else {
+        LineContainers.appendLine(document, xml);
+      }
+    }
+  }
+}




More information about the kune-commits mailing list