www.pudn.com > Bluegammon蓝牙的应用编程.rar > Popup.java
// Copyright (c) 2005 Sony Ericsson Mobile Communications AB // // This software is provided "AS IS," without a warranty of any kind. // ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, // INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. // // THIS SOFTWARE IS COMPLEMENTARY OF JAYWAY AB (www.jayway.se) package bluegammon.gui.popup; import javax.microedition.lcdui.Canvas; import javax.microedition.lcdui.Font; import javax.microedition.lcdui.Graphics; import bluegammon.Device; import bluegammon.Resources; /** ** The
Popuprepresents a popup with text and zero or many * alternatives that user can select among. A popup can be shown for a * specific amount of time or forever, the latter case requiring a user to close * the popup. The choice of the alternatives can be reported by implementing * aPopupListener** This class contains all functionality of a popup, i.e. interaction, * graphics and callback. *
* * @author Peter Andersson */ public class Popup implements Runnable { /** Preset alternative containing OK */ public static final char[][] ALT_OK; /** Preset alternative containing CANCEL */ public static final char[][] ALT_CANCEL; /** Preset alternatives containing YES and NO */ public static final char[][] ALT_YES_NO; /** Preset alternatives containing OK and CANCEL */ public static final char[][] ALT_OK_CANCEL; /** Preset alternatives containing YES, NO, and CANCEL */ public static final char[][] ALT_YES_NO_CANCEL; // Setup preset alternatives static { ALT_OK = new char[1][]; ALT_OK[0] = Resources.getChars(Resources.TXT_OK); ALT_CANCEL = new char[1][]; ALT_CANCEL[0] = Resources.getChars(Resources.TXT_CANCEL); ALT_YES_NO = new char[2][]; ALT_YES_NO[0] = Resources.getChars(Resources.TXT_YES); ALT_YES_NO[1] = Resources.getChars(Resources.TXT_NO); ALT_OK_CANCEL = new char[2][]; ALT_OK_CANCEL[0] = ALT_OK[0]; ALT_OK_CANCEL[1] = ALT_CANCEL[0]; ALT_YES_NO_CANCEL = new char[3][]; ALT_YES_NO_CANCEL[0] = ALT_YES_NO[0]; ALT_YES_NO_CANCEL[1] = ALT_YES_NO[1]; ALT_YES_NO_CANCEL[2] = ALT_CANCEL[0]; } /** The text to show in the popup */ protected char[] m_text; /** Number of alternatives to select in popup */ protected byte m_alternatives; /** Array of texts as chararrays in alternatives */ protected char[][] m_altTexts; /** Time out in seconds */ protected byte m_timeOut; /** Alternative reported back if timeout is reached */ protected byte m_timeOutAlt; /** Current alternative index */ protected byte m_curAlt; /** Listener to this popup */ protected PopupListener m_listener; /** Flag indicating if popup is active */ protected volatile boolean m_active = true; /** Width of popup */ protected int m_w; /** Height of popup */ protected int m_h; /** * Indices indicating where to break the text organized as [line][0 = start * char offset | 1 = char len] */ protected int[][] m_breakTextData; /** Number of visible text lines in popup */ protected int m_visibleLines; /** Current line offset */ protected int m_curLine; /** Maximum line offset */ protected int m_maxLine; /** Horizontal coordinate offset of text */ protected int m_yoffset; /** The life time of this popup */ protected long m_endTime; // Graphical data protected Font m_font = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_SMALL); protected int m_fontHeight = m_font.getHeight(); protected int m_borderColor = 0xdd0000; protected int m_backgroundColor = 0xcc440000; protected int m_textColor = 0xffffff; protected int m_alternativeColor = 0xff2200; protected int m_selectedAlternativeColor = 0xffffff; protected static int[] m_rgbData; /** * Space between actual popup graphics and the rectangle reported in method *init*/ protected static final int OFFSET_POPUP = 8; /** Space between popup edges and text content */ protected static final int OFFSET_TEXT = 2; /** Space between alternative texts */ protected static final int OFFSET_ALT = 4; /** Scrollbar width */ protected static final int SB_WIDTH = 5; /** Textbreak constants */ protected static final char[] TEXTBREAKS = { ' ', '?', ';', ',', '.', '!', ':', '-', '=', '(', ')', '[', ']' }; /** New line constant */ protected static final char NEWLINE = '\n'; /** * Creates an uninitialized popup. Callinitto setup * this popup instance. This constructor is used for instance cache * functionality. */ public Popup() {} /** * Initializes this popup. * * @param text The text to show in popup. * @param altTexts The alternatives to select among or null if no choices. * @param timeOut Time out for this popup in seconds (0 means no timeout). * @param defaultAlt Index of default alternative. * @param timeOutAlt Alternative index reported on time out. * @param listener The popuplistener being reported on selection or timeout or null * if no listener. * @param width Canvas width. * @param height Canvas height. */ public void init(char[] text, char[][] altTexts, byte timeOut, byte defaultAlt, byte timeOutAlt, PopupListener listener, int width, int height) { // Set parameters m_text = text; m_altTexts = altTexts; if (altTexts != null) { m_alternatives = (byte) altTexts.length; } else { m_alternatives = 0; } m_timeOut = timeOut; m_timeOutAlt = timeOutAlt; m_listener = listener; m_curAlt = defaultAlt; m_w = width - (OFFSET_POPUP << 1); m_h = height - (OFFSET_POPUP << 1); m_active = true; if (m_timeOut > 0) { // Set timeout m_endTime = System.currentTimeMillis() + (m_timeOut * 1000); } else if (m_alternatives > 0) { // No timeout m_endTime = 0; } else { // This should never happen - a popup with no alternatives and no timeout m_endTime = System.currentTimeMillis(); } m_visibleLines = Math.max(1, ((m_h - (OFFSET_TEXT << 1)) / m_fontHeight) - 1); int w = m_w - (OFFSET_TEXT << 1) - (SB_WIDTH << 1); m_curLine = 0; m_breakTextData = breakString(text, w); // Calculate height m_yoffset = 0; m_maxLine = m_breakTextData.length - m_visibleLines + 1; if (m_breakTextData.length < m_visibleLines) { int newH = m_breakTextData.length * m_fontHeight; if (m_alternatives > 0) newH += m_fontHeight; newH += OFFSET_TEXT + OFFSET_ALT; m_yoffset = (m_h - newH) >> 1; m_h = newH; } // Create transparent rgb buffer if needed (8 lines) if (m_rgbData == null || m_rgbData.length != m_w * 8) { m_rgbData = new int[m_w * 8]; for (int i = 0; i < m_rgbData.length; i++) { m_rgbData[i] = m_backgroundColor; } } // Start poll thread new Thread(this, "PopupPoll").start(); } /** * Breaks specified character array and returns a break matrix by line, * organized as [line][0 | 1] where 0 means starting offset in text for this * line, 1 means number of characters of this line. * * @param text The string to break * @param width Width in pixels to break on * @return A break index table */ protected int[][] breakString(char[] text, int width) { // Count text lines int offset = 0; int lines = 0; int newOffset; while (offset < text.length) { newOffset = findNextBreak(text, offset, text.length - offset, width, m_font); offset = newOffset; lines++; } int[][] indices = new int[lines][2]; // Setting offset data lines = 0; offset = 0; while (offset < text.length) { newOffset = findNextBreak(text, offset, text.length - offset, width, m_font); indices[lines][0] = offset; indices[lines][1] = newOffset - offset; lines++; offset = newOffset; } return indices; } /** * Returns next break when breaking a string. * * @param text The chars to calculate on * @param offset From what offset to read in chars * @param len How many characters to read * @param w Width * @param f Font * @return Offset of next break or length of text if no more breaks */ public int findNextBreak(char[] text, int offset, int len, int w, Font f) { int breakOffset = offset; int textW = 0; int niceB = -1; char c; charLoop: while (breakOffset <= offset + len && textW < w) { if (breakOffset == offset + len) c = TEXTBREAKS[0]; // last character + 1, fake break char else c = text[breakOffset]; if (c == NEWLINE) { // got a nice break here, new line niceB = breakOffset; break charLoop; } // Try finding break charachters breakCharLoop: for (int i = TEXTBREAKS.length - 1; i >= 0; i--) { if (c == TEXTBREAKS[i]) { niceB = breakOffset; break breakCharLoop; } } if (breakOffset == offset + len - 1) { // Special case, skip the last character niceB = breakOffset + 1; } breakOffset++; textW += f.charWidth(c); } if (niceB > offset && niceB < offset + len - 2 && (text[niceB + 1] == ' ')) return niceB + 2; // case: special case to get rid of extra spaces else if (niceB > offset && niceB < offset + len) return niceB + 1; // case: found a nice break, use this else if (breakOffset > offset + 1) return breakOffset - 1; // case: broke due to text width too big else if (breakOffset == offset) return breakOffset + 1; // case: broken on first char, step one more else return breakOffset; // case: default } /** * Paints the popup. Call this from yourDisplayable's * paint method. * * @param g Graphics context to paint on. */ public void paint(Graphics g) { if (m_active) { // draw transparent background for (int y = OFFSET_POPUP + m_yoffset; y < OFFSET_POPUP + m_yoffset + m_h; y += 8) { g.drawRGB(m_rgbData, 0, m_w, OFFSET_POPUP, y, m_w, Math.min(8, OFFSET_POPUP + m_yoffset + m_h - y), true); } // border g.setColor(m_borderColor); g.drawRect(OFFSET_POPUP, OFFSET_POPUP + m_yoffset, m_w, m_h); // text g.setColor(m_textColor); g.setFont(m_font); int y = OFFSET_POPUP + OFFSET_TEXT + m_yoffset; int maxLine = Math.min(m_curLine + m_visibleLines, m_breakTextData.length); for (int i = m_curLine; i < maxLine; i++) { int offset = m_breakTextData[i][0]; int len = m_breakTextData[i][1]; if (len == 1 && m_text[offset] == NEWLINE) { y += m_fontHeight; } else { if (m_text[offset + len - 1] == NEWLINE) { len--; } g.drawChars(m_text, offset, len, OFFSET_POPUP + OFFSET_TEXT + (m_w >> 1), y, Graphics.TOP | Graphics.HCENTER); y += m_fontHeight; } } // scrollbar if (m_visibleLines < m_breakTextData.length) { int sbh = m_visibleLines * m_fontHeight; // Scrollbar max height int sbstep = ((sbh - 4) << 8) / m_maxLine; // Scrollbar height * 256 int sbX = OFFSET_POPUP + m_w - SB_WIDTH - (SB_WIDTH >> 1); // Scrollbar x-coordinate g.setColor(m_textColor); g.fillRect(sbX, OFFSET_POPUP + OFFSET_TEXT + ((m_curLine * sbstep) >> 8), SB_WIDTH, 4 + (sbstep >> 8)); } // alternatives if (m_alternatives > 0) { y = OFFSET_POPUP + OFFSET_TEXT + m_h + m_yoffset - OFFSET_TEXT - m_fontHeight; int dx = (m_w / (m_alternatives + 1)); int x = OFFSET_POPUP + OFFSET_TEXT; for (int i = 0; i < m_alternatives; i++) { char[] t = m_altTexts[i]; x += dx; int xx = x - (m_font.charsWidth(t, 0, t.length) >> 1); if (m_curAlt != i) { // Unselected alternative g.setColor(m_alternativeColor); g.drawChars(t, 0, t.length, xx, y, Graphics.TOP | Graphics.LEFT); } else { // Selected alternative g.setColor(m_alternativeColor); g.drawChars(t, 0, t.length, xx + 1, y + 1, Graphics.TOP | Graphics.LEFT); g.setColor(m_selectedAlternativeColor); g.drawChars(t, 0, t.length, xx, y, Graphics.TOP | Graphics.LEFT); } } } } } /** * Handles user interaction when pressing a key. * Call this from yourDisplayable's * keyPressed keyRepeated method. * * @param keyCode The keycode. * @param gameCode The gamecode. */ public void keyPressed(int keyCode, int gameCode) { if (m_active) { if (m_alternatives < 1) { // If no choice, any key will do m_active = false; if (m_listener != null) m_listener.selectedChoice(m_curAlt, false); } else { switch (gameCode) { // Scroll text case Canvas.DOWN: { m_curLine++; if (m_curLine >= m_maxLine) m_curLine = 0; break; } case Canvas.UP: { if (m_maxLine > 0) m_curLine--; if (m_curLine < 0) m_curLine = m_maxLine - 1; break; } // Select among choices case Canvas.RIGHT: { m_curAlt++; if (m_curAlt >= m_alternatives) m_curAlt = 0; break; } case Canvas.LEFT: { m_curAlt--; if (m_curAlt < 0) m_curAlt = (byte) (m_alternatives - 1); break; } case Canvas.FIRE: { // Select if (m_curAlt >= 0) { m_active = false; if (m_listener != null) m_listener.selectedChoice(m_curAlt, false); } break; } case Device.KEYCODE_BACK: { m_active = false; if (m_listener != null) m_listener.selectedChoice(m_timeOutAlt, false); break; } } } } } /** * Disposes all resources held by this popup and closes it. */ public void dispose() { m_active = false; m_text = null; m_altTexts = null; m_listener = null; m_breakTextData = null; System.gc(); } /** * Returns whether this popup is active or not. * * @return true if active, false otherwise. */ public boolean isActive() { return m_active; } /** * Returns alternative index on timeout * * @return timeout alternative */ public byte getTimeOutChoice() { return m_timeOutAlt; } /** * Called by framework to check if popup reached its' timeout. * * @return true if timeout, false otherwise. */ protected boolean pollTimeout() { if (m_active) { if (m_endTime > 0 && System.currentTimeMillis() > m_endTime) { m_active = false; if (m_listener != null) { m_listener.selectedChoice(m_timeOutAlt, true); return true; } } } return false; } // Runnable impl to poll this popup public void run() { while (isActive()) { // Poll popup timeout try { Thread.sleep(1000); pollTimeout(); } catch (InterruptedException e) {} } } }