package logbook.gui.background; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.TimeUnit; import logbook.config.AppConfig; import logbook.constants.AppConstants; import logbook.data.context.GlobalContext; import logbook.dto.DeckMissionDto; import logbook.dto.DockDto; import logbook.dto.NdockDto; import logbook.dto.ShipDto; import logbook.gui.ApplicationMain; import logbook.gui.logic.Sound; import logbook.gui.logic.TimeLogic; import logbook.gui.widgets.FleetComposite; import logbook.util.SwtUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.TaskItem; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.ToolTip; import org.eclipse.wb.swt.SWTResourceManager; /** * 非同期にメイン画面を更新します */ public final class AsyncExecApplicationMain extends Thread { private static final Logger LOG = LogManager.getLogger(AsyncExecApplicationMain.class); private static final int ONE_MINUTES = 60; private final ApplicationMain main; /** * 非同期にメイン画面を更新するスレッドのコンストラクター * * @param main メイン画面 */ public AsyncExecApplicationMain(ApplicationMain main) { this.main = main; this.setName("logbook_async_exec_application_main"); } /** * 現在のメイン画面を更新します */ @Override public void run() { try { while (true) { boolean update = GlobalContext.updateContext(); if (update) { // 保有アイテム数を更新する Display.getDefault().asyncExec(new UpdateItemCountTask(this.main)); // 保有艦娘数を更新する Display.getDefault().asyncExec(new UpdateShipCountTask(this.main)); // 艦隊タブを更新する Display.getDefault().asyncExec(new UpdateFleetTabTask(this.main)); } // 遠征と入渠を更新する Display.getDefault().asyncExec(new UpdateDeckNdockTask(this.main)); TimeUnit.SECONDS.sleep(1); } } catch (Exception e) { LOG.fatal("Thread is aborted", e); throw new RuntimeException(e); } } /** * 2つの日付から残り時間を計算する * * @param date1 * @param date2 * @return */ private static long getRest(Date date1, Date date2) { return TimeUnit.MILLISECONDS.toSeconds(date2.getTime() - date1.getTime()); } /** * 保有アイテム数を更新する */ private static final class UpdateItemCountTask implements Runnable { private final ApplicationMain main; /** * コンストラクター */ public UpdateItemCountTask(ApplicationMain main) { this.main = main; } @Override public void run() { Button itemList = this.main.getItemList(); String setText = "Equip(" + GlobalContext.getItemMap().size() + "/" + GlobalContext.maxSlotitem() + ")"; if (!setText.equals(itemList.getText())) { itemList.setText(setText); itemList.getParent().layout(); } } } /** * 保有艦娘数を更新する */ private static final class UpdateShipCountTask implements Runnable { private final ApplicationMain main; /** * コンストラクター */ public UpdateShipCountTask(ApplicationMain main) { this.main = main; } @Override public void run() { Button shipList = this.main.getShipList(); String setText = "Ships(" + GlobalContext.getShipMap().size() + "/" + GlobalContext.maxChara() + ")"; if (setText.equals(shipList.getText())) { return; } shipList.setText(setText); shipList.getParent().layout(); if (AppConfig.get().isUseTaskbarNotify()) { TaskItem item = SwtUtils.getTaskBarItem(this.main.getShell()); if (item != null) { int max = GlobalContext.maxChara(); int size = GlobalContext.getShipMap().size(); int locked = 0; for (Entry<Long, ShipDto> entry : GlobalContext.getShipMap().entrySet()) { if (entry.getValue().getLocked()) { locked++; } } int r = Math.round(((float) (size - locked) / (float) (max - locked)) * 100); item.setProgress(r); if (max <= (size + AppConfig.get().getNotifyFully())) { item.setProgressState(SWT.PAUSED); } else { item.setProgressState(SWT.NORMAL); } } } } } /** * 遠征と入渠を更新する */ private static final class UpdateDeckNdockTask implements Runnable { private static final Logger LOG = LogManager.getLogger(UpdateDeckNdockTask.class); private static final boolean[] FLAG_NOTICE_DECK = { false, false, false }; private static final boolean[] FLAG_NOTICE_NDOCK = { false, false, false, false }; private final ApplicationMain main; /** 日付フォーマット */ private final SimpleDateFormat format = new SimpleDateFormat(AppConstants.DATE_SHORT_FORMAT); /** * コンストラクター */ public UpdateDeckNdockTask(ApplicationMain main) { this.main = main; } @Override public void run() { // 現在時刻 Date now = Calendar.getInstance().getTime(); List<String> notice = new ArrayList<String>(); boolean visibleHome = false; // 遠征を更新する if (this.updateDeck(now, notice)) { Sound.randomExpeditionSoundPlay(); visibleHome |= AppConfig.get().isVisibleOnReturnMission(); } // 入渠を更新する if (this.updateNdock(now, notice)) { Sound.randomDockSoundPlay(); visibleHome |= AppConfig.get().isVisibleOnReturnBathwater(); } if (visibleHome) { this.main.getTabFolder().setSelection(0); } if (AppConfig.get().isUseBalloon()) { // バルーンツールチップを表示する try { // 遠征・入渠のお知らせ if (notice.size() > 0) { ToolTip tip = new ToolTip(this.main.getShell(), SWT.BALLOON | SWT.ICON_INFORMATION); tip.setText("Notification"); tip.setMessage(StringUtils.join(notice, "\r\n")); this.main.getTrayItem().setToolTip(tip); tip.setVisible(true); } } catch (Exception e) { LOG.warn("Failed to display the notification", e); } } } /** * 遠征を更新する * * @param now * @param notice * @return */ private boolean updateDeck(Date now, List<String> notice) { boolean noticeflg = false; Label[] deckNameLabels = { this.main.getDeck1name(), this.main.getDeck2name(), this.main.getDeck3name() }; Text[] deckTimeTexts = { this.main.getDeck1time(), this.main.getDeck2time(), this.main.getDeck3time() }; DeckMissionDto[] deckMissions = GlobalContext.getDeckMissions(); for (int i = 0; i < deckMissions.length; i++) { String time = ""; String dispname = ""; if (deckMissions[i].getMission() != null) { dispname = deckMissions[i].getName() + " (" + deckMissions[i].getMission() + ")"; if (deckMissions[i].getTime() != null) { long rest = getRest(now, deckMissions[i].getTime()); // ツールチップテキストで時刻を表示する deckTimeTexts[i].setToolTipText(this.format.format(deckMissions[i].getTime())); // 20分前、10分前、5分前になったら背景色を変更する if (rest <= (ONE_MINUTES * 5)) { deckTimeTexts[i].setBackground(SWTResourceManager .getColor(AppConstants.TIME_IN_5_MIN)); } else if (rest <= (ONE_MINUTES * 10)) { deckTimeTexts[i].setBackground(SWTResourceManager .getColor(AppConstants.TIME_IN_10_MIN)); } else if (rest <= (ONE_MINUTES * 20)) { deckTimeTexts[i].setBackground(SWTResourceManager .getColor(AppConstants.TIME_IN_20_MIN)); } else { deckTimeTexts[i].setBackground(SWTResourceManager.getColor(SWT.COLOR_WHITE)); } if (this.main.getDeckNotice().getSelection()) { if ((rest <= ONE_MINUTES) && !FLAG_NOTICE_DECK[i]) { notice.add(deckMissions[i].getName() + " has returned from " + deckMissions[i].getMission()); noticeflg = true; FLAG_NOTICE_DECK[i] = true; } else if (AppConfig.get().isMissionRemind() && (rest < -1) && ((rest % AppConfig.get().getRemindInterbal()) == 0)) { // 定期的にリマインドする notice.add(dispname + " will arrive soon."); noticeflg = true; } else if (rest > ONE_MINUTES) { FLAG_NOTICE_DECK[i] = false; } } else { FLAG_NOTICE_DECK[i] = false; } time = TimeLogic.toDateRestString(rest); if (time == null) { time = "Done"; } } } else { deckTimeTexts[i].setBackground(SWTResourceManager.getColor(SWT.COLOR_WHITE)); deckTimeTexts[i].setToolTipText(null); } deckNameLabels[i].setText(dispname); deckTimeTexts[i].setText(time); } return noticeflg; } /** * 入渠を更新する * * @param now * @param notice * @return */ private boolean updateNdock(Date now, List<String> notice) { boolean noticeflg = false; Map<Long, ShipDto> shipMap = GlobalContext.getShipMap(); Label[] ndockNameLabels = { this.main.getNdock1name(), this.main.getNdock2name(), this.main.getNdock3name(), this.main.getNdock4name() }; Text[] ndockTimeTexts = { this.main.getNdock1time(), this.main.getNdock2time(), this.main.getNdock3time(), this.main.getNdock4time() }; NdockDto[] ndocks = GlobalContext.getNdocks(); for (int i = 0; i < ndocks.length; i++) { String name = ""; String time = ""; if (ndocks[i].getNdockid() != 0) { ShipDto ship = shipMap.get(Long.valueOf(ndocks[i].getNdockid())); if (ship != null) { name = ship.getName() + " (Lv" + ship.getLv() + ")"; long rest = getRest(now, ndocks[i].getNdocktime()); // ツールチップテキストで時刻を表示する ndockTimeTexts[i].setToolTipText(this.format.format(ndocks[i].getNdocktime())); // 20分前、10分前、5分前になったら背景色を変更する if (rest <= (ONE_MINUTES * 5)) { ndockTimeTexts[i].setBackground(SWTResourceManager .getColor(AppConstants.TIME_IN_5_MIN)); } else if (rest <= (ONE_MINUTES * 10)) { ndockTimeTexts[i].setBackground(SWTResourceManager .getColor(AppConstants.TIME_IN_10_MIN)); } else if (rest <= (ONE_MINUTES * 20)) { ndockTimeTexts[i].setBackground(SWTResourceManager .getColor(AppConstants.TIME_IN_20_MIN)); } else { ndockTimeTexts[i].setBackground(SWTResourceManager .getColor(SWT.COLOR_WHITE)); } if (this.main.getNdockNotice().getSelection()) { if ((rest <= ONE_MINUTES) && !FLAG_NOTICE_NDOCK[i]) { notice.add("Repair Dock has finished repairing " + name); noticeflg = true; FLAG_NOTICE_NDOCK[i] = true; } else if (rest > ONE_MINUTES) { FLAG_NOTICE_NDOCK[i] = false; } } else { FLAG_NOTICE_NDOCK[i] = false; } time = TimeLogic.toDateRestString(rest); if (time == null) { time = "Done"; } } } else { ndockTimeTexts[i].setBackground(SWTResourceManager.getColor(SWT.COLOR_WHITE)); ndockTimeTexts[i].setToolTipText(null); } ndockNameLabels[i].setText(name); ndockTimeTexts[i].setText(time); } return noticeflg; } } /** * 艦隊タブを更新する */ private static final class UpdateFleetTabTask implements Runnable { private static String[] dockname = new String[4]; private static CTabItem[] tabItems = new CTabItem[4]; private static FleetComposite[] dockComposites = new FleetComposite[4]; private final ApplicationMain main; /** * コンストラクター */ public UpdateFleetTabTask(ApplicationMain main) { this.main = main; } @Override public void run() { // タブを更新する CTabItem maintab = this.main.getTabFolder().getItem(0); maintab.setToolTipText( "Equip:" + GlobalContext.getItemMap().size() + "/" + GlobalContext.maxSlotitem() + " Ships:" + GlobalContext.getShipMap().size() + "/" + GlobalContext.maxChara()); for (int i = 0; i < 4; i++) { DockDto dock = GlobalContext.getDock(Integer.toString(i + 1)); if (dock != null) { FleetComposite tabComposite = dockComposites[i]; CTabItem tabItem = tabItems[i]; if (tabItem == null) { tabItem = new CTabItem(this.main.getTabFolder(), SWT.NONE); tabItem.setText(dock.getName()); // メインコンポジット tabComposite = new FleetComposite(this.main.getTabFolder(), tabItem, this.main); tabItem.setControl(tabComposite); tabItems[i] = tabItem; dockComposites[i] = tabComposite; } if (!dock.getName().equals(dockname[i])) { dockname[i] = dock.getName(); } tabComposite.updateFleet(dock); tabItem.setText(dock.getName()); } } } } }