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.sfjp.jindolf.view.AvatarPics;
54 import jp.sourceforge.jindolf.corelib.TalkType;
59 @SuppressWarnings("serial")
60 public class DaySummary extends JDialog
61 implements WindowListener, ActionListener, ItemListener{
63 private static final NumberFormat AVERAGE_FORM;
64 private static final String PUBTALK = "白発言";
65 private static final String WOLFTALK = "赤発言";
66 private static final String GRAVETALK = "青発言";
67 private static final String PRVTALK = "灰発言";
68 private static final String ALLTALK = "全発言";
69 private static final int HORIZONTAL_GAP = 5;
70 private static final int VERTICAL_GAP = 1;
71 private static final Color COLOR_ALL = new Color(0xffff80);
74 AVERAGE_FORM = NumberFormat.getInstance();
75 AVERAGE_FORM.setMaximumFractionDigits(1);
76 AVERAGE_FORM.setMinimumFractionDigits(1);
80 private final DefaultTableModel tableModel;
81 private final TableColumn avatarColumn;
83 private final JTable tableComp;
84 private final JComboBox<String> typeSelector = new JComboBox<>();
85 private final JButton closeButton = new JButton("閉じる");
86 private final JLabel caption = new JLabel();
87 private final JLabel totalSum = new JLabel();
89 private TalkType talkFilter;
90 private Period period;
95 * 集計結果を表示するモーダルダイアログを生成する。
98 public DaySummary(Frame owner){
102 GUIUtils.modifyWindowAttributes(this, true, false, true);
104 setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
105 addWindowListener(this);
107 this.tableModel = createInitModel();
108 this.tableComp = new JTable();
109 this.tableComp.setModel(this.tableModel);
110 this.tableComp.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
111 this.tableComp.setIntercellSpacing(
112 new Dimension(HORIZONTAL_GAP, VERTICAL_GAP) );
113 this.tableComp.setDefaultEditor(Object.class, null);
114 this.tableComp.setDefaultRenderer(Object.class, new CustomRenderer());
115 this.tableComp.setShowGrid(true);
117 TableColumnModel tcolModel = this.tableComp.getColumnModel();
119 this.avatarColumn = tcolModel.getColumn(0);
121 DefaultTableCellRenderer renderer;
123 renderer = new DefaultTableCellRenderer();
124 renderer.setHorizontalAlignment(SwingConstants.RIGHT);
125 tcolModel.getColumn(1).setCellRenderer(renderer);
127 renderer = new DefaultTableCellRenderer();
128 renderer.setHorizontalAlignment(SwingConstants.RIGHT);
129 tcolModel.getColumn(2).setCellRenderer(renderer);
131 renderer = new DefaultTableCellRenderer();
132 renderer.setHorizontalAlignment(SwingConstants.RIGHT);
133 tcolModel.getColumn(3).setCellRenderer(renderer);
135 this.typeSelector.addItem(PUBTALK);
136 this.typeSelector.addItem(WOLFTALK);
137 this.typeSelector.addItem(GRAVETALK);
138 this.typeSelector.addItem(PRVTALK);
139 this.typeSelector.addItem(ALLTALK);
141 this.closeButton.addActionListener(this);
142 this.typeSelector.addItemListener(this);
144 this.typeSelector.setSelectedItem(null);
145 this.typeSelector.setSelectedItem(PUBTALK);
159 private static DefaultTableModel createInitModel(){
160 DefaultTableModel result;
161 result = new DefaultTableModel();
163 Object[] rowHeads = {"名前", "発言回数", "平均文字列長", "最終発言"};
164 result.setColumnCount(rowHeads.length);
165 result.setColumnIdentifiers(rowHeads);
174 private void clearModel(){
175 int rows = this.tableModel.getRowCount();
176 for(int ct = 1; ct <= rows; ct++){
177 this.tableModel.removeRow(0);
184 * @param talkCount 発言回数
185 * @param totalChars 発言文字総数
186 * @param lastTime 最終発言時刻
188 private void appendRow(Avatar avatar,
192 String talks = talkCount + " 回";
198 average = (double) totalChars / (double) talkCount;
200 String chars = AVERAGE_FORM.format(average) + " 文字";
202 Object[] row = {avatar, talks, chars, lastTime};
203 int rowIndex = this.tableModel.getRowCount();
205 this.tableModel.insertRow(rowIndex, row);
213 private void design(){
214 Container content = getContentPane();
216 LayoutManager layout = new GridBagLayout();
217 GridBagConstraints constraints = new GridBagConstraints();
218 content.setLayout(layout);
220 constraints.insets = new Insets(5, 5, 5, 5);
222 constraints.gridwidth = 1;
223 constraints.fill = GridBagConstraints.NONE;
224 constraints.anchor = GridBagConstraints.WEST;
225 content.add(this.caption, constraints);
227 constraints.gridwidth = GridBagConstraints.REMAINDER;
228 constraints.fill = GridBagConstraints.NONE;
229 content.add(this.typeSelector, constraints);
231 JScrollPane scroller = new JScrollPane(this.tableComp);
232 constraints.weightx = 1.0;
233 constraints.weighty = 1.0;
234 constraints.fill = GridBagConstraints.BOTH;
235 content.add(scroller, constraints);
237 constraints.weightx = 0.0;
238 constraints.weighty = 0.0;
239 constraints.fill = GridBagConstraints.NONE;
240 constraints.anchor = GridBagConstraints.WEST;
241 content.add(this.totalSum, constraints);
243 constraints.weightx = 1.0;
244 constraints.weighty = 0.0;
245 constraints.fill = GridBagConstraints.HORIZONTAL;
246 content.add(new JSeparator(), constraints);
248 constraints.weightx = 0.0;
249 constraints.weighty = 0.0;
250 constraints.fill = GridBagConstraints.NONE;
251 constraints.anchor = GridBagConstraints.EAST;
252 content.add(this.closeButton, constraints);
258 * 与えられたPeriodで集計を更新する。
261 public void summaryPeriod(Period newPeriod){
262 this.period = newPeriod;
269 private void summaryPeriod(){
272 if(this.period == null) return;
274 SortedSet<Avatar> avatarSet = new TreeSet<>();
275 Map<Avatar, Integer> talkCount = new HashMap<>();
276 Map<Avatar, Integer> totalChars = new HashMap<>();
277 Map<Avatar, Talk> lastTalk = new HashMap<>();
279 List<Topic> topicList = this.period.getTopicList();
280 for(Topic topic : topicList){
281 if( ! (topic instanceof Talk)) continue;
282 Talk talk = (Talk) topic;
283 if(talk.getTalkCount() <= 0) continue;
284 if( this.talkFilter != null
285 && talk.getTalkType() != this.talkFilter) continue;
287 Avatar avatar = talk.getAvatar();
289 Integer counts = talkCount.get(avatar);
290 if(counts == null) counts = Integer.valueOf(0);
292 talkCount.put(avatar, counts);
294 Integer total = totalChars.get(avatar);
295 if(total == null) total = Integer.valueOf(0);
296 total += talk.getTotalChars();
297 totalChars.put(avatar, total);
299 lastTalk.put(avatar, talk);
301 avatarSet.add(avatar);
305 for(Avatar avatar : avatarSet){
306 Integer counts = talkCount.get(avatar);
307 Integer total = totalChars.get(avatar);
308 String lastTime = lastTalk.get(avatar).getAnchorNotation();
309 appendRow(avatar, counts, total, lastTime);
313 this.totalSum.setText("合計:" + sum + " 発言");
315 Village village = this.period.getVillage();
316 String villageName = village.getVillageName();
317 String periodCaption = this.period.getCaption();
318 this.caption.setText(villageName + "村 " + periodCaption);
325 * @param event {@inheritDoc}
328 public void windowActivated(WindowEvent event){
334 * @param event {@inheritDoc}
337 public void windowDeactivated(WindowEvent event){
343 * @param event {@inheritDoc}
346 public void windowIconified(WindowEvent event){
352 * @param event {@inheritDoc}
355 public void windowDeiconified(WindowEvent event){
361 * @param event {@inheritDoc}
364 public void windowOpened(WindowEvent event){
370 * ダイアログのクローズボタン押下処理を行う。
371 * @param event ウィンドウ変化イベント {@inheritDoc}
374 public void windowClosing(WindowEvent event){
381 * @param event {@inheritDoc}
384 public void windowClosed(WindowEvent event){
391 * @param event イベント {@inheritDoc}
394 public void actionPerformed(ActionEvent event){
395 if(event.getSource() != this.closeButton) return;
403 * @param event イベント {@inheritDoc}
406 public void itemStateChanged(ItemEvent event){
407 if(event.getStateChange() != ItemEvent.SELECTED) return;
409 Object selected = this.typeSelector.getSelectedItem();
410 if (selected == PUBTALK) this.talkFilter = TalkType.PUBLIC;
411 else if(selected == WOLFTALK) this.talkFilter = TalkType.WOLFONLY;
412 else if(selected == GRAVETALK) this.talkFilter = TalkType.GRAVE;
413 else if(selected == PRVTALK) this.talkFilter = TalkType.PRIVATE;
414 else if(selected == ALLTALK) this.talkFilter = null;
424 private void close(){
432 * Avatar 顔イメージ描画用カスタムセルレンダラ。
434 private class CustomRenderer extends DefaultTableCellRenderer{
439 public CustomRenderer(){
446 * セルに{@link Avatar}がきたら顔アイコンと名前を表示する。
447 * @param value {@inheritDoc}
450 public void setValue(Object value){
451 if(value instanceof Avatar){
452 Avatar avatar = (Avatar) value;
454 Village village = DaySummary.this.period.getVillage();
455 AvatarPics avatarPics = village.getAvatarPics();
456 Image image = avatarPics.getAvatarFaceImage(avatar);
457 if(image == null) image = avatarPics.getGraveImage();
459 ImageIcon icon = new ImageIcon(image);
463 setText(avatar.getName());
465 Dimension prefSize = getPreferredSize();
467 int cellHeight = VERTICAL_GAP * 2 + prefSize.height;
468 if(DaySummary.this.tableComp.getRowHeight() < cellHeight){
469 DaySummary.this.tableComp.setRowHeight(cellHeight);
472 int cellWidth = HORIZONTAL_GAP * 2 + prefSize.width;
473 if( DaySummary.this.avatarColumn.getPreferredWidth()
475 DaySummary.this.avatarColumn.setPreferredWidth(cellWidth);
481 super.setValue(value);
489 * @param table {@inheritDoc}
490 * @param value {@inheritDoc}
491 * @param isSelected {@inheritDoc}
492 * @param hasFocus {@inheritDoc}
493 * @param row {@inheritDoc}
494 * @param column {@inheritDoc}
495 * @return {@inheritDoc}
498 public Component getTableCellRendererComponent(JTable table,
504 Component result = super.getTableCellRendererComponent(table,
511 Object selected = DaySummary.this.typeSelector.getSelectedItem();
512 Color bgColor = null;
513 if(selected == PUBTALK){
514 bgColor = TalkDraw.COLOR_PUBLIC;
515 }else if(selected == WOLFTALK){
516 bgColor = TalkDraw.COLOR_WOLFONLY;
517 }else if(selected == GRAVETALK){
518 bgColor = TalkDraw.COLOR_GRAVE;
519 }else if(selected == PRVTALK){
520 bgColor = TalkDraw.COLOR_PRIVATE;
521 }else if(selected == ALLTALK){
528 result.setForeground(Color.BLACK);
529 result.setBackground(bgColor);