4 * Copyright(c) 2008 olyutorskii
\r
5 * $Id: FilterPanel.java 969 2009-12-24 16:12:58Z olyutorskii $
\r
8 package jp.sourceforge.jindolf;
\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
40 @SuppressWarnings("serial")
\r
41 public class FilterPanel extends JDialog
\r
42 implements ActionListener, TopicFilter{
\r
44 private static final int COLS = 4;
\r
46 private static final String FRAMETITLE = "発言フィルタ - " + Jindolf.TITLE;
\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
54 private final JButton selAllButton = new JButton("全選択");
\r
55 private final JButton selNoneButton = new JButton("全解除");
\r
56 private final JButton negateButton = new JButton("反転");
\r
58 private final JCheckBox checkRealtime =
\r
59 new JCheckBox("リアルタイム更新", true);
\r
60 private final JButton applyButton = new JButton("フィルタ適用");
\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
66 private final EventListenerList listeners = new EventListenerList();
\r
70 * @param owner フレームオーナー
\r
72 public FilterPanel(Frame owner){
\r
73 super(owner, FRAMETITLE, false);
\r
75 GUIUtils.modifyWindowAttributes(this, true, false, true);
\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
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
89 for(JCheckBox avatarCheckBox : this.cbList){
\r
90 avatarCheckBox.addActionListener(this);
\r
93 this.selAllButton.addActionListener(this);
\r
94 this.selNoneButton.addActionListener(this);
\r
95 this.negateButton.addActionListener(this);
\r
97 this.checkRealtime.addActionListener(this);
\r
98 this.applyButton.addActionListener(this);
\r
99 this.applyButton.setEnabled(false);
\r
106 * @param topicPanel システムイベント選択
\r
107 * @param avatarPanel キャラ一覧
\r
108 * @param buttonPanel ボタン群
\r
109 * @param bottomPanel 下段パネル
\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
117 GridBagLayout layout = new GridBagLayout();
\r
118 GridBagConstraints constraints = new GridBagConstraints();
\r
120 content.setLayout(layout);
\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
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
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
142 constraints.insets = new Insets(0, 3, 0, 3);
\r
143 content.add(new JSeparator(), constraints);
\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
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
155 constraints.fill = GridBagConstraints.NONE;
\r
156 constraints.anchor = GridBagConstraints.NORTHEAST;
\r
157 content.add(bottomPanel, constraints);
\r
163 * システムイベントチェックボックス群パネルを作成。
\r
164 * @return システムイベントチェックボックス群パネル
\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
173 JPanel topicPanel = new JPanel();
\r
175 Border border = BorderFactory.createEmptyBorder(2, 2, 2, 2);
\r
176 topicPanel.setBorder(border);
\r
178 GridBagLayout layout = new GridBagLayout();
\r
179 GridBagConstraints constraints = new GridBagConstraints();
\r
181 topicPanel.setLayout(layout);
\r
183 constraints.anchor = GridBagConstraints.WEST;
\r
184 constraints.weightx = 1.0;
\r
185 constraints.weighty = 1.0;
\r
187 constraints.gridwidth = GridBagConstraints.REMAINDER;
\r
188 topicPanel.add(this.checkPublic, constraints);
\r
190 constraints.gridwidth = GridBagConstraints.REMAINDER;
\r
191 topicPanel.add(this.checkWolf, constraints);
\r
193 constraints.gridwidth = GridBagConstraints.REMAINDER;
\r
194 topicPanel.add(this.checkPrivate, constraints);
\r
196 constraints.gridwidth = GridBagConstraints.REMAINDER;
\r
197 topicPanel.add(this.checkGrave, constraints);
\r
199 constraints.gridwidth = GridBagConstraints.REMAINDER;
\r
200 topicPanel.add(this.checkExtra, constraints);
\r
206 * キャラ一覧チェックボックス群パネルを作成。
\r
207 * @return キャラ一覧チェックボックス群パネル
\r
209 private JComponent createAvatarPanel(){
\r
210 JPanel avatarPanel = new JPanel();
\r
212 Border border = BorderFactory.createEmptyBorder(2, 2, 2, 2);
\r
213 avatarPanel.setBorder(border);
\r
215 GridBagLayout layout = new GridBagLayout();
\r
216 GridBagConstraints constraints = new GridBagConstraints();
\r
218 avatarPanel.setLayout(layout);
\r
220 constraints.weightx = 1.0 / COLS;
\r
221 constraints.weighty = 1.0;
\r
222 constraints.anchor = GridBagConstraints.WEST;
\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
233 constraints.gridwidth = 1;
\r
236 avatarPanel.add(checkBox, constraints);
\r
237 this.cbMap.put(avatar, checkBox);
\r
240 return avatarPanel;
\r
247 private JComponent createButtonPanel(){
\r
248 this.selAllButton.setToolTipText(
\r
250 this.selNoneButton.setToolTipText(
\r
251 "全キャラクタの発言をフィルタリングする");
\r
252 this.negateButton.setToolTipText(
\r
253 "(表示⇔フィルタリング)の設定を反転させる");
\r
255 JPanel buttonPanel = new JPanel();
\r
257 GridBagLayout layout = new GridBagLayout();
\r
258 GridBagConstraints constraints = new GridBagConstraints();
\r
260 buttonPanel.setLayout(layout);
\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
268 return buttonPanel;
\r
275 private JComponent createBottomPanel(){
\r
276 JPanel panel = new JPanel();
\r
278 GridBagLayout layout = new GridBagLayout();
\r
279 GridBagConstraints constraints = new GridBagConstraints();
\r
281 panel.setLayout(layout);
\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
293 * @param listener リスナ
\r
295 public void addChangeListener(ChangeListener listener){
\r
296 this.listeners.add(ChangeListener.class, listener);
\r
301 * @param listener リスナ
\r
303 public void removeChangeListener(ChangeListener listener){
\r
304 this.listeners.remove(ChangeListener.class, listener);
\r
311 public ChangeListener[] getChangeListeners(){
\r
312 return this.listeners.getListeners(ChangeListener.class);
\r
316 * 全リスナへフィルタ操作を通知する。
\r
318 protected void fireCheckChanged(){
\r
319 ChangeEvent changeEvent = new ChangeEvent(this);
\r
320 for(ChangeListener listener : getChangeListeners()){
\r
321 listener.stateChanged(changeEvent);
\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
335 this.selAllButton.doClick();
\r
341 * チェックボックスまたはボタン操作時にリスナとして呼ばれる。
\r
343 * @param event イベント
\r
345 public void actionPerformed(ActionEvent event){
\r
346 Object source = event.getSource();
\r
348 boolean isRealtime = this.checkRealtime.isSelected();
\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
358 if(isRealtime && hasChanged){
\r
359 fireCheckChanged();
\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
369 if(isRealtime && hasChanged){
\r
370 fireCheckChanged();
\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
377 avatarCBox.setSelected(true);
\r
381 fireCheckChanged();
\r
383 }else if(source == this.checkRealtime){
\r
385 this.applyButton.setEnabled(false);
\r
386 fireCheckChanged();
\r
388 this.applyButton.setEnabled(true);
\r
390 }else if(source == this.applyButton){
\r
391 fireCheckChanged();
\r
392 }else if(source instanceof JCheckBox){
\r
394 fireCheckChanged();
\r
403 * @param topic {@inheritDoc}
\r
404 * @return {@inheritDoc}
\r
406 public boolean isFiltered(Topic topic){
\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
424 TalkType type = talk.getTalkType();
\r
427 cbox = this.checkPublic;
\r
430 cbox = this.checkWolf;
\r
433 cbox = this.checkPrivate;
\r
436 cbox = this.checkGrave;
\r
442 if( ! cbox.isSelected()){
\r
446 Avatar avatar = talk.getAvatar();
\r
447 cbox = this.cbMap.get(avatar);
\r
448 if( cbox != null && ! cbox.isSelected()){
\r
457 * @return {@inheritDoc}
\r
459 public FilterContext getFilterContext(){
\r
460 return new FilterPanelContext();
\r
465 * @param context {@inheritDoc}
\r
466 * @return {@inheritDoc}
\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
475 return thisContext.context.equals(argContext.context);
\r
481 private final class FilterPanelContext implements FilterContext{
\r
483 private final BitSet context = new BitSet();
\r
488 public FilterPanelContext(){
\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
503 for(Avatar avatar : Avatar.getPredefinedAvatarList()){
\r
504 JCheckBox checkBox = FilterPanel.this.cbMap.get(avatar);
\r
505 this.context.set(index++, checkBox.isSelected());
\r
513 * @return {@inheritDoc}
\r
516 public String toString(){
\r
517 return this.context.toString();
\r