package scrum.client.common;
import ilarkesto.core.logging.Log;
import ilarkesto.core.scope.Scope;
import ilarkesto.gwt.client.Gwt;
import ilarkesto.gwt.client.ObjectMappedFlowPanel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import scrum.client.dnd.BlockDndMarkerWidget;
import scrum.client.dnd.BlockListDropAction;
import scrum.client.workspace.DndManager;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Widget;
/**
* List of <code>BlockWidget</code>s.
*/
public final class BlockListWidget<O> extends AScrumWidget {
// private static final Logger LOG = Logger.get(BlockListWidget.class);
DndManager dndManager;
private ObjectMappedFlowPanel<O, ABlockWidget<O>> list;
private boolean dndSorting = true;
private Comparator<O> autoSorter;
private BlockWidgetFactory<O> blockWidgetFactory;
private Runnable moveObserver;
private BlockListSelectionManager selectionManager;
private BlockListDropAction<O> dropAction;
private BlockDndMarkerWidget dndMarkerBottom;
private FlowPanel panel;
private ElementPredicate<O> highlightPredicate;
public BlockListWidget(BlockWidgetFactory<O> blockWidgetFactory, BlockListDropAction<O> dropAction) {
this.dropAction = dropAction;
this.blockWidgetFactory = blockWidgetFactory;
dndMarkerBottom = new BlockDndMarkerWidget();
}
public void setMinHeight(int height) {
initialize();
panel.getElement().getStyle().setProperty("minHeight", height + "px");
}
public BlockListWidget(BlockWidgetFactory<O> blockWidgetFactory) {
this(blockWidgetFactory, null);
}
public void setSelectionManager(BlockListSelectionManager selectionManager) {
this.selectionManager = selectionManager;
selectionManager.add(this);
}
@Override
protected Widget onInitialization() {
dndManager = Scope.get().getComponent(DndManager.class);
list = new ObjectMappedFlowPanel<O, ABlockWidget<O>>(
new ObjectMappedFlowPanel.WidgetFactory<O, ABlockWidget<O>>() {
public ABlockWidget<O> createWidget(O object) {
ABlockWidget<O> block = blockWidgetFactory.createBlock();
block.setObject(object);
block.setList(BlockListWidget.this);
block.update();
return block;
}
});
list.setMoveObserver(new ObjectMappedFlowPanel.MoveObserver<O, ABlockWidget<O>>() {
public void moved(O object, ABlockWidget<O> oldWidget, ABlockWidget<O> newWidget) {
newWidget.setExtended(oldWidget.isExtended());
}
});
panel = new FlowPanel();
panel.setStyleName("BlockListWidget");
panel.add(list);
panel.add(dndMarkerBottom);
return panel;
}
@Override
protected void onUpdate() {
super.onUpdate();
}
public void addAdditionalStyleName(String styleName) {
initialize();
panel.addStyleName(styleName);
}
public void removeAdditionalStyleName(String styleName) {
initialize();
panel.removeStyleName(styleName);
}
public ABlockWidget<O> getBlock(int row) {
return list.getWidget(row);
}
public O getPrevious(O object) {
int index = list.getObjects().indexOf(object);
if (index < 1) return null;
return list.getObjects().get(index - 1);
}
public ABlockWidget<O> getBlock(O object) {
return list.getWidget(object);
}
public void setMoveObserver(Runnable orderObserver) {
this.moveObserver = orderObserver;
}
public final void setAutoSorter(Comparator<O> autoSorter) {
this.autoSorter = autoSorter;
if (autoSorter != null) setDndSorting(false);
}
public final boolean isDndSorting() {
if (dndManager == null) return false;
return dndSorting;
}
public final void setDndSorting(boolean dndSorting) {
this.dndSorting = dndSorting;
}
public final void clear() {
initialize();
list.clear();
}
public final void setObjects(O... objects) {
setObjects(Gwt.toList(objects));
}
public final void setObjects(List<O> newObjects) {
initialize();
if (autoSorter != null) {
Collections.sort(newObjects, autoSorter);
}
list.set(newObjects);
}
public final void setObjects(Collection<O> newObjects) {
setObjects(new ArrayList<O>(newObjects));
}
public final void drop(ABlockWidget<O> block, int toIndex) {
Log.DEBUG("Dropping to index", toIndex, "->", block);
assert block != null;
if (block.getList() == this) {
list.move(toIndex, block.getObject(), true, moveObserver);
return;
}
dropAction.onDrop(block.getObject());
}
public final int size() {
return list.size();
}
public final int indexOfBlock(ABlockWidget<O> block) {
return indexOfObject(block.getObject());
}
private final int indexOfObject(O object) {
return list.indexOfObject(object);
}
public final boolean extendRow(int row, boolean exclusive) {
if (exclusive) {
if (selectionManager == null) {
collapseAll();
} else {
selectionManager.deselectAll();
}
}
if (row < 0) return false;
ABlockWidget<O> block = getBlock(row);
block.setExtended(true);
block.activate();
return true;
}
public final void collapseRow(int row) {
getBlock(row).setExtended(false);
}
public final void collapseObject(O object) {
collapseRow(indexOfObject(object));
}
public final boolean showObject(O object) {
if (!extendObject(object)) return false;
scrollToObject(object);
return true;
}
public final void scrollToObject(O object) {
getBlock(object).scrollIntoView();
}
public final void extendBlock(ABlockWidget<O> block, boolean exclusive) {
int idx = indexOfBlock(block);
extendRow(idx, exclusive);
assert isExtended(block.getObject());
}
public final boolean extendObject(O object) {
return extendObject(object, true);
}
public final boolean extendObject(O object, boolean exclusive) {
int idx = indexOfObject(object);
if (idx < 0) {
Log.DEBUG("Extending block failed. Object does not exist:", object);
return false;
}
extendRow(idx, exclusive);
assert isExtended(object);
return true;
}
public final void toggleExtension(O object, boolean exclusive) {
if (isExtended(object)) {
if (exclusive) {
if (selectionManager != null) {
selectionManager.deselectAll();
} else {
collapseAll();
}
} else {
collapseObject(object);
}
} else {
extendObject(object, exclusive);
}
}
public final boolean isExtended(O object) {
return getBlock(object).isExtended();
}
public final boolean contains(O object) {
return list.containsObject(object);
}
public final void collapseAll() {
for (ABlockWidget<O> block : list.getWidgets()) {
block.setExtended(false);
}
}
private ABlockWidget<O> getPreviousBlock(ABlockWidget<O> block) {
int idx = indexOfBlock(block);
if (idx < 1) return null;
ABlockWidget<O> previous = getBlock(idx - 1);
assert list.indexOfObject(previous.getObject()) == list.indexOfObject(block.getObject()) - 1;
return previous;
}
private ABlockWidget<O> getNextBlock(ABlockWidget<O> block) {
int idx = indexOfBlock(block);
if (idx < 0 || idx > size() - 2) return null;
ABlockWidget<O> next = getBlock(idx + 1);
assert list.indexOfObject(next.getObject()) == list.indexOfObject(block.getObject()) + 1;
return next;
}
public void deactivateDndMarkers() {
for (ABlockWidget<O> block : list.getWidgets()) {
block.deactivateDndMarkers();
}
dndMarkerBottom.setActive(false);
}
public void deactivateDndMarkers(ABlockWidget<O> block) {
block.deactivateDndMarkers();
ABlockWidget<O> previous = getPreviousBlock(block);
if (previous != null) previous.deactivateDndMarkers();
ABlockWidget<O> next = getNextBlock(block);
if (next != null) next.deactivateDndMarkers();
dndMarkerBottom.setActive(false);
}
public void activateDndMarkerBefore(ABlockWidget<O> block) {
ABlockWidget<O> previous = getPreviousBlock(block);
if (previous != null) previous.deactivateDndMarkers();
ABlockWidget<O> next = getNextBlock(block);
if (next != null) next.deactivateDndMarkers();
dndMarkerBottom.setActive(false);
block.activateDndMarkerTop();
}
public void activateDndMarkerAfter(ABlockWidget<O> block) {
deactivateDndMarkers(block);
ABlockWidget<O> next = getNextBlock(block);
if (next == null) {
dndMarkerBottom.setActive(true);
} else {
next.activateDndMarkerTop();
}
}
public void activateDrop() {
dndMarkerBottom.setActive(true);
}
public List<O> getObjects() {
return list.getObjects();
}
public boolean acceptsDrop(ABlockWidget<O> block) {
if (this == block.getList()) return true;
if (dummy == null) dummy = blockWidgetFactory.createBlock();
return dummy.getClass().getName().equals(block.getClass().getName());
}
private ABlockWidget<O> dummy = null;
@Override
protected void onLoad() {
super.onLoad();
if (dndManager != null) dndManager.registerDropTarget(this);
}
@Override
protected void onUnload() {
if (dndManager != null) dndManager.unregisterDropTarget(this);
super.onUnload();
}
private void updateTaskHighlighting() {
for (ABlockWidget<O> block : list.getWidgets()) {
if (highlightPredicate != null && highlightPredicate.contains(block.getObject()))
block.addStyleName("highlighted");
else block.removeStyleName("highlighted");
}
}
public void setTaskHighlighting(ElementPredicate<O> predicate) {
this.highlightPredicate = predicate;
updateTaskHighlighting();
}
public void clearTaskHighlighting() {
this.highlightPredicate = null;
updateTaskHighlighting();
}
}