diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADWindow.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADWindow.java index 9487694679..0ead498d12 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADWindow.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADWindow.java @@ -17,7 +17,6 @@ package org.adempiere.webui.adwindow; -import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -35,9 +34,7 @@ import org.compiere.model.MToolBarButton; import org.compiere.model.MToolBarButtonRestrict; import org.compiere.model.MWindow; import org.compiere.model.X_AD_ToolBarButton; -import org.compiere.util.CCache; import org.compiere.util.Env; -import org.zkoss.image.AImage; import org.zkoss.zk.ui.Component; /** @@ -59,8 +56,6 @@ public class ADWindow extends AbstractUIPart private Component windowPanelComponent; private MImage image; - - private static final CCache imageCache = new CCache(null, "WindowImageCache", 5, false); private Map> tabToolbarRestricMap = new HashMap>(); @@ -129,23 +124,6 @@ public class ADWindow extends AbstractUIPart return image; } - public AImage getAImage() throws IOException { - MImage image = getMImage(); - AImage aImage = null; - if (image != null) { - synchronized (imageCache) { - aImage = imageCache.get(image.getAD_Image_ID()); - } - if (aImage == null) { - aImage = new AImage(image.getName(), image.getData()); - synchronized (imageCache) { - imageCache.put(image.getAD_Image_ID(), aImage); - } - } - } - return aImage; - } - protected Component doCreatePart(Component parent) { windowPanelComponent = windowContent.createPart(parent); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/Tab.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/Tab.java index a9877334b4..71fa41d1be 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/Tab.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/Tab.java @@ -17,6 +17,13 @@ package org.adempiere.webui.component; +import org.adempiere.webui.adwindow.ADWindow; +import org.adempiere.webui.util.ManageImageCache; +import org.compiere.model.MImage; +import org.compiere.model.MInfoWindow; +import org.zkoss.image.Image; +import org.zkoss.zul.impl.LabelImageElement; + /** * * @author Ashley G Ramdass @@ -40,6 +47,11 @@ public class Tab extends org.zkoss.zul.Tab } + public void setDecorateInfo (DecorateInfo decorateInfo){ + if (decorateInfo != null) + decorateInfo.decorate(this); + } + @Override public void onClose() { Tabpanel tp = (Tabpanel) getLinkedPanel(); @@ -48,4 +60,50 @@ public class Tab extends org.zkoss.zul.Tab } } + /** + * class contain decorate info + * at the moment, has only image info + * at the moment, it's use to transfer decorate info from info window, standard window, report, process,... to tab + * @author hieplq + * + */ + public static class DecorateInfo { + private String imageKey; + private String imageIntenalUrl; + + public void decorate (LabelImageElement comp){ + if (imageIntenalUrl != null) + comp.setImage(imageIntenalUrl); + else if (imageKey != null){ + Image ico = ManageImageCache.instance().getImage(imageKey); + if (ico != null) + comp.setImageContent(ico); + } + } + + public DecorateInfo (MImage imageData){ + imageIntenalUrl = ManageImageCache.getImageInternalUrl(imageData); + if (imageIntenalUrl == null) + imageKey = ManageImageCache.instance().loadImage(imageData); + } + + public DecorateInfo (String imagePath){ + imageIntenalUrl = ManageImageCache.getImageInternalUrl(imagePath); + if (imageIntenalUrl == null) + imageKey = imagePath; + } + + /** + * Helper method for create decorate info from adWindow info + * @param adWindow + * @return + */ + public static DecorateInfo get (ADWindow adWindow){ + return adWindow == null?null:new DecorateInfo(adWindow.getMImage()); + } + + public static DecorateInfo get (MInfoWindow mInfo){ + return mInfo==null?null:new DecorateInfo(mInfo.getImageURL()); + } + } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java index e63b9023d3..85975d2d5c 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java @@ -243,7 +243,7 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria windowContainer.createPart(windowArea); homeTab = new Tabpanel(); - windowContainer.addWindow(homeTab, Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Home")), false); + windowContainer.addWindow(homeTab, Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Home")), false, null); homeTab.getLinkedTab().setSclass("desktop-hometab"); homeTab.setSclass("desktop-home-tabpanel"); BusyDialog busyDialog = new BusyDialog(); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/TabbedDesktop.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/TabbedDesktop.java index 5c78ed9b5d..72cd179aa1 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/TabbedDesktop.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/TabbedDesktop.java @@ -13,7 +13,6 @@ *****************************************************************************/ package org.adempiere.webui.desktop; -import java.io.IOException; import java.util.List; import org.adempiere.util.Callback; @@ -22,6 +21,7 @@ import org.adempiere.webui.adwindow.ADWindow; import org.adempiere.webui.apps.ProcessDialog; import org.adempiere.webui.apps.wf.WFPanel; import org.adempiere.webui.component.DesktopTabpanel; +import org.adempiere.webui.component.Tab.DecorateInfo; import org.adempiere.webui.component.Tabbox; import org.adempiere.webui.component.Tabpanel; import org.adempiere.webui.component.Window; @@ -32,11 +32,11 @@ import org.adempiere.webui.panel.InfoPanel; import org.adempiere.webui.part.WindowContainer; import org.adempiere.webui.window.FDialog; import org.adempiere.webui.window.WTask; +import org.compiere.model.MInfoWindow; import org.compiere.model.MQuery; import org.compiere.model.MTask; import org.compiere.util.Env; import org.compiere.wf.MWorkflow; -import org.zkoss.image.AImage; import org.zkoss.util.media.AMedia; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.event.Event; @@ -73,7 +73,7 @@ public abstract class TabbedDesktop extends AbstractDesktop { String title = pd.getTitle(); pd.setTitle(null); preOpenNewTab(); - windowContainer.addWindow(tabPanel, title, true); + windowContainer.addWindow(tabPanel, title, true, null); Events.postEvent(ProcessDialog.ON_INITIAL_FOCUS_EVENT, pd, null); } return pd; @@ -93,7 +93,7 @@ public abstract class TabbedDesktop extends AbstractDesktop { //do not show window title when open as tab form.setTitle(null); preOpenNewTab(); - windowContainer.addWindow(tabPanel, form.getFormName(), true); + windowContainer.addWindow(tabPanel, form.getFormName(), true, null); form.focus(); } else { form.setAttribute(Window.MODE_KEY, form.getWindowMode()); @@ -117,7 +117,7 @@ public abstract class TabbedDesktop extends AbstractDesktop { String title = infoPanel.getTitle(); infoPanel.setTitle(null); preOpenNewTab(); - windowContainer.addWindow(tabPanel, title, true); + windowContainer.addWindow(tabPanel, title, true, DecorateInfo.get(MInfoWindow.get(infoId, null))); infoPanel.focus(); } else { FDialog.error(0, "NotValid"); @@ -135,7 +135,7 @@ public abstract class TabbedDesktop extends AbstractDesktop { DesktopTabpanel tabPanel = new DesktopTabpanel(); p.setParent(tabPanel); preOpenNewTab(); - windowContainer.addWindow(tabPanel, p.getWorkflow().get_Translation(MWorkflow.COLUMNNAME_Name), true); + windowContainer.addWindow(tabPanel, p.getWorkflow().get_Translation(MWorkflow.COLUMNNAME_Name), true, null); } /** @@ -160,7 +160,8 @@ public abstract class TabbedDesktop extends AbstractDesktop { final DesktopTabpanel tabPanel = new DesktopTabpanel(); String id = AdempiereIdGenerator.escapeId(adWindow.getTitle()); tabPanel.setId(id+"_"+adWindow.getADWindowContent().getWindowNo()); - final Tab tab = windowContainer.addWindow(tabPanel, adWindow.getTitle(), true); + final Tab tab = windowContainer.addWindow(tabPanel, adWindow.getTitle(), true, DecorateInfo.get(adWindow)); + tab.setClosable(false); final OpenWindowRunnable runnable = new OpenWindowRunnable(adWindow, tab, tabPanel, callback); preOpenNewTab(); @@ -233,7 +234,7 @@ public abstract class TabbedDesktop extends AbstractDesktop { Tabpanel tabPanel = new Tabpanel(); window.setParent(tabPanel); preOpenNewTab(); - windowContainer.addWindow(tabPanel, title, closeable); + windowContainer.addWindow(tabPanel, title, closeable, null); } /** @@ -245,7 +246,7 @@ public abstract class TabbedDesktop extends AbstractDesktop { final ADWindow wnd = new ADWindow(Env.getCtx(), AD_Window_ID, query); final DesktopTabpanel tabPanel = new DesktopTabpanel(); - final Tab tab = windowContainer.insertAfter(windowContainer.getSelectedTab(), tabPanel, wnd.getTitle(), true, true); + final Tab tab = windowContainer.insertAfter(windowContainer.getSelectedTab(), tabPanel, wnd.getTitle(), true, true, DecorateInfo.get(wnd)); tab.setClosable(false); final OpenWindowRunnable runnable = new OpenWindowRunnable(wnd, tab, tabPanel, null); preOpenNewTab(); @@ -277,9 +278,9 @@ public abstract class TabbedDesktop extends AbstractDesktop { window.setTitle(null); preOpenNewTab(); if (Window.INSERT_NEXT.equals(window.getAttribute(Window.INSERT_POSITION_KEY))) - windowContainer.insertAfter(windowContainer.getSelectedTab(), tabPanel, title, true, true); + windowContainer.insertAfter(windowContainer.getSelectedTab(), tabPanel, title, true, true, null); else - windowContainer.addWindow(tabPanel, title, true); + windowContainer.addWindow(tabPanel, title, true, null); if (window instanceof IHelpContext) Events.sendEvent(new Event(WindowContainer.ON_WINDOW_CONTAINER_SELECTION_CHANGED_EVENT, window)); } @@ -381,13 +382,6 @@ public abstract class TabbedDesktop extends AbstractDesktop { public void run() { if (adWindow.createPart(tabPanel) != null ) { tab.setClosable(true); - if (adWindow.getMImage() != null) { - try { - AImage aImage = adWindow.getAImage(); - tab.setImageContent(aImage); - } catch (IOException e) { - } - } if (callback != null) { callback.onCallback(adWindow); } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/part/WindowContainer.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/part/WindowContainer.java index fdeaa1a913..44dc00e36d 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/part/WindowContainer.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/part/WindowContainer.java @@ -17,6 +17,7 @@ import java.util.List; import org.adempiere.webui.component.Menupopup; import org.adempiere.webui.component.Tab; +import org.adempiere.webui.component.Tab.DecorateInfo; import org.adempiere.webui.component.Tabbox; import org.adempiere.webui.component.Tabpanel; import org.adempiere.webui.component.Tabpanels; @@ -101,15 +102,63 @@ public class WindowContainer extends AbstractUIPart return tabbox; } + /** + * @deprecated keep for compatible, replace by {@link #addWindow(Component, String, boolean, DecorateInfo)} + * @param comp + * @param title + * @param closeable + * @return + */ + public Tab addWindow(Component comp, String title, boolean closeable){ + return addWindow(comp, title, closeable, true, null); + } + + /** + * @deprecated keep for compatible, replace by {@link #addWindow(Component, String, boolean, boolean, DecorateInfo)} + * @param comp + * @param title + * @param closeable + * @param enable + * @return + */ + public Tab addWindow(Component comp, String title, boolean closeable, boolean enable) { + return addWindow(comp, title, closeable, true, null); + } + + /** + * @deprecated keep for compatible, replace by {@link #insertBefore(Tab, Component, String, boolean, boolean, DecorateInfo)} + * @param refTab + * @param comp + * @param title + * @param closeable + * @param enable + * @return + */ + public Tab insertBefore(Tab refTab, Component comp, String title, boolean closeable, boolean enable){ + return insertBefore(refTab, comp, title, closeable, enable, null); + } + + /** + * @deprecated keep for compatible, replace by {@link #insertAfter(Component, String, boolean, boolean, DecorateInfo)} + * @param refTab + * @param comp + * @param title + * @param closeable + * @param enable + * @return + */ + public Tab insertAfter(Tab refTab, Component comp, String title, boolean closeable, boolean enable){ + return insertAfter(refTab, comp, title, closeable, enable, null); + } /** * * @param comp * @param title * @param closeable */ - public Tab addWindow(Component comp, String title, boolean closeable) + public Tab addWindow(Component comp, String title, boolean closeable, DecorateInfo decorateInfo) { - return addWindow(comp, title, closeable, true); + return addWindow(comp, title, closeable, true, decorateInfo); } /** @@ -119,9 +168,9 @@ public class WindowContainer extends AbstractUIPart * @param closeable * @param enable */ - public Tab addWindow(Component comp, String title, boolean closeable, boolean enable) + public Tab addWindow(Component comp, String title, boolean closeable, boolean enable, DecorateInfo decorateInfo) { - return insertBefore(null, comp, title, closeable, enable); + return insertBefore(null, comp, title, closeable, enable, decorateInfo); } /** @@ -132,9 +181,10 @@ public class WindowContainer extends AbstractUIPart * @param closeable * @param enable */ - public Tab insertBefore(Tab refTab, Component comp, String title, boolean closeable, boolean enable) + public Tab insertBefore(Tab refTab, Component comp, String title, boolean closeable, boolean enable, DecorateInfo decorateInfo) { final Tab tab = new Tab(); + tab.setDecorateInfo(decorateInfo); if (title != null) { setTabTitle(title, tab); @@ -318,12 +368,12 @@ public class WindowContainer extends AbstractUIPart * @param closeable * @param enable */ - public Tab insertAfter(Tab refTab, Component comp, String title, boolean closeable, boolean enable) + public Tab insertAfter(Tab refTab, Component comp, String title, boolean closeable, boolean enable, DecorateInfo decorateInfo) { if (refTab == null) - return addWindow(comp, title, closeable, enable); + return addWindow(comp, title, closeable, enable, decorateInfo); else - return insertBefore((Tab)refTab.getNextSibling(), comp, title, closeable, enable); + return insertBefore((Tab)refTab.getNextSibling(), comp, title, closeable, enable, decorateInfo); } /** diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/util/ManageImageCache.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/util/ManageImageCache.java new file mode 100644 index 0000000000..7ec4bcf0a3 --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/util/ManageImageCache.java @@ -0,0 +1,223 @@ +/********************************************************************** +* This file is part of iDempiere ERP Open Source * +* http://www.idempiere.org * +* * +* Copyright (C) Contributors * +* * +* This program is free software; you can redistribute it and/or * +* modify it under the terms of the GNU General Public License * +* as published by the Free Software Foundation; either version 2 * +* 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 General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the Free Software * +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * +* MA 02110-1301, USA. * +**********************************************************************/ + +package org.adempiere.webui.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.logging.Level; + +import org.adempiere.base.Core; +import org.compiere.model.MImage; +import org.compiere.util.CCache; +import org.compiere.util.CLogger; +import org.compiere.util.Env; +import org.zkoss.image.AImage; +import org.zkoss.image.Image; + +/** + * Normal image can come from inside system or from outside system. + * with image from outside for performance we will cache it. + * this class for manage image cache and provide help function relate + * @author hieplq + * + */ +public class ManageImageCache { + + protected static transient CLogger log = CLogger.getCLogger (ManageImageCache.class); + /** + * this cache is don't expire, if must restart cache when has update image. + * better use a timer, example reset cache after 10 minute has update. it help user can change a batch of image and reset one time. + */ + private final CCache imageCache = new CCache(null, "WindowImageCache", 50, 0, false); + + private static ManageImageCache instance; + + /** + * get instance + * @return + */ + public static ManageImageCache instance(){ + if (instance == null){ + synchronized (ManageImageCache.class){ + if (instance == null) + instance = new ManageImageCache(); + } + } + + return instance; + } + + /** + * investigate image path of MImage, if path is a internal return internal url other return null + * @param image + * @return + */ + public static String getImageInternalUrl (MImage image){ + if (image == null) + return null; + return getImageInternalUrl(image.getImageURL()); + } + + /** + * investigate image path, if path is a internal return internal url other return null + * @param url + * @return + */ + public static String getImageInternalUrl (String url){ + if (url == null || url.trim().length() == 0 || url.indexOf("://") > 0) + return null; + + URL urlRsource = Core.getResourceFinder().getResource(url); + return urlRsource == null?null:urlRsource.getPath(); + } + + /** + * Load image from url + * @param imagePath + * @return + */ + protected static byte [] loadImageData (String imagePath){ + byte [] data = null; + + URLConnection conn; + try { + URL url = new URL(imagePath); + conn = url.openConnection(); + + conn.setUseCaches(false); + InputStream is = conn.getInputStream(); + byte[] buffer = new byte[1024*8]; // 8kB + ByteArrayOutputStream os = new ByteArrayOutputStream(); + int length = -1; + while ((length = is.read(buffer)) != -1) + os.write(buffer, 0, length); + is.close(); + data = os.toByteArray(); + os.close(); + } catch (IOException e) { + if (log.isLoggable(Level.CONFIG)) log.config (e.toString()); + } + + return data; + } + + /** + * if image is don't in cache, load it (imagePath can id of MImage) + * @param imagePath + * @return image load from path. null when has any exception + */ + public Image getImage(String imagePath){ + if (imagePath == null || imagePath.trim().length() == 0) + return null; + + Image aImage = null; + boolean hasCache = false; + synchronized (imageCache) { + hasCache = imageCache.containsKey(imagePath); + } + + if (!hasCache) { + try{ + int mImageId = Integer.parseInt(imagePath); + loadImage(MImage.get(Env.getCtx(), mImageId)); + }catch (NumberFormatException ex){ + loadExtend(imagePath); + } + + } + + synchronized (imageCache) { + aImage = imageCache.get(imagePath); + } + + return aImage; + } + + /** + * if MImage contain extend image or binary image data, load it into cache and return key + * other return null + * @param mImage + * @return + */ + public String loadImage (MImage mImage){ + if (mImage == null) + return null; + + boolean hasCache = false; + String strId = String.valueOf(mImage.get_ID()); + synchronized (imageCache) { + hasCache = imageCache.containsKey(strId); + } + + if(hasCache){ + return strId; + } + + if (mImage.getBinaryData() != null){ + synchronized (imageCache) { + Image loadImage = null; + try { + loadImage = new AImage (mImage.getName(), mImage.getBinaryData()); + } catch (IOException e) { + // do nothing treat image as null + } + imageCache.put(String.valueOf(mImage.get_ID()), loadImage); + } + return strId; + }else if (mImage.getImageURL() != null && mImage.getImageURL().trim().length() > 0 && getImageInternalUrl(mImage.getImageURL()) == null){ + synchronized (imageCache) { + hasCache = imageCache.containsKey(mImage.getImageURL()); + } + if (!hasCache){ + loadExtend (mImage.getImageURL()); + } + return mImage.getImageURL(); + } + + return null; + } + + /** + * load extend image into cache + * @param imagePath + */ + protected void loadExtend (String imagePath){ + byte[] data = loadImageData(imagePath); + AImage aImage = null; + // when can't load image (by incorrect url or disconnect or any exception, just set image as null + if (data != null) + try { + aImage = new AImage(imagePath, data); + } catch (IOException e) { + aImage = null; + } + + synchronized (imageCache) { + imageCache.put(imagePath, aImage); + } + } + +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/util/WebUIResourceFinder.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/util/WebUIResourceFinder.java index ba2e7e1b16..46ffbf2979 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/util/WebUIResourceFinder.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/util/WebUIResourceFinder.java @@ -15,6 +15,7 @@ package org.adempiere.webui.util; import java.net.URL; import java.util.Enumeration; +import java.util.regex.Pattern; import org.adempiere.base.IResourceFinder; import org.adempiere.webui.WebUIActivator; @@ -40,6 +41,7 @@ public class WebUIResourceFinder implements IResourceFinder { return WebUIActivator.getBundleContext().getBundle().findEntries(path, pattern, false); } + protected Pattern patternOnlyName = Pattern.compile("\\w+\\.\\w+"); @Override public URL getResource(String name) { if ("images/iDempiereHR.png".equals(name) || "images/iDempiere.png".equals(name)) { @@ -47,6 +49,9 @@ public class WebUIResourceFinder implements IResourceFinder { } Enumeration e = find(name); URL url = e != null && e.hasMoreElements() ? e.nextElement() : null; + if (url == null && patternOnlyName.matcher(name).matches()){ + name = "images/" + name; + } if (url == null && name.startsWith("org/compiere/images")) { String t = name.substring("org/compiere/".length()); t = ThemeManager.getThemeResource(t);