/* 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.Container; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Insets; import java.awt.Point; 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.util.ArrayList; import java.util.List; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.JPasswordField; import javax.swing.TransferHandler; import org.jdesktop.xswingx.PromptSupport; 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.dataprocessing.IDisplayData; import com.servoy.j2db.dataprocessing.IEditListener; import com.servoy.j2db.dataprocessing.TagResolver; import com.servoy.j2db.dnd.FormDataTransferHandler; import com.servoy.j2db.dnd.ISupportDragNDropTextTransfer; import com.servoy.j2db.ui.IDataRenderer; import com.servoy.j2db.ui.IEventExecutor; import com.servoy.j2db.ui.IFieldComponent; import com.servoy.j2db.ui.ILabel; import com.servoy.j2db.ui.ISupportCachedLocationAndSize; import com.servoy.j2db.ui.ISupportOnRender; import com.servoy.j2db.ui.ISupportPlaceholderText; import com.servoy.j2db.ui.scripting.RuntimeDataPassword; import com.servoy.j2db.util.HtmlUtils; import com.servoy.j2db.util.Text; import com.servoy.j2db.util.Utils; import com.servoy.j2db.util.docvalidator.LengthDocumentValidator; import com.servoy.j2db.util.docvalidator.ValidatingDocument; /** * Runtime swing password field * @author jblok */ public class DataPassword extends JPasswordField implements IFieldComponent, IDisplayData, ISupportCachedLocationAndSize, ISupportDragNDropTextTransfer, ISupportPlaceholderText, ISupportOnRender { private String dataProviderID; private final EventExecutor eventExecutor; private final IApplication application; private MouseAdapter rightclickMouseAdapter = null; private final RuntimeDataPassword scriptable; public DataPassword(IApplication app, RuntimeDataPassword scriptable) { super(); this.scriptable = scriptable; application = app; eventExecutor = new EventExecutor(this) { @Override public void fireLeaveCommands(Object display, boolean focusEvent, int modifiers) { if (hasLeaveCmds()) { editProvider.focusLost(new FocusEvent(DataPassword.this, FocusEvent.FOCUS_LOST)); } super.fireLeaveCommands(display, focusEvent, modifiers); } }; 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 && !Utils.equalObjects(previousValue, getValueObject())) { 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 && !Utils.equalObjects(previousValue, getValueObject())) { JPasswordField field = (JPasswordField)e.getSource(); field.setText((String)previousValue); e.consume(); } } }); addMouseListener(eventExecutor); addKeyListener(eventExecutor); } public final RuntimeDataPassword getScriptObject() { return scriptable; } // 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; } /* * _____________________________________________________________ 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 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, DataPassword.this, e.getModifiers(), e.getPoint()); } } }; addMouseListener(rightclickMouseAdapter); } } private boolean wasEditable; public void setValidationEnabled(boolean b) { if (eventExecutor.getValidationEnabled() == b) return; boolean prevEditState = editState; eventExecutor.setValidationEnabled(b); if (b) { setEditable(wasEditable); } else { wasEditable = isEditable(); if (!Boolean.TRUE.equals(application.getClientProperty(IApplication.LEAVE_FIELDS_READONLY_IN_FIND_MODE))) { setEditable(true); } } editState = prevEditState; } public void setSelectOnEnter(boolean b) { eventExecutor.setSelectOnEnter(b); } public boolean isValueValid() { return isValueValid; } private boolean isValueValid = true; private Object previousValidValue; public void setValueValid(boolean valid, Object oldVal) { application.getRuntimeProperties().put(IServiceProvider.RT_LASTFIELDVALIDATIONFAILED_FLAG, Boolean.valueOf(!valid)); isValueValid = valid; if (!isValueValid) { previousValidValue = oldVal; application.invokeLater(new Runnable() { public void run() { requestFocus(); } }); } else { previousValidValue = null; } } public void notifyLastNewValueWasChange(Object oldVal, Object newVal) { eventExecutor.fireChangeCommand(previousValidValue != null ? previousValidValue : oldVal, newVal, false, this); } //_____________________________________________________________ private Object previousValue; protected ITagResolver resolver; public void setTagResolver(ITagResolver resolver) { this.resolver = resolver; } public void setValueObject(Object o) { try { if (editProvider != null) editProvider.setAdjusting(true); if (needEntireState) { if (resolver != null) { if (tooltip != null) { super.setToolTipText(Text.processTags(tooltip, resolver)); } } else { if (tooltip != null) { super.setToolTipText(null); } } } if (!Utils.equalObjects(previousValue, o)) { previousValue = o; if (o != null) { setText(TagResolver.formatObject(o, application.getLocale(), application.getSettings())); } else { setText(""); //$NON-NLS-1$ } } } finally { if (editProvider != null) editProvider.setAdjusting(false); } fireOnRender(false); } public void fireOnRender(boolean force) { if (scriptable != null) { if (force) scriptable.getRenderEventExecutor().setRenderStateChanged(); scriptable.getRenderEventExecutor().fireOnRender(hasFocus()); } } public Object getValueObject() { return new String(getPassword()); } public boolean needEditListener() { return true; } private EditProvider editProvider = null; public void addEditListener(IEditListener l) { if (editProvider == null) { editProvider = new EditProvider(this, true); addFocusListener(editProvider); getDocument().addDocumentListener(editProvider); addActionListener(editProvider); editProvider.addEditListener(l); editProvider.setEditable(isEditable()); } } @Override public void setEditable(boolean b) { editState = b; super.setEditable(b); if (editProvider != null) editProvider.setEditable(b); } public String getDataProviderID() { return dataProviderID; } public void setDataProviderID(String dataProviderID) { this.dataProviderID = dataProviderID; } public boolean needEntireState() { return needEntireState; } private boolean needEntireState; private List<ILabel> labels; public void setNeedEntireState(boolean b) { needEntireState = b; } /** * @see com.servoy.j2db.ui.IFieldComponent#setMaxLength(int) */ public void setMaxLength(int i) { setDocument(new ValidatingDocument(new LengthDocumentValidator(i))); } 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 (ILabel label : labels) { 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 (ILabel label : labels) { 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(); } private boolean editState; public void setReadOnly(boolean b) { if (b && !isEditable()) return; if (b) { setEditable(false); editState = true; } else { setEditable(editState); } } /* * titleText--------------------------------------------------- */ private String titleText = null; public void setTitleText(String title) { this.titleText = title; } public String getTitleText() { return titleText; } private String tooltip; @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); } } 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 setCachedLocation(Point location) { this.cachedLocation = location; } public void setCachedSize(Dimension size) { this.cachedSize = size; } private Dimension cachedSize; public Dimension getCachedSize() { return cachedSize; } 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; } } } // 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(); } } @Override public String toString() { return scriptable.toString(); } public boolean stopUIEditing(boolean looseFocus) { if (editProvider != null) editProvider.forceCommit(); if (!isValueValid) { application.invokeLater(new Runnable() { public void run() { requestFocus(); } }); return false; } if (looseFocus && eventExecutor.mustFireFocusLostCommand()) { eventExecutor.skipNextFocusLost(); eventExecutor.fireLeaveCommands(this, false, IEventExecutor.MODIFIERS_UNSPECIFIED); } return true; } @Override public void setMargin(Insets m) { //super.setMargin(m); setBorder(BorderFactory.createCompoundBorder(getBorder(), BorderFactory.createEmptyBorder(m.top, m.left, m.bottom, m.right))); } public String getId() { return (String)getClientProperty("Id"); //$NON-NLS-1$ } 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(); } }