All Downloads are FREE. Search and download functionalities are using the official Maven repository.

xdev.ui.XdevPicture Maven / Gradle / Ivy

/*
 * XDEV Application Framework - XDEV Application Framework
 * Copyright © 2003 XDEV Software (https://xdev.software)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 */
package xdev.ui;


import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.beans.Beans;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

import xdev.db.Operator;
import xdev.io.ByteHolder;
import xdev.util.ObjectUtils;
import xdev.util.logging.LoggerFactory;
import xdev.util.logging.XdevLogger;
import xdev.vt.VirtualTable;


/**
 * 
 * The standard component of a picture in XDEV.
 * 
 * 

* Provides methods to load and change pictures. *

* *

* Supported image formats: see * {@link ImageFileFilter#getSupportedImageExtensions()} *

* * @see XdevComponent * @see FormularComponent * * @author XDEV Software * * @since 2.0 * */ @BeanSettings(acceptChildren = true, useXdevCustomizer = true) public class XdevPicture extends XdevComponent implements ImageFormularComponent { /** * Logger instance for this class. */ private static final XdevLogger log = LoggerFactory .getLogger(XdevPicture.class); public final static String RESIZE_MODE_PROPERTY = "resizeMode"; public final static String AUTO_RESIZE_PROPERTY = "autoResize"; public final static String OUTPUT_FORMAT_PROPERTY = "outputFormat"; public final static String IMAGE_PROPERTY = "image"; public final static int RESIZE_STRETCH = 0; public final static int RESIZE_FIT = 1; private int resizeMode = RESIZE_STRETCH; private boolean autoResize = false; private String outputFormat = "png"; private String imagePath; private Image image; private Image resizedImage; private Image savedValue = null; private boolean valid = false; private Dimension lastResizeCheck = new Dimension(); private final FormularComponentSupport support = new FormularComponentSupport( this); /** * Creates an empty {@link XdevPicture}. * * * @see #XdevPicture(String, boolean) */ public XdevPicture() { this(null,false); } /** * Creates an {@link XdevPicture} and load the image from the * imagePath immediately. * * @param imagePath * the path of the image * * @see #XdevPicture(String, boolean) */ public XdevPicture(String imagePath) { this(imagePath,true); } /** * Creates an {@link XdevPicture} with the given imagePath. * * @param imagePath * the path of the image * * @param loadImmediately * true if the picture is loaded immediately, * otherwise false * * @see XdevPicture#refresh(boolean, boolean) */ public XdevPicture(String imagePath, boolean loadImmediately) { super(); this.imagePath = imagePath; if(loadImmediately) { refresh(false,false); } } /** * Creates an {@link XdevPicture} and load the image. The * {@link XdevPicture} is not resizeable. * * @param image * the {@link XdevImage} * * @see #XdevPicture(Image) */ public XdevPicture(XdevImage image) { this(image.getAWTImage()); } /** * Creates an {@link XdevPicture} and load the image. The * {@link XdevPicture} is not resizeable. * * @param image * the image * * @see #XdevPicture(String, boolean) */ public XdevPicture(Image image) { super(); imageLoaded(image,false); } { addComponentListener(new ComponentAdapter() { Timer timer; { timer = new Timer(100,new ActionListener() { @Override public void actionPerformed(ActionEvent e) { checkImageSize(); } }); timer.setRepeats(false); } @Override public void componentResized(ComponentEvent e) { timer.restart(); } }); setOpaque(false); } /* * Opaque is always false to avoid artifacts */ /** * {@inheritDoc} */ @Override public boolean isOpaque() { return false; } @Override protected boolean paintComponent() { return (style != null && style.isOpaque()) || resizedImage != null; } /** * Sets the resize mode and refresh the {@link XdevPicture}. * * @param resizeMode * {@link #RESIZE_STRETCH} or {@link #RESIZE_FIT} * * @throws IllegalArgumentException * if the resizeMode is not supported * * @see #refresh() */ public void setResizeMode(int resizeMode) throws IllegalArgumentException { if(this.resizeMode != resizeMode) { switch(resizeMode) { case RESIZE_FIT: case RESIZE_STRETCH: break; default: throw new IllegalArgumentException("resizeMode"); } int oldValue = this.resizeMode; this.resizeMode = resizeMode; firePropertyChange(RESIZE_MODE_PROPERTY,oldValue,resizeMode); refresh(); } } /** * Returns the resize mode of this {@link XdevPicture}.
* Possible values are: * *
    *
  • {@link #RESIZE_FIT}
  • *
  • {@link #RESIZE_STRETCH}
  • *
* * @return the value of the resize mode as a int */ public int getResizeMode() { return resizeMode; } public void setAutoResize(boolean autoResize) { if(this.autoResize != autoResize) { boolean oldValue = this.autoResize; this.autoResize = autoResize; firePropertyChange(AUTO_RESIZE_PROPERTY,oldValue,autoResize); refresh(); } } /** * Returns true if this {@link XdevPicture} is auto resizable. * * @return true if this {@link XdevPicture} is auto resizable, * otherwise false * */ public boolean isAutoResize() { return autoResize; } /** * Sets the output format of this {@link XdevPicture}. * *

* Default supported formats are: JPG, PNG, GIF and BMP. *

* * @param outputFormat * a {@link String} with the new output format * * @throws IllegalArgumentException * if the outputFormat is null or not * supported * * @see ImageFileFilter#getSupportedImageExtensions() * @see ImageFileFilter#checkImageFormat(String) */ public void setOutputFormat(String outputFormat) throws IllegalArgumentException { if(outputFormat == null) { throw new IllegalArgumentException("output format cannot be null"); } outputFormat = ImageFileFilter.checkImageFormat(outputFormat); if(!this.outputFormat.equals(outputFormat)) { String oldValue = this.outputFormat; this.outputFormat = outputFormat; firePropertyChange(OUTPUT_FORMAT_PROPERTY,oldValue,outputFormat); } } /** * Returns the output format of this {@link XdevPicture}. (Standard output * format is PNG) * *

* Default supported formats are: JPG, PNG, GIF and BMP. *

* * @return the output format */ public String getOutputFormat() { return outputFormat; } /** * {@inheritDoc} */ @Override public XdevImage getImage() { if(image == null) { return null; } return new XdevImage(image); } /** * Returns the resized image, may be the same as {@link #getImage()} if the * component's size matches the size of the image. * * @return the resized image * @since 3.2 */ public XdevImage getResizedImage() { if(resizedImage == null) { return null; } return new XdevImage(resizedImage); } /** * {@inheritDoc} */ @Override public void setImage(XdevImage image) { setImage(image,autoResize); } public void setImage(XdevImage image, boolean resize) { setImage(image == null ? null : image.getAWTImage(),resize); } public void setImage(Image image) { setImage(image,autoResize); } public void setImage(final Image image, final boolean resize) { flush(); if(image == null) { return; } Runnable runnable = new Runnable() { @Override public void run() { imageLoaded(image,resize); } }; getExecutor().submit(runnable); } /** * Sets the path of the image and load the picture. * *

* This is an alias for {@link #setImage(String, boolean)}. *

* * @param imagePath * the image path */ public void setImage(String imagePath) { setImage(imagePath,autoResize); } /** * Sets the path of the image and loads the picture. * * @param imagePath * the path of the image * * @param resize * true to adjust the component's size * * * @see #refresh(boolean) */ public void setImage(String imagePath, boolean resize) { this.imagePath = imagePath; refresh(resize); } /** * Returns the image path of this {@link XdevPicture}. * * @return The string form of the image path */ public String getImagePath() { return imagePath; } /** * Sets the path of the image. * * @param path * the image path */ public void setImagePath(String path) { setImage(path); } /** * */ public void loadImage(InputStream in) throws IOException { loadImage(in,autoResize); } public void loadImage(InputStream in, boolean resize) throws IOException { Image image = ImageIO.read(in); if(image != null) { setImage(image,resize); } } public void loadImage(File file) throws IOException { loadImage(file,autoResize); } public void loadImage(File file, boolean resize) throws IOException { Image image = ImageIO.read(file); if(image != null) { setImage(image,resize); } } public void loadImage(URL url) throws IOException { loadImage(url,autoResize); } public void loadImage(URL url, boolean resize) throws IOException { Image image = ImageIO.read(url); if(image != null) { setImage(image,resize); } } public void loadImage(byte[] data) { loadImage(data,autoResize); } public void loadImage(byte[] data, boolean resize) { Image image = null; try { image = ImageIO.read(new ByteArrayInputStream(data)); } catch(IOException e) { log.error(e); } if(image != null) { setImage(image,resize); } } public void loadImage(String path) { loadImage(path,autoResize); } public void loadImage(String path, boolean resize) { setImage(path,resize); } /** * Refresh this {@link XdevPicture}. * *

* This is an alias for {@link #refresh(boolean)} . *

*/ public void refresh() { refresh(autoResize); } /** * Refresh this {@link XdevPicture}. * *

* This is an alias for {@link #refresh(boolean, boolean)}. *

* * @param resize */ public void refresh(boolean resize) { refresh(resize,true); } private boolean refreshing = false; protected void refresh(final boolean resize, boolean inThread) { if(inThread && Beans.isDesignTime()) { inThread = false; } valid = true; final String imagePath = this.imagePath; if(imagePath == null || imagePath.length() == 0) { flush(); return; } if(refreshing) { return; } refreshing = true; final boolean _inThread = inThread; Runnable runnable = new Runnable() { @Override public void run() { try { flush(); if(imagePath != null && imagePath.length() > 0) { imageLoaded(loadImage(),resize); } } catch(Exception e) { log.error(e); } finally { refreshing = false; // if image is loaded after picture was already closed if(_inThread && !Beans.isDesignTime()) { SwingUtilities.invokeLater(new Runnable() { public void run() { if(!valid) { flush(); } } }); } } } }; if(inThread) { getExecutor().submit(runnable); } else { runnable.run(); } } protected Image loadImage() { try { Image image = getToolkit().createImage(imagePath.getBytes(VirtualTable.CHARSET)); MediaTracker mt = new MediaTracker(this); mt.addImage(image,0); mt.waitForID(0); if(image.getWidth(null) > 0 && image.getHeight(null) > 0) { return image; } } catch(Exception e) { } try { return GraphicUtils.loadImagePlain(imagePath,this); } catch(Exception e) { log.error(e); return null; } } /** * Sets a {@link Image} image to be displayed by this * {@link XdevPicture}. * * @param image * {@link Image} to be displayed * @param resize * true to adjust the component's size */ protected void imageLoaded(final Image image, final boolean resize) { if(SwingUtilities.isEventDispatchThread()) { imageLoadedImpl(image,resize); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { imageLoadedImpl(image,resize); } }); } } private void imageLoadedImpl(Image image, boolean resize) { if(resize && Beans.isDesignTime()) { resize = false; } Image oldImage = this.image; this.image = image; if(image != null) { if(resize) { checkAlphaUse(); Dimension d = new Dimension(image.getWidth(null),image.getHeight(null)); if(getParent() != null && getParent().getLayout() != null) { setPreferredSize(d); revalidate(); } else { setSize(d); } } else { checkImageSize(); } } else if(resize) { Dimension d = new Dimension(0,0); if(getParent() != null && getParent().getLayout() != null) { setPreferredSize(d); revalidate(); } else { setSize(d); } } forcePaint(); firePropertyChange(IMAGE_PROPERTY,oldImage,this.image); } private void checkImageSize() { boolean resize = false; int width = getWidth(); int height = getHeight(); if(image != null) { if(resizedImage == null) { resize = true; } else { int imageWidth = resizedImage.getWidth(this); int imageHeight = resizedImage.getHeight(this); if(resizeMode == RESIZE_FIT) { double wf = (double)width / (double)imageWidth; double hf = (double)height / (double)imageHeight; double f = Math.min(wf,hf); width = (int)Math.round((double)imageWidth * f); height = (int)Math.round((double)imageHeight * f); } if(imageWidth != width || imageHeight != height) { resize = true; } } } if(resize && (lastResizeCheck.width != width || lastResizeCheck.height != height)) { lastResizeCheck.setSize(width,height); getExecutor().submit(new Runnable() { @Override public void run() { resizedImage = resizeImage(image); repaint(); } }); } } protected Image resizeImage(Image image) { switch(resizeMode) { case RESIZE_STRETCH: { image = GraphicUtils.resize(image,getWidth(),getHeight()); } break; case RESIZE_FIT: { image = GraphicUtils.resizeProportional(image,getWidth(),getHeight()); } break; } return image; } public void clear() { imagePath = ""; refresh(autoResize,false); repaint(); } /** * {@inheritDoc} */ @Override protected void paintImage(Graphics2D g) { Dimension d = getSize(); paintBackground(g,0,0,d.width,d.height); drawTexture(g,0,0,d.width,d.height); if(image != null) { if(resizedImage != null) { g.drawImage(resizedImage,(d.width - resizedImage.getWidth(null)) / 2, (d.height - resizedImage.getHeight(null)) / 2,this); } } else if(!valid) { refresh(false); } } /** * {@inheritDoc} */ @Override public String getFormularName() { return support.getFormularName(); } /** * {@inheritDoc} * * @since 3.1 */ @Override public void setDataField(String dataField) { support.setDataField(dataField); } /** * {@inheritDoc} * * @since 3.1 */ @Override public String getDataField() { return support.getDataField(); } /** * {@inheritDoc} */ @Override @Deprecated public final void setFormularValue(VirtualTable vt, int col, Object value) { support.setFormularValue(vt,col,value); } /** * {@inheritDoc} * * @since 3.2 */ @Override public void setFormularValue(VirtualTable vt, Map record) { if(!support.hasDataField()) { return; } Object value = support.getSingleValue(vt,record); Image image = null; byte[] bytes = null; if(value instanceof byte[]) { bytes = (byte[])value; } else if(value instanceof ByteHolder) { bytes = ((ByteHolder)value).toByteArray(); } if(bytes != null) { try { image = ImageIO.read(new ByteArrayInputStream(bytes)); new ImageIcon(image); } catch(IOException e) { log.error(e); } } setImage(image); } /** * {@inheritDoc} */ @Override public Object getFormularValue() { if(image != null) { try { ByteArrayOutputStream out = new ByteArrayOutputStream(); ImageIO.write(GraphicUtils.createBufferedImage(image),outputFormat,out); return out.toByteArray(); } catch(IOException e) { // shouldn't happen log.error(e); } } return null; } /** * {@inheritDoc} */ @Override public void saveState() { savedValue = image; } /** * {@inheritDoc} */ @Override public void restoreState() { setImage(savedValue); } /** * {@inheritDoc} * * @since 3.1 */ @Override public boolean hasStateChanged() { return !ObjectUtils.equals(savedValue,image); } /** * {@inheritDoc} * * @since 3.1 */ @Override public void addValueChangeListener(final ValueChangeListener l) { addPropertyChangeListener(IMAGE_PROPERTY,new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { l.valueChanged(evt); } }); } /** * {@inheritDoc} */ @Override public boolean isMultiSelect() { return false; } /** * {@inheritDoc} */ @Override public boolean verify() { return support.verify(); } /** * {@inheritDoc} * * @since 3.1 */ @Override public void addValidator(Validator validator) { support.addValidator(validator); } /** * {@inheritDoc} * * @since 3.1 */ @Override public void removeValidator(Validator validator) { support.removeValidator(validator); } /** * {@inheritDoc} * * @since 3.1 */ @Override public Validator[] getValidators() { return support.getValidators(); } /** * {@inheritDoc} * * @since 3.1 */ @Override public void validateState() throws ValidationException { support.validateState(); } /** * {@inheritDoc} * * @since 3.1 */ @Override public void validateState(Validation validation) throws ValidationException { support.validateState(validation); } /** * {@inheritDoc} * * @since 3.1 */ @Override public void setFilterOperator(Operator filterOperator) { support.setFilterOperator(filterOperator); } /** * {@inheritDoc} * * @since 3.1 */ @Override public Operator getFilterOperator() { return support.getFilterOperator(); } /** * {@inheritDoc} * * @since 3.2 */ @Override public void setReadOnly(boolean readOnly) { support.setReadOnly(readOnly); } /** * {@inheritDoc} * * @since 3.2 */ @Override public boolean isReadOnly() { return support.isReadOnly(); } private void flush() { if(image != null) { GraphicUtils.flushImage(image); image = null; } if(resizedImage != null) { GraphicUtils.flushImage(resizedImage); resizedImage = null; } lastResizeCheck.setSize(0,0); if(isShowing()) { forcePaint(); } } private static ThreadPoolExecutor executor; private static ThreadPoolExecutor getExecutor() { if(executor == null) { executor = new ThreadPoolExecutor(0,5,0L,TimeUnit.MILLISECONDS, new LinkedBlockingQueue()) { @Override protected void beforeExecute(Thread t, Runnable r) { t.setName("XdevPicture.loader (" + t.getName() + ")"); t.setPriority(Thread.MIN_PRIORITY); } }; } return executor; } /** * {@inheritDoc} */ @Override public void removeNotify() { if(!Beans.isDesignTime()) { flush(); valid = false; } super.removeNotify(); } @Override protected void finalize() throws Throwable { // if(executor != null) // { // executor.shutdownNow(); // executor = null; // } flush(); } /** * {@inheritDoc} */ @BeanProperty(category = DefaultBeanCategories.MISC) @Override public void setForeground(Color fg) { super.setForeground(fg); } /** * {@inheritDoc} */ @BeanProperty(category = DefaultBeanCategories.MISC) @Override public void setFont(Font font) { super.setFont(font); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy