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<String> 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 + " 回";
197 average = (double) totalChars / (double) talkCount;
199 String chars = AVERAGE_FORM.format(average) + " 文字";
201 Object[] row = {avatar, talks, chars, lastTime};
202 int rowIndex = this.tableModel.getRowCount();
204 this.tableModel.insertRow(rowIndex, row);
212 private void design(){
213 Container content = getContentPane();
215 LayoutManager layout = new GridBagLayout();
216 GridBagConstraints constraints = new GridBagConstraints();
217 content.setLayout(layout);
219 constraints.insets = new Insets(5, 5, 5, 5);
221 constraints.gridwidth = 1;
222 constraints.fill = GridBagConstraints.NONE;
223 constraints.anchor = GridBagConstraints.WEST;
224 content.add(this.caption, constraints);
226 constraints.gridwidth = GridBagConstraints.REMAINDER;
227 constraints.fill = GridBagConstraints.NONE;
228 content.add(this.typeSelector, constraints);
230 JScrollPane scroller = new JScrollPane(this.tableComp);
231 constraints.weightx = 1.0;
232 constraints.weighty = 1.0;
233 constraints.fill = GridBagConstraints.BOTH;
234 content.add(scroller, constraints);
236 constraints.weightx = 0.0;
237 constraints.weighty = 0.0;
238 constraints.fill = GridBagConstraints.NONE;
239 constraints.anchor = GridBagConstraints.WEST;
240 content.add(this.totalSum, constraints);
242 constraints.weightx = 1.0;
243 constraints.weighty = 0.0;
244 constraints.fill = GridBagConstraints.HORIZONTAL;
245 content.add(new JSeparator(), constraints);
247 constraints.weightx = 0.0;
248 constraints.weighty = 0.0;
249 constraints.fill = GridBagConstraints.NONE;
250 constraints.anchor = GridBagConstraints.EAST;
251 content.add(this.closeButton, constraints);
257 * 与えられたPeriodで集計を更新する。
260 public void summaryPeriod(Period newPeriod){
261 this.period = newPeriod;
268 private void summaryPeriod(){
271 if(this.period == null) return;
273 SortedSet<Avatar> avatarSet = new TreeSet<>();
274 Map<Avatar, Integer> talkCount = new HashMap<>();
275 Map<Avatar, Integer> totalChars = new HashMap<>();
276 Map<Avatar, Talk> lastTalk = new HashMap<>();
278 List<Topic> topicList = this.period.getTopicList();
279 for(Topic topic : topicList){
280 if( ! (topic instanceof Talk)) continue;
281 Talk talk = (Talk) topic;
282 if(talk.getTalkCount() <= 0) continue;
283 if( this.talkFilter != null
284 && talk.getTalkType() != this.talkFilter) continue;
286 Avatar avatar = talk.getAvatar();
288 Integer counts = talkCount.get(avatar);
289 if(counts == null) counts = Integer.valueOf(0);
291 talkCount.put(avatar, counts);
293 Integer total = totalChars.get(avatar);
294 if(total == null) total = Integer.valueOf(0);
295 total += talk.getTotalChars();
296 totalChars.put(avatar, total);
298 lastTalk.put(avatar, talk);
300 avatarSet.add(avatar);
304 for(Avatar avatar : avatarSet){
305 Integer counts = talkCount.get(avatar);
306 Integer total = totalChars.get(avatar);
307 String lastTime = lastTalk.get(avatar).getAnchorNotation();
308 appendRow(avatar, counts, total, lastTime);
312 this.totalSum.setText("合計:" + sum + " 発言");
314 Village village = this.period.getVillage();
315 String villageName = village.getVillageName();
316 String periodCaption = this.period.getCaption();
317 this.caption.setText(villageName + "村 " + periodCaption);
324 * @param event {@inheritDoc}
327 public void windowActivated(WindowEvent event){
333 * @param event {@inheritDoc}
336 public void windowDeactivated(WindowEvent event){
342 * @param event {@inheritDoc}
345 public void windowIconified(WindowEvent event){
351 * @param event {@inheritDoc}
354 public void windowDeiconified(WindowEvent event){
360 * @param event {@inheritDoc}
363 public void windowOpened(WindowEvent event){
369 * ダイアログのクローズボタン押下処理を行う。
370 * @param event ウィンドウ変化イベント {@inheritDoc}
373 public void windowClosing(WindowEvent event){
380 * @param event {@inheritDoc}
383 public void windowClosed(WindowEvent event){
390 * @param event イベント {@inheritDoc}
393 public void actionPerformed(ActionEvent event){
394 if(event.getSource() != this.closeButton) return;
402 * @param event イベント {@inheritDoc}
405 public void itemStateChanged(ItemEvent event){
406 if(event.getStateChange() != ItemEvent.SELECTED) return;
408 Object selected = this.typeSelector.getSelectedItem();
409 if (selected == PUBTALK) this.talkFilter = TalkType.PUBLIC;
410 else if(selected == WOLFTALK) this.talkFilter = TalkType.WOLFONLY;
411 else if(selected == GRAVETALK) this.talkFilter = TalkType.GRAVE;
412 else if(selected == PRVTALK) this.talkFilter = TalkType.PRIVATE;
413 else if(selected == ALLTALK) this.talkFilter = null;
423 private void close(){
431 * Avatar 顔イメージ描画用カスタムセルレンダラ。
433 private class CustomRenderer extends DefaultTableCellRenderer{
438 public CustomRenderer(){
445 * セルに{@link Avatar}がきたら顔アイコンと名前を表示する。
446 * @param value {@inheritDoc}
449 public void setValue(Object value){
450 if(value instanceof Avatar){
451 Avatar avatar = (Avatar) value;
453 Village village = DaySummary.this.period.getVillage();
454 Image image = village.getAvatarFaceImage(avatar);
455 if(image == null) image = village.getGraveImage();
457 ImageIcon icon = new ImageIcon(image);
461 setText(avatar.getName());
463 Dimension prefSize = getPreferredSize();
465 int cellHeight = VERTICAL_GAP * 2 + prefSize.height;
466 if(DaySummary.this.tableComp.getRowHeight() < cellHeight){
467 DaySummary.this.tableComp.setRowHeight(cellHeight);
470 int cellWidth = HORIZONTAL_GAP * 2 + prefSize.width;
471 if( DaySummary.this.avatarColumn.getPreferredWidth()
473 DaySummary.this.avatarColumn.setPreferredWidth(cellWidth);
479 super.setValue(value);
487 * @param table {@inheritDoc}
488 * @param value {@inheritDoc}
489 * @param isSelected {@inheritDoc}
490 * @param hasFocus {@inheritDoc}
491 * @param row {@inheritDoc}
492 * @param column {@inheritDoc}
493 * @return {@inheritDoc}
496 public Component getTableCellRendererComponent(JTable table,
502 Component result = super.getTableCellRendererComponent(table,
509 Object selected = DaySummary.this.typeSelector.getSelectedItem();
510 Color bgColor = null;
511 if(selected == PUBTALK){
512 bgColor = TalkDraw.COLOR_PUBLIC;
513 }else if(selected == WOLFTALK){
514 bgColor = TalkDraw.COLOR_WOLFONLY;
515 }else if(selected == GRAVETALK){
516 bgColor = TalkDraw.COLOR_GRAVE;
517 }else if(selected == PRVTALK){
518 bgColor = TalkDraw.COLOR_PRIVATE;
519 }else if(selected == ALLTALK){
526 result.setForeground(Color.BLACK);
527 result.setBackground(bgColor);