/*
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.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.IRequestTarget;
import org.apache.wicket.IResourceListener;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.Page;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.Session;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.behavior.SimpleAttributeModifier;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.list.Loop;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.protocol.http.ClientProperties;
import org.apache.wicket.protocol.http.request.WebClientInfo;
import org.apache.wicket.version.undo.Change;
import com.servoy.base.util.ITagResolver;
import com.servoy.j2db.FlattenedSolution;
import com.servoy.j2db.FormController;
import com.servoy.j2db.IApplication;
import com.servoy.j2db.IForm;
import com.servoy.j2db.IScriptExecuter;
import com.servoy.j2db.component.ComponentFactory;
import com.servoy.j2db.dataprocessing.IDisplayRelatedData;
import com.servoy.j2db.dataprocessing.IFoundSetInternal;
import com.servoy.j2db.dataprocessing.IRecordInternal;
import com.servoy.j2db.dataprocessing.ISaveConstants;
import com.servoy.j2db.dataprocessing.ISwingFoundSet;
import com.servoy.j2db.dataprocessing.RelatedFoundSet;
import com.servoy.j2db.dataprocessing.SortColumn;
import com.servoy.j2db.dataprocessing.TagResolver;
import com.servoy.j2db.persistence.IAnchorConstants;
import com.servoy.j2db.persistence.Media;
import com.servoy.j2db.persistence.StaticContentSpecLoader;
import com.servoy.j2db.persistence.TabPanel;
import com.servoy.j2db.server.headlessclient.MainPage;
import com.servoy.j2db.server.headlessclient.TabIndexHelper;
import com.servoy.j2db.server.headlessclient.WebClientSession;
import com.servoy.j2db.server.headlessclient.WebForm;
import com.servoy.j2db.ui.IComponent;
import com.servoy.j2db.ui.IFormLookupPanel;
import com.servoy.j2db.ui.IFormUI;
import com.servoy.j2db.ui.IProviderStylePropertyChanges;
import com.servoy.j2db.ui.IStylePropertyChanges;
import com.servoy.j2db.ui.ISupportSecuritySettings;
import com.servoy.j2db.ui.ISupportSimulateBounds;
import com.servoy.j2db.ui.ISupportSimulateBoundsProvider;
import com.servoy.j2db.ui.ISupportWebBounds;
import com.servoy.j2db.ui.ITabPanel;
import com.servoy.j2db.ui.runtime.IRuntimeComponent;
import com.servoy.j2db.ui.scripting.RuntimeTabPanel;
import com.servoy.j2db.util.Debug;
import com.servoy.j2db.util.HtmlUtils;
import com.servoy.j2db.util.PersistHelper;
import com.servoy.j2db.util.Utils;
/**
* Represents a tabpanel in the webbrowser.
*
* @author jcompagner
*/
public class WebTabPanel extends WebMarkupContainer implements ITabPanel, IDisplayRelatedData, IProviderStylePropertyChanges, ISupportSecuritySettings,
ISupportWebBounds, ISupportWebTabSeq, ListSelectionListener, IWebFormContainer, ISupportSimulateBoundsProvider
{
private static final long serialVersionUID = 1L;
private final IApplication application;
private WebTabFormLookup currentForm;
protected IRecordInternal parentData;
private final List<String> allRelationNames = new ArrayList<String>(5);
protected final List<WebTabHolder> allTabs = new ArrayList<WebTabHolder>(5);
private final List<ISwingFoundSet> related = new ArrayList<ISwingFoundSet>();
private IScriptExecuter scriptExecutor;
private String onTabChangeMethodCmd;
private Object[] onTabChangeArgs;
protected final int orient;
private int tabSequenceIndex = ISupportWebTabSeq.DEFAULT;
private Dimension tabSize;
private final RuntimeTabPanel scriptable;
public WebTabPanel(IApplication application, final RuntimeTabPanel scriptable, String name, int orient, boolean oneTab)
{
super(name);
this.application = application;
this.orient = orient;
final boolean useAJAX = Utils.getAsBoolean(application.getRuntimeProperties().get("useAJAX")); //$NON-NLS-1$
setOutputMarkupPlaceholderTag(true);
if (orient != TabPanel.SPLIT_HORIZONTAL && orient != TabPanel.SPLIT_VERTICAL) add(new Label("webform", new Model<String>("")));//temporary add, in case the tab panel does not contain any tabs //$NON-NLS-1$ //$NON-NLS-2$
// TODO check ignore orient and oneTab??
IModel<Integer> tabsModel = new AbstractReadOnlyModel<Integer>()
{
private static final long serialVersionUID = 1L;
@Override
public Integer getObject()
{
return Integer.valueOf(allTabs.size());
}
};
if (orient != TabPanel.HIDE && orient != TabPanel.SPLIT_HORIZONTAL && orient != TabPanel.SPLIT_VERTICAL &&
!(orient == TabPanel.DEFAULT_ORIENTATION && oneTab))
{
add(new Loop("tablinks", tabsModel) //$NON-NLS-1$
{
private static final long serialVersionUID = 1L;
private String focusedItem;
@Override
protected void populateItem(final LoopItem item)
{
final WebTabHolder holder = allTabs.get(item.getIteration());
MarkupContainer link = null;
link = new ServoySubmitLink("tablink", useAJAX) //$NON-NLS-1$
{
private static final long serialVersionUID = 1L;
/**
* @see wicket.ajax.markup.html.AjaxFallbackLink#onClick(wicket.ajax.AjaxRequestTarget)
*/
@Override
public void onClick(AjaxRequestTarget target)
{
Page page = findPage();
if (page != null)
{
setActiveTabPanel(holder.getPanel());
if (target != null)
{
relinkAtTabPanel(WebTabPanel.this);
focusedItem = item.getId();
WebEventExecutor.generateResponse(target, page);
}
}
}
private void relinkAtForm(WebForm form)
{
form.visitChildren(WebTabPanel.class, new IVisitor<WebTabPanel>()
{
public Object component(WebTabPanel wtp)
{
relinkAtTabPanel(wtp);
return IVisitor.CONTINUE_TRAVERSAL;
}
});
}
private void relinkAtTabPanel(WebTabPanel wtp)
{
wtp.relinkFormIfNeeded();
wtp.visitChildren(WebForm.class, new IVisitor<WebForm>()
{
public Object component(WebForm form)
{
relinkAtForm(form);
return IVisitor.CONTINUE_TRAVERSAL;
}
});
}
@Override
protected void disableLink(final ComponentTag tag)
{
// if the tag is an anchor proper
if (tag.getName().equalsIgnoreCase("a") || tag.getName().equalsIgnoreCase("link") || tag.getName().equalsIgnoreCase("area")) //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
{
// Remove any href from the old link
tag.remove("href"); //$NON-NLS-1$
tag.remove("onclick"); //$NON-NLS-1$
}
}
};
if (item.getId().equals(focusedItem))
{
IRequestTarget currentRequestTarget = RequestCycle.get().getRequestTarget();
if (currentRequestTarget instanceof AjaxRequestTarget)
{
((AjaxRequestTarget)currentRequestTarget).focusComponent(link);
}
focusedItem = null;
}
if (holder.getTooltip() != null)
{
link.setMetaData(TooltipAttributeModifier.TOOLTIP_METADATA, holder.getTooltip());
}
TabIndexHelper.setUpTabIndexAttributeModifier(link, tabSequenceIndex);
link.add(TooltipAttributeModifier.INSTANCE);
if (item.getIteration() == 0) link.add(new AttributeModifier("firsttab", true, new Model<Boolean>(Boolean.TRUE))); //$NON-NLS-1$
link.setEnabled(holder.isEnabled() && WebTabPanel.this.isEnabled());
String text = holder.getText();
if (holder.getDisplayedMnemonic() > 0)
{
final String mnemonic = Character.toString((char)holder.getDisplayedMnemonic());
link.add(new SimpleAttributeModifier("accesskey", mnemonic)); //$NON-NLS-1$
if (text != null && text.contains(mnemonic) && !HtmlUtils.hasUsefulHtmlContent(text))
{
StringBuffer sbBodyText = new StringBuffer(text);
int mnemonicIdx = sbBodyText.indexOf(mnemonic);
if (mnemonicIdx != -1)
{
sbBodyText.insert(mnemonicIdx + 1, "</u>"); //$NON-NLS-1$
sbBodyText.insert(mnemonicIdx, "<u>"); //$NON-NLS-1$
text = sbBodyText.toString();
}
}
}
ServoyTabIcon tabIcon = new ServoyTabIcon("icon", holder, scriptable); //$NON-NLS-1$
link.add(tabIcon);
Label label = new Label("linktext", new Model<String>(text)); //$NON-NLS-1$
label.setEscapeModelStrings(false);
link.add(label);
item.add(link);
IModel<String> selectedOrDisabledClass = new AbstractReadOnlyModel<String>()
{
private static final long serialVersionUID = 1L;
@Override
public String getObject()
{
if (!holder.isEnabled() || !WebTabPanel.this.isEnabled())
{
if (currentForm == holder.getPanel())
{
return "disabled_selected_tab"; //$NON-NLS-1$
}
return "disabled_tab"; //$NON-NLS-1$
}
else
{
if (currentForm == holder.getPanel())
{
return "selected_tab"; //$NON-NLS-1$
}
return "deselected_tab"; //$NON-NLS-1$
}
}
};
item.add(new AttributeModifier("class", true, selectedOrDisabledClass)); //$NON-NLS-1$
label.add(new StyleAppendingModifier(new Model<String>()
{
private static final long serialVersionUID = 1L;
@Override
public String getObject()
{
String style = "white-space: nowrap;"; //$NON-NLS-1$
if (foreground != null)
{
style += " color:" + PersistHelper.createColorString(foreground); //$NON-NLS-1$
}
if (holder.getIcon() != null)
{
style += "; padding-left: 3px"; //$NON-NLS-1$
}
return style;
}
}));
}
});
// All tab panels get their tabs rearranged after they make it to the browser.
// On Chrome & Safari the tab rearrangement produces an ugly flicker effect, because
// initially the tabs are not visible and then they are made visible. By
// sending the tab as invisible and turning it to visible only after the tabs
// are arranged, this jumping/flickering effect is gone. However a small delay can now be
// noticed in Chrome & Safari, which should also be eliminated somehow.
// The tab panel is set to visible in function "rearrageTabsInTabPanel" from "servoy.js".
add(new StyleAppendingModifier(new Model<String>()
{
private static final long serialVersionUID = 1L;
@Override
public String getObject()
{
return "visibility: hidden;overflow:hidden"; //$NON-NLS-1$
}
}));
add(new AbstractServoyDefaultAjaxBehavior()
{
@Override
protected void respond(AjaxRequestTarget target)
{
}
@Override
public void renderHead(IHeaderResponse response)
{
super.renderHead(response);
boolean dontRearrangeHere = false;
if (!(getRequestCycle().getRequestTarget() instanceof AjaxRequestTarget) &&
Utils.getAsBoolean(((MainPage)getPage()).getController().getApplication().getRuntimeProperties().get("enableAnchors"))) //$NON-NLS-1$
{
Component parentForm = getParent();
while ((parentForm != null) && !(parentForm instanceof WebForm))
parentForm = parentForm.getParent();
if (parentForm != null)
{
int anch = ((WebForm)parentForm).getAnchors(WebTabPanel.this.getMarkupId());
if (anch != 0 && anch != IAnchorConstants.DEFAULT) dontRearrangeHere = true;
}
}
if (!dontRearrangeHere)
{
String jsCall = "rearrageTabsInTabPanel('" + WebTabPanel.this.getMarkupId() + "');"; //$NON-NLS-1$ //$NON-NLS-2$
// Safari and Konqueror have some problems with the "domready" event, so for those
// browsers we'll use the "load" event. Otherwise use "domready", it reduces the flicker
// effect when rearranging the tabs.
ClientProperties clp = ((WebClientInfo)Session.get().getClientInfo()).getProperties();
if (clp.isBrowserKonqueror() || clp.isBrowserSafari()) response.renderOnLoadJavascript(jsCall);
else response.renderOnDomReadyJavascript(jsCall);
}
}
@Override
public boolean isEnabled(Component component)
{
return WebClientSession.get().useAjax();
}
});
}
add(StyleAttributeModifierModel.INSTANCE);
add(TooltipAttributeModifier.INSTANCE);
this.scriptable = scriptable;
((ChangesRecorder)scriptable.getChangesRecorder()).setDefaultBorderAndPadding(null, TemplateGenerator.DEFAULT_LABEL_PADDING);
}
public final RuntimeTabPanel getScriptObject()
{
return scriptable;
}
/**
* @return the orient
*/
public int getOrient()
{
return orient;
}
public IStylePropertyChanges getStylePropertyChanges()
{
return scriptable.getChangesRecorder();
}
private void setActiveTabPanel(WebTabFormLookup fl)
{
if (fl != currentForm)
{
WebTabFormLookup previous = currentForm;
if (previous != null)
{
int stopped = application.getFoundSetManager().getEditRecordList().stopEditing(false);
boolean cantStop = stopped != ISaveConstants.STOPPED && stopped != ISaveConstants.AUTO_SAVE_BLOCKED;
List<Runnable> invokeLaterRunnables = new ArrayList<Runnable>();
boolean ok = previous.notifyVisible(false, invokeLaterRunnables);
Utils.invokeLater(application, invokeLaterRunnables);
if (cantStop || !ok)
{
return;
}
}
int previousIndex = -1;
for (int i = 0; i < allTabs.size(); i++)
{
WebTabHolder holder = allTabs.get(i);
if (holder.getPanel() == previous)
{
previousIndex = i;
break;
}
}
if (previousIndex != -1)
{
final int changedIndex = previousIndex;
addStateChange(new Change()
{
@Override
public void undo()
{
if (allTabs.size() > changedIndex)
{
WebTabHolder holder = allTabs.get(changedIndex);
setActiveTabPanel(holder.getPanel());
}
}
});
}
List<Runnable> invokeLaterRunnables2 = new ArrayList<Runnable>();
setCurrentForm(fl, previousIndex, invokeLaterRunnables2);
Utils.invokeLater(application, invokeLaterRunnables2);
}
}
/**
* @param fl
* @param previousIndex
*/
private void setCurrentForm(WebTabFormLookup fl, int previousIndex, List<Runnable> invokeLaterRunnables)
{
if (fl != null && !fl.isFormReady()) return;
getStylePropertyChanges().setChanged();
currentForm = fl;
if (parentData != null)
{
showFoundSet(currentForm, parentData, getDefaultSort());
}
// Test if current one is there
if (currentForm.isReady())
{
WebForm webForm = currentForm.getWebForm();
if (WebTabPanel.this.get(webForm.getId()) != null)
{
// replace it
WebTabPanel.this.replace(webForm);
}
else
{
// else add it
WebTabPanel.this.add(webForm);
}
recomputeTabSequence();
boolean visible = true;
WebForm webform = findParent(WebForm.class);
if (webform != null)
{
visible = webform.getController().isFormVisible();
}
currentForm.notifyVisible(visible, invokeLaterRunnables);
if (onTabChangeMethodCmd != null && previousIndex != -1)
{
scriptExecutor.executeFunction(onTabChangeMethodCmd, Utils.arrayMerge((new Object[] { Integer.valueOf(previousIndex + 1) }), onTabChangeArgs),
true, this, false, StaticContentSpecLoader.PROPERTY_ONCHANGEMETHODID.getPropertyName(), false);
}
}
}
public WebForm getCurrentForm()
{
return currentForm != null ? currentForm.getWebForm() : null;
}
public IFormUI[] getChildForms()
{
WebForm form = getCurrentForm();
if (form != null && form.getParent() == null)
{
form = null;
}
return form != null ? new IFormUI[] { form } : null;
}
/**
* @see org.apache.wicket.MarkupContainer#remove(org.apache.wicket.Component)
*/
@Override
public void remove(Component component)
{
if (currentForm != null && currentForm.isReady() && component == currentForm.getWebForm())
{
currentForm.setWebForm(null);
//replace(new Label("webform", new Model<String>("")));
}
super.remove(component);
}
public void recomputeTabSequence()
{
FormController fc = currentForm.getWebForm().getController();
fc.recomputeTabSequence(tabSequenceIndex);
}
public boolean isCurrentForm(IFormUI formUI)
{
return getCurrentForm() == formUI;
}
/**
* @see wicket.MarkupContainer#onRender(wicket.markup.MarkupStream)
*/
@Override
protected void onRender(MarkupStream markupStream)
{
super.onRender(markupStream);
getStylePropertyChanges().setRendered();
}
/**
* @return
*/
public void initalizeFirstTab()
{
if (currentForm == null && allTabs.size() > 0)
{
WebTabHolder holder = allTabs.get(0);
List<Runnable> invokeLaterRunnables = new ArrayList<Runnable>();
setCurrentForm(holder.getPanel(), -1, invokeLaterRunnables);
Utils.invokeLater(application, invokeLaterRunnables);
}
else if (currentForm != null && currentForm.getWebForm() == null)
{
// webForm was removed from this tabpanel of the current Form (reuse or destroyed)
List<Runnable> invokeLaterRunnables = new ArrayList<Runnable>();
setCurrentForm(currentForm, -1, invokeLaterRunnables);
Utils.invokeLater(application, invokeLaterRunnables);
}
return;
}
private void relinkFormIfNeeded()
{
if (currentForm != null && isVisibleInHierarchy() && (currentForm.getWebForm() == null || currentForm.getWebForm().getParent() != this))
{
if (currentForm.getWebForm() == null)
{
if (size() == 0)
{
// probably current form was destroyed from js code
WebTabPanel.this.add(new Label("webform", new Model<String>("")));
}
}
else if (get(currentForm.getWebForm().getId()) != null)
{
// replace it
replace(currentForm.getWebForm());
}
else
{
// else add it
add(currentForm.getWebForm());
}
}
}
/**
* @see wicket.Component#onAttach()
*/
@Override
protected void onBeforeRender()
{
if (orient != TabPanel.SPLIT_HORIZONTAL && orient != TabPanel.SPLIT_VERTICAL)
{
//tab has to be initialized now.. see also MainPage.listview.onBeforRender..
initalizeFirstTab();
super.onBeforeRender();
relinkFormIfNeeded();
}
else super.onBeforeRender();
}
public void setTabLayoutPolicy(int scroll_tab_layout)
{
//TODO ignore???
}
public IFormLookupPanel createFormLookupPanel(String tabname, String relationName, String formName)
{
return new WebTabFormLookup(tabname, relationName, formName, this, application);
}
public void setCursor(Cursor cursor)
{
}
public void setValidationEnabled(boolean b)
{
}
public void notifyVisible(boolean visible, List<Runnable> invokeLaterRunnables)
{
if (currentForm == null && allTabs.size() > 0)
{
WebTabHolder holder = allTabs.get(0);
setCurrentForm(holder.getPanel(), -1, invokeLaterRunnables);
}
if (currentForm != null && currentForm.getWebForm() != null)
{
FormController controller = currentForm.getWebForm().getController();
//this is not needed when closing
if (visible && parentData != null)
{
showFoundSet(currentForm, parentData, controller.getDefaultSortColumns());
// Test if current one is there
if (currentForm.isReady())
{
if (WebTabPanel.this.get(currentForm.getWebForm().getId()) != null)
{
// replace it
WebTabPanel.this.replace(currentForm.getWebForm());
}
else
{
// else add it
WebTabPanel.this.add(currentForm.getWebForm());
}
recomputeTabSequence();
}
}
controller.notifyVisible(visible, invokeLaterRunnables);
}
}
public void notifyResized()
{
if (currentForm != null && currentForm.isReady())
{
WebForm webForm = currentForm.getWebForm();
FormController controller = webForm.getController();
if (controller != null && webForm.isFormWidthHeightChanged())
{
controller.notifyResized();
webForm.clearFormWidthHeightChangedFlag();
}
}
}
public void setRecord(IRecordInternal parentState, boolean stopEditing)
{
parentData = parentState;
if (currentForm != null)
{
showFoundSet(currentForm, parentState, getDefaultSort());
}
else if (allTabs.size() > 0)
{
showFoundSet(allTabs.get(0).getPanel(), parentState, getDefaultSort());
}
ITagResolver resolver = getTagResolver(parentState);
for (int i = 0; i < allTabs.size(); i++)
{
WebTabHolder element = allTabs.get(i);
if (element.refreshTagStrings(resolver))
{
getStylePropertyChanges().setChanged();
}
}
}
/**
* @param parentState
* @return
*/
private ITagResolver getTagResolver(IRecordInternal parentState)
{
ITagResolver resolver;
WebForm webForm = findParent(WebForm.class);
if (webForm != null)
{
resolver = webForm.getController().getTagResolver();
}
else
{
resolver = TagResolver.createResolver(parentState);
}
return resolver;
}
protected void showFoundSet(WebTabFormLookup flp, IRecordInternal parentState, List<SortColumn> sort)
{
deregisterSelectionListeners();
if (!flp.isReady()) return;
FormController fp = flp.getWebForm().getController();
if (fp != null && flp.getRelationName() != null)
{
IFoundSetInternal relatedFoundset = parentState == null ? null : parentState.getRelatedFoundSet(flp.getRelationName(), sort);
registerSelectionListeners(parentState, flp.getRelationName());
fp.loadData(relatedFoundset, null);
}
ITagResolver resolver = getTagResolver(parentState);
//refresh tab text
for (int i = 0; i < allTabs.size(); i++)
{
WebTabHolder element = allTabs.get(i);
if (element.getPanel() == flp)
{
element.refreshTagStrings(resolver);
break;
}
}
}
private void registerSelectionListeners(IRecordInternal parentState, String relationName)
{
String[] parts = relationName.split("\\."); //$NON-NLS-1$
IRecordInternal currentRecord = parentState;
for (int i = 0; currentRecord != null && i < parts.length - 1; i++)
{
IFoundSetInternal fs = currentRecord.getRelatedFoundSet(parts[i]);
if (fs instanceof ISwingFoundSet)
{
related.add((ISwingFoundSet)fs);
((ISwingFoundSet)fs).getSelectionModel().addListSelectionListener(this);
}
currentRecord = (fs == null) ? null : fs.getRecord(fs.getSelectedIndex());
}
}
private void deregisterSelectionListeners()
{
for (ISwingFoundSet fs : related)
{
fs.getSelectionModel().removeListSelectionListener(this);
}
related.clear();
}
public String getSelectedRelationName()
{
if (currentForm != null)
{
return currentForm.getRelationName();
}
return null;
}
public String[] getAllRelationNames()
{
String[] retval = new String[allRelationNames.size()];
for (int i = 0; i < retval.length; i++)
{
Object relationName = allRelationNames.get(i);
if (relationName != null)
{
retval[i] = relationName.toString();
}
}
return retval;
}
public List<SortColumn> getDefaultSort()
{
if (currentForm != null)
{
// extra test, if the current record is null and the form is not ready just return an empty list.
// record can be null in the destroy, then creating the form doesn't make any sense.
return currentForm.getDefaultSort(parentData != null || currentForm.isReady());
}
else if (allTabs.size() > 0)
{
WebTabHolder holder = allTabs.get(0);
return holder.getPanel().getDefaultSort(parentData != null || holder.getPanel().isReady());
}
return null;
}
public boolean stopUIEditing(boolean looseFocus)
{
if (currentForm != null && currentForm.isReady())
{
return currentForm.getWebForm().getController().stopUIEditing(true);
}
return true;
}
public void destroy()
{
deregisterSelectionListeners();
//TODO should deregister related foundsets??
}
/*
* tab support----------------------------------------------------------------------------
*/
public void addTab(String text, int iconMediaId, IFormLookupPanel flp, String tip)
{
byte[] iconData = ComponentFactory.loadIcon(application.getFlattenedSolution(), new Integer(iconMediaId));
insertTab(text, iconData, flp, tip, allTabs.size(), false);
}
public void insertTab(String text, byte[] iconData, IFormLookupPanel flp, String tip, int index, boolean loaded)
{
allTabs.add(index, new WebTabHolder(text, flp, iconData, tip));
allRelationNames.add(index, flp.getRelationName());
getStylePropertyChanges().setChanged();
if (allTabs.size() == 1 && loaded)
{
// it's the new active one! If the tabPanel is not loaded, don't do this because it will break execution order (it will be done when tabPanel gets shown)
// (renderers are now being created - forms initialisation not complete, and we shouldn't generate any JS callbacks like notifyVisible() and such which can access these forms)
setActiveTabPanel((WebTabFormLookup)flp);
}
}
public void setTabForegroundAt(int index, Color fg)
{
}
public void setTabBackgroundAt(int index, Color bg)
{
}
public boolean addTab(IForm formController, String formName, String tabname, String tabText, String tabtooltip, String iconURL, String fg, String bg,
String relationName, RelatedFoundSet relatedFs, int idx)
{
if (formController != null)
{
//to make sure we don't have recursion on adding a tab, to a tabpanel, that is based
//on the form that the tabpanel is placed on
WebForm webForm = findParent(WebForm.class);
if (webForm != null)
{
FormController parentFormController = webForm.getController();
if (parentFormController != null && parentFormController.equals(formController))
{
return false;
}
}
}
WebTabFormLookup flp = (WebTabFormLookup)createFormLookupPanel(tabname, relationName, formName);
if (formController != null) flp.setReadOnly(formController.isReadOnly());
FlattenedSolution fl = application.getFlattenedSolution();
int mediaId = -1;
if (iconURL != null && !"".equals(iconURL))
{
Media media = fl.getMedia(iconURL.replaceAll("media:///", ""));
if (media != null) mediaId = media.getID();
if (mediaId == -1)
{
Debug.warn("Form '" + formController.getName() + "' with tabpanel '" + this.name + "' has tabicon for tab '" + tabname +
"'in with icon media url : " + iconURL + " not found");
}
}
byte[] iconData = (mediaId == -1 ? null : ComponentFactory.loadIcon(fl, new Integer(mediaId)));
int count = allTabs.size();
int tabIndex = idx;
if (tabIndex == -1 || tabIndex >= count)
{
tabIndex = count;
}
insertTab(application.getI18NMessageIfPrefixed(tabText), iconData, flp, application.getI18NMessageIfPrefixed(tabtooltip), tabIndex, true);
if (fg != null) setTabForegroundAt(tabIndex, PersistHelper.createColor(fg));
if (bg != null) setTabBackgroundAt(tabIndex, PersistHelper.createColor(bg));
// TODO is this if really needed? (insertTab might activate the new tab, but loadData based on relationName only; if it
// doesn't activate... will ever currentForm == flp?)
// if the relatedFs is based on a different record then parentState, it would be wrong to use it... maybe we should only use the relationName
// from the relatedFs - which is already in the relationName param
if (relatedFs != null && currentForm == flp)
{
FormController fp = flp.getWebForm().getController();
if (fp != null && flp.getRelationName() != null && flp.getRelationName().equals(relationName))
{
fp.loadData(relatedFs, null);
}
}
return true;
}
public int getMaxTabIndex()
{
return allTabs.size() - 1;
}
public String getTabFormNameAt(int i)
{
WebTabHolder holder = allTabs.get(i);
return holder.getPanel().getFormName();
}
public int getTabIndex()
{
for (int i = 0; i < allTabs.size(); i++)
{
if (currentForm == null)
{
// no current form set yet, default to first tab
return 0;
}
if (allTabs.get(i).getPanel() == currentForm)
{
return i;
}
}
return -1;
}
public String getTabNameAt(int i)
{
WebTabHolder holder = allTabs.get(i);
return holder.getPanel().getName();
}
public String getTabTextAt(int i)
{
WebTabHolder holder = allTabs.get(i);
return holder.getText();
}
public int getMnemonicAt(int i)
{
WebTabHolder holder = allTabs.get(i);
return holder.getDisplayedMnemonic();
}
public void setMnemonicAt(int i, int m)
{
WebTabHolder holder = allTabs.get(i);
holder.setDisplayedMnemonic(m);
}
public boolean isTabEnabledAt(int index)
{
WebTabHolder holder = allTabs.get(index);
return holder.isEnabled();
}
public boolean removeTabAt(int index)
{
WebTabHolder holder = allTabs.get(index);
List<Runnable> invokeLaterRunnables = new ArrayList<Runnable>();
boolean ok = holder.getPanel().notifyVisible(false, invokeLaterRunnables);
Utils.invokeLater(application, invokeLaterRunnables);
if (!ok)
{
return false;
}
allTabs.remove(index);
if (holder.getPanel() == currentForm)
{
if (allTabs.size() > 0)
{
setActiveTabPanel(allTabs.get(0).getPanel());
}
else
{
//safety
currentForm = null;
replace(new Label("webform", new Model<String>("")));
}
}
return true;
}
public boolean removeAllTabs()
{
for (int i = 0; i < allTabs.size(); i++)
{
WebTabHolder comp = allTabs.get(i);
List<Runnable> invokeLaterRunnables = new ArrayList<Runnable>();
boolean ok = comp.getPanel().notifyVisible(false, invokeLaterRunnables);
Utils.invokeLater(application, invokeLaterRunnables);
if (!ok)
{
return false;
}
}
allTabs.clear();
allRelationNames.clear();
//safety
currentForm = null;
if (WebTabPanel.this.get("webform") != null) //$NON-NLS-1$
{
// replace it
WebTabPanel.this.replace(new Label("webform", new Model<String>("")));//temporary add; //$NON-NLS-1$ //$NON-NLS-2$
}
else
{
// else add it
WebTabPanel.this.add(new Label("webform", new Model<String>("")));//temporary add; //$NON-NLS-1$ //$NON-NLS-2$
}
return true;
}
public void setTabEnabledAt(int i, boolean b)
{
WebTabHolder holder = allTabs.get(i);
holder.setEnabled(b);
}
public void setTabIndex(int index)
{
setActiveTabPanel(allTabs.get(index).getPanel());
}
public void setTabIndex(String name)
{
for (int i = 0; i < allTabs.size(); i++)
{
WebTabHolder holder = allTabs.get(i);
if (Utils.stringSafeEquals(holder.getPanel().getName(), name))
{
setActiveTabPanel(holder.getPanel());
break;
}
}
}
public void setTabTextAt(int i, String s)
{
WebTabHolder holder = allTabs.get(i);
holder.setText(TemplateGenerator.getSafeText(s));
}
/*
* readonly---------------------------------------------------
*/
public boolean isReadOnly()
{
if (currentForm != null)
{
return currentForm.isReadOnly();
}
return false;
}
public void setReadOnly(boolean b)
{
for (int i = 0; i < allTabs.size(); i++)
{
WebTabHolder holder = allTabs.get(i);
holder.getPanel().setReadOnly(b);
}
}
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;
}
private String tooltip;
public void setToolTipText(String tooltip)
{
this.tooltip = Utils.stringIsEmpty(tooltip) ? null : tooltip;
}
/**
* @see com.servoy.j2db.ui.IComponent#getToolTipText()
*/
public String getToolTipText()
{
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;
public void setForeground(Color cfg)
{
this.foreground = cfg;
}
public Color getForeground()
{
return foreground;
}
/*
* visible---------------------------------------------------
*/
public void setComponentVisible(boolean visible)
{
if (viewable)
{
setVisible(visible);
}
}
public void setComponentEnabled(final boolean b)
{
if (accessible)
{
super.setEnabled(b);
visitChildren(IComponent.class, new IVisitor<Component>()
{
public Object component(Component component)
{
if (component instanceof WebForm)
{
((WebForm)component).getController().setComponentEnabled(b);
}
else if (component instanceof IComponent && !(component instanceof MarkupContainer))
{
((IComponent)component).setComponentEnabled(b);
}
else if (!(component instanceof MarkupContainer))
{
component.setEnabled(b);
}
return CONTINUE_TRAVERSAL;
}
});
getStylePropertyChanges().setChanged();
}
}
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 Rectangle getWebBounds()
{
Dimension d = ((ChangesRecorder)getStylePropertyChanges()).calculateWebSize(size.width, size.height, border, new Insets(0, 0, 0, 0), 0, null);
return new Rectangle(location, d);
}
/**
* @see com.servoy.j2db.ui.ISupportWebBounds#getPaddingAndBorder()
*/
public Insets getPaddingAndBorder()
{
return ((ChangesRecorder)getStylePropertyChanges()).getPaddingAndBorder(size.height, border, new Insets(0, 0, 0, 0), 0, null);
}
public void setSize(Dimension size)
{
if (this.size != null && currentForm != null && currentForm.isReady())
{
currentForm.getWebForm().setFormWidth(0);
}
this.size = size;
}
/**
* @see com.servoy.j2db.ui.ITabPanel#addScriptExecuter(com.servoy.j2db.IScriptExecuter)
*/
public void addScriptExecuter(IScriptExecuter executor)
{
this.scriptExecutor = executor;
}
/**
* @see com.servoy.j2db.ui.ITabPanel#setOnTabChangeMethodCmd(java.lang.String, TabPanel)
*/
public void setOnTabChangeMethodCmd(String onTabChangeMethodCmd, Object[] onTabChangeArgs)
{
this.onTabChangeMethodCmd = onTabChangeMethodCmd;
this.onTabChangeArgs = onTabChangeArgs;
}
public void setTabSequenceIndex(int tabIndex)
{
this.tabSequenceIndex = tabIndex;
}
public int getTabSequenceIndex()
{
return tabSequenceIndex;
}
/**
* @param current
* @return
*/
public int getTabIndex(WebForm current)
{
if (currentForm != null && currentForm.getWebForm() == current)
{
Object o = scriptable.js_getTabIndex();
if (o instanceof Integer)
{
if (((Integer)o).intValue() == -1) return -1;
return ((Integer)o).intValue() - 1;
}
}
for (int i = 0; i < allTabs.size(); i++)
{
WebTabHolder holder = allTabs.get(i);
if (holder.getPanel().getFormName() == current.getController().getName())
{
return i;
}
}
return -1;
}
public void valueChanged(ListSelectionEvent e)
{
if (parentData != null)
{
showFoundSet(currentForm, parentData, getDefaultSort());
}
}
public void setTabSize(Dimension tabSize)
{
this.tabSize = tabSize;
}
public Dimension getTabSize()
{
return tabSize;
}
public void setHorizontalAlignment(int alignment)
{
}
public static class ServoyTabIcon extends Label implements IResourceListener
{
private final WebTabHolder holder;
public ServoyTabIcon(String id, final WebTabHolder holder, final IRuntimeComponent scriptable)
{
super(id);
this.holder = holder;
add(new StyleAppendingModifier(new Model<String>()
{
@SuppressWarnings("nls")
@Override
public String getObject()
{
StringBuilder result = new StringBuilder();
if (holder.getIcon() != null)
{
result.append("width: ").append(holder.getIcon().getWidth()).append("px; height: ").append(holder.getIcon().getHeight()).append("px");
result.append("; background-image: url(");
result.append(getResponse().encodeURL(urlFor(IResourceListener.INTERFACE) + "&r=" + Math.random()));
result.append(')');
if (!scriptable.isEnabled())
{
result.append("; filter:alpha(opacity=50);-moz-opacity:.50;opacity:.50");
}
}
else
{
result.append("width: 0px; height: 0px");
}
return result.toString();
}
}));
}
public void onResourceRequested()
{
if (holder.getIcon() != null)
{
holder.getIcon().onResourceRequested();
}
}
}
@Override
protected void onComponentTagBody(MarkupStream markupStream, ComponentTag openTag)
{
if (getBorder() instanceof TitledBorder)
{
getResponse().write(WebBaseButton.getTitledBorderOpenMarkup((TitledBorder)getBorder()));
}
super.onComponentTagBody(markupStream, openTag);
if (getBorder() instanceof TitledBorder)
{
getResponse().write(WebBaseButton.getTitledBorderCloseMarkup());
}
}
public ISupportSimulateBounds getBoundsProvider()
{
return findParent(ISupportSimulateBounds.class);
}
@Override
public void uiRecreated()
{
recomputeTabSequence();
}
}