OSDN Git Service

Merge branch 'Branch_release-'
[jindolf/Jindolf.git] / src / main / java / jp / sfjp / jindolf / view / FilterPanel.java
-/*\r
- * Filter panel\r
- *\r
- * Copyright(c) 2008 olyutorskii\r
- * $Id: FilterPanel.java 969 2009-12-24 16:12:58Z olyutorskii $\r
- */\r
-\r
-package jp.sourceforge.jindolf;\r
-\r
-import java.awt.Container;\r
-import java.awt.Frame;\r
-import java.awt.GridBagConstraints;\r
-import java.awt.GridBagLayout;\r
-import java.awt.Insets;\r
-import java.awt.event.ActionEvent;\r
-import java.awt.event.ActionListener;\r
-import java.util.BitSet;\r
-import java.util.HashMap;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-import java.util.Map;\r
-import javax.swing.BorderFactory;\r
-import javax.swing.JButton;\r
-import javax.swing.JCheckBox;\r
-import javax.swing.JComponent;\r
-import javax.swing.JDialog;\r
-import javax.swing.JPanel;\r
-import javax.swing.JSeparator;\r
-import javax.swing.SwingConstants;\r
-import javax.swing.border.Border;\r
-import javax.swing.event.ChangeEvent;\r
-import javax.swing.event.ChangeListener;\r
-import javax.swing.event.EventListenerList;\r
-import jp.sourceforge.jindolf.corelib.EventFamily;\r
-import jp.sourceforge.jindolf.corelib.TalkType;\r
-\r
-/**\r
- * 発言フィルタ GUI。\r
- */\r
-@SuppressWarnings("serial")\r
-public class FilterPanel extends JDialog\r
-        implements ActionListener, TopicFilter{\r
-\r
-    private static final int COLS = 4;\r
-\r
-    private static final String FRAMETITLE = "発言フィルタ - " + Jindolf.TITLE;\r
-\r
-    private final JCheckBox checkPublic = new JCheckBox("公開", true);\r
-    private final JCheckBox checkWolf = new JCheckBox("狼", true);\r
-    private final JCheckBox checkPrivate = new JCheckBox("独り言", true);\r
-    private final JCheckBox checkGrave = new JCheckBox("墓下", true);\r
-    private final JCheckBox checkExtra = new JCheckBox("Extra", true);\r
-\r
-    private final JButton selAllButton = new JButton("全選択");\r
-    private final JButton selNoneButton = new JButton("全解除");\r
-    private final JButton negateButton = new JButton("反転");\r
-\r
-    private final JCheckBox checkRealtime =\r
-            new JCheckBox("リアルタイム更新", true);\r
-    private final JButton applyButton = new JButton("フィルタ適用");\r
-\r
-    private final Map<Avatar, JCheckBox> cbMap =\r
-            new HashMap<Avatar, JCheckBox>();\r
-    private final List<JCheckBox> cbList = new LinkedList<JCheckBox>();\r
-\r
-    private final EventListenerList listeners = new EventListenerList();\r
-\r
-    /**\r
-     * 発言フィルタを生成する。\r
-     * @param owner フレームオーナー\r
-     */\r
-    public FilterPanel(Frame owner){\r
-        super(owner, FRAMETITLE, false);\r
-\r
-        GUIUtils.modifyWindowAttributes(this, true, false, true);\r
-\r
-        JComponent topicPanel = createTopicPanel();\r
-        JComponent avatarPanel = createAvatarPanel();\r
-        JComponent buttonPanel = createButtonPanel();\r
-        JComponent bottomPanel = createBottomPanel();\r
-        design(topicPanel, avatarPanel, buttonPanel, bottomPanel);\r
-\r
-        this.checkPublic.addActionListener(this);\r
-        this.checkWolf.addActionListener(this);\r
-        this.checkPrivate.addActionListener(this);\r
-        this.checkGrave.addActionListener(this);\r
-        this.checkExtra.addActionListener(this);\r
-\r
-        for(JCheckBox avatarCheckBox : this.cbList){\r
-            avatarCheckBox.addActionListener(this);\r
-        }\r
-\r
-        this.selAllButton.addActionListener(this);\r
-        this.selNoneButton.addActionListener(this);\r
-        this.negateButton.addActionListener(this);\r
-\r
-        this.checkRealtime.addActionListener(this);\r
-        this.applyButton.addActionListener(this);\r
-        this.applyButton.setEnabled(false);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * レイアウトデザインを行う。\r
-     * @param topicPanel システムイベント選択\r
-     * @param avatarPanel キャラ一覧\r
-     * @param buttonPanel ボタン群\r
-     * @param bottomPanel 下段パネル\r
-     */\r
-    private void design(JComponent topicPanel,\r
-                         JComponent avatarPanel,\r
-                         JComponent buttonPanel,\r
-                         JComponent bottomPanel ){\r
-        Container content = getContentPane();\r
-\r
-        GridBagLayout layout = new GridBagLayout();\r
-        GridBagConstraints constraints = new GridBagConstraints();\r
-\r
-        content.setLayout(layout);\r
-\r
-        constraints.weightx = 1.0 / 5;\r
-        constraints.weighty = 1.0;\r
-        constraints.gridheight = 3;\r
-        constraints.fill = GridBagConstraints.BOTH;\r
-        constraints.anchor = GridBagConstraints.CENTER;\r
-        content.add(topicPanel, constraints);\r
-\r
-        constraints.weightx = 0.0;\r
-        constraints.fill = GridBagConstraints.VERTICAL;\r
-        constraints.insets = new Insets(3, 0, 3, 0);\r
-        content.add(new JSeparator(SwingConstants.VERTICAL), constraints);\r
-\r
-        constraints.weightx = 4.0 / 5;\r
-        constraints.weighty = 0.0;\r
-        constraints.gridheight = 1;\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        constraints.fill = GridBagConstraints.HORIZONTAL;\r
-        constraints.insets = new Insets(0, 0, 0, 0);\r
-        content.add(buttonPanel, constraints);\r
-\r
-        constraints.insets = new Insets(0, 3, 0, 3);\r
-        content.add(new JSeparator(), constraints);\r
-\r
-        constraints.weighty = 1.0;\r
-        constraints.fill = GridBagConstraints.BOTH;\r
-        constraints.insets = new Insets(0, 0, 0, 0);\r
-        content.add(avatarPanel, constraints);\r
-\r
-        constraints.weightx = 1.0;\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        constraints.fill = GridBagConstraints.HORIZONTAL;\r
-        content.add(new JSeparator(SwingConstants.HORIZONTAL), constraints);\r
-\r
-        constraints.fill = GridBagConstraints.NONE;\r
-        constraints.anchor = GridBagConstraints.NORTHEAST;\r
-        content.add(bottomPanel, constraints);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * システムイベントチェックボックス群パネルを作成。\r
-     * @return システムイベントチェックボックス群パネル\r
-     */\r
-    private JComponent createTopicPanel(){\r
-        this.checkPublic.setToolTipText("誰にでも見える発言");\r
-        this.checkWolf.setToolTipText("人狼同士にしか見えない発言");\r
-        this.checkPrivate.setToolTipText("本人にしか見えない発言");\r
-        this.checkGrave.setToolTipText("死者同士にしか見えない発言");\r
-        this.checkExtra.setToolTipText("占い先や護衛先などのシステムメッセージ");\r
-\r
-        JPanel topicPanel = new JPanel();\r
-\r
-        Border border = BorderFactory.createEmptyBorder(2, 2, 2, 2);\r
-        topicPanel.setBorder(border);\r
-\r
-        GridBagLayout layout = new GridBagLayout();\r
-        GridBagConstraints constraints = new GridBagConstraints();\r
-\r
-        topicPanel.setLayout(layout);\r
-\r
-        constraints.anchor = GridBagConstraints.WEST;\r
-        constraints.weightx = 1.0;\r
-        constraints.weighty = 1.0;\r
-\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        topicPanel.add(this.checkPublic, constraints);\r
-\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        topicPanel.add(this.checkWolf, constraints);\r
-\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        topicPanel.add(this.checkPrivate, constraints);\r
-\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        topicPanel.add(this.checkGrave, constraints);\r
-\r
-        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-        topicPanel.add(this.checkExtra, constraints);\r
-\r
-        return topicPanel;\r
-    }\r
-\r
-    /**\r
-     * キャラ一覧チェックボックス群パネルを作成。\r
-     * @return キャラ一覧チェックボックス群パネル\r
-     */\r
-    private JComponent createAvatarPanel(){\r
-        JPanel avatarPanel = new JPanel();\r
-\r
-        Border border = BorderFactory.createEmptyBorder(2, 2, 2, 2);\r
-        avatarPanel.setBorder(border);\r
-\r
-        GridBagLayout layout = new GridBagLayout();\r
-        GridBagConstraints constraints = new GridBagConstraints();\r
-\r
-        avatarPanel.setLayout(layout);\r
-\r
-        constraints.weightx = 1.0 / COLS;\r
-        constraints.weighty = 1.0;\r
-        constraints.anchor = GridBagConstraints.WEST;\r
-\r
-        int xPos = 0;\r
-        for(Avatar avatar : Avatar.getPredefinedAvatarList()){\r
-            JCheckBox checkBox = new JCheckBox(avatar.getName(), true);\r
-            checkBox.setToolTipText(avatar.getJobTitle());\r
-            this.cbList.add(checkBox);\r
-            if(xPos >= COLS - 1){\r
-                constraints.gridwidth = GridBagConstraints.REMAINDER;\r
-                xPos = 0;\r
-            }else{\r
-                constraints.gridwidth = 1;\r
-                xPos++;\r
-            }\r
-            avatarPanel.add(checkBox, constraints);\r
-            this.cbMap.put(avatar, checkBox);\r
-        }\r
-\r
-        return avatarPanel;\r
-    }\r
-\r
-    /**\r
-     * ボタン群パネルを生成。\r
-     * @return ボタン群パネル\r
-     */\r
-    private JComponent createButtonPanel(){\r
-        this.selAllButton.setToolTipText(\r
-                "全キャラクタの発言を表示する");\r
-        this.selNoneButton.setToolTipText(\r
-                "全キャラクタの発言をフィルタリングする");\r
-        this.negateButton.setToolTipText(\r
-                "(表示⇔フィルタリング)の設定を反転させる");\r
-\r
-        JPanel buttonPanel = new JPanel();\r
-\r
-        GridBagLayout layout = new GridBagLayout();\r
-        GridBagConstraints constraints = new GridBagConstraints();\r
-\r
-        buttonPanel.setLayout(layout);\r
-\r
-        constraints.weightx = 1.0 / 3;\r
-        constraints.insets = new Insets(3, 3, 3, 3);\r
-        buttonPanel.add(this.selAllButton, constraints);\r
-        buttonPanel.add(this.selNoneButton, constraints);\r
-        buttonPanel.add(this.negateButton, constraints);\r
-\r
-        return buttonPanel;\r
-    }\r
-\r
-    /**\r
-     * 下段パネルを生成する。\r
-     * @return 下段パネル\r
-     */\r
-    private JComponent createBottomPanel(){\r
-        JPanel panel = new JPanel();\r
-\r
-        GridBagLayout layout = new GridBagLayout();\r
-        GridBagConstraints constraints = new GridBagConstraints();\r
-\r
-        panel.setLayout(layout);\r
-\r
-        constraints.fill = GridBagConstraints.NONE;\r
-        constraints.insets = new Insets(3, 3, 3, 3);\r
-        panel.add(this.checkRealtime, constraints);\r
-        panel.add(this.applyButton, constraints);\r
-\r
-        return panel;\r
-    }\r
-\r
-    /**\r
-     * リスナを登録する。\r
-     * @param listener リスナ\r
-     */\r
-    public void addChangeListener(ChangeListener listener){\r
-        this.listeners.add(ChangeListener.class, listener);\r
-    }\r
-\r
-    /**\r
-     * リスナを削除する。\r
-     * @param listener リスナ\r
-     */\r
-    public void removeChangeListener(ChangeListener listener){\r
-        this.listeners.remove(ChangeListener.class, listener);\r
-    }\r
-\r
-    /**\r
-     * 全リスナを取得する。\r
-     * @return リスナの配列\r
-     */\r
-    public ChangeListener[] getChangeListeners(){\r
-        return this.listeners.getListeners(ChangeListener.class);\r
-    }\r
-\r
-    /**\r
-     * 全リスナへフィルタ操作を通知する。\r
-     */\r
-    protected void fireCheckChanged(){\r
-        ChangeEvent changeEvent = new ChangeEvent(this);\r
-        for(ChangeListener listener : getChangeListeners()){\r
-            listener.stateChanged(changeEvent);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * ボタン状態の初期化。\r
-     */\r
-    public void initButtons(){\r
-        this.checkPublic.setSelected(true);\r
-        this.checkWolf.setSelected(true);\r
-        this.checkPrivate.setSelected(true);\r
-        this.checkGrave.setSelected(true);\r
-        this.checkExtra.setSelected(true);\r
-\r
-        this.selAllButton.doClick();\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * チェックボックスまたはボタン操作時にリスナとして呼ばれる。\r
-     * {@inheritDoc}\r
-     * @param event イベント\r
-     */\r
-    public void actionPerformed(ActionEvent event){\r
-        Object source = event.getSource();\r
-\r
-        boolean isRealtime = this.checkRealtime.isSelected();\r
-\r
-        if(source == this.selAllButton){\r
-            boolean hasChanged = false;\r
-            for(JCheckBox avatarCBox : this.cbList){\r
-                if( ! avatarCBox.isSelected()){\r
-                    avatarCBox.setSelected(true);\r
-                    hasChanged = true;\r
-                }\r
-            }\r
-            if(isRealtime && hasChanged){\r
-                fireCheckChanged();\r
-            }\r
-        }else if(source == this.selNoneButton){\r
-            boolean hasChanged = false;\r
-            for(JCheckBox avatarCBox : this.cbList){\r
-                if(avatarCBox.isSelected()){\r
-                    avatarCBox.setSelected(false);\r
-                    hasChanged = true;\r
-                }\r
-            }\r
-            if(isRealtime && hasChanged){\r
-                fireCheckChanged();\r
-            }\r
-        }else if(source == this.negateButton){\r
-            for(JCheckBox avatarCBox : this.cbList){\r
-                if(avatarCBox.isSelected()){\r
-                    avatarCBox.setSelected(false);\r
-                }else{\r
-                    avatarCBox.setSelected(true);\r
-                }\r
-            }\r
-            if(isRealtime){\r
-                fireCheckChanged();\r
-            }\r
-        }else if(source == this.checkRealtime){\r
-            if(isRealtime){\r
-                this.applyButton.setEnabled(false);\r
-                fireCheckChanged();\r
-            }else{\r
-                this.applyButton.setEnabled(true);\r
-            }\r
-        }else if(source == this.applyButton){\r
-            fireCheckChanged();\r
-        }else if(source instanceof JCheckBox){\r
-            if(isRealtime){\r
-                fireCheckChanged();\r
-            }\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param topic {@inheritDoc}\r
-     * @return {@inheritDoc}\r
-     */\r
-    public boolean isFiltered(Topic topic){\r
-        Talk talk;\r
-        if(topic instanceof Talk){\r
-            talk = (Talk) topic;\r
-        }else if(topic instanceof SysEvent){\r
-            SysEvent sysEvent = (SysEvent) topic;\r
-            if(sysEvent.getEventFamily() == EventFamily.EXTRA){\r
-                if( ! this.checkExtra.isSelected() ){\r
-                    return true;\r
-                }\r
-            }\r
-            return false;\r
-        }else{\r
-            return false;\r
-        }\r
-\r
-        JCheckBox cbox;\r
-\r
-        TalkType type = talk.getTalkType();\r
-        switch(type){\r
-        case PUBLIC:\r
-            cbox = this.checkPublic;\r
-            break;\r
-        case WOLFONLY:\r
-            cbox = this.checkWolf;\r
-            break;\r
-        case PRIVATE:\r
-            cbox = this.checkPrivate;\r
-            break;\r
-        case GRAVE:\r
-            cbox = this.checkGrave;\r
-            break;\r
-        default:\r
-            assert false;\r
-            return true;\r
-        }\r
-        if( ! cbox.isSelected()){\r
-            return true;\r
-        }\r
-\r
-        Avatar avatar = talk.getAvatar();\r
-        cbox = this.cbMap.get(avatar);\r
-        if( cbox != null && ! cbox.isSelected()){\r
-            return true;\r
-        }\r
-\r
-        return false;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @return {@inheritDoc}\r
-     */\r
-    public FilterContext getFilterContext(){\r
-        return new FilterPanelContext();\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param context {@inheritDoc}\r
-     * @return {@inheritDoc}\r
-     */\r
-    public boolean isSame(FilterContext context){\r
-        if(context == null) return false;\r
-        if( ! (context instanceof FilterPanelContext) ) return false;\r
-        FilterPanelContext argContext = (FilterPanelContext) context;\r
-        FilterPanelContext thisContext =\r
-                (FilterPanelContext) getFilterContext();\r
-\r
-        return thisContext.context.equals(argContext.context);\r
-    }\r
-\r
-    /**\r
-     * カスタム化されたフィルタ状態。\r
-     */\r
-    private final class FilterPanelContext implements FilterContext{\r
-\r
-        private final BitSet context = new BitSet();\r
-\r
-        /**\r
-         * コンストラクタ。\r
-         */\r
-        public FilterPanelContext(){\r
-            super();\r
-\r
-            int index = 0;\r
-            this.context.set(index++,\r
-                    FilterPanel.this.checkPublic.isSelected());\r
-            this.context.set(index++,\r
-                    FilterPanel.this.checkWolf.isSelected());\r
-            this.context.set(index++,\r
-                    FilterPanel.this.checkPrivate.isSelected());\r
-            this.context.set(index++,\r
-                    FilterPanel.this.checkGrave.isSelected());\r
-            this.context.set(index++,\r
-                    FilterPanel.this.checkExtra.isSelected());\r
-\r
-            for(Avatar avatar : Avatar.getPredefinedAvatarList()){\r
-                JCheckBox checkBox = FilterPanel.this.cbMap.get(avatar);\r
-                this.context.set(index++, checkBox.isSelected());\r
-            }\r
-\r
-            return;\r
-        }\r
-\r
-        /**\r
-         * {@inheritDoc}\r
-         * @return {@inheritDoc}\r
-         */\r
-        @Override\r
-        public String toString(){\r
-            return this.context.toString();\r
-        }\r
-\r
-    }\r
-\r
-}\r
+/*
+ * Filter panel
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sfjp.jindolf.view;
+
+import java.awt.Container;
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JPanel;
+import javax.swing.JSeparator;
+import javax.swing.SwingConstants;
+import javax.swing.border.Border;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+import jp.sfjp.jindolf.data.Avatar;
+import jp.sfjp.jindolf.data.SysEvent;
+import jp.sfjp.jindolf.data.Talk;
+import jp.sfjp.jindolf.data.Topic;
+import jp.sfjp.jindolf.util.GUIUtils;
+import jp.sourceforge.jindolf.corelib.EventFamily;
+import jp.sourceforge.jindolf.corelib.TalkType;
+
+/**
+ * 発言フィルタ GUI。
+ */
+@SuppressWarnings("serial")
+public class FilterPanel extends JDialog
+        implements ActionListener, TopicFilter{
+
+    private static final int COLS = 4;
+
+
+    private final JCheckBox checkPublic = new JCheckBox("公開", true);
+    private final JCheckBox checkWolf = new JCheckBox("狼", true);
+    private final JCheckBox checkPrivate = new JCheckBox("独り言", true);
+    private final JCheckBox checkGrave = new JCheckBox("墓下", true);
+    private final JCheckBox checkExtra = new JCheckBox("Extra", true);
+
+    private final JButton selAllButton = new JButton("全選択");
+    private final JButton selNoneButton = new JButton("全解除");
+    private final JButton negateButton = new JButton("反転");
+
+    private final JCheckBox checkRealtime =
+            new JCheckBox("リアルタイム更新", true);
+    private final JButton applyButton = new JButton("フィルタ適用");
+
+    private final Map<Avatar, JCheckBox> cbMap =
+            new HashMap<>();
+    private final List<JCheckBox> cbList = new LinkedList<>();
+
+    private final EventListenerList listeners = new EventListenerList();
+
+    /**
+     * 発言フィルタを生成する。
+     * @param owner フレームオーナー
+     */
+    @SuppressWarnings("LeakingThisInConstructor")
+    public FilterPanel(Frame owner){
+        super(owner);
+        setModal(false);
+
+        GUIUtils.modifyWindowAttributes(this, true, false, true);
+
+        JComponent topicPanel = createTopicPanel();
+        JComponent avatarPanel = createAvatarPanel();
+        JComponent buttonPanel = createButtonPanel();
+        JComponent bottomPanel = createBottomPanel();
+        design(topicPanel, avatarPanel, buttonPanel, bottomPanel);
+
+        this.checkPublic.addActionListener(this);
+        this.checkWolf.addActionListener(this);
+        this.checkPrivate.addActionListener(this);
+        this.checkGrave.addActionListener(this);
+        this.checkExtra.addActionListener(this);
+
+        for(JCheckBox avatarCheckBox : this.cbList){
+            avatarCheckBox.addActionListener(this);
+        }
+
+        this.selAllButton.addActionListener(this);
+        this.selNoneButton.addActionListener(this);
+        this.negateButton.addActionListener(this);
+
+        this.checkRealtime.addActionListener(this);
+        this.applyButton.addActionListener(this);
+        this.applyButton.setEnabled(false);
+
+        return;
+    }
+
+    /**
+     * レイアウトデザインを行う。
+     * @param topicPanel システムイベント選択
+     * @param avatarPanel キャラ一覧
+     * @param buttonPanel ボタン群
+     * @param bottomPanel 下段パネル
+     */
+    private void design(JComponent topicPanel,
+                         JComponent avatarPanel,
+                         JComponent buttonPanel,
+                         JComponent bottomPanel ){
+        Container content = getContentPane();
+
+        GridBagLayout layout = new GridBagLayout();
+        GridBagConstraints constraints = new GridBagConstraints();
+
+        content.setLayout(layout);
+
+        constraints.weightx = 1.0 / 5;
+        constraints.weighty = 1.0;
+        constraints.gridheight = 3;
+        constraints.fill = GridBagConstraints.BOTH;
+        constraints.anchor = GridBagConstraints.CENTER;
+        content.add(topicPanel, constraints);
+
+        constraints.weightx = 0.0;
+        constraints.fill = GridBagConstraints.VERTICAL;
+        constraints.insets = new Insets(3, 0, 3, 0);
+        content.add(new JSeparator(SwingConstants.VERTICAL), constraints);
+
+        constraints.weightx = 4.0 / 5;
+        constraints.weighty = 0.0;
+        constraints.gridheight = 1;
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        constraints.fill = GridBagConstraints.HORIZONTAL;
+        constraints.insets = new Insets(0, 0, 0, 0);
+        content.add(buttonPanel, constraints);
+
+        constraints.insets = new Insets(0, 3, 0, 3);
+        content.add(new JSeparator(), constraints);
+
+        constraints.weighty = 1.0;
+        constraints.fill = GridBagConstraints.BOTH;
+        constraints.insets = new Insets(0, 0, 0, 0);
+        content.add(avatarPanel, constraints);
+
+        constraints.weightx = 1.0;
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        constraints.fill = GridBagConstraints.HORIZONTAL;
+        content.add(new JSeparator(SwingConstants.HORIZONTAL), constraints);
+
+        constraints.fill = GridBagConstraints.NONE;
+        constraints.anchor = GridBagConstraints.NORTHEAST;
+        content.add(bottomPanel, constraints);
+
+        return;
+    }
+
+    /**
+     * システムイベントチェックボックス群パネルを作成。
+     * @return システムイベントチェックボックス群パネル
+     */
+    private JComponent createTopicPanel(){
+        this.checkPublic.setToolTipText("誰にでも見える発言");
+        this.checkWolf.setToolTipText("人狼同士にしか見えない発言");
+        this.checkPrivate.setToolTipText("本人にしか見えない発言");
+        this.checkGrave.setToolTipText("死者同士にしか見えない発言");
+        this.checkExtra.setToolTipText("占い先や護衛先などのシステムメッセージ");
+
+        JPanel topicPanel = new JPanel();
+
+        Border border = BorderFactory.createEmptyBorder(2, 2, 2, 2);
+        topicPanel.setBorder(border);
+
+        GridBagLayout layout = new GridBagLayout();
+        GridBagConstraints constraints = new GridBagConstraints();
+
+        topicPanel.setLayout(layout);
+
+        constraints.anchor = GridBagConstraints.WEST;
+        constraints.weightx = 1.0;
+        constraints.weighty = 1.0;
+
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        topicPanel.add(this.checkPublic, constraints);
+
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        topicPanel.add(this.checkWolf, constraints);
+
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        topicPanel.add(this.checkPrivate, constraints);
+
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        topicPanel.add(this.checkGrave, constraints);
+
+        constraints.gridwidth = GridBagConstraints.REMAINDER;
+        topicPanel.add(this.checkExtra, constraints);
+
+        return topicPanel;
+    }
+
+    /**
+     * キャラ一覧チェックボックス群パネルを作成。
+     * @return キャラ一覧チェックボックス群パネル
+     */
+    private JComponent createAvatarPanel(){
+        JPanel avatarPanel = new JPanel();
+
+        Border border = BorderFactory.createEmptyBorder(2, 2, 2, 2);
+        avatarPanel.setBorder(border);
+
+        GridBagLayout layout = new GridBagLayout();
+        GridBagConstraints constraints = new GridBagConstraints();
+
+        avatarPanel.setLayout(layout);
+
+        constraints.weightx = 1.0 / COLS;
+        constraints.weighty = 1.0;
+        constraints.anchor = GridBagConstraints.WEST;
+
+        int xPos = 0;
+        for(Avatar avatar : Avatar.getPredefinedAvatarList()){
+            JCheckBox checkBox = new JCheckBox(avatar.getName(), true);
+            checkBox.setToolTipText(avatar.getJobTitle());
+            this.cbList.add(checkBox);
+            if(xPos >= COLS - 1){
+                constraints.gridwidth = GridBagConstraints.REMAINDER;
+                xPos = 0;
+            }else{
+                constraints.gridwidth = 1;
+                xPos++;
+            }
+            avatarPanel.add(checkBox, constraints);
+            this.cbMap.put(avatar, checkBox);
+        }
+
+        return avatarPanel;
+    }
+
+    /**
+     * ボタン群パネルを生成。
+     * @return ボタン群パネル
+     */
+    private JComponent createButtonPanel(){
+        this.selAllButton.setToolTipText(
+                "全キャラクタの発言を表示する");
+        this.selNoneButton.setToolTipText(
+                "全キャラクタの発言をフィルタリングする");
+        this.negateButton.setToolTipText(
+                "(表示⇔フィルタリング)の設定を反転させる");
+
+        JPanel buttonPanel = new JPanel();
+
+        GridBagLayout layout = new GridBagLayout();
+        GridBagConstraints constraints = new GridBagConstraints();
+
+        buttonPanel.setLayout(layout);
+
+        constraints.weightx = 1.0 / 3;
+        constraints.insets = new Insets(3, 3, 3, 3);
+        buttonPanel.add(this.selAllButton, constraints);
+        buttonPanel.add(this.selNoneButton, constraints);
+        buttonPanel.add(this.negateButton, constraints);
+
+        return buttonPanel;
+    }
+
+    /**
+     * 下段パネルを生成する。
+     * @return 下段パネル
+     */
+    private JComponent createBottomPanel(){
+        JPanel panel = new JPanel();
+
+        GridBagLayout layout = new GridBagLayout();
+        GridBagConstraints constraints = new GridBagConstraints();
+
+        panel.setLayout(layout);
+
+        constraints.fill = GridBagConstraints.NONE;
+        constraints.insets = new Insets(3, 3, 3, 3);
+        panel.add(this.checkRealtime, constraints);
+        panel.add(this.applyButton, constraints);
+
+        return panel;
+    }
+
+    /**
+     * リスナを登録する。
+     * @param listener リスナ
+     */
+    public void addChangeListener(ChangeListener listener){
+        this.listeners.add(ChangeListener.class, listener);
+    }
+
+    /**
+     * リスナを削除する。
+     * @param listener リスナ
+     */
+    public void removeChangeListener(ChangeListener listener){
+        this.listeners.remove(ChangeListener.class, listener);
+    }
+
+    /**
+     * 全リスナを取得する。
+     * @return リスナの配列
+     */
+    public ChangeListener[] getChangeListeners(){
+        return this.listeners.getListeners(ChangeListener.class);
+    }
+
+    /**
+     * 全リスナへフィルタ操作を通知する。
+     */
+    protected void fireCheckChanged(){
+        ChangeEvent changeEvent = new ChangeEvent(this);
+        for(ChangeListener listener : getChangeListeners()){
+            listener.stateChanged(changeEvent);
+        }
+    }
+
+    /**
+     * ボタン状態の初期化。
+     */
+    public void initButtons(){
+        this.checkPublic.setSelected(true);
+        this.checkWolf.setSelected(true);
+        this.checkPrivate.setSelected(true);
+        this.checkGrave.setSelected(true);
+        this.checkExtra.setSelected(true);
+
+        this.selAllButton.doClick();
+
+        return;
+    }
+
+    /**
+     * チェックボックスまたはボタン操作時にリスナとして呼ばれる。
+     * {@inheritDoc}
+     * @param event イベント
+     */
+    @Override
+    public void actionPerformed(ActionEvent event){
+        Object source = event.getSource();
+
+        boolean isRealtime = this.checkRealtime.isSelected();
+
+        if(source == this.selAllButton){
+            boolean hasChanged = false;
+            for(JCheckBox avatarCBox : this.cbList){
+                if( ! avatarCBox.isSelected()){
+                    avatarCBox.setSelected(true);
+                    hasChanged = true;
+                }
+            }
+            if(isRealtime && hasChanged){
+                fireCheckChanged();
+            }
+        }else if(source == this.selNoneButton){
+            boolean hasChanged = false;
+            for(JCheckBox avatarCBox : this.cbList){
+                if(avatarCBox.isSelected()){
+                    avatarCBox.setSelected(false);
+                    hasChanged = true;
+                }
+            }
+            if(isRealtime && hasChanged){
+                fireCheckChanged();
+            }
+        }else if(source == this.negateButton){
+            for(JCheckBox avatarCBox : this.cbList){
+                if(avatarCBox.isSelected()){
+                    avatarCBox.setSelected(false);
+                }else{
+                    avatarCBox.setSelected(true);
+                }
+            }
+            if(isRealtime){
+                fireCheckChanged();
+            }
+        }else if(source == this.checkRealtime){
+            if(isRealtime){
+                this.applyButton.setEnabled(false);
+                fireCheckChanged();
+            }else{
+                this.applyButton.setEnabled(true);
+            }
+        }else if(source == this.applyButton){
+            fireCheckChanged();
+        }else if(source instanceof JCheckBox){
+            if(isRealtime){
+                fireCheckChanged();
+            }
+        }
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param topic {@inheritDoc}
+     * @return {@inheritDoc}
+     */
+    @Override
+    public boolean isFiltered(Topic topic){
+        Talk talk;
+        if(topic instanceof Talk){
+            talk = (Talk) topic;
+        }else if(topic instanceof SysEvent){
+            SysEvent sysEvent = (SysEvent) topic;
+            if(sysEvent.getEventFamily() == EventFamily.EXTRA){
+                if( ! this.checkExtra.isSelected() ){
+                    return true;
+                }
+            }
+            return false;
+        }else{
+            return false;
+        }
+
+        JCheckBox cbox;
+
+        TalkType type = talk.getTalkType();
+        switch(type){
+        case PUBLIC:
+            cbox = this.checkPublic;
+            break;
+        case WOLFONLY:
+            cbox = this.checkWolf;
+            break;
+        case PRIVATE:
+            cbox = this.checkPrivate;
+            break;
+        case GRAVE:
+            cbox = this.checkGrave;
+            break;
+        default:
+            assert false;
+            return true;
+        }
+        if( ! cbox.isSelected()){
+            return true;
+        }
+
+        Avatar avatar = talk.getAvatar();
+        cbox = this.cbMap.get(avatar);
+        if( cbox != null && ! cbox.isSelected()){
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return {@inheritDoc}
+     */
+    @Override
+    public FilterContext getFilterContext(){
+        return new FilterPanelContext();
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param context {@inheritDoc}
+     * @return {@inheritDoc}
+     */
+    @Override
+    public boolean isSame(FilterContext context){
+        if(context == null) return false;
+        if( ! (context instanceof FilterPanelContext) ) return false;
+        FilterPanelContext argContext = (FilterPanelContext) context;
+        FilterPanelContext thisContext =
+                (FilterPanelContext) getFilterContext();
+
+        return thisContext.context.equals(argContext.context);
+    }
+
+    /**
+     * カスタム化されたフィルタ状態。
+     */
+    private final class FilterPanelContext implements FilterContext{
+
+        private final BitSet context = new BitSet();
+
+        /**
+         * コンストラクタ。
+         */
+        public FilterPanelContext(){
+            super();
+
+            int index = 0;
+            this.context.set(index++,
+                    FilterPanel.this.checkPublic.isSelected());
+            this.context.set(index++,
+                    FilterPanel.this.checkWolf.isSelected());
+            this.context.set(index++,
+                    FilterPanel.this.checkPrivate.isSelected());
+            this.context.set(index++,
+                    FilterPanel.this.checkGrave.isSelected());
+            this.context.set(index++,
+                    FilterPanel.this.checkExtra.isSelected());
+
+            for(Avatar avatar : Avatar.getPredefinedAvatarList()){
+                JCheckBox checkBox = FilterPanel.this.cbMap.get(avatar);
+                this.context.set(index++, checkBox.isSelected());
+            }
+
+            return;
+        }
+
+        /**
+         * {@inheritDoc}
+         * @return {@inheritDoc}
+         */
+        @Override
+        public String toString(){
+            return this.context.toString();
+        }
+
+    }
+
+}