OSDN Git Service

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