/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option) any
later version.
This program 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along
with this program; if not, see http://www.gnu.org/licenses or write to the Free
Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
*/
package com.servoy.j2db.server.headlessclient.dataui;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Point;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.IAjaxCallDecorator;
import org.apache.wicket.ajax.calldecorator.AjaxCallDecorator;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.mozilla.javascript.Undefined;
import com.servoy.base.scripting.api.IJSEvent.EventType;
import com.servoy.j2db.ControllerUndoManager;
import com.servoy.j2db.FormController;
import com.servoy.j2db.IApplication;
import com.servoy.j2db.IScriptExecuter;
import com.servoy.j2db.IView;
import com.servoy.j2db.dataprocessing.DataAdapterList;
import com.servoy.j2db.dataprocessing.IDisplay;
import com.servoy.j2db.dataprocessing.IDisplayData;
import com.servoy.j2db.dataprocessing.IFoundSetInternal;
import com.servoy.j2db.dataprocessing.IRecordInternal;
import com.servoy.j2db.dataprocessing.Record;
import com.servoy.j2db.dnd.DRAGNDROP;
import com.servoy.j2db.dnd.JSDNDEvent;
import com.servoy.j2db.persistence.Form;
import com.servoy.j2db.persistence.IDataProviderLookup;
import com.servoy.j2db.persistence.IPersist;
import com.servoy.j2db.server.headlessclient.WebForm;
import com.servoy.j2db.server.headlessclient.dnd.DraggableBehavior;
import com.servoy.j2db.ui.DataRendererOnRenderWrapper;
import com.servoy.j2db.ui.IComponent;
import com.servoy.j2db.ui.IDataRenderer;
import com.servoy.j2db.ui.IProviderStylePropertyChanges;
import com.servoy.j2db.ui.IStylePropertyChanges;
import com.servoy.j2db.ui.ISupportOnRenderCallback;
import com.servoy.j2db.ui.ISupportRowStyling;
import com.servoy.j2db.ui.runtime.IRuntimeComponent;
import com.servoy.j2db.util.ComponentFactoryHelper;
import com.servoy.j2db.util.Debug;
import com.servoy.j2db.util.IDelegate;
import com.servoy.j2db.util.IStyleRule;
import com.servoy.j2db.util.IStyleSheet;
import com.servoy.j2db.util.PersistHelper;
import com.servoy.j2db.util.Utils;
/**
* The web implementation of the {@link IDataRenderer}
*
* @author jcompagner
*/
public class WebDataRenderer extends WebMarkupContainer implements IDataRenderer, IProviderStylePropertyChanges
{
private static final long serialVersionUID = 1L;
private Dimension size = new Dimension(0, 0);
private Point location = new Point(0, 0);
// private boolean visible;
private String tooltiptext;
// private Cursor cursor;
private boolean opaque;
private Border border;
private Font font;
private Color foreground;
private Color background;
// private final IApplication application;
private DataAdapterList dataAdapterList;
// private final Set globalFields = new HashSet();
private HashMap<IPersist, IDisplay> fieldComponents = new HashMap<IPersist, IDisplay>();
private final ChangesRecorder jsChangeRecorder = new ChangesRecorder(TemplateGenerator.DEFAULT_FIELD_BORDER_SIZE, TemplateGenerator.DEFAULT_FIELD_PADDING);
private IView parentView;
private final String formPartName;
private FormController dragNdropController;
private final ISupportOnRenderCallback dataRendererOnRenderWrapper;
/**
* @param id
*/
public WebDataRenderer(String id, String formPartName, final IApplication app)
{
super(id);
// application = app;
add(StyleAttributeModifierModel.INSTANCE);
add(TooltipAttributeModifier.INSTANCE);
setOutputMarkupPlaceholderTag(true);
this.formPartName = formPartName;
dataRendererOnRenderWrapper = new DataRendererOnRenderWrapper(this);
add(new StyleAppendingModifier(new Model<String>()
{
private static final long serialVersionUID = 1L;
@Override
public String getObject()
{
WebForm container = findParent(WebForm.class);
if (container != null && container.getBorder() instanceof TitledBorder)
{
int offset = ComponentFactoryHelper.getTitledBorderHeight(container.getBorder());
return "top: " + offset + "px;";
}
return ""; //$NON-NLS-1$
}
})
{
@Override
public boolean isEnabled(Component component)
{
if (WebDataRenderer.this.getParent().get(0) != WebDataRenderer.this) return false;
WebForm container = component.findParent(WebForm.class);
if (container != null && container.getBorder() instanceof TitledBorder)
{
return super.isEnabled(component);
}
return false;
}
});
add(new ServoyAjaxEventBehavior("onclick", null, true)
{
@Override
protected void onEvent(AjaxRequestTarget target)
{
if (app.getFoundSetManager().getEditRecordList().isEditing())
{
app.getFoundSetManager().getEditRecordList().stopEditing(false);
WebEventExecutor.generateResponse(target, getPage());
}
}
@Override
protected IAjaxCallDecorator getAjaxCallDecorator()
{
return new AjaxCallDecorator()
{
private static final long serialVersionUID = 1L;
@Override
public CharSequence decorateScript(CharSequence script)
{
return "if ((event.target || event.srcElement) == this){ " + script + " }"; //$NON-NLS-1$
}
};
}
});
}
public void setParentView(IView parentView)
{
this.parentView = parentView;
}
/**
* @see com.servoy.j2db.ui.IDataRenderer#getDataAdapterList()
*/
public DataAdapterList getDataAdapterList()
{
return dataAdapterList;
}
public void addDisplayComponent(IPersist obj, IDisplay comp)
{
fieldComponents.put(obj, comp);
}
void createDataAdapter(IApplication app, IDataProviderLookup dataProviderLookup, IScriptExecuter el, ControllerUndoManager undoManager) throws Exception
{
dataAdapterList = new DataAdapterList(app, dataProviderLookup, fieldComponents, el.getFormController(), null, undoManager);
//make it really fields only
HashMap<IPersist, IDisplay> f = new HashMap<IPersist, IDisplay>();
Iterator<Map.Entry<IPersist, IDisplay>> it = fieldComponents.entrySet().iterator();
while (it.hasNext())
{
Map.Entry<IPersist, IDisplay> element = it.next();
if (element.getValue() instanceof IDisplayData)
{
// String id = ((IDisplayData)element.getValue()).getDataProviderID();
// if (dataProviderLookup.getDataProvider(id) instanceof ScriptVariable)
// {
// globalFields.add(element.getValue());
// }
f.put(element.getKey(), element.getValue());
}
}
fieldComponents = f;
}
/**
* @see com.servoy.j2db.ui.IDataRenderer#setAllNonFieldsEnabled(boolean)
*/
public void setAllNonFieldsEnabled(boolean b)
{
// TODO Auto-generated method stub
}
/**
* @see com.servoy.j2db.ui.IDataRenderer#setAllNonRowFieldsEnabled(boolean)
*/
public void setAllNonRowFieldsEnabled(boolean b)
{
// TODO Auto-generated method stub
}
/**
* @see com.servoy.j2db.ui.IDataRenderer#stopUIEditing()
*/
public boolean stopUIEditing(boolean looseFocus)
{
if (dataAdapterList != null) return dataAdapterList.stopUIEditing(looseFocus);
return true;
}
/**
* @see com.servoy.j2db.ui.IDataRenderer#setName(java.lang.String)
*/
public void setName(String name)
{
// ignore id can't be set in web
}
/**
* @see com.servoy.j2db.ui.IComponent#getName()
*/
public String getName()
{
return getId();
}
public void destroy()
{
if (dataAdapterList != null)
{
dataAdapterList.destroy();
dataAdapterList = null;
}
if (getRequestCycle() != null)
{
removeAll();
if (getParent() != null)
{
remove();
}
}
}
public void setComponentVisible(boolean visible)
{
// this.visible = visible;
}
public void setLocation(Point location)
{
this.location = location;
}
public void setSize(Dimension size)
{
this.size = size;
}
public void setForeground(Color foreground)
{
this.foreground = foreground;
}
public void setBackground(Color background)
{
this.background = background;
}
public void setFont(Font font)
{
this.font = font;
}
public void setBorder(Border border)
{
this.border = border;
}
public void setOpaque(boolean opaque)
{
this.opaque = opaque;
}
public void setCursor(Cursor cursor)
{
// this.cursor = cursor;
}
public void setToolTipText(String tooltiptext)
{
if (Utils.stringIsEmpty(tooltiptext))
{
this.tooltiptext = null;
}
else
{
this.tooltiptext = tooltiptext;
}
}
/**
* @see com.servoy.j2db.ui.IComponent#getToolTipText()
*/
public String getToolTipText()
{
return tooltiptext;
}
public Point getLocation()
{
return location;
}
public Dimension getSize()
{
return size;
}
public void notifyVisible(boolean b, List<Runnable> invokeLaterRunnables)
{
if (dataAdapterList != null)
{
//we just forward the call
dataAdapterList.notifyVisible(b, invokeLaterRunnables);
}
}
public void refreshRecord(IRecordInternal record)
{
IModel< ? > model = getDefaultModel();
if (model != null)
{
model.detach();
}
if (dataAdapterList != null)
{
dataAdapterList.setRecord(record, true);
}
DataAdapterList.setDataRendererComponentsRenderState(this, record);
if (getOnRenderComponent().getRenderEventExecutor().hasRenderCallback())
{
jsChangeRecorder.setChanged();
}
}
public Color getBackground()
{
return background;
}
public Border getBorder()
{
return border;
}
public Font getFont()
{
return font;
}
public Color getForeground()
{
return foreground;
}
public boolean isOpaque()
{
return opaque;
}
public void setComponentEnabled(boolean enabled)
{
setEnabled(enabled);
}
@SuppressWarnings("nls")
public Iterator< ? extends IComponent> getComponentIterator()
{
final Iterator< ? extends Component> wicketIterator = iterator();
Iterator< ? extends IComponent> iterator = new Iterator<IComponent>()
{
private IComponent next;
public boolean hasNext()
{
if (next != null) return true;
while (wicketIterator.hasNext())
{
Object comp = wicketIterator.next();
Object delegate = comp;
while (delegate instanceof IDelegate< ? > && !(delegate instanceof IComponent))
{
delegate = ((IDelegate< ? >)delegate).getDelegate();
}
if (delegate instanceof IComponent)
{
next = (IComponent)delegate;
return true;
}
Debug.error("Component found which is not an IComponent " + comp + " for a datarenderer " + this, new RuntimeException(
comp.getClass().toString()));
}
return false;
}
public IComponent next()
{
if (next == null)
{
hasNext();
}
if (next == null)
{
throw new NoSuchElementException("Component iterator of " + this + " has no more elements");
}
IComponent returnValue = next;
next = null;
return returnValue;
}
public void remove()
{
wicketIterator.remove();
}
};
return iterator;
}
public void add(IComponent c, String name)
{
//ignore
}
public void remove(IComponent c)
{
//ignore
}
@Override
protected void onBeforeRender()
{
super.onBeforeRender();
if (getBgcolor() == null)
{
String parentViewBGColor = getParentViewBgColor();
if (parentViewBGColor != null)
{
setBgcolor(parentViewBGColor);
}
}
dataRendererOnRenderWrapper.getRenderEventExecutor().fireOnRender(false);
}
@Override
protected void onRender(final MarkupStream markupStream)
{
super.onRender(markupStream);
jsChangeRecorder.setRendered();
}
public IStylePropertyChanges getStylePropertyChanges()
{
String parentViewBackground = getParentViewBgColor();
if (parentViewBackground != null)
{
if (!parentViewBackground.equals(getBgcolor()))
{
setBgcolor(parentViewBackground);
}
}
return jsChangeRecorder;
}
/*
* bgcolor as string---------------------------------------------------
*/
private String getBgcolor()
{
return PersistHelper.createColorString(background);
}
private void setBgcolor(String bgcolor)
{
background = PersistHelper.createColor(bgcolor);
jsChangeRecorder.setBgcolor(bgcolor);
}
private String getParentViewBgColor()
{
if (parentView != null)
{
String rowBGColorCalculation = parentView.getRowBGColorScript();
IRecordInternal rec = (IRecordInternal)getDefaultModelObject();
if (rec != null && rec.getRawData() != null)
{
IFoundSetInternal parentFoundSet = rec.getParentFoundSet();
int recIndex = parentFoundSet.getSelectedIndex();
boolean isSelected = recIndex == parentFoundSet.getRecordIndex(rec);
if (rowBGColorCalculation != null)
{
Object bg_color = null;
if (rec.getRawData().containsCalculation(rowBGColorCalculation))
{
// data renderer is always on the selected index.
bg_color = parentFoundSet.getCalculationValue(
rec,
rowBGColorCalculation,
Utils.arrayMerge(new Object[] { new Integer(recIndex), new Boolean(isSelected), null, null, Boolean.FALSE },
Utils.parseJSExpressions(parentView.getRowBGColorArgs())), null);
}
else
{
try
{
FormController currentForm = dataAdapterList.getFormController();
bg_color = currentForm.executeFunction(
rowBGColorCalculation,
Utils.arrayMerge(
new Object[] { new Integer(parentFoundSet.getSelectedIndex()), new Boolean(isSelected), null, null, currentForm.getName(), rec, Boolean.FALSE },
Utils.parseJSExpressions(parentView.getRowBGColorArgs())), true, null, true, null);
}
catch (Exception ex)
{
Debug.error(ex);
}
}
if (bg_color != null && !(bg_color.toString().trim().length() == 0) && !(bg_color instanceof Undefined))
{
return bg_color.toString();
}
}
if (parentView instanceof ISupportRowStyling)
{
ISupportRowStyling parentViewWithRowStyling = (ISupportRowStyling)parentView;
IStyleSheet ss = parentViewWithRowStyling.getRowStyleSheet();
IStyleRule style = isSelected ? parentViewWithRowStyling.getRowSelectedStyle() : null;
if (style != null && style.getAttributeCount() == 0) style = null;
if (style == null)
{
style = (recIndex % 2 == 0) ? parentViewWithRowStyling.getRowEvenStyle() : parentViewWithRowStyling.getRowOddStyle();
}
if (ss != null && style != null)
{
return PersistHelper.createColorString(ss.getBackground(style));
}
}
}
}
return null;
}
public String getFormPartName()
{
return this.formPartName;
}
public int onDrag(JSDNDEvent event)
{
Form form = dragNdropController.getForm();
int onDragID = form.getOnDragMethodID();
if (onDragID > 0)
{
Object dragReturn = dragNdropController.executeFunction(Integer.toString(onDragID), new Object[] { event }, false, null, false, "onDragMethodID"); //$NON-NLS-1$
if (dragReturn instanceof Number) return ((Number)dragReturn).intValue();
}
return DRAGNDROP.NONE;
}
public boolean onDragOver(JSDNDEvent event)
{
Form form = dragNdropController.getForm();
int onDragOverID = form.getOnDragOverMethodID();
if (onDragOverID > 0)
{
Object dragOverReturn = dragNdropController.executeFunction(Integer.toString(onDragOverID), new Object[] { event }, false, null, false,
"onDragOverMethodID"); //$NON-NLS-1$
if (dragOverReturn instanceof Boolean) return ((Boolean)dragOverReturn).booleanValue();
}
return form.getOnDropMethodID() > 0;
}
public boolean onDrop(JSDNDEvent event)
{
Form form = dragNdropController.getForm();
int onDropID = form.getOnDropMethodID();
if (onDropID > 0)
{
Object dropHappened = dragNdropController.executeFunction(Integer.toString(onDropID), new Object[] { event }, false, null, false, "onDropMethodID"); //$NON-NLS-1$
if (dropHappened instanceof Boolean) return ((Boolean)dropHappened).booleanValue();
}
return false;
}
public void onDragEnd(JSDNDEvent event)
{
Form form = dragNdropController.getForm();
int onDragEndID = form.getOnDragEndMethodID();
if (onDragEndID > 0)
{
dragNdropController.executeFunction(Integer.toString(onDragEndID), new Object[] { event }, false, null, false, "onDragEndMethodID"); //$NON-NLS-1$
}
}
public IComponent getDragSource(Point xy)
{
// don't need this, ignore
return null;
}
public String getDragFormName()
{
return getDataAdapterList().getFormController().getName();
}
public boolean isGridView()
{
return false;
}
public IRecordInternal getDragRecord(Point xy)
{
return getDataAdapterList().getState();
}
public int getYOffset()
{
return yOffset;
}
private int yOffset;
public void initDragNDrop(FormController formController, int clientDesignYOffset)
{
this.yOffset = clientDesignYOffset;
Form form = formController.getForm();
if (form.getOnDragMethodID() > 0 || form.getOnDragEndMethodID() > 0 || form.getOnDragOverMethodID() > 0 || form.getOnDropMethodID() > 0)
{
this.dragNdropController = formController;
if (dragNdropController != null) addDragNDropBehavior();
}
}
public FormController getDragNDropController()
{
return dragNdropController;
}
private void addDragNDropBehavior()
{
DraggableBehavior dragBehavior = new DraggableBehavior()
{
private IComponent hoverComponent;
private boolean isHoverAcceptDrop;
@Override
protected void onDragEnd(String id, int x, int y, int m, AjaxRequestTarget ajaxRequestTarget)
{
if (getCurrentDragOperation() != DRAGNDROP.NONE)
{
JSDNDEvent event = WebDataRenderer.this.createScriptEvent(EventType.onDragEnd, getDragComponent(), null, m);
event.setData(getDragData());
event.setDataMimeType(getDragDataMimeType());
event.setDragResult(getDropResult() ? getCurrentDragOperation() : DRAGNDROP.NONE);
WebDataRenderer.this.onDragEnd(event);
}
super.onDragEnd(id, x, y, m, ajaxRequestTarget);
}
@Override
protected boolean onDragStart(final String id, int x, int y, int m, AjaxRequestTarget ajaxRequestTarget)
{
IComponent comp = getBindedComponentChild(id);
JSDNDEvent event = WebDataRenderer.this.createScriptEvent(EventType.onDrag, comp, new Point(x, y), m);
setDropResult(false);
int dragOp = WebDataRenderer.this.onDrag(event);
if (dragOp == DRAGNDROP.NONE) return false;
setCurrentDragOperation(dragOp);
setDragComponent(comp);
setDragData(event.getData(), event.getDataMimeType());
hoverComponent = null;
isHoverAcceptDrop = false;
return true;
}
@Override
protected void onDrop(String id, final String targetid, int x, int y, int m, AjaxRequestTarget ajaxRequestTarget)
{
if (getCurrentDragOperation() != DRAGNDROP.NONE)
{
IComponent comp = getBindedComponentChild(targetid);
if (hoverComponent == comp && !isHoverAcceptDrop) return;
WebDataRenderer renderer = WebDataRenderer.this;
JSDNDEvent event = renderer.createScriptEvent(EventType.onDrop, comp, new Point(x, y), m);
event.setData(getDragData());
event.setDataMimeType(getDragDataMimeType());
setDropResult(renderer.onDrop(event));
}
}
// private ISupportDragNDrop testTarget(ISupportDragNDrop ddComp, JSEvent event)
// {
// if (event.js_getSource() instanceof SpecialTabPanel)
// {
// SpecialTabPanel tabPanel = (SpecialTabPanel)event.js_getSource();
// Component selectedComponent = tabPanel.getEnclosingComponent().getSelectedComponent();
// if (selectedComponent instanceof FormLookupPanel)
// {
// FormController formControler = ((FormLookupPanel)selectedComponent).getFormPanel();
// event.setSource(null);
// event.setFormName(formControler.getName());
// event.setElementName(null);
// return (ISupportDragNDrop)formControler.getViewComponent();
// }
// }
// return ddComp;
// }
@Override
protected void onDropHover(String id, final String targetid, int m, AjaxRequestTarget ajaxRequestTarget)
{
if (getCurrentDragOperation() != DRAGNDROP.NONE)
{
IComponent comp = getBindedComponentChild(targetid);
JSDNDEvent event = WebDataRenderer.this.createScriptEvent(EventType.onDragOver, comp, null, m);
event.setData(getDragData());
event.setDataMimeType(getDragDataMimeType());
isHoverAcceptDrop = WebDataRenderer.this.onDragOver(event);
hoverComponent = comp;
}
}
@Override
public IComponent getBindedComponentChild(final String childId)
{
IComponent comp = super.getBindedComponentChild(childId);
if (comp == null) comp = WebDataRenderer.this;
return comp;
}
};
dragBehavior.setUseProxy(true);
add(dragBehavior);
}
private JSDNDEvent createScriptEvent(EventType type, IComponent dragSource, Point xy, int modifiers)
{
JSDNDEvent jsEvent = new JSDNDEvent();
jsEvent.setType(type);
jsEvent.setFormName(getDragFormName());
IRecordInternal dragRecord = getDragRecord(xy);
if (dragRecord instanceof Record) jsEvent.setRecord((Record)dragRecord);
if (dragSource instanceof IDataRenderer)
{
IDataRenderer dr = (IDataRenderer)dragSource;
FormController fct = dr.getDataAdapterList().getFormController();
jsEvent.setSource(fct.getFormScope());
}
else
{
jsEvent.setSource(dragSource);
if (dragSource != null)
{
String dragSourceName = dragSource.getName();
if (dragSourceName == null) dragSourceName = dragSource.getId();
jsEvent.setElementName(dragSourceName);
}
}
if (xy != null) jsEvent.setLocation(xy);
jsEvent.setModifiers(modifiers);
return jsEvent;
}
/*
* @see com.servoy.j2db.ui.ISupportOnRenderWrapper#getOnRenderComponent()
*/
public ISupportOnRenderCallback getOnRenderComponent()
{
return dataRendererOnRenderWrapper;
}
/*
* @see com.servoy.j2db.ui.ISupportOnRenderWrapper#getOnRenderElementType()
*/
public String getOnRenderElementType()
{
return IRuntimeComponent.FORM;
}
/*
* @see com.servoy.j2db.ui.ISupportOnRenderWrapper#getOnRenderToString()
*/
public String getOnRenderToString()
{
return dataAdapterList != null ? dataAdapterList.getFormController().getForm().toString() : super.toString();
}
}