OSDN Git Service

from subversion repository
[jindolf/Jindolf.git] / src / main / java / jp / sourceforge / jindolf / FilterPanel.java
1 /*\r
2  * Filter panel\r
3  *\r
4  * Copyright(c) 2008 olyutorskii\r
5  * $Id: FilterPanel.java 969 2009-12-24 16:12:58Z olyutorskii $\r
6  */\r
7 \r
8 package jp.sourceforge.jindolf;\r
9 \r
10 import java.awt.Container;\r
11 import java.awt.Frame;\r
12 import java.awt.GridBagConstraints;\r
13 import java.awt.GridBagLayout;\r
14 import java.awt.Insets;\r
15 import java.awt.event.ActionEvent;\r
16 import java.awt.event.ActionListener;\r
17 import java.util.BitSet;\r
18 import java.util.HashMap;\r
19 import java.util.LinkedList;\r
20 import java.util.List;\r
21 import java.util.Map;\r
22 import javax.swing.BorderFactory;\r
23 import javax.swing.JButton;\r
24 import javax.swing.JCheckBox;\r
25 import javax.swing.JComponent;\r
26 import javax.swing.JDialog;\r
27 import javax.swing.JPanel;\r
28 import javax.swing.JSeparator;\r
29 import javax.swing.SwingConstants;\r
30 import javax.swing.border.Border;\r
31 import javax.swing.event.ChangeEvent;\r
32 import javax.swing.event.ChangeListener;\r
33 import javax.swing.event.EventListenerList;\r
34 import jp.sourceforge.jindolf.corelib.EventFamily;\r
35 import jp.sourceforge.jindolf.corelib.TalkType;\r
36 \r
37 /**\r
38  * 発言フィルタ GUI。\r
39  */\r
40 @SuppressWarnings("serial")\r
41 public class FilterPanel extends JDialog\r
42         implements ActionListener, TopicFilter{\r
43 \r
44     private static final int COLS = 4;\r
45 \r
46     private static final String FRAMETITLE = "発言フィルタ - " + Jindolf.TITLE;\r
47 \r
48     private final JCheckBox checkPublic = new JCheckBox("公開", true);\r
49     private final JCheckBox checkWolf = new JCheckBox("狼", true);\r
50     private final JCheckBox checkPrivate = new JCheckBox("独り言", true);\r
51     private final JCheckBox checkGrave = new JCheckBox("墓下", true);\r
52     private final JCheckBox checkExtra = new JCheckBox("Extra", true);\r
53 \r
54     private final JButton selAllButton = new JButton("全選択");\r
55     private final JButton selNoneButton = new JButton("全解除");\r
56     private final JButton negateButton = new JButton("反転");\r
57 \r
58     private final JCheckBox checkRealtime =\r
59             new JCheckBox("リアルタイム更新", true);\r
60     private final JButton applyButton = new JButton("フィルタ適用");\r
61 \r
62     private final Map<Avatar, JCheckBox> cbMap =\r
63             new HashMap<Avatar, JCheckBox>();\r
64     private final List<JCheckBox> cbList = new LinkedList<JCheckBox>();\r
65 \r
66     private final EventListenerList listeners = new EventListenerList();\r
67 \r
68     /**\r
69      * 発言フィルタを生成する。\r
70      * @param owner フレームオーナー\r
71      */\r
72     public FilterPanel(Frame owner){\r
73         super(owner, FRAMETITLE, false);\r
74 \r
75         GUIUtils.modifyWindowAttributes(this, true, false, true);\r
76 \r
77         JComponent topicPanel = createTopicPanel();\r
78         JComponent avatarPanel = createAvatarPanel();\r
79         JComponent buttonPanel = createButtonPanel();\r
80         JComponent bottomPanel = createBottomPanel();\r
81         design(topicPanel, avatarPanel, buttonPanel, bottomPanel);\r
82 \r
83         this.checkPublic.addActionListener(this);\r
84         this.checkWolf.addActionListener(this);\r
85         this.checkPrivate.addActionListener(this);\r
86         this.checkGrave.addActionListener(this);\r
87         this.checkExtra.addActionListener(this);\r
88 \r
89         for(JCheckBox avatarCheckBox : this.cbList){\r
90             avatarCheckBox.addActionListener(this);\r
91         }\r
92 \r
93         this.selAllButton.addActionListener(this);\r
94         this.selNoneButton.addActionListener(this);\r
95         this.negateButton.addActionListener(this);\r
96 \r
97         this.checkRealtime.addActionListener(this);\r
98         this.applyButton.addActionListener(this);\r
99         this.applyButton.setEnabled(false);\r
100 \r
101         return;\r
102     }\r
103 \r
104     /**\r
105      * レイアウトデザインを行う。\r
106      * @param topicPanel システムイベント選択\r
107      * @param avatarPanel キャラ一覧\r
108      * @param buttonPanel ボタン群\r
109      * @param bottomPanel 下段パネル\r
110      */\r
111     private void design(JComponent topicPanel,\r
112                          JComponent avatarPanel,\r
113                          JComponent buttonPanel,\r
114                          JComponent bottomPanel ){\r
115         Container content = getContentPane();\r
116 \r
117         GridBagLayout layout = new GridBagLayout();\r
118         GridBagConstraints constraints = new GridBagConstraints();\r
119 \r
120         content.setLayout(layout);\r
121 \r
122         constraints.weightx = 1.0 / 5;\r
123         constraints.weighty = 1.0;\r
124         constraints.gridheight = 3;\r
125         constraints.fill = GridBagConstraints.BOTH;\r
126         constraints.anchor = GridBagConstraints.CENTER;\r
127         content.add(topicPanel, constraints);\r
128 \r
129         constraints.weightx = 0.0;\r
130         constraints.fill = GridBagConstraints.VERTICAL;\r
131         constraints.insets = new Insets(3, 0, 3, 0);\r
132         content.add(new JSeparator(SwingConstants.VERTICAL), constraints);\r
133 \r
134         constraints.weightx = 4.0 / 5;\r
135         constraints.weighty = 0.0;\r
136         constraints.gridheight = 1;\r
137         constraints.gridwidth = GridBagConstraints.REMAINDER;\r
138         constraints.fill = GridBagConstraints.HORIZONTAL;\r
139         constraints.insets = new Insets(0, 0, 0, 0);\r
140         content.add(buttonPanel, constraints);\r
141 \r
142         constraints.insets = new Insets(0, 3, 0, 3);\r
143         content.add(new JSeparator(), constraints);\r
144 \r
145         constraints.weighty = 1.0;\r
146         constraints.fill = GridBagConstraints.BOTH;\r
147         constraints.insets = new Insets(0, 0, 0, 0);\r
148         content.add(avatarPanel, constraints);\r
149 \r
150         constraints.weightx = 1.0;\r
151         constraints.gridwidth = GridBagConstraints.REMAINDER;\r
152         constraints.fill = GridBagConstraints.HORIZONTAL;\r
153         content.add(new JSeparator(SwingConstants.HORIZONTAL), constraints);\r
154 \r
155         constraints.fill = GridBagConstraints.NONE;\r
156         constraints.anchor = GridBagConstraints.NORTHEAST;\r
157         content.add(bottomPanel, constraints);\r
158 \r
159         return;\r
160     }\r
161 \r
162     /**\r
163      * システムイベントチェックボックス群パネルを作成。\r
164      * @return システムイベントチェックボックス群パネル\r
165      */\r
166     private JComponent createTopicPanel(){\r
167         this.checkPublic.setToolTipText("誰にでも見える発言");\r
168         this.checkWolf.setToolTipText("人狼同士にしか見えない発言");\r
169         this.checkPrivate.setToolTipText("本人にしか見えない発言");\r
170         this.checkGrave.setToolTipText("死者同士にしか見えない発言");\r
171         this.checkExtra.setToolTipText("占い先や護衛先などのシステムメッセージ");\r
172 \r
173         JPanel topicPanel = new JPanel();\r
174 \r
175         Border border = BorderFactory.createEmptyBorder(2, 2, 2, 2);\r
176         topicPanel.setBorder(border);\r
177 \r
178         GridBagLayout layout = new GridBagLayout();\r
179         GridBagConstraints constraints = new GridBagConstraints();\r
180 \r
181         topicPanel.setLayout(layout);\r
182 \r
183         constraints.anchor = GridBagConstraints.WEST;\r
184         constraints.weightx = 1.0;\r
185         constraints.weighty = 1.0;\r
186 \r
187         constraints.gridwidth = GridBagConstraints.REMAINDER;\r
188         topicPanel.add(this.checkPublic, constraints);\r
189 \r
190         constraints.gridwidth = GridBagConstraints.REMAINDER;\r
191         topicPanel.add(this.checkWolf, constraints);\r
192 \r
193         constraints.gridwidth = GridBagConstraints.REMAINDER;\r
194         topicPanel.add(this.checkPrivate, constraints);\r
195 \r
196         constraints.gridwidth = GridBagConstraints.REMAINDER;\r
197         topicPanel.add(this.checkGrave, constraints);\r
198 \r
199         constraints.gridwidth = GridBagConstraints.REMAINDER;\r
200         topicPanel.add(this.checkExtra, constraints);\r
201 \r
202         return topicPanel;\r
203     }\r
204 \r
205     /**\r
206      * キャラ一覧チェックボックス群パネルを作成。\r
207      * @return キャラ一覧チェックボックス群パネル\r
208      */\r
209     private JComponent createAvatarPanel(){\r
210         JPanel avatarPanel = new JPanel();\r
211 \r
212         Border border = BorderFactory.createEmptyBorder(2, 2, 2, 2);\r
213         avatarPanel.setBorder(border);\r
214 \r
215         GridBagLayout layout = new GridBagLayout();\r
216         GridBagConstraints constraints = new GridBagConstraints();\r
217 \r
218         avatarPanel.setLayout(layout);\r
219 \r
220         constraints.weightx = 1.0 / COLS;\r
221         constraints.weighty = 1.0;\r
222         constraints.anchor = GridBagConstraints.WEST;\r
223 \r
224         int xPos = 0;\r
225         for(Avatar avatar : Avatar.getPredefinedAvatarList()){\r
226             JCheckBox checkBox = new JCheckBox(avatar.getName(), true);\r
227             checkBox.setToolTipText(avatar.getJobTitle());\r
228             this.cbList.add(checkBox);\r
229             if(xPos >= COLS - 1){\r
230                 constraints.gridwidth = GridBagConstraints.REMAINDER;\r
231                 xPos = 0;\r
232             }else{\r
233                 constraints.gridwidth = 1;\r
234                 xPos++;\r
235             }\r
236             avatarPanel.add(checkBox, constraints);\r
237             this.cbMap.put(avatar, checkBox);\r
238         }\r
239 \r
240         return avatarPanel;\r
241     }\r
242 \r
243     /**\r
244      * ボタン群パネルを生成。\r
245      * @return ボタン群パネル\r
246      */\r
247     private JComponent createButtonPanel(){\r
248         this.selAllButton.setToolTipText(\r
249                 "全キャラクタの発言を表示する");\r
250         this.selNoneButton.setToolTipText(\r
251                 "全キャラクタの発言をフィルタリングする");\r
252         this.negateButton.setToolTipText(\r
253                 "(表示⇔フィルタリング)の設定を反転させる");\r
254 \r
255         JPanel buttonPanel = new JPanel();\r
256 \r
257         GridBagLayout layout = new GridBagLayout();\r
258         GridBagConstraints constraints = new GridBagConstraints();\r
259 \r
260         buttonPanel.setLayout(layout);\r
261 \r
262         constraints.weightx = 1.0 / 3;\r
263         constraints.insets = new Insets(3, 3, 3, 3);\r
264         buttonPanel.add(this.selAllButton, constraints);\r
265         buttonPanel.add(this.selNoneButton, constraints);\r
266         buttonPanel.add(this.negateButton, constraints);\r
267 \r
268         return buttonPanel;\r
269     }\r
270 \r
271     /**\r
272      * 下段パネルを生成する。\r
273      * @return 下段パネル\r
274      */\r
275     private JComponent createBottomPanel(){\r
276         JPanel panel = new JPanel();\r
277 \r
278         GridBagLayout layout = new GridBagLayout();\r
279         GridBagConstraints constraints = new GridBagConstraints();\r
280 \r
281         panel.setLayout(layout);\r
282 \r
283         constraints.fill = GridBagConstraints.NONE;\r
284         constraints.insets = new Insets(3, 3, 3, 3);\r
285         panel.add(this.checkRealtime, constraints);\r
286         panel.add(this.applyButton, constraints);\r
287 \r
288         return panel;\r
289     }\r
290 \r
291     /**\r
292      * リスナを登録する。\r
293      * @param listener リスナ\r
294      */\r
295     public void addChangeListener(ChangeListener listener){\r
296         this.listeners.add(ChangeListener.class, listener);\r
297     }\r
298 \r
299     /**\r
300      * リスナを削除する。\r
301      * @param listener リスナ\r
302      */\r
303     public void removeChangeListener(ChangeListener listener){\r
304         this.listeners.remove(ChangeListener.class, listener);\r
305     }\r
306 \r
307     /**\r
308      * 全リスナを取得する。\r
309      * @return リスナの配列\r
310      */\r
311     public ChangeListener[] getChangeListeners(){\r
312         return this.listeners.getListeners(ChangeListener.class);\r
313     }\r
314 \r
315     /**\r
316      * 全リスナへフィルタ操作を通知する。\r
317      */\r
318     protected void fireCheckChanged(){\r
319         ChangeEvent changeEvent = new ChangeEvent(this);\r
320         for(ChangeListener listener : getChangeListeners()){\r
321             listener.stateChanged(changeEvent);\r
322         }\r
323     }\r
324 \r
325     /**\r
326      * ボタン状態の初期化。\r
327      */\r
328     public void initButtons(){\r
329         this.checkPublic.setSelected(true);\r
330         this.checkWolf.setSelected(true);\r
331         this.checkPrivate.setSelected(true);\r
332         this.checkGrave.setSelected(true);\r
333         this.checkExtra.setSelected(true);\r
334 \r
335         this.selAllButton.doClick();\r
336 \r
337         return;\r
338     }\r
339 \r
340     /**\r
341      * チェックボックスまたはボタン操作時にリスナとして呼ばれる。\r
342      * {@inheritDoc}\r
343      * @param event イベント\r
344      */\r
345     public void actionPerformed(ActionEvent event){\r
346         Object source = event.getSource();\r
347 \r
348         boolean isRealtime = this.checkRealtime.isSelected();\r
349 \r
350         if(source == this.selAllButton){\r
351             boolean hasChanged = false;\r
352             for(JCheckBox avatarCBox : this.cbList){\r
353                 if( ! avatarCBox.isSelected()){\r
354                     avatarCBox.setSelected(true);\r
355                     hasChanged = true;\r
356                 }\r
357             }\r
358             if(isRealtime && hasChanged){\r
359                 fireCheckChanged();\r
360             }\r
361         }else if(source == this.selNoneButton){\r
362             boolean hasChanged = false;\r
363             for(JCheckBox avatarCBox : this.cbList){\r
364                 if(avatarCBox.isSelected()){\r
365                     avatarCBox.setSelected(false);\r
366                     hasChanged = true;\r
367                 }\r
368             }\r
369             if(isRealtime && hasChanged){\r
370                 fireCheckChanged();\r
371             }\r
372         }else if(source == this.negateButton){\r
373             for(JCheckBox avatarCBox : this.cbList){\r
374                 if(avatarCBox.isSelected()){\r
375                     avatarCBox.setSelected(false);\r
376                 }else{\r
377                     avatarCBox.setSelected(true);\r
378                 }\r
379             }\r
380             if(isRealtime){\r
381                 fireCheckChanged();\r
382             }\r
383         }else if(source == this.checkRealtime){\r
384             if(isRealtime){\r
385                 this.applyButton.setEnabled(false);\r
386                 fireCheckChanged();\r
387             }else{\r
388                 this.applyButton.setEnabled(true);\r
389             }\r
390         }else if(source == this.applyButton){\r
391             fireCheckChanged();\r
392         }else if(source instanceof JCheckBox){\r
393             if(isRealtime){\r
394                 fireCheckChanged();\r
395             }\r
396         }\r
397 \r
398         return;\r
399     }\r
400 \r
401     /**\r
402      * {@inheritDoc}\r
403      * @param topic {@inheritDoc}\r
404      * @return {@inheritDoc}\r
405      */\r
406     public boolean isFiltered(Topic topic){\r
407         Talk talk;\r
408         if(topic instanceof Talk){\r
409             talk = (Talk) topic;\r
410         }else if(topic instanceof SysEvent){\r
411             SysEvent sysEvent = (SysEvent) topic;\r
412             if(sysEvent.getEventFamily() == EventFamily.EXTRA){\r
413                 if( ! this.checkExtra.isSelected() ){\r
414                     return true;\r
415                 }\r
416             }\r
417             return false;\r
418         }else{\r
419             return false;\r
420         }\r
421 \r
422         JCheckBox cbox;\r
423 \r
424         TalkType type = talk.getTalkType();\r
425         switch(type){\r
426         case PUBLIC:\r
427             cbox = this.checkPublic;\r
428             break;\r
429         case WOLFONLY:\r
430             cbox = this.checkWolf;\r
431             break;\r
432         case PRIVATE:\r
433             cbox = this.checkPrivate;\r
434             break;\r
435         case GRAVE:\r
436             cbox = this.checkGrave;\r
437             break;\r
438         default:\r
439             assert false;\r
440             return true;\r
441         }\r
442         if( ! cbox.isSelected()){\r
443             return true;\r
444         }\r
445 \r
446         Avatar avatar = talk.getAvatar();\r
447         cbox = this.cbMap.get(avatar);\r
448         if( cbox != null && ! cbox.isSelected()){\r
449             return true;\r
450         }\r
451 \r
452         return false;\r
453     }\r
454 \r
455     /**\r
456      * {@inheritDoc}\r
457      * @return {@inheritDoc}\r
458      */\r
459     public FilterContext getFilterContext(){\r
460         return new FilterPanelContext();\r
461     }\r
462 \r
463     /**\r
464      * {@inheritDoc}\r
465      * @param context {@inheritDoc}\r
466      * @return {@inheritDoc}\r
467      */\r
468     public boolean isSame(FilterContext context){\r
469         if(context == null) return false;\r
470         if( ! (context instanceof FilterPanelContext) ) return false;\r
471         FilterPanelContext argContext = (FilterPanelContext) context;\r
472         FilterPanelContext thisContext =\r
473                 (FilterPanelContext) getFilterContext();\r
474 \r
475         return thisContext.context.equals(argContext.context);\r
476     }\r
477 \r
478     /**\r
479      * カスタム化されたフィルタ状態。\r
480      */\r
481     private final class FilterPanelContext implements FilterContext{\r
482 \r
483         private final BitSet context = new BitSet();\r
484 \r
485         /**\r
486          * コンストラクタ。\r
487          */\r
488         public FilterPanelContext(){\r
489             super();\r
490 \r
491             int index = 0;\r
492             this.context.set(index++,\r
493                     FilterPanel.this.checkPublic.isSelected());\r
494             this.context.set(index++,\r
495                     FilterPanel.this.checkWolf.isSelected());\r
496             this.context.set(index++,\r
497                     FilterPanel.this.checkPrivate.isSelected());\r
498             this.context.set(index++,\r
499                     FilterPanel.this.checkGrave.isSelected());\r
500             this.context.set(index++,\r
501                     FilterPanel.this.checkExtra.isSelected());\r
502 \r
503             for(Avatar avatar : Avatar.getPredefinedAvatarList()){\r
504                 JCheckBox checkBox = FilterPanel.this.cbMap.get(avatar);\r
505                 this.context.set(index++, checkBox.isSelected());\r
506             }\r
507 \r
508             return;\r
509         }\r
510 \r
511         /**\r
512          * {@inheritDoc}\r
513          * @return {@inheritDoc}\r
514          */\r
515         @Override\r
516         public String toString(){\r
517             return this.context.toString();\r
518         }\r
519 \r
520     }\r
521 \r
522 }\r