package ilarkesto.gwt.client;
import ilarkesto.gwt.client.animation.AnimatingFlowPanel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Widget;
public class ObjectMappedFlowPanel<O extends Object, W extends Widget> extends Composite implements HasWidgets {
public static Map<Object, Integer> objectHeights = new HashMap<Object, Integer>();
private AnimatingFlowPanel<W> panel;
private WidgetFactory<O, W> widgetFactory;
private MoveObserver<O, W> moveObserver;
private List<O> objectList;
private Map<O, W> widgetMap;
private boolean virgin = true;
public ObjectMappedFlowPanel(WidgetFactory<O, W> widgetFactory) {
this.widgetFactory = widgetFactory;
objectList = new ArrayList<O>();
widgetMap = new HashMap<O, W>();
panel = new AnimatingFlowPanel<W>();
initWidget(panel);
}
public void set(List<O> newObjects) {
boolean animationAllowed = !virgin;
virgin = false;
if (objectList.equals(newObjects)) return;
// remove old objects
List<O> objectsToRemove = getOterObjects(newObjects);
if (!objectsToRemove.isEmpty()) {
boolean animate = animationAllowed && objectsToRemove.size() == 1;
for (O object : objectsToRemove) {
remove(object, animate);
}
}
if (objectList.equals(newObjects)) return;
// add new objects
List<O> objectsToAdd = new ArrayList<O>(newObjects);
objectsToAdd.removeAll(objectList);
if (!objectsToAdd.isEmpty()) {
boolean animate = animationAllowed && objectsToAdd.size() == 1;
for (O object : objectsToAdd) {
int index = newObjects.indexOf(object);
if (index > objectList.size()) {
index = objectList.size();
}
insert(index, object, animate, null);
}
}
if (objectList.equals(newObjects)) return;
// move existing objects
int index = 0;
for (O object : newObjects) {
int currentIndex = objectList.indexOf(object);
if (currentIndex != index) {
move(index, object, false, null);
}
index++;
}
assert objectList.equals(newObjects);
}
private W insert(int index, O object, boolean animate, Runnable runAfter) {
assert object != null;
assert !objectList.contains(object);
assert objectHeights != null;
assert panel != null;
W widget = createWidget(object);
if (animate) {
panel.insertAnimated(index, widget, objectHeights.get(object), runAfter);
} else {
panel.insert(index, widget);
if (runAfter != null) runAfter.run();
}
objectList.add(index, object);
assert objectList.contains(object);
assert objectList.size() == widgetMap.size();
return widget;
}
private W remove(O object, boolean animate) {
assert containsObject(object);
W widget = getWidget(object);
if (animate) {
int height = widget.getElement().getOffsetHeight();
objectHeights.put(object, height);
panel.removeAnimated(widget);
} else {
panel.remove(widget);
}
objectList.remove(object);
widgetMap.remove(object);
assert !containsObject(object);
assert objectList.size() == widgetMap.size();
return widget;
}
public W move(int toIndex, O object, boolean animate, Runnable runAfterMoved) {
assert toIndex >= 0 && toIndex <= objectList.size();
assert objectList.contains(object);
W oldWidget = remove(object, animate);
W newWidget = insert(toIndex, object, animate, runAfterMoved);
if (moveObserver != null) moveObserver.moved(object, oldWidget, newWidget);
assert objectList.size() == widgetMap.size();
return newWidget;
}
public void clear() {
panel.clear();
objectList.clear();
widgetMap.clear();
}
private List<O> getOterObjects(Collection<O> objects) {
List<O> ret = new ArrayList<O>();
for (O object : objectList) {
if (!objects.contains(object)) ret.add(object);
}
return ret;
}
public List<O> getObjects() {
return objectList;
}
public Collection<W> getWidgets() {
return widgetMap.values();
}
public int indexOfObject(O object) {
return objectList.indexOf(object);
}
public W getWidget(int index) {
return getWidget(objectList.get(index));
}
public W getWidget(O object) {
assert object != null;
W widget = widgetMap.get(object);
assert widget != null;
return widget;
}
private W createWidget(O object) {
W widget = widgetFactory.createWidget(object);
widgetMap.put(object, widget);
return widget;
}
public boolean containsObject(O object) {
return objectList.contains(object);
}
public int size() {
return objectList.size();
}
@Override
public Iterator<Widget> iterator() {
return panel.iterator();
}
@Override
public void add(Widget w) {
throw new RuntimeException("Not implemented.");
}
@Override
public boolean remove(Widget w) {
return false;
}
public void setMoveObserver(MoveObserver<O, W> moveObserver) {
this.moveObserver = moveObserver;
}
public static interface WidgetFactory<O extends Object, W extends Widget> {
W createWidget(O object);
}
public static interface MoveObserver<O extends Object, W extends Widget> {
void moved(O object, W oldWidget, W newWidget);
}
}