/* 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.smart.dataui; import java.awt.AWTEvent; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; import java.awt.Point; import java.awt.dnd.DropTarget; import java.awt.event.ActionEvent; import java.awt.event.FocusEvent; import java.awt.event.InputEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.lang.reflect.Method; import java.math.BigDecimal; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.text.ParseException; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.JFormattedTextField; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.TransferHandler; import javax.swing.event.ListDataListener; import javax.swing.plaf.ComponentUI; import javax.swing.text.Caret; import javax.swing.text.DateFormatter; import javax.swing.text.DefaultFormatter; import javax.swing.text.DefaultFormatterFactory; import javax.swing.text.Document; import javax.swing.text.DocumentFilter; import javax.swing.text.InternationalFormatter; import javax.swing.text.MaskFormatter; import javax.swing.text.NavigationFilter; import javax.swing.text.NumberFormatter; import org.jdesktop.xswingx.PromptSupport; import com.servoy.base.util.ITagResolver; import com.servoy.j2db.ControllerUndoManager; import com.servoy.j2db.IApplication; import com.servoy.j2db.IFormUIInternal; import com.servoy.j2db.IScriptExecuter; import com.servoy.j2db.IServiceProvider; import com.servoy.j2db.ISmartClientApplication; import com.servoy.j2db.component.ComponentFormat; import com.servoy.j2db.dataprocessing.CustomValueList; import com.servoy.j2db.dataprocessing.GlobalMethodValueList; import com.servoy.j2db.dataprocessing.IDisplayData; import com.servoy.j2db.dataprocessing.IEditListener; import com.servoy.j2db.dataprocessing.IValueList; import com.servoy.j2db.dataprocessing.ValueFactory.DbIdentValue; import com.servoy.j2db.dnd.FormDataTransferHandler; import com.servoy.j2db.dnd.ISupportDragNDropTextTransfer; import com.servoy.j2db.persistence.Column; import com.servoy.j2db.persistence.IColumnTypes; import com.servoy.j2db.ui.IDataRenderer; import com.servoy.j2db.ui.IEditProvider; 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.ISupportCachedLocationAndSize; import com.servoy.j2db.ui.ISupportEditProvider; import com.servoy.j2db.ui.ISupportFormatter; import com.servoy.j2db.ui.ISupportOnRender; import com.servoy.j2db.ui.ISupportPlaceholderText; import com.servoy.j2db.ui.ISupportSpecialClientProperty; import com.servoy.j2db.ui.ISupportValueList; import com.servoy.j2db.ui.scripting.AbstractRuntimeField; import com.servoy.j2db.util.Debug; import com.servoy.j2db.util.FormatParser.ParsedFormat; import com.servoy.j2db.util.HtmlUtils; import com.servoy.j2db.util.ISkinnable; import com.servoy.j2db.util.RoundHalfUpDecimalFormat; import com.servoy.j2db.util.ScopesUtils; import com.servoy.j2db.util.StateFullSimpleDateFormat; import com.servoy.j2db.util.Text; import com.servoy.j2db.util.UIUtils; import com.servoy.j2db.util.Utils; import com.servoy.j2db.util.docvalidator.LengthDocumentValidator; import com.servoy.j2db.util.docvalidator.LowerCaseDocumentValidator; import com.servoy.j2db.util.docvalidator.NumberDocumentValidator; import com.servoy.j2db.util.docvalidator.UpperCaseDocumentValidator; import com.servoy.j2db.util.docvalidator.ValidatingDocument; import com.servoy.j2db.util.text.FixedMaskFormatter; import com.servoy.j2db.util.text.ServoyMaskFormatter; /** * Runtime swing field * @author jblok, jcompagner */ public class DataField extends JFormattedTextField implements IDisplayData, IFieldComponent, ISkinnable, ISupportCachedLocationAndSize, ISupportDragNDropTextTransfer, ISupportEditProvider, ISupportValueList, ISupportSpecialClientProperty, ISupportFormatter, IFormattingComponent, ISupportPlaceholderText, ISupportOnRender { private static final long serialVersionUID = 1L; private static final String MAX_LENGTH_VALIDATOR = "maxLength"; //$NON-NLS-1$ private String tooltip; // when a parse error occurred the value should always be set in setValue(), otherwise unsaved data is displayed in the field private boolean parseErrorOccurred = false; private MouseAdapter rightclickMouseAdapter = null; private final AbstractRuntimeField<IFieldComponent> scriptable; /** * A formatter that extends our formatter to check for the valuelist * If the valuelist is attached then the value -> string must first get from the list (and that will then be formatter by the mask) * for string -> value what is get out of the mask will be checked if it is in the list. as a display and then the real will be given. * */ private final class ValueListMaskFormatter extends ServoyMaskFormatter { /** * @param mask * @param displayFormatter */ private ValueListMaskFormatter(String mask, boolean displayFormatter) throws ParseException { super(mask, displayFormatter); } /* * (non-Javadoc) * * @see com.servoy.j2db.smart.dataui.ServoyMaskFormatter#valueToString(java.lang.Object) */ @Override public String valueToString(Object value) throws ParseException { if (list != null) { int index = list.realValueIndexOf(value); if (index != -1) { return super.valueToString(list.getElementAt(index)); } else { if (list.hasRealValues()) { if (!eventExecutor.getValidationEnabled() && value != null) { return value.toString(); } else { return super.valueToString(null); } } } } return super.valueToString(value); } /* * (non-Javadoc) * * @see com.servoy.j2db.smart.dataui.ServoyMaskFormatter#stringToValue(java.lang.String) */ @Override public Object stringToValue(String value) throws ParseException { Object valueObject = super.stringToValue(value); if (list != null) { int index = list.indexOf(valueObject); if (index != -1) { return list.getRealElementAt(index); } else { if (list.hasRealValues()) { if (!eventExecutor.getValidationEnabled()) { return valueObject; } else { return null; } } } } return valueObject; } } /** * @author jcompagner */ public class NullNumberFormatter extends NumberFormatter { private static final long serialVersionUID = 1L; private NumberDocumentValidator validator; private final int maxLength; /** * Constructor for NullNumberFormatter. * * @param format * */ public NullNumberFormatter(NumberFormat format) { this(format, -1); } public NullNumberFormatter(NumberFormat format, int maxLength) { super(format); this.maxLength = maxLength; // format.setGroupingUsed(true); // this is done now directly in RoundHalfUp... } /* * (non-Javadoc) * * @see javax.swing.text.InternationalFormatter#install(javax.swing.JFormattedTextField) */ @Override public void install(JFormattedTextField ftf) { int caret = getCaretPosition(); super.install(ftf); int length = ftf.getDocument().getLength(); setCaretPosition(caret > length ? length : caret); } /** * @see javax.swing.text.DefaultFormatter#getNavigationFilter() */ @Override protected NavigationFilter getNavigationFilter() { return super.getNavigationFilter(); } /** * @see javax.swing.text.DefaultFormatter#getDocumentFilter() */ @Override protected DocumentFilter getDocumentFilter() { if (list == null) { if (validator == null) { validator = new NumberDocumentValidator(RoundHalfUpDecimalFormat.getDecimalFormatSymbols(application.getLocale()), maxLength); } return validator; } return super.getDocumentFilter(); } /* * (non-Javadoc) * * @see javax.swing.text.InternationalFormatter#valueToString(java.lang.Object) */ @Override public String valueToString(Object value) throws ParseException { if (value == null || value.toString().trim().equals("")) { //$NON-NLS-1$ return ""; //$NON-NLS-1$ } // if it is converted by a converter it could already be a string, then just display that. if (value instanceof String) return (String)value; if (list != null) { int index = list.realValueIndexOf(value); if (index != -1) { value = list.getElementAt(index); if (value != null) { try { return super.valueToString(value); } catch (Exception e) { } return value.toString(); } } else { if (list.hasRealValues()) { if (!eventExecutor.getValidationEnabled()) { return value != null ? value.toString() : null; } else { value = null; } } } } if (displayFormat.endsWith("-") && value != null && ((Number)value).intValue() >= 0) //$NON-NLS-1$ { return super.valueToString(value) + " "; //$NON-NLS-1$ } return super.valueToString(value); } /** * @see javax.swing.JFormattedTextField.AbstractFormatter#stringToValue(java.lang.String) */ @Override public Object stringToValue(String text) throws ParseException { if (text == null || text.trim().equals("")) return null; //$NON-NLS-1$ if (list != null) { int index = list.indexOf(text); if (index != -1) { return list.getRealElementAt(index); } else { if (list.hasRealValues()) { if (!eventExecutor.getValidationEnabled()) { return text; } else { return null; } } else { return super.stringToValue(text); } } } if (displayFormat.endsWith("-")) //$NON-NLS-1$ { text = text.trim(); } Object o = super.stringToValue(text); Object previousValue = getValue(); if (previousValue != null && o != null && previousValue.getClass() != o.getClass()) { // use for example the wicket converters if (previousValue instanceof Float) { o = new Float(((Number)o).floatValue()); } else if (previousValue instanceof BigDecimal) { o = new BigDecimal(((Number)o).doubleValue()); } else if (previousValue instanceof Double) { o = new Double(((Number)o).doubleValue()); } } return o; } } /** * Interface for setting initial value to formatter. * * @author rgansevles */ public interface ISetInitialValue<T> { void setInitialValue(T val); } public class NullDateFormatter extends DateFormatter implements ISetInitialValue<Date> { private static final long serialVersionUID = 1L; private final boolean editFormatter; private Date lastMergedDate; /** * Constructor for NullNumberFormatter. * * @param format */ public NullDateFormatter(StateFullSimpleDateFormat format) { this(format, false); } /** * Constructor for NullNumberFormatter. * * @param format */ public NullDateFormatter(StateFullSimpleDateFormat format, boolean editFormatter) { super(format); this.editFormatter = editFormatter; setOverwriteMode(true); } /** * @see javax.swing.JFormattedTextField.AbstractFormatter#getFormattedTextField() */ @Override protected JFormattedTextField getFormattedTextField() { JFormattedTextField field = super.getFormattedTextField(); if (field == null) field = DataField.this; return field; } /** * @see javax.swing.text.InternationalFormatter#getActions() */ @Override protected Action[] getActions() { if (isEditable()) { Action[] superActions = super.getActions(); // be able to catch the "increment"/"decrement" actions (so we can restrain the changes to only the selected field) if (superActions != null) { for (int i = 0; i < superActions.length; i++) { if ("increment".equals(superActions[i].getValue(Action.NAME)) || "decrement".equals(superActions[i].getValue(Action.NAME))) //$NON-NLS-1$ //$NON-NLS-2$ { final Action superAction = superActions[i]; superActions[i] = new AbstractAction((String)superActions[i].getValue(Action.NAME)) { public void actionPerformed(ActionEvent e) { if (Boolean.TRUE.equals(UIUtils.getUIProperty(DataField.this, IApplication.DATE_FORMATTERS_ROLL_INSTEAD_OF_ADD, Boolean.FALSE))) { ((StateFullSimpleDateFormat)getFormat()).setRollInsteadOfAdd(true); try { superAction.actionPerformed(e); } finally { ((StateFullSimpleDateFormat)getFormat()).setRollInsteadOfAdd(false); } } else { superAction.actionPerformed(e); } } }; } } } return superActions; } return null; } public void setLenient(boolean lenient) { ((StateFullSimpleDateFormat)getFormat()).setLenient(lenient); } /* * (non-Javadoc) * * @see javax.swing.text.InternationalFormatter#install(javax.swing.JFormattedTextField) */ @Override public void install(JFormattedTextField ftf) { int caret = getCaretPosition(); super.install(ftf); int length = ftf.getDocument().getLength(); setCaretPosition(caret > length ? length : caret); } public void setInitialValue(Date val) { this.lastMergedDate = val; } /** * @see javax.swing.JFormattedTextField.AbstractFormatter#stringToValue(java.lang.String) */ @Override public Object stringToValue(String text) throws ParseException { if (text == null || text.trim().equals("") || text.equals(editFormat) || text.equals(displayFormat)) { return null; } // keep track of last parsed value so that when the field is made empty (editable combo), next date merge will use prev value, not null StateFullSimpleDateFormat format = (StateFullSimpleDateFormat)getFormat(); format.setOriginal(lastMergedDate); super.stringToValue(text); lastMergedDate = format.getMergedDate(); return lastMergedDate; } /** * @see javax.swing.JFormattedTextField.AbstractFormatter#valueToString(java.lang.Object) */ @Override public String valueToString(Object value) throws ParseException { if (value == null) { if (editFormatter && getFormattedTextField().isEditable()) { SwingUtilities.invokeLater(new Runnable() { public void run() { selectAll(); } }); return editFormat; } else return ""; //$NON-NLS-1$ } // if it is converted by a converter it could already be a string, then just display that. if (value instanceof String) return (String)value; try { return super.valueToString(value); } catch (ParseException e) { Debug.error("Error formatting date: " + value + " to:" + displayFormat + " for dataproviderid: " + dataProviderID); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ throw e; } } /** * @param placeHolder * @return * @throws ParseException */ public AbstractFormatter getMaskFormatter(char placeHolder) throws ParseException { String pattern = ((StateFullSimpleDateFormat)getFormat()).toPattern(); String maskPattern = pattern.replace('y', '#').replace('M', '#').replace('w', '#').replace('W', '#').replace('D', '#').replace('d', '#').replace( 'F', '#').replace('a', '?').replace('H', '#').replace('k', '#').replace('K', '#').replace('h', '#').replace('m', '#').replace('s', '#').replace( 'S', '#'); FixedMaskFormatter maskFormatter = new FixedMaskDateFormatter(maskPattern); maskFormatter.setValueClass(String.class); if (placeHolder != 0) { maskFormatter.setPlaceholderCharacter(placeHolder); } else { maskFormatter.setPlaceholder(pattern); } return maskFormatter; } private final class FixedMaskDateFormatter extends FixedMaskFormatter implements ISetInitialValue<Date> { /** * @param mask */ private FixedMaskDateFormatter(String mask) throws ParseException { super(mask); } public void setInitialValue(Date val) { NullDateFormatter.this.setInitialValue(val); } /** * @see com.servoy.j2db.util.text.ServoyMaskFormatter#valueToString(java.lang.Object) */ @Override public String valueToString(Object value) throws ParseException { return super.valueToString(NullDateFormatter.this.valueToString(value)); } /** * @see com.servoy.j2db.util.text.ServoyMaskFormatter#stringToValue(java.lang.String) */ @Override public Object stringToValue(String value) throws ParseException { Object s = super.stringToValue(value); if (s instanceof String) { return NullDateFormatter.this.stringToValue((String)s); } return s; } /** * @see javax.swing.JFormattedTextField.AbstractFormatter#getActions() */ @Override protected Action[] getActions() { return NullDateFormatter.this.getActions(); } } } private class TextFormatter extends InternationalFormatter { private static final long serialVersionUID = 1L; /** * @see javax.swing.text.InternationalFormatter#install(javax.swing.JFormattedTextField) */ @Override public void install(JFormattedTextField ftf) { int caret = getCaretPosition(); super.install(ftf); int length = ftf.getDocument().getLength(); setCaretPosition(caret > length ? length : caret); } /* * (non-Javadoc) * * @see javax.swing.text.InternationalFormatter#stringToValue(java.lang.String) */ @Override public Object stringToValue(String text) throws ParseException { if (list != null) { int index = list.indexOf(text); if (index > -1) { return list.getRealElementAt(index); } else { if (list.hasRealValues()) { if (!eventExecutor.getValidationEnabled()) { return text; } else { return null; } } else { return super.stringToValue(text); } } } else { return super.stringToValue(text); } } /* * (non-Javadoc) * * @see javax.swing.text.InternationalFormatter#valueToString(java.lang.Object) */ @Override public String valueToString(Object value) throws ParseException { if (list != null) { // if it is in find mode and the list reports to have real values and it is a global method valuelist // test first if the given value is really a real value by comparing a real value class with the give class. if (value != null && !eventExecutor.getValidationEnabled() && list.hasRealValues() && list instanceof GlobalMethodValueList) { if (list.getSize() == 0 || (list.getSize() == 1 && list.getAllowEmptySelection())) { ((GlobalMethodValueList)list).fill(); } if (list.getSize() > 0) { Object real = list.getRealElementAt(list.getSize() - 1); if (real != null && !real.getClass().equals(value.getClass())) { return value.toString(); } } } int index = list.realValueIndexOf(value); if (index != -1) { value = list.getElementAt(index); } else { if (list.hasRealValues()) { if (!eventExecutor.getValidationEnabled()) { return value != null ? value.toString() : null; } else { value = null; } } } } return super.valueToString(value); } } private class EditingFixedDefaultFormatterFactory extends DefaultFormatterFactory { public EditingFixedDefaultFormatterFactory(JFormattedTextField.AbstractFormatter defaultFormat) { super(defaultFormat); } public EditingFixedDefaultFormatterFactory(JFormattedTextField.AbstractFormatter defaultFormat, JFormattedTextField.AbstractFormatter displayFormat, JFormattedTextField.AbstractFormatter editFormat) { super(defaultFormat, displayFormat, editFormat); } public EditingFixedDefaultFormatterFactory(JFormattedTextField.AbstractFormatter defaultFormat, JFormattedTextField.AbstractFormatter displayFormat, JFormattedTextField.AbstractFormatter editFormat, JFormattedTextField.AbstractFormatter nullFormat) { super(defaultFormat, displayFormat, editFormat, nullFormat); } @Override public JFormattedTextField.AbstractFormatter getFormatter(JFormattedTextField source) { JFormattedTextField.AbstractFormatter format = null; if (source == null) { return null; } Object value = source.getValue(); if (value == null) { format = getNullFormatter(); } if (format == null) { // added isEditable check if (source.hasFocus() && source.isEditable()) { format = getEditFormatter(); } else { format = getDisplayFormatter(); } if (format == null) { format = getDefaultFormatter(); } } return format; } } protected EventExecutor eventExecutor; private ControllerUndoManager undoManager; protected IApplication application; protected IValueList list; private final Document plainDocument; private ValidatingDocument editorDocument; private final String decimalSeparator; private boolean decimalMode; private Caret defaultCaret; private Caret overtypeCaret; private boolean toggleOverwrite = true; DataField(IApplication application, AbstractRuntimeField<IFieldComponent> scriptable, IValueList list) { this(application, scriptable); this.list = list; } public DataField(IApplication application, AbstractRuntimeField<IFieldComponent> scriptable) { super();// new InternationalFormatter()); //why is InternationalFormatter // needed, causes trouble on date objects?? this.application = application; eventExecutor = new EventExecutor(this) { @Override public void fireLeaveCommands(Object display, boolean focusEvent, int modifiers) { if (hasLeaveCmds()) { try { commitEdit(); } catch (ParseException ex) { Debug.error(ex); } } super.fireLeaveCommands(display, focusEvent, modifiers); } }; plainDocument = getDocument(); setDocument(editorDocument = new ValidatingDocument()); this.scriptable = scriptable; addKeyListener(new KeyAdapter() { private boolean enterKeyPressed = false; @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { enterKeyPressed = true; } if (e.getKeyCode() == KeyEvent.VK_ESCAPE && !isValueValid) { e.consume(); } } @Override public void keyReleased(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER && enterKeyPressed) { enterKeyPressed = false; eventExecutor.actionPerformed(e.getModifiers()); } if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { if (!isValueValid) { restorePreviousValidValue(); e.consume(); } } } }); DecimalFormatSymbols dfs = RoundHalfUpDecimalFormat.getDecimalFormatSymbols(application.getLocale()); decimalSeparator = String.valueOf(dfs.getDecimalSeparator()); setFocusLostBehavior(COMMIT); addMouseListener(eventExecutor); addKeyListener(eventExecutor); setDragEnabledEx(true); } public final AbstractRuntimeField<IFieldComponent> getScriptObject() { return scriptable; } private Caret getOvertypeCaret() { if (overtypeCaret == null) { defaultCaret = getCaret(); overtypeCaret = new OvertypeCaret(); if (defaultCaret == null) { overtypeCaret.setBlinkRate(500); } else { overtypeCaret.setBlinkRate(defaultCaret.getBlinkRate()); } } return overtypeCaret; } private Caret getDefaultCaret() { getOvertypeCaret(); return defaultCaret; } // MAC FIX @Override public Insets getInsets() { Insets insets = super.getInsets(); if (insets == null) { insets = new Insets(0, 0, 0, 0); } return insets; } // MAC FIX @Override public Insets getMargin() { Insets insets = super.getMargin(); if (insets == null) { insets = new Insets(0, 0, 0, 0); } return insets; } public void destroy() { if (list != null) list.deregister(); } private void setDragEnabledEx(boolean b) { try { Method m = getClass().getMethod("setDragEnabled", new Class[] { boolean.class }); //$NON-NLS-1$ m.invoke(this, new Object[] { new Boolean(b) }); } catch (Exception e) { } } /** * Fix for bad font rendering (bad kerning == strange spacing) in java 1.5 see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5097047 */ @Override public FontMetrics getFontMetrics(Font font) { if (application != null)//getFontMetrics can be called in the constructor super call before application is assigned { boolean isPrinting = Utils.getAsBoolean(application.getRuntimeProperties().get("isPrinting")); //$NON-NLS-1$ if (isPrinting) { Graphics g = (Graphics)application.getRuntimeProperties().get("printGraphics"); //$NON-NLS-1$ if (g != null) { String text = getText(); // only return print graphics font metrics if text does not start with 'W', // because of left side bearing issue if (!(text != null && text.length() > 0 && text.charAt(0) == 'W')) return g.getFontMetrics(font); } } } return super.getFontMetrics(font); } private ControllerUndoManager getUndoManager() { if (undoManager == null) { Container con = getParent(); while (con != null && !(con instanceof IFormUIInternal)) { con = con.getParent(); } if (con != null) { undoManager = ((IFormUIInternal)con).getUndoManager(); } } return undoManager; } @Override public void setUI(ComponentUI ui) { super.setUI(ui); } protected String dataProviderID; public boolean needEditListener() { return true; } protected EditProvider editProvider = null; public IEditProvider getEditProvider() { return editProvider; } public void addEditListener(IEditListener l) { if (editProvider == null) { editProvider = new EditProvider(this); addFocusListener(editProvider); addPropertyChangeListener("value", editProvider); //$NON-NLS-1$ editProvider.addEditListener(l); editProvider.setEditable(isEditable()); try { DropTarget dt = getDropTarget(); if (dt != null) dt.addDropTargetListener(editProvider); } catch (Exception e) { Debug.error(e); } } } public IEventExecutor getEventExecutor() { return eventExecutor; } @Override public void setEditable(boolean b) { editState = b; super.setEditable(b); if (editProvider != null) editProvider.setEditable(b); } private int maxLength = -1; public void setMaxLength(int i) { // do not set max length check when it has been set in setFormat() if (list == null && editorDocument.getValidator(MAX_LENGTH_VALIDATOR) == null) { editorDocument.setValidator(MAX_LENGTH_VALIDATOR, new LengthDocumentValidator(i)); } maxLength = i; } @Override public void setMargin(Insets m) { // super.setMargin(m); //seems to have no effect setBorder(BorderFactory.createCompoundBorder(getBorder(), BorderFactory.createEmptyBorder(m.top, m.left, m.bottom, m.right))); } public String getDataProviderID() { return dataProviderID; } public void setDataProviderID(String id) { dataProviderID = id; } private boolean needEntireState; public boolean needEntireState() { return needEntireState; } public void setNeedEntireState(boolean b) { needEntireState = b; } public Object getValueObject() { return getValue(); } protected ITagResolver resolver; public void setTagResolver(ITagResolver resolver) { this.resolver = resolver; } public void setValueObject(Object obj) { try { if (editProvider != null) { editProvider.setAdjusting(true); } if (((DefaultFormatterFactory)getFormatterFactory()) != null && ((DefaultFormatterFactory)getFormatterFactory()).getEditFormatter() instanceof ISetInitialValue) { ((ISetInitialValue)((DefaultFormatterFactory)getFormatterFactory()).getEditFormatter()).setInitialValue(obj); } setValueEx(obj); } catch (Exception e) { try { setValueEx(null); } catch (Exception ex) { } Debug.trace("Format Error in field " + getName() + " " + dataProviderID + " " + e); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } finally { if (editProvider != null) { editProvider.setAdjusting(false); } } fireOnRender(false); } public void fireOnRender(boolean force) { if (!isIgnoreOnRender && scriptable != null) { if (force) scriptable.getRenderEventExecutor().setRenderStateChanged(); scriptable.getRenderEventExecutor().fireOnRender(hasFocus()); } } // also used by datacalendar we than don't want the edit to be blocked by // adjusting void setValueExFromCalendar(Object obj) { boolean editable = false; if (editProvider != null) { editable = editProvider.isEditable(); editProvider.setEditable(true); // can this be done.. change command fired twice now without it. editProvider.setAdjusting(true); editProvider.startEdit(); } try { if (getFormatterFactory() == disabledFormatter && saveFormatter != null) { try { obj = saveFormatter.getFormatter(this).valueToString(obj); } catch (ParseException e) { Debug.error(e); } } setValueEx(obj); } finally { if (editProvider != null) { editProvider.setAdjusting(false); editProvider.commitData(); editProvider.setEditable(editable); } } } private void setValueEx(Object obj) { if (needEntireState && !skipPropertyChange) { if (resolver != null) { // do not process tags on other objects like numbers, these will be formatted using the formatter if (obj instanceof String) { obj = Text.processTags((String)obj, resolver); } } if (tooltip != null) { super.setToolTipText(""); //$NON-NLS-1$ } } if (obj instanceof DbIdentValue) { obj = ((DbIdentValue)obj).getPkValue(); } // when a parse error occurred the value should always be set in setValue(), otherwise unsaved data is displayed in the field if (parseErrorOccurred || !Utils.equalObjects(getValue(), obj)) { if (obj instanceof String && getFormatterFactory() == null) { // this may no longer be necessary as a FormatterFactory should always be set now // when setFormat() gets called... (a null formatter factory would mean that text // field commitEdit() would not work - so you cannot really use such a field for editing) setFormatterFactory(new EditingFixedDefaultFormatterFactory(new InternationalFormatter())); } boolean enableEdits = false; if (getUndoManager() != null) { if (!undoManager.isIgnoreEdits()) { enableEdits = true; undoManager.setIgnoreEdits(true); } } super.setValue(obj); // if text is changed through the outside (a new record is set, set the caret to the length pos) // and the current field has not the focus? if (hasFocus() && isDisplayable()) { setCaretPosition(getText().length()); } if (enableEdits) { undoManager.setIgnoreEdits(false); } setValueValid(true, null); } } protected boolean skipPropertyChange = false; @Override protected void invalidEdit() { if (eventExecutor.getValidationEnabled()) { super.invalidEdit(); } } /** * @see javax.swing.text.JTextComponent#setText(java.lang.String) */ @Override public void setText(String t) { if (!Utils.equalObjects(getText(), t)) super.setText(t); } // work around for bug in jdk 1.4 ,assumes that (isEditing == true) @Override protected void processFocusEvent(FocusEvent e) { boolean enableEdits = false; if (getUndoManager() != null) { if (!undoManager.isIgnoreEdits()) { enableEdits = true; undoManager.setIgnoreEdits(true); } } Object o = getValue(); int start = getSelectionStart(); int end = getSelectionEnd(); super.processFocusEvent(e); if ((start != end && getValue() == o) || (Utils.isAppleMacOS() && getValue() == o)) { select(start, end); } if (enableEdits) { undoManager.setIgnoreEdits(false); } } // work around for bug in jdk 1.4 ,the jformattedtextfield does fire odd values @Override protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { if (!isValueValid && "value".equals(propertyName) && Utils.equalObjects(newValue, previousValidValue)) //$NON-NLS-1$ { // added this because invalid fields with DATE/NUMBER data providers will // have oldValue = last valid value, not the currently displayed invalid value - thus // the equalObjects would return true although it shouldn't and the field would not be validated // through DisplaysAdapter setValueValid(true, null); } if ("value".equals(propertyName) && skipPropertyChange) //$NON-NLS-1$ { return; } else if (oldValue == null && newValue != null) { if (!newValue.equals("")) //$NON-NLS-1$ { super.firePropertyChange(propertyName, oldValue, newValue); } } else if ("value".equals(propertyName) && Utils.equalObjects(oldValue, newValue)) //$NON-NLS-1$ { skipPropertyChange = true; try { setValueEx(oldValue); } finally { skipPropertyChange = false; } return; } else { super.firePropertyChange(propertyName, oldValue, newValue); } } private int dataType; /* * format--------------------------------------------------- */ protected String displayFormat; protected String editFormat; private AbstractFormatterFactory saveFormatter; private final AbstractFormatterFactory disabledFormatter = new DefaultFormatterFactory(new InternationalFormatter()); private List<ILabel> labels; /** * This method is only used by DataCalendar to initialize the JDateChooser format */ public String getFormat() { if (fp == null) return null; if (fp.hasEditFormat()) { return fp.getEditFormat(); } else { return fp.getDisplayFormat(); } } protected ParsedFormat fp; public void installFormat(ComponentFormat componentFormat) { fp = componentFormat.parsedFormat; this.dataType = componentFormat.uiType; this.displayFormat = null; this.editFormat = null; editorDocument.clearValidators(); boolean emptyCustom = (list instanceof CustomValueList) && !(list instanceof GlobalMethodValueList) && list.getSize() == 0; if (!fp.isEmpty() && (list == null || (!list.hasRealValues() && !emptyCustom))) { displayFormat = fp.getDisplayFormat(); editFormat = fp.getEditFormat(); if (fp.getMaxLength() != null && fp.getMaxLength().intValue() > 0) { editorDocument.setValidator(MAX_LENGTH_VALIDATOR, new LengthDocumentValidator(fp.getMaxLength().intValue())); } if (fp.isAllLowerCase()) { editorDocument.setValidator("LowerCaseDocumentValidator", new LowerCaseDocumentValidator()); //$NON-NLS-1$ TextFormatter display = new TextFormatter(); TextFormatter edit = new TextFormatter(); setFormatterFactory(new EditingFixedDefaultFormatterFactory(display, display, edit, edit)); } else if (fp.isAllUpperCase()) { editorDocument.setValidator("UpperCaseDocumentValidator", new UpperCaseDocumentValidator()); //$NON-NLS-1$ TextFormatter display = new TextFormatter(); TextFormatter edit = new TextFormatter(); setFormatterFactory(new EditingFixedDefaultFormatterFactory(display, display, edit, edit)); } else if (fp.isNumberValidator()) { editorDocument.setValidator("NumberDocumentValidator", new NumberDocumentValidator()); //$NON-NLS-1$ TextFormatter display = new TextFormatter(); TextFormatter edit = new TextFormatter(); setFormatterFactory(new EditingFixedDefaultFormatterFactory(display, display, edit, edit)); } else { int maxLength = fp.getMaxLength() == null ? -1 : fp.getMaxLength().intValue(); // if there is no display format, but the max length is set, then generate a display format. if (maxLength != -1 && (displayFormat == null || displayFormat.length() == 0)) { // if this is just a text type textfield then just set those formatters (the max length is already set) if (Column.mapToDefaultType(dataType) == IColumnTypes.TEXT) { TextFormatter display = new TextFormatter(); TextFormatter edit = new TextFormatter(); setFormatterFactory(new EditingFixedDefaultFormatterFactory(display, display, edit, edit)); } else { char[] chars = new char[maxLength]; for (int i = 0; i < chars.length; i++) chars[i] = '#'; displayFormat = new String(chars); } } if (displayFormat != null) { if (editFormat == null) editFormat = displayFormat; try { JFormattedTextField.AbstractFormatter displayFormatter = null; JFormattedTextField.AbstractFormatter editFormatter = null; switch (Column.mapToDefaultType(dataType)) { case IColumnTypes.NUMBER : displayFormatter = new NullNumberFormatter(new RoundHalfUpDecimalFormat(displayFormat, application.getLocale()));// example: $#,###.## editFormatter = new NullNumberFormatter(new RoundHalfUpDecimalFormat(editFormat, application.getLocale()), maxLength); setFormatterFactory(new EditingFixedDefaultFormatterFactory(displayFormatter, displayFormatter, editFormatter, editFormatter)); break; case IColumnTypes.INTEGER : displayFormatter = new NullNumberFormatter(new RoundHalfUpDecimalFormat(displayFormat, application.getLocale()));// example: $#,###.## editFormatter = new NullNumberFormatter(new RoundHalfUpDecimalFormat(editFormat, application.getLocale()), maxLength); setFormatterFactory(new EditingFixedDefaultFormatterFactory(displayFormatter, displayFormatter, editFormatter, editFormatter)); break; case IColumnTypes.DATETIME : boolean mask = fp.isMask(); char placeHolder = fp.getPlaceHolderCharacter(); if (mask) editFormat = displayFormat; displayFormatter = new NullDateFormatter(new StateFullSimpleDateFormat(displayFormat, false)); editFormatter = new NullDateFormatter(new StateFullSimpleDateFormat(editFormat, Boolean.TRUE.equals(UIUtils.getUIProperty(this, IApplication.DATE_FORMATTERS_LENIENT, Boolean.TRUE))), !mask); if (mask) { editFormatter = ((NullDateFormatter)editFormatter).getMaskFormatter(placeHolder); } else { // date formats are default in override mode setCaret(getOvertypeCaret()); } setFormatterFactory(new EditingFixedDefaultFormatterFactory(displayFormatter, displayFormatter, editFormatter)); // example: MM/dd/yyyy break; default : displayFormatter = new ValueListMaskFormatter(displayFormat, true); editFormatter = new ValueListMaskFormatter(displayFormat, false); if (fp.isRaw()) { ((ServoyMaskFormatter)editFormatter).setValueContainsLiteralCharacters(false); ((ServoyMaskFormatter)displayFormatter).setValueContainsLiteralCharacters(false); } if (fp.getAllowedCharacters() != null) { ((ServoyMaskFormatter)editFormatter).setValidCharacters(fp.getAllowedCharacters()); ((ServoyMaskFormatter)displayFormatter).setValidCharacters(fp.getAllowedCharacters()); } if (editFormat != null) { if (editFormat.length() == 1) { ((ServoyMaskFormatter)editFormatter).setPlaceholderCharacter(editFormat.charAt(0)); } else { ((ServoyMaskFormatter)editFormatter).setPlaceholder(editFormat); } } setFormatterFactory(new EditingFixedDefaultFormatterFactory(displayFormatter, displayFormatter, editFormatter)); // format overrules max length check editorDocument.setValidator(MAX_LENGTH_VALIDATOR, new LengthDocumentValidator(0)); break; } } catch (Exception ex) { Debug.error(ex); } } } } else //for text fields { TextFormatter display = new TextFormatter(); TextFormatter edit = new TextFormatter(); setFormatterFactory(new EditingFixedDefaultFormatterFactory(display, display, edit, edit)); } if (maxLength >= 0 && editorDocument.getValidator(MAX_LENGTH_VALIDATOR) == null) { editorDocument.setValidator(MAX_LENGTH_VALIDATOR, new LengthDocumentValidator(maxLength)); } } public void setComponentVisible(boolean b_visible) { if (viewable || !b_visible) { setVisible(b_visible); } } @Override public void setVisible(boolean flag) { super.setVisible(flag); if (labels != null) { for (int i = 0; i < labels.size(); i++) { ILabel label = labels.get(i); label.setComponentVisible(flag); } } } 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); 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; } /* * readonly--------------------------------------------------- */ public boolean isReadOnly() { return !isEditable(); } protected boolean editState; public void setReadOnly(boolean b) { if (b && !isEditable()) return; if (b) { setEditable(false); editState = true; } else { setEditable(editState); } } public int getAbsoluteFormLocationY() { Container parent = getParent(); while ((parent != null) && !(parent instanceof IDataRenderer)) { parent = parent.getParent(); } if (parent != null) { return ((IDataRenderer)parent).getYOffset() + getLocation().y; } return getLocation().y; } private Point cachedLocation; public Point getCachedLocation() { return cachedLocation; } public void setClientProperty(Object key, Object value) { if (IApplication.DATE_FORMATTERS_LENIENT.equals(key)) { AbstractFormatterFactory ff = getFormatterFactory(); if (ff instanceof DefaultFormatterFactory) { AbstractFormatter formatter = ((DefaultFormatterFactory)ff).getEditFormatter(); if (formatter instanceof NullDateFormatter) { ((NullDateFormatter)formatter).setLenient(Boolean.TRUE.equals(UIUtils.getUIProperty(this, IApplication.DATE_FORMATTERS_LENIENT, Boolean.TRUE))); } } } } /* * size--------------------------------------------------- */ private Dimension cachedSize; public Dimension getCachedSize() { return cachedSize; } public void setCachedLocation(Point location) { this.cachedLocation = location; } public void setCachedSize(Dimension size) { this.cachedSize = size; } // If component not shown or not added yet // and request focus is called it should wait for the component // to be created. boolean wantFocus = false; @Override public void addNotify() { super.addNotify(); if (wantFocus) { wantFocus = false; requestFocus(); } } /* * titleText--------------------------------------------------- */ private String titleText = null; public void setTitleText(String title) { this.titleText = title; } public String getTitleText() { return Text.processTags(titleText, resolver); } @Override public String getToolTipText() { if (resolver != null && tooltip != null) { String oldValue = tooltip; tooltip = null; super.setToolTipText(Text.processTags(oldValue, resolver)); tooltip = oldValue; } return super.getToolTipText(); } @Override public String getToolTipText(MouseEvent event) { return getToolTipText(); } @Override public void setToolTipText(String tip) { if (tip != null && tip.indexOf("%%") != -1) //$NON-NLS-1$ { tooltip = tip; } else if (!Utils.stringIsEmpty(tip)) { if (!Utils.stringContainsIgnoreCase(tip, "<html")) //$NON-NLS-1$ { super.setToolTipText(tip); } else if (HtmlUtils.hasUsefulHtmlContent(tip)) { super.setToolTipText(tip); } } else { super.setToolTipText(null); } } /* * _____________________________________________________________ Methods for event handling */ public void addScriptExecuter(IScriptExecuter el) { eventExecutor.setScriptExecuter(el); } public void setEnterCmds(String[] ids, Object[][] args) { eventExecutor.setEnterCmds(ids, args); } public void setLeaveCmds(String[] ids, Object[][] args) { eventExecutor.setLeaveCmds(ids, args); } @Override public Color getForeground() { if (isValueValid()) { return super.getForeground(); } return Color.red; } 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)); if (!valid) { application.invokeLater(new Runnable() { public void run() { requestFocus(); } }); } if (valid == isValueValid) { // if valid state does not change do nothing; // second call with valid=false should keep oldVal from first call return; } isValueValid = valid; repaint(); // foreground color changes if (isValueValid) { previousValidValue = null; } else { previousValidValue = oldVal; } } public void restorePreviousValidValue() { if (!isValueValid) { setValueObject(previousValidValue); setValueValid(true, null); if (editProvider != null) editProvider.commitData(); } } public void notifyLastNewValueWasChange(Object oldVal, Object newVal) { if (previousValidValue != null) { oldVal = previousValidValue; } eventExecutor.fireChangeCommand(oldVal, newVal, false, this); } public void setChangeCmd(String id, Object[] args) { eventExecutor.setChangeCmd(id, args); } public void setActionCmd(String id, Object[] args) { eventExecutor.setActionCmd(id, args); } public void setRightClickCommand(String rightClickCmd, Object[] args) { eventExecutor.setRightClickCmd(rightClickCmd, args); if (rightClickCmd != null && rightclickMouseAdapter == null) { rightclickMouseAdapter = new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { if (e.isPopupTrigger()) handle(e); } @Override public void mouseReleased(MouseEvent e) { if (e.isPopupTrigger()) handle(e); } private void handle(MouseEvent e) { if (isEnabled()) { eventExecutor.fireRightclickCommand(true, DataField.this, e.getModifiers(), e.getPoint()); } } }; addMouseListener(rightclickMouseAdapter); } } protected boolean wasEditable; public void setValidationEnabled(boolean b) { if (eventExecutor.getValidationEnabled() == b) return; if (dataProviderID != null && ScopesUtils.isVariableScope(dataProviderID)) return; eventExecutor.setValidationEnabled(b); boolean prevEditState = editState; if (b) { setEditable(wasEditable); setDocument(editorDocument); if (editProvider != null) { editProvider.setAdjusting(true); } try { setValue(null);//prevent errors } finally { if (editProvider != null) { editProvider.setAdjusting(false); } } setFormatterFactory(saveFormatter); } else { wasEditable = isEditable(); if (!Boolean.TRUE.equals(application.getClientProperty(IApplication.LEAVE_FIELDS_READONLY_IN_FIND_MODE))) { setEditable(true); } setDocument(plainDocument); saveFormatter = getFormatterFactory(); // create empty formatter setFormatterFactory(disabledFormatter); } editState = prevEditState; } public void setSelectOnEnter(boolean b) { eventExecutor.setSelectOnEnter(b); } //_____________________________________________________________ public void requestFocusToComponent() { // if (!hasFocus()) Don't test on hasFocus (it can have focus,but other component already did requestFocus) { if (isDisplayable()) { // Must do it in a runnable or else others after a script can get focus first again.. application.invokeLater(new Runnable() { public void run() { requestFocus(); } }); } else { wantFocus = true; } } } public IValueList getValueList() { return list; } public void setValueList(IValueList vl) { this.list = vl; } public ListDataListener getListener() { return null; } /** * @see javax.swing.JFormattedTextField#commitEdit() */ @Override public void commitEdit() throws ParseException { parseErrorOccurred = false; AbstractFormatterFactory factory = getFormatterFactory(); if (factory instanceof DefaultFormatterFactory) { AbstractFormatter formatter = ((DefaultFormatterFactory)factory).getEditFormatter(); if (formatter != null && formatter != getFormatter()) { // only a editFormatter can commit an edit. return; } } try { super.commitEdit(); } catch (ParseException e) { parseErrorOccurred = true; setValueValid(false, getValueObject()); throw e; } } /** * @see javax.swing.JComponent#processKeyBinding(javax.swing.KeyStroke,java.awt.event.KeyEvent, int, boolean) */ @Override protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) { if (!hasFocus()) { // FIX for tableview (JTable) that the field doesn't already have focus before it get's the first keyevent if (getFormatterFactory() instanceof DefaultFormatterFactory && ((DefaultFormatterFactory)getFormatterFactory()).getEditFormatter() != null) { DefaultFormatterFactory dff = (DefaultFormatterFactory)getFormatterFactory(); setFormatter(dff.getEditFormatter()); } setCaretPosition(getText().length()); selectAll(); eventExecutor.skipSelectOnEnter(); } if (e.getKeyCode() == KeyEvent.VK_DECIMAL && isEditable()) { if (e.getID() == KeyEvent.KEY_PRESSED) { decimalMode = true; } else { decimalMode = false; } } else if (decimalMode) { if (e.getID() == KeyEvent.KEY_TYPED) { replaceSelection(decimalSeparator); return true; } } else { decimalMode = false; AbstractFormatterFactory formatterFactory = getFormatterFactory(); DefaultFormatter formatter = null; if (formatterFactory instanceof DefaultFormatterFactory) { DefaultFormatterFactory factory = (DefaultFormatterFactory)formatterFactory; AbstractFormatter editFormatter = factory.getEditFormatter(); if (editFormatter == null) editFormatter = factory.getDefaultFormatter(); if (editFormatter instanceof DefaultFormatter && !(editFormatter instanceof MaskFormatter)) { formatter = (DefaultFormatter)editFormatter; } } if (Column.mapToDefaultType(dataType) == IColumnTypes.DATETIME && formatter != null && formatter.getOverwriteMode() && isEditable()) { String selected = getSelectedText(); if (selected == null || !getText().equals(selected)) { if (e.getKeyCode() == KeyEvent.VK_DELETE) { if (e.getID() == KeyEvent.KEY_RELEASED) { Caret c = getCaret(); if (c.getDot() >= 0 && getText().length() > (c.getDot())) { int dot = c.getDot(); setSelectionStart(c.getDot()); setSelectionEnd(c.getDot() + 1); replaceSelection(" "); //$NON-NLS-1$ c.setDot(dot); } } return true; } else if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE) { if (e.getID() == KeyEvent.KEY_RELEASED) { Caret c = getCaret(); if (c.getDot() > 0) { int dot = c.getDot(); setSelectionStart(c.getDot() - 1); setSelectionEnd(c.getDot()); replaceSelection(" "); //$NON-NLS-1$ c.setDot(dot - 1); return true; } e.setKeyCode(KeyEvent.VK_LEFT); e.setKeyChar(KeyEvent.CHAR_UNDEFINED); ks = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, ks.getModifiers(), ks.isOnKeyRelease()); } return true; } } } if (e.getKeyCode() == KeyEvent.VK_INSERT && formatter != null) { if (e.getID() == KeyEvent.KEY_PRESSED && toggleOverwrite) { formatter.setOverwriteMode(!formatter.getOverwriteMode()); toggleOverwrite = false; ((ISmartClientApplication)application).updateInsertModeIcon(this); int caretPos = getCaretPosition(); if (formatter.getOverwriteMode()) { setCaret(getOvertypeCaret()); } else { setCaret(getDefaultCaret()); } setCaretPosition(caretPos); } else if (e.getID() == KeyEvent.KEY_RELEASED) { toggleOverwrite = true; } } return super.processKeyBinding(ks, e, condition, pressed); } return false; } @Override public void setFormatter(AbstractFormatter format) { super.setFormatter(format); } @Override public String toString() { return scriptable.toString(); } public boolean stopUIEditing(boolean looseFocus) { try { commitEdit(); } catch (ParseException e) { return false; } if (!isValueValid) { application.invokeLater(new Runnable() { public void run() { requestFocus(); } }); return false; } if (looseFocus && canLooseFocus() && eventExecutor.mustFireFocusLostCommand()) { eventExecutor.skipNextFocusLost(); eventExecutor.fireLeaveCommands(this, false, IEventExecutor.MODIFIERS_UNSPECIFIED); } return true; } protected boolean canLooseFocus() { return true; } public String getId() { return (String)getClientProperty("Id"); //$NON-NLS-1$ } private Color opaqueBackground; private static final Color TRANSPARENT_BACKGROUND = new Color(0, 0, 0, 0); // work around transparency problem on MacOS Leopard // http://lists.apple.com/archives/java-dev/2007/Nov/msg00253.html @Override public void setOpaque(boolean opaque) { if (opaque != isOpaque()) { if (opaque) { super.setBackground(opaqueBackground); } else if (opaqueBackground != null) { opaqueBackground = getBackground(); super.setBackground(TRANSPARENT_BACKGROUND); } } super.setOpaque(opaque); } // work around transparency problem in MacOS Leopard // http://lists.apple.com/archives/java-dev/2007/Nov/msg00253.html @Override public void setBackground(Color color) { if (isOpaque()) { super.setBackground(color); } else { opaqueBackground = color; } } private boolean isIgnoreOnRender; public void setIgnoreOnRender(boolean isIgnoreOnRender) { this.isIgnoreOnRender = isIgnoreOnRender; } private TransferHandler textTransferHandler; /* * @see com.servoy.j2db.dnd.ISupportTextTransfer#clearTransferHandler() */ public void clearTransferHandler() { textTransferHandler = getTransferHandler(); setTransferHandler(null); } public TransferHandler getTextTransferHandler() { return textTransferHandler; } @Override public void copy() { if (textTransferHandler != null) { Action copyAction = FormDataTransferHandler.getCopyFormDataAction(); copyAction.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, (String)copyAction.getValue(Action.NAME), EventQueue.getMostRecentEventTime(), getCurrentEventModifiers())); } else super.copy(); } @Override public void cut() { if (textTransferHandler != null && isEditable() && isEnabled()) { Action cutAction = FormDataTransferHandler.getCutFormDataAction(); cutAction.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, (String)cutAction.getValue(Action.NAME), EventQueue.getMostRecentEventTime(), getCurrentEventModifiers())); } else super.cut(); } @Override public void paste() { if (textTransferHandler != null && isEditable() && isEnabled()) { Action pasteAction = FormDataTransferHandler.getPasteFormDataAction(); pasteAction.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, (String)pasteAction.getValue(Action.NAME), EventQueue.getMostRecentEventTime(), getCurrentEventModifiers())); } else super.paste(); } private int getCurrentEventModifiers() { int modifiers = 0; AWTEvent currentEvent = EventQueue.getCurrentEvent(); if (currentEvent instanceof InputEvent) { modifiers = ((InputEvent)currentEvent).getModifiers(); } else if (currentEvent instanceof ActionEvent) { modifiers = ((ActionEvent)currentEvent).getModifiers(); } return modifiers; } @Override public void setPlaceholderText(String text) { PromptSupport.uninstall(this); PromptSupport.setPrompt(application.getI18NMessageIfPrefixed(text), this); repaint(); } }