1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package tsukuba_bunko.peko;
20
21 import java.awt.event.ActionListener;
22 import java.awt.event.MouseEvent;
23 import java.awt.event.MouseListener;
24 import java.awt.event.KeyEvent;
25 import java.awt.event.KeyListener;
26
27 import java.text.MessageFormat;
28
29 import java.util.Timer;
30 import java.util.TimerTask;
31
32 import javax.swing.ButtonGroup;
33 import javax.swing.ImageIcon;
34 import javax.swing.JButton;
35 import javax.swing.JCheckBoxMenuItem;
36 import javax.swing.JDialog;
37 import javax.swing.JFrame;
38 import javax.swing.JMenu;
39 import javax.swing.JMenuBar;
40 import javax.swing.JMenuItem;
41 import javax.swing.JOptionPane;
42 import javax.swing.JRadioButtonMenuItem;
43 import javax.swing.KeyStroke;
44
45 import tsukuba_bunko.util.GenericListener;
46
47 import tsukuba_bunko.peko.canvas.CanvasManager;
48
49 import tsukuba_bunko.peko.resource.ResourceManager;
50
51
52 /***
53 * プレイヤーの操作を受け付けるコントローラです。
54 * @author $Author: ppoi $
55 * @version $Revision: 1.6 $
56 */
57 public class ActionControler implements KeyListener, MouseListener {
58
59 /***
60 * プレイヤーが操作するまで停止するストップモード(デフォルト)
61 */
62 public static final long SM_DEFAULT = -1;
63
64 /***
65 * ノンストップモード
66 */
67 public static final long SM_NONSTOP = 0;
68
69
70 /***
71 * 早送りプレイ(次の選択肢までスキップ)モード
72 */
73 public static final int PM_SKIP = 1;
74
75 /***
76 * 通常プレイモード
77 */
78 public static final int PM_NORMAL = 0;
79
80 /***
81 * 自動送りプレイモード
82 */
83 public static final int PM_AUTOMATIC = 2;
84
85
86 /***
87 */
88 private Object _lock = this;
89
90 /***
91 * 有効かどうか
92 */
93 private boolean _activity = false;
94
95 /***
96 * プレイモード
97 */
98 private int _playMode = -1;
99
100 /***
101 * プレイモード変更ボタン(メニュー)リスト
102 */
103 private JMenuItem[] _buttons = new JMenuItem[3];
104
105 /***
106 * エフェクトを使用しないメニュー
107 */
108 private JMenuItem _usingEffectMenu = null;
109
110 /***
111 * セーブメニュー
112 */
113 private JMenuItem _saveMenu = null;
114
115 /***
116 * ロードメニュー
117 */
118 private JMenuItem _loadMenu = null;
119
120 /***
121 * タイトルに戻るメニュー
122 */
123 private JMenuItem _returnTileMenu = null;
124
125 /***
126 * 過去の文章メニュー
127 */
128 private JMenuItem _readAgainMenu = null;
129
130 /***
131 * タイマー
132 */
133 private Timer _timer = new Timer( true );
134
135 /***
136 * 最新のタスク
137 */
138 private TimerTask _lastTask = null;
139
140
141 /***
142 * 読み返しコントローラ
143 */
144 private TextHistoryControler _readAgainControler = null;
145
146 /***
147 * 最後に押されたキーのキーコード
148 */
149 private int _lastKeyCode = -1;
150
151 /***
152 * active 待ちかどうか
153 */
154 private boolean _waitingActivate = false;
155
156 /***
157 * テキストを隠しているかどうか
158 */
159 private boolean _hiddenText = false;
160
161
162 /***
163 * <code>ActionControler</code> のインスタンスを作成します。
164 */
165 public ActionControler()
166 {
167 super();
168 prepareMenuBar();
169 setPlayModeToNormal();
170 }
171
172 /***
173 * ActionContorler を有効にするかどうかを設定します。
174 * @param activity 有効にする場合 <code>false</code>、それ以外 <code>false</code>。
175 */
176 public void setActive( boolean activity )
177 {
178 _activity = activity;
179 if( !activity ) {
180 _lastKeyCode = -1;
181 }
182 else if( _playMode != ActionControler.PM_NORMAL ) {
183 if( _waitingActivate ) {
184 synchronized( _lock ) {
185 if( _waitingActivate ) {
186 _lock.notify();
187 }
188 }
189 }
190 }
191 }
192
193 /***
194 * ActionControler が有効かどうかを判定します。
195 * @return 有効な場合 <code>true</code>、それ以外 <code>false</code>。
196 */
197 public boolean isActive()
198 {
199 return _activity && !PekoSystem.getInstance().getCanvasManager().isShowingSelect();
200 }
201
202 /***
203 * セーブ機能の有効/無効を設定します。
204 * @param enabled 有効にする場合 <code>true</code>、それ以外の場合 <code>false</code>
205 */
206 public synchronized void setSaveEnabled( boolean enabled )
207 {
208 _saveMenu.setEnabled( enabled );
209 }
210
211 /***
212 * 現在のプレイモードを取得します。
213 */
214 public int getPlayMode()
215 {
216 return _playMode;
217 }
218
219 /***
220 * プレイモードを設定します。
221 * @param playMode プレイモード。<code>PM_FORWARD</code>、<code>PM_NORMAL</code>、<code>PM_AUTOMATIC</code> から選択。
222 */
223 protected void setPlayMode( int playMode )
224 {
225 _playMode = playMode;
226 _buttons[playMode].setSelected( true );
227 if( playMode == ActionControler.PM_SKIP ) {
228 _readAgainMenu.setEnabled( false );
229 }
230 else if( !_readAgainMenu.isEnabled() ) {
231 _readAgainMenu.setEnabled( true );
232 }
233 PekoSystem.getInstance().getCanvasManager().getStageCanvas().setUsingEffect( !_usingEffectMenu.isSelected() && (playMode != ActionControler.PM_SKIP) );
234 if( (_playMode != ActionControler.PM_NORMAL) && !PekoSystem.getInstance().getCanvasManager().isShowingSelect() ) {
235 start();
236 }
237 }
238
239 /***
240 * プレイモードを「早送り」に設定します。
241 */
242 public void setPlayModeToSKip()
243 {
244 setPlayMode( ActionControler.PM_SKIP );
245 }
246
247 /***
248 * プレイモードを「通常」に設定します。
249 */
250 public void setPlayModeToNormal()
251 {
252 setPlayMode( ActionControler.PM_NORMAL );
253 }
254
255 /***
256 * プレイモードを「自動」に設定します。
257 */
258 public void setPlayModeToAutomatic()
259 {
260 setPlayMode( ActionControler.PM_AUTOMATIC );
261 }
262
263 /***
264 * 画像切り替えエフェクトを使用するかどうかを切り替えます。
265 */
266 public void changeUsingEffect()
267 {
268 PekoSystem.getInstance().getCanvasManager().getStageCanvas().setUsingEffect( !_usingEffectMenu.isSelected() && (_playMode != ActionControler.PM_SKIP) );
269 }
270
271 /***
272 * 読み返しコントローラを表示します。
273 */
274 public void showReadAgain()
275 {
276 setActive( false );
277 _saveMenu.setEnabled( false );
278 _loadMenu.setEnabled( false );
279 _returnTileMenu.setEnabled( false );
280 _readAgainMenu.setEnabled( false );
281 if( _readAgainControler == null ) {
282 _readAgainControler = new TextHistoryControler();
283 _readAgainControler.setLocationRelativeTo( PekoSystem.getInstance().getMainWindow() );
284 }
285 _readAgainControler.setVisible( true );
286 setActive( true );
287 _saveMenu.setEnabled( true );
288 _loadMenu.setEnabled( true );
289 _returnTileMenu.setEnabled( true );
290 _readAgainMenu.setEnabled( true );
291 }
292
293 /***
294 * ロードダイアログを表示します。
295 */
296 public void showLoadDialog()
297 {
298 setActive( false );
299 _saveMenu.setEnabled( false );
300 _loadMenu.setEnabled( false );
301 _returnTileMenu.setEnabled( false );
302 _readAgainMenu.setEnabled( false );
303 PekoSystem system = PekoSystem.getInstance();
304 if( !system.load() ) {
305 setActive( true );
306 }
307 _saveMenu.setEnabled( true );
308 _loadMenu.setEnabled( true );
309 _returnTileMenu.setEnabled( true );
310 _readAgainMenu.setEnabled( true );
311 }
312
313 /***
314 * セーブダイアログを表示します。
315 */
316 public synchronized void showSaveDialog()
317 {
318 PekoSystem system = PekoSystem.getInstance();
319 setActive( false );
320 _saveMenu.setEnabled( false );
321 _loadMenu.setEnabled( false );
322 _returnTileMenu.setEnabled( false );
323 _readAgainMenu.setEnabled( false );
324 system.save();
325 _saveMenu.setEnabled( true );
326 _loadMenu.setEnabled( true );
327 _returnTileMenu.setEnabled( true );
328 _readAgainMenu.setEnabled( true );
329 setActive( true );
330 }
331
332 /***
333 * タイトル画面に戻ります。
334 */
335 public void returnTitle()
336 {
337 returnTitle( false );
338 }
339
340 /***
341 * タイトル画面に戻ります。
342 */
343 public void returnTitle( boolean silence )
344 {
345 setActive( false );
346 setPlayMode( ActionControler.PM_NORMAL );
347 _saveMenu.setEnabled( false );
348 _loadMenu.setEnabled( false );
349 _returnTileMenu.setEnabled( false );
350 _readAgainMenu.setEnabled( false );
351 if( silence ) {
352 PekoSystem.getInstance().showTitle();
353 }
354 else {
355 ResourceManager resources = ResourceManager.getInstance();
356 String message = (String)resources.getResource( "peko.dialog.return-title.message" );
357 if( message == null ) {
358 message = "Are you return title ?";
359 }
360
361 String title = (String)resources.getResource( "peko.dialog.return-title.title" );
362 if( title == null ) {
363 title = "Confirm";
364 }
365
366 if( JOptionPane.OK_OPTION == JOptionPane.showConfirmDialog(PekoSystem.getInstance().getMainWindow(), message, title, JOptionPane.OK_CANCEL_OPTION) ) {
367 PekoSystem.getInstance().showTitle();
368 }
369 else {
370 setActive( true );
371 }
372 }
373 _saveMenu.setEnabled( true );
374 _loadMenu.setEnabled( true );
375 _returnTileMenu.setEnabled( true );
376 _readAgainMenu.setEnabled( true );
377 }
378
379
380 /***
381 * {@link #stop()} により停止されたスレッドを再開します。
382 */
383 public void start()
384 {
385 synchronized( _lock ) {
386 if( _lastTask != null ) {
387 _lastTask.cancel();
388 }
389 _lastTask = null;
390 _lock.notify();
391 Logger.debug( "[system.controler] notify to finish wait." );
392 }
393 }
394
395 /***
396 * プレイヤーがアクションを起こすまで現在のスレッドを停止します。
397 */
398 public void stop()
399 {
400 stop( ActionControler.SM_DEFAULT );
401 }
402
403 /***
404 * 現在のスレッドを停止します。
405 * @param wait 待ち時間。プレイヤーが操作するまでストップする場合は <code>SM_DEFAULT</code> を、無停止の場合は <code>SM_NONSTOP</code> を指定する。
406 */
407 public void stop( long wait )
408 {
409 Logger.debug( "[system.controler] stop :" + wait );
410 if( (wait == 0) || (_playMode == ActionControler.PM_SKIP) ) {
411
412 }
413 else if( wait < 0 ) {
414 synchronized( _lock ) {
415 try {
416 if( (_playMode == ActionControler.PM_AUTOMATIC) && !PekoSystem.getInstance().getCanvasManager().isShowingSelect() ) {
417 TimerTask task = new TimerTask() {
418 public void run() {
419 synchronized( _lock ) {
420 _lock.notify();
421 }
422 }
423 };
424 _timer.schedule( task, 1000L );
425 _lastTask = task;
426 }
427 _lock.wait();
428 }
429 catch( InterruptedException ie ) {
430 Logger.debug( "[system.action] interrupted." );
431 }
432 }
433 }
434 else {
435 synchronized( _lock ) {
436 try {
437 TimerTask task = new TimerTask() {
438 public void run() {
439 synchronized( _lock ) {
440 _lock.notify();
441 }
442 }
443 };
444 _timer.schedule( task, wait );
445 _lastTask = task;
446 _lock.wait();
447 }
448 catch( InterruptedException ie ) {
449 Logger.debug( "[system.action] interrupted." );
450 }
451 }
452 }
453
454 if( !isActive() && (_lastTask != null) ) {
455 synchronized( _lock ) {
456 if( !_activity && (_lastTask != null) ) {
457 _waitingActivate = true;
458 try {
459 _lock.wait();
460 _lock.wait( 500 );
461 }
462 catch( InterruptedException ie ) {
463 }
464 _waitingActivate = false;
465 }
466 }
467 }
468
469 _lastTask = null;
470 Logger.debug( "[system.controler] finish wait." );
471 }
472
473
474 /***
475 * メニューバーを準備します。
476 */
477 private void prepareMenuBar()
478 {
479 PekoSystem system = PekoSystem.getInstance();
480 ResourceManager resources = ResourceManager.getInstance();
481
482 JMenuBar menubar = new JMenuBar();
483 JMenu menu = null;
484 JMenuItem item = null;
485
486 menu = new JMenu( (String)resources.getResource("peko.menu.system.menu-caption") );
487 menu.setMnemonic( 'S' );
488 menubar.add( menu );
489
490 item = new JMenuItem( (String)resources.getResource("peko.menu.system.save") );
491 item.setEnabled( false );
492 item.setMnemonic( 'S' );
493 item.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_MASK) );
494 GenericListener.connect( item, this, ActionListener.class, "actionPerformed", "showSaveDialog", true );
495 menu.add( item );
496 _saveMenu = item;
497
498 item = new JMenuItem( (String)resources.getResource("peko.menu.system.load") );
499 item.setEnabled( false );
500 item.setMnemonic( 'O' );
501 item.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_MASK) );
502 GenericListener.connect( item, this, ActionListener.class, "actionPerformed", "showLoadDialog", true );
503 menu.add( item );
504 _loadMenu = item;
505
506 menu.addSeparator();
507
508 item = new JMenuItem( (String)resources.getResource("peko.menu.system.return-title") );
509 item.setEnabled( false );
510 item.setMnemonic( 'T' );
511 item.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_T, KeyEvent.CTRL_MASK) );
512 GenericListener.connect( item, this, ActionListener.class, "actionPerformed", "returnTitle", true );
513 menu.add( item );
514 _returnTileMenu = item;
515
516 item = new JMenuItem( (String)resources.getResource("peko.menu.system.read-again") );
517 item.setEnabled( false );
518 item.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_R, 0) );
519 item.setMnemonic( 'R' );
520 GenericListener.connect( item, this, ActionListener.class, "actionPerformed", "showReadAgain" );
521 menu.add( item );
522 _readAgainMenu = item;
523
524 menu.addSeparator();
525
526 item = new JMenuItem( (String)resources.getResource("peko.menu.system.exit") );
527 item.setMnemonic( 'X' );
528 item.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_Q, KeyEvent.CTRL_MASK) );
529 GenericListener.connect( item, system, ActionListener.class, "actionPerformed", "exit" );
530 menu.add( item );
531
532
533 menu = new JMenu( (String)resources.getResource("peko.menu.config.menu-caption") );
534 menu.setMnemonic( 'C' );
535 menubar.add( menu );
536
537 JMenu submenu = new JMenu( (String)resources.getResource("peko.menu.config.playmode.menu-caption") );
538 submenu.setMnemonic( 'P' );
539 menu.add( submenu );
540
541 ButtonGroup group = new ButtonGroup();
542 item = new JRadioButtonMenuItem( (String)resources.getResource("peko.menu.config.playmode.normal") );
543 item.setMnemonic( 'N' );
544
545 GenericListener.connect( item, this, ActionListener.class, "actionPerformed", "setPlayModeToNormal" );
546 group.add( item );
547 submenu.add( item );
548 _buttons[ ActionControler.PM_NORMAL ] = item;
549
550 item = new JRadioButtonMenuItem( (String)resources.getResource("peko.menu.config.playmode.automatic") );
551 item.setMnemonic( 'A' );
552
553 GenericListener.connect( item, this, ActionListener.class, "actionPerformed", "setPlayModeToAutomatic" );
554 group.add( item );
555 submenu.add( item );
556 _buttons[ ActionControler.PM_AUTOMATIC ] = item;
557
558 item = new JRadioButtonMenuItem( (String)resources.getResource("peko.menu.config.playmode.skip") );
559 item.setMnemonic( 'S' );
560 item.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_S, 0) );
561 GenericListener.connect( item, this, ActionListener.class, "actionPerformed", "setPlayModeToSKip" );
562 group.add( item );
563 submenu.add( item );
564 _buttons[ ActionControler.PM_SKIP ] = item;
565
566 menu.addSeparator();
567
568 item = new JCheckBoxMenuItem( (String)resources.getResource("peko.menu.config.use-effect") );
569 item.setSelected( false );
570 item.setMnemonic( 'E' );
571 GenericListener.connect( item, this, ActionListener.class, "actionPerformed", "changeUsingEffect" );
572 menu.add( item );
573 _usingEffectMenu = item;
574
575
576 menu = new JMenu( (String)resources.getResource("peko.menu.help.menu-caption") );
577 menu.setMnemonic( 'H' );
578 menubar.add( menu );
579
580 item = new JMenuItem( (String)resources.getResource("peko.menu.help.help-contents") );
581 item.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0) );
582 item.setMnemonic( 'H' );
583 item.setEnabled( false );
584
585 menu.add( item );
586
587 item = new JMenuItem( (String)resources.getResource("peko.menu.help.version") );
588 item.setMnemonic( 'A' );
589 GenericListener.connect( item, system, ActionListener.class, "actionPerformed", "showGameVersionInfo" );
590 menu.add( item );
591
592 menu.addSeparator();
593
594 item = new JMenuItem( (String)resources.getResource("peko.menu.help.about-pvns") );
595 item.setMnemonic( 'P' );
596 GenericListener.connect( item, system, ActionListener.class, "actionPerformed", "showSystemVersionInfo" );
597 menu.add( item );
598
599
600 JFrame mainWindow = system.getMainWindow();
601 mainWindow.setJMenuBar( menubar );
602 mainWindow.pack();
603 mainWindow.setResizable( false );
604 }
605
606
607
608
609
610 public void mousePressed( MouseEvent ev )
611 {
612 }
613
614 public void mouseReleased( MouseEvent ev )
615 {
616 }
617
618 public void mouseEntered( MouseEvent ev )
619 {
620 }
621
622 public void mouseExited( MouseEvent ev )
623 {
624 }
625
626 public void mouseClicked( MouseEvent ev )
627 {
628 int code = ev.getModifiers();
629 if( isActive() && (code == MouseEvent.BUTTON1_MASK) ) {
630 start();
631 }
632 else if( (_playMode == ActionControler.PM_SKIP) && (code == MouseEvent.BUTTON3_MASK) ) {
633 setPlayModeToNormal();
634 return;
635 }
636 else if( _hiddenText ) {
637 CanvasManager canvasManager = PekoSystem.getInstance().getCanvasManager();
638 canvasManager.hideTextCanvas();
639 setActive( true );
640 }
641 }
642
643
644
645
646
647 public void keyPressed( KeyEvent ev )
648 {
649 if( isActive() ) {
650 int code = ev.getKeyCode();
651 if( _lastKeyCode == -1 ) {
652 _lastKeyCode = code;
653 }
654 else if( _lastKeyCode != code ) {
655 _lastKeyCode = -1;
656 }
657 }
658 }
659
660 public void keyReleased( KeyEvent ev )
661 {
662 int code = ev.getKeyCode();
663 if( code != _lastKeyCode ) {
664 _lastKeyCode = -1;
665 return;
666 }
667
668 if( _hiddenText ) {
669 CanvasManager canvasManager = PekoSystem.getInstance().getCanvasManager();
670 canvasManager.showTextCanvas();
671 _saveMenu.setEnabled( true );
672 _loadMenu.setEnabled( true );
673 _returnTileMenu.setEnabled( true );
674 _hiddenText = false;
675 }
676 else if( isActive() && (code == KeyEvent.VK_ENTER) ) {
677 start();
678 }
679 else if( (_playMode != ActionControler.PM_NORMAL) && (code == KeyEvent.VK_ESCAPE) ) {
680 setPlayModeToNormal();
681 }
682 else if( !_hiddenText && (code == KeyEvent.VK_SPACE) ) {
683 CanvasManager canvasManager = PekoSystem.getInstance().getCanvasManager();
684 _saveMenu.setEnabled( false );
685 _loadMenu.setEnabled( false );
686 _returnTileMenu.setEnabled( false );
687 canvasManager.hideTextCanvas();
688 _hiddenText = true;
689 }
690 _lastKeyCode = -1;
691 }
692
693 public void keyTyped( KeyEvent ev )
694 {
695 }
696 }
697
698
699
700
701 /***
702 * テキストの履歴をブラウジングするコントローラです。
703 */
704 class TextHistoryControler extends JDialog {
705
706 /***
707 * serial version UID
708 */
709 private static final long serialVersionUID = -5321584449609116120L;
710
711 /***
712 * 現在のインデックス
713 */
714 private int _index = 0;
715
716 /***
717 * キャンバスマネージャ
718 */
719 private CanvasManager _canvasManager = null;
720
721 /***
722 * 「前」ボタン
723 */
724 private JButton _previous = null;
725
726 /***
727 * 「次」ボタン
728 */
729 private JButton _next = null;
730
731
732 /***
733 * <code>TextHistoryControler</code> のインスタンスを作成します。
734 */
735 public TextHistoryControler()
736 {
737 super( PekoSystem.getInstance().getMainWindow(), true );
738 initialize();
739 }
740
741
742 /***
743 * 初期化処理を実行します。
744 */
745 private void initialize()
746 {
747 _canvasManager = PekoSystem.getInstance().getCanvasManager();
748 ClassLoader cl = getClass().getClassLoader();
749
750 getContentPane().setLayout( new java.awt.FlowLayout() );
751 JButton button = new JButton();
752 button.setIcon( new ImageIcon(cl.getResource("left-arrow.png")) );
753 getContentPane().add( button );
754 GenericListener.connect( button, this, ActionListener.class, "actionPerformed", "previous" );
755 _previous = button;
756 button.setEnabled( (_canvasManager.getPageHistoryCount() >= 1) );
757
758 button = new JButton();
759 button.setIcon( new ImageIcon(cl.getResource("right-arrow.png")) );
760 button.setEnabled( false );
761 getContentPane().add( button );
762 _next = button;
763 GenericListener.connect( button, this, ActionListener.class, "actionPerformed", "next" );
764
765 pack();
766 setResizable( false );
767 }
768
769 /***
770 * 前のページを表示します。
771 */
772 public void previous()
773 {
774 ResourceManager resources = ResourceManager.getInstance();
775 _index++;
776 setTitle( ((MessageFormat)resources.getResource("peko.dialog.read-again.title-format")).format(new Object[]{String.valueOf(_index)}) );
777 _next.setEnabled( (_index > 1 ) );
778 if( _index == _canvasManager.getPageHistoryCount() ) {
779 _previous.setEnabled( false );
780 }
781 _previous.setEnabled( _canvasManager.readAgain(_index) );
782 }
783
784 /***
785 * 次のページを表示します。
786 */
787 public void next()
788 {
789 ResourceManager resources = ResourceManager.getInstance();
790 _index--;
791 setTitle( ((MessageFormat)resources.getResource("peko.dialog.read-again.title-format")).format(new Object[]{String.valueOf(_index)}) );
792 _previous.setEnabled( true );
793 if( _index == 1 ) {
794 _next.setEnabled( false );
795 }
796 _canvasManager.readAgain( _index );
797 }
798
799
800
801
802
803 public void setVisible( boolean visible )
804 {
805 if( visible ) {
806 _index = 0;
807 if( _canvasManager.getPageHistoryCount() > 0 ) {
808 previous();
809 }
810 }
811 else {
812 _canvasManager.returnCurrent();
813 }
814 super.setVisible( visible );
815 }
816 }