View Javadoc

1   /*
2    * All Rights Reserved.
3    * Copyright (C) 1999-2005 Tsukuba Bunko.
4    *
5    * Licensed under the BSD License ("the License"); you may not use
6    * this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *       http://www.tsukuba-bunko.org/licenses/LICENSE.txt
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   *
17   * $Id: SelectCanvas.java,v 1.3 2005/07/24 20:55:57 ppoi Exp $
18   */
19  package tsukuba_bunko.peko.canvas.select;
20  
21  import	java.awt.Dimension;
22  
23  import	java.awt.event.KeyEvent;
24  import	java.awt.event.KeyListener;
25  
26  import	java.util.List;
27  import	java.util.Map;
28  
29  import	javax.swing.JComponent;
30  
31  import	tsukuba_bunko.peko.ActionControler;
32  import	tsukuba_bunko.peko.Logger;
33  import	tsukuba_bunko.peko.PekoSystem;
34  
35  
36  /***
37   * 選択肢を表示するキャンバスです。
38   * @author	$Author: ppoi $
39   * @version	$Revision: 1.3 $
40   */
41  public class SelectCanvas	extends JComponent	implements KeyListener	{
42  
43  	/***
44  	 * serial version UID
45  	 */
46  	private static final long	serialVersionUID	= 103943516971774006L;
47  
48  	/***
49  	 * ボタンの位置:左寄せ
50  	 */
51  	public static final int	ALIGN_LEFT = 0;
52  
53  	/***
54  	 * ボタンの位置:中央
55  	 */
56  	public static final int	ALIGN_CENTER = 1;
57  
58  	/***
59  	 * ボタンの位置:右寄せ
60  	 */
61  	public static final int	ALIGN_RIGHT = 2;
62  
63  	/***
64  	 * ボタンの位置:上
65  	 */
66  	public static final int	VALIGN_TOP = 0;
67  
68  	/***
69  	 * ボタンの位置:中央
70  	 */
71  	public static final int	VALIGN_MIDDLE = 1;
72  
73  	/***
74  	 * ボタンの位置:下
75  	 */
76  	public static final int	VALIGN_BOTTOM = 2;
77  
78  
79  	/***
80  	 * 有効フラグ
81  	 */
82  	private boolean	_active = false;
83  
84  	/***
85  	 * 選択肢リスト
86  	 */
87  	private List	_buttons = null;
88  
89  	/***
90  	 * 選択された ID
91  	 */
92  	private String	_id = null;
93  
94  	/***
95  	 * 現在選択中のボタン
96  	 */
97  	private SelectItemButton	_selected = null;
98  
99  	/***
100 	 * 現在選択中のボタンのインデックス
101 	 */
102 	private int	_selectedIndex = -1;
103 
104 
105 	/***
106 	 * 最後に押されたキーのキーコード
107 	 */
108 	private int	_lastKeyCode = -1;
109 
110 
111 	/***
112 	 * 選択肢ボタンの配置位置
113 	 */
114 	private int	_align = SelectCanvas.ALIGN_CENTER;
115 
116 	/***
117 	 */
118 	private int	_verticalAlign = SelectCanvas.VALIGN_TOP;
119 
120 	/***
121 	 * 選択肢の列数
122 	 */
123 	private int	_columns = 1;
124 
125 	/***
126 	 * 列間
127 	 */
128 	private int	_columnSpan = 20;
129 
130 	/***
131 	 * 行間
132 	 */
133 	private int	_rowSpan = 20;
134 
135 	/***
136 	 * 境界線とルートキャンバスの枠との間
137 	 */
138 	private int	_boundSpan = 20;
139 
140 	/***
141 	 * ボタンスタイルマップ
142 	 */
143 	private Map	_buttonStyle = null;
144 
145 	/***
146 	 * Dimension cache
147 	 */
148 	private Dimension	_dimensionCache = null;
149 
150 
151 	/***
152 	 * <code>SelectCanvas</code> のインスタンスを作成します。
153 	 */
154 	public SelectCanvas()
155 	{
156 		super();
157 	}
158 
159 
160 	/***
161 	 * 選択肢ボタンの配置位置を設定します。設定した内容は、次回選択指表示時から反映されます。
162 	 * @param	align	配置位置
163 	 * @throws	IllegalArgumentException	align が {@link SelectCanvas#ALIGN_CENTER} または {@link SelectCanvas#ALIGN_LEFT} または {@link SelectCanvas#ALIGN_RIGHT} 以外の場合。
164 	 */
165 	public void setAlignment( int align )
166 	{
167 		if( (align == SelectCanvas.ALIGN_CENTER) || (align == SelectCanvas.ALIGN_LEFT) || (align == SelectCanvas.ALIGN_RIGHT) )	{
168 			_align = align;
169 		}
170 		else	{
171 			throw new IllegalArgumentException( "invalid alignment type is specified." );
172 		}
173 	}
174 
175 	/***
176 	 * 選択肢ボタンの配置位置を取得します。
177 	 * @return	選択肢ボタンの配置位置
178 	 */
179 	public int getAlignment()
180 	{
181 		return _align;
182 	}
183 
184 	/***
185 	 * 選択肢ボタンの垂直方向の配置位置を設定します。
186 	 * @param	valign	配置位置
187 	 * @throws	IllegalArgumentException	align が {@link SelectCanvas#VALIGN_TOP} または {@link SelectCanvas#VALIGN_MIDDLE} または {@link SelectCanvas#VALIGN_BOTTOM} 以外の場合。
188 	 */
189 	public void setVerticalAlignment( int valign )
190 	{
191 		if( (valign == SelectCanvas.ALIGN_CENTER) || (valign == SelectCanvas.ALIGN_LEFT) || (valign == SelectCanvas.ALIGN_RIGHT) )	{
192 			_verticalAlign = valign;
193 		}
194 		else	{
195 			throw new IllegalArgumentException( "invalid vertical alignment type is specified." );
196 		}
197 	}
198 
199 	/***
200 	 * 選択肢ボタンの垂直方向の配置位置を取得します。
201 	 * @return	選択肢ボタンの配置位置
202 	 */
203 	public int getVericalAlignment()
204 	{
205 		return _verticalAlign;
206 	}
207 
208 	/***
209 	 * 選択肢ボタンの列数を設定します。設定した内容は、次回選択指表示時から反映されます。
210 	 * @param	columns 列数
211 	 * @throws	IllegalArgumentException	<code>columns <= 0</code> の場合
212 	 */
213 	public void setColumns( int columns )
214 	{
215 		_columns = columns;
216 	}
217 
218 	/***
219 	 * 選択肢ボタンの列数を取得します。
220 	 * @return	選択肢ボタンの列数
221 	 */
222 	public int getColumns()
223 	{
224 		return _columns;
225 	}
226 
227 	/***
228 	 * 列間を設定します。設定した内容は、次回選択指表示時から反映されます。
229 	 * @param	span	列間の長さ
230 	 */
231 	public void setColumnSpan( int span )
232 	{
233 		_columnSpan = span;
234 	}
235 
236 	/***
237 	 * 列間を取得します。
238 	 * @return	列間の長さ
239 	 */
240 	public int getColumnSpan()
241 	{
242 		return _columnSpan;
243 	}
244 
245 	/***
246 	 * 行間を設定します。設定した内容は、次回選択指表示時から反映されます。
247 	 * @param	span	行間の長さ
248 	 */
249 	public void setRowSpan( int span )
250 	{
251 		_rowSpan = span;
252 	}
253 
254 	/***
255 	 * 行間を取得します。
256 	 * @return	行間の長さ
257 	 */
258 	public int getRowSpan()
259 	{
260 		return _rowSpan;
261 	}
262 
263 	/***
264 	 * 選択肢領域の境界線とルートキャンバスの枠との間を設定します。設定した内容は、次回選択指表示時から反映されます。
265 	 * @param	span	行間の長さ
266 	 */
267 	public void setBoundSpan( int span )
268 	{
269 		_boundSpan = span;
270 	}
271 
272 	/***
273 	 * 選択肢領域の境界線とルートキャンバスの枠との間を取得します。
274 	 * @return	行間の長さ
275 	 */
276 	public int getBoundSpan()
277 	{
278 		return _boundSpan;
279 	}
280 
281 	/***
282 	 * 選択肢ボタンのスタイルを設定します。設定した内容は、次回選択指表示時から反映されます。
283 	 * @param	styles	選択肢ボタンのスタイルマップ
284 	 */
285 	public void setButtonStyle( Map styles )
286 	{
287 		_buttonStyle = styles;
288 	}
289 
290 	/***
291 	 * 選択肢ボタンのスタイルを取得します。
292 	 * @return	選択肢ボタンのスタイル
293 	 */
294 	public Map getButtonStyle()
295 	{
296 		return _buttonStyle;
297 	}
298 
299 	/***
300 	 * 選択肢を表示し、ユーザーが選択した選択肢の ID を取得します。
301 	 * @param	selectItems	選択肢
302 	 * @return	選択された選択肢の ID
303 	 */
304 	public String select( List selectItems )
305 	{
306 		return select( selectItems, -1 );
307 	}
308 
309 	/***
310 	 * 選択肢を表示し、ユーザーが選択した選択肢の ID を取得します。
311 	 * @param	selectItems	選択肢
312 	 * @param	timeout	タイムアウトするまでの時間[ms]
313 	 * @return	選択された選択肢の ID
314 	 */
315 	public String select( List selectItems, long timeout )
316 	{
317 		if( _buttonStyle == null )	{
318 			throw new IllegalStateException( "button styles is not specified." );
319 		}
320 
321 		if( _active )	{
322 			Logger.debug( "[canvas.select] already show selection." );
323 			return null;
324 		}
325 
326 		synchronized( this )	{
327 			_id = null;
328 
329 			Dimension	canvasSize = getSize( _dimensionCache );
330 
331 			int	size = selectItems.size();
332 			int	buttonWidth = 320;
333 			Integer	intValue = (Integer)_buttonStyle.get( SelectItemButton.STYLE_WIDTH );
334 			if( intValue != null )	{
335 				buttonWidth = intValue.intValue();
336 			}
337 
338 			setVisible( false );
339 
340 			SelectItem	item = null;
341 			SelectItemButton	button = null;
342 			List	buttons = new java.util.ArrayList( size );
343 			int	x = 0;
344 			int	y = 0;
345 			int	rowHeight = 0;
346 			Dimension	buttonSize = new Dimension( 0, 0 );
347 			for( int i = 0; i < size; ++i )	{
348 				if( (i % _columns) == 0 )	{
349 					y += rowHeight;
350 					if( item != null )	{
351 						y += _rowSpan;
352 					}
353 					x = 0;
354 					rowHeight = 0;
355 				}
356 				else	{
357 					x += (buttonSize.width + _columnSpan);
358 				}
359 
360 				item = (SelectItem)selectItems.get( i );
361 				button = new SelectItemButton( this );
362 				button.setSelectItem( item );
363 				button.prepare( _buttonStyle );
364 				buttons.add( button );
365 
366 				add( button );
367 				button.setLocation( x, y );
368 				buttonSize = button.getSize( buttonSize );
369 				if( buttonSize.height > rowHeight )	{
370 					rowHeight = buttonSize.height;
371 				}
372 			}
373 
374 			int	areaWidth = (buttonWidth * _columns) + (_columnSpan * (_columns - 1));
375 			int	leftPosition = 0;
376 			if( areaWidth > canvasSize.width )	{
377 				leftPosition = 0;
378 			}
379 			else if( (areaWidth + _boundSpan) > canvasSize.width )	{
380 				leftPosition = (canvasSize.width - areaWidth) / 2;
381 			}
382 			else if( _align == SelectCanvas.ALIGN_CENTER )	{
383 				leftPosition = (canvasSize.width - areaWidth) / 2;
384 			}
385 			else if( _align == SelectCanvas.ALIGN_LEFT )	{
386 				leftPosition = _boundSpan;
387 			}
388 			else if( _align == SelectCanvas.ALIGN_RIGHT )	{
389 				leftPosition = canvasSize.width - areaWidth - _boundSpan;
390 			}
391 
392 			int	areaHeight = y + rowHeight;
393 			int	topPosition = 0;
394 			if( areaHeight > canvasSize.height )	{
395 				topPosition = 0;
396 			}
397 			else if( (areaHeight + _boundSpan) > canvasSize.height )	{
398 				topPosition = (canvasSize.height - areaHeight) / 2;
399 			}
400 			else if( _verticalAlign == SelectCanvas.VALIGN_TOP )	{
401 				topPosition = _boundSpan;
402 			}
403 			else if( _verticalAlign == SelectCanvas.VALIGN_MIDDLE )	{
404 				topPosition = (canvasSize.height - areaHeight) / 2;
405 			}
406 			else if( _verticalAlign == SelectCanvas.VALIGN_BOTTOM )	{
407 				topPosition = canvasSize.height - areaHeight - _boundSpan;
408 			}
409 			setLocation( leftPosition, topPosition );
410 
411 			_buttons = buttons;
412 			_active = true;
413 
414 			setVisible( true );
415 		}
416 
417 		Logger.debug( "[canvas.select] wait for selecting." );
418 		ActionControler	controler = PekoSystem.getInstance().getActionControler();
419 		if( timeout > 0 )	{
420 			controler.stop( timeout );
421 		}
422 		else	{
423 			controler.stop();
424 		}
425 
426 		synchronized( this )	{
427 			int	size = _buttons.size();
428 			for( int i = 0; i < size; ++i )	{
429 				remove( (SelectItemButton)_buttons.get(i) );
430 			}
431 			_buttons.clear();
432 			_buttons = null;
433 		}
434 
435 		Logger.debug( "[canvas.select] id: " + _id );
436 		return _id;
437 	}
438 
439 	/***
440 	 * 選択肢の選択待ちを解除します。
441 	 */
442 	public void cancel()
443 	{
444 		_active = false;
445 		synchronized( this )	{
446 			if( _buttons != null )	{
447 				_id = null;
448 				ActionControler	controler = PekoSystem.getInstance().getActionControler();
449 				controler.start();
450 				Logger.debug( "[cenvas.select] canceled." );
451 			}
452 		}
453 	}
454 
455 
456 //
457 //	選択イベント処理
458 //
459 	/***
460 	 * <code>button</code> が選択状態になりました。
461 	 * @param	button	選択状態になった SelectItemButton インスタンス
462 	 */
463 	public void itemSelecting( SelectItemButton button )
464 	{
465 		SelectItemButton	old = _selected;
466 		_selected = button;
467 		if( (old != null) && (old != button) )	{
468 			old.setSelected( false );
469 		}
470 		_selectedIndex = _buttons.indexOf( button );
471 	}
472 
473 	/***
474 	 * <code>button</code> が非選択状態になりました
475 	 * @param	button	非選択状態になった SelectItemButton インスタンス
476 	 */
477 	public void itemDeselected( SelectItemButton button )
478 	{
479 		if( _selected == button )	{
480 			_selected = null;
481 			_selectedIndex = -1;
482 		}
483 	}
484 
485 	/***
486 	 * <code>button</code> が選択されました。
487 	 * @param	button	選択された SelectItemButton インスタンス
488 	 */
489 	public void itemSelected( SelectItemButton button )
490 	{
491 		if( _active )	{
492 			synchronized( this )	{
493 				if( _active )	{
494 					_selected = null;
495 					_selectedIndex = -1;
496 					_id = button.getSelectItem().getID();
497 					_active = false;
498 					Logger.debug( "[canvas.select] selected: " + _id );
499 					ActionControler	controler = PekoSystem.getInstance().getActionControler();
500 					controler.start();
501 				}
502 			}
503 		}
504 	}
505 
506 
507 //
508 //	KeyListener の実装
509 //
510 	public void keyPressed( KeyEvent ev )
511 	{
512 		if( _active )	{
513 			int	code = ev.getKeyCode();
514 			if( _lastKeyCode == -1 )	{
515 				_lastKeyCode = code;
516 			}
517 			else if( _lastKeyCode != code )	{
518 				_lastKeyCode = -1;
519 			}
520 
521 			int	index = _selectedIndex;
522 			int	size = _buttons.size();
523 
524 			if( code == KeyEvent.VK_DOWN )	{
525 				index = (index + 1) % size;
526 			}
527 			else if( (code == KeyEvent.VK_RIGHT) && (_columns > 1) )	{
528 				index = (index + 1) % size;
529 			}
530 			else if( code == KeyEvent.VK_UP )	 {
531 				index--;
532 				if( index < 0 )	{
533 					index = _buttons.size() -1 ;
534 				}
535 			}
536 			else if( (code == KeyEvent.VK_LEFT) && (_columns > 1) )	{
537 				index--;
538 				if( index < 0 )	{
539 					index = _buttons.size() -1 ;
540 				}
541 			}
542 			else	{
543 				return;
544 			}
545 			SelectItemButton	button = (SelectItemButton)_buttons.get( index );
546 			button.setSelected( true );
547 		}
548 	}
549 
550 	public void keyReleased( KeyEvent ev )
551 	{
552 		if( !_active || (_buttons == null) )	{
553 			return;
554 		}
555 
556 		int	code = ev.getKeyCode();
557 		if( code != _lastKeyCode )	{
558 			_lastKeyCode = -1;
559 			return;
560 		}
561 
562 		if( (code == KeyEvent.VK_ENTER) && (_selected != null) )	{
563 			itemSelected( _selected );
564 		}
565 		_lastKeyCode = -1;
566 	}
567 
568 	public void keyTyped( KeyEvent ev )
569 	{
570 	}
571 }