Package org.waveprotocol.wave.client.doodad.selection

Source Code of org.waveprotocol.wave.client.doodad.selection.SelectionExtractor

/**
* Copyright 2011 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.doodad.selection;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;

import org.waveprotocol.wave.client.editor.EditorContext;
import org.waveprotocol.wave.client.editor.EditorUpdateEvent;
import org.waveprotocol.wave.client.editor.EditorUpdateEvent.EditorUpdateListener;
import org.waveprotocol.wave.client.scheduler.TimerService;
import org.waveprotocol.wave.model.document.MutableAnnotationSet;
import org.waveprotocol.wave.model.document.util.FocusedRange;
import org.waveprotocol.wave.model.document.util.Range;

/**
* Contains logic for reporting a selection as annotations.
*
* @author danilatos@google.com (Daniel Danilatos)
*/
public class SelectionExtractor implements EditorUpdateListener {

  private final TimerService clock;
  private final String address;
  private final String sessionId;

  public SelectionExtractor(TimerService clock, String address, String sessionId) {
    Preconditions.checkNotNull(address, "Address must not be null");
    Preconditions.checkNotNull(sessionId, "Session id must not be null");
    this.clock = clock;
    this.address = address;
    this.sessionId = sessionId;
  }

  /**
   * Starts writing the location of this session's browser selection into the
   * edited document.
   */
  public void start(EditorContext context) {
    writeSelection(context);
    context.addUpdateListener(this);
  }

  /**
   * Stops writing the location of this session's browser selection into the
   * edited document.
   */
  public void stop(EditorContext context) {
    context.removeUpdateListener(this);
    MutableAnnotationSet.Persistent document = context.getDocument();
    int size = document.size();
    String rangeKey = SelectionAnnotationHandler.rangeKey(sessionId);
    String endKey = SelectionAnnotationHandler.endKey(sessionId);
    String dataKey = SelectionAnnotationHandler.dataKey(sessionId);

    document.setAnnotation(0, size, dataKey, null);
    document.setAnnotation(0, size, rangeKey, null);
    document.setAnnotation(0, size, endKey, null);
  }

  @Override
  public void onUpdate(EditorUpdateEvent event) {
    if (event.selectionLocationChanged()) {
      EditorContext context = event.context();

      // Special case to exempt caret annotations from being undoable.
      context.getResponsibilityManager().startIndirectSequence();
      try {
        writeSelection(context);
      } finally {
        context.getResponsibilityManager().endIndirectSequence();
      }
    }
  }

  private void writeSelection(EditorContext context) {
    MutableAnnotationSet.Persistent document = context.getDocument();
    FocusedRange selection = context.getSelectionHelper().getSelectionRange();
    String compositionState = context.getImeCompositionState();
    double currentTimeMillis = clock.currentTimeMillis();

    writeSelection(document, selection, compositionState, currentTimeMillis);
  }

  @VisibleForTesting
  void writeSelection(MutableAnnotationSet.Persistent document, FocusedRange selection,
      String compositionState, double currentTimeMillis) {
    // TODO(danilatos): Use focus and not end
    Range range = selection == null ? null : selection.asRange();
    String rangeKey = SelectionAnnotationHandler.rangeKey(sessionId);
    String endKey = SelectionAnnotationHandler.endKey(sessionId);
    String dataKey = SelectionAnnotationHandler.dataKey(sessionId);
    String value = address;

    int size = document.size();

    // If we have a selection, then continually update regardless of old value,
    // to refresh the timestamp.
    if (range != null) {
      document.setAnnotation(0, size, dataKey, address + "," + currentTimeMillis + ","
          + (compositionState != null ? compositionState : ""));
    }

    // TODO(danilatos): This fiddliness is necessary to avoid gratuitous
    // re-rendering.
    // Later the code can just use the resetAnnotation method, which will be
    // MUCH
    // simpler, once we have proper fine-granularity notifications.
    int currentFocus = document.firstAnnotationChange(0, size, endKey, null);
    int currentEnd = document.lastAnnotationChange(0, size, rangeKey, null);
    if (currentEnd == -1) {
      currentEnd = currentFocus;
    }
    if (currentEnd != -1) {
      // if old selection is annotated
      int currentStart = document.firstAnnotationChange(0, size, rangeKey, null);
      if (currentStart == -1 || currentStart > currentEnd) {
        currentStart = currentEnd;
      }
      if (range != null) {
        // if new selection exists
        int newStart = range.getStart();
        int newEnd = range.getEnd();
        int newFocus = selection.getFocus();

        if (newFocus < currentFocus) {
          document.setAnnotation(newFocus, currentFocus, endKey, value);
        } else if (newFocus > currentFocus) {
          document.setAnnotation(currentFocus, newFocus, endKey, null);
        }

        if (currentStart >= newEnd || newStart >= currentEnd) {
          // If not overlapping
          document.setAnnotation(currentStart, currentEnd, rangeKey, null);
          document.setAnnotation(newStart, newEnd, rangeKey, value);
        } else {
          // If overlapping

          if (currentStart < newStart) {
            document.setAnnotation(currentStart, newStart, rangeKey, null);
          } else if (currentStart > newStart) {
            document.setAnnotation(newStart, currentStart, rangeKey, value);
          }
          if (currentEnd < newEnd) {
            document.setAnnotation(currentEnd, newEnd, rangeKey, value);
          } else if (currentEnd > newEnd) {
            document.setAnnotation(newEnd, currentEnd, rangeKey, null);
          }
        }
      } else {
        // no new selection, clear old one
        document.setAnnotation(currentFocus, size, endKey, null);
        document.setAnnotation(currentStart, currentEnd, rangeKey, null);
        document.setAnnotation(0, size, dataKey, null);
      }
    } else {
      // no old selection
      if (range != null) {
        // new selection exists
        document.setAnnotation(selection.getFocus(), size, endKey, value);
        document.setAnnotation(range.getStart(), range.getEnd(), rangeKey, value);
      }
    }
  }
}
TOP

Related Classes of org.waveprotocol.wave.client.doodad.selection.SelectionExtractor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.