1 /***
2 * Redistribution and use in source and binary forms, with or without
3 * modification, are permitted provided that the following conditions are
4 * met :
5 *
6 * . Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 *
9 * . Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * . The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $Id$
29 */
30
31 package palmed.edit.scrollbar;
32
33 import java.util.TimerTask;
34 import javax.microedition.lcdui.Graphics;
35
36 /***
37 * This class captures common behaviours of scrollbars.
38 *
39 * @author Mathieu Champlon
40 * @version $Revision$ $Date$
41 */
42 public final class Scrollbar implements IScrollbar
43 {
44 /***
45 * The background color.
46 */
47 private static final int BACKGROUND_COLOR = 0xADAAAD;
48 /***
49 * The foreground color.
50 */
51 private static final int FOREGROUND_COLOR = 0x000000;
52 /***
53 * The size of the arrows in pixels.
54 */
55 private static final int ARROW_SIZE = 8;
56 /***
57 * The width of the scrollbar in pixels.
58 */
59 private static final int WIDTH = 2 * ARROW_SIZE + 1;
60 /***
61 * The space taken by arrows in pixels.
62 */
63 private static final int ARROW_SPACING = ARROW_SIZE + 2;
64 /***
65 * The width of the body and cursor in pixels.
66 */
67 private static final int BODY_WIDTH = WIDTH / 3;
68 /***
69 * The minimum cursor size allowed in pixels.
70 */
71 private static final int CURSOR_MINIMUM_SIZE = 20;
72 /***
73 * The values defining the cursor size and position.
74 */
75 private int max_, visible_, value_;
76 /***
77 * The size of the scrollbar in pixels.
78 */
79 private int size_;
80 /***
81 * Position in pixels from where a drag event occurs.
82 */
83 private int dragFrom_;
84 /***
85 * Value of the slidebar from which a drag event occurs.
86 */
87 private int dragFromValue_;
88 /***
89 * The scrolling timer.
90 */
91 private final RepeatableTimer timer_;
92 /***
93 * The viewport.
94 */
95 private final IScrollable viewport_;
96 /***
97 * The drawer.
98 */
99 private final IDrawer drawer_;
100
101 /***
102 * Create a scrollbar.
103 *
104 * @param viewport the viewport to register
105 * @param drawer the scrollbar drawer
106 */
107 public Scrollbar( final IScrollable viewport, final IDrawer drawer )
108 {
109 if( viewport == null )
110 throw new IllegalArgumentException( "parameter 'viewport' is null" );
111 if( drawer == null )
112 throw new IllegalArgumentException( "parameter 'drawer' is null" );
113 viewport_ = viewport;
114 drawer_ = drawer;
115 size_ = 0;
116 dragFrom_ = -1;
117 timer_ = new RepeatableTimer();
118 }
119
120 /***
121 * Paint the scrollbar.
122 *
123 * @param g the graphics
124 */
125 public void paint( final Graphics g )
126 {
127 g.setColor( BACKGROUND_COLOR );
128 paintBackground( g );
129 g.setColor( FOREGROUND_COLOR );
130 paintArrows( g );
131 paintBody( g );
132 paintCursor( g );
133 }
134
135 private void paintBackground( final Graphics g )
136 {
137 g.setStrokeStyle( Graphics.SOLID );
138 drawer_.drawBackground( g, size_, WIDTH );
139 }
140
141 private void paintArrows( final Graphics g )
142 {
143 drawer_.drawArrow( g, WIDTH - 1, ARROW_SIZE, 0 );
144 drawer_.drawArrow( g, WIDTH - 1, size_ - ARROW_SIZE - 1, size_ - 1 );
145 }
146
147 private void paintBody( final Graphics g )
148 {
149 drawer_.drawBody( g, BODY_WIDTH, WIDTH - BODY_WIDTH - 1, ARROW_SPACING, size_ - ARROW_SPACING - 1 );
150 }
151
152 private void paintCursor( final Graphics g )
153 {
154 final int size = computeCursorSize();
155 final int position = computeCursorPosition( size );
156 drawer_.drawCursor( g, BODY_WIDTH, WIDTH - 2 * BODY_WIDTH, position, size );
157 }
158
159 private int getBodySize()
160 {
161 return size_ - 2 * ARROW_SPACING;
162 }
163
164 private int computeCursorPosition( final int size )
165 {
166 if( visible_ < max_ )
167 return ARROW_SPACING + value_ * (getBodySize() - size) / (max_ - visible_);
168 return ARROW_SPACING;
169 }
170
171 private int computeCursorSize()
172 {
173 if( visible_ < max_ )
174 return Math.max( CURSOR_MINIMUM_SIZE, getBodySize() * visible_ / max_ );
175 return getBodySize();
176 }
177
178 /***
179 * Scroll the cursor to the given value.
180 *
181 * @param value the new value
182 */
183 public void scroll( final int value )
184 {
185 value_ = value;
186 }
187
188 /***
189 * {@inheritDoc}
190 */
191 public void update( final int visible, final int max )
192 {
193 visible_ = visible;
194 max_ = max;
195 }
196
197 /***
198 * Resize the scrollbar.
199 *
200 * @param size the new size in pixels
201 */
202 public void resize( final int size )
203 {
204 size_ = size;
205 }
206
207 /***
208 * Handle a click event.
209 *
210 * @param offset the click offset
211 */
212 public void click( final int offset )
213 {
214 dragFrom_ = -1;
215 final int size = computeCursorSize();
216 final int position = computeCursorPosition( size );
217 click( offset, position, size );
218 if( dragFrom_ == -1 )
219 {
220 timer_.start( new TimerTask()
221 {
222 public void run()
223 {
224 click( offset, position, size );
225 }
226 } );
227 }
228 }
229
230 private void click( final int offset, final int position, final int size )
231 {
232 if( offset < position )
233 {
234 if( offset > ARROW_SPACING )
235 viewport_.scroll( -visible_ );
236 else
237 viewport_.scroll( -1 );
238 }
239 else if( offset > position + size )
240 {
241 if( offset < size_ - ARROW_SPACING )
242 viewport_.scroll( visible_ );
243 else
244 viewport_.scroll( 1 );
245 }
246 else
247 {
248 dragFrom_ = offset;
249 dragFromValue_ = value_;
250 }
251 }
252
253 /***
254 * Handle an unclick event.
255 */
256 public void unclick()
257 {
258 timer_.stop();
259 }
260
261 /***
262 * Handle a drag event.
263 *
264 * @param offset the offset to drag to
265 */
266 public void drag( final int offset )
267 {
268 if( dragFrom_ != -1 )
269 {
270 final int size = getBodySize();
271 final int steps = (offset - dragFrom_) * max_ / size + dragFromValue_ - value_;
272 viewport_.scroll( steps );
273 }
274 }
275
276 /***
277 * Retrieve the width.
278 *
279 * @return the width in pixels
280 */
281 public int getWidth()
282 {
283 return WIDTH;
284 }
285
286 /***
287 * Retrieve the size.
288 *
289 * @return the size in pixels
290 */
291 public int getSize()
292 {
293 return size_;
294 }
295 }