OSDN Git Service

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