/* 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.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.Insets; import java.awt.Point; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.SwingUtilities; import javax.swing.TransferHandler; import javax.swing.text.Document; import com.servoy.base.util.ITagResolver; import com.servoy.j2db.IApplication; 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.IDisplayData; import com.servoy.j2db.dataprocessing.IEditListener; import com.servoy.j2db.dataprocessing.TagResolver; import com.servoy.j2db.gui.JDateChooser; import com.servoy.j2db.persistence.IColumnTypes; import com.servoy.j2db.ui.BaseEventExecutor; import com.servoy.j2db.ui.IDataRenderer; 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.ISupportOnRender; import com.servoy.j2db.ui.scripting.RuntimeDataCalendar; import com.servoy.j2db.util.Debug; import com.servoy.j2db.util.EnablePanel; import com.servoy.j2db.util.FormatParser; import com.servoy.j2db.util.IDelegate; import com.servoy.j2db.util.ISupplyFocusChildren; import com.servoy.j2db.util.ScopesUtils; /** * Runtime swing calendar field * @author jblok */ public class DataCalendar extends EnablePanel implements IFieldComponent, IDisplayData, ActionListener, IDelegate, ISupplyFocusChildren<Component>, ISupportCachedLocationAndSize, IFormattingComponent, ISupportOnRender { private final DataField enclosedComponent; private String dataProviderID; private final JButton showCal; private final IApplication application; private List<ILabel> labels; private MouseAdapter rightclickMouseAdapter = null; private final RuntimeDataCalendar scriptable; public DataCalendar(IApplication app, RuntimeDataCalendar scriptable) { this.application = app; setLayout(new BorderLayout()); enclosedComponent = new DataField(app, scriptable); enclosedComponent.setIgnoreOnRender(true); enclosedComponent.setBorder(BorderFactory.createEmptyBorder()); enclosedComponent.setOpaque(false); add(enclosedComponent, BorderLayout.CENTER); showCal = new AbstractScriptButton(app, null /* no scriptable */) { @Override public String toString() // super uses scriptable { return "show-calendar button for " + DataCalendar.this.toString(); } }; showCal.setText("..."); //$NON-NLS-1$ showCal.addActionListener(this); showCal.setPreferredSize(new Dimension(20, 15)); showCal.setRequestFocusEnabled(false); add(showCal, BorderLayout.EAST); setOpaque(true); setBackground(Color.white); setBorder(BorderFactory.createEtchedBorder()); this.scriptable = scriptable; } public final RuntimeDataCalendar getScriptObject() { return this.scriptable; } public Object getDelegate() { return enclosedComponent; } public Document getDocument() { return enclosedComponent.getDocument(); } /* * (non-Javadoc) * * @see java.awt.Component#setName(java.lang.String) */ @Override public void setName(String name) { super.setName(name); enclosedComponent.setName(name); } public void installFormat(ComponentFormat componentFormat) { // calendar field always works with dates (even if it is attached to a dataprovider of another type // - for example it could work with a text column that has a Date <-> String converter) ComponentFormat cf; if (componentFormat.parsedFormat.isEmpty()) { // use default locale short date/time format cf = new ComponentFormat(FormatParser.parseFormatProperty(new SimpleDateFormat().toPattern()), IColumnTypes.DATETIME, componentFormat.uiType); } else { cf = new ComponentFormat(componentFormat.parsedFormat, IColumnTypes.DATETIME, componentFormat.uiType); } enclosedComponent.installFormat(cf); } public void setMargin(Insets i) { enclosedComponent.setMargin(i); } public Insets getMargin() { return enclosedComponent.getMargin(); } @Override public void setFont(Font f) { super.setFont(f); if (enclosedComponent != null) enclosedComponent.setFont(f); } @Override public Font getFont() { if (enclosedComponent != null) { return enclosedComponent.getFont(); } return super.getFont(); } public void addScriptExecuter(IScriptExecuter el) { enclosedComponent.addScriptExecuter(el); } public IEventExecutor getEventExecutor() { return enclosedComponent != null ? enclosedComponent.getEventExecutor() : null; } public void setEnterCmds(String[] ids, Object[][] args) { enclosedComponent.setEnterCmds(ids, null); } public void setLeaveCmds(String[] ids, Object[][] args) { enclosedComponent.setLeaveCmds(ids, null); } public void setActionCmd(String id, Object[] args) { enclosedComponent.setActionCmd(id, args); } public void setRightClickCommand(String rightClickCmd, Object[] args) { enclosedComponent.setRightClickCommand(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()) { ((BaseEventExecutor)enclosedComponent.getEventExecutor()).fireRightclickCommand(true, enclosedComponent, e.getModifiers(), e.getPoint()); } } }; addMouseListener(rightclickMouseAdapter); } } public void notifyLastNewValueWasChange(Object oldVal, Object newVal) { enclosedComponent.notifyLastNewValueWasChange(oldVal, newVal); } public void setValueValid(boolean valid, Object oldVal) { enclosedComponent.setValueValid(valid, oldVal); } public boolean isValueValid() { return enclosedComponent.isValueValid(); } public void setChangeCmd(String id, Object[] args) { enclosedComponent.setChangeCmd(id, args); } public void setHorizontalAlignment(int a) { enclosedComponent.setHorizontalAlignment(a); } public void setMaxLength(int i) { //ignore } public void actionPerformed(ActionEvent e) { if (Boolean.TRUE.equals(application.getRuntimeProperties().get(IServiceProvider.RT_LASTFIELDVALIDATIONFAILED_FLAG)) && isValueValid()) { if (Debug.tracing()) { Debug.trace("Calendar not shown because a field is marked invalid"); //$NON-NLS-1$ } return; } JDateChooser chooser = (JDateChooser)((ISmartClientApplication)application).getWindow("JDateChooser"); //$NON-NLS-1$ Window windowParent = SwingUtilities.getWindowAncestor(this); if (chooser == null || SwingUtilities.getWindowAncestor(chooser) != windowParent) { if (chooser != null) { chooser.dispose(); chooser = null; ((ISmartClientApplication)application).registerWindow("JDateChooser", chooser); //$NON-NLS-1$ } String dateFormat = TagResolver.getFormatString(Date.class, application.getSettings()); if (windowParent instanceof JFrame) { chooser = new JDateChooser((JFrame)windowParent, application.getI18NMessage("servoy.dateChooser.selectDate"), dateFormat); //$NON-NLS-1$ } else if (windowParent instanceof JDialog) { chooser = new JDateChooser((JDialog)windowParent, application.getI18NMessage("servoy.dateChooser.selectDate"), dateFormat); //$NON-NLS-1$ } else { Debug.warn("Cannot create date chooser for parent container " + windowParent); } if (chooser != null) ((ISmartClientApplication)application).registerWindow("JDateChooser", chooser); //$NON-NLS-1$ } if (chooser != null) { enclosedComponent.requestFocus(); Object value = enclosedComponent.getValue(); if (value != null && value instanceof Date) { Calendar cal = chooser.getSelectedDate(); cal.setTime((Date)value); chooser.updateCalendar(cal); } else if (value == null) { Calendar cal = chooser.getSelectedDate(); cal.setTime(chooser.format(new Date(), enclosedComponent.getFormat())); chooser.updateCalendar(cal); } if (chooser.showDialog(enclosedComponent.getFormat()) == JDateChooser.ACCEPT_OPTION) { enclosedComponent.requestFocus(); Calendar selectedDate = chooser.getSelectedDate(); enclosedComponent.setValueExFromCalendar(selectedDate.getTime()); } } } public void addEditListener(IEditListener l) { if (enclosedComponent != null) enclosedComponent.addEditListener(l); } /* * (non-Javadoc) * * @see com.servoy.j2db.dataui.IFieldComponent#setToolTipText(java.lang.String) */ @Override public void setToolTipText(String tip) { if (enclosedComponent != null) enclosedComponent.setToolTipText(tip); if (tip == null || tip.indexOf("%%") == -1) //$NON-NLS-1$ { super.setToolTipText(tip); } } public void setValueObject(Object obj) { enclosedComponent.setValueObject(obj); fireOnRender(false); } public void fireOnRender(boolean force) { if (scriptable != null) { if (force) scriptable.getRenderEventExecutor().setRenderStateChanged(); scriptable.getRenderEventExecutor().fireOnRender(enclosedComponent.hasFocus()); } } public void setTagResolver(ITagResolver resolver) { enclosedComponent.setTagResolver(resolver); } public Object getValueObject() { return enclosedComponent.getValue(); } public boolean needEditListener() { return true; } public boolean needEntireState() { return enclosedComponent.needEntireState(); } public void setNeedEntireState(boolean b) { enclosedComponent.setNeedEntireState(b); } public String getDataProviderID() { return enclosedComponent.getDataProviderID(); } public void setDataProviderID(String id) { enclosedComponent.setDataProviderID(id); } public void setValidationEnabled(boolean b) { enclosedComponent.setValidationEnabled(b); if (dataProviderID != null && ScopesUtils.isVariableScope(dataProviderID)) return; if (b) { showCal.setEnabled(!readOnly && isEnabled()); } else if (!showCal.isEnabled() && isEnabled()) { if (!Boolean.TRUE.equals(application.getClientProperty(IApplication.LEAVE_FIELDS_READONLY_IN_FIND_MODE))) { boolean oldReadonly = readOnly; setReadOnly(false); readOnly = oldReadonly; } } } @Override public void setForeground(Color fg) { super.setForeground(fg); if (enclosedComponent != null) enclosedComponent.setForeground(fg); } 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 void setComponentEnabled(final boolean b) { if (accessible || !b) { super.setEnabled(b); if (b && readOnly) showCal.setEnabled(false); 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; } public boolean isReadOnly() { return !showCal.isEnabled() && !enclosedComponent.isEditable(); } private boolean fieldEditState = true; private boolean readOnly = false; @Override public void setReadOnly(boolean readOnly) { this.readOnly = readOnly; if (readOnly && !showCal.isEnabled()) return; if (readOnly) { enclosedComponent.setEditable(false); showCal.setEnabled(false); } else { enclosedComponent.setEditable(fieldEditState); showCal.setEnabled(isEnabled()); } } /* * editable--------------------------------------------------- */ public boolean isEditable() { return enclosedComponent.isEditable(); } public void setEditable(boolean b) { fieldEditState = b; enclosedComponent.setEditable(b); } /* * titleText--------------------------------------------------- */ public void setTitleText(String title) { if (enclosedComponent != null) enclosedComponent.setTitleText(title); } public String getTitleText() { if (enclosedComponent != null) { return enclosedComponent.getTitleText(); } return null; } 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; } private Dimension cachedSize; public Dimension getCachedSize() { return cachedSize; } public void setCachedLocation(Point location) { this.cachedLocation = location; } public void setCachedSize(Dimension size) { this.cachedSize = size; } public void requestFocusToComponent() { if (isDisplayable()) { enclosedComponent.requestFocusToComponent(); } else { wantFocus = true; } } // 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; enclosedComponent.requestFocus(); } } @Override public void requestFocus() { enclosedComponent.requestFocus(); } public Component[] getFocusChildren() { return new Component[] { enclosedComponent, showCal }; } @Override public String toString() { return scriptable.toString(); } /* * (non-Javadoc) * * @see com.servoy.j2db.dataprocessing.IDisplay#stopEditing() */ public boolean stopUIEditing(boolean looseFocus) { if (enclosedComponent != null) return enclosedComponent.stopUIEditing(looseFocus); return true; } /* * (non-Javadoc) * * @see com.servoy.j2db.dataui.IFieldComponent#setSelectOnEnter(boolean) */ public void setSelectOnEnter(boolean b) { if (enclosedComponent != null) enclosedComponent.setSelectOnEnter(b); } /** * @param adapter */ public void addActionListener(ActionListener adapter) { showCal.addActionListener(adapter); } public String getId() { return (String)getClientProperty("Id"); } @Override public void setTransferHandler(TransferHandler newHandler) { super.setTransferHandler(newHandler); enclosedComponent.setTransferHandler(newHandler); } public List<ILabel> getLabelsFor() { return labels; } }