4 * License : The MIT License
5 * Copyright(c) 2008 olyutorskii
8 package jp.sfjp.jindolf.summary;
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;
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;
58 @SuppressWarnings("serial")
59 public class DaySummary extends JDialog
60 implements WindowListener, ActionListener, ItemListener{
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);
73 AVERAGE_FORM = NumberFormat.getInstance();
74 AVERAGE_FORM.setMaximumFractionDigits(1);
75 AVERAGE_FORM.setMinimumFractionDigits(1);
79 private final DefaultTableModel tableModel;
80 private final TableColumn avatarColumn;
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();
88 private TalkType talkFilter;
89 private Period period;
94 * 集計結果を表示するモーダルダイアログを生成する。
97 public DaySummary(Frame owner){
101 GUIUtils.modifyWindowAttributes(this, true, false, true);
103 setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
104 addWindowListener(this);
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);
116 TableColumnModel tcolModel = this.tableComp.getColumnModel();
118 this.avatarColumn = tcolModel.getColumn(0);
120 DefaultTableCellRenderer renderer;
122 renderer = new DefaultTableCellRenderer();
123 renderer.setHorizontalAlignment(SwingConstants.RIGHT);
124 tcolModel.getColumn(1).setCellRenderer(renderer);
126 renderer = new DefaultTableCellRenderer();
127 renderer.setHorizontalAlignment(SwingConstants.RIGHT);
128 tcolModel.getColumn(2).setCellRenderer(renderer);
130 renderer = new DefaultTableCellRenderer();
131 renderer.setHorizontalAlignment(SwingConstants.RIGHT);
132 tcolModel.getColumn(3).setCellRenderer(renderer);
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);
140 this.closeButton.addActionListener(this);
141 this.typeSelector.addItemListener(this);
143 this.typeSelector.setSelectedItem(null);
144 this.typeSelector.setSelectedItem(PUBTALK);
158 private static DefaultTableModel createInitModel(){
159 DefaultTableModel result;
160 result = new DefaultTableModel();
162 Object[] rowHeads = {"名前", "発言回数", "平均文字列長", "最終発言"};
163 result.setColumnCount(rowHeads.length);
164 result.setColumnIdentifiers(rowHeads);
173 private void clearModel(){
174 int rows = this.tableModel.getRowCount();
175 for(int ct = 1; ct <= rows; ct++){
176 this.tableModel.removeRow(0);
183 * @param talkCount 発言回数
184 * @param totalChars 発言文字総数
185 * @param lastTime 最終発言時刻
187 private void appendRow(Avatar avatar,
191 String talks = talkCount + " 回";
194 if(talkCount <= 0) average = 0.0;
195 else average = (double)totalChars / (double)talkCount;
196 String chars = AVERAGE_FORM.format(average) + " 文字";
198 Object[] row = {avatar, talks, chars, lastTime};
199 int rowIndex = this.tableModel.getRowCount();
201 this.tableModel.insertRow(rowIndex, row);
209 private void design(){
210 Container content = getContentPane();
212 LayoutManager layout = new GridBagLayout();
213 GridBagConstraints constraints = new GridBagConstraints();
214 content.setLayout(layout);
216 constraints.insets = new Insets(5, 5, 5, 5);
218 constraints.gridwidth = 1;
219 constraints.fill = GridBagConstraints.NONE;
220 constraints.anchor = GridBagConstraints.WEST;
221 content.add(this.caption, constraints);
223 constraints.gridwidth = GridBagConstraints.REMAINDER;
224 constraints.fill = GridBagConstraints.NONE;
225 content.add(this.typeSelector, constraints);
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);
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);
239 constraints.weightx = 1.0;
240 constraints.weighty = 0.0;
241 constraints.fill = GridBagConstraints.HORIZONTAL;
242 content.add(new JSeparator(), constraints);
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);
254 * 与えられたPeriodで集計を更新する。
257 public void summaryPeriod(Period newPeriod){
258 this.period = newPeriod;
265 private void summaryPeriod(){
268 if(this.period == null) return;
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>();
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;
283 Avatar avatar = talk.getAvatar();
285 Integer counts = talkCount.get(avatar);
286 if(counts == null) counts = Integer.valueOf(0);
288 talkCount.put(avatar, counts);
290 Integer total = totalChars.get(avatar);
291 if(total == null) total = Integer.valueOf(0);
292 total += talk.getTotalChars();
293 totalChars.put(avatar, total);
295 lastTalk.put(avatar, talk);
297 avatarSet.add(avatar);
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);
309 this.totalSum.setText("合計:" + sum + " 発言");
311 Village village = this.period.getVillage();
312 String villageName = village.getVillageName();
313 String periodCaption = this.period.getCaption();
314 this.caption.setText(villageName + "村 " + periodCaption);
321 * @param event {@inheritDoc}
324 public void windowActivated(WindowEvent event){
330 * @param event {@inheritDoc}
333 public void windowDeactivated(WindowEvent event){
339 * @param event {@inheritDoc}
342 public void windowIconified(WindowEvent event){
348 * @param event {@inheritDoc}
351 public void windowDeiconified(WindowEvent event){
357 * @param event {@inheritDoc}
360 public void windowOpened(WindowEvent event){
366 * ダイアログのクローズボタン押下処理を行う。
367 * @param event ウィンドウ変化イベント {@inheritDoc}
370 public void windowClosing(WindowEvent event){
377 * @param event {@inheritDoc}
380 public void windowClosed(WindowEvent event){
387 * @param event イベント {@inheritDoc}
390 public void actionPerformed(ActionEvent event){
391 if(event.getSource() != this.closeButton) return;
399 * @param event イベント {@inheritDoc}
402 public void itemStateChanged(ItemEvent event){
403 if(event.getStateChange() != ItemEvent.SELECTED) return;
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;
420 private void close(){
428 * Avatar 顔イメージ描画用カスタムセルレンダラ。
430 private class CustomRenderer extends DefaultTableCellRenderer{
435 public CustomRenderer(){
442 * セルに{@link Avatar}がきたら顔アイコンと名前を表示する。
443 * @param value {@inheritDoc}
446 public void setValue(Object value){
447 if(value instanceof Avatar){
448 Avatar avatar = (Avatar) value;
450 Village village = DaySummary.this.period.getVillage();
451 Image image = village.getAvatarFaceImage(avatar);
452 if(image == null) image = village.getGraveImage();
454 ImageIcon icon = new ImageIcon(image);
458 setText(avatar.getName());
460 Dimension prefSize = getPreferredSize();
462 int cellHeight = VERTICAL_GAP * 2 + prefSize.height;
463 if(DaySummary.this.tableComp.getRowHeight() < cellHeight){
464 DaySummary.this.tableComp.setRowHeight(cellHeight);
467 int cellWidth = HORIZONTAL_GAP * 2 + prefSize.width;
468 if( DaySummary.this.avatarColumn.getPreferredWidth()
470 DaySummary.this.avatarColumn.setPreferredWidth(cellWidth);
476 super.setValue(value);
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}
493 public Component getTableCellRendererComponent(JTable table,
499 Component result = super.getTableCellRendererComponent(table,
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){
523 result.setForeground(Color.BLACK);
524 result.setBackground(bgColor);