Author: yurize
Date: Thu Nov  1 20:33:20 2012
New Revision: 1404748

URL: http://svn.apache.org/viewvc?rev=1404748&view=rev
Log:
Adds "New wave with the participants of the current wave" feature. By
rocklund.
https://reviews.apache.org/r/7353

Added:
    
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantSelectorWidget.java
    
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantSelectorWidget.ui.xml
    
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantWidget.java
    
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantWidget.ui.xml
Modified:
    
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StageTwoProvider.java
    
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StagesProvider.java
    
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/WebClient.java
    
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/events/WaveCreationEvent.java
    
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/events/WaveCreationEventHandler.java
    incubator/wave/trunk/src/org/waveprotocol/wave/client/StageThree.java
    
incubator/wave/trunk/src/org/waveprotocol/wave/client/uibuilder/OutputHelper.java
    
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantController.java
    
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/View.java
    
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/DomAsViewProvider.java
    
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/FullStructure.java
    
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/Participants.css
    
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/ParticipantsViewBuilder.java
    
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/TypeCodes.java
    
incubator/wave/trunk/src/org/waveprotocol/wave/concurrencycontrol/wave/CcBasedWavelet.java
    
incubator/wave/trunk/src/org/waveprotocol/wave/model/conversation/Conversation.java
    
incubator/wave/trunk/src/org/waveprotocol/wave/model/conversation/WaveletBasedConversation.java
    incubator/wave/trunk/src/org/waveprotocol/wave/model/wave/Wavelet.java
    
incubator/wave/trunk/src/org/waveprotocol/wave/model/wave/opbased/OpBasedWavelet.java
    
incubator/wave/trunk/test/org/waveprotocol/wave/model/wave/opbased/OpBasedWaveletTestBase.java

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StageTwoProvider.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StageTwoProvider.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StageTwoProvider.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StageTwoProvider.java
 Thu Nov  1 20:33:20 2012
@@ -35,6 +35,8 @@ import org.waveprotocol.wave.model.wave.
 import org.waveprotocol.wave.model.wave.data.impl.WaveViewDataImpl;
 import org.waveprotocol.wave.model.waveref.WaveRef;
 
+import java.util.Set;
+
 /**
  * Provides stage 2 of the staged loading of the wave panel
  *
@@ -56,16 +58,20 @@ public class StageTwoProvider extends St
    * new protocol.
    */
   private AsyncHolder.Accessor<StageTwo> whenReady;
+  private final Set<ParticipantId> otherParticipants;
 
   /**
    * @param waveId the id of the wave to open, or null to create a new wave
    * @param channel communication channel
    * @param idGenerator
+   * @param otherParticipants the participants to add to the newly created 
wave,
+   *        in addition to the creator. {@code null} if only the creator
+   *        should be added.
    * @param unsavedIndicatorElement
    */
   public StageTwoProvider(StageOne stageOne, WaveRef waveRef, 
RemoteViewServiceMultiplexer channel,
       boolean isNewWave, IdGenerator idGenerator, ProfileManager profiles,
-      UnsavedDataListener unsavedDataListener) {
+      UnsavedDataListener unsavedDataListener, Set<ParticipantId> 
otherParticipants) {
     super(stageOne, unsavedDataListener);
     Preconditions.checkArgument(stageOne != null);
     Preconditions.checkArgument(waveRef != null);
@@ -75,6 +81,7 @@ public class StageTwoProvider extends St
     this.isNewWave = isNewWave;
     this.idGenerator = idGenerator;
     this.profiles = profiles;
+    this.otherParticipants = otherParticipants;
   }
 
   @Override
@@ -110,6 +117,9 @@ public class StageTwoProvider extends St
     if (isNewWave) {
       // For a new wave, initial state comes from local initialization.
       getConversations().createRoot().getRootThread().appendBlip();
+
+      // Adding any initial participant to the new wave
+      getConversations().getRoot().addParticipantIds(otherParticipants);
       super.install();
       whenReady.use(StageTwoProvider.this);
     } else {

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StagesProvider.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StagesProvider.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StagesProvider.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StagesProvider.java
 Thu Nov  1 20:33:20 2012
@@ -41,8 +41,11 @@ import org.waveprotocol.wave.client.wave
 import org.waveprotocol.wave.client.wavepanel.view.dom.full.BlipQueueRenderer;
 import org.waveprotocol.wave.model.conversation.ConversationView;
 import org.waveprotocol.wave.model.id.IdGenerator;
+import org.waveprotocol.wave.model.wave.ParticipantId;
 import org.waveprotocol.wave.model.waveref.WaveRef;
 
+import java.util.Set;
+
 /**
  * Stages for loading the undercurrent Wave Panel
  *
@@ -75,6 +78,8 @@ public class StagesProvider extends Stag
   private StageThree three;
   private WaveContext wave;
 
+  private Set<ParticipantId> participants;
+
   /**
    * @param wavePanelElement the DOM element to become the wave panel.
    * @param unsavedIndicatorElement the element that displays the wave saved 
state.
@@ -86,11 +91,13 @@ public class StagesProvider extends Stag
    * @param channel the communication channel.
    * @param isNewWave true if the wave is a new client-created wave
    * @param idGenerator
+   * @param participants the participants to add to the newly created wave. 
null
+   *                     if only the creator should be added
    */
   public StagesProvider(Element wavePanelElement, Element 
unsavedIndicatorElement,
       LogicalPanel rootPanel, FramedPanel waveFrame, WaveRef waveRef, 
RemoteViewServiceMultiplexer channel,
       IdGenerator idGenerator, ProfileManager profiles, WaveStore store, 
boolean isNewWave,
-      String localDomain) {
+      String localDomain, Set<ParticipantId> participants) {
     this.wavePanelElement = wavePanelElement;
     this.unsavedIndicatorElement = unsavedIndicatorElement;
     this.waveFrame = waveFrame;
@@ -102,6 +109,7 @@ public class StagesProvider extends Stag
     this.waveStore = store;
     this.isNewWave = isNewWave;
     this.localDomain = localDomain;
+    this.participants = participants;
   }
 
   @Override
@@ -127,7 +135,7 @@ public class StagesProvider extends Stag
   @Override
   protected AsyncHolder<StageTwo> createStageTwoLoader(StageOne one) {
     return haltIfClosed(new StageTwoProvider(this.one = one, waveRef, channel, 
isNewWave,
-        idGenerator, profiles, new 
SavedStateIndicator(unsavedIndicatorElement)));
+        idGenerator, profiles, new 
SavedStateIndicator(unsavedIndicatorElement), participants));
   }
 
   @Override

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/WebClient.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/WebClient.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/WebClient.java 
(original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/WebClient.java 
Thu Nov  1 20:33:20 2012
@@ -48,6 +48,7 @@ import org.waveprotocol.box.webclient.se
 import org.waveprotocol.box.webclient.search.SearchPanelWidget;
 import org.waveprotocol.box.webclient.search.SearchPresenter;
 import org.waveprotocol.box.webclient.search.SimpleSearch;
+import org.waveprotocol.box.webclient.search.WaveContext;
 import org.waveprotocol.box.webclient.search.WaveStore;
 import org.waveprotocol.box.webclient.widget.error.ErrorIndicatorPresenter;
 import org.waveprotocol.box.webclient.widget.frame.FramedPanel;
@@ -71,6 +72,7 @@ import org.waveprotocol.wave.model.waver
 import org.waveprotocol.wave.util.escapers.GwtWaverefEncoder;
 
 import java.util.Date;
+import java.util.Set;
 import java.util.logging.Logger;
 
 /**
@@ -152,13 +154,12 @@ public class WebClient implements EntryP
         new WaveCreationEventHandler() {
 
           @Override
-          public void onCreateRequest(WaveCreationEvent event) {
+          public void onCreateRequest(WaveCreationEvent event, 
Set<ParticipantId> participantSet) {
             LOG.info("WaveCreationEvent received");
             if (channel == null) {
               throw new RuntimeException("Spaghetti attack.  Create occured 
before login");
             }
-
-            openWave(WaveRef.of(idGenerator.newWaveId()), true);
+            openWave(WaveRef.of(idGenerator.newWaveId()), true, 
participantSet);
           }
         });
 
@@ -208,7 +209,7 @@ public class WebClient implements EntryP
         new SearchPresenter.WaveActionHandler() {
           @Override
           public void onCreateWave() {
-            ClientEvents.get().fireEvent(WaveCreationEvent.CREATE_NEW_WAVE);
+            ClientEvents.get().fireEvent(new WaveCreationEvent());
           }
 
           @Override
@@ -228,7 +229,7 @@ public class WebClient implements EntryP
     ClientEvents.get().addWaveSelectionEventHandler(new 
WaveSelectionEventHandler() {
       @Override
       public void onSelection(WaveRef waveRef) {
-        openWave(waveRef, false);
+        openWave(waveRef, false, null);
       }
     });
   }
@@ -288,8 +289,10 @@ public class WebClient implements EntryP
    *
    * @param waveRef wave id to open
    * @param isNewWave whether the wave is being created by this client session.
+   * @param participants the participants to add to the newly created wave.
+   *        {@code null} if only the creator should be added
    */
-  private void openWave(WaveRef waveRef, boolean isNewWave) {
+  private void openWave(WaveRef waveRef, boolean isNewWave, Set<ParticipantId> 
participants) {
     LOG.info("WebClient.openWave()");
 
     if (wave != null) {
@@ -304,7 +307,7 @@ public class WebClient implements EntryP
     Element unsavedIndicator = 
Document.get().getElementById("unsavedStateContainer");
     StagesProvider wave =
         new StagesProvider(holder, unsavedIndicator, waveHolder, waveFrame, 
waveRef, channel, idGenerator,
-            profiles, waveStore, isNewWave, Session.get().getDomain());
+            profiles, waveStore, isNewWave, Session.get().getDomain(), 
participants);
     this.wave = wave;
     wave.load(new Command() {
       @Override

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/events/WaveCreationEvent.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/events/WaveCreationEvent.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/events/WaveCreationEvent.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/events/WaveCreationEvent.java
 Thu Nov  1 20:33:20 2012
@@ -17,18 +17,25 @@
 package org.waveprotocol.box.webclient.client.events;
 
 import com.google.gwt.event.shared.GwtEvent;
+import org.waveprotocol.wave.model.wave.ParticipantId;
+import java.util.Set;
 
 public class WaveCreationEvent extends GwtEvent<WaveCreationEventHandler> {
   public static final Type<WaveCreationEventHandler> TYPE = new 
Type<WaveCreationEventHandler>();
 
-  public static final WaveCreationEvent CREATE_NEW_WAVE = new 
WaveCreationEvent();
+  private final Set<ParticipantId> participants;
 
-  private WaveCreationEvent() {
+  public WaveCreationEvent() {
+    this.participants = null;
+  }
+
+  public WaveCreationEvent(Set<ParticipantId> participants) {
+    this.participants = participants;
   }
 
   @Override
   protected void dispatch(WaveCreationEventHandler handler) {
-    handler.onCreateRequest(this);
+    handler.onCreateRequest(this, participants);
   }
 
   @Override

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/events/WaveCreationEventHandler.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/events/WaveCreationEventHandler.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/events/WaveCreationEventHandler.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/events/WaveCreationEventHandler.java
 Thu Nov  1 20:33:20 2012
@@ -17,10 +17,12 @@
 package org.waveprotocol.box.webclient.client.events;
 
 import com.google.gwt.event.shared.EventHandler;
+import org.waveprotocol.wave.model.wave.ParticipantId;
+import java.util.Set;
 
 public abstract class WaveCreationEventHandler implements EventHandler {
   /**
    * Called when something wants to create a new wave.
    */
-  public abstract void onCreateRequest(WaveCreationEvent event);
+  public abstract void onCreateRequest(WaveCreationEvent event, 
Set<ParticipantId> participants);
 }

Modified: incubator/wave/trunk/src/org/waveprotocol/wave/client/StageThree.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/StageThree.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- incubator/wave/trunk/src/org/waveprotocol/wave/client/StageThree.java 
(original)
+++ incubator/wave/trunk/src/org/waveprotocol/wave/client/StageThree.java Thu 
Nov  1 20:33:20 2012
@@ -201,7 +201,7 @@ public interface StageThree {
       WaveTitleHandler.install(edit, models);
       ReplyIndicatorController.install(actions, edit, panel);
       EditController.install(focus, actions, panel);
-      ParticipantController.install(panel, models, profiles, getLocalDomain());
+      ParticipantController.install(panel, models, profiles, getLocalDomain(), 
user);
       KeepFocusInView.install(edit, panel);
       stageTwo.getDiffController().upgrade(edit);
     }

Modified: 
incubator/wave/trunk/src/org/waveprotocol/wave/client/uibuilder/OutputHelper.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/uibuilder/OutputHelper.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/client/uibuilder/OutputHelper.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/client/uibuilder/OutputHelper.java
 Thu Nov  1 20:33:20 2012
@@ -174,4 +174,17 @@ public final class OutputHelper {
         + "></img>");
     builder.append(img);
   }
+
+  public static void button(SafeHtmlBuilder builder,
+      String id,
+      String clazz,
+      String kind,
+      String title,
+      String caption) {
+    builder.appendHtmlConstant("<button " //
+        + (id != null ? " id='" + id + "'" : "") //
+        + (clazz != null ? " class='" + clazz + "'" : "") //
+        + (kind != null ? " " + BuilderHelper.KIND_ATTRIBUTE + "='" + kind + 
"'" : "") //
+        + ">"+ caption + "</button>");
+  }
 }

Modified: 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantController.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantController.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantController.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantController.java
 Thu Nov  1 20:33:20 2012
@@ -23,6 +23,8 @@ import com.google.gwt.event.dom.client.C
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.user.client.Window;
 
+import org.waveprotocol.box.webclient.client.ClientEvents;
+import org.waveprotocol.box.webclient.client.events.WaveCreationEvent;
 import org.waveprotocol.wave.client.account.Profile;
 import org.waveprotocol.wave.client.account.ProfileManager;
 import org.waveprotocol.wave.client.common.safehtml.EscapeUtils;
@@ -35,6 +37,7 @@ import org.waveprotocol.wave.client.wave
 import org.waveprotocol.wave.client.wavepanel.view.dom.DomAsViewProvider;
 import org.waveprotocol.wave.client.wavepanel.view.dom.ModelAsViewProvider;
 import org.waveprotocol.wave.client.wavepanel.view.dom.full.TypeCodes;
+import org.waveprotocol.wave.client.widget.popup.UniversalPopup;
 import org.waveprotocol.wave.client.widget.profile.ProfilePopupPresenter;
 import org.waveprotocol.wave.client.widget.profile.ProfilePopupView;
 import org.waveprotocol.wave.model.conversation.Conversation;
@@ -43,6 +46,8 @@ import org.waveprotocol.wave.model.util.
 import org.waveprotocol.wave.model.wave.InvalidParticipantAddress;
 import org.waveprotocol.wave.model.wave.ParticipantId;
 
+import java.util.Set;
+
 /**
  * Installs the add/remove participant controls.
  *
@@ -52,26 +57,31 @@ public final class ParticipantController
   private final ModelAsViewProvider models;
   private final ProfileManager profiles;
   private final String localDomain;
+  private final ParticipantId user;
+  private UniversalPopup popup = null;
 
   /**
    * @param localDomain nullable. if provided, automatic suffixing will occur.
+   * @param user the logged in user
    */
   ParticipantController(
       DomAsViewProvider views, ModelAsViewProvider models, ProfileManager 
profiles,
-      String localDomain) {
+      String localDomain, ParticipantId user) {
     this.views = views;
     this.models = models;
     this.profiles = profiles;
     this.localDomain = localDomain;
+    this.user = user;
   }
 
   /**
    * Builds and installs the participant control feature.
+   * @param user the logged in user
    */
   public static void install(WavePanel panel, ModelAsViewProvider models, 
ProfileManager profiles,
-      String localDomain) {
+      String localDomain, ParticipantId user) {
     ParticipantController controller =
-        new ParticipantController(panel.getViewProvider(), models, profiles, 
localDomain);
+        new ParticipantController(panel.getViewProvider(), models, profiles, 
localDomain, user);
     controller.install(panel.getHandlers());
   }
 
@@ -83,6 +93,14 @@ public final class ParticipantController
         return true;
       }
     });
+    
handlers.registerClickHandler(TypeCodes.kind(Type.NEW_WAVE_WITH_PARTICIPANTS),
+      new WaveClickHandler() {
+        @Override
+        public boolean onClick(ClickEvent event, Element context) {
+          handleNewWaveWithParticipantsButtonClicked(context);
+          return true;
+        }
+      });
     handlers.registerClickHandler(TypeCodes.kind(Type.PARTICIPANT), new 
WaveClickHandler() {
       @Override
       public boolean onClick(ClickEvent event, Element context) {
@@ -130,6 +148,34 @@ public final class ParticipantController
   }
 
   /**
+   * Creates a new wave with the participants of the current wave. Showing
+   * a popup dialog where the user can chose to deselect users that should not
+   * be participants in the new wave
+   */
+  private void handleNewWaveWithParticipantsButtonClicked(Element context) {
+    ParticipantsView participantsUi = 
views.fromNewWaveWithParticipantsButton(context);
+    ParticipantSelectorWidget selector = new ParticipantSelectorWidget();
+    popup = null;
+    selector.setListener(new ParticipantSelectorWidget.Listener() {
+      @Override
+      public void onSelect(Set<ParticipantId> participants) {
+        if (popup != null) {
+          popup.hide();
+        }
+        ClientEvents.get().fireEvent(
+            new WaveCreationEvent(participants));
+      }
+
+      @Override
+      public void onCancel() {
+        popup.hide();
+      }
+    });
+    popup = selector.showInPopup(user,
+        models.getParticipants(participantsUi).getParticipantIds(), profiles);
+  }
+
+  /**
    * Shows an add-participant popup.
    */
   private void handleAddButtonClicked(Element context) {

Added: 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantSelectorWidget.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantSelectorWidget.java?rev=1404748&view=auto
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantSelectorWidget.java
 (added)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantSelectorWidget.java
 Thu Nov  1 20:33:20 2012
@@ -0,0 +1,164 @@
+package org.waveprotocol.wave.client.wavepanel.impl.edit;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.DockLayoutPanel;
+import com.google.gwt.user.client.ui.FlowPanel;
+
+import org.waveprotocol.wave.client.account.ProfileManager;
+import org.waveprotocol.wave.client.widget.common.ImplPanel;
+import org.waveprotocol.wave.client.widget.popup.CenterPopupPositioner;
+import org.waveprotocol.wave.client.widget.popup.PopupChrome;
+import org.waveprotocol.wave.client.widget.popup.PopupChromeFactory;
+import org.waveprotocol.wave.client.widget.popup.PopupFactory;
+import org.waveprotocol.wave.client.widget.popup.TitleBar;
+import org.waveprotocol.wave.client.widget.popup.UniversalPopup;
+import org.waveprotocol.wave.model.wave.ParticipantId;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Selector for participants to add to a newly created wave. Allowing an user
+ * to create a new wave with participants from another wave.
+ *
+ * @author [email protected]
+ */
+public class ParticipantSelectorWidget extends Composite implements
+    ParticipantWidget.Listener {
+  public interface Listener {
+    void onSelect(Set<ParticipantId> participants);
+    void onCancel();
+  }
+
+  interface Binder extends UiBinder<ImplPanel, ParticipantSelectorWidget> {
+  }
+
+  private static final Binder BINDER = GWT.create(Binder.class);
+
+  @UiField ImplPanel self;
+  @UiField DockLayoutPanel dockPanel;
+  @UiField Button createButton;
+  @UiField Button cancelButton;
+  @UiField Button selectAllButton;
+  @UiField Button deselectAllButton;
+  @UiField FlowPanel options;
+
+  private Listener listener;
+  private Set<ParticipantId> participants;
+  private ParticipantId user;
+  private ProfileManager profiles;
+
+  public ParticipantSelectorWidget() {
+    initWidget(self = BINDER.createAndBindUi(this));
+  }
+
+  public void setListener(Listener listener) {
+    this.listener = listener;
+  }
+
+  /**
+   * Shows in a popup, and returns the popup.
+   * @param user the logged in user. The popup does not show it but makes sure 
it is
+   *             in the participant set returned
+   */
+  public UniversalPopup showInPopup(ParticipantId user, Set<ParticipantId> 
participants,
+      ProfileManager profiles) {
+    PopupChrome chrome = PopupChromeFactory.createPopupChrome();
+    UniversalPopup popup = PopupFactory.createPopup(
+        null, new CenterPopupPositioner(), chrome, true);
+
+    TitleBar titleBar = popup.getTitleBar();
+    titleBar.setTitleText("Select participants");
+    popup.add(ParticipantSelectorWidget.this);
+
+    this.user = user;
+    this.participants = new HashSet<ParticipantId>(participants);
+    this.profiles = profiles;
+
+    // If there is only one participant, create the wave without showing the 
popup
+    if (participants.size() == 1) {
+      if (listener != null) {
+        listener.onSelect(participants);
+      }
+      return popup;
+    }
+
+    createParticipantList(participants);
+    popup.show();
+    setFocusAndHeight();
+
+    return popup;
+  }
+
+  private void setFocusAndHeight() {
+    Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
+      @Override
+      public void execute() {
+        createButton.setFocus(true);
+        dockPanel
+            .setHeight((ParticipantSelectorWidget.this.getParent()
+                .getElement().getOffsetHeight()) + "px");
+      }
+    });
+  }
+
+  private void createParticipantList(Set<ParticipantId> participants) {
+    options.clear();
+    for (ParticipantId participant : participants) {
+      if (!participant.equals(user)) {
+        ParticipantWidget participantWidget = new ParticipantWidget();
+        participantWidget.setParticipant(participant);
+        
participantWidget.setImage(profiles.getProfile(participant).getImageUrl());
+        participantWidget.setListener(this);
+        options.add(participantWidget);
+      }
+    }
+  }
+
+  @UiHandler("createButton")
+  void onClickCreateButton(ClickEvent event) {
+    if (listener != null) {
+      listener.onSelect(participants);
+    }
+  }
+
+  @UiHandler("cancelButton")
+  void onClickCancelButton(ClickEvent event) {
+    if (listener != null) {
+      listener.onCancel();
+    }
+  }
+
+  @UiHandler("selectAllButton")
+  void onSelectAllClicked(ClickEvent event) {
+    for (int i = 0; i < options.getWidgetCount(); i++) {
+      ((ParticipantWidget)options.getWidget(i)).mark();
+    }
+  }
+
+  @UiHandler("deselectAllButton")
+  void onDeselectAllClicked(ClickEvent event) {
+    for (int i = 0; i < options.getWidgetCount(); i++) {
+      ((ParticipantWidget)options.getWidget(i)).unMark();
+    }
+  }
+
+  @Override
+  public void onMark(ParticipantId participant) {
+    if (!participants.contains(participant)) {
+      participants.add(participant);
+    }
+  }
+
+  @Override
+  public void onUnMark(ParticipantId participant) {
+    participants.remove(participant);
+  }
+}

Added: 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantSelectorWidget.ui.xml
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantSelectorWidget.ui.xml?rev=1404748&view=auto
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantSelectorWidget.ui.xml
 (added)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantSelectorWidget.ui.xml
 Thu Nov  1 20:33:20 2012
@@ -0,0 +1,73 @@
+<ui:UiBinder
+    xmlns:ui="urn:ui:com.google.gwt.uibinder"
+    xmlns:g="urn:import:com.google.gwt.user.client.ui"
+    xmlns:w="urn:import:org.waveprotocol.wave.client.widget.common" >
+
+  <ui:style>
+    .self {
+      padding: 0 15px;
+      height: 700px;
+    }
+
+    .custom, .selectionButtons, .self label {
+      margin: 15px 0;
+    }
+
+    .custom {
+      float: left
+    }
+
+    .selectionButtons {
+      float: right
+    }
+
+    .self label {
+      display: block;
+      font-weight: bold;
+    }
+
+    .center {
+      height: 15px;
+    }
+
+    .dock {
+      width: 600px;
+    }
+
+    .html {
+      height: 50px;
+    }
+  </ui:style>
+
+  <w:ImplPanel ui:field="self" styleName="{style.self}">
+  <g:DockLayoutPanel unit="PX" ui:field="dockPanel"
+    styleName="{style.dock}">
+    <g:north size="50">
+      <g:HTMLPanel>
+        <div class="{style.custom}">
+          <label>Select the participants to add to the new wave</label>
+        </div>
+        <div class="{style.selectionButtons}">
+          <g:Button ui:field="selectAllButton">Select all</g:Button>
+          <g:Button ui:field="deselectAllButton">Deselect all</g:Button>
+        </div>
+      </g:HTMLPanel>
+    </g:north>
+    <g:center>
+      <!-- list of participants -->
+      <g:ScrollPanel>
+        <g:FlowPanel ui:field="options" />
+      </g:ScrollPanel>
+    </g:center>
+    <g:south size="65">
+      <g:HTMLPanel styleName="{style.html}">
+      <div class="{style.center}" />
+        <center>
+          <g:Button ui:field="createButton">Create</g:Button>
+          <g:Button ui:field="cancelButton">Cancel</g:Button>
+        </center>
+      </g:HTMLPanel>
+    </g:south>
+  </g:DockLayoutPanel>
+</w:ImplPanel>
+</ui:UiBinder>
\ No newline at end of file

Added: 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantWidget.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantWidget.java?rev=1404748&view=auto
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantWidget.java
 (added)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantWidget.java
 Thu Nov  1 20:33:20 2012
@@ -0,0 +1,91 @@
+package org.waveprotocol.wave.client.wavepanel.impl.edit;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.ImageElement;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.ui.Composite;
+
+import org.waveprotocol.wave.client.widget.common.ImplPanel;
+import org.waveprotocol.wave.model.wave.ParticipantId;
+
+/**
+ * A widget for displaying participants
+ *
+ * @author [email protected]
+ */
+class ParticipantWidget extends Composite {
+  interface Listener {
+    void onMark(ParticipantId participant);
+    void onUnMark(ParticipantId participant);
+  }
+
+  interface Binder extends UiBinder<ImplPanel, ParticipantWidget> {
+  }
+
+  private static final Binder BINDER = GWT.create(Binder.class);
+
+  @UiField ImplPanel self;
+  @UiField Element title;
+  @UiField ImageElement image;
+  private Listener listener;
+
+  private boolean marked;
+  ParticipantId participant;
+
+  public ParticipantWidget() {
+    initWidget(self = BINDER.createAndBindUi(this));
+    mark();
+  }
+
+  public void setImage(String url) {
+    image.setSrc(url);
+  }
+
+  /**
+   * Sets the visible title.
+   *
+   * Semantics differ from UiObject method.
+   */
+  @Override
+  public void setTitle(String text) {
+    title.setInnerText(text);
+  }
+
+  public void setParticipant(ParticipantId participant) {
+    this.participant = participant;
+    setTitle(participant.toString());
+  }
+
+  public void setListener(Listener listener) {
+    this.listener = listener;
+  }
+
+  @UiHandler("self")
+  void onClick(ClickEvent e) {
+    if (marked) {
+      unMark();
+    } else {
+      mark();
+    }
+  }
+
+  public void mark() {
+    marked = true;
+    self.getElement().getStyle().setBackgroundColor("#789e35");
+    if (listener != null) {
+      listener.onMark(participant);
+    }
+  }
+
+  public void unMark() {
+    marked = false;
+    self.getElement().getStyle().setBackgroundColor("#fff");
+    if (listener != null) {
+      listener.onUnMark(participant);
+    }
+  }
+}

Added: 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantWidget.ui.xml
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantWidget.ui.xml?rev=1404748&view=auto
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantWidget.ui.xml
 (added)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/edit/ParticipantWidget.ui.xml
 Thu Nov  1 20:33:20 2012
@@ -0,0 +1,41 @@
+<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
+  xmlns:w="urn:import:org.waveprotocol.wave.client.widget.common" 
xmlns:g="urn:import:com.google.gwt.user.client.ui">
+
+  <ui:style>
+    .self {
+      overflow: hidden;
+      cursor: pointer;
+      border: 1px solid #ccc;
+      margin: 0px 0px;
+    }
+
+    .img {
+      float: left;
+    }
+
+    .img img {
+      width: 50px;
+      height: 50px;
+    }
+
+    .tx {
+      float: left;
+      vertical-align:middle;
+      margin-top: auto;
+      margin-bottom: auto;
+    }
+
+    .img, .tx, .self h3 {
+      margin: 15px;
+    }
+  </ui:style>
+
+  <w:ImplPanel ui:field="self" styleName="{style.self}">
+     <div class="{style.img}">
+       <img ui:field="image"/>
+    </div>
+    <div class="{style.tx}">
+      <h3 ui:field="title"/>
+    </div>
+  </w:ImplPanel>
+</ui:UiBinder>
\ No newline at end of file

Modified: 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/View.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/View.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/View.java 
(original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/View.java 
Thu Nov  1 20:33:20 2012
@@ -63,6 +63,7 @@ public interface View {
     PARTICIPANT,
     PARTICIPANTS,
     ADD_PARTICIPANT,
+    NEW_WAVE_WITH_PARTICIPANTS,
   }
 
   Type getType();

Modified: 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/DomAsViewProvider.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/DomAsViewProvider.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/DomAsViewProvider.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/DomAsViewProvider.java
 Thu Nov  1 20:33:20 2012
@@ -69,6 +69,9 @@ public interface DomAsViewProvider {
   /** @return the participants view that surrounds the button {@code source}. 
*/
   ParticipantsView fromAddButton(Element source);
 
+  /** @return the participants view that surrounds the button {@code source}. 
*/
+  ParticipantsView fromNewWaveWithParticipantsButton(Element source);
+
   /** @return {@code source} exposed as a top-conversation view. */
   TopConversationView asTopConversation(Element source);
 

Modified: 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/FullStructure.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/FullStructure.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/FullStructure.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/FullStructure.java
 Thu Nov  1 20:33:20 2012
@@ -807,6 +807,17 @@ public class FullStructure implements Up
     return asParticipants(e);
   }
 
+  @Override
+  public ParticipantsView fromNewWaveWithParticipantsButton(Element e) {
+    Preconditions.checkArgument(e == null || typeOf(e) == 
Type.NEW_WAVE_WITH_PARTICIPANTS);
+    while (e != null && !hasKnownType(e)) {
+      e = e.getParentElement();
+    }
+    // Assume that the nearest kinded ancestor of the add button is the
+    // participants view (an exception is thrown if not).
+    return asParticipants(e);
+  }
+
   private AnchorView asAnchor(View v) {
     Preconditions.checkArgument(v == null || v.getType() == Type.ANCHOR);
     return (AnchorView) v;

Modified: 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/Participants.css
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/Participants.css?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/Participants.css
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/Participants.css
 Thu Nov  1 20:33:20 2012
@@ -88,7 +88,7 @@
   /* Positioned contained for add button. */
   position: relative;
   /* Room for absolutely positioned add button. */
-  padding-right: 4.5em;
+  padding-right: 11em;
 }
 
 .participant {
@@ -98,7 +98,7 @@
 
 /* Name-based rendering. */
 div.participant {
-  line-height: 42px;
+  line-height: 44px;
   margin: 4px 0 0 12px;
   text-decoration: underline;
   color: blue;
@@ -142,7 +142,7 @@ img.participant {
   padding: 14px 0 8px 4px;
   background-color: #c9e2fc;
   bottom: 0;
-  width: 4em;
+  width: 10em;
 }
 
 .extra {
@@ -163,6 +163,12 @@ img.participant {
   padding-top: 21px;
 }
 
+.newWaveWithParticipantsButton {
+  position: absolute;
+  width: 7em;
+  margin-left: 5px;
+}
+
 /*
  * Sprites.
  */
@@ -184,6 +190,6 @@ img.participant {
 @sprite .addButton {
   gwt-image: 'addButton';
   /* The button is a span, to conform to HTML's backward schema. */
-  display: block;
+  display: inline-block;
   cursor: pointer;
-}
+}
\ No newline at end of file

Modified: 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/ParticipantsViewBuilder.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/ParticipantsViewBuilder.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/ParticipantsViewBuilder.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/ParticipantsViewBuilder.java
 Thu Nov  1 20:33:20 2012
@@ -21,6 +21,7 @@ import static org.waveprotocol.wave.clie
 import static org.waveprotocol.wave.client.uibuilder.OutputHelper.open;
 import static org.waveprotocol.wave.client.uibuilder.OutputHelper.openSpan;
 import static org.waveprotocol.wave.client.uibuilder.OutputHelper.openSpanWith;
+import static org.waveprotocol.wave.client.uibuilder.OutputHelper.button;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.gwt.resources.client.ClientBundle;
@@ -68,6 +69,7 @@ public final class ParticipantsViewBuild
     String expandButton();
     String collapseButton();
     String addButton();
+    String newWaveWithParticipantsButton();
   }
 
   /** An enum for all the components of a participants view. */
@@ -132,6 +134,9 @@ public final class ParticipantsViewBuild
             }
             closeSpan(output);
             appendSpan(output, null, css.addButton(), 
TypeCodes.kind(Type.ADD_PARTICIPANT));
+            button(output, null, css.newWaveWithParticipantsButton(),
+                TypeCodes.kind(Type.NEW_WAVE_WITH_PARTICIPANTS),
+                "New wave with the participants of current wave", "New wave");
           }
           closeSpan(output);
 
@@ -139,6 +144,9 @@ public final class ParticipantsViewBuild
           openSpan(output, null, css.simple(), null);
           {
             appendSpan(output, null, css.addButton(), 
TypeCodes.kind(Type.ADD_PARTICIPANT));
+            button(output, null, css.newWaveWithParticipantsButton(),
+                TypeCodes.kind(Type.NEW_WAVE_WITH_PARTICIPANTS),
+                "New wave with the participants of current wave", "New wave");
           }
           closeSpan(output);
         }

Modified: 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/TypeCodes.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/TypeCodes.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/TypeCodes.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/TypeCodes.java
 Thu Nov  1 20:33:20 2012
@@ -62,6 +62,7 @@ public final class TypeCodes {
     TYPES.put("p", Type.PARTICIPANT);
     TYPES.put("i", Type.MENU_ITEM);
     TYPES.put("a", Type.ADD_PARTICIPANT);
+    TYPES.put("npw", Type.NEW_WAVE_WITH_PARTICIPANTS);
 
     TYPES.each(new ProcV<Type>() {
       @Override

Modified: 
incubator/wave/trunk/src/org/waveprotocol/wave/concurrencycontrol/wave/CcBasedWavelet.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/concurrencycontrol/wave/CcBasedWavelet.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/concurrencycontrol/wave/CcBasedWavelet.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/concurrencycontrol/wave/CcBasedWavelet.java
 Thu Nov  1 20:33:20 2012
@@ -307,6 +307,12 @@ public final class CcBasedWavelet implem
   //
 
   @Override
+  public void addParticipantIds(Set<ParticipantId> participants) {
+    checkNotFailed();
+    wavelet.addParticipantIds(participants);
+  }
+
+  @Override
   public void addParticipant(ParticipantId participant) {
     checkNotFailed();
     wavelet.addParticipant(participant);

Modified: 
incubator/wave/trunk/src/org/waveprotocol/wave/model/conversation/Conversation.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/model/conversation/Conversation.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/model/conversation/Conversation.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/model/conversation/Conversation.java
 Thu Nov  1 20:33:20 2012
@@ -157,6 +157,15 @@ public interface Conversation {
   Set<ParticipantId> getParticipantIds();
 
   /**
+   * Adds a set of participant ids to this conversation. Does nothing for
+   * participants that are already participant on this conversation. Does
+   * nothing if the participants set is {@code null}.
+   *
+   * @param participants the participant ids to add
+   */
+  void addParticipantIds(Set<ParticipantId> participants);
+
+  /**
    * Adds a participant to this conversation. Does nothing if the participant 
is
    * already a participant on this conversation.
    *

Modified: 
incubator/wave/trunk/src/org/waveprotocol/wave/model/conversation/WaveletBasedConversation.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/model/conversation/WaveletBasedConversation.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/model/conversation/WaveletBasedConversation.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/model/conversation/WaveletBasedConversation.java
 Thu Nov  1 20:33:20 2012
@@ -377,6 +377,12 @@ public final class WaveletBasedConversat
   }
 
   @Override
+  public void addParticipantIds(Set<ParticipantId> participants) {
+    checkIsUsable();
+    wavelet.addParticipantIds(participants);
+  }
+
+  @Override
   public void addParticipant(ParticipantId participant) {
     checkIsUsable();
     wavelet.addParticipant(participant);

Modified: incubator/wave/trunk/src/org/waveprotocol/wave/model/wave/Wavelet.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/model/wave/Wavelet.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- incubator/wave/trunk/src/org/waveprotocol/wave/model/wave/Wavelet.java 
(original)
+++ incubator/wave/trunk/src/org/waveprotocol/wave/model/wave/Wavelet.java Thu 
Nov  1 20:33:20 2012
@@ -120,6 +120,15 @@ public interface Wavelet {
   Set<ParticipantId> getParticipantIds();
 
   /**
+   * Adds a set of participant ids to this conversation. Does nothing for
+   * participants that are already participant on this conversation. Does
+   * nothing if the participants set is {@code null}.
+   *
+   * @param participants the participant ids to add
+   */
+  void addParticipantIds(Set<ParticipantId> participants);
+
+  /**
    * Gets the version number of this wavelet.
    *
    * @return this wavelet's version

Modified: 
incubator/wave/trunk/src/org/waveprotocol/wave/model/wave/opbased/OpBasedWavelet.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/model/wave/opbased/OpBasedWavelet.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/wave/model/wave/opbased/OpBasedWavelet.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/wave/model/wave/opbased/OpBasedWavelet.java
 Thu Nov  1 20:33:20 2012
@@ -469,6 +469,19 @@ public class OpBasedWavelet implements O
   //
 
   /**
+   * Creates and consumes an {@link AddParticipant} operation for each
+   * participant in the set.
+   */
+  @Override
+  public void addParticipantIds(Set<ParticipantId> participants) {
+    if (participants != null) {
+      for (ParticipantId participant : participants) {
+        addParticipant(participant);
+      }
+    }
+  }
+
+  /**
    * Creates and consumes an {@link AddParticipant} operation.
    */
   @Override

Modified: 
incubator/wave/trunk/test/org/waveprotocol/wave/model/wave/opbased/OpBasedWaveletTestBase.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/test/org/waveprotocol/wave/model/wave/opbased/OpBasedWaveletTestBase.java?rev=1404748&r1=1404747&r2=1404748&view=diff
==============================================================================
--- 
incubator/wave/trunk/test/org/waveprotocol/wave/model/wave/opbased/OpBasedWaveletTestBase.java
 (original)
+++ 
incubator/wave/trunk/test/org/waveprotocol/wave/model/wave/opbased/OpBasedWaveletTestBase.java
 Thu Nov  1 20:33:20 2012
@@ -35,7 +35,9 @@ import org.waveprotocol.wave.model.wave.
 import org.waveprotocol.wave.model.wave.data.ObservableWaveletData;
 
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * White-box test for {@link OpBasedWavelet}.
@@ -98,6 +100,23 @@ public abstract class OpBasedWaveletTest
     assertEquals(creator, ((AddParticipant) 
sink.getConsumedOp()).getParticipantId());
   }
 
+  public void testAddingParticipantIdSetProducesManyOperations() {
+    target.addParticipant(target.getCreatorId());
+    Set<ParticipantId> participants = new HashSet<ParticipantId>();
+    for (int i = 0; i < 20; i++) {
+      ParticipantId participant = new ParticipantId("foo" + i + "@bar.com");
+      participants.add(participant);
+    }
+    target.addParticipantIds(participants);
+    List<WaveletOperation> operations = sink.getOps();
+    for (WaveletOperation op : operations) {
+      assertTrue("The operation was not an AddParticipant operator",
+          op instanceof AddParticipant);
+      participants.remove(((AddParticipant)op).getParticipantId());
+    }
+    assertEquals("Not all participants resulted in an operation",0, 
participants.size());
+  }
+
   public void testAddingManyParticipantProducesManyOperations() {
     // Creator must be added as first op.
     target.addParticipant(target.getCreatorId());


Reply via email to