OSDN Git Service

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