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