001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.midp.chameleon.layers;
028:
029: import com.sun.midp.chameleon.*;
030: import javax.microedition.lcdui.*;
031: import com.sun.midp.chameleon.skins.ScrollIndSkin;
032: import com.sun.midp.lcdui.EventConstants;
033: import com.sun.midp.log.Logging;
034: import com.sun.midp.log.LogChannels;
035:
036: import java.util.Timer;
037: import java.util.TimerTask;
038:
039: /**
040: * A ScrollBarLayer is a region of the display used for showing the Form's
041: * and Alert's scroll bar.
042: *
043: */
044: public class ScrollBarLayer extends ScrollIndLayer {
045: /**
046: * information of previous pointer event, used
047: * for 'drag' event calculations
048: */
049: private int lastx = -1;
050: private int lasty = -1;
051: private int gap = 0;
052:
053: /** currect scroll type */
054: private int scrollType = SCROLL_NONE;
055:
056: /** last proportion value */
057: protected int proportion;
058:
059: /** last position value */
060: protected int position;
061:
062: /** thumb top in the whole scrollbar's coordinates space */
063: private int thumbY;
064:
065: /** thumb height, calculated based on the viewable height */
066: private int thumbHeight;
067:
068: /** The height of arrow area. IMPL_NOTE: has to be moved to skin */
069: protected static final int ARROW_HEIGHT = 18;
070:
071: /** The min height of the thumb */
072: protected static final int THUMB_HEIGHT_MIN = 12;
073:
074: /** The min number of pixels processed while thumb is dragging */
075: protected static final int DRAG_MIN = 5;
076:
077: /** The bar height. Calculated from the scrolling layer height */
078: int barHeight;
079:
080: /** delay for the arrow repaint to make arrow press visible for the user */
081: protected static final int ARROW_PRESS_DELAY = 100; // 100 ms
082: /** timer for the arrow repaint */
083: protected Timer aT; // = null
084:
085: /**
086: * Construct a new ScrollBarLayer, visible, but transparent
087: */
088: public ScrollBarLayer(CLayer layer, ScrollListener listener) {
089: super (layer, listener);
090: setBackground(null, ScrollIndSkin.COLOR_BG);
091: }
092:
093: /**
094: * Construct a new ScrollBarLayer, visible, but transparent
095: */
096: public ScrollBarLayer(CLayer layer) {
097: this (layer, null);
098: }
099:
100: /**
101: * Set timer for the arrow repaint
102: */
103: private void setTimer() {
104: cancelTimer();
105: if (scrollType == SCROLL_LINEDOWN
106: || scrollType == SCROLL_LINEUP) {
107: if (visible) {
108: requestRepaint(0, 0, bounds[W], ARROW_HEIGHT);
109: requestRepaint(0, bounds[H] - ARROW_HEIGHT, bounds[W],
110: ARROW_HEIGHT);
111: aT = new Timer();
112: aT.schedule(new ArrowTimerTask(), ARROW_PRESS_DELAY);
113: }
114: }
115: }
116:
117: /**
118: * Cancel timer for the arrow repaint
119: */
120: private void cancelTimer() {
121: if (aT != null) {
122: aT.cancel();
123: aT = null;
124: scrollType = SCROLL_NONE;
125: if (visible) {
126: requestRepaint(0, 0, bounds[W], ARROW_HEIGHT);
127: requestRepaint(0, bounds[H] - ARROW_HEIGHT, bounds[W],
128: ARROW_HEIGHT);
129: }
130: }
131: }
132:
133: /**
134: * Timer task for the arrow repaint
135: */
136: protected class ArrowTimerTask extends TimerTask {
137: /**
138: * Cancel timer as soon as it issues
139: */
140: public void run() {
141: cancelTimer();
142: }
143: }
144:
145: /**
146: * Calculate layer bounds depending on the scrollable
147: */
148: public void setBounds() {
149: if (scrollable != null) {
150: int[] scrollableBounds = scrollable.getBounds();
151: // NOTE : although we define different scrollbar width in RomizedProperties.java,
152: // it seems that different with is not needed except for some popups.
153: bounds[W] = ScrollIndSkin.WIDTH;
154: bounds[H] = scrollableBounds[H] - 1; // make it look better in all layers
155: bounds[Y] = scrollableBounds[Y];
156: int shift = 0;
157: if ((scrollable instanceof PopupLayer)
158: && !(scrollable instanceof MenuLayer)) {
159: // the scrollbar in Choicegroup-popup and dateEditor popup should be smaller as limited space.
160: bounds[W] -= 3;
161: shift = 1;
162: }
163: bounds[X] = scrollableBounds[X] + scrollableBounds[W]
164: - bounds[W] - shift;
165: // the scrollbar move left one pixel as the docking layer draws its bound one pixel less
166:
167: // the scrollbar move left one pixel as the alert draws its bound one pixel less
168: if (alertMode) {
169: bounds[X] -= 1;
170: }
171: barHeight = bounds[H] - ARROW_HEIGHT * 2;
172: }
173: }
174:
175: /**
176: * Set the current vertical scroll position and proportion.
177: *
178: * @param scrollPosition vertical scroll position.
179: * @param scrollProportion vertical scroll proportion.
180: */
181: public void setVerticalScroll(int scrollPosition,
182: int scrollProportion) {
183: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
184: Logging.report(Logging.INFORMATION, LogChannels.LC_HIGHUI,
185: "ScrollBar.setVertical: pos = " + scrollPosition
186: + " prop = " + scrollProportion);
187: }
188: setVisible(scrollProportion < 100);
189: if (position != scrollPosition
190: || proportion != scrollProportion) {
191: proportion = scrollProportion;
192: position = scrollPosition;
193: }
194: }
195:
196: /**
197: * Paint the scroll bar
198: * @param g the graphics context to paint in
199: */
200: protected void paintBody(Graphics g) {
201: int arrowSignHeight = 4;
202: int arrowSignWidth = 7;
203:
204: // draw up and down arrows
205: g
206: .setColor(scrollType == SCROLL_LINEUP ? ScrollIndSkin.COLOR_UP_ARROW
207: : ScrollIndSkin.COLOR_FG);
208: g.fillRect(0, 0, bounds[W], ARROW_HEIGHT);
209:
210: g
211: .setColor(scrollType == SCROLL_LINEDOWN ? ScrollIndSkin.COLOR_UP_ARROW
212: : ScrollIndSkin.COLOR_FG);
213: g
214: .fillRect(0, bounds[H] - ARROW_HEIGHT, bounds[W],
215: ARROW_HEIGHT);
216:
217: g.setColor(ScrollIndSkin.COLOR_FRAME);
218:
219: g.drawLine(0, ARROW_HEIGHT, bounds[W], ARROW_HEIGHT);
220: g.drawLine(0, bounds[H] - ARROW_HEIGHT, bounds[W], bounds[H]
221: - ARROW_HEIGHT);
222:
223: int x2 = bounds[W] / 2;
224: int y2 = ARROW_HEIGHT / 2 - arrowSignHeight / 2;
225:
226: // draw down arrow
227: g
228: .setColor(scrollType == SCROLL_LINEUP ? ScrollIndSkin.COLOR_DN_ARROW
229: : ScrollIndSkin.COLOR_UP_ARROW);
230: g.fillTriangle(x2 - arrowSignWidth / 2, y2 + arrowSignHeight
231: - 1, x2, y2, x2 + arrowSignWidth / 2, y2
232: + arrowSignHeight - 1);
233:
234: y2 = bounds[H] - ARROW_HEIGHT + ARROW_HEIGHT / 2
235: + arrowSignHeight / 2;
236:
237: // draw up arrow
238: g
239: .setColor(scrollType == SCROLL_LINEDOWN ? ScrollIndSkin.COLOR_DN_ARROW
240: : ScrollIndSkin.COLOR_UP_ARROW);
241:
242: g.fillTriangle(x2 - arrowSignWidth / 2, y2 - arrowSignHeight
243: + 1, x2, y2, x2 + arrowSignWidth / 2, y2
244: - arrowSignHeight + 1);
245:
246: // draw thumb
247: g.translate(0, ARROW_HEIGHT);
248:
249: thumbHeight = barHeight * proportion / 100;
250: if (thumbHeight < THUMB_HEIGHT_MIN) {
251: thumbHeight = THUMB_HEIGHT_MIN;
252: }
253: thumbY = (barHeight - thumbHeight) * position / 100; //this value is in translated coordinates space
254:
255: if (thumbY + thumbHeight > barHeight) {
256: thumbY = barHeight - thumbHeight;
257: }
258:
259: g.setColor(ScrollIndSkin.COLOR_FG);
260: g.fillRect(1, thumbY, bounds[W], thumbHeight);
261:
262: //3 horizontal stripes at center of thumb
263: g.setColor(ScrollIndSkin.COLOR_FRAME);
264: g.drawLine(3, thumbY - 2 + thumbHeight / 2, bounds[W] - 3,
265: thumbY - 2 + thumbHeight / 2);
266: g.drawLine(3, thumbY + thumbHeight / 2, bounds[W] - 3, thumbY
267: + thumbHeight / 2);
268: g.drawLine(3, thumbY + 2 + thumbHeight / 2, bounds[W] - 3,
269: thumbY + 2 + thumbHeight / 2);
270:
271: //draw the frame of the scroll bar
272: g.setColor(ScrollIndSkin.COLOR_FRAME);
273: g.drawLine(0, thumbY, bounds[W] - 1, thumbY);
274: g.drawLine(0, thumbY + thumbHeight, bounds[W] - 1, thumbY
275: + thumbHeight);
276:
277: g.translate(0, -ARROW_HEIGHT);
278:
279: g.drawLine(0, 0, 0, bounds[H]);
280: if (true) { // IMPL_NOTE: add param drawBOrder
281: g.drawRect(0, 0, bounds[W], bounds[H]);
282: }
283:
284: thumbY += ARROW_HEIGHT; //translate it to the whole scrollbar coordinates space!
285: }
286:
287: /**
288: * Determine he scroll type basing on the pointer coordinates
289: * @param x - x coordinate
290: * @param y - y coordinate
291: * @return the scroll type
292: * The possible types of scrolling are
293: * @see #SCROLL_NONE
294: * @see #SCROLL_LINEUP
295: * @see #SCROLL_LINEDOWN
296: * @see #SCROLL_PAGEUP
297: * @see #SCROLL_PAGEDOWN
298: * @see #SCROLL_THUMBTRACK
299: */
300: private int getScrollType(int x, int y) {
301: int ret;
302: if (x < 0 || x > bounds[W] || y < 0 || y > bounds[H]) {
303: ret = SCROLL_NONE;
304: } else if (y < ARROW_HEIGHT) {
305: ret = SCROLL_LINEUP;
306: } else if (y > (bounds[H] - ARROW_HEIGHT)) {
307: ret = SCROLL_LINEDOWN;
308: } else if (y < thumbY) {
309: ret = SCROLL_PAGEUP;
310: } else if (y > thumbY + thumbHeight) {
311: ret = SCROLL_PAGEDOWN;
312: } else {
313: ret = SCROLL_THUMBTRACK;
314: }
315: return ret;
316: }
317:
318: /**
319: * Handle input from a pen tap. Parameters describe
320: * the type of pen event and the x,y location in the
321: * layer at which the event occurred. Important : the
322: * x,y location of the pen tap will already be translated
323: * into the coordinate space of the layer.
324: *
325: * @param type the type of pen event
326: * @param x the x coordinate of the event
327: * @param y the y coordinate of the event
328: */
329: public boolean pointerInput(int type, int x, int y) {
330: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
331: Logging.report(Logging.INFORMATION, LogChannels.LC_HIGHUI,
332: "Scroll.pointer type =" + type + ", x =" + x
333: + ", y = " + y + "\n" + "bounds[X] = "
334: + bounds[X] + " bounds[Y] = " + bounds[Y]
335: + " bonds[W] = " + bounds[W]
336: + " bounds[H] = " + bounds[H]);
337: }
338:
339: switch (type) {
340: case EventConstants.PRESSED:
341: // case EventConstants.HOLD:
342: // no action for tap-and-hold in scrollbar
343: // cancel timer for any press.
344: cancelTimer();
345:
346: scrollType = getScrollType(x, y);
347: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
348: Logging.report(Logging.INFORMATION,
349: LogChannels.LC_HIGHUI, "Pressed, scrollType="
350: + scrollType);
351: }
352: switch (scrollType) {
353:
354: case SCROLL_LINEDOWN:
355: case SCROLL_LINEUP:
356: listener.scrollContent(scrollType, 0);
357: setTimer();
358: break;
359: case SCROLL_PAGEUP:
360: case SCROLL_PAGEDOWN:
361: listener.scrollContent(scrollType, 0);
362: break;
363: case SCROLL_THUMBTRACK:
364: gap = y - thumbY;
365: lastx = x;
366: lasty = y;
367: break;
368: case SCROLL_NONE:
369: break;
370: }
371: break;
372: case EventConstants.RELEASED:
373: scrollType = SCROLL_NONE;
374: lastx = -1;
375: lasty = -1;
376: gap = 0;
377: break;
378: case EventConstants.DRAGGED:
379: if (scrollType == SCROLL_THUMBTRACK) {
380: if (y < lasty - DRAG_MIN
381: || y > lasty + DRAG_MIN
382: ||
383: /* accumulate drag events till reaching DRAG_MIN
384: or till reaching drag boundaries */
385: y <= (ARROW_HEIGHT + gap)
386: || y >= (bounds[H] - ARROW_HEIGHT - thumbHeight + gap)) {
387:
388: lasty = y;
389: y = y - gap - ARROW_HEIGHT;
390: int pos = 100 * y / (barHeight - thumbHeight);
391: pos = (pos < 0) ? 0 : pos;
392: pos = (pos > 100) ? 100 : pos;
393: listener.scrollContent(SCROLL_THUMBTRACK, pos);
394: }
395: }
396: break;
397: default:
398: break;
399: }
400:
401: /* we should process all of the pointer event inside scroll layer
402: and don't pass it to underlying layer */
403: return true;
404: }
405:
406: /**
407: * Set new scrollable
408: * @param layer new scrollable controlling the scrolling layer
409: * @return true if the scrollable is changed, false - otherwise
410: */
411: public boolean setScrollable(CLayer layer) {
412: boolean ret = super .setScrollable(layer);
413: if (ret) {
414: setBounds();
415: position = 0;
416: proportion = 0;
417: lastx = -1;
418: lasty = -1;
419: gap = 0;
420: scrollType = SCROLL_NONE;
421: }
422: return ret;
423: }
424: }
|