OSDN Git Service

3c58acc47a3fddf4eac51c8332d4c5513aaf3b9f
[jindolf/Jindolf.git] / src / main / java / jp / sfjp / jindolf / summary / VillageDigest.java
1 /*
2  * Village digest GUI
3  *
4  * License : The MIT License
5  * Copyright(c) 2009 olyutorskii
6  */
7
8 package jp.sfjp.jindolf.summary;
9
10 import java.awt.Container;
11 import java.awt.Dimension;
12 import java.awt.EventQueue;
13 import java.awt.Frame;
14 import java.awt.GridBagConstraints;
15 import java.awt.GridBagLayout;
16 import java.awt.Image;
17 import java.awt.Insets;
18 import java.awt.LayoutManager;
19 import java.awt.Rectangle;
20 import java.awt.event.ActionEvent;
21 import java.awt.event.ActionListener;
22 import java.awt.event.ItemEvent;
23 import java.awt.event.ItemListener;
24 import java.awt.event.WindowAdapter;
25 import java.awt.event.WindowEvent;
26 import java.text.DateFormat;
27 import java.util.Date;
28 import java.util.List;
29 import javax.swing.BorderFactory;
30 import javax.swing.DefaultComboBoxModel;
31 import javax.swing.ImageIcon;
32 import javax.swing.JButton;
33 import javax.swing.JComboBox;
34 import javax.swing.JComponent;
35 import javax.swing.JDialog;
36 import javax.swing.JLabel;
37 import javax.swing.JPanel;
38 import javax.swing.JPopupMenu;
39 import javax.swing.JScrollPane;
40 import javax.swing.JTabbedPane;
41 import javax.swing.JTextArea;
42 import javax.swing.JViewport;
43 import javax.swing.border.Border;
44 import jp.sfjp.jindolf.data.Avatar;
45 import jp.sfjp.jindolf.data.Period;
46 import jp.sfjp.jindolf.data.Player;
47 import jp.sfjp.jindolf.data.Village;
48 import jp.sfjp.jindolf.dxchg.ClipboardAction;
49 import jp.sfjp.jindolf.dxchg.FaceIconSet;
50 import jp.sfjp.jindolf.dxchg.TextPopup;
51 import jp.sfjp.jindolf.dxchg.WebButton;
52 import jp.sfjp.jindolf.dxchg.WolfBBS;
53 import jp.sfjp.jindolf.util.GUIUtils;
54 import jp.sfjp.jindolf.util.Monodizer;
55 import jp.sfjp.jindolf.view.AvatarPics;
56 import jp.sourceforge.jindolf.corelib.GameRole;
57 import jp.sourceforge.jindolf.corelib.Team;
58
59 /**
60  * 決着のついた村のダイジェストを表示する。
61  */
62 @SuppressWarnings("serial")
63 public class VillageDigest
64         extends JDialog
65         implements ActionListener,
66                    ItemListener {
67
68     private static final String ITEMDELIM = " : ";
69
70
71     private final JComponent summaryPanel = buildSummaryPanel();
72
73     private final JLabel faceLabel = new JLabel();
74     private final ImageIcon faceIcon = new ImageIcon();
75     private final JComboBox<Avatar> playerBox = new JComboBox<>();
76     private final DefaultComboBoxModel<Avatar> playerListModel =
77             new DefaultComboBoxModel<>();
78     private final JButton prevPlayer = new JButton("↑");
79     private final JButton nextPlayer = new JButton("↓");
80     private final JLabel roleLabel = new JLabel();
81     private final JLabel destinyLabel = new JLabel();
82     private final JLabel specialSkillLabel = new JLabel();
83     private final JLabel entryLabel = new JLabel();
84     private final JLabel idLabel = new JLabel();
85     private final WebButton urlLine = new WebButton();
86     private final JComponent playerPanel = buildPlayerPanel();
87
88     private final JComboBox<FaceIconSet> iconSetBox = new JComboBox<>();
89     private final DefaultComboBoxModel<FaceIconSet> iconSetListModel =
90             new DefaultComboBoxModel<>();
91     private final JLabel authorLabel = new JLabel();
92     private final JLabel authorUrlLabel = new JLabel();
93     private final WebButton iconCatalog = new WebButton();
94     private final JButton genCastTableButton =
95             new JButton("キャスト表Wiki生成");
96     private final JButton copyClipButton =
97             new JButton("クリップボードにコピー");
98     private final JTextArea templateArea = new JTextArea();
99     private final JButton voteButton = new JButton("投票Wiki生成");
100     private final JButton vlgWikiButton = new JButton("村詳細Wiki生成");
101     private final JComponent clipboardPanel = buildClipboardPanel();
102
103     private final JButton closeButton = new JButton("閉じる");
104
105     private Village village;
106
107     private GameSummary gameSummary;
108
109
110     /**
111      * コンストラクタ。
112      * @param owner 親フレーム
113      */
114     public VillageDigest(Frame owner){
115         super(owner);
116         setModal(true);
117
118         GUIUtils.modifyWindowAttributes(this, true, false, true);
119
120         setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
121         addWindowListener(new WindowAdapter(){
122             @Override
123             public void windowClosing(WindowEvent event){
124                 actionClose();
125                 return;
126             }
127         });
128
129         this.faceLabel.setIcon(this.faceIcon);
130
131         this.playerBox.setModel(this.playerListModel);
132         this.playerBox.addItemListener(this);
133
134         this.prevPlayer.setMargin(new Insets(1, 1, 1, 1));
135         this.prevPlayer.addActionListener(this);
136         this.prevPlayer.setToolTipText("前のプレイヤー");
137
138         this.nextPlayer.setMargin(new Insets(1, 1, 1, 1));
139         this.nextPlayer.addActionListener(this);
140         this.nextPlayer.setToolTipText("次のプレイヤー");
141
142         this.iconSetBox.setModel(this.iconSetListModel);
143         this.iconSetBox.addItemListener(this);
144         for(FaceIconSet iconSet : WolfBBS.getFaceIconSetList()){
145             this.iconSetListModel.addElement(iconSet);
146         }
147
148         this.iconCatalog.setURLText(
149                 "http://wolfbbs.jp/"
150                 +"%A4%DE%A4%C8%A4%E1%A5%B5%A5%A4%A5%C8%A4%C7"
151                 +"%CD%F8%CD%D1%B2%C4%C7%BD%A4%CA%A5%A2%A5%A4"
152                 +"%A5%B3%A5%F3%B2%E8%C1%FC.html");
153         this.iconCatalog.setCaption("顔アイコン見本ページ");
154
155         this.templateArea.setEditable(true);
156         this.templateArea.setLineWrap(true);
157         Monodizer.monodize(this.templateArea);
158         JPopupMenu popup = new TextPopup();
159         this.templateArea.setComponentPopupMenu(popup);
160
161         this.genCastTableButton.addActionListener(this);
162         this.voteButton.addActionListener(this);
163         this.vlgWikiButton.addActionListener(this);
164         this.copyClipButton.addActionListener(this);
165
166         this.closeButton.addActionListener(this);
167
168         Monodizer.monodize(this.idLabel);
169         Monodizer.monodize(this.authorUrlLabel);
170
171         Container content = getContentPane();
172         design(content);
173
174         return;
175     }
176
177
178     /**
179      * キャプション付き項目をコンテナに追加。
180      * @param container コンテナ
181      * @param caption 項目キャプション名
182      * @param delimiter デリミタ文字
183      * @param item 項目アイテム
184      */
185     private static void addCaptionedItem(Container container,
186                                            CharSequence caption,
187                                            CharSequence delimiter,
188                                            Object item ){
189         LayoutManager layout = container.getLayout();
190         if( ! (layout instanceof GridBagLayout) ){
191             throw new IllegalArgumentException();
192         }
193
194         JLabel captionLabel   = new JLabel(caption.toString());
195         JLabel delimiterLabel = new JLabel(delimiter.toString());
196         JComponent itemComp;
197         if(item instanceof JComponent){
198             itemComp = (JComponent) item;
199         }else{
200             itemComp = new JLabel(item.toString());
201         }
202
203         GridBagConstraints constraints = new GridBagConstraints();
204
205         constraints.weightx = 0.0;
206         constraints.weighty = 0.0;
207         constraints.fill    = GridBagConstraints.NONE;
208         constraints.insets  = new Insets(2, 2, 2, 2);
209
210         constraints.gridwidth = 1;
211         constraints.anchor    = GridBagConstraints.NORTHEAST;
212         container.add(captionLabel, constraints);
213         container.add(delimiterLabel, constraints);
214
215         constraints.weightx = 1.0;
216         constraints.gridwidth = GridBagConstraints.REMAINDER;
217         constraints.anchor    = GridBagConstraints.NORTHWEST;
218         container.add(itemComp, constraints);
219
220         return;
221     }
222
223     /**
224      * キャプション付き項目をコンテナに追加。
225      * @param container コンテナ
226      * @param caption 項目キャプション名
227      * @param item 項目アイテム
228      */
229     private static void addCaptionedItem(Container container,
230                                            CharSequence caption,
231                                            Object item ){
232         addCaptionedItem(container, caption, ITEMDELIM, item);
233         return;
234     }
235
236     /**
237      * レイアウトの最後に詰め物をする。
238      * @param container コンテナ
239      */
240     private static void addFatPad(Container container){
241         LayoutManager layout = container.getLayout();
242         if( ! (layout instanceof GridBagLayout) ){
243             throw new IllegalArgumentException();
244         }
245
246         JComponent pad = new JPanel();
247
248         GridBagConstraints constraints = new GridBagConstraints();
249         constraints.weightx = 1.0;
250         constraints.weighty = 1.0;
251         constraints.fill    = GridBagConstraints.BOTH;
252         constraints.gridwidth = GridBagConstraints.REMAINDER;
253         container.add(pad, constraints);
254
255         return;
256     }
257
258     /**
259      * GridBagLayoutでレイアウトする空コンポーネントを生成する。
260      * @return 空コンポーネント
261      */
262     private static JComponent createGridBagComponent(){
263         JComponent result = new JPanel();
264         LayoutManager layout = new GridBagLayout();
265         result.setLayout(layout);
266         return result;
267     }
268
269     /**
270      * 村サマリ画面の生成。
271      * @return 村サマリ画面
272      */
273     private JComponent buildSummaryPanel(){
274         JComponent result = createGridBagComponent();
275
276         Border border = BorderFactory.createEmptyBorder(5, 5, 5, 5);
277         result.setBorder(border);
278
279         return result;
280     }
281
282     /**
283      * プレイヤーサマリ画面の生成。
284      * @return プレイヤーサマリ画面
285      */
286     private JComponent buildPlayerPanel(){
287         JComponent result = createGridBagComponent();
288         GridBagConstraints constraints = new GridBagConstraints();
289
290         constraints.weightx = 0.0;
291         constraints.weighty = 0.0;
292         constraints.fill    = GridBagConstraints.NONE;
293         constraints.anchor  = GridBagConstraints.NORTHEAST;
294         constraints.insets  = new Insets(2, 2, 2, 2);
295         result.add(this.faceLabel, constraints);
296
297         result.add(new JLabel(ITEMDELIM), constraints);
298
299         constraints.anchor  = GridBagConstraints.NORTHWEST;
300         result.add(this.playerBox, constraints);
301         result.add(this.prevPlayer, constraints);
302         constraints.gridwidth = GridBagConstraints.REMAINDER;
303         result.add(this.nextPlayer, constraints);
304
305         addCaptionedItem(result, "役職",      this.roleLabel);
306         addCaptionedItem(result, "運命",      this.destinyLabel);
307         addCaptionedItem(result, "特殊技能",  this.specialSkillLabel);
308         addCaptionedItem(result, "エントリ#", this.entryLabel);
309         addCaptionedItem(result, "ID",        this.idLabel);
310         addCaptionedItem(result, "URL",       this.urlLine);
311
312         constraints.weightx = 1.0;
313         constraints.weighty = 1.0;
314         constraints.fill    = GridBagConstraints.BOTH;
315         constraints.gridwidth = GridBagConstraints.REMAINDER;
316         result.add(new JPanel(), constraints);
317
318         Border border = BorderFactory.createEmptyBorder(5, 5, 5, 5);
319         result.setBorder(border);
320
321         return result;
322     }
323
324     /**
325      * キャスト表生成画面を生成する。
326      * @return キャスト表生成画面
327      */
328     private JComponent buildCastPanel(){
329         JComponent result = createGridBagComponent();
330         GridBagConstraints constraints = new GridBagConstraints();
331
332         constraints.anchor  = GridBagConstraints.NORTHEAST;
333         constraints.gridwidth = GridBagConstraints.REMAINDER;
334         constraints.insets  = new Insets(2, 2, 2, 2);
335         result.add(this.iconCatalog, constraints);
336
337         addCaptionedItem(result, "顔アイコンセットを選択", this.iconSetBox);
338         addCaptionedItem(result, "作者", this.authorLabel);
339         addCaptionedItem(result, "URL", this.authorUrlLabel);
340
341         constraints.weightx = 1.0;
342         constraints.weighty = 0.0;
343         constraints.fill    = GridBagConstraints.NONE;
344         constraints.insets  = new Insets(2, 2, 2, 2);
345         constraints.anchor  = GridBagConstraints.NORTHEAST;
346         constraints.gridwidth = GridBagConstraints.REMAINDER;
347         result.add(this.genCastTableButton, constraints);
348
349         Border border = BorderFactory.createTitledBorder("キャスト表Wiki生成");
350         result.setBorder(border);
351
352         return result;
353     }
354
355     /**
356      * 投票Box生成画面を生成する。
357      * @return 投票Box生成画面
358      */
359     private JComponent buildVotePanel(){
360         JComponent result = createGridBagComponent();
361         GridBagConstraints constraints = new GridBagConstraints();
362
363         constraints.weightx = 1.0;
364         constraints.weighty = 0.0;
365         constraints.fill    = GridBagConstraints.NONE;
366         constraints.anchor  = GridBagConstraints.NORTHEAST;
367         constraints.gridwidth = GridBagConstraints.REMAINDER;
368         constraints.insets  = new Insets(2, 2, 2, 2);
369         result.add(this.voteButton, constraints);
370
371         Border border = BorderFactory.createTitledBorder("投票Wiki生成");
372         result.setBorder(border);
373
374         return result;
375     }
376
377     /**
378      * 村詳細Wiki生成画面を生成する。
379      * @return 村詳細Wiki生成画面
380      */
381     private JComponent buildVillageWikiPanel(){
382         JComponent result = createGridBagComponent();
383         GridBagConstraints constraints = new GridBagConstraints();
384
385         constraints.weightx = 1.0;
386         constraints.weighty = 0.0;
387         constraints.fill    = GridBagConstraints.NONE;
388         constraints.anchor  = GridBagConstraints.NORTHEAST;
389         constraints.gridwidth = GridBagConstraints.REMAINDER;
390         constraints.insets  = new Insets(2, 2, 2, 2);
391         result.add(this.vlgWikiButton, constraints);
392
393         Border border = BorderFactory.createTitledBorder("村詳細Wiki生成");
394         result.setBorder(border);
395
396         return result;
397     }
398
399     /**
400      * Wikiテキスト領域GUIの生成。
401      * @return Wikiテキスト領域GUI
402      */
403     private JComponent buildClipText(){
404         JComponent result = createGridBagComponent();
405         GridBagConstraints constraints = new GridBagConstraints();
406
407         Border border;
408
409         constraints.insets = new Insets(2, 2, 2, 2);
410
411         constraints.weightx   = 0.0;
412         constraints.weighty   = 0.0;
413         constraints.fill      = GridBagConstraints.NONE;
414         constraints.anchor    = GridBagConstraints.NORTHEAST;
415         constraints.gridwidth = GridBagConstraints.REMAINDER;
416         result.add(this.copyClipButton, constraints);
417
418         border = BorderFactory.createEmptyBorder(2, 2, 2, 2);
419         this.templateArea.setBorder(border);
420         JScrollPane scroller = new JScrollPane();
421         JViewport viewPort = scroller.getViewport();
422         viewPort.setView(this.templateArea);
423         scroller.setHorizontalScrollBarPolicy(
424                 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
425         );
426         scroller.setMinimumSize(new Dimension(10, 50));
427
428         constraints.weightx = 1.0;
429         constraints.weighty = 1.0;
430         constraints.fill    = GridBagConstraints.BOTH;
431         constraints.gridwidth = GridBagConstraints.REMAINDER;
432         result.add(scroller, constraints);
433
434         border = BorderFactory.createTitledBorder("PukiWikiテキスト");
435         result.setBorder(border);
436
437         return result;
438     }
439
440     /**
441      * テンプレート生成画面を生成する。
442      * @return テンプレート生成画面
443      */
444     private JComponent buildClipboardPanel(){
445         JComponent result = createGridBagComponent();
446         GridBagConstraints constraints = new GridBagConstraints();
447
448         constraints.insets = new Insets(3, 3, 3, 3);
449
450         constraints.weightx = 1.0;
451         constraints.weighty = 0.0;
452         constraints.fill    = GridBagConstraints.HORIZONTAL;
453         constraints.anchor  = GridBagConstraints.NORTHWEST;
454         constraints.gridwidth = GridBagConstraints.REMAINDER;
455
456         JComponent castPanel = buildCastPanel();
457         result.add(castPanel, constraints);
458
459         JComponent vlgWikiPanel = buildVillageWikiPanel();
460         result.add(vlgWikiPanel, constraints);
461
462         JComponent votePanel = buildVotePanel();
463         result.add(votePanel, constraints);
464
465         constraints.fill    = GridBagConstraints.NONE;
466         constraints.anchor  = GridBagConstraints.CENTER;
467         result.add(new JLabel("↓↓↓"), constraints);
468         constraints.fill    = GridBagConstraints.HORIZONTAL;
469         constraints.anchor  = GridBagConstraints.NORTHWEST;
470
471         constraints.weightx = 1.0;
472         constraints.weighty = 1.0;
473         constraints.fill    = GridBagConstraints.BOTH;
474         constraints.gridwidth = GridBagConstraints.REMAINDER;
475         JComponent clipText = buildClipText();
476         result.add(clipText, constraints);
477
478         return result;
479     }
480
481     /**
482      * 画面レイアウトを行う。
483      * @param container コンテナ
484      */
485     private void design(Container container){
486         LayoutManager layout = new GridBagLayout();
487         GridBagConstraints constraints = new GridBagConstraints();
488
489         container.setLayout(layout);
490
491         JScrollPane scroller1 = new JScrollPane();
492         scroller1.getVerticalScrollBar().setUnitIncrement(15);
493         scroller1.getHorizontalScrollBar().setUnitIncrement(15);
494         JViewport viewPort1 = scroller1.getViewport();
495         viewPort1.setView(this.summaryPanel);
496
497         JScrollPane scroller2 = new JScrollPane();
498         scroller2.getVerticalScrollBar().setUnitIncrement(15);
499         scroller2.getHorizontalScrollBar().setUnitIncrement(15);
500         JViewport viewPort2 = scroller2.getViewport();
501         viewPort2.setView(this.playerPanel);
502
503         JTabbedPane tabComp = new JTabbedPane();
504         tabComp.add("村詳細", scroller1);
505         tabComp.add("プレイヤー詳細", scroller2);
506         tabComp.add("まとめサイト用Wiki生成", this.clipboardPanel);
507
508         constraints.weightx   = 1.0;
509         constraints.weighty   = 1.0;
510         constraints.fill      = GridBagConstraints.BOTH;
511         constraints.anchor    = GridBagConstraints.NORTHWEST;
512         constraints.gridwidth = GridBagConstraints.REMAINDER;
513         container.add(tabComp, constraints);
514
515         constraints.insets = new Insets(3, 3, 3, 3);
516         constraints.weightx = 0.0;
517         constraints.weighty = 0.0;
518         constraints.fill    = GridBagConstraints.NONE;
519         constraints.anchor  = GridBagConstraints.SOUTHEAST;
520         container.add(this.closeButton, constraints);
521
522         return;
523     }
524
525     /**
526      * このモーダルダイアログを閉じる。
527      */
528     private void actionClose(){
529         setVisible(false);
530         dispose();
531         return;
532     }
533
534     /**
535      * 村を設定する。
536      * @param village 村
537      */
538     public void setVillage(Village village){
539         clear();
540
541         this.village = village;
542         if(village == null) return;
543
544         this.gameSummary = new GameSummary(this.village);
545
546         updateSummary();
547
548         for(Player player : this.gameSummary.getPlayerList()){
549             Avatar avatar = player.getAvatar();
550             this.playerListModel.addElement(avatar);
551         }
552
553         if(this.playerListModel.getSize() >= 2){ // 強制イベント発生
554             Object player2nd = this.playerListModel.getElementAt(1);
555             this.playerListModel.setSelectedItem(player2nd);
556             Object player1st = this.playerListModel.getElementAt(0);
557             this.playerListModel.setSelectedItem(player1st);
558         }
559
560         return;
561     }
562
563     /**
564      * 村詳細画面の更新。
565      */
566     private void updateSummary(){
567         String villageName = this.village.getVillageFullName();
568
569         Team winnerTeam = this.gameSummary.getWinnerTeam();
570         String wonTeam = winnerTeam.getTeamName();
571
572         int avatarNum = this.gameSummary.countAvatarNum();
573         String totalMember = "ゲルト + " + (avatarNum - 1) + "名 = "
574                             + avatarNum + "名";
575
576         JComponent roleDetail = createGridBagComponent();
577         for(GameRole role : GameRole.values()){
578             List<Player> players = this.gameSummary.getRoledPlayerList(role);
579             if(players.size() <= 0) continue;
580             String roleName = role.getRoleName();
581             addCaptionedItem(roleDetail, roleName, " × ", players.size());
582         }
583
584         String suddenDeath = this.gameSummary.countSuddenDeath() + "名";
585
586         DateFormat dform =
587                 DateFormat.getDateTimeInstance(DateFormat.FULL,
588                                                DateFormat.FULL);
589         Date date;
590         date = this.gameSummary.get1stTalkDate();
591         String talk1st = dform.format(date);
592         date = this.gameSummary.getLastTalkDate();
593         String talkLast = dform.format(date);
594
595         int limitHour   = this.village.getLimitHour();
596         int limitMinute = this.village.getLimitMinute();
597         StringBuilder limit = new StringBuilder();
598         if(limitHour < 10) limit.append('0');
599         limit.append(limitHour).append(':');
600         if(limitMinute < 10) limit.append('0');
601         limit.append(limitMinute);
602
603         JComponent transition = createGridBagComponent();
604         for(int day = 1; day < this.village.getPeriodSize(); day++){
605             List<Player> players = this.gameSummary.getSurvivorList(day);
606             CharSequence roleSeq =
607                     GameSummary.getRoleBalanceSequence(players);
608             String daySeq;
609             Period period = this.village.getPeriod(day);
610             daySeq = period.getCaption();
611             addCaptionedItem(transition, daySeq, roleSeq);
612         }
613
614         StringBuilder schedule = new StringBuilder();
615         int progressDays = this.village.getProgressDays();
616         schedule.append("プロローグ + ")
617                 .append(progressDays)
618                 .append("日 + エピローグ");
619
620         CharSequence exeInfo = this.gameSummary.dumpExecutionInfo();
621         CharSequence eatInfo = this.gameSummary.dumpAssaultInfo();
622         CharSequence scoreSeer = this.gameSummary.dumpSeerActivity();
623         CharSequence scoreHunter = this.gameSummary.dumpHunterActivity();
624
625         this.summaryPanel.removeAll();
626
627         addCaptionedItem(this.summaryPanel, "村名",     villageName);
628         addCaptionedItem(this.summaryPanel, "勝者",     wonTeam);
629         addCaptionedItem(this.summaryPanel, "所要日数", schedule);
630         addCaptionedItem(this.summaryPanel, "更新時刻", limit);
631         addCaptionedItem(this.summaryPanel, "発言開始", talk1st);
632         addCaptionedItem(this.summaryPanel, "最終発言", talkLast);
633         addCaptionedItem(this.summaryPanel, "参加人数", totalMember);
634         addCaptionedItem(this.summaryPanel, "役職内訳", roleDetail);
635         addCaptionedItem(this.summaryPanel, "処刑内訳", exeInfo);
636         addCaptionedItem(this.summaryPanel, "襲撃内訳", eatInfo);
637         addCaptionedItem(this.summaryPanel, "突然死",   suddenDeath);
638         addCaptionedItem(this.summaryPanel, "人口推移", transition);
639         addCaptionedItem(this.summaryPanel, "占成績", scoreSeer);
640         addCaptionedItem(this.summaryPanel, "狩成績", scoreHunter);
641
642         addFatPad(this.summaryPanel);
643
644         return;
645     }
646
647     /**
648      * アクションイベントの振り分け。
649      * @param event アクションイベント
650      */
651     @Override
652     public void actionPerformed(ActionEvent event){
653         Object source = event.getSource();
654
655         if(source == this.closeButton){
656             actionClose();
657         }else if(source == this.copyClipButton){
658             actionCopyToClipboard();
659         }else if(source == this.genCastTableButton){
660             actionGenCastTable();
661         }else if(source == this.voteButton){
662             actionGenVoteBox();
663         }else if(source == this.vlgWikiButton){
664             actionGenVillageWiki();
665         }else if(source == this.prevPlayer){
666             int index = this.playerBox.getSelectedIndex();
667             if(index <= 0) return;
668             index--;
669             this.playerBox.setSelectedIndex(index);
670         }else if(source == this.nextPlayer){
671             int index = this.playerBox.getSelectedIndex();
672             int playerNum = this.playerBox.getItemCount();
673             if(index >= playerNum - 1) return;
674             index++;
675             this.playerBox.setSelectedIndex(index);
676         }
677
678         return;
679     }
680
681     /**
682      * キャスト表Wikiデータの生成を行う。
683      */
684     private void actionGenCastTable(){
685         Object selected = this.iconSetListModel.getSelectedItem();
686         if( ! (selected instanceof FaceIconSet) ) return;
687         FaceIconSet iconSet = (FaceIconSet) selected;
688
689         CharSequence wikiText = this.gameSummary.dumpCastingBoard(iconSet);
690
691         putWikiText(wikiText);
692
693         return;
694     }
695
696     /**
697      * 投票Boxを生成する。
698      */
699     private void actionGenVoteBox(){
700         CharSequence wikiText = this.gameSummary.dumpVoteBox();
701         putWikiText(wikiText);
702         return;
703     }
704
705     /**
706      * 村詳細Wikiを生成する。
707      */
708     private void actionGenVillageWiki(){
709         CharSequence wikiText = this.gameSummary.dumpVillageWiki();
710         putWikiText(wikiText);
711         return;
712     }
713
714     /**
715      * Wikiテキストをテキストボックスに出力する。
716      * スクロール位置は一番上に。
717      * @param wikiText Wikiテキスト
718      */
719     private void putWikiText(CharSequence wikiText){
720         this.templateArea.setText(wikiText.toString());
721         // 最上部へスクロールアップ
722         EventQueue.invokeLater(new Runnable(){
723             @Override
724             public void run(){
725                 JTextArea area = VillageDigest.this.templateArea;
726                 area.scrollRectToVisible(new Rectangle());
727             }
728         });
729         // TODO あらかじめテキストを全選択させておきたい
730         return;
731     }
732
733     /**
734      * Wiki文字列をクリップボードへコピーする。
735      */
736     private void actionCopyToClipboard(){
737         CharSequence text = this.templateArea.getText();
738         ClipboardAction.copyToClipboard(text);
739         return;
740     }
741
742     /**
743      * プレイヤーの選択操作。
744      * @param avatar 選択されたAvatar
745      */
746     private void selectPlayer(Avatar avatar){
747         if(avatar == this.playerBox.getItemAt(0)){
748             this.prevPlayer.setEnabled(false);
749         }else{
750             this.prevPlayer.setEnabled(true);
751         }
752
753         int playerNum = this.playerBox.getItemCount();
754         if(avatar == this.playerBox.getItemAt(playerNum - 1)){
755             this.nextPlayer.setEnabled(false);
756         }else{
757             this.nextPlayer.setEnabled(true);
758         }
759
760         AvatarPics avatarPics = this.village.getAvatarPics();
761         Image image = avatarPics.getAvatarFaceImage(avatar);
762         this.faceIcon.setImage(image);
763         this.faceLabel.setIcon(null);          // なぜかこれが必要
764         this.faceLabel.setIcon(this.faceIcon);
765
766         Player player = this.gameSummary.getPlayer(avatar);
767
768         GameRole role = player.getRole();
769         this.roleLabel.setText(role.getRoleName());
770
771         String destinyMessage = player.getDestinyMessage();
772         this.destinyLabel.setText(destinyMessage);
773
774         CharSequence specialSkill = "";
775         switch(role){
776         case SEER:
777             specialSkill = this.gameSummary.dumpSeerActivity();
778             break;
779         case HUNTER:
780             specialSkill = this.gameSummary.dumpHunterActivity();
781             break;
782         default:
783             break;
784         }
785         this.specialSkillLabel.setText(specialSkill.toString());
786
787         this.entryLabel.setText(String.valueOf(player.getEntryNo()));
788
789         String userId = player.getIdName();
790         this.idLabel.setText(userId);
791
792         String urlText = player.getUrlText();
793         String caption = urlText;
794
795         if(urlText == null || urlText.length() <= 0){
796             urlText = WolfBBS.encodeURLFromId(userId);
797             caption = "もしかして " + urlText;
798         }
799
800         this.urlLine.setURLText(urlText);
801         this.urlLine.setCaption(caption);
802
803         return;
804     }
805
806     /**
807      * 顔アイコンセットの選択操作。
808      * @param iconSet 顔アイコンセット
809      */
810     private void selectIconSet(FaceIconSet iconSet){
811         String author  = iconSet.getAuthor();
812         String urlText = iconSet.getUrlText();
813         this.authorLabel   .setText(author + "氏");
814         this.authorUrlLabel.setText(urlText);
815         return;
816     }
817
818     /**
819      * コンボボックス操作の受信。
820      * @param event コンボボックス操作イベント
821      */
822     @Override
823     public void itemStateChanged(ItemEvent event){
824         int state = event.getStateChange();
825         if(state != ItemEvent.SELECTED) return;
826
827         Object source = event.getSource();
828         Object item = event.getItem();
829         if(item == null) return;
830
831         if(source == this.playerBox){
832             if( ! (item instanceof Avatar) ) return;
833             Avatar avatar = (Avatar) item;
834             selectPlayer(avatar);
835         }else if(source == this.iconSetBox){
836             if( ! (item instanceof FaceIconSet) ) return;
837             FaceIconSet iconSet = (FaceIconSet) item;
838             selectIconSet(iconSet);
839         }
840
841         return;
842     }
843
844     /**
845      * 表示内容をクリアする。
846      */
847     private void clear(){
848         this.templateArea.setText("");
849         this.playerListModel.removeAllElements();
850         return;
851     }
852
853     // TODO ハムスター対応
854 }