/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2011 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 java.util.Locale;
import java.util.StringTokenizer;
import javax.swing.JComponent;
import javax.swing.border.Border;
import javax.swing.event.ListDataListener;
import javax.swing.text.Document;
import org.apache.wicket.Page;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.ResourceReference;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.ListMultipleChoice;
import org.apache.wicket.model.IModel;
import org.apache.wicket.util.convert.IConverter;
import org.apache.wicket.util.string.AppendingStringBuffer;
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.component.ComponentFormat;
import com.servoy.j2db.dataprocessing.IDisplayData;
import com.servoy.j2db.dataprocessing.IDisplayRelatedData;
import com.servoy.j2db.dataprocessing.IEditListener;
import com.servoy.j2db.dataprocessing.IRecordInternal;
import com.servoy.j2db.dataprocessing.IValueList;
import com.servoy.j2db.dataprocessing.SortColumn;
import com.servoy.j2db.persistence.IColumnTypes;
import com.servoy.j2db.scripting.JSEvent;
import com.servoy.j2db.server.headlessclient.MainPage;
import com.servoy.j2db.ui.IEventExecutor;
import com.servoy.j2db.ui.IFieldComponent;
import com.servoy.j2db.ui.IFormattingComponent;
import com.servoy.j2db.ui.ILabel;
import com.servoy.j2db.ui.IProviderStylePropertyChanges;
import com.servoy.j2db.ui.IScrollPane;
import com.servoy.j2db.ui.IStylePropertyChanges;
import com.servoy.j2db.ui.ISupportOnRender;
import com.servoy.j2db.ui.ISupportScroll;
import com.servoy.j2db.ui.ISupportSimulateBounds;
import com.servoy.j2db.ui.ISupportSimulateBoundsProvider;
import com.servoy.j2db.ui.ISupportValueList;
import com.servoy.j2db.ui.ISupportWebBounds;
import com.servoy.j2db.ui.scripting.AbstractRuntimeScrollableValuelistComponent;
import com.servoy.j2db.util.RoundHalfUpDecimalFormat;
import com.servoy.j2db.util.StateFullSimpleDateFormat;
import com.servoy.j2db.util.Text;
import com.servoy.j2db.util.Utils;
/**
* Represents a single/multiple choice list field in the web browser.
*
* @author lvostinar
*
*/
public class WebDataListBox extends ListMultipleChoice implements IDisplayData, IFieldComponent, IDisplayRelatedData, IResolveObject,
IProviderStylePropertyChanges, IScrollPane, ISupportWebBounds, IRightClickListener, IOwnTabSequenceHandler, ISupportValueList, IFormattingComponent,
ISupportSimulateBoundsProvider, ISupportOnRender, ISupportScroll
{
private static final long serialVersionUID = 1L;
private static final String NO_COLOR = "NO_COLOR"; //$NON-NLS-1$
private final IApplication application;
private final WebComboModelListModelWrapper list;
private final WebEventExecutor eventExecutor;
// private Cursor cursor;
private boolean needEntireState;
// private int maxLength;
private Insets margin;
// private int horizontalAlignment;
private String inputId;
private String tmpForeground = NO_COLOR;
private IValueList vl;
private int tabIndex = -1;
private final boolean multiSelection;
private int vScrollPolicy;
private final AbstractRuntimeScrollableValuelistComponent<IFieldComponent, JComponent> scriptable;
public WebDataListBox(IApplication application, AbstractRuntimeScrollableValuelistComponent<IFieldComponent, JComponent> scriptable, String id,
IValueList vl, boolean multiSelection)
{
super(id);
this.application = application;
this.vl = vl;
boolean useAJAX = Utils.getAsBoolean(application.getRuntimeProperties().get("useAJAX")); //$NON-NLS-1$
eventExecutor = new WebEventExecutor(this, useAJAX);
setOutputMarkupPlaceholderTag(true);
list = new WebComboModelListModelWrapper(vl, true, true);
list.setMultiValueSelect(multiSelection);
setChoices(list);
setChoiceRenderer(new WebChoiceRenderer(this, list));
add(StyleAttributeModifierModel.INSTANCE);
add(TooltipAttributeModifier.INSTANCE);
this.multiSelection = multiSelection;
this.scriptable = scriptable;
scriptable.setList(list);
((ChangesRecorder)scriptable.getChangesRecorder()).setDefaultBorderAndPadding(TemplateGenerator.DEFAULT_LABEL_PADDING,
TemplateGenerator.DEFAULT_LABEL_PADDING);
add(new ScrollBehavior(this));
}
public final AbstractRuntimeScrollableValuelistComponent<IFieldComponent, JComponent> getScriptObject()
{
return scriptable;
}
/**
* @see org.apache.wicket.Component#getLocale()
*/
@Override
public Locale getLocale()
{
return application.getLocale();
}
public IStylePropertyChanges getStylePropertyChanges()
{
return scriptable.getChangesRecorder();
}
/**
* @see wicket.Component#onModelChanged()
*/
@Override
protected void onModelChanged()
{
getModel().setObject(getConvertedInput());
}
/*
* _____________________________________________________________ Methods for event handling
*/
public void addScriptExecuter(IScriptExecuter el)
{
eventExecutor.setScriptExecuter(el);
}
public IEventExecutor getEventExecutor()
{
return eventExecutor;
}
public void setEnterCmds(String[] ids, Object[][] args)
{
eventExecutor.setEnterCmds(ids, args);
}
public void setLeaveCmds(String[] ids, Object[][] args)
{
eventExecutor.setLeaveCmds(ids, args);
}
public boolean isValueValid()
{
return isValueValid;
}
private boolean isValueValid = true;
private Object previousValidValue;
private FormatConverter converter;
public void setValueValid(boolean valid, Object oldVal)
{
application.getRuntimeProperties().put(IServiceProvider.RT_LASTFIELDVALIDATIONFAILED_FLAG, Boolean.valueOf(!valid));
isValueValid = valid;
if (!isValueValid)
{
previousValidValue = oldVal;
requestFocusToComponent();
if (tmpForeground == NO_COLOR)
{
tmpForeground = scriptable.getFgcolor();
scriptable.setFgcolor("red"); //$NON-NLS-1$
}
}
else
{
previousValidValue = null;
if (tmpForeground != NO_COLOR)
{
scriptable.setFgcolor(tmpForeground);
tmpForeground = NO_COLOR;
}
}
}
public void notifyLastNewValueWasChange(final Object oldVal, final Object newVal)
{
if (eventExecutor.hasChangeCmd() || eventExecutor.hasActionCmd())
{
application.invokeLater(new Runnable()
{
public void run()
{
WebEventExecutor.setSelectedIndex(WebDataListBox.this, null, IEventExecutor.MODIFIERS_UNSPECIFIED);
eventExecutor.fireChangeCommand(previousValidValue == null ? oldVal : previousValidValue, newVal, false, WebDataListBox.this);
//if change cmd is not succeeded also don't call action cmd?
if (isValueValid)
{
eventExecutor.fireActionCommand(false, WebDataListBox.this);
}
}
});
}
else
{
setValueValid(true, null);
}
}
public void setChangeCmd(String id, Object[] args)
{
eventExecutor.setChangeCmd(id, args);
}
public void setActionCmd(String id, Object[] args)
{
eventExecutor.setActionCmd(id, args);
}
public void setValidationEnabled(boolean b)
{
if (vl.getFallbackValueList() != null)
{
if (b)
{
list.register(vl);
}
else
{
list.register(vl.getFallbackValueList());
}
}
eventExecutor.setValidationEnabled(b);
}
public void setSelectOnEnter(boolean b)
{
eventExecutor.setSelectOnEnter(b);
}
//_____________________________________________________________
@Override
protected void onRender(final MarkupStream markupStream)
{
super.onRender(markupStream);
getStylePropertyChanges().setRendered();
IModel< ? > model = getInnermostModel();
if (model instanceof RecordItemModel)
{
((RecordItemModel)model).updateRenderedValue(this);
}
}
@Override
protected void onComponentTag(ComponentTag tag)
{
super.onComponentTag(tag);
if (!multiSelection)
{
tag.remove("multiple"); //$NON-NLS-1$
}
tag.put("size", Math.max(2, getChoices().size())); //$NON-NLS-1$
boolean useAJAX = Utils.getAsBoolean(application.getRuntimeProperties().get("useAJAX")); //$NON-NLS-1$
if (useAJAX)
{
Object oe = scriptable.getClientProperty("ajax.enabled"); //$NON-NLS-1$
if (oe != null) useAJAX = Utils.getAsBoolean(oe);
}
if (!useAJAX)
{
Form< ? > f = getForm();
if (f != null)
{
if (eventExecutor.hasRightClickCmd())
{
CharSequence urlr = urlFor(IRightClickListener.INTERFACE);
// We need a "return false;" so that the context menu is not displayed in the browser.
tag.put("oncontextmenu", f.getJsForInterfaceUrl(urlr) + " return false;"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
}
/**
* @see wicket.markup.html.form.FormComponent#getInputName()
*/
@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 setEditable(boolean b)
{
editState = b;
editable = b;
}
/**
* @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;
}
/**
* @see com.servoy.j2db.ui.IFieldComponent#setMaxLength(int)
*/
public void setMaxLength(int maxLength)
{
// this.maxLength = maxLength;
}
/**
* @see com.servoy.j2db.ui.IFieldComponent#setMargin(java.awt.Insets)
*/
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;
}
/**
* @see com.servoy.j2db.ui.IComponent#setCursor(java.awt.Cursor)
*/
public void setCursor(Cursor cursor)
{
// this.cursor = cursor;
}
public Object getValueObject()
{
return getDefaultModelObject();
}
public void setValueObject(Object value)
{
// add this code in order for js_getSelectedItems to work
List<Integer> selectedRows = new ArrayList<Integer>();
if (value instanceof String)
{
String delim = (eventExecutor.getValidationEnabled() ? "\n" : "%\n"); //$NON-NLS-1$//$NON-NLS-2$
StringTokenizer tk = new StringTokenizer(value.toString(), delim);
while (tk.hasMoreTokens())
{
int row = list.realValueIndexOf(tk.nextToken());
if (row >= 0) selectedRows.add(Integer.valueOf(row));
}
}
else
{
int row = list.realValueIndexOf(value);
if (row >= 0) selectedRows.add(Integer.valueOf(row));
}
// first select then unselect because list cannot remain without selection
for (Integer selectedRow : selectedRows)
{
list.setElementAt(Boolean.TRUE, selectedRow.intValue());
}
for (int i = 0; i < list.size(); i++)
{
if (!selectedRows.contains(Integer.valueOf(i)))
{
list.setElementAt(Boolean.FALSE, i);
}
}
((ChangesRecorder)getStylePropertyChanges()).testChanged(this, value);
if (getStylePropertyChanges().isChanged())
{
// this component is going to update it's contents, without the user changing the
// components contents; so remove invalid state if necessary
setValueValid(true, null);
}
}
/**
* @see com.servoy.j2db.dataprocessing.IDisplayData#needEditListener()
*/
public boolean needEditListener()
{
return false;
}
public void addEditListener(IEditListener l)
{
}
@Override
public IConverter getConverter(Class< ? > cls)
{
if (converter != null) return converter;
ComponentFormat cf = getScriptObject().getComponentFormat();
switch (cf.uiType)
{
case IColumnTypes.DATETIME :
converter = new FormatConverter(this, eventExecutor, new StateFullSimpleDateFormat(cf.parsedFormat.getDisplayFormat(), /* getClientTimeZone() */
null, application.getLocale(), true), cf.parsedFormat);
break;
case IColumnTypes.INTEGER :
case IColumnTypes.NUMBER :
converter = new FormatConverter(this, eventExecutor, new RoundHalfUpDecimalFormat(cf.parsedFormat.getDisplayFormat(), application.getLocale()),
cf.parsedFormat);
break;
default :
return super.getConverter(cls);
}
return converter;
}
/*
* format---------------------------------------------------
*/
public void installFormat(ComponentFormat componentFormat)
{
converter = null;
}
@Override
public String toString()
{
return scriptable.toString("value:" + getDefaultModelObjectAsString()); //$NON-NLS-1$
}
/*
* _____________________________________________________________ Methods for IDisplayRelatedData
*/
public void setRecord(IRecordInternal state, boolean stopEditing)
{
boolean listContentChanged = false;
Object[] oldListValue = list.toArray();
list.fill(state);
listContentChanged = !list.compareTo(oldListValue);
if (listContentChanged)
{
getStylePropertyChanges().setChanged();
}
}
public String getSelectedRelationName()
{
if (relationName == null && list != null)
{
relationName = list.getRelationName();
}
return relationName;
}
private String relationName = null;
public String[] getAllRelationNames()
{
String selectedRelationName = getSelectedRelationName();
if (selectedRelationName == null)
{
return new String[0];
}
else
{
return new String[] { selectedRelationName };
}
}
public boolean stopUIEditing(boolean looseFocus)
{
if (!isValueValid)
{
requestFocusToComponent();
return false;
}
return true;
}
public void notifyVisible(boolean b, List<Runnable> invokeLaterRunnables)
{
//ignore
}
public void destroy()
{
list.deregister();
}
public List<SortColumn> getDefaultSort()
{
return null;
}
/**
* @see com.servoy.j2db.dataprocessing.IDisplayData#getDocument()
*/
public Document getDocument()
{
return null;
}
/**
* @see wicket.markup.html.form.ListMultipleChoice#updateModel()
*/
@Override
public void updateModel()
{
Object ci = getConvertedInput();
if (ci instanceof List && ((List)ci).size() == 0 && vl != null && !vl.getAllowEmptySelection())
{
getStylePropertyChanges().setChanged(); // if valuelist doesn't allow null, don't change the model's value and mark for re-render
}
else
{
boolean b = getStylePropertyChanges().isChanged();
setModelObject(ci);
// if before updating the model the changed flag was false make sure it stays that way.
if (!b)
{
getStylePropertyChanges().setRendered();
}
}
}
/*
* _____________________________________________________________ Methods for model object resolve
*/
public Object resolveRealValue(Object displayVal)
{
if (displayVal instanceof List)
{
return getScriptObject().getChoiceValue(((List< ? >)displayVal).toArray(), false);
}
return null;
}
public Object resolveDisplayValue(Object realVal)
{
return getScriptObject().resolveChoiceValues(realVal);
}
/**
* @see com.servoy.j2db.ui.IScrollPane#setHorizontalScrollBarPolicy(int)
*/
public void setHorizontalScrollBarPolicy(int policy)
{
}
/**
* @see com.servoy.j2db.ui.IScrollPane#setVerticalScrollBarPolicy(int)
*/
public void setVerticalScrollBarPolicy(int policy)
{
this.vScrollPolicy = policy;
}
public void requestFocusToComponent()
{
// TODO this doesn't work as expected right now - should request focus on one of the inner
// input tags - created during onComponentTagBody
// is the current container always the right one...
IMainContainer currentContainer = ((FormManager)application.getFormManager()).getCurrentContainer();
if (currentContainer instanceof MainPage)
{
((MainPage)currentContainer).componentToFocus(this);
}
}
public IValueList getValueList()
{
return vl;
}
public ListDataListener getListener()
{
return null;
}
public void setValueList(IValueList vl)
{
this.vl = vl;
list.register(vl);
getStylePropertyChanges().setChanged();
}
/*
* dataprovider---------------------------------------------------
*/
private String dataProviderID;
public void setDataProviderID(String dataProviderID)
{
this.dataProviderID = dataProviderID;
list.setDataProviderID(dataProviderID);
}
public String getDataProviderID()
{
return dataProviderID;
}
private boolean editState;
private boolean editable = true;
public void setReadOnly(boolean b)
{
if (b)
{
setEditable(false);
editState = true;
}
else
{
setEditable(editState);
}
}
public boolean isReadOnly()
{
return !editable;
}
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)
{
if (Utils.stringIsEmpty(tooltip))
{
this.tooltip = null;
}
else
{
this.tooltip = 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 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 boolean isEditable()
{
return !isReadOnly();
}
public void addLabelFor(ILabel label)
{
if (labels == null) labels = new ArrayList<ILabel>(3);
labels.add(label);
}
public List<ILabel> getLabelsFor()
{
return labels;
}
public void setComponentEnabled(final boolean b)
{
if (accessible || !b)
{
super.setEnabled(b);
getStylePropertyChanges().setChanged();
if (labels != null)
{
for (int i = 0; i < labels.size(); i++)
{
ILabel label = labels.get(i);
label.setComponentEnabled(b);
}
}
}
}
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, null, null, 0, null);
return new Rectangle(location, d);
}
/**
* @see com.servoy.j2db.ui.ISupportWebBounds#getPaddingAndBorder()
*/
public Insets getPaddingAndBorder()
{
return ((ChangesRecorder)getStylePropertyChanges()).getPaddingAndBorder(size.height, null, null, 0, null);
}
public void setSize(Dimension size)
{
this.size = size;
}
public void setRightClickCommand(String rightClickCmd, Object[] args)
{
eventExecutor.setRightClickCmd(rightClickCmd, args);
}
public void onRightClick()
{
Form< ? > f = getForm();
if (f != null)
{
// If form validation fails, we don't execute the method.
if (f.process()) eventExecutor.onEvent(JSEvent.EventType.rightClick, null, this, IEventExecutor.MODIFIERS_UNSPECIFIED);
}
}
public void handleOwnTabIndex(int newTabIndex)
{
this.tabIndex = newTabIndex;
}
@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);
}
}
@Override
protected boolean isDisabled(Object object, int index, String selected)
{
return isReadOnly();
}
/*
* (non-Javadoc)
*
* @see org.apache.wicket.markup.html.form.AbstractChoice#appendOptionHtml(org.apache.wicket.util.string.AppendingStringBuffer, java.lang.Object, int,
* java.lang.String)
*/
@Override
protected void appendOptionHtml(AppendingStringBuffer buffer, Object choice, int index, String selected)
{
Object displayValue = getChoiceRenderer().getDisplayValue(choice);
if (IValueList.SEPARATOR.equals(displayValue))
{
// create a separator
CharSequence url = RequestCycle.get().urlFor(new ResourceReference(WebDataListBox.class, "grayDot.gif"));
buffer.append("\n<optgroup class=\"separator\" label=\" \"></optgroup>");
}
else
{
super.appendOptionHtml(buffer, choice, index, selected);
}
}
public ISupportSimulateBounds getBoundsProvider()
{
return findParent(ISupportSimulateBounds.class);
}
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 getMarkupId();
}
}