/* 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.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.geom.AffineTransform; import java.io.ByteArrayOutputStream; import java.net.URL; import java.util.StringTokenizer; import javax.swing.GrayFilter; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.IconUIResource; import com.servoy.j2db.IApplication; import com.servoy.j2db.IScriptExecuter; import com.servoy.j2db.MediaURLStreamHandler; import com.servoy.j2db.component.ComponentFactory; import com.servoy.j2db.persistence.Media; import com.servoy.j2db.ui.IButton; import com.servoy.j2db.ui.IDataRenderer; import com.servoy.j2db.ui.IEventExecutor; import com.servoy.j2db.ui.ILabel; import com.servoy.j2db.ui.ISupportCachedLocationAndSize; import com.servoy.j2db.ui.scripting.AbstractRuntimeButton; import com.servoy.j2db.util.Debug; import com.servoy.j2db.util.HtmlUtils; import com.servoy.j2db.util.ISkinnable; import com.servoy.j2db.util.ImageLoader; import com.servoy.j2db.util.UIUtils; import com.servoy.j2db.util.Utils; import com.servoy.j2db.util.gui.JpegEncoder; import com.servoy.j2db.util.gui.MyImageIcon; /** * @author jcompagner */ public abstract class AbstractScriptButton extends JButton implements ISkinnable, IButton, ISupportCachedLocationAndSize { private static final long serialVersionUID = 1L; private int rotation; private int mediaOption; private boolean specialPaint = false; protected IApplication application; protected final AbstractRuntimeButton<IButton> scriptable; protected EventExecutor eventExecutor; private MouseAdapter doubleclickMouseAdapter; private MouseAdapter rightclickMouseAdapter; public AbstractScriptButton(IApplication app, AbstractRuntimeButton<IButton> scriptable) { this.scriptable = scriptable; application = app; // setContentAreaFilled(false); eventExecutor = new EventExecutor(this); } public final AbstractRuntimeButton<IButton> getScriptObject() { return scriptable; } /** * 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")); if (isPrinting) { Graphics g = (Graphics)application.getRuntimeProperties().get("printGraphics"); 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); } @Override public void setToolTipText(String tip) { 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); } } /* * does not work, when onAction is fired the repaint event is not yet posted public AbstractScriptButton() { addActionListener(new ActionListener() { public * void actionPerformed(ActionEvent e) { Utils.dispatchEvents(700); } }); } * * //We override this method becouse we want first added listners to be notified first! protected void fireActionPerformed(ActionEvent event) { // Guaranteed to * return a non-null array Object[] listeners = listenerList.getListenerList(); ActionEvent e = null; // Process the listeners first to last, notifying // those * that are interested in this event for (int i = 0 ; i < listeners.length ; i+=2) { if (listeners[i]==ActionListener.class) { // Lazily create the event: if (e * == null) { String actionCommand = event.getActionCommand(); if(actionCommand == null) { actionCommand = getActionCommand(); } e = new ActionEvent(this, * ActionEvent.ACTION_PERFORMED, actionCommand, event.getWhen(), event.getModifiers()); } ((ActionListener)listeners[i+1]).actionPerformed(e); } } } */ @Override public void setIcon(Icon icon) { Icon prevIcon = getIcon(); if (prevIcon instanceof MyImageIcon) { Icon rollOver = getRolloverIcon(); if (rollOver instanceof MyImageIcon) { ((MyImageIcon)prevIcon).removeImageIcon((MyImageIcon)rollOver); } } ComponentFactory.deregisterIcon(prevIcon); if (mediaOption != 1 && icon instanceof ImageIcon) { //do scaling cropping if needed icon = new MyImageIcon(application, this, (ImageIcon)icon, mediaOption); Icon rollOver = getRolloverIcon(); if (rollOver instanceof MyImageIcon) { ((MyImageIcon)icon).addImageIcon((MyImageIcon)rollOver); } } ComponentFactory.registerIcon(icon); super.setIcon(icon); } public int getMediaIcon() { return mediaId; } private int mediaId; public void setMediaIcon(int mediaId) { this.mediaId = mediaId; try { setIcon(ComponentFactory.loadIcon(application.getFlattenedSolution(), new Integer(mediaId))); Media media = application.getFlattenedSolution().getMedia(mediaId); if (media != null) text_url = MediaURLStreamHandler.MEDIA_URL_DEF + media.getName(); } catch (Exception ex) { Debug.error(ex); } } private IconUIResource disabledIcon; @Override public Icon getDisabledIcon() { if (disabledIcon != null) return disabledIcon; if (getIcon() instanceof MyImageIcon) { MyImageIcon icon = (MyImageIcon)getIcon(); icon.getIconWidth(); ImageIcon scaledIcon = icon.getScaledIcon(0, 0); if (scaledIcon != null) { disabledIcon = new IconUIResource(new ImageIcon(GrayFilter.createDisabledImage((scaledIcon).getImage()))); return disabledIcon; } } return super.getDisabledIcon(); } public void setIcon(byte[] data) { Icon prevIcon = getIcon(); if (prevIcon instanceof MyImageIcon) { Icon rollOver = getRolloverIcon(); if (rollOver instanceof MyImageIcon) { ((MyImageIcon)prevIcon).removeImageIcon((MyImageIcon)rollOver); } } ComponentFactory.deregisterIcon(prevIcon); Icon icon = null; // media option 1 == crop so no scaling if (mediaOption != 1 && data != null) { //do scaling cropping if needed icon = new MyImageIcon(application, this, data, mediaOption); Icon rollOver = getRolloverIcon(); if (rollOver instanceof MyImageIcon) { ((MyImageIcon)icon).addImageIcon((MyImageIcon)rollOver); } } else { icon = ImageLoader.getIcon(data, -1, -1, true); } ComponentFactory.registerIcon(icon); super.setIcon(icon); } /** * @see com.servoy.j2db.ui.ILabel#setRolloverIcon(int) */ public void setRolloverIcon(int rollOverMediaId) { try { setRolloverIcon(ComponentFactory.loadIcon(application.getFlattenedSolution(), new Integer(rollOverMediaId))); } catch (Exception ex) { Debug.error(ex); } } public void setRolloverIcon(byte[] data) { ComponentFactory.deregisterIcon(getRolloverIcon()); Icon rolloverIcon = null; // media option 1 == crop so no scaling if (mediaOption != 1 && data != null) { //do scaling cropping if needed rolloverIcon = new MyImageIcon(application, this, data, mediaOption); Icon icon = getIcon(); if (icon instanceof MyImageIcon) { ((MyImageIcon)icon).addImageIcon((MyImageIcon)rolloverIcon); } } else { rolloverIcon = ImageLoader.getIcon(data, -1, -1, true); } super.setRolloverIcon(rolloverIcon); } @Override public void paint(Graphics g) { if (rotation == 0) { super.paint(g); } else { AffineTransform at = ((Graphics2D)g).getTransform(); AffineTransform save = (AffineTransform)at.clone(); try { int w = getWidth(); int h = getHeight(); if (rotation >= 45 && rotation <= 135) { specialPaint = true; at.rotate(Math.toRadians(rotation), w / 2, w / 2); } else if (rotation >= 135 && rotation <= 225) { at.rotate(Math.toRadians(rotation), w / 2, h / 2); } else { specialPaint = true; at.rotate(Math.toRadians(rotation), h / 2, h / 2); } ((Graphics2D)g).setTransform(at); super.paint(g); } finally { specialPaint = false; ((Graphics2D)g).setTransform(save); // now paint the border doPaintBorder = true; paintBorder(g); doPaintBorder = false; } } } private boolean doPaintBorder; @Override public boolean isBorderPainted() { return (doPaintBorder ? true : super.isBorderPainted()); } @Override public int getWidth() { if (specialPaint) { return super.getHeight(); } else { return super.getWidth(); } } @Override public int getHeight() { if (specialPaint) { return super.getWidth(); } else { return super.getHeight(); } } @Override public void setUI(ComponentUI ui) { super.setUI(ui); } /** * @see com.servoy.j2db.ui.runtime.IRuntimeBaseLabel#setImageURL(java.lang.String) */ public void setImageURL(String text_url) { this.text_url = text_url; try { if (text_url == null) { setIcon((Icon)null); } else { URL url = new URL(text_url); setIcon(new ImageIcon(url)); } } catch (Exception e) { Debug.error(e); } } private String text_url; public String getImageURL() { return text_url; } public void setRolloverImageURL(String image_url) { this.rollover_url = image_url; try { if (image_url != null) { ImageIcon rolloverIcon = new ImageIcon(new URL(image_url)); if (mediaOption != 1) { super.setRolloverIcon(new MyImageIcon(application, this, rolloverIcon, mediaOption)); } else { super.setRolloverIcon(rolloverIcon); } } else setRolloverIcon((Icon)null); } catch (Exception e) { Debug.error(e); } } private String rollover_url; public String getRolloverImageURL() { return rollover_url; } public void setComponentEnabled(final boolean b) { if (accessible) { super.setEnabled(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; } // 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(); } } 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; } } } private int textTransformMode; 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; private ActionListener actionAdapter; public Dimension getCachedSize() { return cachedSize; } public void setCachedLocation(Point location) { this.cachedLocation = location; } public void setCachedSize(Dimension size) { this.cachedSize = size; } public byte[] getThumbnailJPGImage(int width, int height) { return getThumbnailJPGImage(this, width, height, getIcon()); } static byte[] getThumbnailJPGImage(Component component, int width, int height, Icon icon) { Image image = null; if (icon instanceof MyImageIcon) { ImageIcon myIcon = ((MyImageIcon)icon).getScaledIcon(width, height); image = myIcon.getImage(); } else if (icon instanceof ImageIcon) { image = ((ImageIcon)icon).getImage(); } if (image != null) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); JpegEncoder encoder = new JpegEncoder(component, image, 100, baos); encoder.compress(); return baos.toByteArray(); } return null; } @Override public String toString() { return scriptable.toString(); } /** * @return */ public int getRotation() { return rotation; } /** * @param i */ public void setRotation(int i) { rotation = i; if (i != 0) { //fix for bad painting setContentAreaFilled(false); setBorderPainted(false); // setFocusPainted(false); } } /** * @return Returns the mediaOption. */ public int getMediaOption() { return mediaOption; } /** * @param mediaOption The mediaOption to set. */ public void setMediaOption(int mediaOption) { this.mediaOption = mediaOption; } private Timer clickTimer; /** * @see com.servoy.j2db.ui.ILabel#setActionCommand(java.lang.String, Object[]) */ public void setActionCommand(String id, Object[] args) { eventExecutor.setActionCmd(id, args); if (id != null && actionAdapter == null) { actionAdapter = new ActionListener() { public void actionPerformed(final ActionEvent e) { if (doubleclickMouseAdapter != null) { clickTimer = new Timer(UIUtils.getClickInterval(), new ActionListener() { public void actionPerformed(ActionEvent ev) { eventExecutor.fireActionCommand(true, AbstractScriptButton.this, e.getModifiers()); } }); clickTimer.setRepeats(false); //after expiring once, stop the timer clickTimer.start(); } else { eventExecutor.fireActionCommand(true, AbstractScriptButton.this, e.getModifiers()); } } }; addActionListener(actionAdapter); } } /** * @see com.servoy.j2db.ui.ILabel#addScriptExecuter(com.servoy.j2db.IScriptExecuter) */ public void addScriptExecuter(IScriptExecuter el) { eventExecutor.setScriptExecuter(el); } public void setComponentVisible(boolean b_visible) { if (viewable) { setVisible(b_visible); } } public boolean isReadOnly() { return true; } /** * @see com.servoy.j2db.ui.ILabel#setTextTransform(int) */ public void setTextTransform(int mode) { this.textTransformMode = mode; } /** * @see javax.swing.JLabel#setText(java.lang.String) */ @Override public void setText(String text) { if (text != null && text.length() > 0) { if (textTransformMode == ILabel.CAPITALIZE) { StringBuffer sb = new StringBuffer(text.length()); StringTokenizer st = new StringTokenizer(text); while (st.hasMoreTokens()) { String word = st.nextToken(); sb.append(Character.toUpperCase(word.charAt(0))); sb.append(word.substring(1)); sb.append(" "); } text = sb.substring(0, sb.length() - 1); } else if (textTransformMode == ILabel.LOWERCASE) { text = text.toLowerCase(); } else if (textTransformMode == ILabel.UPPERCASE) { text = text.toUpperCase(); } } super.setText(text); } public String getId() { return (String)getClientProperty("Id"); } /** * @see com.servoy.j2db.ui.IStandardLabel#setDisplayedMnemonic(char) */ public void setDisplayedMnemonic(char mnemonic) { setMnemonic(mnemonic); } public void setDoubleClickCommand(String id, Object[] args) { eventExecutor.setDoubleClickCmd(id, args); if (id != null && doubleclickMouseAdapter == null) { doubleclickMouseAdapter = new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { // Don't allow double click with other buttons except left button. if ((e.getClickCount() == 2) && SwingUtilities.isLeftMouseButton(e) && isEnabled()) { if (clickTimer != null) clickTimer.stop(); eventExecutor.fireDoubleclickCommand(true, AbstractScriptButton.this, e.getModifiers(), e.getPoint()); } } }; addMouseListener(doubleclickMouseAdapter); setMultiClickThreshhold(UIUtils.getClickInterval()); } } public void setRightClickCommand(String id, Object[] args) { eventExecutor.setRightClickCmd(id, args); if (id != 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, AbstractScriptButton.this, e.getModifiers(), e.getPoint()); } } }; addMouseListener(rightclickMouseAdapter); } } public IEventExecutor getEventExecutor() { return eventExecutor; } public String getParameterValue(String param) { return null; } public int getFontSize() { return 0; } public Object getLabelFor() { return null; } public int getDisplayedMnemonic() { return getMnemonic(); } }