OSDN Git Service

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