OSDN Git Service

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