OSDN Git Service

通信ログからトークン、提督名、艦隊名を削除する
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / MainForm.cs
index 8190aa9..810d826 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
@@ -183,12 +181,13 @@ namespace KancolleSniffer
                 return;\r
             var request = session.Request.BodyAsString;\r
             var response = session.Response.BodyAsString;\r
+            Privacy.Remove(ref url, ref request, ref response);\r
             if (response == null || !response.StartsWith("svdata="))\r
             {\r
                 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 +242,7 @@ namespace KancolleSniffer
             }\r
         }\r
 \r
-        private string UnescapeString(string s)\r
+        private string UnEscapeString(string s)\r
         {\r
             try\r
             {\r
@@ -284,6 +283,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 +430,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 +452,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 +520,11 @@ namespace KancolleSniffer
             if (!_listForm.Visible)\r
                 return;\r
             var idx = (int)((Control)sender).Tag;\r
-            var statuses = _sniffer.GetShipStatuses(_currentFleet);\r
-            if (statuses.Length <= idx)\r
-                return;\r
-            _listForm.ShowShip(statuses[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 +541,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 +580,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 +606,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,17 +640,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 statuses = _sniffer.GetShipStatuses(_currentFleet);\r
-            panel7Ships.Visible = statuses.Length == 7;\r
-            _mainLabels.SetShipLabels(statuses);\r
-            if (_sniffer.CombinedFleetType == 0)\r
+            var fleets = _sniffer.Fleets;\r
+            var ships = fleets[_currentFleet].ActualShips;\r
+            panel7Ships.Visible = ships.Count == 7;\r
+            _mainLabels.SetShipLabels(ships);\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(_sniffer.GetShipStatuses(0), _sniffer.GetShipStatuses(1));\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
@@ -654,23 +673,43 @@ 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
         public void UpdateFighterPower(bool combined)\r
         {\r
+            var fleets = _sniffer.Fleets;\r
             var fp = combined\r
-                ? _sniffer.GetFighterPower(0).Zip(_sniffer.GetFighterPower(1), (a, b) => a + b).ToArray()\r
-                : _sniffer.GetFighterPower(_currentFleet);\r
+                ? fleets[0].FighterPower.Zip(fleets[1].FighterPower, (a, b) => a + b).ToArray()\r
+                : fleets[_currentFleet].FighterPower;\r
             labelFighterPower.Text = fp[0].ToString("D");\r
             var cr = combined\r
-                ? _sniffer.GetContactTriggerRate(0) + _sniffer.GetContactTriggerRate(1)\r
-                : _sniffer.GetContactTriggerRate(_currentFleet);\r
+                ? fleets[0].ContactTriggerRate + fleets[1].ContactTriggerRate\r
+                : fleets[_currentFleet].ContactTriggerRate;\r
             var text = "制空: " + (fp[0] == fp[1] ? $"{fp[0]}" : $"{fp[0]}~{fp[1]}") +\r
                        $" 触接: {cr * 100:f1}";\r
             _toolTip.SetToolTip(labelFighterPower, text);\r
@@ -679,9 +718,10 @@ namespace KancolleSniffer
 \r
         private void UpdateLoS()\r
         {\r
-            labelLoS.Text = RoundDown(_sniffer.GetFleetLineOfSights(_currentFleet, 1)).ToString("F1");\r
-            var text = $"係数3: {RoundDown(_sniffer.GetFleetLineOfSights(_currentFleet, 3)):F1}\r\n" +\r
-                       $"係数4: {RoundDown(_sniffer.GetFleetLineOfSights(_currentFleet, 4)):F1}";\r
+            var fleet = _sniffer.Fleets[_currentFleet];\r
+            labelLoS.Text = RoundDown(fleet.GetLineOfSights(1)).ToString("F1");\r
+            var text = $"係数3: {RoundDown(fleet.GetLineOfSights(3)):F1}\r\n" +\r
+                       $"係数4: {RoundDown(fleet.GetLineOfSights(4)):F1}";\r
             _toolTip.SetToolTip(labelLoS, text);\r
             _toolTip.SetToolTip(labelLoSCaption, text);\r
         }\r
@@ -695,6 +735,7 @@ namespace KancolleSniffer
         {\r
             ResetBattleInfo();\r
             _listForm.UpdateBattleResult();\r
+            _listForm.UpdateAirBattleResult();\r
             if (_sniffer.Battle.BattleState == BattleState.None)\r
                 return;\r
             panelBattleInfo.BringToFront();\r
@@ -703,8 +744,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
@@ -727,9 +771,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
@@ -751,7 +800,7 @@ namespace KancolleSniffer
 \r
             for (var i = 0; i < fuelSq.Length; i++)\r
             {\r
-                var stat = _sniffer.ChargeStatuses[i];\r
+                var stat = _sniffer.Fleets[i].ChargeStatus;\r
                 fuelSq[i].ImageIndex = stat.Fuel;\r
                 bullSq[i].ImageIndex = stat.Bull;\r
             }\r
@@ -760,26 +809,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
@@ -814,6 +902,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
@@ -824,7 +914,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
@@ -847,7 +937,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
@@ -880,7 +970,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
@@ -908,20 +998,19 @@ namespace KancolleSniffer
         {\r
             if (_config.UsePresetAkashi)\r
                 UpdatePresetAkashiTimer();\r
-            var statuses = _sniffer.GetShipStatuses(_currentFleet);\r
-            _mainLabels.SetAkashiTimer(statuses,\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
@@ -942,7 +1031,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
@@ -1026,7 +1115,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
@@ -1068,9 +1157,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
@@ -1104,7 +1194,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
@@ -1119,26 +1212,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
@@ -1147,11 +1261,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
@@ -1159,8 +1289,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
@@ -1172,7 +1300,7 @@ namespace KancolleSniffer
             {\r
                 labelBucketHistory.Visible = true;\r
                 labelBucketHistory.BringToFront();\r
-                labelBucketHistoryButton.BackColor = _activeButtonColor;\r
+                labelBucketHistoryButton.BackColor = CustomColors.ActiveButtonColor;\r
             }\r
         }\r
 \r
@@ -1193,7 +1321,7 @@ namespace KancolleSniffer
             {\r
                 panelMaterialHistory.Visible = true;\r
                 panelMaterialHistory.BringToFront();\r
-                labelMaterialHistoryButton.BackColor = _activeButtonColor;\r
+                labelMaterialHistoryButton.BackColor = CustomColors.ActiveButtonColor;\r
             }\r
         }\r
 \r
@@ -1203,7 +1331,7 @@ namespace KancolleSniffer
             labelMaterialHistoryButton.BackColor = DefaultBackColor;\r
         }\r
 \r
-        public void ResetAchievemnt()\r
+        public void ResetAchievement()\r
         {\r
             _sniffer.Achievement.Reset();\r
             UpdateItemInfo();\r
@@ -1220,7 +1348,7 @@ namespace KancolleSniffer
             {\r
                 panelRepairList.Visible = true;\r
                 panelRepairList.BringToFront();\r
-                labelRepairListButton.BackColor = _activeButtonColor;\r
+                labelRepairListButton.BackColor = CustomColors.ActiveButtonColor;\r
             }\r
         }\r
 \r
@@ -1252,7 +1380,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