OSDN Git Service

タイマーと終了時刻の切り替えをわかりやすくする
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / MainForm.cs
index d2525e7..be88edc 100644 (file)
@@ -24,9 +24,16 @@ using System.Net;
 using System.Runtime.InteropServices;\r
 using System.Text;\r
 using System.Text.RegularExpressions;\r
+using System.Threading;\r
 using System.Threading.Tasks;\r
 using System.Windows.Forms;\r
+using KancolleSniffer.Log;\r
+using KancolleSniffer.Model;\r
+using KancolleSniffer.Net;\r
+using KancolleSniffer.Util;\r
+using KancolleSniffer.View;\r
 using Microsoft.CSharp.RuntimeBinder;\r
+using Timer = System.Windows.Forms.Timer;\r
 using static System.Math;\r
 \r
 namespace KancolleSniffer\r
@@ -51,7 +58,6 @@ namespace KancolleSniffer
         private string _debugLogFile;\r
         private IEnumerator<string> _playLog;\r
         private DateTime _prev, _now;\r
-        private bool _inSortie;\r
 \r
         private readonly ErrorDialog _errorDialog = new ErrorDialog();\r
         private readonly ErrorLog _errorLog;\r
@@ -115,7 +121,6 @@ namespace KancolleSniffer
         {\r
             var target = "";\r
             _sniffer.LoadState();\r
-            DataLoader.LoadTpSpec();\r
             _watcher.SynchronizingObject = this;\r
             _watcherTimer.Tick += (sender, ev) =>\r
             {\r
@@ -126,7 +131,7 @@ namespace KancolleSniffer
                         _sniffer.LoadState();\r
                         break;\r
                     case "TP.csv":\r
-                        DataLoader.LoadTpSpec();\r
+                        _sniffer.AdditionalData.LoadTpSpec();\r
                         break;\r
                 }\r
             };\r
@@ -159,18 +164,11 @@ namespace KancolleSniffer
 \r
             public void Stop(string key, int fleet) => _manager.StopRepeat(key, fleet);\r
 \r
-            public void Suspend() => _manager.SuspendRepeat();\r
+            public void Suspend(string exception = null) => _manager.SuspendRepeat(exception);\r
 \r
             public void Resume() => _manager.ResumeRepeat();\r
         }\r
 \r
-        public class ConfigFileException : Exception\r
-        {\r
-            public ConfigFileException(string message, Exception innerException) : base(message, innerException)\r
-            {\r
-            }\r
-        }\r
-\r
         private void HttpProxy_AfterSessionComplete(HttpProxy.Session session)\r
         {\r
             BeginInvoke(new Action<HttpProxy.Session>(ProcessRequest), session);\r
@@ -188,7 +186,7 @@ namespace KancolleSniffer
                 WriteDebugLog(url, request, response);\r
                 return;\r
             }\r
-            response = UnescapeString(response.Remove(0, "svdata=".Length));\r
+            response = UnEscapeString(response.Remove(0, "svdata=".Length));\r
             WriteDebugLog(url, request, response);\r
             ProcessRequestMain(url, request, response);\r
         }\r
@@ -243,7 +241,7 @@ namespace KancolleSniffer
             }\r
         }\r
 \r
-        private string UnescapeString(string s)\r
+        private string UnEscapeString(string s)\r
         {\r
             try\r
             {\r
@@ -284,6 +282,8 @@ namespace KancolleSniffer
                 UpdateShipInfo();\r
             if ((update & Sniffer.Update.Battle) != 0)\r
                 UpdateBattleInfo();\r
+            if ((update & Sniffer.Update.Cell) != 0)\r
+                UpdateCellInfo();\r
         }\r
 \r
         private void MainForm_Load(object sender, EventArgs e)\r
@@ -429,9 +429,9 @@ namespace KancolleSniffer
         private void ApplyConfig()\r
         {\r
             _listForm.TopMost = TopMost = _config.TopMost;\r
-            _sniffer.Item.MarginShips = _config.MarginShips;\r
+            _sniffer.ShipCounter.Margin = _config.MarginShips;\r
             UpdateNumOfShips();\r
-            _sniffer.Item.MarginEquips = _config.MarginEquips;\r
+            _sniffer.ItemCounter.Margin = _config.MarginEquips;\r
             UpdateNumOfEquips();\r
             _sniffer.Achievement.ResetHours = _config.ResetHours;\r
             labelAkashiRepair.Visible = labelAkashiRepairTimer.Visible =\r
@@ -451,7 +451,7 @@ namespace KancolleSniffer
         public void ApplyLogSetting()\r
         {\r
             LogServer.OutputDir = _config.Log.OutputDir;\r
-            LogServer.MaterialHistory = _sniffer.Material.MaterialHistory;\r
+            LogServer.LogProcessor = new LogProcessor(_sniffer.Material.MaterialHistory, _sniffer.MapDictionary);\r
             _sniffer.EnableLog(_config.Log.On ? LogType.All : LogType.None);\r
             _sniffer.MaterialLogInterval = _config.Log.MaterialLogInterval;\r
             _sniffer.LogOutputDir = _config.Log.OutputDir;\r
@@ -519,10 +519,11 @@ namespace KancolleSniffer
             if (!_listForm.Visible)\r
                 return;\r
             var idx = (int)((Control)sender).Tag;\r
-            var ships = _sniffer.Fleets[_currentFleet].Ships;\r
-            if (ships.Length <= idx)\r
-                return;\r
-            _listForm.ShowShip(ships[idx].Id);\r
+            var ship = (_combinedFleet\r
+                ? _sniffer.Fleets[0].Ships.Concat(_sniffer.Fleets[1].Ships).ToArray()\r
+                : _sniffer.Fleets[_currentFleet].Ships)[idx];\r
+            if (!ship.Empty)\r
+                _listForm.ShowShip(ship.Id);\r
         }\r
 \r
         private void UpdateItemInfo()\r
@@ -539,34 +540,34 @@ namespace KancolleSniffer
             _toolTip.SetToolTip(labelAchievement,\r
                 "今月 " + _sniffer.Achievement.ValueOfMonth.ToString("F1") + "\n" +\r
                 "EO " + _sniffer.ExMap.Achievement);\r
-            UpdateMaterialHistry();\r
+            UpdateMaterialHistory();\r
             if (_listForm.Visible)\r
                 _listForm.UpdateList();\r
         }\r
 \r
         private void UpdateNumOfShips()\r
         {\r
-            var item = _sniffer.Item;\r
-            labelNumOfShips.Text = $"{item.NowShips:D}/{item.MaxShips:D}";\r
-            labelNumOfShips.ForeColor = item.TooManyShips ? CUDColor.Red : Color.Black;\r
-            if (item.AlarmShips)\r
+            var ship = _sniffer.ShipCounter;\r
+            labelNumOfShips.Text = $"{ship.Now:D}/{ship.Max:D}";\r
+            labelNumOfShips.ForeColor = ship.TooMany ? CUDColors.Red : Color.Black;\r
+            if (ship.Alarm)\r
             {\r
-                var message = $"残り{_sniffer.Item.MaxShips - _sniffer.Item.NowShips:D}隻";\r
+                var message = $"残り{ship.Rest:D}隻";\r
                 _notificationManager.Enqueue("艦娘数超過", message);\r
-                item.AlarmShips = false;\r
+                ship.Alarm = false;\r
             }\r
         }\r
 \r
         private void UpdateNumOfEquips()\r
         {\r
-            var item = _sniffer.Item;\r
-            labelNumOfEquips.Text = $"{item.NowEquips:D}/{item.MaxEquips:D}";\r
-            labelNumOfEquips.ForeColor = item.TooManyEquips ? CUDColor.Red : Color.Black;\r
-            if (item.AlarmEquips)\r
+            var item = _sniffer.ItemCounter;\r
+            labelNumOfEquips.Text = $"{item.Now:D}/{item.Max:D}";\r
+            labelNumOfEquips.ForeColor = item.TooMany ? CUDColors.Red : Color.Black;\r
+            if (item.Alarm)\r
             {\r
-                var message = $"残り{_sniffer.Item.MaxEquips - _sniffer.Item.NowEquips:D}個";\r
+                var message = $"残り{item.Rest:D}個";\r
                 _notificationManager.Enqueue("装備数超過", message);\r
-                item.AlarmEquips = false;\r
+                item.Alarm = false;\r
             }\r
         }\r
 \r
@@ -578,7 +579,7 @@ namespace KancolleSniffer
             labelBucketHistory.Text = $"{day:+#;-#;±0} 今日\n{week:+#;-#;±0} 今週";\r
         }\r
 \r
-        private void UpdateMaterialHistry()\r
+        private void UpdateMaterialHistory()\r
         {\r
             var labels = new[] {labelFuelHistory, labelBulletHistory, labelSteelHistory, labelBouxiteHistory};\r
             var text = new[] {"燃料", "弾薬", "鋼材", "ボーキ"};\r
@@ -604,14 +605,18 @@ namespace KancolleSniffer
         private void UpdateShipInfo()\r
         {\r
             SetCurrentFleet();\r
+            SetCombined();\r
             UpdatePanelShipInfo();\r
             NotifyDamagedShip();\r
             UpdateChargeInfo();\r
             UpdateRepairList();\r
+            UpdateMissionLabels();\r
             if (_listForm.Visible)\r
                 _listForm.UpdateList();\r
         }\r
 \r
+        private bool _inSortie;\r
+\r
         private void SetCurrentFleet()\r
         {\r
             var states = _sniffer.Fleets.Select(fleet => fleet.State).ToArray();\r
@@ -634,18 +639,30 @@ namespace KancolleSniffer
             }\r
         }\r
 \r
+        private bool _prevCombined;\r
+\r
+        private void SetCombined()\r
+        {\r
+            if (_sniffer.IsCombinedFleet && !_prevCombined)\r
+            {\r
+                _combinedFleet = true;\r
+                _currentFleet = 0;\r
+            }\r
+            _prevCombined = _sniffer.IsCombinedFleet;\r
+        }\r
+\r
         private void UpdatePanelShipInfo()\r
         {\r
             var fleets = _sniffer.Fleets;\r
-            var ships = fleets[_currentFleet].Ships;\r
-            panel7Ships.Visible = ships.Length == 7;\r
+            var ships = fleets[_currentFleet].ActualShips;\r
+            panel7Ships.Visible = ships.Count == 7;\r
             _mainLabels.SetShipLabels(ships);\r
-            if (_sniffer.CombinedFleetType == 0)\r
+            if (!_sniffer.IsCombinedFleet)\r
                 _combinedFleet = false;\r
-            labelFleet1.Text = _combinedFleet ? "連合" : "第一";\r
+            labelFleet1.Text = _combinedFleet ? CombinedName : "第一";\r
             panelCombinedFleet.Visible = _combinedFleet;\r
             if (_combinedFleet)\r
-                _mainLabels.SetCombinedShipLabels(fleets[0].Ships, fleets[1].Ships);\r
+                _mainLabels.SetCombinedShipLabels(fleets[0].ActualShips, fleets[1].ActualShips);\r
             for (var i = 0; i < _labelCheckFleets.Length; i++)\r
                 _labelCheckFleets[i].Visible = _currentFleet == i;\r
             UpdateAkashiTimer();\r
@@ -655,11 +672,30 @@ namespace KancolleSniffer
             UpdateCondTimers();\r
         }\r
 \r
+        private string CombinedName\r
+        {\r
+            get\r
+            {\r
+                switch (_sniffer.Fleets[0].CombinedType)\r
+                {\r
+                    case CombinedType.Carrier:\r
+                        return "機動";\r
+                    case CombinedType.Surface:\r
+                        return "水上";\r
+                    case CombinedType.Transport:\r
+                        return "輸送";\r
+                    default:\r
+                        return "連合";\r
+                }\r
+            }\r
+        }\r
+\r
         private void NotifyDamagedShip()\r
         {\r
             if (!_sniffer.BadlyDamagedShips.Any())\r
                 return;\r
-            _notificationManager.Enqueue("大破警告", string.Join(" ", _sniffer.BadlyDamagedShips));\r
+            _notificationManager.StopRepeat("大破警告");\r
+            SetNotification("大破警告", string.Join(" ", _sniffer.BadlyDamagedShips));\r
             _notificationManager.Flash();\r
         }\r
 \r
@@ -698,6 +734,7 @@ namespace KancolleSniffer
         {\r
             ResetBattleInfo();\r
             _listForm.UpdateBattleResult();\r
+            _listForm.UpdateAirBattleResult();\r
             if (_sniffer.Battle.BattleState == BattleState.None)\r
                 return;\r
             panelBattleInfo.BringToFront();\r
@@ -706,8 +743,11 @@ namespace KancolleSniffer
             UpdateBattleFighterPower();\r
             if ((_config.Spoilers & Spoiler.ResultRank) != 0)\r
                 ShowResultRank();\r
-            if (_sniffer.Battle.BattleState == BattleState.Day)\r
-                _listForm.UpdateAirBattleResult();\r
+        }\r
+\r
+        private void UpdateCellInfo()\r
+        {\r
+            _listForm.UpdateCellInfo();\r
         }\r
 \r
         private void ResetBattleInfo()\r
@@ -730,9 +770,14 @@ namespace KancolleSniffer
                 _toolTip.SetToolTip(labelEnemyFighterPower, text);\r
                 _toolTip.SetToolTip(labelEnemyFighterPowerCaption, text);\r
             }\r
-            UpdateFighterPower(_sniffer.CombinedFleetType > 0 && battle.EnemyIsCombined);\r
+            else\r
+            {\r
+                _toolTip.SetToolTip(labelEnemyFighterPower, "");\r
+                _toolTip.SetToolTip(labelEnemyFighterPowerCaption, "");\r
+            }\r
+            UpdateFighterPower(_sniffer.IsCombinedFleet && battle.EnemyIsCombined);\r
             labelFighterPower.ForeColor = new[]\r
-                {DefaultForeColor, DefaultForeColor, CUDColor.Blue, CUDColor.Green, CUDColor.Orange, CUDColor.Red}[\r
+                {DefaultForeColor, DefaultForeColor, CUDColors.Blue, CUDColors.Green, CUDColors.Orange, CUDColors.Red}[\r
                 battle.AirControlLevel + 1];\r
         }\r
 \r
@@ -763,26 +808,65 @@ namespace KancolleSniffer
         private void UpdateNDocLabels()\r
         {\r
             _mainLabels.SetNDockLabels(_sniffer.NDock);\r
+            SetNDockLabel();\r
         }\r
 \r
+        private void SetNDockLabel()\r
+        {\r
+            labelNDock.Text = (_config.ShowEndTime & TimerKind.NDock) != 0 ? "入渠終了" : "入渠";\r
+        }\r
 \r
         private void labelNDock_Click(object sender, EventArgs e)\r
         {\r
             _config.ShowEndTime ^= TimerKind.NDock;\r
+            SetNDockLabel();\r
             UpdateTimers();\r
         }\r
 \r
         private void UpdateMissionLabels()\r
         {\r
-            foreach (var entry in\r
-                new[] {labelMissionName1, labelMissionName2, labelMissionName3}.Zip(_sniffer.Missions,\r
-                    (label, mission) => new {label, mission.Name}))\r
-                entry.label.Text = entry.Name;\r
+            var nameLabels = new[] {labelMissionName1, labelMissionName2, labelMissionName3};\r
+            var paramsLabels = new[] {labelMissionParams1, labelMissionParams2, labelMissionParams3};\r
+            var names = _sniffer.Missions.Select(mission => mission.Name).ToArray();\r
+            for (var i = 0; i < ShipInfo.FleetCount - 1; i++)\r
+            {\r
+                paramsLabels[i].Visible = false;\r
+                if (string.IsNullOrEmpty(names[i]))\r
+                {\r
+                    paramsLabels[i].Text = GenerateFleetParamsForMission(i + 1);\r
+                    paramsLabels[i].Visible = true;\r
+                }\r
+                nameLabels[i].Text = names[i];\r
+            }\r
+            SetMissionLabel();\r
+        }\r
+\r
+        private void SetMissionLabel()\r
+        {\r
+            labelMission.Text = (_config.ShowEndTime & TimerKind.Mission) != 0 ? "遠征終了" : "遠征";\r
+        }\r
+\r
+        private string GenerateFleetParamsForMission(int fleetNumber)\r
+        {\r
+            var result = new List<string>();\r
+            var fleet = _sniffer.Fleets[fleetNumber];\r
+            var kira = fleet.Ships.Count(ship => ship.Cond > 49);\r
+            var plus = fleet.Ships[0].Cond > 49;\r
+            if (kira > 0)\r
+                result.Add($"キラ{kira}{(plus ? "+" : "")}");\r
+            var drums = fleet.Ships.SelectMany(ship => ship.Slot).Count(item => item.Spec.IsDrum);\r
+            var drumShips = fleet.Ships.Count(ship => ship.Slot.Any(item => item.Spec.IsDrum));\r
+            if (drums > 0)\r
+                result.Add($"ド{drums}({drumShips}隻)");\r
+            if (fleet.DaihatsuBonus > 0)\r
+                result.Add($"ダ{fleet.DaihatsuBonus * 100:f1}%");\r
+            return string.Join(" ", result);\r
         }\r
 \r
         private void labelMission_Click(object sender, EventArgs e)\r
         {\r
             _config.ShowEndTime ^= TimerKind.Mission;\r
+            SetMissionLabel();\r
             UpdateTimers();\r
         }\r
 \r
@@ -817,6 +901,8 @@ namespace KancolleSniffer
             for (var i = 0; i < _sniffer.Missions.Length; i++)\r
             {\r
                 var entry = _sniffer.Missions[i];\r
+                if (entry.Name == "前衛支援任務" || entry.Name == "艦隊決戦支援任務")\r
+                    continue;\r
                 CheckAlarm("遠征終了", entry.Timer, i + 1, entry.Name);\r
             }\r
             for (var i = 0; i < _sniffer.NDock.Length; i++)\r
@@ -827,7 +913,7 @@ namespace KancolleSniffer
             for (var i = 0; i < _sniffer.KDock.Length; i++)\r
             {\r
                 var timer = _sniffer.KDock[i];\r
-                CheckAlarm("建造完了", timer, 0, $"第{i + 1:D}ドック");\r
+                CheckAlarm("建造完了", timer, i, "");\r
             }\r
             NotifyCondTimers();\r
             NotifyAkashiTimer();\r
@@ -850,7 +936,7 @@ namespace KancolleSniffer
 \r
         private void SetTimerColor(Label label, AlarmTimer timer, DateTime now)\r
         {\r
-            label.ForeColor = timer.IsFinished(now) ? CUDColor.Red : Color.Black;\r
+            label.ForeColor = timer.IsFinished(now) ? CUDColors.Red : Color.Black;\r
         }\r
 \r
         private void UpdateCondTimers()\r
@@ -883,7 +969,7 @@ namespace KancolleSniffer
             {\r
                 labelCondTimerTitle.Text = "cond49まで";\r
                 labelCondTimer.Text = (span >= TimeSpan.Zero ? span : TimeSpan.Zero).ToString(@"mm\:ss");\r
-                labelCondTimer.ForeColor = span <= TimeSpan.Zero ? CUDColor.Red : DefaultForeColor;\r
+                labelCondTimer.ForeColor = span <= TimeSpan.Zero ? CUDColors.Red : DefaultForeColor;\r
             }\r
         }\r
 \r
@@ -911,19 +997,19 @@ namespace KancolleSniffer
         {\r
             if (_config.UsePresetAkashi)\r
                 UpdatePresetAkashiTimer();\r
-            _mainLabels.SetAkashiTimer(_sniffer.Fleets[_currentFleet].Ships,\r
-                _sniffer.AkashiTimer.GetTimers(_currentFleet));\r
+            _mainLabels.SetAkashiTimer(_sniffer.Fleets[_currentFleet].ActualShips,\r
+                _sniffer.AkashiTimer.GetTimers(_currentFleet, _now));\r
         }\r
 \r
         private void UpdatePresetAkashiTimer()\r
         {\r
             var akashi = _sniffer.AkashiTimer;\r
-            var span = akashi.PresetDeckTimer;\r
-            var color = span == TimeSpan.Zero && akashi.CheckPresetRepairing() ? CUDColor.Red : DefaultForeColor;\r
+            var span = akashi.GetPresetDeckTimer(_now);\r
+            var color = span == TimeSpan.Zero && akashi.CheckPresetRepairing() ? CUDColors.Red : DefaultForeColor;\r
             var text = span == TimeSpan.MinValue ? "" : span.ToString(@"mm\:ss");\r
             labelAkashiRepairTimer.ForeColor = color;\r
             labelAkashiRepairTimer.Text = text;\r
-            if (akashi.CheckPresetRepairing() && !akashi.CheckRepairing(_currentFleet))\r
+            if (akashi.CheckPresetRepairing() && !akashi.CheckRepairing(_currentFleet, _now))\r
             {\r
                 labelPresetAkashiTimer.ForeColor = color;\r
                 labelPresetAkashiTimer.Text = text;\r
@@ -944,7 +1030,7 @@ namespace KancolleSniffer
                 _notificationManager.StopRepeat("泊地修理");\r
                 return;\r
             }\r
-            if (!akashi.CheckRepairing() && !(akashi.CheckPresetRepairing() && _config.UsePresetAkashi))\r
+            if (!akashi.CheckRepairing(_now) && !(akashi.CheckPresetRepairing() && _config.UsePresetAkashi))\r
             {\r
                 _notificationManager.StopRepeat("泊地修理");\r
                 return;\r
@@ -1028,7 +1114,7 @@ namespace KancolleSniffer
                         continue;\r
                     }\r
                     count[i].Text = " " + c;\r
-                    count[i].ForeColor = c.Cleared ? CUDColor.Green : Color.Black;\r
+                    count[i].ForeColor = c.Cleared ? CUDColors.Green : Color.Black;\r
                     _toolTip.SetToolTip(count[i], c.ToToolTip());\r
                 }\r
                 else\r
@@ -1070,9 +1156,10 @@ namespace KancolleSniffer
 \r
         [DllImport("winmm.dll")]\r
         private static extern int mciSendString(String command,\r
-            StringBuilder buffer, int bufferSize, IntPtr hwndCallback);\r
+            StringBuilder buffer, int bufferSize, IntPtr hWndCallback);\r
 \r
 // ReSharper disable InconsistentNaming\r
+        // ReSharper disable once IdentifierTypo\r
         private const int MM_MCINOTIFY = 0x3B9;\r
 \r
         private const int MCI_NOTIFY_SUCCESSFUL = 1;\r
@@ -1106,7 +1193,10 @@ namespace KancolleSniffer
             };\r
             foreach (var a in labels)\r
             {\r
-                for (var fleet = 0; fleet < labels[0].Length; fleet++)\r
+                a[0].Tag = 0;\r
+                a[0].Click += labelFleet1_Click;\r
+                a[0].DoubleClick += labelFleet1_DoubleClick;\r
+                for (var fleet = 1; fleet < labels[0].Length; fleet++)\r
                 {\r
                     a[fleet].Tag = fleet;\r
                     a[fleet].Click += labelFleet_Click;\r
@@ -1121,26 +1211,47 @@ namespace KancolleSniffer
                 return;\r
             var fleet = (int)((Label)sender).Tag;\r
             if (_currentFleet == fleet)\r
-            {\r
-                if (fleet > 0)\r
-                    return;\r
-                _combinedFleet = _sniffer.CombinedFleetType > 0 && !_combinedFleet;\r
-                UpdatePanelShipInfo();\r
                 return;\r
-            }\r
             _combinedFleet = false;\r
             _currentFleet = fleet;\r
             UpdatePanelShipInfo();\r
         }\r
 \r
+        private readonly SemaphoreSlim _clickSemaphore = new SemaphoreSlim(1);\r
+        private readonly SemaphoreSlim _doubleClickSemaphore = new SemaphoreSlim(0);\r
+\r
+        private async void labelFleet1_Click(object sender, EventArgs e)\r
+        {\r
+            if (!_started)\r
+                return;\r
+            if (_currentFleet != 0)\r
+            {\r
+                labelFleet_Click(sender, e);\r
+                return;\r
+            }\r
+            if (!_clickSemaphore.Wait(0))\r
+                return;\r
+            try\r
+            {\r
+                if (await _doubleClickSemaphore.WaitAsync(SystemInformation.DoubleClickTime))\r
+                    return;\r
+            }\r
+            finally\r
+            {\r
+                _clickSemaphore.Release();\r
+            }\r
+            _combinedFleet = _sniffer.IsCombinedFleet && !_combinedFleet;\r
+            UpdatePanelShipInfo();\r
+        }\r
+\r
         private void labelFleet1_MouseHover(object sender, EventArgs e)\r
         {\r
-            labelFleet1.Text = _currentFleet == 0 && _sniffer.CombinedFleetType > 0 && !_combinedFleet ? "連合" : "第一";\r
+            labelFleet1.Text = _currentFleet == 0 && _sniffer.IsCombinedFleet && !_combinedFleet ? "連合" : "第一";\r
         }\r
 \r
         private void labelFleet1_MouseLeave(object sender, EventArgs e)\r
         {\r
-            labelFleet1.Text = _combinedFleet ? "連合" : "第一";\r
+            labelFleet1.Text = _combinedFleet ? CombinedName : "第一";\r
         }\r
 \r
         private void labelFleet_DoubleClick(object sender, EventArgs e)\r
@@ -1149,11 +1260,27 @@ namespace KancolleSniffer
                 return;\r
             var fleet = (int)((Label)sender).Tag;\r
             var text = TextGenerator.GenerateFleetData(_sniffer, fleet);\r
+            CopyFleetText(text, (Label)sender);\r
+        }\r
+\r
+        private void labelFleet1_DoubleClick(object sender, EventArgs e)\r
+        {\r
+            if (!_started)\r
+                return;\r
+            _doubleClickSemaphore.Release();\r
+            var text = TextGenerator.GenerateFleetData(_sniffer, 0);\r
+            if (_combinedFleet)\r
+                text += TextGenerator.GenerateFleetData(_sniffer, 1);\r
+            CopyFleetText(text, (Label)sender);\r
+        }\r
+\r
+        private void CopyFleetText(string text, Label fleetButton)\r
+        {\r
             if (string.IsNullOrEmpty(text))\r
                 return;\r
             Clipboard.SetText(text);\r
             _tooltipCopy.Active = true;\r
-            _tooltipCopy.Show("コピーしました。", (Label)sender);\r
+            _tooltipCopy.Show("コピーしました。", fleetButton);\r
             Task.Run(async () =>\r
             {\r
                 await Task.Delay(1000);\r
@@ -1161,8 +1288,6 @@ namespace KancolleSniffer
             });\r
         }\r
 \r
-        private readonly Color _activeButtonColor = Color.FromArgb(152, 179, 208);\r
-\r
         private void labelBucketHistoryButton_Click(object sender, EventArgs e)\r
         {\r
             if (labelBucketHistory.Visible)\r
@@ -1174,7 +1299,7 @@ namespace KancolleSniffer
             {\r
                 labelBucketHistory.Visible = true;\r
                 labelBucketHistory.BringToFront();\r
-                labelBucketHistoryButton.BackColor = _activeButtonColor;\r
+                labelBucketHistoryButton.BackColor = CustomColors.ActiveButtonColor;\r
             }\r
         }\r
 \r
@@ -1195,7 +1320,7 @@ namespace KancolleSniffer
             {\r
                 panelMaterialHistory.Visible = true;\r
                 panelMaterialHistory.BringToFront();\r
-                labelMaterialHistoryButton.BackColor = _activeButtonColor;\r
+                labelMaterialHistoryButton.BackColor = CustomColors.ActiveButtonColor;\r
             }\r
         }\r
 \r
@@ -1205,7 +1330,7 @@ namespace KancolleSniffer
             labelMaterialHistoryButton.BackColor = DefaultBackColor;\r
         }\r
 \r
-        public void ResetAchievemnt()\r
+        public void ResetAchievement()\r
         {\r
             _sniffer.Achievement.Reset();\r
             UpdateItemInfo();\r
@@ -1222,7 +1347,7 @@ namespace KancolleSniffer
             {\r
                 panelRepairList.Visible = true;\r
                 panelRepairList.BringToFront();\r
-                labelRepairListButton.BackColor = _activeButtonColor;\r
+                labelRepairListButton.BackColor = CustomColors.ActiveButtonColor;\r
             }\r
         }\r
 \r
@@ -1254,7 +1379,7 @@ namespace KancolleSniffer
 \r
         private void labelClearQuest_MouseDown(object sender, MouseEventArgs e)\r
         {\r
-            labelClearQuest.BackColor = _activeButtonColor;\r
+            labelClearQuest.BackColor = CustomColors.ActiveButtonColor;\r
         }\r
 \r
         private void labelClearQuest_MouseUp(object sender, MouseEventArgs e)\r