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.ui;
32
33 import java.util.Enumeration;
34 import java.util.Vector;
35 import javax.microedition.lcdui.Graphics;
36 import javax.microedition.lcdui.Item;
37
38 /***
39 * This class implements a custom toolbar.
40 *
41 * @author Mathieu Champlon
42 * @version $Revision$ $Date$
43 */
44 public final class Toolbar implements IToolbar, IComponent
45 {
46 /***
47 * The height of the top border.
48 */
49 private static final int TOP_BORDER_HEIGHT = 2;
50 /***
51 * The height of the bottom border.
52 */
53 private static final int BOTTOM_BORDER_HEIGHT = 1;
54 /***
55 * The height of a button row.
56 */
57 private static final int ROW_HEIGHT = 26;
58 /***
59 * The ordinate of the middle point in the first row.
60 */
61 private static final int MIDDLE_OF_FIRST_ROW = (TOP_BORDER_HEIGHT + BOTTOM_BORDER_HEIGHT + ROW_HEIGHT) / 2;
62 /***
63 * The height of the shrinked toolbar.
64 */
65 private static final int SHRINKED_HEIGHT = 17;
66 /***
67 * The border color.
68 */
69 private static final int BORDER_COLOR = 0xFFFFFF;
70 /***
71 * The background color.
72 */
73 private static final int BACKGROUND_COLOR = 0xADAAAD;
74 /***
75 * The button hover color.
76 */
77 private static final int BUTTON_HOVER_COLOR = 0x3165CE;
78 /***
79 * The color of the shrink/expand buttons.
80 */
81 private static final int SHRINK_EXPAND_BUTTONS_COLOR = 0x000000;
82 /***
83 * The width of the shrink/expand area.
84 */
85 private static final int SHRINK_EXPAND_AREA_WIDTH = 8;
86 /***
87 * The shift from the middle of the row for the shrink/expand buttons.
88 */
89 private static final int SHRINK_EXPAND_BUTTONS_SHIFT = 4;
90 /***
91 * The size of each facet of the shrink/expand buttons.
92 */
93 private static final int SHRINK_EXPAND_BUTTONS_SIZE = 3;
94 /***
95 * The command listener.
96 */
97 private final ICommandListener listener_;
98 /***
99 * The layout manager.
100 */
101 private final ILayoutManager manager_;
102 /***
103 * The buttons.
104 */
105 private final Vector buttons_;
106 /***
107 * The width of the toolbar.
108 */
109 private int width_;
110 /***
111 * The number of pages displayed.
112 */
113 private int pages_;
114 /***
115 * The total number of pages for displaying all buttons.
116 */
117 private int maxPages_;
118
119 /***
120 * Create a toolbar.
121 *
122 * @param listener the command listener
123 * @param manager the layout manager
124 */
125 public Toolbar( final ICommandListener listener, final ILayoutManager manager )
126 {
127 if( listener == null )
128 throw new IllegalArgumentException( "parameter 'listener' is null" );
129 if( manager == null )
130 throw new IllegalArgumentException( "parameter 'manager' is null" );
131 listener_ = listener;
132 manager_ = manager;
133 buttons_ = new Vector();
134 width_ = 0;
135 pages_ = 1;
136 maxPages_ = 1;
137 }
138
139 private int getPages()
140 {
141 if( pages_ > maxPages_ )
142 return maxPages_;
143 return pages_;
144 }
145
146 /***
147 * {@inheritDoc}
148 */
149 public void paint( final Graphics g )
150 {
151 paintBackground( g );
152 if( getPages() != 0 )
153 {
154 paintBorder( g );
155 paintButtons( g );
156 paintShrinkButton( g );
157 }
158 if( getPages() != maxPages_ )
159 paintExpandButton( g );
160 }
161
162 private void paintBorder( final Graphics g )
163 {
164 g.setColor( BORDER_COLOR );
165 g.setStrokeStyle( Graphics.SOLID );
166 g.drawLine( 0, 0, width_, 0 );
167 }
168
169 private void paintBackground( final Graphics g )
170 {
171 g.setColor( BACKGROUND_COLOR );
172 g.fillRect( 0, 0, width_, getHeight() );
173 }
174
175 private void paintButtons( final Graphics g )
176 {
177 g.setColor( BUTTON_HOVER_COLOR );
178 int start = 0;
179 int page = 0;
180 final Enumeration buttons = buttons_.elements();
181 while( buttons.hasMoreElements() )
182 {
183 final Button button = (Button)buttons.nextElement();
184 paintButton( g, start, pageToPixel( page ), button );
185 start += button.getWidth();
186 if( start >= width_ - SHRINK_EXPAND_AREA_WIDTH )
187 {
188 ++page;
189 start = 0;
190 }
191 }
192 }
193
194 private void paintButton( final Graphics g, final int x, final int y, final Button button )
195 {
196 g.translate( x, y );
197 button.paint( g );
198 g.translate( -x, -y );
199 }
200
201 private void paintShrinkButton( final Graphics g )
202 {
203 g.setColor( SHRINK_EXPAND_BUTTONS_COLOR );
204 final int x = width_ - SHRINK_EXPAND_AREA_WIDTH;
205 final int y = MIDDLE_OF_FIRST_ROW + SHRINK_EXPAND_BUTTONS_SHIFT;
206 g.fillTriangle( x, y, x + 2 * SHRINK_EXPAND_BUTTONS_SIZE, y, x + SHRINK_EXPAND_BUTTONS_SIZE, y
207 + SHRINK_EXPAND_BUTTONS_SIZE );
208 }
209
210 private void paintExpandButton( final Graphics g )
211 {
212 g.setColor( SHRINK_EXPAND_BUTTONS_COLOR );
213 final int x = width_ - SHRINK_EXPAND_AREA_WIDTH;
214 final int y = MIDDLE_OF_FIRST_ROW - SHRINK_EXPAND_BUTTONS_SHIFT;
215 g.fillTriangle( x, y, x + 2 * SHRINK_EXPAND_BUTTONS_SIZE, y, x + SHRINK_EXPAND_BUTTONS_SIZE, y
216 - SHRINK_EXPAND_BUTTONS_SIZE );
217 }
218
219 /***
220 * {@inheritDoc}
221 */
222 public void resize( final int width, final int height )
223 {
224 width_ = width;
225 final int maxPages = computeMaxPages();
226 if( maxPages_ != maxPages )
227 manager_.refresh();
228 maxPages_ = maxPages;
229 }
230
231 private int computeMaxPages()
232 {
233 int start = 0;
234 int maxPages = 1;
235 final Enumeration buttons = buttons_.elements();
236 while( buttons.hasMoreElements() )
237 {
238 final Button button = (Button)buttons.nextElement();
239 start += button.getWidth();
240 if( start >= width_ - SHRINK_EXPAND_AREA_WIDTH )
241 {
242 ++maxPages;
243 start = 0;
244 }
245 }
246 return maxPages;
247 }
248
249 /***
250 * {@inheritDoc}
251 */
252 public boolean click( final int x, final int y )
253 {
254 if( pixelToPage( y ) == 0 && x >= width_ - SHRINK_EXPAND_AREA_WIDTH )
255 {
256 if( getPages() == 0 || (getPages() != maxPages_ && y < MIDDLE_OF_FIRST_ROW) )
257 expand();
258 else
259 shrink();
260 }
261 else
262 clickButton( x, y );
263 return true;
264 }
265
266 private void clickButton( final int x, final int y )
267 {
268 int start = 0;
269 int page = 0;
270 final Enumeration buttons = buttons_.elements();
271 while( buttons.hasMoreElements() )
272 {
273 final Button button = (Button)buttons.nextElement();
274 if( x >= start && x < start + button.getWidth() && page == pixelToPage( y ) )
275 button.enter();
276 start += button.getWidth();
277 if( start >= width_ - SHRINK_EXPAND_AREA_WIDTH )
278 {
279 ++page;
280 start = 0;
281 }
282 }
283 }
284
285 private int pageToPixel( final int page )
286 {
287 return TOP_BORDER_HEIGHT + ROW_HEIGHT * page;
288 }
289
290 private int pixelToPage( final int y )
291 {
292 return (y - TOP_BORDER_HEIGHT) / ROW_HEIGHT;
293 }
294
295 private void shrink()
296 {
297 pages_ = Math.max( 0, getPages() - 1 );
298 manager_.refresh();
299 }
300
301 private void expand()
302 {
303 pages_ = Math.min( maxPages_, getPages() + 1 );
304 manager_.refresh();
305 }
306
307 /***
308 * {@inheritDoc}
309 */
310 public void drag( final int x, final int y )
311 {
312 final Enumeration buttons = buttons_.elements();
313 while( buttons.hasMoreElements() )
314 ((Button)buttons.nextElement()).leave();
315 if( y >= 0 && x < width_ - SHRINK_EXPAND_AREA_WIDTH )
316 click( x, y );
317 }
318
319 /***
320 * {@inheritDoc}
321 */
322 public void unclick( final int x, final int y )
323 {
324 final Enumeration buttons = buttons_.elements();
325 while( buttons.hasMoreElements() )
326 ((Button)buttons.nextElement()).trigger();
327 }
328
329 /***
330 * {@inheritDoc}
331 */
332 public void add( final ICommand command, final String image )
333 {
334 buttons_.addElement( new Button( command, listener_, image ) );
335 }
336
337 /***
338 * {@inheritDoc}
339 */
340 public int getLayout()
341 {
342 if( getPages() > 0 )
343 return Item.LAYOUT_EXPAND;
344 return Item.LAYOUT_DEFAULT;
345 }
346
347 /***
348 * {@inheritDoc}
349 */
350 public int getWidth()
351 {
352 if( getPages() > 0 )
353 return width_;
354 return SHRINK_EXPAND_AREA_WIDTH;
355 }
356
357 /***
358 * {@inheritDoc}
359 */
360 public int getHeight()
361 {
362 if( getPages() > 0 )
363 return ROW_HEIGHT * getPages() + TOP_BORDER_HEIGHT + BOTTOM_BORDER_HEIGHT;
364 return SHRINKED_HEIGHT;
365 }
366 }