OSDN Git Service

一覧の艦娘の分類をセカンダリに反映させるのに再起動が必要なのを直す
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / View / ShipListPanel / ShipListPanel.cs
1 // Copyright (C) 2016 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 //\r
3 // Licensed under the Apache License, Version 2.0 (the "License");\r
4 // you may not use this file except in compliance with the License.\r
5 // You may obtain a copy of the License at\r
6 //\r
7 //    http://www.apache.org/licenses/LICENSE-2.0\r
8 //\r
9 // Unless required by applicable law or agreed to in writing, software\r
10 // distributed under the License is distributed on an "AS IS" BASIS,\r
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
12 // See the License for the specific language governing permissions and\r
13 // limitations under the License.\r
14 \r
15 using System;\r
16 using System.Collections.Generic;\r
17 using System.Linq;\r
18 using System.Runtime.InteropServices;\r
19 using System.Windows.Forms;\r
20 using KancolleSniffer.Forms;\r
21 using KancolleSniffer.Model;\r
22 using KancolleSniffer.View.ListWindow;\r
23 using static System.Math;\r
24 \r
25 namespace KancolleSniffer.View.ShipListPanel\r
26 {\r
27     public class ShipListPanel : Panel, IPanelResize\r
28     {\r
29         public const int LabelHeight = 12;\r
30         public const int LineHeight = 16;\r
31         private ShipStatus[] _shipList;\r
32         private readonly List<ShipLabel.Hp> _hpLabels = new List<ShipLabel.Hp>();\r
33         private readonly ShipListLabels _shipListLabels;\r
34         private readonly GroupConfigLabels _groupConfigLabels;\r
35         private readonly RepairListLabels _repairListLabels;\r
36         private int _labelCount;\r
37         private string _mode;\r
38         private bool _hpPercent;\r
39 \r
40         public List<List<int>> GroupSettings\r
41         {\r
42             get => _groupConfigLabels.GroupSettings;\r
43             set => _groupConfigLabels.GroupSettings = value;\r
44         }\r
45 \r
46 \r
47         public bool GroupUpdated\r
48         {\r
49             get => _groupConfigLabels.GroupUpdated;\r
50             set => _groupConfigLabels.GroupUpdated = value;\r
51         }\r
52 \r
53         public ScrollBar ScrollBar { get; }\r
54 \r
55         public ShipStatus GetShip(int i)\r
56         {\r
57             return _shipList[i + ScrollBar.Value];\r
58         }\r
59 \r
60         public ShipListPanel()\r
61         {\r
62             ScrollBar = new VScrollBar {Dock = DockStyle.Right, Visible = false};\r
63             ScrollBar.ValueChanged += ScrollBarOnValueChanged;\r
64             Controls.Add(ScrollBar);\r
65             _shipListLabels = new ShipListLabels(this);\r
66             _groupConfigLabels = new GroupConfigLabels(this);\r
67             _repairListLabels = new RepairListLabels(this);\r
68         }\r
69 \r
70         private void ScrollBarOnValueChanged(object sender, EventArgs eventArgs)\r
71         {\r
72             SuspendDrawing();\r
73             SetShipLabels();\r
74             ResumeDrawing();\r
75         }\r
76 \r
77         public void ApplyResize()\r
78         {\r
79             if (_shipList == null || _shipList.Length == 0 || !Visible)\r
80                 return;\r
81             SuspendDrawing();\r
82             SetupLabels();\r
83             ResizeLabels();\r
84             SetShipLabels();\r
85             ResumeDrawing();\r
86         }\r
87 \r
88         protected override void OnMouseWheel(MouseEventArgs e)\r
89         {\r
90             if (!ScrollBar.Visible)\r
91                 return;\r
92             ScrollBar.Value = Max(ScrollBar.Minimum, Min(ScrollBar.Maximum - ScrollBar.LargeChange + 1,\r
93                 ScrollBar.Value - e.Delta * SystemInformation.MouseWheelScrollLines / 120));\r
94         }\r
95 \r
96         public void Update(Sniffer sniffer, string mode, ShipListConfig settings)\r
97         {\r
98             _mode = mode;\r
99             CreateShipList(sniffer, settings);\r
100             SuspendDrawing();\r
101             SetupLabels();\r
102             ResizeLabels();\r
103             SetShipLabels();\r
104             ResumeDrawing();\r
105         }\r
106 \r
107         [DllImport("user32.dll")]\r
108         private static extern int SendMessage(IntPtr hWnd, int wMsg, bool wParam, IntPtr lParam);\r
109 \r
110         private void SuspendDrawing()\r
111         {\r
112             SendMessage(Handle, 11, false, IntPtr.Zero); // WM_SETREDRAW = 11\r
113             SuspendLayout();\r
114         }\r
115 \r
116         private void ResumeDrawing()\r
117         {\r
118             ResumeLayout();\r
119             SendMessage(Handle, 11, true, IntPtr.Zero);\r
120             Refresh();\r
121         }\r
122 \r
123         private void CreateShipList(Sniffer sniffer, ShipListConfig settings)\r
124         {\r
125             var ships = FilterByShipTypes(\r
126                 _mode == "修復" ? sniffer.RepairList : FilterByGroup(sniffer.ShipList, settings.ShipGroup, _mode),\r
127                 settings.ShipCategories).ToArray();\r
128             var order = _mode == "修復" ? ListForm.SortOrder.Repair : settings.SortOrder;\r
129             if (!settings.ShipType)\r
130             {\r
131                 _shipList = ships.OrderBy(s => s, new CompareShip(false, order)).ToArray();\r
132                 return;\r
133             }\r
134             _shipList = ships.Select(ship => new {Id = ship.Spec.ShipType, Name = ship.Spec.ShipTypeName})\r
135                 .Distinct().Select(type => new ShipStatus\r
136                 {\r
137                     Spec = new ShipSpec {Name = type.Name, ShipType = type.Id},\r
138                     Level = 1000\r
139                 }).Concat(ships).OrderBy(ship => ship, new CompareShip(true, order)).ToArray();\r
140         }\r
141 \r
142         private static IEnumerable<ShipStatus> FilterByGroup(IEnumerable<ShipStatus> ships,\r
143             IReadOnlyList<List<int>> groups, string groupName)\r
144         {\r
145             var g = Array.FindIndex(new[] {"A", "B", "C", "D"}, x => x == groupName);\r
146             if (g == -1)\r
147                 return ships;\r
148             if (groups.Count == 0)\r
149                 return new ShipStatus[0];\r
150             return from s in ships where groups[g].Contains(s.Id) select s;\r
151         }\r
152 \r
153         private static readonly int[][] ShipTypeIds =\r
154         {\r
155             new[] // 戦艦\r
156             {\r
157                 8, // 巡洋戦艦\r
158                 9, // 戦艦\r
159                 10 // 航空戦艦\r
160             },\r
161             new[] // 空母\r
162             {\r
163                 18, // 装甲空母\r
164                 11, // 正規空母\r
165                 7 // 軽空母\r
166             },\r
167             new[] // 重巡\r
168             {\r
169                 5, // 重巡洋艦\r
170                 6 // 航空巡洋艦\r
171             },\r
172             new[] // 軽巡\r
173             {\r
174                 3, // 軽巡洋艦\r
175                 4, // 重雷装巡洋艦\r
176                 21 // 練習巡洋艦\r
177             },\r
178             new[] // 駆逐\r
179             {\r
180                 2 // 駆逐艦\r
181             },\r
182             new[] // 海防\r
183             {\r
184                 1 // 海防艦\r
185             },\r
186             new[] // 潜水\r
187             {\r
188                 13, // 潜水艦\r
189                 14 // 潜水空母\r
190             },\r
191             new[] // 補助\r
192             {\r
193                 16, // 水上機母艦\r
194                 17, // 揚陸艦\r
195                 19, // 工作艦\r
196                 20, // 潜水母艦\r
197                 22 // 補給艦\r
198             }\r
199         };\r
200 \r
201         private static readonly int[] ShipTypeSortIds = CreateShipTypeSortIds();\r
202 \r
203         private static int[] CreateShipTypeSortIds()\r
204         {\r
205             var ids = ShipTypeIds.SelectMany(x => x).ToArray();\r
206             var res = new int[ids.Max() + 1];\r
207             for (var i = 0; i < ids.Length; i++)\r
208                 res[ids[i]] = i;\r
209             return res;\r
210         }\r
211 \r
212         private static IEnumerable<ShipStatus> FilterByShipTypes(IEnumerable<ShipStatus> ships, ShipCategory shipTypes)\r
213         {\r
214             var ids = Enumerable.Range(0, ShipTypeIds.Length)\r
215                 .Where(type => ((int)shipTypes & (1 << type)) != 0)\r
216                 .SelectMany(type => ShipTypeIds[type]).ToArray();\r
217             return ships.Where(ship => ids.Contains(ship.Spec.ShipType));\r
218         }\r
219 \r
220         public IEnumerable<ShipStatus> CurrentShipList => _shipList.Where(ship => ship.Level != 1000);\r
221 \r
222         private class CompareShip : IComparer<ShipStatus>\r
223         {\r
224             private readonly bool _shipType;\r
225             private readonly ListForm.SortOrder _order;\r
226 \r
227             public CompareShip(bool type, ListForm.SortOrder order)\r
228             {\r
229                 _shipType = type;\r
230                 _order = order;\r
231             }\r
232 \r
233             public int Compare(ShipStatus a, ShipStatus b)\r
234             {\r
235                 if (a == null || b == null)\r
236                     throw new ArgumentNullException();\r
237                 if (_shipType)\r
238                 {\r
239                     if (a.Spec.ShipType != b.Spec.ShipType)\r
240                         return ShipTypeSortIds[a.Spec.ShipType] - ShipTypeSortIds[b.Spec.ShipType];\r
241                     if (a.Level != b.Level)\r
242                     {\r
243                         if (a.Level == 1000)\r
244                             return -1;\r
245                         if (b.Level == 1000)\r
246                             return 1;\r
247                     }\r
248                 }\r
249                 if (_order == ListForm.SortOrder.Repair && a.RepairTime != b.RepairTime)\r
250                     return (int)(b.RepairTime - a.RepairTime).TotalSeconds;\r
251                 if (a.Cond != b.Cond)\r
252                 {\r
253                     if (_order == ListForm.SortOrder.CondAscend)\r
254                         return a.Cond - b.Cond;\r
255                     if (_order == ListForm.SortOrder.CondDescend)\r
256                         return b.Cond - a.Cond;\r
257                 }\r
258                 if (a.Level != b.Level)\r
259                 {\r
260                     if (_order == ListForm.SortOrder.ExpToNextAscend)\r
261                         return b.Level - a.Level;\r
262                     if (_order == ListForm.SortOrder.ExpToNextDescend)\r
263                         return a.Level - b.Level;\r
264                 }\r
265                 if (a.ExpToNext != b.ExpToNext)\r
266                 {\r
267                     if (_order == ListForm.SortOrder.ExpToNextAscend)\r
268                         return a.ExpToNext - b.ExpToNext;\r
269                     if (_order == ListForm.SortOrder.ExpToNextDescend)\r
270                         return b.ExpToNext - a.ExpToNext;\r
271                 }\r
272                 if (_shipType)\r
273                 {\r
274                     if (a.Spec.SortId != b.Spec.SortId)\r
275                         return a.Spec.SortId - b.Spec.SortId;\r
276                     if (a.Level != b.Level)\r
277                         return b.Level - a.Level;\r
278                 }\r
279                 else\r
280                 {\r
281                     if (a.Level != b.Level)\r
282                         return b.Level - a.Level;\r
283                     if (a.Spec.SortId != b.Spec.SortId)\r
284                         return a.Spec.SortId - b.Spec.SortId;\r
285                 }\r
286                 return a.Id - b.Id;\r
287             }\r
288         }\r
289 \r
290         private void SetupLabels()\r
291         {\r
292             for (; _labelCount * LineHeight < Height; _labelCount++)\r
293             {\r
294                 _groupConfigLabels.CreateComponents(_labelCount);\r
295                 _repairListLabels.CreateLabels(_labelCount);\r
296                 _shipListLabels.CreateShipLabels(_labelCount);\r
297             }\r
298             SetupScrollBar();\r
299         }\r
300 \r
301         private void SetupScrollBar()\r
302         {\r
303             var needBar = Scaler.ScaleHeight((float)_shipList.Length * LineHeight) > Height;\r
304             if (!needBar)\r
305             {\r
306                 ScrollBar.Visible = false;\r
307                 ScrollBar.Value = 0;\r
308                 return;\r
309             }\r
310             ScrollBar.Visible = true;\r
311             ScrollBar.Minimum = 0;\r
312             var lines = Max(1, Height / Scaler.ScaleHeight(LineHeight));\r
313             var max = _shipList.Length - lines;\r
314             var largeChange = Min(lines, max);\r
315             ScrollBar.LargeChange = largeChange;\r
316             ScrollBar.Maximum = Max(0, max + largeChange - 1); // ScrollBarを最大まで動かしてもmaxには届かない\r
317             ScrollBar.Value = Min(ScrollBar.Value, max);\r
318         }\r
319 \r
320         private void ResizeLabels()\r
321         {\r
322             var width = Width - SystemInformation.VerticalScrollBarWidth - 2;\r
323             for (var i = 0; i < _labelCount; i++)\r
324             {\r
325                 _shipListLabels.Resize(i, width);\r
326                 _groupConfigLabels.Resize(i, width);\r
327                 _repairListLabels.Resize(i, width);\r
328             }\r
329         }\r
330 \r
331         public void SetHpPercent(ShipLabel.Hp label)\r
332         {\r
333             if (_hpPercent)\r
334                 label.ToggleHpPercent();\r
335             _hpLabels.Add(label);\r
336             label.DoubleClick += HpLabelClickHandler;\r
337         }\r
338 \r
339         private void SetShipLabels()\r
340         {\r
341             for (var i = 0; i < (Height + LineHeight - 1) / LineHeight; i++)\r
342             {\r
343                 HidePanels(i);\r
344                 if (i + ScrollBar.Value >= _shipList.Length)\r
345                     continue;\r
346                 if (InShipStatus(_mode))\r
347                     _shipListLabels.SetShipStatus(i);\r
348                 if (_mode == "分類")\r
349                     _groupConfigLabels.SetGrouping(i);\r
350                 if (_mode == "修復")\r
351                     _repairListLabels.SetRepairList(i);\r
352             }\r
353         }\r
354 \r
355         public void SetShipType(int i)\r
356         {\r
357             _shipListLabels.SetShipType(i);\r
358         }\r
359 \r
360         private void HidePanels(int i)\r
361         {\r
362             _shipListLabels.HidePanel(i);\r
363             _repairListLabels.HidePanel(i);\r
364             _groupConfigLabels.HidePanel(i);\r
365         }\r
366 \r
367         private bool InShipStatus(string mode) => Array.Exists(new[] {"全艦", "A", "B", "C", "D"}, x => mode == x);\r
368 \r
369         public event Action HpLabelClick;\r
370 \r
371         private void HpLabelClickHandler(object sender, EventArgs ev)\r
372         {\r
373             HpLabelClick?.Invoke();\r
374         }\r
375 \r
376         public void ToggleHpPercent()\r
377         {\r
378             _hpPercent = !_hpPercent;\r
379             foreach (var label in _hpLabels)\r
380                 label.ToggleHpPercent();\r
381         }\r
382 \r
383         public void ShowShip(int id)\r
384         {\r
385             if (!ScrollBar.Visible)\r
386                 return;\r
387             var i = Array.FindIndex(_shipList, s => s.Id == id);\r
388             if (i == -1)\r
389                 return;\r
390             ScrollBar.Value = Min(i, ScrollBar.Maximum + 1 - ScrollBar.LargeChange);\r
391             SetShipLabels();\r
392         }\r
393     }\r
394 }