/* Copyright (c) 2009 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 com.google.appengine.demos.sticky.client;
import com.google.appengine.demos.sticky.client.model.Model;
import com.google.appengine.demos.sticky.client.model.Note;
import com.google.appengine.demos.sticky.client.model.Surface;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.WidgetCollection;
/**
* A widget to display the collection of notes that are on a particular
* {@link Surface}.
*
* @author knorton@google.com (Kelly Norton)
*/
public class SurfaceView extends FlowPanel implements Model.DataObserver {
/**
* A widget for displaying a single {@link Note}.
*/
private class NoteView extends SimplePanel implements Note.Observer,
MouseUpHandler, MouseDownHandler, MouseMoveHandler,
ValueChangeHandler<String> {
private final Note note;
private final DivElement titleElement;
private final TextArea content = new TextArea();
// Dragging state.
private boolean dragging;
private int dragOffsetX, dragOffsetY;
/**
* @param note
* the note to render
*/
public NoteView(Note note) {
this.note = note;
setStyleName("note");
note.setObserver(this);
// Build simple DOM Structure.
final Element elem = getElement();
elem.getStyle().setProperty("position", "absolute");
titleElement = elem.appendChild(Document.get().createDivElement());
titleElement.setClassName("note-title");
content.setStyleName("note-content");
content.addValueChangeHandler(this);
setWidget(content);
render();
addDomHandler(this, MouseDownEvent.getType());
addDomHandler(this, MouseMoveEvent.getType());
addDomHandler(this, MouseUpEvent.getType());
}
public void onMouseDown(MouseDownEvent event) {
SurfaceView.this.select(this);
if (!note.isOwnedByCurrentUser()) {
return;
}
final EventTarget target = event.getNativeEvent().getEventTarget();
assert Element.is(target);
if (!Element.is(target)) {
return;
}
if (titleElement.isOrHasChild(Element.as(target))) {
dragging = true;
final Element elem = getElement().cast();
dragOffsetX = event.getX();
dragOffsetY = event.getY();
DOM.setCapture(elem);
event.preventDefault();
}
}
public void onMouseMove(MouseMoveEvent event) {
if (dragging) {
setPixelPosition(event.getX() + getAbsoluteLeft() - dragOffsetX,
event.getY() + getAbsoluteTop() - dragOffsetY);
event.preventDefault();
}
}
public void onMouseUp(MouseUpEvent event) {
if (dragging) {
dragging = false;
DOM.releaseCapture(getElement());
event.preventDefault();
model.updateNotePosition(note, getAbsoluteLeft(), getAbsoluteTop(),
note.getWidth(), note.getHeight());
}
}
public void onUpdate(Note note) {
render();
}
public void onValueChange(ValueChangeEvent<String> event) {
model.updateNoteContent(note, event.getValue());
}
public void setPixelPosition(int x, int y) {
final Style style = getElement().getStyle();
style.setPropertyPx("left", x);
style.setPropertyPx("top", y);
}
public void setPixelSize(int width, int height) {
content.setPixelSize(width, height);
}
private void render() {
setPixelPosition(note.getX(), note.getY());
setPixelSize(note.getWidth(), note.getHeight());
titleElement.setInnerHTML(note.getAuthorName());
final String noteContent = note.getContent();
content.setText((noteContent == null) ? "" : noteContent);
content.setReadOnly(!note.isOwnedByCurrentUser());
}
private void select() {
getElement().getStyle().setProperty("zIndex", "" + nextZIndex());
}
}
private final Model model;
private NoteView selectedNoteView;
private int zIndex = 1;
/**
* @param model
* the model to which the Ui will bind itself
*/
public SurfaceView(Model model) {
this.model = model;
final Element elem = getElement();
elem.setId("surface");
elem.getStyle().setProperty("position", "absolute");
model.addDataObserver(this);
}
public void onNoteCreated(Note note) {
final NoteView view = new NoteView(note);
add(view);
select(view);
}
public void onSurfaceCreated(Surface group) {
}
public void onSurfaceNotesReceived(Note[] notes) {
removeAllNotes();
for (int i = 0, n = notes.length; i < n; ++i) {
add(new NoteView(notes[i]));
}
}
public void onSurfaceSelected(Surface nowSelected, Surface wasSelected) {
}
public void onSurfacesReceived(Surface[] surfaces) {
}
private int nextZIndex() {
return zIndex++;
}
private void removeAllNotes() {
final WidgetCollection kids = getChildren();
while (kids.size() > 0) {
remove(kids.size() - 1);
}
}
private void select(NoteView noteView) {
assert noteView != null;
if (selectedNoteView != noteView) {
noteView.select();
selectedNoteView = noteView;
}
}
}