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 }