/*
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.Insets;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.swing.JEditorPane;
import javax.swing.border.Border;
import javax.swing.text.Document;
import org.apache.wicket.Page;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.internal.HtmlHeaderContainer;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.util.convert.ConversionException;
import com.servoy.base.util.ITagResolver;
import com.servoy.j2db.FormManager;
import com.servoy.j2db.IApplication;
import com.servoy.j2db.IMainContainer;
import com.servoy.j2db.IScriptExecuter;
import com.servoy.j2db.IServiceProvider;
import com.servoy.j2db.dataprocessing.IDisplayData;
import com.servoy.j2db.dataprocessing.IEditListener;
import com.servoy.j2db.server.headlessclient.MainPage;
import com.servoy.j2db.server.headlessclient.TabIndexAttributeModifier;
import com.servoy.j2db.ui.IEventExecutor;
import com.servoy.j2db.ui.IFieldComponent;
import com.servoy.j2db.ui.ILabel;
import com.servoy.j2db.ui.IProviderStylePropertyChanges;
import com.servoy.j2db.ui.IStylePropertyChanges;
import com.servoy.j2db.ui.ISupportInputSelection;
import com.servoy.j2db.ui.ISupportOnRender;
import com.servoy.j2db.ui.ISupportScroll;
import com.servoy.j2db.ui.ISupportSpecialClientProperty;
import com.servoy.j2db.ui.scripting.AbstractRuntimeField;
import com.servoy.j2db.ui.scripting.AbstractRuntimeTextEditor;
import com.servoy.j2db.util.ScopesUtils;
import com.servoy.j2db.util.Text;
import com.servoy.j2db.util.Utils;
/**
* html editor component, bound to YUI editor
* @author jblok
*/
@SuppressWarnings("nls")
public class WebDataHtmlArea extends FormComponent implements IFieldComponent, IDisplayData, IProviderStylePropertyChanges, ISupportInputSelection,
IOwnTabSequenceHandler, ISupportSpecialClientProperty, ISupportOnRender, ISupportScroll
{
public static final String htmlTextStartTags = "<html><body>";
public static final String htmlTextEndTags = "</body></html>";
private static final long serialVersionUID = 1L;
private boolean needEntireState;
private Insets margin;
private String inputId;
private final TabIndexAttributeModifier tabIndexAttributeModifier;
private String configuration;
private final AbstractRuntimeField<IFieldComponent> scriptable;
private final IApplication application;
private final WebEventExecutor eventExecutor;
private final TextArea textArea;
private final AbstractServoyDefaultAjaxBehavior scrollBehavior;
public WebDataHtmlArea(IApplication application, AbstractRuntimeTextEditor<IFieldComponent, JEditorPane> scriptable, String id)
{
super(id);
this.application = application;
this.scriptable = scriptable;
setVersioned(false);
setOutputMarkupPlaceholderTag(true);
add(StyleAttributeModifierModel.INSTANCE);
add(TooltipAttributeModifier.INSTANCE);
boolean useAJAX = Utils.getAsBoolean(application.getRuntimeProperties().get("useAJAX")); //$NON-NLS-1$
eventExecutor = new WebEventExecutor(this, useAJAX);
textArea = new TextArea("editor_" + id)
{
@Override
protected Object convertValue(String[] value) throws ConversionException
{
String convert = (String)super.convertValue(value);
if (convert != null && !convert.startsWith(htmlTextStartTags))
{
// html editor takes away <html> tags making text incompatible with SC
convert = htmlTextStartTags + convert + htmlTextEndTags;
}
return convert;
}
@Override
public String getInputName()
{
if (inputId == null)
{
Page page = findPage();
if (page instanceof MainPage)
{
inputId = ((MainPage)page).nextInputNameId();
}
else
{
return super.getInputName();
}
}
return inputId;
}
@Override
protected String getModelValue()
{
return WebDataHtmlArea.this.getDefaultModelObjectAsString();
}
@Override
public void updateModel()
{
super.updateModel();
WebDataHtmlArea.this.setDefaultModelObject(super.getConvertedInput());
}
};
textArea.add(new ServoyAjaxFormComponentUpdatingBehavior("onsubmit")
{
private static final long serialVersionUID = 1L;
@Override
protected void onUpdate(AjaxRequestTarget target)
{
WebEventExecutor.generateResponse(target, findPage());
}
});
add(textArea);
textArea.setOutputMarkupId(true);
tabIndexAttributeModifier = new TabIndexAttributeModifier(-1);
textArea.add(tabIndexAttributeModifier);
textArea.add(new StyleAppendingModifier(new Model<String>("visibility:hidden")));
add(scrollBehavior = new AbstractServoyDefaultAjaxBehavior()
{
@Override
protected void respond(AjaxRequestTarget target)
{
WebDataHtmlArea.this.setScroll(Utils.getAsInteger(RequestCycle.get().getRequest().getParameter("locationX")),
Utils.getAsInteger(RequestCycle.get().getRequest().getParameter("locationY")));
}
});
}
/**
* @see org.apache.wicket.Component#renderHead(org.apache.wicket.markup.html.internal.HtmlHeaderContainer)
*/
@Override
public void renderHead(final HtmlHeaderContainer container)
{
super.renderHead(container);
IHeaderResponse response = container.getHeaderResponse();
String defaultConfiguration = (String)application.getClientProperty(IApplication.HTML_EDITOR_CONFIGURATION);
String script = "Servoy.HTMLEdit.attach('" + getMarkupId() + "','" + textArea.getMarkupId() + "'," + (isEnabled() && !isReadOnly()) + "," +
(configuration != null ? configuration : null) + "," + (defaultConfiguration != null ? defaultConfiguration : null) + "," + scroll.x + "," +
scroll.y + ",'" + scrollBehavior.getCallbackUrl() + "');";
response.renderOnLoadJavascript(script);
}
@Override
public void handleOwnTabIndex(int newTabIndex)
{
tabIndexAttributeModifier.setTabIndex(newTabIndex);
}
public void setClientProperty(Object key, Object value)
{
if (IApplication.HTML_EDITOR_CONFIGURATION.equals(key))
{
configuration = (String)value;
}
}
private boolean accessible = true;
public void setAccessible(boolean b)
{
if (!b) setComponentEnabled(b);
accessible = b;
}
private boolean viewable = true;
public void setViewable(boolean b)
{
if (!b) setComponentVisible(b);
this.viewable = b;
}
public boolean isViewable()
{
return viewable;
}
/*
* location---------------------------------------------------
*/
private Point location = new Point(0, 0);
public int getAbsoluteFormLocationY()
{
WebDataRenderer parent = findParent(WebDataRenderer.class);
if (parent != null)
{
return parent.getYOffset() + getLocation().y;
}
return getLocation().y;
}
public void setLocation(Point location)
{
this.location = location;
}
public Point getLocation()
{
return location;
}
/*
* size---------------------------------------------------
*/
private Dimension size = new Dimension(0, 0);
public Dimension getSize()
{
return size;
}
public void setSize(Dimension size)
{
this.size = size;
}
@Override
public String toString()
{
return scriptable.toString("value:" + getDefaultModelObjectAsString()); //$NON-NLS-1$
}
public final AbstractRuntimeField<IFieldComponent> getScriptObject()
{
return scriptable;
}
/**
* @see org.apache.wicket.Component#getLocale()
*/
@Override
public Locale getLocale()
{
return application.getLocale();
}
public IStylePropertyChanges getStylePropertyChanges()
{
return scriptable.getChangesRecorder();
}
@Override
protected void onRender(final MarkupStream markupStream)
{
super.onRender(markupStream);
getStylePropertyChanges().setRendered();
IModel< ? > model = getInnermostModel();
if (model instanceof RecordItemModel)
{
((RecordItemModel)model).updateRenderedValue(this);
}
}
private boolean editable;
public void setEditable(boolean b)
{
editState = b;
editable = b;
}
public boolean isEditable()
{
return editable;
}
public boolean isReadOnly()
{
return !editable;
}
private boolean editState;
public void setReadOnly(boolean b)
{
if (b && !editable) return;
if (b)
{
setEditable(false);
editState = true;
}
else
{
setEditable(editState);
}
}
/*
* dataprovider---------------------------------------------------
*/
public void setDataProviderID(String dataProviderID)
{
this.dataProviderID = dataProviderID;
}
public String getDataProviderID()
{
return dataProviderID;
}
private String dataProviderID;
public void setName(String n)
{
name = n;
}
private String name;
public String getName()
{
return name;
}
/*
* border---------------------------------------------------
*/
private Border border;
public void setBorder(Border border)
{
this.border = border;
}
public Border getBorder()
{
return border;
}
/*
* opaque---------------------------------------------------
*/
public void setOpaque(boolean opaque)
{
this.opaque = opaque;
}
private boolean opaque;
public boolean isOpaque()
{
return opaque;
}
/*
* titleText---------------------------------------------------
*/
private String titleText = null;
public void setTitleText(String title)
{
this.titleText = title;
}
public String getTitleText()
{
return Text.processTags(titleText, resolver);
}
private String tooltip;
public void setToolTipText(String tooltip)
{
this.tooltip = Utils.stringIsEmpty(tooltip) ? null : tooltip;
}
protected ITagResolver resolver;
public void setTagResolver(ITagResolver resolver)
{
this.resolver = resolver;
}
/**
* @see com.servoy.j2db.ui.IComponent#getToolTipText()
*/
public String getToolTipText()
{
if (tooltip != null && getInnermostModel() instanceof RecordItemModel)
{
return Text.processTags(tooltip, resolver);
}
return tooltip;
}
/*
* font---------------------------------------------------
*/
public void setFont(Font font)
{
this.font = font;
}
private Font font;
public Font getFont()
{
return font;
}
private Color background;
public void setBackground(Color cbg)
{
this.background = cbg;
}
public Color getBackground()
{
return background;
}
private Color foreground;
private List<ILabel> labels;
public List<ILabel> getLabelsFor()
{
return labels;
}
public void setForeground(Color cfg)
{
this.foreground = cfg;
}
public Color getForeground()
{
return foreground;
}
/*
* visible---------------------------------------------------
*/
public void setComponentVisible(boolean visible)
{
if (viewable || !visible)
{
setVisible(visible);
if (labels != null)
{
for (int i = 0; i < labels.size(); i++)
{
ILabel label = labels.get(i);
label.setComponentVisible(visible);
}
}
}
}
public void addLabelFor(ILabel label)
{
if (labels == null) labels = new ArrayList<ILabel>(3);
labels.add(label);
}
public List<ILabel> getLabelForElementNames()
{
return labels;
}
public void setComponentEnabled(final boolean b)
{
if (accessible || !b)
{
super.setEnabled(b);
((ChangesRecorder)getStylePropertyChanges()).setChanged();
if (labels != null)
{
for (int i = 0; i < labels.size(); i++)
{
ILabel label = labels.get(i);
label.setComponentEnabled(b);
}
}
}
}
@Override
protected void onBeforeRender()
{
super.onBeforeRender();
fireOnRender(false);
}
public void fireOnRender(boolean force)
{
if (scriptable != null)
{
boolean isFocused = false;
IMainContainer currentContainer = ((FormManager)application.getFormManager()).getCurrentContainer();
if (currentContainer instanceof MainPage)
{
isFocused = this.equals(((MainPage)currentContainer).getFocusedComponent());
}
if (force) scriptable.getRenderEventExecutor().setRenderStateChanged();
scriptable.getRenderEventExecutor().fireOnRender(isFocused);
}
}
/**
* @see com.servoy.j2db.ui.IFieldComponent#setSelectOnEnter(boolean)
*/
public void setSelectOnEnter(boolean selectOnEnter)
{
// this.selectOnEnter = selectOnEnter;
}
/**
* @see com.servoy.j2db.ui.IFieldComponent#addScriptExecuter(com.servoy.j2db.IScriptExecuter)
*/
public void addScriptExecuter(IScriptExecuter scriptExecuter)
{
eventExecutor.setScriptExecuter(scriptExecuter);
}
public IEventExecutor getEventExecutor()
{
return eventExecutor;
}
/**
* @see com.servoy.j2db.ui.IFieldComponent#setEnterCmds(java.lang.String, Object[][])
*/
public void setEnterCmds(String[] enterCmds, Object[][] args)
{
eventExecutor.setEnterCmds(enterCmds, args);
}
/**
* @see com.servoy.j2db.ui.IFieldComponent#setLeaveCmds(String[], Object[][])
*/
public void setLeaveCmds(String[] leaveCmds, Object[][] args)
{
eventExecutor.setLeaveCmds(leaveCmds, args);
}
/**
* @see com.servoy.j2db.ui.IFieldComponent#setActionCmd(java.lang.String, Object[])
*/
public void setActionCmd(String actionCmd, Object[] args)
{
eventExecutor.setActionCmd(actionCmd, args);
}
/**
* @see com.servoy.j2db.ui.IFieldComponent#setChangeCmd(java.lang.String, Object[])
*/
public void setChangeCmd(String changeCmd, Object[] args)
{
eventExecutor.setChangeCmd(changeCmd, args);
}
/**
* @see com.servoy.j2db.ui.IFieldComponent#setNeedEntireState(boolean)
*/
public void setNeedEntireState(boolean needEntireState)
{
this.needEntireState = needEntireState;
}
/**
* @see com.servoy.j2db.dataprocessing.IDisplayData#needEntireState()
*/
public boolean needEntireState()
{
return needEntireState;
}
public void setMaxLength(int maxLength)
{
}
public void setMargin(Insets margin)
{
this.margin = margin;
}
public Insets getMargin()
{
return margin;
}
/**
* @see com.servoy.j2db.ui.IFieldComponent#setHorizontalAlignment(int)
*/
public void setHorizontalAlignment(int horizontalAlignment)
{
// this.horizontalAlignment = horizontalAlignment;
}
public void setCursor(Cursor cursor)
{
// this.cursor = cursor;
}
public Object getValueObject()
{
return getDefaultModelObjectAsString();
}
public void setValueObject(Object value)
{
((ChangesRecorder)getStylePropertyChanges()).testChanged(this, value);
}
public boolean needEditListener()
{
return false;
}
public void addEditListener(IEditListener l)
{
}
/**
* @see com.servoy.j2db.dataprocessing.IDisplayData#getDocument()
*/
public Document getDocument()
{
return null;
}
public boolean isValueValid()
{
return isValueValid;
}
private boolean isValueValid = true;
private Object previousValidValue;
public void setValueValid(boolean valid, Object oldVal)
{
application.getRuntimeProperties().put(IServiceProvider.RT_LASTFIELDVALIDATIONFAILED_FLAG, Boolean.valueOf(!valid));
isValueValid = valid;
if (!isValueValid)
{
previousValidValue = oldVal;
requestFocusToComponent();
}
else
{
previousValidValue = null;
}
}
public void notifyLastNewValueWasChange(final Object oldVal, final Object newVal)
{
if (eventExecutor.hasChangeCmd())
{
application.invokeLater(new Runnable()
{
public void run()
{
WebEventExecutor.setSelectedIndex(WebDataHtmlArea.this, null, IEventExecutor.MODIFIERS_UNSPECIFIED);
eventExecutor.fireChangeCommand(previousValidValue == null ? oldVal : previousValidValue, newVal, false, WebDataHtmlArea.this);
}
});
}
else
{
setValueValid(true, null);
}
}
private boolean wasEditable;
public void setValidationEnabled(boolean b)
{
if (eventExecutor.getValidationEnabled() == b) return;
if (ScopesUtils.isVariableScope(dataProviderID)) return;
eventExecutor.setValidationEnabled(b);
boolean prevEditState = editState;
if (b)
{
setEditable(wasEditable);
}
else
{
wasEditable = editable;
if (!Boolean.TRUE.equals(application.getClientProperty(IApplication.LEAVE_FIELDS_READONLY_IN_FIND_MODE)))
{
setEditable(true);
}
}
editState = prevEditState;
}
/**
* @see com.servoy.j2db.dataprocessing.IDisplay#stopUIEditing(boolean)
*/
public boolean stopUIEditing(boolean looseFocus)
{
if (!isValueValid)
{
requestFocusToComponent();
return false;
}
return true;
}
/*
* jsmethods---------------------------------------------------
*/
public void requestFocusToComponent()
{
IMainContainer currentContainer = ((FormManager)application.getFormManager()).getCurrentContainer();
if (currentContainer instanceof MainPage)
{
((MainPage)currentContainer).componentToFocus(this);
}
}
public void setRightClickCommand(String rightClickCmd, Object[] args)
{
eventExecutor.setRightClickCmd(rightClickCmd, args);
}
@Override
public String getInputName()
{
if (inputId == null)
{
Page page = findPage();
if (page instanceof MainPage)
{
inputId = ((MainPage)page).nextInputNameId();
}
else
{
return super.getInputName();
}
}
return inputId;
}
public void selectAll()
{
}
public void replaceSelectedText(String s)
{
Page page = findPage();
if (page instanceof MainPage)
{
((MainPage)page).getPageContributor().addDynamicJavaScript("tinymce.activeEditor.selection.setContent('" + s + "')");
}
}
public String getSelectedText()
{
return null;
}
public String getEditorID()
{
return textArea.getMarkupId();
}
private final Point scroll = new Point(0, 0);
/*
* (non-Javadoc)
*
* @see com.servoy.j2db.ui.ISupportScroll#setScroll(int, int)
*/
@Override
public void setScroll(int x, int y)
{
scroll.x = x;
scroll.y = y;
}
/*
* (non-Javadoc)
*
* @see com.servoy.j2db.ui.ISupportScroll#getScroll()
*/
@Override
public Point getScroll()
{
return scroll;
}
/*
* (non-Javadoc)
*
* @see com.servoy.j2db.ui.ISupportScroll#getScrollComponentMarkupId()
*/
@Override
public String getScrollComponentMarkupId()
{
return null;
}
}