package logbook.gui; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import logbook.config.ShipGroupConfig; import logbook.config.bean.ShipGroupBean; import logbook.config.bean.ShipGroupListBean; import logbook.constants.AppConstants; import logbook.data.Data; import logbook.data.DataType; import logbook.data.context.GlobalContext; import logbook.dto.DockDto; import logbook.dto.ShipDto; import logbook.gui.logic.ShipGroupListener; import logbook.gui.logic.ShipGroupObserver; import logbook.gui.logic.TableItemCreator; import logbook.gui.logic.TableRowHeader; import logbook.internal.CondTiming; import logbook.scripting.TableItemCreatorProxy; import logbook.util.ReportUtils; import logbook.util.SwtUtils; import logbook.util.SwtUtils.TableDragAndDropListener; import org.apache.commons.lang3.StringUtils; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; import org.eclipse.wb.swt.SWTResourceManager; /** * グループエディター * */ public final class ShipFilterGroupDialog extends AbstractTableDialog implements ShipGroupListener { private Text text; private SashForm sashForm; private Composite sideComposite; private Composite mainComposite; private Combo shipcombo; private Table groupTable; private Button btnAddShip; private Button btnRemoveShip; private final Map<String, ShipDto> shipmap = new HashMap<String, ShipDto>(); /** 現在表示しているグループ */ private GroupProperty property; /** * Create the dialog. * @param parent * @param menuItem */ public ShipFilterGroupDialog(Shell parent, MenuItem menuItem) { super(parent, menuItem); } /** * Create contents of the dialog. */ @Override protected void createContentsBefore() { GridLayout shellLayout = new GridLayout(1, false); shellLayout.verticalSpacing = 1; shellLayout.marginWidth = 1; shellLayout.marginHeight = 1; shellLayout.marginBottom = 1; shellLayout.horizontalSpacing = 1; this.shell.setLayout(shellLayout); this.sashForm = new SashForm(this.shell, SWT.SMOOTH); this.sashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); this.sideComposite = new Composite(this.sashForm, SWT.NONE); GridLayout sideLayout = new GridLayout(2, false); sideLayout.verticalSpacing = 1; sideLayout.marginWidth = 1; sideLayout.marginHeight = 1; sideLayout.marginBottom = 1; sideLayout.horizontalSpacing = 1; this.sideComposite.setLayout(sideLayout); Button btnAdd = new Button(this.sideComposite, SWT.NONE); btnAdd.addSelectionListener(new AddGroupAdapter(this)); btnAdd.setImage(SWTResourceManager.getImage(ShipFilterGroupDialog.class, AppConstants.R_ICON_ADD)); Button btnRemove = new Button(this.sideComposite, SWT.NONE); btnRemove.addSelectionListener(new RemoveGroupAdapter(this)); btnRemove.setImage(SWTResourceManager.getImage(ShipFilterGroupDialog.class, AppConstants.R_ICON_DELETE)); final Table groupTable = this.groupTable = new Table(this.sideComposite, SWT.BORDER); this.groupTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); this.groupTable.addSelectionListener(new GroupTableSelectionAdapter(this)); final TableColumn groupTableColumn = new TableColumn(this.groupTable, SWT.LEFT); groupTableColumn.setText("グループ"); groupTableColumn.setMoveable(false); groupTableColumn.setResizable(false); this.groupTable.setHeaderVisible(false); this.groupTable.addListener(SWT.Resize, new Listener() { @Override public void handleEvent(Event e) { groupTableColumn.setWidth(groupTable.getClientArea().width); } }); SwtUtils.addItemDragAndDropMoveSupport(new Table[] { this.groupTable }, new TableDragAndDropListener() { @Override public boolean canDragSource(TableItem source) { return true; } @Override public TableItem create(Table table, TableItem source, int index) { // 実際のリストを操作 List<ShipGroupBean> list = ShipGroupConfig.get().getGroup(); GroupProperty prop = (GroupProperty) source.getData(); int oldIndex = list.indexOf(prop.getShipGroupBean()); list.add(index, prop.getShipGroupBean()); list.remove(oldIndex < index ? oldIndex : oldIndex + 1); // テーブルアイテム作成 TableItem item = new TableItem(groupTable, SWT.NONE, index); prop.setTreeItem(item); item.setText(source.getText()); item.setData(prop); return item; } @Override public void finished(TableItem newItem) { ShipGroupObserver.listChanged(); } @Override public String tableItemToString(TableItem item) { return item.getText(); } }); ShipGroupListBean shipGroupList = ShipGroupConfig.get(); for (ShipGroupBean bean : shipGroupList.getGroup()) { TableItem groupItem = new TableItem(this.groupTable, SWT.NONE); //groupItem.setImage(SWTResourceManager // .getImage(ShipFilterGroupDialog.class, AppConstants.R_ICON_FOLDER)); groupItem.setText(bean.getName()); groupItem.setData(new GroupProperty(bean, groupItem)); } this.mainComposite = new Composite(this.sashForm, SWT.NONE); GridLayout mainLayout = new GridLayout(4, false); mainLayout.verticalSpacing = 1; mainLayout.marginWidth = 1; mainLayout.marginHeight = 1; mainLayout.marginBottom = 1; mainLayout.horizontalSpacing = 1; this.mainComposite.setLayout(mainLayout); this.text = new Text(this.mainComposite, SWT.BORDER); this.text.addModifyListener(new ModifyNameAdapter(this)); this.text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 4, 1)); this.text.setEnabled(false); } /** * Create contents of the dialog. */ @Override protected void createContents() { this.table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 4, 1)); this.shipcombo = new Combo(this.mainComposite, SWT.READ_ONLY); this.setShipComboData(); this.shipcombo.setEnabled(false); this.btnAddShip = new Button(this.mainComposite, SWT.NONE); this.btnAddShip.setText("追加"); this.btnAddShip.addSelectionListener(new AddShipAdapter(this)); this.btnAddShip.setEnabled(false); this.btnRemoveShip = new Button(this.mainComposite, SWT.NONE); this.btnRemoveShip.setText("除去"); this.btnRemoveShip.addSelectionListener(new RemoveShipAdapter(this)); this.btnRemoveShip.setEnabled(false); Button updateShip = new Button(this.mainComposite, SWT.NONE); updateShip.setText("更新"); updateShip.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { ShipFilterGroupDialog.this.setShipComboData(); } }); this.sashForm.setWeights(new int[] { 2, 5 }); new MenuItem(this.tablemenu, SWT.SEPARATOR); final MenuItem[] addFleetShipItems = new MenuItem[4]; for (int i = 1; i <= 4; ++i) { MenuItem addFleetMenu = addFleetShipItems[i - 1] = new MenuItem(this.tablemenu, SWT.NONE); addFleetMenu.setText("第" + i + "艦隊の艦娘を追加"); addFleetMenu.addSelectionListener(new AddFleetShipAdapter(this, Integer.toString(i))); } this.table.addListener(SWT.MenuDetect, new Listener() { @Override public void handleEvent(Event event) { for (int i = 1; i <= 4; ++i) { DockDto dto = GlobalContext.getDock(Integer.toString(i)); addFleetShipItems[i - 1].setEnabled( (ShipFilterGroupDialog.this.property != null) && (dto != null) && (dto.getShips().size() > 0)); } } }); SwtUtils.addItemDragAndDropMoveSupport(new Table[] { this.table }, new TableDragAndDropListener() { @Override public boolean canDragSource(TableItem source) { // ソート順なし かつ 1つのみ選択状態 return (ShipFilterGroupDialog.this.property != null) && (ShipFilterGroupDialog.this.config.getSortKeys()[0] == null) && (ShipFilterGroupDialog.this.table.getSelection().length == 1); } @Override public TableItem create(Table table, TableItem source, int index) { // 実際のリストを操作 if (ShipFilterGroupDialog.this.property != null) { Set<Integer> set = ShipFilterGroupDialog.this.property.getShipGroupBean().getShips(); ShipDto ship = (ShipDto) source.getData(); Integer[] origarray = set.toArray(new Integer[0]); set.clear(); for (int i = 0; i < origarray.length; ++i) { if (i == index) { set.add(ship.getId()); } if (origarray[i] != ship.getId()) { set.add(origarray[i]); } } set.add(ship.getId()); // 最後に追加する場合 } // reloadが走るのでここではダミーを返しておく return source; } @Override public void finished(TableItem newItem) { if (ShipFilterGroupDialog.this.property != null) { ShipGroupObserver.groupShipChanged(ShipFilterGroupDialog.this.property.getShipGroupBean()); } } @Override public String tableItemToString(TableItem item) { return ((ShipDto) item.getData()).getFullName(); } }); ShipGroupObserver.addListener(this); this.table.addListener(SWT.Dispose, new Listener() { @Override public void handleEvent(Event event) { ShipGroupObserver.removeListener(ShipFilterGroupDialog.this); } }); } @Override protected Composite getTableParent() { return this.mainComposite; } @Override protected String getTitleMain() { return "グループエディター"; } @Override protected Point getSize() { return SwtUtils.DPIAwareSize(new Point(600, 350)); } @Override protected String[] getTableHeader() { return new String[] { "", "ID", "艦隊", "名前", "艦種", "Lv", "疲労" }; } @Override protected void updateTableBody() { List<Comparable[]> body = new ArrayList<Comparable[]>(); CondTiming condTiming = GlobalContext.getCondTiming(); if (this.property != null) { List<ShipDto> shipList = this.property.getShipList(); for (int i = 0; i < shipList.size(); i++) { ShipDto ship = shipList.get(i); body.add(new Comparable[] { new TableRowHeader(i + 1, ship), ship.getId(), ship.getFleetid(), ship.getName(), ship.getType(), ship.getLv(), ship.getEstimatedCond(condTiming) }); } } this.body = body; } @Override protected TableItemCreator getTableItemCreator() { return TableItemCreatorProxy.get(AppConstants.SHIPGROUPTABLE_PREFIX); /* return new TableItemCreator() { @Override public void init() { } @Override public TableItem create(Table table, Comparable[] text, int count) { ShipDto ship = (ShipDto) ((TableRowHeader) text[0]).get(); TableItem item = new TableItem(table, SWT.NONE); // 偶数行に背景色を付ける if ((count % 2) != 0) { item.setBackground(SWTResourceManager.getColor(AppConstants.ROW_BACKGROUND)); } item.setBackground(6, CreateReportLogic.getTableCondColor(ship.getEstimatedCond())); item.setText(ReportUtils.toStringArray(text)); return item; } }; */ } @Override protected void reloadTable() { super.reloadTable(); // 名前・ボタンの状態をセットします if (this.property != null) { ShipGroupBean group = this.property.getShipGroupBean(); this.text.setText(group.getName()); this.text.setEnabled(true); this.shipcombo.setEnabled(true); this.btnAddShip.setEnabled(true); this.btnRemoveShip.setEnabled(true); } else { this.text.setText(""); this.text.setEnabled(false); this.shipcombo.setEnabled(false); this.btnAddShip.setEnabled(false); this.btnRemoveShip.setEnabled(false); } } /** * コンボボックスに艦娘をセットします * * @param combo */ private void setShipComboData() { // コンボボックスから全ての艦娘を削除 this.shipcombo.removeAll(); // 表示用文字列と艦娘の紐付けを削除 this.shipmap.clear(); // 艦娘IDの最大を取得してゼロ埋め長さを算出 int maxshipid = 0; for (ShipDto ship : GlobalContext.getShipMap().values()) { maxshipid = Math.max(ship.getId(), maxshipid); } int padlength = String.valueOf(maxshipid).length(); // 表示用文字列と艦娘の紐付けを追加 for (ShipDto ship : GlobalContext.getShipMap().values()) { this.shipmap.put(this.getShipLabel(ship, padlength), ship); } // 艦娘を経験値順でソート List<ShipDto> ships = new ArrayList<ShipDto>(this.shipmap.values()); Collections.sort(ships, new Comparator<ShipDto>() { @Override public int compare(ShipDto o1, ShipDto o2) { return Integer.compare(o2.getExp(), o1.getExp()); } }); // コンボボックスに追加 for (int i = 0; i < ships.size(); i++) { String key = this.getShipLabel(ships.get(i), padlength); this.shipcombo.add(key); } // コントロールを再配置 this.shipcombo.pack(); this.shipcombo.getParent().layout(); } /** * 艦娘のプルダウン表示用文字列を作成します * * @param ship * @param padlength * @return */ private String getShipLabel(ShipDto ship, int padlength) { return new StringBuilder().append(StringUtils.leftPad(String.valueOf(ship.getId()), padlength, '0')) .append(": ").append(ship.getName()).append(" (Lv").append(ship.getLv() + ")").toString(); } /** * グループを追加するときに呼び出されるアダプター * */ private static final class AddGroupAdapter extends SelectionAdapter { /** ダイアログ */ private final ShipFilterGroupDialog dialog; /** * コンストラクター */ public AddGroupAdapter(ShipFilterGroupDialog dialog) { this.dialog = dialog; } private ShipGroupBean createNewGroup(List<ShipGroupBean> list) { Set<Integer> idSet = new HashSet<Integer>(); for (ShipGroupBean shipGroup : list) { idSet.add(shipGroup.getId()); } Random rnd = new Random(System.currentTimeMillis()); // 一意のIDを生成する int newId = rnd.nextInt(); while (idSet.contains(newId) || (newId == 0)) newId = rnd.nextInt(); ShipGroupBean bean = new ShipGroupBean(); bean.setId(newId); bean.setName("新規グループ"); list.add(bean); return bean; } @Override public void widgetSelected(SelectionEvent e) { List<ShipGroupBean> shipGroupList = ShipGroupConfig.get().getGroup(); ShipGroupBean bean = this.createNewGroup(shipGroupList); TableItem item = new TableItem(this.dialog.groupTable, SWT.NONE); //item.setImage(SWTResourceManager.getImage(ShipFilterGroupDialog.class, AppConstants.R_ICON_FOLDER)); item.setText(bean.getName()); item.setData(new GroupProperty(bean, item)); ShipGroupObserver.listChanged(); } } /** * グループを除去するときに呼び出されるアダプター * */ private static final class RemoveGroupAdapter extends SelectionAdapter { /** ダイアログ */ private final ShipFilterGroupDialog dialog; /** * コンストラクター */ public RemoveGroupAdapter(ShipFilterGroupDialog dialog) { this.dialog = dialog; } @Override public void widgetSelected(SelectionEvent e) { if (this.dialog.property != null) { ShipGroupBean target = this.dialog.property.getShipGroupBean(); MessageBox box = new MessageBox(this.dialog.shell, SWT.YES | SWT.NO | SWT.ICON_QUESTION); box.setText("グループを除去"); box.setMessage("「" + target.getName() + "」を除去しますか?"); if (box.open() == SWT.YES) { List<ShipGroupBean> shipGroupList = ShipGroupConfig.get().getGroup(); for (int i = 0; i < shipGroupList.size(); i++) { if (shipGroupList.get(i) == target) { shipGroupList.remove(i); } } this.dialog.text.setText(""); this.dialog.property.getTreeItem().dispose(); this.dialog.property = null; ShipGroupObserver.listChanged(); } } } } /** * 名前を変更した時に呼び出されるアダプター * */ private static final class ModifyNameAdapter implements ModifyListener { /** ダイアログ */ private final ShipFilterGroupDialog dialog; /** * コンストラクター */ public ModifyNameAdapter(ShipFilterGroupDialog dialog) { this.dialog = dialog; } @Override public void modifyText(ModifyEvent e) { if (this.dialog.property != null) { ShipGroupBean group = this.dialog.property.getShipGroupBean(); String text = this.dialog.text.getText(); String groupName = group.getName(); if (!text.equals(groupName)) { // 変わった時だけ this.dialog.property.getTreeItem().setText(text); group.setName(text); ShipGroupObserver.groupNameChanged(group); } } } } /** * ツリーをクリックした時に呼び出されるアダプター * */ private static final class GroupTableSelectionAdapter extends SelectionAdapter { /** ダイアログ */ private final ShipFilterGroupDialog dialog; /** * コンストラクター */ public GroupTableSelectionAdapter(ShipFilterGroupDialog dialog) { this.dialog = dialog; } @Override public void widgetSelected(SelectionEvent e) { if (e.item != null) { Object data = e.item.getData(); if (data instanceof GroupProperty) { this.dialog.property = (GroupProperty) data; } else { this.dialog.property = null; } this.dialog.reloadTable(); } } } /** * 艦娘を追加するときに呼び出されるアダプター * */ private static final class AddShipAdapter extends SelectionAdapter { /** ダイアログ */ private final ShipFilterGroupDialog dialog; /** * コンストラクター */ public AddShipAdapter(ShipFilterGroupDialog dialog) { this.dialog = dialog; } @Override public void widgetSelected(SelectionEvent e) { if (this.dialog.property != null) { if (this.dialog.shipcombo.getSelectionIndex() >= 0) { ShipGroupBean target = this.dialog.property.getShipGroupBean(); ShipDto ship = this.dialog.shipmap.get(this.dialog.shipcombo.getItem(this.dialog.shipcombo .getSelectionIndex())); target.getShips().add(ship.getId()); ShipGroupObserver.groupShipChanged(target); } } } } /** * 艦娘を除去するときに呼び出されるアダプター * */ private static final class RemoveShipAdapter extends SelectionAdapter { /** ダイアログ */ private final ShipFilterGroupDialog dialog; /** * コンストラクター */ public RemoveShipAdapter(ShipFilterGroupDialog dialog) { this.dialog = dialog; } @Override public void widgetSelected(SelectionEvent e) { if (this.dialog.property != null) { ShipGroupBean bean = this.dialog.property.getShipGroupBean(); TableItem[] items = this.dialog.table.getSelection(); List<Integer> ids = new ArrayList<>(); List<String> name = new ArrayList<>(); for (TableItem tableItem : items) { ShipDto ship = (ShipDto) tableItem.getData(); ids.add(ship.getId()); name.add(ship.getFriendlyName()); } MessageBox box = new MessageBox(this.dialog.shell, SWT.YES | SWT.NO | SWT.ICON_QUESTION); box.setText("選択した艦娘をグループから除去"); box.setMessage("「" + StringUtils.join(name, ",") + "」をグループから除去しますか?"); if (box.open() == SWT.YES) { for (Integer id : ids) { bean.getShips().remove(id); } ShipGroupObserver.groupShipChanged(bean); } } } } /** * 艦娘を除去するときに呼び出されるアダプター * */ private static final class AddFleetShipAdapter extends SelectionAdapter { /** ダイアログ */ private final ShipFilterGroupDialog dialog; private final String fleetid; /** * コンストラクター */ public AddFleetShipAdapter(ShipFilterGroupDialog dialog, String fleetid) { this.dialog = dialog; this.fleetid = fleetid; } @Override public void widgetSelected(SelectionEvent e) { if (this.dialog.property != null) { ShipGroupBean bean = this.dialog.property.getShipGroupBean(); DockDto dto = GlobalContext.getDock(this.fleetid); if ((dto != null) && (dto.getShips().size() > 0)) { List<Integer> ids = new ArrayList<>(); List<String> name = new ArrayList<>(); for (ShipDto ship : dto.getShips()) { ids.add(ship.getId()); name.add(ship.getFriendlyName()); } MessageBox box = new MessageBox(this.dialog.shell, SWT.YES | SWT.NO | SWT.ICON_QUESTION); box.setText("選択した艦娘をグループに追加"); box.setMessage("「" + StringUtils.join(name, ",") + "」をグループに追加しますか?"); if (box.open() == SWT.YES) { for (Integer id : ids) { bean.getShips().add(id); } ShipGroupObserver.groupShipChanged(bean); } } } } } /** * テーブルに表示する所有艦娘のグループ * */ private static final class GroupProperty { /** 所有艦娘のグループ */ private final ShipGroupBean shipGroup; /** TreeItem */ private TableItem item; /** * コンストラクター * * @param shipGroup 所有艦娘のグループ * @param item TreeItem */ public GroupProperty(ShipGroupBean shipGroup, TableItem item) { this.shipGroup = shipGroup; this.item = item; } /** * 所有艦娘のグループを取得します * * @return 所有艦娘のグループ */ public ShipGroupBean getShipGroupBean() { return this.shipGroup; } /** * TreeItemを取得します * @return TreeItem */ public TableItem getTreeItem() { return this.item; } /** * @param item2 */ public void setTreeItem(TableItem item2) { this.item = item2; } /** * 艦娘のリストを取得します * * @return 艦娘のリスト */ public List<ShipDto> getShipList() { List<ShipDto> ships = new ArrayList<ShipDto>(); Map<Integer, ShipDto> shipMap = GlobalContext.getShipMap(); for (int id : this.shipGroup.getShips()) { ShipDto ship = shipMap.get(id); if (ship != null) { ships.add(ship); } } return ships; } } @Override public void listChanged() { // 自分で変更しているので何もしない } @Override public void groupNameChanged(ShipGroupBean group) { // 自分で変更しているので何もしない } @Override public void groupShipChanged(ShipGroupBean group) { if ((this.property != null) && (this.property.getShipGroupBean() == group)) { this.reloadTable(); } } /** * 更新する必要のあるデータ */ @Override public void update(DataType type, Data data) { if (ReportUtils.isShipUpdate(type)) { this.needsUpdate = true; } } }