/* 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.MyScrollBarPanel; import CacheWolf.database.CacheHolderDetail; import CacheWolf.utils.Common; import CacheWolf.utils.MyLocale; import CacheWolf.utils.STRreplace; import CacheWolf.utils.SafeXML; import ewe.fx.Dimension; import ewe.fx.Graphics; import ewe.fx.IImage; import ewe.fx.Image; import ewe.fx.Point; import ewe.fx.Rect; import ewe.fx.mImage; import ewe.graphics.AniImage; import ewe.graphics.ImageDragContext; import ewe.graphics.InteractivePanel; import ewe.io.FileBase; import ewe.sys.Vm; import ewe.ui.CellConstants; import ewe.ui.CellPanel; import ewe.ui.ControlConstants; import ewe.ui.ControlEvent; import ewe.ui.Event; import ewe.ui.HtmlDisplay; import ewe.ui.IKeys; import ewe.ui.IScroll; import ewe.ui.KeyEvent; import ewe.ui.PanelSplitter; import ewe.ui.ScrollBarPanel; import ewe.ui.ScrollablePanel; import ewe.ui.SplittablePanel; import ewe.ui.mButton; import ewe.ui.mTextPad; /** * Class to create the panel that holds hints and logs. * It holds a method to cryt and decrypt hints. * Two buttons allow for navigation through the logs. 5 logs are displayed at * a time. This was implemented to allow for better performance on the * PocketPC. This number can be changed in the preferences. * Class ID=400 */ public class HintLogPanel extends CellPanel { private int crntLogPosition = 0; private CacheHolderDetail currCache; private final int DEFAULT_STRINGBUFFER_SIZE = 8000; private mTextPad hint = new mTextPad(); //mTextPad logs = new mTextPad(); private HtmlDisplay logs = new HtmlDisplay(); private AniImage htmlTxtImage; private fastScrollText htmlImagDisp = new fastScrollText(); private mButton codeButton; private IImage decodeButtonImage, encodeButtonImage; private mButton moreBt; private mButton prevBt; private MyScrollBarPanel sbplog; private int lastScrollbarWidth = 0; private boolean hintIsDecoded = false; public HintLogPanel() { CellPanel codeButtonPanel = new CellPanel(); codeButtonPanel.equalWidths = true; codeButton = GuiImageBroker.getButton(MyLocale.getMsg(400, "Decode"), "decode"); encodeButtonImage = GuiImageBroker.makeImageForButton(codeButton, MyLocale.getMsg(401, "Encode"), "encode"); decodeButtonImage = GuiImageBroker.makeImageForButton(codeButton, MyLocale.getMsg(400, "Decode"), "decode"); codeButtonPanel.addNext(codeButton); CellPanel logButtonPanel = new CellPanel(); logButtonPanel.equalWidths = true; prevBt = GuiImageBroker.getButton("<<", "previous"); logButtonPanel.addNext(prevBt); // DONTSTRETCH, (HFILL | WEST) moreBt = GuiImageBroker.getButton(">>", "next"); logButtonPanel.addLast(moreBt); // DONTSTRETCH, (HFILL | EAST) SplittablePanel split = new SplittablePanel(PanelSplitter.VERTICAL); CellPanel logpane = split.getNextPanel(); CellPanel hintpane = split.getNextPanel(); MyLocale.setSplitterSize(split); split.setSplitter(PanelSplitter.AFTER | PanelSplitter.HIDDEN, PanelSplitter.BEFORE | PanelSplitter.HIDDEN, 0); int initialHintHeight = Preferences.itself().initialHintHeight; if (initialHintHeight < 0 || initialHintHeight > 1000) initialHintHeight = Preferences.itself().DEFAULT_INITIAL_HINT_HEIGHT; hintpane.setPreferredSize(100, initialHintHeight); if (Preferences.itself().tabsAtTop) { if (Preferences.itself().menuAtTab) hintpane.addLast(codeButtonPanel, DONTSTRETCH, FILL); } else { if (!Preferences.itself().menuAtTab) hintpane.addLast(codeButtonPanel, DONTSTRETCH, FILL); } ScrollBarPanel sbphint = new MyScrollBarPanel(hint); hint.modify(ControlConstants.NotEditable, 0); hintpane.addLast(sbphint, CellConstants.STRETCH, (CellConstants.FILL | CellConstants.WEST)); if (Preferences.itself().tabsAtTop) { if (!Preferences.itself().menuAtTab) hintpane.addLast(codeButtonPanel, DONTSTRETCH, FILL); else logpane.addLast(logButtonPanel, DONTSTRETCH, FILL); } else { if (Preferences.itself().menuAtTab) hintpane.addLast(codeButtonPanel, DONTSTRETCH, FILL); else logpane.addLast(logButtonPanel, DONTSTRETCH, FILL); } sbplog = new MyScrollBarPanel(htmlImagDisp, ScrollablePanel.NeverShowHorizontalScrollers); Rect r = new Rect(new Dimension(Preferences.itself().getScreenWidth() - sbplog.vbar.getRect().width, 20)); htmlImagDisp.virtualSize = r; htmlImagDisp.checkScrolls(); logpane.addLast(sbplog.getScrollablePanel(), CellConstants.STRETCH, CellConstants.FILL); if (Preferences.itself().tabsAtTop) { if (!Preferences.itself().menuAtTab) logpane.addLast(logButtonPanel, DONTSTRETCH, FILL); } else { if (Preferences.itself().menuAtTab) logpane.addLast(logButtonPanel, DONTSTRETCH, FILL); } this.addLast(split); clear(); } public void setText(CacheHolderDetail cache) { if (currCache != cache) { this.currCache = cache; resetHintText(); crntLogPosition = 0; setLogs(0); moreBt.modify(0, ControlConstants.Disabled); prevBt.modify(0, ControlConstants.Disabled); } } public void clear() { clearOutput(); currCache = null; } private void clearOutput() { if (htmlTxtImage != null) { htmlImagDisp.removeImage(htmlTxtImage); htmlTxtImage.free(); } } private void setLogs(int crntLogPosition) { Vm.showWait(true); StringBuffer dummy = new StringBuffer(DEFAULT_STRINGBUFFER_SIZE); int counter = 0; int nLogs = currCache.CacheLogs.size(); int logsPerPage = Preferences.itself().logsPerPage; for (int i = crntLogPosition; i < nLogs; i++) { dummy.append(SafeXML.html2iso8859s1(currCache.CacheLogs.getLog(i).toHtml())); dummy.append("<br>"); if (++counter >= logsPerPage) break; } clearOutput(); logs.resizeTo(width, 50); // The cache GCP0T6 crashes the HtmlDisplay // As a temporary fix try { logs.startHtml(); // set documentroot to prevent html renderer from loading remote images logs.getDecoderProperties().set("documentroot", FileBase.getProgramDirectory()); logs.addHtml(dummy.toString(), new ewe.sys.Handle()); logs.endHtml(); } catch (Exception e) { logs = new HtmlDisplay(); Preferences.itself().log("Error rendering HTML", e, true); logs.setPlainText("Ewe VM: Internal error displaying logs"); } int h = logs.getLineHeight() * logs.getNumLines(); htmlTxtImage = new AniImage(new Image(width, h)); htmlTxtImage.setLocation(0, 0); htmlTxtImage.properties |= mImage.IsMoveable; Graphics draw = new Graphics(htmlTxtImage.image); logs.resizeTo(htmlTxtImage.getWidth() - lastScrollbarWidth, htmlTxtImage.getHeight()); logs.doPaint(draw, new Rect(0, 0, htmlTxtImage.getWidth(), htmlTxtImage.getHeight())); htmlImagDisp.addImage(htmlTxtImage); Rect r = new Rect(new Dimension(width, h)); htmlImagDisp.virtualSize = r; htmlImagDisp.origin = new Point(); htmlImagDisp.checkScrolls(); // Can I get a reasonable value for scrollbarWidth before calling checkScrolls() // and in a more reasonable way? // Now its ugly: I paint it, calculate the scrollbars and then resize the panel... // Better: Now I only redo it when the scrollbar width changed, which is not the case // normally. int scrollbarWidth = sbplog.vbar.getRect().width; if (scrollbarWidth != lastScrollbarWidth) { lastScrollbarWidth = scrollbarWidth; logs.resizeTo(htmlTxtImage.getWidth() - scrollbarWidth, htmlTxtImage.getHeight()); logs.doPaint(draw, new Rect(0, 0, htmlTxtImage.getWidth(), htmlTxtImage.getHeight())); } htmlImagDisp.repaintNow(); repaintNow(); Vm.showWait(false); } /** * Method that handles user input on this panel. * It handles decryption of hints and navigation through * the logs (always 5 at a time). Navigation of logs is required * for performance reasons on the pocketpc. */ public void onEvent(Event ev) { if (ev instanceof ControlEvent && ev.type == ControlEvent.PRESSED) { int minLogs = java.lang.Math.min(Preferences.itself().logsPerPage, currCache.CacheLogs.size()); if (ev.target == moreBt) { prevBt.modify(0, ControlConstants.Disabled); prevBt.repaintNow(); crntLogPosition += minLogs; if (crntLogPosition >= currCache.CacheLogs.size()) { //crntLogPosition = cache.CacheLogs.size()-5; crntLogPosition = currCache.CacheLogs.size() - minLogs; moreBt.modify(ControlConstants.Disabled, 0); moreBt.repaintNow(); } setLogs(crntLogPosition); } // = moreBt if (ev.target == prevBt) { moreBt.modify(0, ControlConstants.Disabled); moreBt.repaintNow(); crntLogPosition -= minLogs; if (crntLogPosition <= 0) { prevBt.modify(ControlConstants.Disabled, 0); prevBt.repaintNow(); crntLogPosition = 0; } setLogs(crntLogPosition); } if (ev.target == codeButton) { if (!hintIsDecoded) decodeHintText(); else resetHintText(); } } } private void decodeHintText() { if (hint.getText().length() > 0) { Object selection = hint.getSelection(); if (selection != null) hint.replaceSelection(Common.rot13(selection.toString())); else hint.setText(Common.rot13(hint.getText())); GuiImageBroker.setButtonIconAndText(codeButton, MyLocale.getMsg(401, "Encode"), encodeButtonImage); hintIsDecoded = true; } } private void resetHintText() { if (!currCache.Hints.equals("null")) hint.setText(STRreplace.replace(this.currCache.Hints, "<br>", "\n")); else hint.setText(""); GuiImageBroker.setButtonIconAndText(codeButton, MyLocale.getMsg(400, "Decode"), decodeButtonImage); hintIsDecoded = false; } } class fastScrollText extends InteractivePanel { // TODO extend this class in a way that text can be marked and copied private boolean scrollVertical = true; private boolean scrollHorizontal = false; public boolean imageNotDragged(ImageDragContext drag, Point where) { if (drag == null || drag.image == null) return super.imageNotDragged(drag, where); getDim(null); if (drag.image.location.y <= 0) { drag.image.move(0, drag.image.location.y); } else { drag.image.move(0, 0); } return super.imageNotDragged(drag, where); } // I copied it here because the original has a bug when scrolling // added the support for scrolling / draggin only vertically // rewrite to support for images bigger than the canvas // the return value is never used // ============================================================ public boolean imageDragged(ImageDragContext dc, Point where) // ============================================================ { dc.curPoint = new Point(where.x, where.y); AniImage moving = dc.image; Rect r = getDim(null); boolean didAutoScroll = false; Point to = new Point(where.x - dc.start.x, where.y - dc.start.y); if (!scrollHorizontal) to.x = 0; if (!scrollVertical) to.y = 0; //if (origin.y - to.y < 0 || origin.y - to.y + r.height > moving.location.height) return true; if (moving == null) { // this is not used only copied if (!dragBackground) return true; int dx = dc.start.x - where.x, dy = dc.start.y - where.y; if (where.x < origin.x || where.x >= origin.x + r.width || where.y < origin.y || where.y >= origin.y + r.height && autoScrolling) { if (where.x <= origin.x) dx = scrollStep; if (where.x >= origin.x + r.width) dx = -scrollStep; if (where.y <= origin.y) dy = scrollStep; if (where.y >= origin.y + r.height) dy = -scrollStep; // here +/- is wrong in InteractivePanel.java dc.start.x = where.x; dc.start.y = where.y; } //dc.start.move(where.x,where.y); if (dx != 0 || dy != 0) scroll(dx, dy); refresh(); return true; } else if (true/* || where.x < origin.x || where.x >= origin.x+r.width || where.y < origin.y || where.y >= origin.y+r.height*/) { if (autoScrolling) { didAutoScroll = true; scroll(-to.x, -to.y); } } /* if (moving.canGo(to)) { moving.move(to.x,to.y); draggingImage(dc); if (ri != null) ri.dragEvent(this,ri.Drag,dc); } */checkTouching(dc, false); if (didAutoScroll) refresh(); else refresh(dc.image, null);//updateImage(dc.image); return (true); } public void onKeyEvent(KeyEvent ev) { if (ev.type == KeyEvent.KEY_PRESS) { if (ev.key == IKeys.DOWN) { doScroll(IScroll.Vertical, IScroll.ScrollHigher, 1); refresh(); } if (ev.key == IKeys.UP) { doScroll(IScroll.Vertical, IScroll.ScrollLower, 1); refresh(); } if (ev.key == IKeys.PAGE_DOWN) { doScroll(IScroll.Vertical, IScroll.PageHigher, 1); refresh(); } if (ev.key == IKeys.PAGE_UP) { doScroll(IScroll.Vertical, IScroll.PageLower, 1); refresh(); } } } }