OSDN Git Service

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