OSDN Git Service

Merge branch 'Branch_release-'
[jindolf/Jindolf.git] / src / main / java / jp / sfjp / jindolf / summary / VillageDigest.java
-/*\r
- * Village digest GUI\r
- *\r
- * Copyright(c) 2009 olyutorskii\r
- * $Id: VillageDigest.java 956 2009-12-13 15:14:07Z olyutorskii $\r
- */\r
-\r
-package jp.sourceforge.jindolf;\r
-\r
-import java.awt.Container;\r
-import java.awt.Dimension;\r
-import java.awt.EventQueue;\r
-import java.awt.Frame;\r
-import java.awt.GridBagConstraints;\r
-import java.awt.GridBagLayout;\r
-import java.awt.Image;\r
-import java.awt.Insets;\r
-import java.awt.LayoutManager;\r
-import java.awt.Rectangle;\r
-import java.awt.event.ActionEvent;\r
-import java.awt.event.ActionListener;\r
-import java.awt.event.ItemEvent;\r
-import java.awt.event.ItemListener;\r
-import java.awt.event.WindowAdapter;\r
-import java.awt.event.WindowEvent;\r
-import java.text.DateFormat;\r
-import java.util.Date;\r
-import java.util.List;\r
-import javax.swing.BorderFactory;\r
-import javax.swing.DefaultComboBoxModel;\r
-import javax.swing.ImageIcon;\r
-import javax.swing.JButton;\r
-import javax.swing.JComboBox;\r
-import javax.swing.JComponent;\r
-import javax.swing.JDialog;\r
-import javax.swing.JLabel;\r
-import javax.swing.JPanel;\r
-import javax.swing.JPopupMenu;\r
-import javax.swing.JScrollPane;\r
-import javax.swing.JTabbedPane;\r
-import javax.swing.JTextArea;\r
-import javax.swing.JViewport;\r
-import javax.swing.border.Border;\r
-import jp.sourceforge.jindolf.corelib.GameRole;\r
-import jp.sourceforge.jindolf.corelib.Team;\r
-\r
-/**\r
- * 決着のついた村のサマリを表示する。\r
- */\r
-@SuppressWarnings("serial")\r
-public class VillageDigest\r
-        extends JDialog\r
-        implements ActionListener,\r
-                   ItemListener {\r
-\r
-    private static final String FRAMETITLE =\r
-            "村のダイジェスト - " + Jindolf.TITLE;\r
-    private static final String ITEMDELIM = " : ";\r
-\r
-    /**\r
-     * キャプション付き項目をコンテナに追加。\r
-     * @param container コンテナ\r
-     * @param caption 項目キャプション名\r
-     * @param delimiter デリミタ文字\r
-     * @param item 項目アイテム\r
-     */\r
-    private static void addCaptionedItem(Container container,\r
-                                           CharSequence caption,\r
-                                           CharSequence delimiter,\r
-                                           Object item ){\r
-        LayoutManager layout = container.getLayout();\r
-        if( ! (layout instanceof GridBagLayout) ){\r
-            throw new IllegalArgumentException();\r
-        }\r
-\r
-        JLabel captionLabel   = new JLabel(caption.toString());\r
-        JLabel delimiterLabel = new JLabel(delimiter.toString());\r
-        JComponent itemComp;\r
-        if(item instanceof JComponent){\r
-            itemComp = (JComponent) item;\r
-        }else{\r
-            itemComp = new JLabel(item.toString());\r
-        }\r
-\r
-        GridBagConstraints constraints = new GridBagConstraints();\r
-\r
-        constraints.weightx = 0.0;\r
-        constraints.weighty = 0.0;\r
-        constraints.fill    = GridBagConstraints.NONE;\r
-        constraints.insets  = new Insets(2, 2, 2, 2);\r
-\r
-        constraints.gridwidth = 1;\r
-        constraints.anchor    = GridBagConstraints.NORTHEAST;\r
-        container.add(captionLabel, constraints);\r
-        container.add(delimiterLabel, constraints);\r
-\r
-        constraints.weightx = 1.0;\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        constraints.anchor    = GridBagConstraints.NORTHWEST;\r
-        container.add(itemComp, constraints);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * キャプション付き項目をコンテナに追加。\r
-     * @param container コンテナ\r
-     * @param caption 項目キャプション名\r
-     * @param item 項目アイテム\r
-     */\r
-    private static void addCaptionedItem(Container container,\r
-                                           CharSequence caption,\r
-                                           Object item ){\r
-        addCaptionedItem(container, caption, ITEMDELIM, item);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * レイアウトの最後に詰め物をする。\r
-     * @param container コンテナ\r
-     */\r
-    private static void addFatPad(Container container){\r
-        LayoutManager layout = container.getLayout();\r
-        if( ! (layout instanceof GridBagLayout) ){\r
-            throw new IllegalArgumentException();\r
-        }\r
-\r
-        JComponent pad = new JPanel();\r
-\r
-        GridBagConstraints constraints = new GridBagConstraints();\r
-        constraints.weightx = 1.0;\r
-        constraints.weighty = 1.0;\r
-        constraints.fill    = GridBagConstraints.BOTH;\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        container.add(pad, constraints);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * GridBagLayoutでレイアウトする空コンポーネントを生成する。\r
-     * @return 空コンポーネント\r
-     */\r
-    private static JComponent createGridBagComponent(){\r
-        JComponent result = new JPanel();\r
-        LayoutManager layout = new GridBagLayout();\r
-        result.setLayout(layout);\r
-        return result;\r
-    }\r
-\r
-    private final JComponent summaryPanel = buildSummaryPanel();\r
-\r
-    private final JLabel faceLabel = new JLabel();\r
-    private final ImageIcon faceIcon = new ImageIcon();\r
-    private final JComboBox playerBox = new JComboBox();\r
-    private final DefaultComboBoxModel playerListModel =\r
-            new DefaultComboBoxModel();\r
-    private final JButton prevPlayer = new JButton("↑");\r
-    private final JButton nextPlayer = new JButton("↓");\r
-    private final JLabel roleLabel = new JLabel();\r
-    private final JLabel destinyLabel = new JLabel();\r
-    private final JLabel specialSkillLabel = new JLabel();\r
-    private final JLabel entryLabel = new JLabel();\r
-    private final JLabel idLabel = new JLabel();\r
-    private final WebButton urlLine = new WebButton();\r
-    private final JComponent playerPanel = buildPlayerPanel();\r
-\r
-    private final JComboBox iconSetBox = new JComboBox();\r
-    private final DefaultComboBoxModel iconSetListModel =\r
-            new DefaultComboBoxModel();\r
-    private final JLabel authorLabel = new JLabel();\r
-    private final JLabel authorUrlLabel = new JLabel();\r
-    private final WebButton iconCatalog = new WebButton();\r
-    private final JButton genCastTableButton =\r
-            new JButton("キャスト表Wiki生成");\r
-    private final JButton copyClipButton =\r
-            new JButton("クリップボードにコピー");\r
-    private final JTextArea templateArea = new JTextArea();\r
-    private final JButton voteButton = new JButton("投票Wiki生成");\r
-    private final JButton vlgWikiButton = new JButton("村詳細Wiki生成");\r
-    private final JComponent clipboardPanel = buildClipboardPanel();\r
-\r
-    private final JButton closeButton = new JButton("閉じる");\r
-\r
-    private Village village;\r
-\r
-    private GameSummary gameSummary;\r
-\r
-    /**\r
-     * コンストラクタ。\r
-     * @param owner 親フレーム\r
-     */\r
-    public VillageDigest(Frame owner){\r
-        super(owner, FRAMETITLE, true);\r
-\r
-        GUIUtils.modifyWindowAttributes(this, true, false, true);\r
-\r
-        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);\r
-        addWindowListener(new WindowAdapter(){\r
-            @Override\r
-            public void windowClosing(WindowEvent event){\r
-                actionClose();\r
-                return;\r
-            }\r
-        });\r
-\r
-        this.faceLabel.setIcon(this.faceIcon);\r
-\r
-        this.playerBox.setModel(this.playerListModel);\r
-        this.playerBox.addItemListener(this);\r
-\r
-        this.prevPlayer.setMargin(new Insets(1, 1, 1, 1));\r
-        this.prevPlayer.addActionListener(this);\r
-        this.prevPlayer.setToolTipText("前のプレイヤー");\r
-\r
-        this.nextPlayer.setMargin(new Insets(1, 1, 1, 1));\r
-        this.nextPlayer.addActionListener(this);\r
-        this.nextPlayer.setToolTipText("次のプレイヤー");\r
-\r
-        this.iconSetBox.setModel(this.iconSetListModel);\r
-        this.iconSetBox.addItemListener(this);\r
-        for(FaceIconSet iconSet : WolfBBS.getFaceIconSetList()){\r
-            this.iconSetListModel.addElement(iconSet);\r
-        }\r
-\r
-        this.iconCatalog.setURLText(\r
-                "http://wolfbbs.jp/"\r
-                +"%A4%DE%A4%C8%A4%E1%A5%B5%A5%A4%A5%C8%A4%C7"\r
-                +"%CD%F8%CD%D1%B2%C4%C7%BD%A4%CA%A5%A2%A5%A4"\r
-                +"%A5%B3%A5%F3%B2%E8%C1%FC.html");\r
-        this.iconCatalog.setCaption("顔アイコン見本ページ");\r
-\r
-        this.templateArea.setEditable(true);\r
-        this.templateArea.setLineWrap(true);\r
-        Monodizer.monodize(this.templateArea);\r
-        JPopupMenu popup = new TextPopup();\r
-        this.templateArea.setComponentPopupMenu(popup);\r
-\r
-        this.genCastTableButton.addActionListener(this);\r
-        this.voteButton.addActionListener(this);\r
-        this.vlgWikiButton.addActionListener(this);\r
-        this.copyClipButton.addActionListener(this);\r
-\r
-        this.closeButton.addActionListener(this);\r
-\r
-        Monodizer.monodize(this.idLabel);\r
-        Monodizer.monodize(this.authorUrlLabel);\r
-\r
-        Container content = getContentPane();\r
-        design(content);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 村サマリ画面の生成。\r
-     * @return 村サマリ画面\r
-     */\r
-    private JComponent buildSummaryPanel(){\r
-        JComponent result = createGridBagComponent();\r
-\r
-        Border border = BorderFactory.createEmptyBorder(5, 5, 5, 5);\r
-        result.setBorder(border);\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * プレイヤーサマリ画面の生成。\r
-     * @return プレイヤーサマリ画面\r
-     */\r
-    private JComponent buildPlayerPanel(){\r
-        JComponent result = createGridBagComponent();\r
-        GridBagConstraints constraints = new GridBagConstraints();\r
-\r
-        constraints.weightx = 0.0;\r
-        constraints.weighty = 0.0;\r
-        constraints.fill    = GridBagConstraints.NONE;\r
-        constraints.anchor  = GridBagConstraints.NORTHEAST;\r
-        constraints.insets  = new Insets(2, 2, 2, 2);\r
-        result.add(this.faceLabel, constraints);\r
-\r
-        result.add(new JLabel(ITEMDELIM), constraints);\r
-\r
-        constraints.anchor  = GridBagConstraints.NORTHWEST;\r
-        result.add(this.playerBox, constraints);\r
-        result.add(this.prevPlayer, constraints);\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        result.add(this.nextPlayer, constraints);\r
-\r
-        addCaptionedItem(result, "役職",      this.roleLabel);\r
-        addCaptionedItem(result, "運命",      this.destinyLabel);\r
-        addCaptionedItem(result, "特殊技能",  this.specialSkillLabel);\r
-        addCaptionedItem(result, "エントリ#", this.entryLabel);\r
-        addCaptionedItem(result, "ID",        this.idLabel);\r
-        addCaptionedItem(result, "URL",       this.urlLine);\r
-\r
-        constraints.weightx = 1.0;\r
-        constraints.weighty = 1.0;\r
-        constraints.fill    = GridBagConstraints.BOTH;\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        result.add(new JPanel(), constraints);\r
-\r
-        Border border = BorderFactory.createEmptyBorder(5, 5, 5, 5);\r
-        result.setBorder(border);\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * キャスト表生成画面を生成する。\r
-     * @return キャスト表生成画面\r
-     */\r
-    private JComponent buildCastPanel(){\r
-        JComponent result = createGridBagComponent();\r
-        GridBagConstraints constraints = new GridBagConstraints();\r
-\r
-        constraints.anchor  = GridBagConstraints.NORTHEAST;\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        constraints.insets  = new Insets(2, 2, 2, 2);\r
-        result.add(this.iconCatalog, constraints);\r
-\r
-        addCaptionedItem(result, "顔アイコンセットを選択", this.iconSetBox);\r
-        addCaptionedItem(result, "作者", this.authorLabel);\r
-        addCaptionedItem(result, "URL", this.authorUrlLabel);\r
-\r
-        constraints.weightx = 1.0;\r
-        constraints.weighty = 0.0;\r
-        constraints.fill    = GridBagConstraints.NONE;\r
-        constraints.insets  = new Insets(2, 2, 2, 2);\r
-        constraints.anchor  = GridBagConstraints.NORTHEAST;\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        result.add(this.genCastTableButton, constraints);\r
-\r
-        Border border = BorderFactory.createTitledBorder("キャスト表Wiki生成");\r
-        result.setBorder(border);\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * 投票Box生成画面を生成する。\r
-     * @return 投票Box生成画面\r
-     */\r
-    private JComponent buildVotePanel(){\r
-        JComponent result = createGridBagComponent();\r
-        GridBagConstraints constraints = new GridBagConstraints();\r
-\r
-        constraints.weightx = 1.0;\r
-        constraints.weighty = 0.0;\r
-        constraints.fill    = GridBagConstraints.NONE;\r
-        constraints.anchor  = GridBagConstraints.NORTHEAST;\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        constraints.insets  = new Insets(2, 2, 2, 2);\r
-        result.add(this.voteButton, constraints);\r
-\r
-        Border border = BorderFactory.createTitledBorder("投票Wiki生成");\r
-        result.setBorder(border);\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * 村詳細Wiki生成画面を生成する。\r
-     * @return 村詳細Wiki生成画面\r
-     */\r
-    private JComponent buildVillageWikiPanel(){\r
-        JComponent result = createGridBagComponent();\r
-        GridBagConstraints constraints = new GridBagConstraints();\r
-\r
-        constraints.weightx = 1.0;\r
-        constraints.weighty = 0.0;\r
-        constraints.fill    = GridBagConstraints.NONE;\r
-        constraints.anchor  = GridBagConstraints.NORTHEAST;\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        constraints.insets  = new Insets(2, 2, 2, 2);\r
-        result.add(this.vlgWikiButton, constraints);\r
-\r
-        Border border = BorderFactory.createTitledBorder("村詳細Wiki生成");\r
-        result.setBorder(border);\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * Wikiテキスト領域GUIの生成。\r
-     * @return Wikiテキスト領域GUI\r
-     */\r
-    private JComponent buildClipText(){\r
-        JComponent result = createGridBagComponent();\r
-        GridBagConstraints constraints = new GridBagConstraints();\r
-\r
-        Border border;\r
-\r
-        constraints.insets = new Insets(2, 2, 2, 2);\r
-\r
-        constraints.weightx   = 0.0;\r
-        constraints.weighty   = 0.0;\r
-        constraints.fill      = GridBagConstraints.NONE;\r
-        constraints.anchor    = GridBagConstraints.NORTHEAST;\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        result.add(this.copyClipButton, constraints);\r
-\r
-        border = BorderFactory.createEmptyBorder(2, 2, 2, 2);\r
-        this.templateArea.setBorder(border);\r
-        JScrollPane scroller = new JScrollPane();\r
-        JViewport viewPort = scroller.getViewport();\r
-        viewPort.setView(this.templateArea);\r
-        scroller.setHorizontalScrollBarPolicy(\r
-                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER\r
-        );\r
-        scroller.setMinimumSize(new Dimension(10, 50));\r
-\r
-        constraints.weightx = 1.0;\r
-        constraints.weighty = 1.0;\r
-        constraints.fill    = GridBagConstraints.BOTH;\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        result.add(scroller, constraints);\r
-\r
-        border = BorderFactory.createTitledBorder("PukiWikiテキスト");\r
-        result.setBorder(border);\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * テンプレート生成画面を生成する。\r
-     * @return テンプレート生成画面\r
-     */\r
-    private JComponent buildClipboardPanel(){\r
-        JComponent result = createGridBagComponent();\r
-        GridBagConstraints constraints = new GridBagConstraints();\r
-\r
-        constraints.insets = new Insets(3, 3, 3, 3);\r
-\r
-        constraints.weightx = 1.0;\r
-        constraints.weighty = 0.0;\r
-        constraints.fill    = GridBagConstraints.HORIZONTAL;\r
-        constraints.anchor  = GridBagConstraints.NORTHWEST;\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-\r
-        JComponent castPanel = buildCastPanel();\r
-        result.add(castPanel, constraints);\r
-\r
-        JComponent vlgWikiPanel = buildVillageWikiPanel();\r
-        result.add(vlgWikiPanel, constraints);\r
-\r
-        JComponent votePanel = buildVotePanel();\r
-        result.add(votePanel, constraints);\r
-\r
-        constraints.fill    = GridBagConstraints.NONE;\r
-        constraints.anchor  = GridBagConstraints.CENTER;\r
-        result.add(new JLabel("↓↓↓"), constraints);\r
-        constraints.fill    = GridBagConstraints.HORIZONTAL;\r
-        constraints.anchor  = GridBagConstraints.NORTHWEST;\r
-\r
-        constraints.weightx = 1.0;\r
-        constraints.weighty = 1.0;\r
-        constraints.fill    = GridBagConstraints.BOTH;\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        JComponent clipText = buildClipText();\r
-        result.add(clipText, constraints);\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * 画面レイアウトを行う。\r
-     * @param container コンテナ\r
-     */\r
-    private void design(Container container){\r
-        LayoutManager layout = new GridBagLayout();\r
-        GridBagConstraints constraints = new GridBagConstraints();\r
-\r
-        container.setLayout(layout);\r
-\r
-        JScrollPane scroller1 = new JScrollPane();\r
-        scroller1.getVerticalScrollBar().setUnitIncrement(15);\r
-        scroller1.getHorizontalScrollBar().setUnitIncrement(15);\r
-        JViewport viewPort1 = scroller1.getViewport();\r
-        viewPort1.setView(this.summaryPanel);\r
-\r
-        JScrollPane scroller2 = new JScrollPane();\r
-        scroller2.getVerticalScrollBar().setUnitIncrement(15);\r
-        scroller2.getHorizontalScrollBar().setUnitIncrement(15);\r
-        JViewport viewPort2 = scroller2.getViewport();\r
-        viewPort2.setView(this.playerPanel);\r
-\r
-        JTabbedPane tabComp = new JTabbedPane();\r
-        tabComp.add("村詳細", scroller1);\r
-        tabComp.add("プレイヤー詳細", scroller2);\r
-        tabComp.add("まとめサイト用Wiki生成", this.clipboardPanel);\r
-\r
-        constraints.weightx   = 1.0;\r
-        constraints.weighty   = 1.0;\r
-        constraints.fill      = GridBagConstraints.BOTH;\r
-        constraints.anchor    = GridBagConstraints.NORTHWEST;\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        container.add(tabComp, constraints);\r
-\r
-        constraints.insets = new Insets(3, 3, 3, 3);\r
-        constraints.weightx = 0.0;\r
-        constraints.weighty = 0.0;\r
-        constraints.fill    = GridBagConstraints.NONE;\r
-        constraints.anchor  = GridBagConstraints.SOUTHEAST;\r
-        container.add(this.closeButton, constraints);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * このモーダルダイアログを閉じる。\r
-     */\r
-    private void actionClose(){\r
-        setVisible(false);\r
-        dispose();\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 村を設定する。\r
-     * @param village 村\r
-     */\r
-    public void setVillage(Village village){\r
-        clear();\r
-\r
-        this.village = village;\r
-        if(village == null) return;\r
-\r
-        this.gameSummary = new GameSummary(this.village);\r
-\r
-        updateSummary();\r
-\r
-        for(Player player : this.gameSummary.getPlayerList()){\r
-            Avatar avatar = player.getAvatar();\r
-            this.playerListModel.addElement(avatar);\r
-        }\r
-\r
-        if(this.playerListModel.getSize() >= 2){ // 強制イベント発生\r
-            Object player2nd = this.playerListModel.getElementAt(1);\r
-            this.playerListModel.setSelectedItem(player2nd);\r
-            Object player1st = this.playerListModel.getElementAt(0);\r
-            this.playerListModel.setSelectedItem(player1st);\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 村詳細画面の更新。\r
-     */\r
-    private void updateSummary(){\r
-        String villageName = this.village.getVillageFullName();\r
-\r
-        Team winnerTeam = this.gameSummary.getWinnerTeam();\r
-        String wonTeam = winnerTeam.getTeamName();\r
-\r
-        int avatarNum = this.gameSummary.countAvatarNum();\r
-        String totalMember = "ゲルト + " + (avatarNum - 1) + "名 = "\r
-                            + avatarNum + "名";\r
-\r
-        JComponent roleDetail = createGridBagComponent();\r
-        for(GameRole role : GameRole.values()){\r
-            List<Player> players = this.gameSummary.getRoledPlayerList(role);\r
-            if(players.size() <= 0) continue;\r
-            String roleName = role.getRoleName();\r
-            addCaptionedItem(roleDetail, roleName, " × ", players.size());\r
-        }\r
-\r
-        String suddenDeath = this.gameSummary.countSuddenDeath() + "名";\r
-\r
-        DateFormat dform =\r
-                DateFormat.getDateTimeInstance(DateFormat.FULL,\r
-                                               DateFormat.FULL);\r
-        Date date;\r
-        date = this.gameSummary.get1stTalkDate();\r
-        String talk1st = dform.format(date);\r
-        date = this.gameSummary.getLastTalkDate();\r
-        String talkLast = dform.format(date);\r
-\r
-        int limitHour   = this.village.getLimitHour();\r
-        int limitMinute = this.village.getLimitMinute();\r
-        StringBuilder limit = new StringBuilder();\r
-        if(limitHour < 10) limit.append('0');\r
-        limit.append(limitHour).append(':');\r
-        if(limitMinute < 10) limit.append('0');\r
-        limit.append(limitMinute);\r
-\r
-        JComponent transition = createGridBagComponent();\r
-        for(int day = 1; day < this.village.getPeriodSize(); day++){\r
-            List<Player> players = this.gameSummary.getSurvivorList(day);\r
-            CharSequence roleSeq =\r
-                    GameSummary.getRoleBalanceSequence(players);\r
-            String daySeq;\r
-            Period period = this.village.getPeriod(day);\r
-            daySeq = period.getCaption();\r
-            addCaptionedItem(transition, daySeq, roleSeq);\r
-        }\r
-\r
-        StringBuilder schedule = new StringBuilder();\r
-        int progressDays = this.village.getProgressDays();\r
-        schedule.append("プロローグ + ")\r
-                .append(progressDays)\r
-                .append("日 + エピローグ");\r
-\r
-        CharSequence exeInfo = this.gameSummary.dumpExecutionInfo();\r
-        CharSequence eatInfo = this.gameSummary.dumpAssaultInfo();\r
-        CharSequence scoreSeer = this.gameSummary.dumpSeerActivity();\r
-        CharSequence scoreHunter = this.gameSummary.dumpHunterActivity();\r
-\r
-        this.summaryPanel.removeAll();\r
-\r
-        addCaptionedItem(this.summaryPanel, "村名",     villageName);\r
-        addCaptionedItem(this.summaryPanel, "勝者",     wonTeam);\r
-        addCaptionedItem(this.summaryPanel, "所要日数", schedule);\r
-        addCaptionedItem(this.summaryPanel, "更新時刻", limit);\r
-        addCaptionedItem(this.summaryPanel, "発言開始", talk1st);\r
-        addCaptionedItem(this.summaryPanel, "最終発言", talkLast);\r
-        addCaptionedItem(this.summaryPanel, "参加人数", totalMember);\r
-        addCaptionedItem(this.summaryPanel, "役職内訳", roleDetail);\r
-        addCaptionedItem(this.summaryPanel, "処刑内訳", exeInfo);\r
-        addCaptionedItem(this.summaryPanel, "襲撃内訳", eatInfo);\r
-        addCaptionedItem(this.summaryPanel, "突然死",   suddenDeath);\r
-        addCaptionedItem(this.summaryPanel, "人口推移", transition);\r
-        addCaptionedItem(this.summaryPanel, "占成績", scoreSeer);\r
-        addCaptionedItem(this.summaryPanel, "狩成績", scoreHunter);\r
-\r
-        addFatPad(this.summaryPanel);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * アクションイベントの振り分け。\r
-     * @param event アクションイベント\r
-     */\r
-    public void actionPerformed(ActionEvent event){\r
-        Object source = event.getSource();\r
-\r
-        if(source == this.closeButton){\r
-            actionClose();\r
-        }else if(source == this.copyClipButton){\r
-            actionCopyToClipboard();\r
-        }else if(source == this.genCastTableButton){\r
-            actionGenCastTable();\r
-        }else if(source == this.voteButton){\r
-            actionGenVoteBox();\r
-        }else if(source == this.vlgWikiButton){\r
-            actionGenVillageWiki();\r
-        }else if(source == this.prevPlayer){\r
-            int index = this.playerBox.getSelectedIndex();\r
-            if(index <= 0) return;\r
-            index--;\r
-            this.playerBox.setSelectedIndex(index);\r
-        }else if(source == this.nextPlayer){\r
-            int index = this.playerBox.getSelectedIndex();\r
-            int playerNum = this.playerBox.getItemCount();\r
-            if(index >= playerNum - 1) return;\r
-            index++;\r
-            this.playerBox.setSelectedIndex(index);\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * キャスト表Wikiデータの生成を行う。\r
-     */\r
-    private void actionGenCastTable(){\r
-        Object selected = this.iconSetListModel.getSelectedItem();\r
-        if( ! (selected instanceof FaceIconSet) ) return;\r
-        FaceIconSet iconSet = (FaceIconSet) selected;\r
-\r
-        CharSequence wikiText = this.gameSummary.dumpCastingBoard(iconSet);\r
-\r
-        putWikiText(wikiText);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 投票Boxを生成する。\r
-     */\r
-    private void actionGenVoteBox(){\r
-        CharSequence wikiText = this.gameSummary.dumpVoteBox();\r
-        putWikiText(wikiText);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 村詳細Wikiを生成する。\r
-     */\r
-    private void actionGenVillageWiki(){\r
-        CharSequence wikiText = this.gameSummary.dumpVillageWiki();\r
-        putWikiText(wikiText);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * Wikiテキストをテキストボックスに出力する。\r
-     * スクロール位置は一番上に。\r
-     * @param wikiText Wikiテキスト\r
-     */\r
-    private void putWikiText(CharSequence wikiText){\r
-        this.templateArea.setText(wikiText.toString());\r
-        // 最上部へスクロールアップ\r
-        EventQueue.invokeLater(new Runnable(){\r
-            public void run(){\r
-                templateArea.scrollRectToVisible(new Rectangle());\r
-            }\r
-        });\r
-        // TODO あらかじめテキストを全選択させておきたい\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * Wiki文字列をクリップボードへコピーする。\r
-     */\r
-    private void actionCopyToClipboard(){\r
-        CharSequence text = this.templateArea.getText();\r
-        ClipboardAction.copyToClipboard(text);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * プレイヤーの選択操作。\r
-     * @param avatar 選択されたAvatar\r
-     */\r
-    private void selectPlayer(Avatar avatar){\r
-        if(avatar == this.playerBox.getItemAt(0)){\r
-            this.prevPlayer.setEnabled(false);\r
-        }else{\r
-            this.prevPlayer.setEnabled(true);\r
-        }\r
-\r
-        int playerNum = this.playerBox.getItemCount();\r
-        if(avatar == this.playerBox.getItemAt(playerNum - 1)){\r
-            this.nextPlayer.setEnabled(false);\r
-        }else{\r
-            this.nextPlayer.setEnabled(true);\r
-        }\r
-\r
-        Image image = village.getAvatarFaceImage(avatar);\r
-        this.faceIcon.setImage(image);\r
-        this.faceLabel.setIcon(null);          // なぜかこれが必要\r
-        this.faceLabel.setIcon(this.faceIcon);\r
-\r
-        Player player = this.gameSummary.getPlayer(avatar);\r
-\r
-        GameRole role = player.getRole();\r
-        this.roleLabel.setText(role.getRoleName());\r
-\r
-        String destinyMessage = player.getDestinyMessage();\r
-        this.destinyLabel.setText(destinyMessage);\r
-\r
-        CharSequence specialSkill = "";\r
-        switch(role){\r
-        case SEER:\r
-            specialSkill = this.gameSummary.dumpSeerActivity();\r
-            break;\r
-        case HUNTER:\r
-            specialSkill = this.gameSummary.dumpHunterActivity();\r
-            break;\r
-        default:\r
-            break;\r
-        }\r
-        this.specialSkillLabel.setText(specialSkill.toString());\r
-\r
-        this.entryLabel.setText(String.valueOf(player.getEntryNo()));\r
-\r
-        String userId = player.getIdName();\r
-        this.idLabel.setText(userId);\r
-\r
-        String urlText = player.getUrlText();\r
-        String caption = urlText;\r
-\r
-        if(urlText == null || urlText.length() <= 0){\r
-            urlText = WolfBBS.encodeURLFromId(userId);\r
-            caption = "もしかして " + urlText;\r
-        }\r
-\r
-        this.urlLine.setURLText(urlText);\r
-        this.urlLine.setCaption(caption);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 顔アイコンセットの選択操作。\r
-     * @param iconSet 顔アイコンセット\r
-     */\r
-    private void selectIconSet(FaceIconSet iconSet){\r
-        String author  = iconSet.getAuthor();\r
-        String urlText = iconSet.getUrlText();\r
-        this.authorLabel   .setText(author + "氏");\r
-        this.authorUrlLabel.setText(urlText);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * コンボボックス操作の受信。\r
-     * @param event コンボボックス操作イベント\r
-     */\r
-    public void itemStateChanged(ItemEvent event){\r
-        int state = event.getStateChange();\r
-        if(state != ItemEvent.SELECTED) return;\r
-\r
-        Object source = event.getSource();\r
-        Object item = event.getItem();\r
-        if(item == null) return;\r
-\r
-        if(source == this.playerBox){\r
-            if( ! (item instanceof Avatar) ) return;\r
-            Avatar avatar = (Avatar) item;\r
-            selectPlayer(avatar);\r
-        }else if(source == this.iconSetBox){\r
-            if( ! (item instanceof FaceIconSet) ) return;\r
-            FaceIconSet iconSet = (FaceIconSet) item;\r
-            selectIconSet(iconSet);\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 表示内容をクリアする。\r
-     */\r
-    private void clear(){\r
-        this.templateArea.setText("");\r
-        this.playerListModel.removeAllElements();\r
-        return;\r
-    }\r
-\r
-    // TODO ハムスター対応\r
-}\r
+/*
+ * Village digest GUI
+ *
+ * License : The MIT License
+ * Copyright(c) 2009 olyutorskii
+ */
+
+package jp.sfjp.jindolf.summary;
+
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.List;
+import javax.swing.BorderFactory;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextArea;
+import javax.swing.JViewport;
+import javax.swing.border.Border;
+import jp.sfjp.jindolf.data.Avatar;
+import jp.sfjp.jindolf.data.Period;
+import jp.sfjp.jindolf.data.Player;
+import jp.sfjp.jindolf.data.Village;
+import jp.sfjp.jindolf.dxchg.ClipboardAction;
+import jp.sfjp.jindolf.dxchg.FaceIconSet;
+import jp.sfjp.jindolf.dxchg.TextPopup;
+import jp.sfjp.jindolf.dxchg.WebButton;
+import jp.sfjp.jindolf.dxchg.WolfBBS;
+import jp.sfjp.jindolf.util.GUIUtils;
+import jp.sfjp.jindolf.util.Monodizer;
+import jp.sourceforge.jindolf.corelib.GameRole;
+import jp.sourceforge.jindolf.corelib.Team;
+
+/**
+ * 決着のついた村のダイジェストを表示する。
+ */
+@SuppressWarnings("serial")
+public class VillageDigest
+        extends JDialog
+        implements ActionListener,
+                   ItemListener {
+
+    private static final String ITEMDELIM = " : ";
+
+
+    private final JComponent summaryPanel = buildSummaryPanel();
+
+    private final JLabel faceLabel = new JLabel();
+    private final ImageIcon faceIcon = new ImageIcon();
+    private final JComboBox<Avatar> playerBox = new JComboBox<>();
+    private final DefaultComboBoxModel<Avatar> playerListModel =
+            new DefaultComboBoxModel<>();
+    private final JButton prevPlayer = new JButton("↑");
+    private final JButton nextPlayer = new JButton("↓");
+    private final JLabel roleLabel = new JLabel();
+    private final JLabel destinyLabel = new JLabel();
+    private final JLabel specialSkillLabel = new JLabel();
+    private final JLabel entryLabel = new JLabel();
+    private final JLabel idLabel = new JLabel();
+    private final WebButton urlLine = new WebButton();
+    private final JComponent playerPanel = buildPlayerPanel();
+
+    private final JComboBox<FaceIconSet> iconSetBox = new JComboBox<>();
+    private final DefaultComboBoxModel<FaceIconSet> iconSetListModel =
+            new DefaultComboBoxModel<>();
+    private final JLabel authorLabel = new JLabel();
+    private final JLabel authorUrlLabel = new JLabel();
+    private final WebButton iconCatalog = new WebButton();
+    private final JButton genCastTableButton =
+            new JButton("キャスト表Wiki生成");
+    private final JButton copyClipButton =
+            new JButton("クリップボードにコピー");
+    private final JTextArea templateArea = new JTextArea();
+    private final JButton voteButton = new JButton("投票Wiki生成");
+    private final JButton vlgWikiButton = new JButton("村詳細Wiki生成");
+    private final JComponent clipboardPanel = buildClipboardPanel();
+
+    private final JButton closeButton = new JButton("閉じる");
+
+    private Village village;
+
+    private GameSummary gameSummary;
+
+
+    /**
+     * コンストラクタ。
+     * @param owner 親フレーム
+     */
+    public VillageDigest(Frame owner){
+        super(owner);
+        setModal(true);
+
+        GUIUtils.modifyWindowAttributes(this, true, false, true);
+
+        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+        addWindowListener(new WindowAdapter(){
+            @Override
+            public void windowClosing(WindowEvent event){
+                actionClose();
+                return;
+            }
+        });
+
+        this.faceLabel.setIcon(this.faceIcon);
+
+        this.playerBox.setModel(this.playerListModel);
+        this.playerBox.addItemListener(this);
+
+        this.prevPlayer.setMargin(new Insets(1, 1, 1, 1));
+        this.prevPlayer.addActionListener(this);
+        this.prevPlayer.setToolTipText("前のプレイヤー");
+
+        this.nextPlayer.setMargin(new Insets(1, 1, 1, 1));
+        this.nextPlayer.addActionListener(this);
+        this.nextPlayer.setToolTipText("次のプレイヤー");
+
+        this.iconSetBox.setModel(this.iconSetListModel);
+        this.iconSetBox.addItemListener(this);
+        for(FaceIconSet iconSet : WolfBBS.getFaceIconSetList()){
+            this.iconSetListModel.addElement(iconSet);
+        }
+
+        this.iconCatalog.setURLText(
+                "http://wolfbbs.jp/"
+                +"%A4%DE%A4%C8%A4%E1%A5%B5%A5%A4%A5%C8%A4%C7"
+                +"%CD%F8%CD%D1%B2%C4%C7%BD%A4%CA%A5%A2%A5%A4"
+                +"%A5%B3%A5%F3%B2%E8%C1%FC.html");
+        this.iconCatalog.setCaption("顔アイコン見本ページ");
+
+        this.templateArea.setEditable(true);
+        this.templateArea.setLineWrap(true);
+        Monodizer.monodize(this.templateArea);
+        JPopupMenu popup = new TextPopup();
+        this.templateArea.setComponentPopupMenu(popup);
+
+        this.genCastTableButton.addActionListener(this);
+        this.voteButton.addActionListener(this);
+        this.vlgWikiButton.addActionListener(this);
+        this.copyClipButton.addActionListener(this);
+
+        this.closeButton.addActionListener(this);
+
+        Monodizer.monodize(this.idLabel);
+        Monodizer.monodize(this.authorUrlLabel);
+
+        Container content = getContentPane();
+        design(content);
+
+        return;
+    }
+
+
+    /**
+     * キャプション付き項目をコンテナに追加。
+     * @param container コンテナ
+     * @param caption 項目キャプション名
+     * @param delimiter デリミタ文字
+     * @param item 項目アイテム
+     */
+    private static void addCaptionedItem(Container container,
+                                           CharSequence caption,
+                                           CharSequence delimiter,
+                                           Object item ){
+        LayoutManager layout = container.getLayout();
+        if( ! (layout instanceof GridBagLayout) ){
+            throw new IllegalArgumentException();
+        }
+
+        JLabel captionLabel   = new JLabel(caption.toString());
+        JLabel delimiterLabel = new JLabel(delimiter.toString());
+        JComponent itemComp;
+        if(item instanceof JComponent){
+            itemComp = (JComponent) item;
+        }else{
+            itemComp = new JLabel(item.toString());
+        }
+
+        GridBagConstraints constraints = new GridBagConstraints();
+
+        constraints.weightx = 0.0;
+        constraints.weighty = 0.0;
+        constraints.fill    = GridBagConstraints.NONE;
+        constraints.insets  = new Insets(2, 2, 2, 2);
+
+        constraints.gridwidth = 1;
+        constraints.anchor    = GridBagConstraints.NORTHEAST;
+        container.add(captionLabel, constraints);
+        container.add(delimiterLabel, constraints);
+
+        constraints.weightx = 1.0;
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        constraints.anchor    = GridBagConstraints.NORTHWEST;
+        container.add(itemComp, constraints);
+
+        return;
+    }
+
+    /**
+     * キャプション付き項目をコンテナに追加。
+     * @param container コンテナ
+     * @param caption 項目キャプション名
+     * @param item 項目アイテム
+     */
+    private static void addCaptionedItem(Container container,
+                                           CharSequence caption,
+                                           Object item ){
+        addCaptionedItem(container, caption, ITEMDELIM, item);
+        return;
+    }
+
+    /**
+     * レイアウトの最後に詰め物をする。
+     * @param container コンテナ
+     */
+    private static void addFatPad(Container container){
+        LayoutManager layout = container.getLayout();
+        if( ! (layout instanceof GridBagLayout) ){
+            throw new IllegalArgumentException();
+        }
+
+        JComponent pad = new JPanel();
+
+        GridBagConstraints constraints = new GridBagConstraints();
+        constraints.weightx = 1.0;
+        constraints.weighty = 1.0;
+        constraints.fill    = GridBagConstraints.BOTH;
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        container.add(pad, constraints);
+
+        return;
+    }
+
+    /**
+     * GridBagLayoutでレイアウトする空コンポーネントを生成する。
+     * @return 空コンポーネント
+     */
+    private static JComponent createGridBagComponent(){
+        JComponent result = new JPanel();
+        LayoutManager layout = new GridBagLayout();
+        result.setLayout(layout);
+        return result;
+    }
+
+    /**
+     * 村サマリ画面の生成。
+     * @return 村サマリ画面
+     */
+    private JComponent buildSummaryPanel(){
+        JComponent result = createGridBagComponent();
+
+        Border border = BorderFactory.createEmptyBorder(5, 5, 5, 5);
+        result.setBorder(border);
+
+        return result;
+    }
+
+    /**
+     * プレイヤーサマリ画面の生成。
+     * @return プレイヤーサマリ画面
+     */
+    private JComponent buildPlayerPanel(){
+        JComponent result = createGridBagComponent();
+        GridBagConstraints constraints = new GridBagConstraints();
+
+        constraints.weightx = 0.0;
+        constraints.weighty = 0.0;
+        constraints.fill    = GridBagConstraints.NONE;
+        constraints.anchor  = GridBagConstraints.NORTHEAST;
+        constraints.insets  = new Insets(2, 2, 2, 2);
+        result.add(this.faceLabel, constraints);
+
+        result.add(new JLabel(ITEMDELIM), constraints);
+
+        constraints.anchor  = GridBagConstraints.NORTHWEST;
+        result.add(this.playerBox, constraints);
+        result.add(this.prevPlayer, constraints);
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        result.add(this.nextPlayer, constraints);
+
+        addCaptionedItem(result, "役職",      this.roleLabel);
+        addCaptionedItem(result, "運命",      this.destinyLabel);
+        addCaptionedItem(result, "特殊技能",  this.specialSkillLabel);
+        addCaptionedItem(result, "エントリ#", this.entryLabel);
+        addCaptionedItem(result, "ID",        this.idLabel);
+        addCaptionedItem(result, "URL",       this.urlLine);
+
+        constraints.weightx = 1.0;
+        constraints.weighty = 1.0;
+        constraints.fill    = GridBagConstraints.BOTH;
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        result.add(new JPanel(), constraints);
+
+        Border border = BorderFactory.createEmptyBorder(5, 5, 5, 5);
+        result.setBorder(border);
+
+        return result;
+    }
+
+    /**
+     * キャスト表生成画面を生成する。
+     * @return キャスト表生成画面
+     */
+    private JComponent buildCastPanel(){
+        JComponent result = createGridBagComponent();
+        GridBagConstraints constraints = new GridBagConstraints();
+
+        constraints.anchor  = GridBagConstraints.NORTHEAST;
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        constraints.insets  = new Insets(2, 2, 2, 2);
+        result.add(this.iconCatalog, constraints);
+
+        addCaptionedItem(result, "顔アイコンセットを選択", this.iconSetBox);
+        addCaptionedItem(result, "作者", this.authorLabel);
+        addCaptionedItem(result, "URL", this.authorUrlLabel);
+
+        constraints.weightx = 1.0;
+        constraints.weighty = 0.0;
+        constraints.fill    = GridBagConstraints.NONE;
+        constraints.insets  = new Insets(2, 2, 2, 2);
+        constraints.anchor  = GridBagConstraints.NORTHEAST;
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        result.add(this.genCastTableButton, constraints);
+
+        Border border = BorderFactory.createTitledBorder("キャスト表Wiki生成");
+        result.setBorder(border);
+
+        return result;
+    }
+
+    /**
+     * 投票Box生成画面を生成する。
+     * @return 投票Box生成画面
+     */
+    private JComponent buildVotePanel(){
+        JComponent result = createGridBagComponent();
+        GridBagConstraints constraints = new GridBagConstraints();
+
+        constraints.weightx = 1.0;
+        constraints.weighty = 0.0;
+        constraints.fill    = GridBagConstraints.NONE;
+        constraints.anchor  = GridBagConstraints.NORTHEAST;
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        constraints.insets  = new Insets(2, 2, 2, 2);
+        result.add(this.voteButton, constraints);
+
+        Border border = BorderFactory.createTitledBorder("投票Wiki生成");
+        result.setBorder(border);
+
+        return result;
+    }
+
+    /**
+     * 村詳細Wiki生成画面を生成する。
+     * @return 村詳細Wiki生成画面
+     */
+    private JComponent buildVillageWikiPanel(){
+        JComponent result = createGridBagComponent();
+        GridBagConstraints constraints = new GridBagConstraints();
+
+        constraints.weightx = 1.0;
+        constraints.weighty = 0.0;
+        constraints.fill    = GridBagConstraints.NONE;
+        constraints.anchor  = GridBagConstraints.NORTHEAST;
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        constraints.insets  = new Insets(2, 2, 2, 2);
+        result.add(this.vlgWikiButton, constraints);
+
+        Border border = BorderFactory.createTitledBorder("村詳細Wiki生成");
+        result.setBorder(border);
+
+        return result;
+    }
+
+    /**
+     * Wikiテキスト領域GUIの生成。
+     * @return Wikiテキスト領域GUI
+     */
+    private JComponent buildClipText(){
+        JComponent result = createGridBagComponent();
+        GridBagConstraints constraints = new GridBagConstraints();
+
+        Border border;
+
+        constraints.insets = new Insets(2, 2, 2, 2);
+
+        constraints.weightx   = 0.0;
+        constraints.weighty   = 0.0;
+        constraints.fill      = GridBagConstraints.NONE;
+        constraints.anchor    = GridBagConstraints.NORTHEAST;
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        result.add(this.copyClipButton, constraints);
+
+        border = BorderFactory.createEmptyBorder(2, 2, 2, 2);
+        this.templateArea.setBorder(border);
+        JScrollPane scroller = new JScrollPane();
+        JViewport viewPort = scroller.getViewport();
+        viewPort.setView(this.templateArea);
+        scroller.setHorizontalScrollBarPolicy(
+                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
+        );
+        scroller.setMinimumSize(new Dimension(10, 50));
+
+        constraints.weightx = 1.0;
+        constraints.weighty = 1.0;
+        constraints.fill    = GridBagConstraints.BOTH;
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        result.add(scroller, constraints);
+
+        border = BorderFactory.createTitledBorder("PukiWikiテキスト");
+        result.setBorder(border);
+
+        return result;
+    }
+
+    /**
+     * テンプレート生成画面を生成する。
+     * @return テンプレート生成画面
+     */
+    private JComponent buildClipboardPanel(){
+        JComponent result = createGridBagComponent();
+        GridBagConstraints constraints = new GridBagConstraints();
+
+        constraints.insets = new Insets(3, 3, 3, 3);
+
+        constraints.weightx = 1.0;
+        constraints.weighty = 0.0;
+        constraints.fill    = GridBagConstraints.HORIZONTAL;
+        constraints.anchor  = GridBagConstraints.NORTHWEST;
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+
+        JComponent castPanel = buildCastPanel();
+        result.add(castPanel, constraints);
+
+        JComponent vlgWikiPanel = buildVillageWikiPanel();
+        result.add(vlgWikiPanel, constraints);
+
+        JComponent votePanel = buildVotePanel();
+        result.add(votePanel, constraints);
+
+        constraints.fill    = GridBagConstraints.NONE;
+        constraints.anchor  = GridBagConstraints.CENTER;
+        result.add(new JLabel("↓↓↓"), constraints);
+        constraints.fill    = GridBagConstraints.HORIZONTAL;
+        constraints.anchor  = GridBagConstraints.NORTHWEST;
+
+        constraints.weightx = 1.0;
+        constraints.weighty = 1.0;
+        constraints.fill    = GridBagConstraints.BOTH;
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        JComponent clipText = buildClipText();
+        result.add(clipText, constraints);
+
+        return result;
+    }
+
+    /**
+     * 画面レイアウトを行う。
+     * @param container コンテナ
+     */
+    private void design(Container container){
+        LayoutManager layout = new GridBagLayout();
+        GridBagConstraints constraints = new GridBagConstraints();
+
+        container.setLayout(layout);
+
+        JScrollPane scroller1 = new JScrollPane();
+        scroller1.getVerticalScrollBar().setUnitIncrement(15);
+        scroller1.getHorizontalScrollBar().setUnitIncrement(15);
+        JViewport viewPort1 = scroller1.getViewport();
+        viewPort1.setView(this.summaryPanel);
+
+        JScrollPane scroller2 = new JScrollPane();
+        scroller2.getVerticalScrollBar().setUnitIncrement(15);
+        scroller2.getHorizontalScrollBar().setUnitIncrement(15);
+        JViewport viewPort2 = scroller2.getViewport();
+        viewPort2.setView(this.playerPanel);
+
+        JTabbedPane tabComp = new JTabbedPane();
+        tabComp.add("村詳細", scroller1);
+        tabComp.add("プレイヤー詳細", scroller2);
+        tabComp.add("まとめサイト用Wiki生成", this.clipboardPanel);
+
+        constraints.weightx   = 1.0;
+        constraints.weighty   = 1.0;
+        constraints.fill      = GridBagConstraints.BOTH;
+        constraints.anchor    = GridBagConstraints.NORTHWEST;
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        container.add(tabComp, constraints);
+
+        constraints.insets = new Insets(3, 3, 3, 3);
+        constraints.weightx = 0.0;
+        constraints.weighty = 0.0;
+        constraints.fill    = GridBagConstraints.NONE;
+        constraints.anchor  = GridBagConstraints.SOUTHEAST;
+        container.add(this.closeButton, constraints);
+
+        return;
+    }
+
+    /**
+     * このモーダルダイアログを閉じる。
+     */
+    private void actionClose(){
+        setVisible(false);
+        dispose();
+        return;
+    }
+
+    /**
+     * 村を設定する。
+     * @param village 村
+     */
+    public void setVillage(Village village){
+        clear();
+
+        this.village = village;
+        if(village == null) return;
+
+        this.gameSummary = new GameSummary(this.village);
+
+        updateSummary();
+
+        for(Player player : this.gameSummary.getPlayerList()){
+            Avatar avatar = player.getAvatar();
+            this.playerListModel.addElement(avatar);
+        }
+
+        if(this.playerListModel.getSize() >= 2){ // 強制イベント発生
+            Object player2nd = this.playerListModel.getElementAt(1);
+            this.playerListModel.setSelectedItem(player2nd);
+            Object player1st = this.playerListModel.getElementAt(0);
+            this.playerListModel.setSelectedItem(player1st);
+        }
+
+        return;
+    }
+
+    /**
+     * 村詳細画面の更新。
+     */
+    private void updateSummary(){
+        String villageName = this.village.getVillageFullName();
+
+        Team winnerTeam = this.gameSummary.getWinnerTeam();
+        String wonTeam = winnerTeam.getTeamName();
+
+        int avatarNum = this.gameSummary.countAvatarNum();
+        String totalMember = "ゲルト + " + (avatarNum - 1) + "名 = "
+                            + avatarNum + "名";
+
+        JComponent roleDetail = createGridBagComponent();
+        for(GameRole role : GameRole.values()){
+            List<Player> players = this.gameSummary.getRoledPlayerList(role);
+            if(players.size() <= 0) continue;
+            String roleName = role.getRoleName();
+            addCaptionedItem(roleDetail, roleName, " × ", players.size());
+        }
+
+        String suddenDeath = this.gameSummary.countSuddenDeath() + "名";
+
+        DateFormat dform =
+                DateFormat.getDateTimeInstance(DateFormat.FULL,
+                                               DateFormat.FULL);
+        Date date;
+        date = this.gameSummary.get1stTalkDate();
+        String talk1st = dform.format(date);
+        date = this.gameSummary.getLastTalkDate();
+        String talkLast = dform.format(date);
+
+        int limitHour   = this.village.getLimitHour();
+        int limitMinute = this.village.getLimitMinute();
+        StringBuilder limit = new StringBuilder();
+        if(limitHour < 10) limit.append('0');
+        limit.append(limitHour).append(':');
+        if(limitMinute < 10) limit.append('0');
+        limit.append(limitMinute);
+
+        JComponent transition = createGridBagComponent();
+        for(int day = 1; day < this.village.getPeriodSize(); day++){
+            List<Player> players = this.gameSummary.getSurvivorList(day);
+            CharSequence roleSeq =
+                    GameSummary.getRoleBalanceSequence(players);
+            String daySeq;
+            Period period = this.village.getPeriod(day);
+            daySeq = period.getCaption();
+            addCaptionedItem(transition, daySeq, roleSeq);
+        }
+
+        StringBuilder schedule = new StringBuilder();
+        int progressDays = this.village.getProgressDays();
+        schedule.append("プロローグ + ")
+                .append(progressDays)
+                .append("日 + エピローグ");
+
+        CharSequence exeInfo = this.gameSummary.dumpExecutionInfo();
+        CharSequence eatInfo = this.gameSummary.dumpAssaultInfo();
+        CharSequence scoreSeer = this.gameSummary.dumpSeerActivity();
+        CharSequence scoreHunter = this.gameSummary.dumpHunterActivity();
+
+        this.summaryPanel.removeAll();
+
+        addCaptionedItem(this.summaryPanel, "村名",     villageName);
+        addCaptionedItem(this.summaryPanel, "勝者",     wonTeam);
+        addCaptionedItem(this.summaryPanel, "所要日数", schedule);
+        addCaptionedItem(this.summaryPanel, "更新時刻", limit);
+        addCaptionedItem(this.summaryPanel, "発言開始", talk1st);
+        addCaptionedItem(this.summaryPanel, "最終発言", talkLast);
+        addCaptionedItem(this.summaryPanel, "参加人数", totalMember);
+        addCaptionedItem(this.summaryPanel, "役職内訳", roleDetail);
+        addCaptionedItem(this.summaryPanel, "処刑内訳", exeInfo);
+        addCaptionedItem(this.summaryPanel, "襲撃内訳", eatInfo);
+        addCaptionedItem(this.summaryPanel, "突然死",   suddenDeath);
+        addCaptionedItem(this.summaryPanel, "人口推移", transition);
+        addCaptionedItem(this.summaryPanel, "占成績", scoreSeer);
+        addCaptionedItem(this.summaryPanel, "狩成績", scoreHunter);
+
+        addFatPad(this.summaryPanel);
+
+        return;
+    }
+
+    /**
+     * アクションイベントの振り分け。
+     * @param event アクションイベント
+     */
+    @Override
+    public void actionPerformed(ActionEvent event){
+        Object source = event.getSource();
+
+        if(source == this.closeButton){
+            actionClose();
+        }else if(source == this.copyClipButton){
+            actionCopyToClipboard();
+        }else if(source == this.genCastTableButton){
+            actionGenCastTable();
+        }else if(source == this.voteButton){
+            actionGenVoteBox();
+        }else if(source == this.vlgWikiButton){
+            actionGenVillageWiki();
+        }else if(source == this.prevPlayer){
+            int index = this.playerBox.getSelectedIndex();
+            if(index <= 0) return;
+            index--;
+            this.playerBox.setSelectedIndex(index);
+        }else if(source == this.nextPlayer){
+            int index = this.playerBox.getSelectedIndex();
+            int playerNum = this.playerBox.getItemCount();
+            if(index >= playerNum - 1) return;
+            index++;
+            this.playerBox.setSelectedIndex(index);
+        }
+
+        return;
+    }
+
+    /**
+     * キャスト表Wikiデータの生成を行う。
+     */
+    private void actionGenCastTable(){
+        Object selected = this.iconSetListModel.getSelectedItem();
+        if( ! (selected instanceof FaceIconSet) ) return;
+        FaceIconSet iconSet = (FaceIconSet) selected;
+
+        CharSequence wikiText = this.gameSummary.dumpCastingBoard(iconSet);
+
+        putWikiText(wikiText);
+
+        return;
+    }
+
+    /**
+     * 投票Boxを生成する。
+     */
+    private void actionGenVoteBox(){
+        CharSequence wikiText = this.gameSummary.dumpVoteBox();
+        putWikiText(wikiText);
+        return;
+    }
+
+    /**
+     * 村詳細Wikiを生成する。
+     */
+    private void actionGenVillageWiki(){
+        CharSequence wikiText = this.gameSummary.dumpVillageWiki();
+        putWikiText(wikiText);
+        return;
+    }
+
+    /**
+     * Wikiテキストをテキストボックスに出力する。
+     * スクロール位置は一番上に。
+     * @param wikiText Wikiテキスト
+     */
+    private void putWikiText(CharSequence wikiText){
+        this.templateArea.setText(wikiText.toString());
+        // 最上部へスクロールアップ
+        EventQueue.invokeLater(new Runnable(){
+            @Override
+            public void run(){
+                JTextArea area = VillageDigest.this.templateArea;
+                area.scrollRectToVisible(new Rectangle());
+            }
+        });
+        // TODO あらかじめテキストを全選択させておきたい
+        return;
+    }
+
+    /**
+     * Wiki文字列をクリップボードへコピーする。
+     */
+    private void actionCopyToClipboard(){
+        CharSequence text = this.templateArea.getText();
+        ClipboardAction.copyToClipboard(text);
+        return;
+    }
+
+    /**
+     * プレイヤーの選択操作。
+     * @param avatar 選択されたAvatar
+     */
+    private void selectPlayer(Avatar avatar){
+        if(avatar == this.playerBox.getItemAt(0)){
+            this.prevPlayer.setEnabled(false);
+        }else{
+            this.prevPlayer.setEnabled(true);
+        }
+
+        int playerNum = this.playerBox.getItemCount();
+        if(avatar == this.playerBox.getItemAt(playerNum - 1)){
+            this.nextPlayer.setEnabled(false);
+        }else{
+            this.nextPlayer.setEnabled(true);
+        }
+
+        Image image = this.village.getAvatarFaceImage(avatar);
+        this.faceIcon.setImage(image);
+        this.faceLabel.setIcon(null);          // なぜかこれが必要
+        this.faceLabel.setIcon(this.faceIcon);
+
+        Player player = this.gameSummary.getPlayer(avatar);
+
+        GameRole role = player.getRole();
+        this.roleLabel.setText(role.getRoleName());
+
+        String destinyMessage = player.getDestinyMessage();
+        this.destinyLabel.setText(destinyMessage);
+
+        CharSequence specialSkill = "";
+        switch(role){
+        case SEER:
+            specialSkill = this.gameSummary.dumpSeerActivity();
+            break;
+        case HUNTER:
+            specialSkill = this.gameSummary.dumpHunterActivity();
+            break;
+        default:
+            break;
+        }
+        this.specialSkillLabel.setText(specialSkill.toString());
+
+        this.entryLabel.setText(String.valueOf(player.getEntryNo()));
+
+        String userId = player.getIdName();
+        this.idLabel.setText(userId);
+
+        String urlText = player.getUrlText();
+        String caption = urlText;
+
+        if(urlText == null || urlText.length() <= 0){
+            urlText = WolfBBS.encodeURLFromId(userId);
+            caption = "もしかして " + urlText;
+        }
+
+        this.urlLine.setURLText(urlText);
+        this.urlLine.setCaption(caption);
+
+        return;
+    }
+
+    /**
+     * 顔アイコンセットの選択操作。
+     * @param iconSet 顔アイコンセット
+     */
+    private void selectIconSet(FaceIconSet iconSet){
+        String author  = iconSet.getAuthor();
+        String urlText = iconSet.getUrlText();
+        this.authorLabel   .setText(author + "氏");
+        this.authorUrlLabel.setText(urlText);
+        return;
+    }
+
+    /**
+     * コンボボックス操作の受信。
+     * @param event コンボボックス操作イベント
+     */
+    @Override
+    public void itemStateChanged(ItemEvent event){
+        int state = event.getStateChange();
+        if(state != ItemEvent.SELECTED) return;
+
+        Object source = event.getSource();
+        Object item = event.getItem();
+        if(item == null) return;
+
+        if(source == this.playerBox){
+            if( ! (item instanceof Avatar) ) return;
+            Avatar avatar = (Avatar) item;
+            selectPlayer(avatar);
+        }else if(source == this.iconSetBox){
+            if( ! (item instanceof FaceIconSet) ) return;
+            FaceIconSet iconSet = (FaceIconSet) item;
+            selectIconSet(iconSet);
+        }
+
+        return;
+    }
+
+    /**
+     * 表示内容をクリアする。
+     */
+    private void clear(){
+        this.templateArea.setText("");
+        this.playerListModel.removeAllElements();
+        return;
+    }
+
+    // TODO ハムスター対応
+}