4 * License : The MIT License
5 * Copyright(c) 2008 olyutorskii
8 package jp.sfjp.jindolf.view;
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;
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;
45 @SuppressWarnings("serial")
46 public class FilterPanel extends JDialog
47 implements ActionListener, TopicFilter{
49 private static final int COLS = 4;
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);
58 private final JButton selAllButton = new JButton("全選択");
59 private final JButton selNoneButton = new JButton("全解除");
60 private final JButton negateButton = new JButton("反転");
62 private final JCheckBox checkRealtime =
63 new JCheckBox("リアルタイム更新", true);
64 private final JButton applyButton = new JButton("フィルタ適用");
66 private final Map<Avatar, JCheckBox> cbMap =
68 private final List<JCheckBox> cbList = new LinkedList<>();
70 private final EventListenerList listeners = new EventListenerList();
74 * @param owner フレームオーナー
76 @SuppressWarnings("LeakingThisInConstructor")
77 public FilterPanel(Frame owner){
81 GUIUtils.modifyWindowAttributes(this, true, false, true);
83 JComponent topicPanel = createTopicPanel();
84 JComponent avatarPanel = createAvatarPanel();
85 JComponent buttonPanel = createButtonPanel();
86 JComponent bottomPanel = createBottomPanel();
87 design(topicPanel, avatarPanel, buttonPanel, bottomPanel);
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);
95 for(JCheckBox avatarCheckBox : this.cbList){
96 avatarCheckBox.addActionListener(this);
99 this.selAllButton.addActionListener(this);
100 this.selNoneButton.addActionListener(this);
101 this.negateButton.addActionListener(this);
103 this.checkRealtime.addActionListener(this);
104 this.applyButton.addActionListener(this);
105 this.applyButton.setEnabled(false);
112 * @param topicPanel システムイベント選択
113 * @param avatarPanel キャラ一覧
114 * @param buttonPanel ボタン群
115 * @param bottomPanel 下段パネル
117 private void design(JComponent topicPanel,
118 JComponent avatarPanel,
119 JComponent buttonPanel,
120 JComponent bottomPanel ){
121 Container content = getContentPane();
123 GridBagLayout layout = new GridBagLayout();
124 GridBagConstraints constraints = new GridBagConstraints();
126 content.setLayout(layout);
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);
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);
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);
148 constraints.insets = new Insets(0, 3, 0, 3);
149 content.add(new JSeparator(), constraints);
151 constraints.weighty = 1.0;
152 constraints.fill = GridBagConstraints.BOTH;
153 constraints.insets = new Insets(0, 0, 0, 0);
154 content.add(avatarPanel, constraints);
156 constraints.weightx = 1.0;
157 constraints.gridwidth = GridBagConstraints.REMAINDER;
158 constraints.fill = GridBagConstraints.HORIZONTAL;
159 content.add(new JSeparator(SwingConstants.HORIZONTAL), constraints);
161 constraints.fill = GridBagConstraints.NONE;
162 constraints.anchor = GridBagConstraints.NORTHEAST;
163 content.add(bottomPanel, constraints);
169 * システムイベントチェックボックス群パネルを作成。
170 * @return システムイベントチェックボックス群パネル
172 private JComponent createTopicPanel(){
173 this.checkPublic.setToolTipText("誰にでも見える発言");
174 this.checkWolf.setToolTipText("人狼同士にしか見えない発言");
175 this.checkPrivate.setToolTipText("本人にしか見えない発言");
176 this.checkGrave.setToolTipText("死者同士にしか見えない発言");
177 this.checkExtra.setToolTipText("占い先や護衛先などのシステムメッセージ");
179 JPanel topicPanel = new JPanel();
181 Border border = BorderFactory.createEmptyBorder(2, 2, 2, 2);
182 topicPanel.setBorder(border);
184 GridBagLayout layout = new GridBagLayout();
185 GridBagConstraints constraints = new GridBagConstraints();
187 topicPanel.setLayout(layout);
189 constraints.anchor = GridBagConstraints.WEST;
190 constraints.weightx = 1.0;
191 constraints.weighty = 1.0;
193 constraints.gridwidth = GridBagConstraints.REMAINDER;
194 topicPanel.add(this.checkPublic, constraints);
196 constraints.gridwidth = GridBagConstraints.REMAINDER;
197 topicPanel.add(this.checkWolf, constraints);
199 constraints.gridwidth = GridBagConstraints.REMAINDER;
200 topicPanel.add(this.checkPrivate, constraints);
202 constraints.gridwidth = GridBagConstraints.REMAINDER;
203 topicPanel.add(this.checkGrave, constraints);
205 constraints.gridwidth = GridBagConstraints.REMAINDER;
206 topicPanel.add(this.checkExtra, constraints);
212 * キャラ一覧チェックボックス群パネルを作成。
213 * @return キャラ一覧チェックボックス群パネル
215 private JComponent createAvatarPanel(){
216 JPanel avatarPanel = new JPanel();
218 Border border = BorderFactory.createEmptyBorder(2, 2, 2, 2);
219 avatarPanel.setBorder(border);
221 GridBagLayout layout = new GridBagLayout();
222 GridBagConstraints constraints = new GridBagConstraints();
224 avatarPanel.setLayout(layout);
226 constraints.weightx = 1.0 / COLS;
227 constraints.weighty = 1.0;
228 constraints.anchor = GridBagConstraints.WEST;
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;
239 constraints.gridwidth = 1;
242 avatarPanel.add(checkBox, constraints);
243 this.cbMap.put(avatar, checkBox);
253 private JComponent createButtonPanel(){
254 this.selAllButton.setToolTipText(
256 this.selNoneButton.setToolTipText(
257 "全キャラクタの発言をフィルタリングする");
258 this.negateButton.setToolTipText(
259 "(表示⇔フィルタリング)の設定を反転させる");
261 JPanel buttonPanel = new JPanel();
263 GridBagLayout layout = new GridBagLayout();
264 GridBagConstraints constraints = new GridBagConstraints();
266 buttonPanel.setLayout(layout);
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);
281 private JComponent createBottomPanel(){
282 JPanel panel = new JPanel();
284 GridBagLayout layout = new GridBagLayout();
285 GridBagConstraints constraints = new GridBagConstraints();
287 panel.setLayout(layout);
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);
299 * @param listener リスナ
301 public void addChangeListener(ChangeListener listener){
302 this.listeners.add(ChangeListener.class, listener);
307 * @param listener リスナ
309 public void removeChangeListener(ChangeListener listener){
310 this.listeners.remove(ChangeListener.class, listener);
317 public ChangeListener[] getChangeListeners(){
318 return this.listeners.getListeners(ChangeListener.class);
324 protected void fireCheckChanged(){
325 ChangeEvent changeEvent = new ChangeEvent(this);
326 for(ChangeListener listener : getChangeListeners()){
327 listener.stateChanged(changeEvent);
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);
341 this.selAllButton.doClick();
347 * チェックボックスまたはボタン操作時にリスナとして呼ばれる。
352 public void actionPerformed(ActionEvent event){
353 Object source = event.getSource();
355 boolean isRealtime = this.checkRealtime.isSelected();
357 if(source == this.selAllButton){
358 boolean hasChanged = false;
359 for(JCheckBox avatarCBox : this.cbList){
360 if( ! avatarCBox.isSelected()){
361 avatarCBox.setSelected(true);
365 if(isRealtime && hasChanged){
368 }else if(source == this.selNoneButton){
369 boolean hasChanged = false;
370 for(JCheckBox avatarCBox : this.cbList){
371 if(avatarCBox.isSelected()){
372 avatarCBox.setSelected(false);
376 if(isRealtime && hasChanged){
379 }else if(source == this.negateButton){
380 for(JCheckBox avatarCBox : this.cbList){
381 if(avatarCBox.isSelected()){
382 avatarCBox.setSelected(false);
384 avatarCBox.setSelected(true);
390 }else if(source == this.checkRealtime){
392 this.applyButton.setEnabled(false);
395 this.applyButton.setEnabled(true);
397 }else if(source == this.applyButton){
399 }else if(source instanceof JCheckBox){
410 * @param topic {@inheritDoc}
411 * @return {@inheritDoc}
414 public boolean isFiltered(Topic topic){
416 if(topic instanceof Talk){
418 }else if(topic instanceof SysEvent){
419 SysEvent sysEvent = (SysEvent) topic;
420 if(sysEvent.getEventFamily() == EventFamily.EXTRA){
421 if( ! this.checkExtra.isSelected() ){
432 TalkType type = talk.getTalkType();
435 cbox = this.checkPublic;
438 cbox = this.checkWolf;
441 cbox = this.checkPrivate;
444 cbox = this.checkGrave;
450 if( ! cbox.isSelected()){
454 Avatar avatar = talk.getAvatar();
455 cbox = this.cbMap.get(avatar);
456 if( cbox != null && ! cbox.isSelected()){
465 * @return {@inheritDoc}
468 public FilterContext getFilterContext(){
469 return new FilterPanelContext();
474 * @param context {@inheritDoc}
475 * @return {@inheritDoc}
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();
485 return thisContext.context.equals(argContext.context);
491 private final class FilterPanelContext implements FilterContext{
493 private final BitSet context = new BitSet();
498 public FilterPanelContext(){
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());
513 for(Avatar avatar : Avatar.getPredefinedAvatarList()){
514 JCheckBox checkBox = FilterPanel.this.cbMap.get(avatar);
515 this.context.set(index++, checkBox.isSelected());
523 * @return {@inheritDoc}
526 public String toString(){
527 return this.context.toString();