/*
* Copyright 2010 kk-electronic a/s.
*
* This file is part of KKPortal.
*
* KKPortal is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KKPortal is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KKPortal. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kk_electronic.kkportal.core.ui;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
import com.kk_electronic.kkportal.core.ModuleWindowFactory;
import com.kk_electronic.kkportal.core.activity.Activity;
import com.kk_electronic.kkportal.core.event.ContentChangedEvent;
import com.kk_electronic.kkportal.core.event.NewContentEvent;
import com.kk_electronic.kkportal.core.event.TabSelectedEvent;
import com.kk_electronic.kkportal.core.event.TabSelectedEvent.Handler;
import com.kk_electronic.kkportal.core.services.ModuleService.ModuleInfo;
import com.kk_electronic.kkportal.core.tabs.ModuleInfoProvider;
import com.kk_electronic.kkportal.core.tabs.TabInfo;
import com.kk_electronic.kkportal.core.tabs.TabsModel;
import com.kk_electronic.kkportal.core.util.Pair;
/**
* Note this is not a good example yet. Does not have View-Presenter separation
*
* @author Jes Andersen type filter text
*/
public class Canvas implements NewContentEvent.Handler, ContentChangedEvent.Handler, Activity, Handler, GroupDisplay.Handler<ModuleWindow> {
private final GroupDisplay<ModuleWindow> display;
private final ModuleInfoProvider moduleInfoProvider;
private final ModuleWindowFactory windowFactory;
private List<List<ModuleWindow>> groupedModuleWindows;
private final TabsModel tabsModel;
private TabInfo tabInfo;
private final static int timerStart = 1000;
private final static int timerSchedule = 500;
private final static int timerIterationCount = 30;
/**
* The higher the value the more likely the interface is to make new columns when
* dropping a module on the canvas.
* Must be in the range [0.0;0.5]
*/
public double createaffinity = 0.2;
@Inject
public Canvas(EventBus eventBus, GroupDisplay<ModuleWindow> display, TabsModel tabsModel,
ModuleInfoProvider moduleInfoProvider,
ModuleWindowFactory windowFactory) {
this.display = display;
this.tabsModel = tabsModel;
this.moduleInfoProvider = moduleInfoProvider;
this.windowFactory = windowFactory;
eventBus.addHandler(ContentChangedEvent.TYPE, this);
eventBus.addHandler(NewContentEvent.TYPE, this);
tabsModel.addTabSelectedHandler(this);
display.setHandler(this);
showTab(tabsModel.getSelectedTab());
}
@Override
public Widget asWidget() {
return display.asWidget();
}
public void showTab(final TabInfo tabInfo) {
if (tabInfo != null && !tabInfo.equals(this.tabInfo)) {
firstRun = true;
}
this.tabInfo = tabInfo;
if (tabInfo == null || tabInfo.getModuleIds() == null) {
display.setWidgets(null);
return;
}
HashSet<Integer> needed = new HashSet<Integer>();
for (List<Integer> i : tabInfo.getModuleIds()) {
needed.addAll(i);
}
moduleInfoProvider.translate(needed,
new AsyncCallback<Map<Integer, ModuleInfo>>() {
@Override
public void onFailure(Throwable caught) {
GWT.log("View failed to get module infos", caught);
}
@Override
public void onSuccess(Map<Integer, ModuleInfo> result) {
updateDisplay(tabInfo.getModuleIds(),result);
if (firstRun) {
delayedSizeCheck.schedule(timerStart);
firstRun = false;
}
}
});
}
private boolean firstRun = true; // Flag for updateDisplay Timer
private void updateDisplay(List<List<Integer>> ids,
Map<Integer, ModuleInfo> map) {
groupedModuleWindows = new ArrayList<List<ModuleWindow>>();
for (int index=0,l=ids.size();index<l;index++) {
List<Integer> idcolumn = ids.get(index);
List<ModuleWindow> column = new ArrayList<ModuleWindow>();
groupedModuleWindows.add(column);
for (int id : idcolumn) {
final ModuleWindow moduleWindow = windowFactory.get(map.get(id));
column.add(moduleWindow);
moduleWindow.setFirstColumn(index==0);
moduleWindow.setLastColumn((index+1)==l);
moduleWindow.addDeleteHandler(new MouseDownHandler() {
@Override
public void onMouseDown(MouseDownEvent event) {
delete(moduleWindow);
event.stopPropagation();
}
});
}
}
display.setWidgets(groupedModuleWindows);
}
protected boolean delete(ModuleWindow moduleWindow) {
for(List<ModuleWindow> column:groupedModuleWindows){
if(column.remove(moduleWindow)) {
tabsModel.setModuleIds(tabInfo, this.getIds(groupedModuleWindows));
return true;
}
}
return false;
}
@Override
public void onTabSelected(TabSelectedEvent event) {
showTab(event.getTabInfo());
}
@Override
public void onElementDrop(double x, int y, ModuleWindow element) {
Widget p = element.getParent();
x = 100.0 * (double)((element.getAbsoluteLeft()-p.getAbsoluteLeft() + element.getOffsetWidth()/2)) / p.getOffsetWidth();
y = (element.getAbsoluteTop() - p.getAbsoluteTop() + element.getOffsetHeight()/2);
GWT.log("Before move"+groupedModuleWindows.toString());
for(List<ModuleWindow> moduleWindows : groupedModuleWindows){
if(moduleWindows.remove(element)) break;
}
List<ModuleWindow> column = findColumn(x);
int index = findIndex(column,y);
column.add(index,element);
List<List<ModuleWindow>> newgroupedModuleWindows = new ArrayList<List<ModuleWindow>>();
for(List<ModuleWindow> moduleWindows : groupedModuleWindows){
if(!moduleWindows.isEmpty()) newgroupedModuleWindows.add(moduleWindows);
}
groupedModuleWindows = newgroupedModuleWindows;
element.setFirstColumn(column == groupedModuleWindows.get(0));
GWT.log("After move"+groupedModuleWindows.toString());
tabsModel.setModuleIds(tabInfo,getIds(groupedModuleWindows));
}
private List<List<Integer>> getIds(
List<List<ModuleWindow>> listlist) {
List<List<Integer>> retval = new ArrayList<List<Integer>>();
for(List<ModuleWindow> list:listlist){
List<Integer> ids = new ArrayList<Integer>();
for(ModuleWindow o:list){
ids.add(o.getModule().getId());
}
retval.add(ids);
}
return retval;
}
private int findIndex(List<? extends KnownHeight> column, int y) {
int index = 0;
for(KnownHeight x:column){
int h = x.getDesiredHeight();
if(y < h/2) break;
y-= h;
index++;
}
return index;
}
private List<ModuleWindow> findColumn(final double x) {
//TODO: Support variable width
double cw = x;
for (List<ModuleWindow> column : groupedModuleWindows) {
double width = 100.0/groupedModuleWindows.size();
if (cw < width){
double columnprocent = cw/width;
if(columnprocent < createaffinity){
List<ModuleWindow> newcolumn = new ArrayList<ModuleWindow>();
groupedModuleWindows.add(groupedModuleWindows.indexOf(column), newcolumn);
return newcolumn;
}
if((1 - columnprocent) < createaffinity){
List<ModuleWindow> newcolumn = new ArrayList<ModuleWindow>();
groupedModuleWindows.add(groupedModuleWindows.indexOf(column)+1, newcolumn);
return newcolumn;
}
GWT.log("DROP-" + columnprocent);
return column;
}
cw -= width;
}
List<ModuleWindow> newcolumn = new ArrayList<ModuleWindow>();
groupedModuleWindows.add(newcolumn);
return newcolumn;
}
@Override
public void onContentSizeChanged(ContentChangedEvent event) {
delayedSizeCheck.run();
}
@Override
public void onNewContent(NewContentEvent event) {
delayedSizeCheck.schedule(timerStart);
}
Timer delayedSizeCheck = new Timer() {
private int i = 0;
private boolean updated = false;
@Override
public void run() {
if (display.checkForResizes()) {
showTab(tabsModel.getSelectedTab());
updated = true;
}
if (i >= timerIterationCount) {
i = 0;
if(updated) {
saveModuleHeights();
updated = false;
}
} else {
this.schedule(timerSchedule);
//GWT.log("Check Height Timer run! #" + i);
i = i + 1;
}
}
};
private void saveModuleHeights() {
if (tabsModel.getSelectedTab() == null ||
tabsModel.getSelectedTab().getModuleIds() == null) {
return;
}
HashSet<Integer> needed = new HashSet<Integer>();
for (List<Integer> i : tabsModel.getSelectedTab().getModuleIds()) {
needed.addAll(i);
}
moduleInfoProvider.translate(needed, new AsyncCallback<Map<Integer,ModuleInfo>>(){
@Override
public void onFailure(Throwable caught) {
GWT.log("Canvas Height failed to get module infos", caught);
}
@Override
public void onSuccess(Map<Integer, ModuleInfo> result) {
List<Pair<Integer, Integer>> list = new ArrayList<Pair<Integer,Integer>>();
for (Entry<Integer, ModuleInfo> entry : result.entrySet()) {
list.add(new Pair<Integer, Integer>(entry.getKey(), entry.getValue().getHeight()));
}
tabsModel.setModuleHeight(list);
}
});
}
}