package logbook.gui; import java.util.ArrayList; import java.util.Calendar; import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Timer; import java.util.TreeMap; import logbook.config.AppConfig; import logbook.config.bean.TableConfigBean; import logbook.dto.BattleAggDetailsDto; import logbook.dto.BattleAggUnitDto; import logbook.dto.BattleResultDto; import logbook.dto.MapCellDto; import logbook.dto.ResultRank; import logbook.gui.listener.TreeKeyShortcutAdapter; import logbook.gui.listener.TreeToClipboardAdapter; import logbook.gui.logic.GuiUpdator; import logbook.internal.BattleAggDate; import logbook.internal.BattleAggUnit; import logbook.internal.BattleResultServer; import logbook.internal.LoggerHolder; import logbook.util.SwtUtils; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.swt.widgets.TreeItem; /** * 出撃統計 * */ public class BattleAggDialog extends WindowBase { /** ロガー */ private static final LoggerHolder LOG = new LoggerHolder(BattleAggDialog.class); /** ヘッダー */ private final String[] header = this.getTableHeader(); private final Shell parent; /** タイマー */ protected Timer timer; /** シェル */ private Shell shell; /** ツリーテーブル */ private Tree tree; /** ツリー要素マネージャ */ private AggTableItems treeItems; /** メニューバー */ private Menu menubar; /** [操作]メニュー */ private Menu opemenu; private Menu tablemenu; protected TableConfigBean config; protected MenuItem cyclicReloadMenuItem; protected boolean needsUpdate = true; /** * Create the dialog. * @param parent * @param menuItem */ public BattleAggDialog(Shell parent, MenuItem menuItem) { super(menuItem); this.parent = parent; } /** * Open the dialog. * @return the result */ @Override public void open() { // 初期化済みの場合 if (this.isWindowInitialized()) { // リロードして表示 this.reloadTable(); this.setVisible(true); return; } this.createContents(); this.setWindowInitialized(true); this.setVisible(true); } /** * Create contents of the dialog. */ private void createContents() { // シェルを作成 super.createContents(this.parent, SWT.SHELL_TRIM | SWT.MODELESS, true); this.shell = this.getShell(); this.shell.setText(this.getTitle()); this.shell.setLayout(new FillLayout(SWT.HORIZONTAL)); // メニューバー this.createMenubar(); this.menubar = this.getMenubar(); // ツリーテーブル this.tree = new Tree(this.shell, SWT.BORDER | SWT.FULL_SELECTION | SWT.VIRTUAL | SWT.MULTI); this.tree.addKeyListener(new TreeKeyShortcutAdapter(this.header, this.tree)); this.tree.setLinesVisible(true); this.tree.setHeaderVisible(true); this.treeItems = new AggTableItems(this.tree); // メニューバーのメニュー if (this.isNoMenubar()) { this.opemenu = this.menubar; } else { MenuItem operoot = new MenuItem(this.menubar, SWT.CASCADE); operoot.setText("操作"); this.opemenu = new Menu(operoot); operoot.setMenu(this.opemenu); } MenuItem reload = new MenuItem(this.opemenu, SWT.NONE); reload.setText("再読み込み(&R)\tF5"); reload.setAccelerator(SWT.F5); reload.addSelectionListener(new TableReloadAdapter()); // ウィンドウの基本メニューを設定 super.registerEvents(); // テーブル右クリックメニュー this.tablemenu = this.getPopupMenu(); this.tree.setMenu(this.tablemenu); MenuItem sendclipbord = new MenuItem(this.tablemenu, SWT.NONE); sendclipbord.addSelectionListener(new TreeToClipboardAdapter(this.header, this.tree)); sendclipbord.setText("クリップボードにコピー(&C)"); if (!this.isNoMenubar()) { MenuItem reloadtable = new MenuItem(this.tablemenu, SWT.NONE); reloadtable.setText("再読み込み(&R)"); reloadtable.addSelectionListener(new TableReloadAdapter()); } this.setTableHeader(); this.reloadTable(); // データの更新を受け取る final Runnable listener = new GuiUpdator(new Runnable() { @Override public void run() { BattleAggDialog.this.reloadTable(); } }); BattleResultServer.addListener(listener); this.shell.addListener(SWT.Dispose, new Listener() { @Override public void handleEvent(Event event) { BattleResultServer.removeListener(listener); } }); } /** * タイトルを返します * @return String */ protected String getTitle() { return "出撃統計"; } /** * ウィンドウサイズを保存・リストアするべきか? * @return */ @Override protected boolean shouldSaveWindowSize() { return true; } /** * ウィンドウのデフォルトサイズを取得 * @return Point */ @Override protected Point getDefaultSize() { return SwtUtils.DPIAwareSize(new Point(600, 350)); } protected TableConfigBean getConfig() { if (this.config == null) { this.config = AppConfig.get().getTableConfigMap().get(this.getWindowId()); if (this.config == null) { this.config = new TableConfigBean(); } } return this.config; } @Override public void save() { if (this.config != null) { this.config.setCyclicReload(this.cyclicReloadMenuItem.getSelection()); AppConfig.get().getTableConfigMap().put(this.getWindowId(), this.config); } super.save(); } /** * テーブルヘッダーをセットする */ private void setTableHeader() { for (int i = 0; i < this.header.length; i++) { TreeColumn col = new TreeColumn(this.tree, SWT.LEFT); col.setText(this.header[i]); } this.packTableHeader(); } /** * テーブルヘッダーの幅を調節する */ private void packTableHeader() { TreeColumn[] columns = this.tree.getColumns(); for (int i = 0; i < columns.length; i++) { columns[i].pack(); } } /** * テーブルヘッダーを返します * @return String[] */ private String[] getTableHeader() { return new String[] { "集計", "出撃合計", "勝利合計", "S勝利", "A勝利", "B勝利", "C敗北", "D以下" }; } /** * テーブルをリロードする */ private void reloadTable() { this.tree.setRedraw(false); TableItemReference[] selectedItems = this.treeItems.getSelection(); TableItemReference topItem = this.treeItems.getTopItem(); this.treeItems.disposeTree(); this.treeItems.setData(this.load()); this.treeItems.setSelectedItems(selectedItems); this.treeItems.setTopItem(topItem); this.packTableHeader(); this.tree.setRedraw(true); this.needsUpdate = false; } /** * 報告書を読み込み、集計結果を返す * @return 集計結果 */ private Map<BattleAggUnit, BattleAggUnitDto> load() { Map<BattleAggUnit, BattleAggUnitDto> aggMap = new EnumMap<>(BattleAggUnit.class); // 今日 Calendar today = BattleAggDate.TODAY.get(); // 先週 Calendar lastWeek = BattleAggDate.LAST_WEEK.get(); // 先月 Calendar lastMonth = BattleAggDate.LAST_MONTH.get(); // 読み込む最小の日付(>=) Calendar min = lastMonth; // 海戦・ドロップ報告書読み込み try { for (BattleResultDto dto : BattleResultServer.get().getList()) { Calendar date = BattleAggDate.fromDate(dto.getBattleDate()); MapCellDto mapCell = dto.getMapCell(); ResultRank rank = dto.getRank(); // 演習はスキップ if (mapCell == null) continue; // 読み込む最小の日付未満の場合は読み飛ばす if (min.compareTo(date) > 0) { continue; } // デイリー集計 this.agg(BattleAggUnit.DAILY, aggMap, today, Calendar.DAY_OF_YEAR, date, mapCell, rank); // ウィークリー集計 this.agg(BattleAggUnit.WEEKLY, aggMap, today, Calendar.WEEK_OF_YEAR, date, mapCell, rank); // マンスリー集計 this.agg(BattleAggUnit.MONTHLY, aggMap, today, Calendar.MONTH, date, mapCell, rank); // 先週の集計 this.agg(BattleAggUnit.LAST_WEEK, aggMap, lastWeek, Calendar.WEEK_OF_YEAR, date, mapCell, rank); // 先月の集計 this.agg(BattleAggUnit.LAST_MONTH, aggMap, lastMonth, Calendar.MONTH, date, mapCell, rank); } } catch (Exception e) { ApplicationMain.main.printMessage("出撃統計作成に失敗しました"); LOG.get().warn("出撃統計作成に失敗", e); } return aggMap; } /** * 集計する * * @param unit 集計単位(デイリーなど) * @param to 集計結果 * @param std 基準日 * @param field {@link Calendar#get(int)}のフィールド値 * @param target 集計対象の日付 * @param area 海域 * @param rank ランク * @param isStart 出撃 * @param isBoss ボス */ private void agg(BattleAggUnit unit, Map<BattleAggUnit, BattleAggUnitDto> to, Calendar std, int field, Calendar target, MapCellDto area, ResultRank rank) { int stdn = std.get(field); int tarn = target.get(field); //if (std.get(field) == target.get(field)) if (stdn == tarn) { BattleAggUnitDto aggUnit = to.get(unit); if (aggUnit == null) { aggUnit = new BattleAggUnitDto(); to.put(unit, aggUnit); } aggUnit.add(area, rank); } } /** * テーブルを再読み込みするリスナーです */ protected class TableReloadAdapter extends SelectionAdapter { @Override public void widgetSelected(SelectionEvent e) { BattleAggDialog.this.reloadTable(); } } /** * TreeItemはリロードすると消えてしまうので消えない対応データを持っておく */ protected class TableItemReference { AggTableItem aggItem; boolean boss; public TableItemReference(AggTableItem aggItem, boolean boss) { this.aggItem = aggItem; this.boss = boss; } } protected class AggTableItems { private final Map<BattleAggUnit, AggTableItem> childs = new TreeMap<>(); private final Tree root; public AggTableItems(Tree root) { this.root = root; } public void disposeTree() { // expand状態を記憶+TreeItemへの参照を削除 for (AggTableItem item : this.childs.values()) { item.storeAndReleaseItem(); } // テーブルをクリア this.root.removeAll(); } public void setData(Map<BattleAggUnit, BattleAggUnitDto> aggMap) { for (Entry<BattleAggUnit, BattleAggUnitDto> entry : aggMap.entrySet()) { AggTableItem aggItem = this.getChild(entry.getKey()); aggItem.setDto(this.root, entry.getKey().toString(), entry.getValue()); } } public TableItemReference[] getSelection() { List<TableItemReference> list = new ArrayList<>(); for (TreeItem item : this.root.getSelection()) { AggTableItem aggItem = (AggTableItem) item.getData(); list.add(aggItem.getItemReference(item)); } return list.toArray(new TableItemReference[list.size()]); } public void setSelectedItems(TableItemReference[] items) { List<TreeItem> list = new ArrayList<>(); for (TableItemReference item : items) { TreeItem treeItem = item.aggItem.getTreeItem(item.boss); if (treeItem != null) { list.add(treeItem); } } this.root.setSelection(list.toArray(new TreeItem[list.size()])); } public TableItemReference getTopItem() { TreeItem topItem = this.root.getTopItem(); if (topItem == null) return null; AggTableItem aggItem = (AggTableItem) topItem.getData(); return aggItem.getItemReference(topItem); } public void setTopItem(TableItemReference item) { if (item == null) return; TreeItem treeItem = item.aggItem.getTreeItem(item.boss); if (treeItem != null) { this.root.setTopItem(treeItem); } } private AggTableItem getChild(BattleAggUnit unit) { if (this.childs.containsKey(unit)) { return this.childs.get(unit); } AggTableItem child = new AggTableItem(); this.childs.put(unit, child); return child; } } protected class AggTableItem { private final Map<Integer, AggTableItem> childs = new TreeMap<>(); private TreeItem item; private TreeItem bossItem; private boolean expanded; public void setDto(Tree root, String key, BattleAggUnitDto dto) { this.item = new TreeItem(root, SWT.NONE); this.setDto(key, dto.getTotal()); for (Entry<Integer, BattleAggDetailsDto> areaEntry : dto.getAreaDetails()) { AggTableItem aggItem = this.getChild(areaEntry.getKey()); aggItem.setDto(this, areaEntry.getValue()); } } private void setDto(AggTableItem parent, BattleAggDetailsDto area) { this.item = new TreeItem(parent.item, SWT.NONE); this.setDto(area.getAreaName(), area); } private void setDto(String title, BattleAggDetailsDto area) { // メイン this.item.setText(new String[] { title, Integer.toString(area.getStart()), Integer.toString(area.getWin()), Integer.toString(area.getS()), Integer.toString(area.getA()), Integer.toString(area.getB()), Integer.toString(area.getC()), Integer.toString(area.getD()) }); this.item.setData(this); // ボス this.bossItem = new TreeItem(this.item, SWT.NONE); this.bossItem.setText(new String[] { "ボス", "-", Integer.toString(area.getBossWin()), Integer.toString(area.getBossS()), Integer.toString(area.getBossA()), Integer.toString(area.getBossB()), Integer.toString(area.getBossC()), Integer.toString(area.getBossD()) }); this.bossItem.setData(this); if (this.expanded) { this.item.setExpanded(true); } } private AggTableItem getChild(Integer areaId) { if (this.childs.containsKey(areaId)) { return this.childs.get(areaId); } AggTableItem child = new AggTableItem(); this.childs.put(areaId, child); return child; } public TableItemReference getItemReference(TreeItem item) { if (this.item == item) { return new TableItemReference(this, false); } else if (this.bossItem == item) { return new TableItemReference(this, true); } return null; } public TreeItem getTreeItem(boolean boss) { return boss ? this.bossItem : this.item; } public void storeAndReleaseItem() { for (AggTableItem item : this.childs.values()) { item.storeAndReleaseItem(); } if (this.item != null) { this.expanded = this.item.getExpanded(); this.item = this.bossItem = null; } } } }