/* GNU General Public License CacheWolf is a software for PocketPC, Win and Linux that enables paperless caching. It supports the sites geocaching.com and opencaching.de Copyright (C) 2006 CacheWolf development team See http://www.cachewolf.de/ for more information. 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; version 2 of the License. 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 St, Fifth Floor, Boston, MA 02110-1301 USA */ package CacheWolf; import CacheWolf.controls.GuiImageBroker; import CacheWolf.controls.InfoBox; import CacheWolf.controls.MyScrollBarPanel; import CacheWolf.database.CacheHolderDetail; import CacheWolf.database.CacheImage; import CacheWolf.database.CacheImages; import CacheWolf.utils.Files; import CacheWolf.utils.MyLocale; import CacheWolf.utils.SafeXML; import ewe.filechooser.FileChooser; import ewe.filechooser.FileChooserBase; import ewe.fx.Color; import ewe.fx.Dimension; import ewe.fx.Font; import ewe.fx.FontMetrics; import ewe.fx.Graphics; import ewe.fx.Image; import ewe.fx.Point; import ewe.fx.Rect; import ewe.fx.mImage; import ewe.graphics.AniImage; import ewe.graphics.InteractivePanel; import ewe.io.File; import ewe.sys.SystemResourceException; import ewe.sys.Vm; import ewe.ui.CellConstants; import ewe.ui.CellPanel; import ewe.ui.ControlBase; import ewe.ui.ControlEvent; import ewe.ui.Event; import ewe.ui.Form; import ewe.ui.FormBase; import ewe.ui.Gui; import ewe.ui.InputBox; import ewe.ui.Menu; import ewe.ui.PenEvent; import ewe.ui.ScrollBarPanel; import ewe.ui.mButton; import ewe.ui.mLabel; public class ImagePanel extends CellPanel { ImagesPanel images; mButton btnAddPicture; MyScrollBarPanel imagesPanel; final CellPanel pnlTools; private CacheHolderDetail cacheDetails; public ImagePanel() { // Public constructor images = new ImagesPanel(); imagesPanel = new MyScrollBarPanel(images); pnlTools = new CellPanel(); btnAddPicture = GuiImageBroker.getButton(MyLocale.getMsg(341, "Eigene Bilder"), "imageadd"); btnAddPicture.setToolTip(MyLocale.getMsg(348, "Add user pictures")); pnlTools.equalWidths = true; pnlTools.addLast(btnAddPicture); if (Preferences.itself().tabsAtTop) { if (Preferences.itself().menuAtTab) addLast(pnlTools, DONTSTRETCH, FILL).setTag(SPAN, new Dimension(3, 1)); } else { if (!Preferences.itself().menuAtTab) addLast(pnlTools, DONTSTRETCH, FILL).setTag(SPAN, new Dimension(3, 1)); } addLast(imagesPanel, STRETCH, FILL); if (Preferences.itself().tabsAtTop) { if (!Preferences.itself().menuAtTab) addLast(pnlTools, DONTSTRETCH, FILL).setTag(SPAN, new Dimension(3, 1)); } else { if (Preferences.itself().menuAtTab) addLast(pnlTools, DONTSTRETCH, FILL).setTag(SPAN, new Dimension(3, 1)); } } public void clearImages() { images.clearImages(); this.cacheDetails = null; } public void setImages(CacheHolderDetail cacheDetails) { if (this.cacheDetails != cacheDetails) { this.cacheDetails = cacheDetails; images.setImages(cacheDetails); } } public void onEvent(final Event ev) { if (ev instanceof ControlEvent && ev.type == ControlEvent.PRESSED) { if (ev.target == btnAddPicture) { addUserImage(); } ev.consumed = true; } } private void addUserImage() { File imgFile; String imgDesc, imgDestName; FileChooser fc = new FileChooser(FileChooserBase.OPEN, MainForm.profile.dataDir); fc.setTitle("Select image file:"); if (fc.execute() != FormBase.IDCANCEL) { imgFile = fc.getChosenFile(); imgDesc = new InputBox("Description").input("", 10); // Create Destination Filename String ext = imgFile.getFileExt().substring(imgFile.getFileExt().lastIndexOf('.')); imgDestName = cacheDetails.getParent().getCode() + "_U_" + (cacheDetails.userImages.size() + 1) + ext; CacheImage userCacheImage = new CacheImage(CacheImage.FROMUSER); userCacheImage.setFilename(imgDestName); userCacheImage.setTitle(imgDesc); cacheDetails.userImages.add(userCacheImage); // Copy File Files.copy(imgFile.getFullPath(), MainForm.profile.dataDir + imgDestName); // Save Data cacheDetails.saveCacheDetails(MainForm.profile.dataDir); } } } /** * Class to display the cache and log images. It creates a thumbnail view and * allows the user to click on an image that will then be displayed in its original size * as long as the image fits the application size. If the application size is not sufficient * then the image will be scaled to the available screen size. * A right mouseclick on an image will open a dialogue to delete the file. */ class ImagesPanel extends InteractivePanel { /** Picture to replace deleted pictures */ private final String NO_IMAGE = "no_picture.png"; /** Minimum time (msec) to recognise a long pen down event (=right mouse key) */ private final int LONG_PEN_DOWN_DURATION = 500; // private final int thumb_max_size = 300; // private final int thumb_min_size = 100; private final int padding = 20; private int thumb_size = 0; private int locX, locY, locCounter; /** Start and duration of pen-pressed event to simulate right mouse key */ private long start, duration = 0; /** * Constructor to create the image panel. * <p> */ public ImagesPanel() { // Public constructor } static CacheHolderDetail oldChD = null; /** * Method to set the individual cache images. * Gets called immediatly before panel is displayed * * @see MainTab#onEvent(Event ev) */ public void setImages(CacheHolderDetail chD) { if (chD != oldChD) { Vm.showWait(true); clearImages(); thumb_size = ((Preferences.itself().getScreenWidth() - 2 * padding) / 3); thumb_size = thumb_size - padding; double rowCounter1 = 0; if (chD.images.getDisplayImages(chD.getParent().getCode()).size() > 0) { rowCounter1 = chD.images.getDisplayImages(chD.getParent().getCode()).size(); rowCounter1 = java.lang.Math.ceil(rowCounter1 / 3); } double rowCounter2 = 0; if (chD.userImages.size() > 0) { rowCounter2 = chD.userImages.size(); rowCounter2 = java.lang.Math.ceil(rowCounter2 / 3); } int rowCounter = (int) (rowCounter1 + rowCounter2); Rect r = new Rect(0, 0, Preferences.itself().getScreenWidth(), rowCounter * thumb_size + rowCounter * padding + padding); this.virtualSize = r; // this.setPreferredSize(pref.myAppWidth, rowCounter*thumb_size+rowCounter*padding+40); // this.checkScrolls(); // this.refresh(); locY = 0; addTitle(MyLocale.getMsg(340, "Cache Images:")); locY = 20; locX = padding; addImages(chD.images.getDisplayImages(chD.getParent().getCode())); // load user images if (locCounter == 1 || locCounter == 2) locY = locY + thumb_size; if (chD.userImages.size() > 0) { addTitle(MyLocale.getMsg(341, "User Images:")); locY = locY + 20; locX = padding; locCounter = 0; addImages(chD.userImages); } oldChD = chD; } // cache!=oldCache if (locY > this.height) { this.checkScrolls(); } this.refresh(); Vm.showWait(false); // this.repaintNow(); } /** * Clear the images in the panel * */ public void clearImages() { oldChD = null; int lgr = images.size(); for (int i = 0; i < lgr; i++) { this.removeImage((AniImage) images.get(0)); } } /** * Add a title above the cache images and above the user images * * @param title * Title to add ("cache images" or "user images") */ private void addTitle(String title) { AniImage aImg; Font titleFont = new Font("Verdana", Font.BOLD, 20); FontMetrics fm = getFontMetrics(); int stringWidth = fm.getTextWidth(title); int stringHeight = fm.getHeight(); Image img = new Image(stringWidth * 2, stringHeight + 5); Graphics g = new Graphics(img); g.setColor(new Color(195, 195, 195)); g.fillRect(0, 0, stringWidth * 2, stringHeight + 5); g.setColor(new Color(0, 0, 0)); g.setFont(titleFont); g.drawText(title, 0, 0); g.free(); aImg = new AniImage(img); aImg.setLocation(0, locY); addImage(aImg); aImg.refresh(); } /** * Add the images to the panel. Can add both normal and user images * * @param pImages * Vector of images or userImages * @param imagesText * Vector of image texts or user image texts */ private void addImages(CacheImages pImages) { String location, imgText; mImage mI; int scaleX, scaleY; double dummyC; ImagePanelImage ipi; AniImage AimgText; locCounter = 0; for (int i = 0; i < pImages.size(); i++) { location = pImages.get(i).getFilename(); boolean doit = true; for (int j = i + 1; j < pImages.size(); j++) { String jmgFile = pImages.get(j).getFilename(); if (location.equals(jmgFile)) { doit = false; break; } } if (doit) { location = MainForm.profile.dataDir + location; if (!(new File(location)).exists()) { location = NO_IMAGE; if (!Preferences.itself().showDeletedImages) continue; // Don't show the deleted Image if user does not want it } try { mI = new mImage(location); // actuall new mImage(location); should do the following "if" but it doesn't anyhow if (mI.getWidth() <= 0 || mI.getHeight() <= 0) throw new IllegalArgumentException(location); scaleX = thumb_size; scaleY = thumb_size; dummyC = 0; double thumb_size2 = thumb_size; if (mI.getWidth() > mI.getHeight()) { scaleX = thumb_size; dummyC = (double) mI.getHeight() / (double) mI.getWidth(); dummyC = dummyC * thumb_size2; scaleY = (int) dummyC; } if (mI.getWidth() <= mI.getHeight()) { scaleY = thumb_size; dummyC = (double) mI.getWidth() / (double) mI.getHeight(); dummyC = dummyC * thumb_size2; scaleX = (int) dummyC; } if (mI.getWidth() <= thumb_size) { scaleX = mI.getWidth(); scaleY = mI.getHeight(); } mI = mI.scale(scaleX, scaleY, null, 0); mI.freeSource(); ipi = new ImagePanelImage(mI); ipi.freeSource(); // mI.free(); --> this only works in java-VM, in ewe it will delete the image, so leave it commented out ipi.fileName = location; // this is set only to easily identify the filename of the image clicked ipi.setLocation(locX, locY); addImage(ipi); // Name of picture: if (pImages.size() > i) { /* if (location.equals(NO_IMAGE)) imgText = MyLocale.getMsg(342, "Deleted"); else */ imgText = SafeXML.html2iso8859s1(pImages.get(i).getTitle()); if (imgText.length() == 0) imgText = pImages.get(i).getFilename(); AimgText = new AniImage(); AimgText = getImageText(imgText); AimgText.setLocation(locX, locY + scaleY); addImage(AimgText); AimgText.refresh(); ipi.imageText = imgText; ipi.imageComment = SafeXML.html2iso8859s1(pImages.get(i).getComment()); if (ipi.imageComment.length() == 0) ipi.imageComment = SafeXML.html2iso8859s1(pImages.get(i).getURL()); } ipi.refresh(); locX = locX + thumb_size + padding; locCounter++; if (locCounter > 2) { locCounter = 0; locX = padding; locY = locY + thumb_size + padding; } } catch (IllegalArgumentException imex) { // file not found, could not decode etc. new InfoBox(MyLocale.getMsg(5500, "Fehler"), MyLocale.getMsg(322, "Kann Bild/Karte nicht laden") + ":\n" + imex.getMessage()).wait(FormBase.OKB); } catch (OutOfMemoryError e) { // TODO show an error icon in the panel instead of nothing new InfoBox(MyLocale.getMsg(5500, "Error"), MyLocale.getMsg(343, "Not enough free memory to load cache image") + ":\n" + location).wait(FormBase.OKB); } catch (SystemResourceException e) { // TODO show an error icon in the panel instead of nothing new InfoBox(MyLocale.getMsg(5500, "Error"), MyLocale.getMsg(343, "Not enough free memory to load cache image") + "\n" + location).wait(FormBase.OKB); } } } // for } private AniImage getImageText(String pText) { Font aniImageFont = new Font("Verdana", Font.BOLD, 14); FontMetrics fm = getFontMetrics(); int stringWidth = fm.getTextWidth(pText); int stringHeight = fm.getHeight(); Image img = new Image(stringWidth * 2, stringHeight + 5); Graphics g = new Graphics(img); g.setColor(new Color(195, 195, 195)); g.fillRect(0, 0, stringWidth * 2, stringHeight + 5); g.setColor(new Color(0, 0, 0)); g.setFont(aniImageFont); g.drawText(pText, 0, 0); g.free(); AniImage a = new AniImage(img); return a; } /** * React to when a user clicks an image. * If left mouse key is clicked, will open a new window displaying the image scaled * to window size if the image is larger, otherwise the true size is displayed. * If right mouse key is clicked, a dialogue to delete the image wil be displayed */ public void imageClicked(AniImage which, Point pos) { if ((ControlBase.currentPenEvent.modifiers & PenEvent.RIGHT_BUTTON) == PenEvent.RIGHT_BUTTON || duration > LONG_PEN_DOWN_DURATION) { // Right button pressed - delete image to conserve space if (which instanceof ImagePanelImage && !((ImagePanelImage) which).fileName.equals(NO_IMAGE)) { if (new InfoBox(MyLocale.getMsg(144, "Warning"), MyLocale.getMsg(344, "Delete image") + " \"" + ((ImagePanelImage) which).imageText + "\"?").wait(FormBase.IDYES | FormBase.IDNO) == FormBase.IDOK) { try { File f = new File(((ImagePanelImage) which).fileName); f.delete(); removeImage(which); } catch (Exception e) { // ignore } } } } else { if (which instanceof ImagePanelImage) { ImagePanelImage ich = (ImagePanelImage) which; String fn = ich.fileName; try { ImageDetailForm iF = new ImageDetailForm(fn, ich.imageText, ich.imageComment); iF.execute(null, Gui.CENTER_FRAME); } catch (IllegalArgumentException e) { new InfoBox(MyLocale.getMsg(5500, "Fehler"), MyLocale.getMsg(322, "Kann Bild/Karte nicht finden")).wait(FormBase.OKB); } catch (OutOfMemoryError e) { new InfoBox(MyLocale.getMsg(5500, "Error"), MyLocale.getMsg(343, "Not enough free memory to load cache image") + "\n" + fn).wait(FormBase.OKB); } } } } /** * Create a "pen held down" event on hardware that does not support a right mouse key (e.g. Windows Mobile) * by measuring the time between pen down and pen up events. This is used in imageClicked to differentiate * between left and right mouse keys. */ public void onPenEvent(PenEvent ev) { if (ev.type == PenEvent.PEN_DOWN) { start = Vm.getTimeStampLong(); } if (ev.type == PenEvent.PEN_UP) { duration = Vm.getTimeStampLong() - start; } super.onPenEvent(ev); } } /** * The ImagePanelImage extends AniImage by a fileName. * This is an easy way to identify the image clicked, * what is needed to display the full image from the * thumbnail. */ class ImagePanelImage extends AniImage { public String fileName = ""; public String imageText = ""; public String imageComment = ""; public ImagePanelImage(mImage i) { super(i); } } /** * Class creates a view on the image scaled * to the application size, but only if the image is larger than * the available app size. */ class ImageDetailForm extends Form { ImageInteractivePanel ipp = new ImageInteractivePanel(); ScrollBarPanel scp; public ImageDetailForm(String imgLoc, String imgTitle, String imgComment) { scp = new MyScrollBarPanel(ipp); ipp.setImage(imgLoc); this.title = "Image"; this.setPreferredSize(Preferences.itself().getScreenWidth(), Preferences.itself().getScreenHeight()); this.addLast(scp, CellConstants.STRETCH, CellConstants.FILL); this.addLast(new mLabel(imgTitle), CellConstants.DONTSTRETCH, (CellConstants.DONTFILL | CellConstants.WEST)); this.addLast(new mLabel(imgComment), CellConstants.DONTSTRETCH, (CellConstants.DONTFILL | CellConstants.WEST)); } public void onEvent(Event ev) { if (ev instanceof ControlEvent && ev.type == ControlEvent.EXITED) { ev.consumed = true; this.close(0); } else super.onEvent(ev); } } /** * This class handles the resizing im images */ class ImageInteractivePanel extends InteractivePanel { int state = -1; // 0 = nothing, -1 = scaled to window, 1 = scaled to original size ScrollBarPanel scp; String imgLoc = ""; AniImage pic = null; private Menu mClose = new Menu(new String[] { "Close" }, ""); public ImageInteractivePanel() { super(); this.setMenu(mClose); } public void resizeTo(int w, int h) { this.width = w; this.height = h; if (state == -1) fitImageToWindow(); virtualSize = new Rect(0, 0, java.lang.Math.max(w, pic.getWidth()), java.lang.Math.max(h, pic.getHeight())); checkScrolls(); super.resizeTo(w, h); } public void setImage(String filename) { imgLoc = filename; mImage mI = new mImage(imgLoc); if (pic != null) { this.removeImage(pic); pic.freeSource(); pic.free(); } pic = new AniImage(mI); pic.setLocation(0, 0); mI.freeSource(); // mI.free(); this works in the java-VM, but it will delete the image in the ewe-vm --> leave it commeted out this.addImage(pic); virtualSize = new Rect(pic.getSize(null)); checkScrolls(); } public void fitImageToWindow() { Rect s = this.parent.getRect(); int ww = pic.getWidth(); int wh = pic.getHeight(); double scale = java.lang.Math.max((double) ww / (double) s.width, (double) wh / (double) s.height); if (scale != 1) { this.removeImage(pic); AniImage tmp = new AniImage(pic.scale((int) (ww / scale), (int) (wh / scale), null, 0)); pic.freeSource(); pic.free(); pic = tmp; pic.setLocation(0, 0); this.addImage(pic); virtualSize = new Rect(pic.getSize(null)); checkScrolls(); } } public void imageClicked(AniImage which, Point pos) { state = -state; if (state == 1) { setImage(imgLoc); this.repaintNow(); } if (state == -1) { fitImageToWindow(); this.repaintNow(); } } public void penRightReleased(Point p) { menuState.doShowMenu(p, true, null); // direct call (not through doMenu) is neccesary because it will exclude the whole table } public void penHeld(Point p) { menuState.doShowMenu(p, true, null); } public void popupMenuEvent(Object selectedItem) { postEvent(new ControlEvent(ControlEvent.EXITED, this)); } public void formClosing() { super.formClosing(); if (pic != null) { pic.freeSource(); pic.free(); } } }