OSDN Git Service

a3ec97fdaecde5fe9ec8400eca9ac93c553ff9ad
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / 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.Drawing;\r
18 using System.Linq;\r
19 using System.Runtime.InteropServices;\r
20 using System.Windows.Forms;\r
21 using static System.Math;\r
22 \r
23 namespace KancolleSniffer\r
24 {\r
25     public class ShipListPanel : Panel\r
26     {\r
27         private const int LabelHeight = 12;\r
28         public const int LineHeight = 16;\r
29         private ShipStatus[] _shipList;\r
30         private readonly List<ShipLabel[]> _labelList = new List<ShipLabel[]>();\r
31         private readonly List<Panel> _labelPanelList = new List<Panel>();\r
32         private readonly List<CheckBox[]> _checkBoxesList = new List<CheckBox[]>();\r
33         private readonly List<ShipLabel[]> _groupingLabelList = new List<ShipLabel[]>();\r
34         private readonly List<Panel> _groupingPanelList = new List<Panel>();\r
35         private readonly List<ShipLabel[]> _repairLabelList = new List<ShipLabel[]>();\r
36         private readonly List<Panel> _repairPanelList = new List<Panel>();\r
37         private string _mode;\r
38 \r
39         public const int GroupCount = 4;\r
40         public HashSet<int>[] GroupSettings { get; } = new HashSet<int>[GroupCount];\r
41 \r
42         public ScrollBar ScrollBar { get; }\r
43 \r
44         public ShipListPanel()\r
45         {\r
46             ScrollBar = new VScrollBar {Dock = DockStyle.Right, Visible = false};\r
47             ScrollBar.ValueChanged += ScrollBarOnValueChanged;\r
48             Controls.Add(ScrollBar);\r
49         }\r
50 \r
51         private void ScrollBarOnValueChanged(object sender, EventArgs eventArgs)\r
52         {\r
53             SuspendDrawing();\r
54             SetShipLabels();\r
55             ResumeDrawing();\r
56         }\r
57 \r
58         protected override void OnResize(EventArgs ev)\r
59         {\r
60             base.OnResize(ev);\r
61             if (_shipList == null || _shipList.Length == 0 || !Visible)\r
62                 return;\r
63             SuspendDrawing();\r
64             SetupLabels();\r
65             SetShipLabels();\r
66             ResumeDrawing();\r
67         }\r
68 \r
69         protected override void OnMouseWheel(MouseEventArgs e)\r
70         {\r
71             ScrollBar.Value = Max(ScrollBar.Minimum, Min(ScrollBar.Maximum - ScrollBar.LargeChange + 1,\r
72                 ScrollBar.Value - e.Delta * SystemInformation.MouseWheelScrollLines / 120));\r
73         }\r
74 \r
75         public void Update(Sniffer sniffer, string mode, ListForm.SortOrder sortOrder, bool byShipType)\r
76         {\r
77             _mode = mode;\r
78             CreateShipList(sniffer, sortOrder, byShipType);\r
79             SuspendDrawing();\r
80             SetupLabels();\r
81             SetShipLabels();\r
82             ResumeDrawing();\r
83         }\r
84 \r
85         [DllImport("user32.dll")]\r
86         public static extern int SendMessage(IntPtr hWnd, int wMsg, bool wParam, IntPtr lParam);\r
87 \r
88         private void SuspendDrawing()\r
89         {\r
90             SendMessage(Handle, 11, false, IntPtr.Zero); // WM_SETREDRAW = 11\r
91             SuspendLayout();\r
92         }\r
93 \r
94         public void ResumeDrawing()\r
95         {\r
96             ResumeLayout();\r
97             SendMessage(Handle, 11, true, IntPtr.Zero);\r
98             Refresh();\r
99         }\r
100 \r
101         void CreateShipList(Sniffer sniffer, ListForm.SortOrder sortOrder, bool byShipType)\r
102         {\r
103             var ships = _mode == "修復" ? sniffer.RepairList : FilterByGroup(sniffer.ShipList, _mode).ToArray();\r
104             var order = _mode == "修復" ? ListForm.SortOrder.Repair : sortOrder;\r
105             if (!byShipType)\r
106             {\r
107                 _shipList = ships.OrderBy(s => s, new CompareShip(false, order)).ToArray();\r
108                 return;\r
109             }\r
110             var types = ships.Select(s => new {Id = s.Spec.ShipType, Name = s.Spec.ShipTypeName})\r
111                 .Distinct()\r
112                 .Select(stype =>\r
113                     new ShipStatus\r
114                     {\r
115                         Spec = new ShipSpec {Name = stype.Name, ShipType = stype.Id},\r
116                         Level = 1000,\r
117                         NowHp = -1000,\r
118                         Cond = -1000\r
119                     });\r
120             _shipList = ships.Concat(types).OrderBy(s => s, new CompareShip(true, order)).ToArray();\r
121         }\r
122 \r
123         private IEnumerable<ShipStatus> FilterByGroup(IEnumerable<ShipStatus> ships, string group)\r
124         {\r
125             var g = Array.FindIndex(new[] {"A", "B", "C", "D"}, x => x == group);\r
126             if (g == -1)\r
127                 return ships;\r
128             return from s in ships where GroupSettings[g].Contains(s.Id) select s;\r
129         }\r
130 \r
131         public IEnumerable<ShipStatus> CurrentShipList => _shipList.Where(ship => ship.Level != 1000);\r
132 \r
133         private class CompareShip : IComparer<ShipStatus>\r
134         {\r
135             private readonly bool _shipType;\r
136             private readonly ListForm.SortOrder _order;\r
137 \r
138             public CompareShip(bool type, ListForm.SortOrder order)\r
139             {\r
140                 _shipType = type;\r
141                 _order = order;\r
142             }\r
143 \r
144             public int Compare(ShipStatus a, ShipStatus b)\r
145             {\r
146                 if (a == null || b == null)\r
147                     throw new ArgumentNullException();\r
148                 if (_shipType)\r
149                 {\r
150                     if (a.Spec.ShipType != b.Spec.ShipType)\r
151                         return a.Spec.ShipType - b.Spec.ShipType;\r
152                     if (a.Level != b.Level)\r
153                     {\r
154                         if (a.Level == 1000)\r
155                             return -1;\r
156                         if (b.Level == 1000)\r
157                             return 1;\r
158                     }\r
159                 }\r
160                 if (_order == ListForm.SortOrder.Repair && a.RepairTime != b.RepairTime)\r
161                     return (int)(b.RepairTime - a.RepairTime).TotalSeconds;\r
162                 if (a.Cond != b.Cond)\r
163                 {\r
164                     if (_order == ListForm.SortOrder.CondAscend)\r
165                         return a.Cond - b.Cond;\r
166                     if (_order == ListForm.SortOrder.CondDescend)\r
167                         return b.Cond - a.Cond;\r
168                 }\r
169                 if (a.Level != b.Level)\r
170                 {\r
171                     if (_order == ListForm.SortOrder.ExpToNextAscend)\r
172                         return b.Level - a.Level;\r
173                     if (_order == ListForm.SortOrder.ExpToNextDescend)\r
174                         return a.Level - b.Level;\r
175                     if (!_shipType) // Condが同じかSortOrder.Noneで艦種なし\r
176                         return b.Level - a.Level;\r
177                 }\r
178                 if (a.ExpToNext != b.ExpToNext)\r
179                 {\r
180                     if (_order == ListForm.SortOrder.ExpToNextAscend)\r
181                         return a.ExpToNext - b.ExpToNext;\r
182                     if (_order == ListForm.SortOrder.ExpToNextDescend)\r
183                         return b.ExpToNext - a.ExpToNext;\r
184                 }\r
185                 if (a.Spec.SortNo != b.Spec.SortNo)\r
186                     return a.Spec.SortNo - b.Spec.SortNo;\r
187                 return a.Id - b.Id;\r
188             }\r
189         }\r
190 \r
191         private void SetupLabels()\r
192         {\r
193             for (var i = _labelList.Count; i * LineHeight < Height; i++)\r
194             {\r
195                 CreateGroupingComponents(i);\r
196                 CreateRepairLabels(i);\r
197                 CreateShipLabels(i);\r
198             }\r
199             for (var i = 0; i * LineHeight < Height; i++)\r
200             {\r
201                 _labelPanelList[i].Visible = InShipStatus(_mode);\r
202                 _groupingPanelList[i].Visible = _mode == "分類";\r
203                 _repairPanelList[i].Visible = _mode == "修復";\r
204             }\r
205             SetupScrollBar();\r
206         }\r
207 \r
208         private void SetupScrollBar()\r
209         {\r
210             var needBar = _shipList.Length * LineHeight * ShipLabel.ScaleFactor.Height > Height;\r
211             if (!needBar)\r
212             {\r
213                 ScrollBar.Visible = false;\r
214                 ScrollBar.Value = 0;\r
215                 return;\r
216             }\r
217             ScrollBar.Visible = true;\r
218             ScrollBar.Minimum = 0;\r
219             var lines = Max(1, Height / (int)Round(LineHeight * ShipLabel.ScaleFactor.Height));\r
220             var max = _shipList.Length - lines;\r
221             ScrollBar.LargeChange = Min(lines, max);\r
222             ScrollBar.Maximum =\r
223                 Max(0, max + ScrollBar.LargeChange - 1); // ScrollBarを最大まで動かしてもmaxには届かない\r
224             ScrollBar.Value = Min(ScrollBar.Value, max);\r
225         }\r
226 \r
227         private void CreateGroupingComponents(int i)\r
228         {\r
229             var y = 3 + LineHeight * i;\r
230             var cfgp = new Panel\r
231             {\r
232                 Location = new Point(0, y - 2),\r
233                 Size = new Size(ListForm.PanelWidth, LineHeight - 1),\r
234                 BackColor = ShipLabels.ColumnColors[(i + 1) % 2],\r
235             };\r
236             cfgp.Scale(ShipLabel.ScaleFactor);\r
237             cfgp.Tag = cfgp.Location.Y;\r
238             var cfgl = new[]\r
239             {\r
240                 new ShipLabel\r
241                 {\r
242                     Location = new Point(91, 2),\r
243                     Size = new Size(23, LabelHeight),\r
244                     TextAlign = ContentAlignment.MiddleRight\r
245                 },\r
246                 new ShipLabel {Location = new Point(10, 2), AutoSize = true},\r
247                 new ShipLabel {Location = new Point(1, 2), AutoSize = true}\r
248             };\r
249 \r
250             var cb = new CheckBox[GroupCount];\r
251             for (var j = 0; j < cb.Length; j++)\r
252             {\r
253                 cb[j] = new CheckBox\r
254                 {\r
255                     Location = new Point(125 + j * 24, 2),\r
256                     FlatStyle = FlatStyle.Flat,\r
257                     Size = new Size(12, 11),\r
258                     Tag = i * 10 + j\r
259                 };\r
260                 cb[j].Scale(ShipLabel.ScaleFactor);\r
261                 cb[j].CheckedChanged += checkboxGroup_CheckedChanged;\r
262             }\r
263             _groupingLabelList.Add(cfgl);\r
264             _checkBoxesList.Add(cb);\r
265             _groupingPanelList.Add(cfgp);\r
266             // ReSharper disable CoVariantArrayConversion\r
267             cfgp.Controls.AddRange(cfgl);\r
268             cfgp.Controls.AddRange(cb);\r
269             // ReSharper restore CoVariantArrayConversion\r
270             Controls.Add(cfgp);\r
271             foreach (var label in cfgl)\r
272             {\r
273                 label.Scale();\r
274                 label.PresetColor =\r
275                     label.BackColor = ShipLabels.ColumnColors[(i + 1) % 2];\r
276             }\r
277         }\r
278 \r
279         private void checkboxGroup_CheckedChanged(object sender, EventArgs e)\r
280         {\r
281             var cb = (CheckBox)sender;\r
282             var group = (int)cb.Tag % 10;\r
283             var idx = (int)cb.Tag / 10;\r
284             if (cb.Checked)\r
285             {\r
286                 GroupSettings[group].Add(_shipList[idx + ScrollBar.Value].Id);\r
287             }\r
288             else\r
289             {\r
290                 GroupSettings[group].Remove(_shipList[idx + ScrollBar.Value].Id);\r
291             }\r
292         }\r
293 \r
294         private void CreateRepairLabels(int i)\r
295         {\r
296             var y = 3 + LineHeight * i;\r
297             const int height = LabelHeight;\r
298             var rpp = new Panel\r
299             {\r
300                 Location = new Point(0, y - 2),\r
301                 Size = new Size(ListForm.PanelWidth, LineHeight - 1),\r
302                 BackColor = ShipLabels.ColumnColors[(i + 1) % 2],\r
303             };\r
304             rpp.Scale(ShipLabel.ScaleFactor);\r
305             rpp.Tag = rpp.Location.Y;\r
306             var rpl = new[]\r
307             {\r
308                 new ShipLabel {Location = new Point(118, 2), AutoSize = true, AnchorRight = true},\r
309                 new ShipLabel\r
310                 {\r
311                     Location = new Point(117, 2),\r
312                     Size = new Size(23, height),\r
313                     TextAlign = ContentAlignment.MiddleRight\r
314                 },\r
315                 new ShipLabel {Location = new Point(141, 2), AutoSize = true},\r
316                 new ShipLabel {Location = new Point(186, 2), AutoSize = true},\r
317                 new ShipLabel {Location = new Point(10, 2), AutoSize = true},\r
318                 new ShipLabel {Location = new Point(1, 2), AutoSize = true}\r
319             };\r
320             _repairLabelList.Add(rpl);\r
321             _repairPanelList.Add(rpp);\r
322             // ReSharper disable once CoVariantArrayConversion\r
323             rpp.Controls.AddRange(rpl);\r
324             Controls.Add(rpp);\r
325             foreach (var label in rpl)\r
326             {\r
327                 label.Scale();\r
328                 label.PresetColor =\r
329                     label.BackColor = ShipLabels.ColumnColors[(i + 1) % 2];\r
330             }\r
331         }\r
332 \r
333         private void CreateShipLabels(int i)\r
334         {\r
335             var y = 3 + LineHeight * i;\r
336             const int height = LabelHeight;\r
337             var lbp = new Panel\r
338             {\r
339                 Location = new Point(0, y - 2),\r
340                 Size = new Size(ListForm.PanelWidth, LineHeight - 1),\r
341                 BackColor = ShipLabels.ColumnColors[(i + 1) % 2]\r
342             };\r
343             lbp.Scale(ShipLabel.ScaleFactor);\r
344             var labels = new[]\r
345             {\r
346                 new ShipLabel {Location = new Point(126, 2), AutoSize = true, AnchorRight = true},\r
347                 new ShipLabel\r
348                 {\r
349                     Location = new Point(129, 2),\r
350                     Size = new Size(23, height),\r
351                     TextAlign = ContentAlignment.MiddleRight\r
352                 },\r
353                 new ShipLabel\r
354                 {\r
355                     Location = new Point(155, 2),\r
356                     Size = new Size(23, height),\r
357                     TextAlign = ContentAlignment.MiddleRight\r
358                 },\r
359                 new ShipLabel\r
360                 {\r
361                     Location = new Point(176, 2),\r
362                     Size = new Size(41, height),\r
363                     TextAlign = ContentAlignment.MiddleRight\r
364                 },\r
365                 new ShipLabel {Location = new Point(10, 2), AutoSize = true},\r
366                 new ShipLabel {Location = new Point(1, 2), AutoSize = true}\r
367             };\r
368             _labelList.Add(labels);\r
369             _labelPanelList.Add(lbp);\r
370             // ReSharper disable once CoVariantArrayConversion\r
371             lbp.Controls.AddRange(labels);\r
372             Controls.Add(lbp);\r
373             foreach (var label in labels)\r
374             {\r
375                 label.Scale();\r
376                 label.PresetColor =\r
377                     label.BackColor = ShipLabels.ColumnColors[(i + 1) % 2];\r
378             }\r
379         }\r
380 \r
381         private void SetShipLabels()\r
382         {\r
383             for (var i = 0; i < (Height + LineHeight - 1) / LineHeight; i++)\r
384             {\r
385                 if (InShipStatus(_mode))\r
386                     SetShipStatus(i);\r
387                 if (_mode == "分類")\r
388                     SetGrouping(i);\r
389                 if (_mode == "修復")\r
390                     SetRepairList(i);\r
391             }\r
392         }\r
393 \r
394         private bool InShipStatus(string mode) => Array.Exists(new[] {"全員", "A", "B", "C", "D"}, x => mode == x);\r
395 \r
396         private void SetShipStatus(int i)\r
397         {\r
398             var panel = _labelPanelList[i];\r
399             if (i + ScrollBar.Value >= _shipList.Length)\r
400             {\r
401                 panel.Visible = false;\r
402                 return;\r
403             }\r
404             var s = _shipList[i + ScrollBar.Value];\r
405             var labels = _labelList[i];\r
406             if (s.Level == 1000) // 艦種の表示\r
407             {\r
408                 SetShipType(i);\r
409                 return;\r
410             }\r
411             labels[0].SetHp(s);\r
412             labels[1].SetCond(s);\r
413             labels[2].SetLevel(s);\r
414             labels[3].SetExpToNext(s);\r
415             labels[4].SetName(s, ShipNameWidth.ShipList);\r
416             labels[5].SetFleet(s);\r
417             panel.Visible = true;\r
418         }\r
419 \r
420         private void SetShipType(int i)\r
421         {\r
422             var s = _shipList[i + ScrollBar.Value];\r
423             var labels = _labelList[i];\r
424             for (var c = 0; c < 4; c++)\r
425             {\r
426                 labels[c].Text = "";\r
427                 labels[c].BackColor = labels[c].PresetColor;\r
428             }\r
429             labels[4].SetName("");\r
430             labels[5].Text = s.Name;\r
431             _labelPanelList[i].Visible = true;\r
432         }\r
433 \r
434         private void SetGrouping(int i)\r
435         {\r
436             var panel = _groupingPanelList[i];\r
437             if (i + ScrollBar.Value >= _shipList.Length)\r
438             {\r
439                 panel.Visible = false;\r
440                 _labelPanelList[i].Visible = false;\r
441                 return;\r
442             }\r
443             var s = _shipList[i + ScrollBar.Value];\r
444             var labels = _groupingLabelList[i];\r
445             if (s.Level == 1000)\r
446             {\r
447                 panel.Visible = false;\r
448                 SetShipType(i);\r
449                 return;\r
450             }\r
451             labels[0].SetLevel(s);\r
452             labels[1].SetName(s, ShipNameWidth.GroupConfig);\r
453             labels[2].SetFleet(s);\r
454             var cb = _checkBoxesList[i];\r
455             for (var j = 0; j < cb.Length; j++)\r
456                 cb[j].Checked = GroupSettings[j].Contains(s.Id);\r
457             panel.Visible = true;\r
458         }\r
459 \r
460         private void SetRepairList(int i)\r
461         {\r
462             var panel = _repairPanelList[i];\r
463             if (i + ScrollBar.Value >= _shipList.Length)\r
464             {\r
465                 panel.Visible = false;\r
466                 _labelPanelList[i].Visible = false;\r
467                 return;\r
468             }\r
469             var s = _shipList[i + ScrollBar.Value];\r
470             if (s.Level == 1000)\r
471             {\r
472                 panel.Visible = false;\r
473                 SetShipType(i);\r
474                 return;\r
475             }\r
476             var rpl = _repairLabelList[i];\r
477             rpl[0].SetHp(s);\r
478             rpl[1].SetLevel(s);\r
479             rpl[2].SetRepairTime(s);\r
480             rpl[3].Text = TimeSpan.FromSeconds(s.RepairSecPerHp).ToString(@"mm\:ss");\r
481             rpl[4].SetName(s, ShipNameWidth.RepairListFull);\r
482             rpl[5].SetFleet(s);\r
483             panel.Visible = true;\r
484         }\r
485 \r
486         public void ShowShip(int id)\r
487         {\r
488             var i = Array.FindIndex(_shipList, s => s.Id == id);\r
489             if (i == -1)\r
490                 return;\r
491             ScrollBar.Value = Min(i, ScrollBar.Maximum + 1 - ScrollBar.LargeChange);\r
492             SetShipLabels();\r
493         }\r
494     }\r
495 }