/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2012 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.util.Arrays;
import java.util.List;
import java.util.Properties;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import org.apache.wicket.Component;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.ResourceReference;
import org.apache.wicket.Response;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.util.convert.IConverter;
import org.apache.wicket.util.string.Strings;
import org.wicketstuff.minis.spinner.Spinner;
import com.servoy.j2db.IApplication;
import com.servoy.j2db.dataprocessing.IDisplayRelatedData;
import com.servoy.j2db.dataprocessing.IRecordInternal;
import com.servoy.j2db.dataprocessing.IValueList;
import com.servoy.j2db.dataprocessing.SortColumn;
import com.servoy.j2db.ui.IComponent;
import com.servoy.j2db.ui.ISupportValueList;
import com.servoy.j2db.ui.scripting.RuntimeDataField;
import com.servoy.j2db.ui.scripting.RuntimeSpinner;
/**
* Spinner-like component for web. Similar in implementation to calendar (it adds markup for spin buttons)
* and to comboBox - because it can change values based on a valuelist.
* @author acostescu
*/
public class WebDataSpinner extends WebDataCompositeTextField implements ISupportValueList, IDisplayRelatedData
{
private static final long serialVersionUID = 1L;
private IValueList valueList;
private ModifiedSpinner spinnerBehavior;
private ListDataListener valueListChangeListener;
private String relationName = null;
private String[] currentValues;
private boolean ignoreChanges = false;
public WebDataSpinner(IApplication application, RuntimeSpinner scriptable, String id, IValueList valueList)
{
super(application, scriptable, id);
setValueList(valueList);
}
@Override
protected WebDataField createTextField(RuntimeDataField fieldScriptable)
{
return new SpinField(application, fieldScriptable, this);
}
private Spinner createNewSpinner()
{
currentValues = getNewValues();
return spinnerBehavior = new ModifiedSpinner(currentValues);
}
/*
* (non-Javadoc)
*
* @see org.apache.wicket.Component#getConverter(java.lang.Class)
*/
@Override
public IConverter getConverter(Class< ? > type)
{
return field.getConverter(type);
}
/*
* (non-Javadoc)
*
* @see com.servoy.j2db.server.headlessclient.dataui.WebDataCompositeTextField#setValidationEnabled(boolean)
*/
@Override
public void setValidationEnabled(boolean b)
{
super.setValidationEnabled(b);
if (valueList.getFallbackValueList() != null)
{
updateSpinnerBehavior();
}
}
private String[] getNewValues()
{
String[] values = null;
if (valueList != null)
{
IValueList vl = valueList;
if (!field.getEventExecutor().getValidationEnabled() && vl.getFallbackValueList() != null)
{
vl = vl.getFallbackValueList();
}
int size = vl.getSize();
if (size > 1 || (size == 1 && vl.getElementAt(0) != null && String.valueOf(vl.getElementAt(0)).trim().length() > 0))
{
values = new String[size];
Object v;
IConverter converter = field.getConverter(field.getType());
for (int i = 0; i < size; i++)
{
v = vl.getRealElementAt(i);
String val = (v != null) ? converter.convertToString(v, getLocale()) : "";
if (val == null) val = "";
values[size - i - 1] = val;
}
}
}
if (values == null)
{
values = new String[] { "", "" }; //$NON-NLS-1$//$NON-NLS-2$
}
return values;
}
public IValueList getValueList()
{
return valueList;
}
public void setValueList(IValueList vl)
{
if (valueList != null) vl.removeListDataListener(valueListChangeListener);
valueList = vl;
if (valueList != null && valueListChangeListener == null)
{
valueListChangeListener = new ListDataListener()
{
public void intervalRemoved(ListDataEvent e)
{
updateSpinnerBehavior();
}
public void intervalAdded(ListDataEvent e)
{
updateSpinnerBehavior();
}
public void contentsChanged(ListDataEvent e)
{
updateSpinnerBehavior();
}
};
}
field.setValueList(vl);
if (valueList != null) valueList.addListDataListener(valueListChangeListener);
updateSpinnerBehavior();
}
private void updateSpinnerBehavior()
{
if (ignoreChanges) return;
if (spinnerBehavior != null) field.remove(spinnerBehavior);
field.add(createNewSpinner());
getStylePropertyChanges().setChanged();
}
public ListDataListener getListener()
{
return valueListChangeListener;
}
public void setRecord(IRecordInternal state, boolean stopEditing)
{
boolean listContentChanged = false;
try
{
ignoreChanges = true;
valueList.fill(state);
listContentChanged = !Arrays.equals(currentValues, getNewValues());
}
finally
{
ignoreChanges = false;
}
if (listContentChanged)
{
updateSpinnerBehavior();
}
}
public String getSelectedRelationName()
{
if (relationName == null && valueList != null)
{
relationName = valueList.getRelationName();
}
return relationName;
}
public String[] getAllRelationNames()
{
String selectedRelationName = getSelectedRelationName();
if (selectedRelationName == null)
{
return new String[0];
}
else
{
return new String[] { selectedRelationName };
}
}
public void notifyVisible(boolean b, List<Runnable> invokeLaterRunnables)
{
//ignore
}
public List<SortColumn> getDefaultSort()
{
return null;
}
public void destroy()
{
valueList.deregister();
}
private boolean editState;
@Override
public void setReadOnly(boolean b)
{
if (b && !editable) return;
if (b)
{
setEditable(false);
editState = true;
}
else
{
setEditable(editState);
}
applyReadonlyState();
}
@Override
public void setEditable(boolean b)
{
super.setEditable(b);
editState = b;
applyReadonlyState();
}
@Override
public boolean isReadOnly()
{
return !isEditable();
}
private void applyReadonlyState()
{
showExtraComponents = !isReadOnly();
}
protected class SpinField extends AugmentedTextField
{
public SpinField(IApplication application, RuntimeDataField scriptable, IComponent enclosingComponent)
{
super(application, scriptable, enclosingComponent);
super.setEditable(false);
}
@Override
public void setEditable(boolean b)
{
// do not allow this to change - currently spinner field is never editable
}
}
protected class ModifiedSpinner extends Spinner
{
String[] values;
public ModifiedSpinner(String[] values)
{
this.values = values;
}
@Override
protected void configure(Properties p)
{
/**
* This method can be overridden to customize the Spinner.<br/>
* The following options are used by the Spinner:
* <ul>
* <li>interval The amount to increment (default=1)
* <li>round The number of decimal points to which to round (default=0)
* <li>min The lowest allowed value, false for no min (default=false)
* <li>max The highest allowed value, false for no max (default=false)
* <li>prefix String to prepend when updating (default='')
* <li>suffix String to append when updating (default='')
* <li>data An array giving a list of items through which to iterate
* <li>onIncrement Function to call after incrementing
* <li>onDecrement Function to call after decrementing
* <li>afterUpdate Function to call after update of the value
* <li>onStop Function to call on click or mouseup (default=false)
* </ul>
*
* @param p
*/
super.configure(p);
p.put("data", values); //$NON-NLS-1$
p.put("emptyInputCheck", Boolean.valueOf(false)); //$NON-NLS-1$
p.put("afterUpdate", new Object() { //$NON-NLS-1$
@Override
public String toString()
{
return "function(spinnerBehavior) { spinnerBehavior.inputElement.onchange(); }"; //$NON-NLS-1$
}
});
}
@Override
public void beforeRender(Component component)
{
// do not add default spans for + -; img buttons substitute those
}
/**
* @see org.apache.wicket.behavior.AbstractBehavior#onRendered(org.apache.wicket.Component)
*/
@Override
public void onRendered(Component component)
{
// do not add default spans for + -; img buttons substitute those
if (getSpinUpComponent() == null)
{
Response response = component.getResponse();
response.write("</td><td style = \"margin: 0px; padding: 0px; width: 5px;\"> </td><td style = \"margin: 0px; padding: 0px; width: 16px;\">"); //$NON-NLS-1$
response.write("<table style=\"margin: 0px; padding: 0px; border-collapse: collapse; table-layout: fixed;\"><tbody><tr style=\"margin: 0px; padding: 0px;\">"); //$NON-NLS-1$
response.write("<td style=\"margin: 0px; padding: 0px;\">"); //$NON-NLS-1$
response.write("\n<img style=\"cursor: pointer; border: none;\" id=\""); //$NON-NLS-1$
response.write(field.getMarkupId() + "-SpinnerUp"); //$NON-NLS-1$
response.write("\" src=\""); //$NON-NLS-1$
response.write(Strings.escapeMarkup(getUpIconUrl().toString()));
response.write("\"/></td></tr><tr style=\"margin: 0px; padding: 0px;\"><td style=\"margin: 0px; padding: 0px;\">"); //$NON-NLS-1$
response.write("\n<img style=\"cursor: pointer; border: none;\" id=\""); //$NON-NLS-1$
response.write(field.getMarkupId() + "-SpinnerDown"); //$NON-NLS-1$
response.write("\" src=\""); //$NON-NLS-1$
response.write(Strings.escapeMarkup(getDownIconUrl().toString()));
response.write("\"/></td></tr></tbody></table>"); //$NON-NLS-1$
}
}
protected CharSequence getUpIconUrl()
{
return RequestCycle.get().urlFor(new ResourceReference(WebDataSpinner.class, "images/spinnerUp.gif")); //$NON-NLS-1$
}
protected CharSequence getDownIconUrl()
{
return RequestCycle.get().urlFor(new ResourceReference(WebDataSpinner.class, "images/spinnerDown.gif")); //$NON-NLS-1$
}
@Override
public boolean isEnabled(Component component)
{
return shouldShowExtraComponents();
}
@Override
public void renderHead(IHeaderResponse response)
{
// spinner component overwrites onblur, add it as listener
response.renderOnDomReadyJavascript("var spinner = wicketGet('" + WebDataSpinner.this.field.getMarkupId() +
"');Wicket.Event.add(spinner,'blur',spinner.onblur);");
super.renderHead(response);
}
}
}