OSDN Git Service

Maven3対応。
[jindolf/Jindolf.git] / src / main / java / jp / sfjp / jindolf / summary / DaySummary.java
1 /*
2  * summary of day panel
3  *
4  * License : The MIT License
5  * Copyright(c) 2008 olyutorskii
6  */
7
8 package jp.sfjp.jindolf.summary;
9
10 import java.awt.Color;
11 import java.awt.Component;
12 import java.awt.Container;
13 import java.awt.Dimension;
14 import java.awt.Frame;
15 import java.awt.GridBagConstraints;
16 import java.awt.GridBagLayout;
17 import java.awt.Image;
18 import java.awt.Insets;
19 import java.awt.LayoutManager;
20 import java.awt.event.ActionEvent;
21 import java.awt.event.ActionListener;
22 import java.awt.event.ItemEvent;
23 import java.awt.event.ItemListener;
24 import java.awt.event.WindowEvent;
25 import java.awt.event.WindowListener;
26 import java.text.NumberFormat;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.SortedSet;
31 import java.util.TreeSet;
32 import javax.swing.ImageIcon;
33 import javax.swing.JButton;
34 import javax.swing.JComboBox;
35 import javax.swing.JDialog;
36 import javax.swing.JLabel;
37 import javax.swing.JScrollPane;
38 import javax.swing.JSeparator;
39 import javax.swing.JTable;
40 import javax.swing.ListSelectionModel;
41 import javax.swing.SwingConstants;
42 import javax.swing.table.DefaultTableCellRenderer;
43 import javax.swing.table.DefaultTableModel;
44 import javax.swing.table.TableColumn;
45 import javax.swing.table.TableColumnModel;
46 import jp.sfjp.jindolf.data.Avatar;
47 import jp.sfjp.jindolf.data.Period;
48 import jp.sfjp.jindolf.data.Talk;
49 import jp.sfjp.jindolf.data.Topic;
50 import jp.sfjp.jindolf.data.Village;
51 import jp.sfjp.jindolf.glyph.TalkDraw;
52 import jp.sfjp.jindolf.util.GUIUtils;
53 import jp.sourceforge.jindolf.corelib.TalkType;
54
55 /**
56  * その日ごとの集計。
57  */
58 @SuppressWarnings("serial")
59 public class DaySummary extends JDialog
60         implements WindowListener, ActionListener, ItemListener{
61
62     private static final NumberFormat AVERAGE_FORM;
63     private static final String PUBTALK   = "白発言";
64     private static final String WOLFTALK  = "赤発言";
65     private static final String GRAVETALK = "青発言";
66     private static final String PRVTALK   = "灰発言";
67     private static final String ALLTALK   = "全発言";
68     private static final int HORIZONTAL_GAP = 5;
69     private static final int VERTICAL_GAP   = 1;
70     private static final Color COLOR_ALL = new Color(0xffff80);
71
72     static{
73         AVERAGE_FORM = NumberFormat.getInstance();
74         AVERAGE_FORM.setMaximumFractionDigits(1);
75         AVERAGE_FORM.setMinimumFractionDigits(1);
76     }
77
78
79     private final DefaultTableModel tableModel;
80     private final TableColumn avatarColumn;
81
82     private final JTable tableComp;
83     private final JComboBox typeSelector = new JComboBox();
84     private final JButton closeButton = new JButton("閉じる");
85     private final JLabel caption = new JLabel();
86     private final JLabel totalSum = new JLabel();
87
88     private TalkType talkFilter;
89     private Period period;
90
91
92     /**
93      * コンストラクタ。
94      * 集計結果を表示するモーダルダイアログを生成する。
95      * @param owner オーナー
96      */
97     public DaySummary(Frame owner){
98         super(owner);
99         setModal(true);
100
101         GUIUtils.modifyWindowAttributes(this, true, false, true);
102
103         setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
104         addWindowListener(this);
105
106         this.tableModel = createInitModel();
107         this.tableComp = new JTable();
108         this.tableComp.setModel(this.tableModel);
109         this.tableComp.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
110         this.tableComp.setIntercellSpacing(
111                 new Dimension(HORIZONTAL_GAP, VERTICAL_GAP) );
112         this.tableComp.setDefaultEditor(Object.class, null);
113         this.tableComp.setDefaultRenderer(Object.class, new CustomRenderer());
114         this.tableComp.setShowGrid(true);
115
116         TableColumnModel tcolModel = this.tableComp.getColumnModel();
117
118         this.avatarColumn = tcolModel.getColumn(0);
119
120         DefaultTableCellRenderer renderer;
121
122         renderer = new DefaultTableCellRenderer();
123         renderer.setHorizontalAlignment(SwingConstants.RIGHT);
124         tcolModel.getColumn(1).setCellRenderer(renderer);
125
126         renderer = new DefaultTableCellRenderer();
127         renderer.setHorizontalAlignment(SwingConstants.RIGHT);
128         tcolModel.getColumn(2).setCellRenderer(renderer);
129
130         renderer = new DefaultTableCellRenderer();
131         renderer.setHorizontalAlignment(SwingConstants.RIGHT);
132         tcolModel.getColumn(3).setCellRenderer(renderer);
133
134         this.typeSelector.addItem(PUBTALK);
135         this.typeSelector.addItem(WOLFTALK);
136         this.typeSelector.addItem(GRAVETALK);
137         this.typeSelector.addItem(PRVTALK);
138         this.typeSelector.addItem(ALLTALK);
139
140         this.closeButton.addActionListener(this);
141         this.typeSelector.addItemListener(this);
142
143         this.typeSelector.setSelectedItem(null);
144         this.typeSelector.setSelectedItem(PUBTALK);
145
146         design();
147
148         clearModel();
149
150         return;
151     }
152
153
154     /**
155      * 初期のデータモデルを生成する。
156      * @return データモデル
157      */
158     private static DefaultTableModel createInitModel(){
159         DefaultTableModel result;
160         result = new DefaultTableModel();
161
162         Object[] rowHeads = {"名前", "発言回数", "平均文字列長", "最終発言"};
163         result.setColumnCount(rowHeads.length);
164         result.setColumnIdentifiers(rowHeads);
165
166         return result;
167     }
168
169
170     /**
171      * テーブルをクリアする。
172      */
173     private void clearModel(){
174         int rows = this.tableModel.getRowCount();
175         for(int ct = 1; ct <= rows; ct++){
176             this.tableModel.removeRow(0);
177         }
178     }
179
180     /**
181      * 行を追加する。
182      * @param avatar アバター
183      * @param talkCount 発言回数
184      * @param totalChars 発言文字総数
185      * @param lastTime 最終発言時刻
186      */
187     private void appendRow(Avatar avatar,
188                             Integer talkCount,
189                             Integer totalChars,
190                             String lastTime ){
191         String talks = talkCount + " 回";
192
193         double average;
194         if(talkCount <= 0) average = 0.0;
195         else               average = (double)totalChars / (double)talkCount;
196         String chars = AVERAGE_FORM.format(average) + " 文字";
197
198         Object[] row = {avatar, talks, chars, lastTime};
199         int rowIndex = this.tableModel.getRowCount();
200
201         this.tableModel.insertRow(rowIndex, row);
202
203         return;
204     }
205
206     /**
207      * デザインを行う。
208      */
209     private void design(){
210         Container content = getContentPane();
211
212         LayoutManager layout = new GridBagLayout();
213         GridBagConstraints constraints = new GridBagConstraints();
214         content.setLayout(layout);
215
216         constraints.insets = new Insets(5, 5, 5, 5);
217
218         constraints.gridwidth = 1;
219         constraints.fill = GridBagConstraints.NONE;
220         constraints.anchor = GridBagConstraints.WEST;
221         content.add(this.caption, constraints);
222
223         constraints.gridwidth = GridBagConstraints.REMAINDER;
224         constraints.fill = GridBagConstraints.NONE;
225         content.add(this.typeSelector, constraints);
226
227         JScrollPane scroller = new JScrollPane(this.tableComp);
228         constraints.weightx = 1.0;
229         constraints.weighty = 1.0;
230         constraints.fill = GridBagConstraints.BOTH;
231         content.add(scroller, constraints);
232
233         constraints.weightx = 0.0;
234         constraints.weighty = 0.0;
235         constraints.fill = GridBagConstraints.NONE;
236         constraints.anchor = GridBagConstraints.WEST;
237         content.add(this.totalSum, constraints);
238
239         constraints.weightx = 1.0;
240         constraints.weighty = 0.0;
241         constraints.fill = GridBagConstraints.HORIZONTAL;
242         content.add(new JSeparator(), constraints);
243
244         constraints.weightx = 0.0;
245         constraints.weighty = 0.0;
246         constraints.fill = GridBagConstraints.NONE;
247         constraints.anchor = GridBagConstraints.EAST;
248         content.add(this.closeButton, constraints);
249
250         return;
251     }
252
253     /**
254      * 与えられたPeriodで集計を更新する。
255      * @param newPeriod 日
256      */
257     public void summaryPeriod(Period newPeriod){
258         this.period = newPeriod;
259         summaryPeriod();
260     }
261
262     /**
263      * 集計を更新する。
264      */
265     private void summaryPeriod(){
266         clearModel();
267
268         if(this.period == null) return;
269
270         SortedSet<Avatar> avatarSet = new TreeSet<Avatar>();
271         Map<Avatar, Integer> talkCount  = new HashMap<Avatar, Integer>();
272         Map<Avatar, Integer> totalChars = new HashMap<Avatar, Integer>();
273         Map<Avatar, Talk> lastTalk = new HashMap<Avatar, Talk>();
274
275         List<Topic> topicList = this.period.getTopicList();
276         for(Topic topic : topicList){
277             if( ! (topic instanceof Talk)) continue;
278             Talk talk = (Talk) topic;
279             if(talk.getTalkCount() <= 0) continue;
280             if(   this.talkFilter != null
281                && talk.getTalkType() != this.talkFilter) continue;
282
283             Avatar avatar = talk.getAvatar();
284
285             Integer counts = talkCount.get(avatar);
286             if(counts == null) counts = Integer.valueOf(0);
287             counts++;
288             talkCount.put(avatar, counts);
289
290             Integer total = totalChars.get(avatar);
291             if(total == null) total = Integer.valueOf(0);
292             total += talk.getTotalChars();
293             totalChars.put(avatar, total);
294
295             lastTalk.put(avatar, talk);
296
297             avatarSet.add(avatar);
298         }
299
300         int sum = 0;
301         for(Avatar avatar : avatarSet){
302             Integer counts = talkCount.get(avatar);
303             Integer total = totalChars.get(avatar);
304             String lastTime = lastTalk.get(avatar).getAnchorNotation();
305             appendRow(avatar, counts, total, lastTime);
306             sum += counts;
307         }
308
309         this.totalSum.setText("合計:" + sum + " 発言");
310
311         Village village = this.period.getVillage();
312         String villageName = village.getVillageName();
313         String periodCaption = this.period.getCaption();
314         this.caption.setText(villageName + "村 " + periodCaption);
315
316         return;
317     }
318
319     /**
320      * {@inheritDoc}
321      * @param event {@inheritDoc}
322      */
323     @Override
324     public void windowActivated(WindowEvent event){
325         return;
326     }
327
328     /**
329      * {@inheritDoc}
330      * @param event {@inheritDoc}
331      */
332     @Override
333     public void windowDeactivated(WindowEvent event){
334         return;
335     }
336
337     /**
338      * {@inheritDoc}
339      * @param event {@inheritDoc}
340      */
341     @Override
342     public void windowIconified(WindowEvent event){
343         return;
344     }
345
346     /**
347      * {@inheritDoc}
348      * @param event {@inheritDoc}
349      */
350     @Override
351     public void windowDeiconified(WindowEvent event){
352         return;
353     }
354
355     /**
356      * {@inheritDoc}
357      * @param event {@inheritDoc}
358      */
359     @Override
360     public void windowOpened(WindowEvent event){
361         return;
362     }
363
364     /**
365      * {@inheritDoc}
366      * ダイアログのクローズボタン押下処理を行う。
367      * @param event ウィンドウ変化イベント {@inheritDoc}
368      */
369     @Override
370     public void windowClosing(WindowEvent event){
371         close();
372         return;
373     }
374
375     /**
376      * {@inheritDoc}
377      * @param event {@inheritDoc}
378      */
379     @Override
380     public void windowClosed(WindowEvent event){
381         return;
382     }
383
384     /**
385      * {@inheritDoc}
386      * クローズボタン押下処理。
387      * @param event イベント {@inheritDoc}
388      */
389     @Override
390     public void actionPerformed(ActionEvent event){
391         if(event.getSource() != this.closeButton) return;
392         close();
393         return;
394     }
395
396     /**
397      * {@inheritDoc}
398      * コンボボックス操作処理。
399      * @param event イベント {@inheritDoc}
400      */
401     @Override
402     public void itemStateChanged(ItemEvent event){
403         if(event.getStateChange() != ItemEvent.SELECTED) return;
404
405         Object selected = this.typeSelector.getSelectedItem();
406         if     (selected == PUBTALK)   this.talkFilter = TalkType.PUBLIC;
407         else if(selected == WOLFTALK)  this.talkFilter = TalkType.WOLFONLY;
408         else if(selected == GRAVETALK) this.talkFilter = TalkType.GRAVE;
409         else if(selected == PRVTALK)   this.talkFilter = TalkType.PRIVATE;
410         else if(selected == ALLTALK)   this.talkFilter = null;
411
412         summaryPeriod();
413
414         return;
415     }
416
417     /**
418      * このパネルを閉じる。
419      */
420     private void close(){
421         clearModel();
422         this.period = null;
423         setVisible(false);
424         return;
425     }
426
427     /**
428      * Avatar 顔イメージ描画用カスタムセルレンダラ。
429      */
430     private class CustomRenderer extends DefaultTableCellRenderer{
431
432         /**
433          * コンストラクタ。
434          */
435         public CustomRenderer(){
436             super();
437             return;
438         }
439
440         /**
441          * {@inheritDoc}
442          * セルに{@link Avatar}がきたら顔アイコンと名前を表示する。
443          * @param value {@inheritDoc}
444          */
445         @Override
446         public void setValue(Object value){
447             if(value instanceof Avatar){
448                 Avatar avatar = (Avatar) value;
449
450                 Village village = DaySummary.this.period.getVillage();
451                 Image image = village.getAvatarFaceImage(avatar);
452                 if(image == null) image = village.getGraveImage();
453                 if(image != null){
454                     ImageIcon icon = new ImageIcon(image);
455                     setIcon(icon);
456                 }
457
458                 setText(avatar.getName());
459
460                 Dimension prefSize = getPreferredSize();
461
462                 int cellHeight = VERTICAL_GAP * 2 + prefSize.height;
463                 if(DaySummary.this.tableComp.getRowHeight() < cellHeight){
464                     DaySummary.this.tableComp.setRowHeight(cellHeight);
465                 }
466
467                 int cellWidth = HORIZONTAL_GAP * 2 + prefSize.width;
468                 if(  DaySummary.this.avatarColumn.getPreferredWidth()
469                    < cellWidth ){
470                     DaySummary.this.avatarColumn.setPreferredWidth(cellWidth);
471                 }
472
473                 return;
474             }
475
476             super.setValue(value);
477
478             return;
479         }
480
481         /**
482          *  {@inheritDoc}
483          * 統計種別によってセル色を変える。
484          * @param table {@inheritDoc}
485          * @param value {@inheritDoc}
486          * @param isSelected {@inheritDoc}
487          * @param hasFocus {@inheritDoc}
488          * @param row {@inheritDoc}
489          * @param column {@inheritDoc}
490          * @return {@inheritDoc}
491          */
492         @Override
493         public Component getTableCellRendererComponent(JTable table,
494                                                            Object value,
495                                                            boolean isSelected,
496                                                            boolean hasFocus,
497                                                            int row,
498                                                            int column ){
499             Component result = super.getTableCellRendererComponent(table,
500                                                                    value,
501                                                                    isSelected,
502                                                                    hasFocus,
503                                                                    row,
504                                                                    column );
505
506             Object selected = DaySummary.this.typeSelector.getSelectedItem();
507             Color bgColor = null;
508             if(selected == PUBTALK){
509                 bgColor = TalkDraw.COLOR_PUBLIC;
510             }else if(selected == WOLFTALK){
511                 bgColor = TalkDraw.COLOR_WOLFONLY;
512             }else if(selected == GRAVETALK){
513                 bgColor = TalkDraw.COLOR_GRAVE;
514             }else if(selected == PRVTALK){
515                 bgColor = TalkDraw.COLOR_PRIVATE;
516             }else if(selected == ALLTALK){
517                 bgColor = COLOR_ALL;
518             }else{
519                 assert false;
520                 return null;
521             }
522
523             result.setForeground(Color.BLACK);
524             result.setBackground(bgColor);
525
526             return result;
527         }
528     }
529
530 }