OSDN Git Service

60b4e0c4e1f6d5cd560075c57af43c4b6dff014b
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / ShipLabels.cs
1 // Copyright (C) 2014, 2015 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.Drawing;\r
17 using System.Linq;\r
18 using System.Text.RegularExpressions;\r
19 using System.Windows.Forms;\r
20 using static System.Math;\r
21 \r
22 namespace KancolleSniffer\r
23 {\r
24     public enum ShipNameWidth\r
25     {\r
26         MainPanel = 93,\r
27         AkashiTimer = 53,\r
28         NDock = 69,\r
29         RepairList = NDock,\r
30         RepairListFull = 75,\r
31         ShipList = 82,\r
32         GroupConfig = 82,\r
33         Max = int.MaxValue\r
34     }\r
35 \r
36     public class ShipLabels\r
37     {\r
38         private readonly ShipLabel[][] _labels = new ShipLabel[ShipInfo.MemberCount][];\r
39         private readonly ShipLabel[][] _combinedLabels = new ShipLabel[ShipInfo.MemberCount * 2][];\r
40         private readonly ShipLabel[] _akashiTimers = new ShipLabel[ShipInfo.MemberCount];\r
41         private readonly ShipLabel[][] _repairList = new ShipLabel[16][];\r
42         private Control _panelRepairList;\r
43         private readonly ShipLabel[][] _ndockLabels = new ShipLabel[DockInfo.DockCount][];\r
44         public static Color[] ColumnColors = {SystemColors.Control, Color.FromArgb(255, 250, 250, 250)};\r
45 \r
46         public void CreateShipLabels(Control parent, EventHandler onClick)\r
47         {\r
48             parent.SuspendLayout();\r
49             const int top = 3, height = 12, lh = 16;\r
50             ShipLabel[] headings;\r
51             parent.Controls.AddRange(headings = new[]\r
52             {\r
53                 new ShipLabel {Location = new Point(109, top), Text = "HP", AutoSize = true},\r
54                 new ShipLabel {Location = new Point(128, top), Text = "cond", AutoSize = true},\r
55                 new ShipLabel {Location = new Point(163, top), Text = "Lv", AutoSize = true},\r
56                 new ShipLabel {Location = new Point(194, top), Text = "Exp", AutoSize = true},\r
57                 new ShipLabel {Location = new Point(0, 1), Size = new Size(parent.Width, lh - 1)}\r
58             });\r
59             foreach (var label in headings)\r
60             {\r
61                 label.Scale();\r
62                 label.BackColor = ColumnColors[1];\r
63             }\r
64             for (var i = 0; i < _labels.Length; i++)\r
65             {\r
66                 var y = top + lh * (i + 1);\r
67                 parent.Controls.AddRange(_labels[i] = new[]\r
68                 {\r
69                     new ShipLabel {Location = new Point(129, y), AutoSize = true, AnchorRight = true},\r
70                     new ShipLabel\r
71                     {\r
72                         Location = new Point(132, y),\r
73                         Size = new Size(23, height),\r
74                         TextAlign = ContentAlignment.MiddleRight\r
75                     },\r
76                     new ShipLabel\r
77                     {\r
78                         Location = new Point(157, y),\r
79                         Size = new Size(23, height),\r
80                         TextAlign = ContentAlignment.MiddleRight\r
81                     },\r
82                     new ShipLabel\r
83                     {\r
84                         Location = new Point(177, y),\r
85                         Size = new Size(41, height),\r
86                         TextAlign = ContentAlignment.MiddleRight\r
87                     },\r
88                     new ShipLabel {Location = new Point(2, y), AutoSize = true}, // 名前のZ-orderを下に\r
89                     new ShipLabel {Location = new Point(0, y - 2), Size = new Size(parent.Width, lh - 1)}\r
90                 });\r
91                 foreach (var label in _labels[i])\r
92                 {\r
93                     label.Scale();\r
94                     label.PresetColor = label.BackColor = ColumnColors[i % 2];\r
95                     label.Tag = i;\r
96                     label.Click += onClick;\r
97                 }\r
98             }\r
99             parent.ResumeLayout();\r
100         }\r
101 \r
102         public void SetShipLabels(ShipStatus[] statuses)\r
103         {\r
104             for (var i = 0; i < _labels.Length; i++)\r
105             {\r
106                 var labels = _labels[i];\r
107                 if (i < statuses.Length)\r
108                 {\r
109                     var s = statuses[i];\r
110                     labels[0].SetHp(s);\r
111                     labels[1].SetCond(s);\r
112                     labels[2].SetLevel(s);\r
113                     labels[3].SetExpToNext(s);\r
114                     labels[4].SetName(s, ShipNameWidth.MainPanel);\r
115                 }\r
116                 else\r
117                 {\r
118                     labels[0].Text = labels[1].Text = labels[2].Text = labels[3].Text = "";\r
119                     labels[4].SetName("");\r
120                     labels[0].BackColor = labels[1].BackColor = labels[0].PresetColor;\r
121                 }\r
122             }\r
123         }\r
124 \r
125         public void CreateCombinedShipLabels(Control parent, EventHandler onClick)\r
126         {\r
127             parent.SuspendLayout();\r
128             const int top = 3, height = 12, lh = 16;\r
129             const int parentWidth = 220; // parent.Widthを使うとDPIスケーリング時に計算がくるうので\r
130             ShipLabel[] headings;\r
131             parent.Controls.AddRange(headings = new[]\r
132             {\r
133                 new ShipLabel {Location = new Point(68, top), Text = "HP", AutoSize = true},\r
134                 new ShipLabel {Location = new Point(86, top), Text = "cnd", AutoSize = true},\r
135                 new ShipLabel {Location = new Point(177, top), Text = "HP", AutoSize = true},\r
136                 new ShipLabel {Location = new Point(195, top), Text = "cnd", AutoSize = true},\r
137                 new ShipLabel {Location = new Point(0, 1), Size = new Size(parentWidth, lh - 1)}\r
138             });\r
139             foreach (var label in headings)\r
140             {\r
141                 label.Scale();\r
142                 label.BackColor = ColumnColors[1];\r
143             }\r
144             for (var i = 0; i < _combinedLabels.Length; i++)\r
145             {\r
146                 var x = parentWidth / 2 * (i / ShipInfo.MemberCount);\r
147                 var y = top + lh * ((i % ShipInfo.MemberCount) + 1);\r
148                 parent.Controls.AddRange(_combinedLabels[i] = new[]\r
149                 {\r
150                     new ShipLabel {Location = new Point(x + 88, y), AutoSize = true, AnchorRight = true},\r
151                     new ShipLabel\r
152                     {\r
153                         Location = new Point(x + 86, y),\r
154                         Size = new Size(23, height),\r
155                         TextAlign = ContentAlignment.MiddleRight\r
156                     },\r
157                     new ShipLabel {Location = new Point(x + 2, y), AutoSize = true}, // 名前のZ-orderを下に\r
158                     new ShipLabel {Location = new Point(x, y - 2), Size = new Size(parentWidth / 2, lh - 1)}\r
159                 });\r
160                 foreach (var label in _combinedLabels[i])\r
161                 {\r
162                     label.Scale();\r
163                     label.PresetColor = label.BackColor = ColumnColors[i % 2];\r
164                     label.Tag = i;\r
165                     label.Click += onClick;\r
166                 }\r
167             }\r
168             parent.ResumeLayout();\r
169         }\r
170 \r
171         public void SetCombinedShipLabels(ShipStatus[] first, ShipStatus[] second)\r
172         {\r
173             for (var i = 0; i < _combinedLabels.Length; i++)\r
174             {\r
175                 var idx = i % ShipInfo.MemberCount;\r
176                 var statuses = i < ShipInfo.MemberCount ? first : second;\r
177                 var labels = _combinedLabels[i];\r
178                 if (idx < statuses.Length)\r
179                 {\r
180                     var s = statuses[idx];\r
181                     labels[0].SetHp(s);\r
182                     labels[1].SetCond(s);\r
183                     labels[2].SetName(s);\r
184                 }\r
185                 else\r
186                 {\r
187                     labels[0].Text = labels[1].Text = "";\r
188                     labels[2].SetName("");\r
189                     labels[0].BackColor = labels[1].BackColor = labels[0].PresetColor;\r
190                 }\r
191             }\r
192         }\r
193 \r
194         public void CreateAkashiTimers(Control parent)\r
195         {\r
196             parent.SuspendLayout();\r
197             for (var i = 0; i < _akashiTimers.Length; i++)\r
198             {\r
199                 const int x = 55;\r
200                 var y = 3 + 16 * (i + 1);\r
201                 ShipLabel label;\r
202                 parent.Controls.Add(\r
203                     label = _akashiTimers[i] =\r
204                         new ShipLabel\r
205                         {\r
206                             Location = new Point(x, y),\r
207                             Size = new Size(34, 12),\r
208                             TextAlign = ContentAlignment.TopRight\r
209                         });\r
210                 label.BackColor = ColumnColors[i % 2];\r
211             }\r
212             foreach (var label in _akashiTimers)\r
213                 label.Scale();\r
214             parent.ResumeLayout();\r
215         }\r
216 \r
217         public void SetAkashiTimer(ShipStatus[] statuses, AkashiTimer.RepairSpan[] timers)\r
218         {\r
219             var shortest = -1;\r
220             for (var i = 0; i < timers.Length; i++)\r
221             {\r
222                 if (timers[i].Span <= TimeSpan.Zero)\r
223                     continue;\r
224                 if (shortest == -1 || timers[i].Span < timers[shortest].Span)\r
225                     shortest = i;\r
226             }\r
227             for (var i = 0; i < _akashiTimers.Length; i++)\r
228             {\r
229                 var label = _akashiTimers[i];\r
230                 var labelHp = _labels[i][0];\r
231                 var labelName = _labels[i][4];\r
232                 if (i >= timers.Length || timers[i].Span == TimeSpan.MinValue)\r
233                 {\r
234                     label.Visible = false;\r
235                     labelHp.ForeColor = Control.DefaultForeColor;\r
236                     continue;\r
237                 }\r
238                 var timer = timers[i];\r
239                 var stat = statuses[i];\r
240                 label.Visible = true;\r
241                 label.Text = timer.Span.ToString(@"mm\:ss");\r
242                 label.ForeColor = Control.DefaultForeColor;\r
243                 labelName.SetName(stat, ShipNameWidth.AkashiTimer);\r
244                 if (timer.Diff == 0)\r
245                 {\r
246                     labelHp.ForeColor = Control.DefaultForeColor;\r
247                     continue;\r
248                 }\r
249                 if (i == shortest)\r
250                     label.ForeColor = Color.Red;\r
251                 labelHp.ForeColor = Color.DimGray;\r
252                 labelHp.SetHp(stat.NowHp + timer.Diff, stat.MaxHp);\r
253             }\r
254         }\r
255 \r
256         public void CreateRepairList(Control parent, EventHandler onClick)\r
257         {\r
258             parent.SuspendLayout();\r
259             for (var i = 0; i < _repairList.Length; i++)\r
260             {\r
261                 var y = 3 + i * 16;\r
262                 const int height = 12;\r
263                 parent.Controls.AddRange(_repairList[i] = new[]\r
264                 {\r
265                     new ShipLabel {Location = new Point(0, y), Size = new Size(11, height)},\r
266                     new ShipLabel {Location = new Point(119, y), Size = new Size(5, height - 1)},\r
267                     new ShipLabel {Location = new Point(75, y), AutoSize = true},\r
268                     new ShipLabel {Location = new Point(9, y), AutoSize = true},\r
269                     new ShipLabel {Location = new Point(0, y - 2), Size = new Size(parent.Width, height + 3)}\r
270                 });\r
271                 foreach (var label in _repairList[i])\r
272                 {\r
273                     label.Scale();\r
274                     label.PresetColor = label.BackColor = ColumnColors[(i + 1) % 2];\r
275                     label.Click += onClick;\r
276                 }\r
277             }\r
278             _panelRepairList = parent;\r
279             parent.ResumeLayout();\r
280         }\r
281 \r
282         public void SetRepairList(ShipStatus[] list)\r
283         {\r
284             const int fleet = 0, name = 3, time = 2, damage = 1;\r
285             var parent = _panelRepairList;\r
286             var num = Min(list.Length, _repairList.Length);\r
287             if (num == 0)\r
288             {\r
289                 parent.Size = new Size(parent.Width, (int)Round(ShipLabel.ScaleFactor.Height * 19));\r
290                 var labels = _repairList[0];\r
291                 labels[fleet].Text = "";\r
292                 labels[name].SetName("なし");\r
293                 labels[time].Text = "";\r
294                 labels[damage].BackColor = labels[damage].PresetColor;\r
295                 return;\r
296             }\r
297             parent.Size = new Size(parent.Width, (int)Round(ShipLabel.ScaleFactor.Height * (num * 16 + 3)));\r
298             for (var i = 0; i < num; i++)\r
299             {\r
300                 var s = list[i];\r
301                 var labels = _repairList[i];\r
302                 labels[fleet].SetFleet(s);\r
303                 labels[name].SetName(s, ShipNameWidth.RepairList);\r
304                 labels[time].SetRepairTime(s);\r
305                 labels[damage].BackColor = ShipLabel.DamageColor(s, labels[damage].PresetColor);\r
306             }\r
307         }\r
308 \r
309         public void CreateNDockLabels(Control parent, EventHandler onClick)\r
310         {\r
311             for (var i = 0; i < _ndockLabels.Length; i++)\r
312             {\r
313                 var y = 3 + i * 15;\r
314                 parent.Controls.AddRange(\r
315                     _ndockLabels[i] = new[]\r
316                     {\r
317                         new ShipLabel\r
318                         {\r
319                             Location = new Point(138, y),\r
320                             AutoSize = true,\r
321                             AnchorRight = true\r
322                         },\r
323                         new ShipLabel {Location = new Point(29, y), AutoSize = true} // 名前のZ-orderを下に\r
324                     });\r
325                 foreach (var label in _ndockLabels[i])\r
326                 {\r
327                     label.Scale();\r
328                     label.Click += onClick;\r
329                 }\r
330             }\r
331         }\r
332 \r
333         public void SetNDockLabels(NameAndTimer[] ndock)\r
334         {\r
335             for (var i = 0; i < _ndockLabels.Length; i++)\r
336                 _ndockLabels[i][1].SetName("", ndock[i].Name, "", ShipNameWidth.NDock);\r
337         }\r
338 \r
339         public void SetNDockTimer(int dock, RingTimer timer, bool finishTime)\r
340         {\r
341             var label = _ndockLabels[dock][0];\r
342             label.ForeColor = timer.IsFinished ? Color.Red : Color.Black;\r
343             label.Text = timer.ToString(finishTime);\r
344         }\r
345     }\r
346 \r
347     [System.ComponentModel.DesignerCategory("Code")]\r
348     public class ShipLabel : Label\r
349     {\r
350         public static SizeF ScaleFactor { get; set; }\r
351         public static Font LatinFont { get; set; } = new Font("Tahoma", 8f);\r
352         public Color PresetColor { get; set; }\r
353         public bool AnchorRight { get; set; }\r
354         private int _right = int.MinValue;\r
355         private int _left;\r
356 \r
357         public ShipLabel()\r
358         {\r
359             UseMnemonic = false;\r
360         }\r
361 \r
362         public void SetName(ShipStatus status, ShipNameWidth width = ShipNameWidth.Max)\r
363         {\r
364             var empty = status.Id != -1 && status.Slot.All(e => e.Id == -1) ? "▫" : "";\r
365             var dc = status.PreparedDamageControl;\r
366             var dcname = dc == 42 ? "[ダ]" : dc == 43 ? "[メ]" : "";\r
367             SetName((status.Escaped ? "[避]" : dcname), status.Name, empty, width);\r
368         }\r
369 \r
370         public void SetName(string name)\r
371         {\r
372             SetName("", name, "");\r
373         }\r
374 \r
375         public void SetName(string prefix, string name, string suffix, ShipNameWidth width = ShipNameWidth.Max)\r
376         {\r
377             if (name == null)\r
378                 return;\r
379             var lu = new Regex(@"^\p{Lu}").IsMatch(name);\r
380             var shift = (int)Round(ScaleFactor.Height);\r
381             if (lu && Font.Equals(Parent.Font))\r
382             {\r
383                 Location += new Size(0, -shift);\r
384                 Font = LatinFont;\r
385             }\r
386             else if (!lu && !Font.Equals(Parent.Font))\r
387             {\r
388                 Location += new Size(0, shift);\r
389                 Font = Parent.Font;\r
390             }\r
391             var result = prefix + name + suffix;\r
392             var measured = TextRenderer.MeasureText(result, Font).Width;\r
393             if (measured <= (int)width)\r
394             {\r
395                 Text = result;\r
396                 return;\r
397             }\r
398             var truncated = "";\r
399             foreach (var ch in name)\r
400             {\r
401                 var tmp = truncated + ch;\r
402                 if (TextRenderer.MeasureText(tmp, Font).Width > (int)width)\r
403                     break;\r
404                 truncated = tmp;\r
405             }\r
406             Text = prefix + truncated.TrimEnd(' ') + suffix;\r
407         }\r
408 \r
409         public void SetHp(ShipStatus status)\r
410         {\r
411             Text = $"{status.NowHp:D}/{status.MaxHp:D}";\r
412             BackColor = DamageColor(status, PresetColor);\r
413         }\r
414 \r
415         public void SetHp(int now, int max)\r
416         {\r
417             SetHp(new ShipStatus {NowHp = now, MaxHp = max});\r
418         }\r
419 \r
420         public static Color DamageColor(ShipStatus status, Color backcolor)\r
421         {\r
422             switch (status.DamageLevel)\r
423             {\r
424                 case ShipStatus.Damage.Badly:\r
425                     return Color.Red;\r
426                 case ShipStatus.Damage.Half:\r
427                     return Color.Orange;\r
428                 case ShipStatus.Damage.Small:\r
429                     return Color.FromArgb(240, 240, 0);\r
430                 default:\r
431                     return backcolor;\r
432             }\r
433         }\r
434 \r
435         public void SetCond(ShipStatus status)\r
436         {\r
437             var cond = status.Cond;\r
438             Text = cond.ToString("D");\r
439             BackColor = cond >= 50\r
440                 ? Color.Yellow\r
441                 : cond >= 30\r
442                     ? PresetColor\r
443                     : cond >= 20 ? Color.Orange : Color.Red;\r
444         }\r
445 \r
446         public void SetLevel(ShipStatus status)\r
447         {\r
448             Text = status.Level.ToString("D");\r
449         }\r
450 \r
451         public void SetExpToNext(ShipStatus status)\r
452         {\r
453             Text = status.ExpToNext.ToString("D");\r
454         }\r
455 \r
456         public void SetRepairTime(ShipStatus status)\r
457         {\r
458             SetRepairTime(status.RepairTime);\r
459         }\r
460 \r
461         public void SetRepairTime(TimeSpan span)\r
462         {\r
463             Text = $@"{(int)span.TotalHours:d2}:{span:mm\:ss}";\r
464         }\r
465 \r
466         public void SetFleet(ShipStatus status)\r
467         {\r
468             Text = new[] {"", "1", "2", "3", "4"}[status.Fleet + 1];\r
469         }\r
470 \r
471         protected override void OnLayout(LayoutEventArgs levent)\r
472         {\r
473             base.OnLayout(levent);\r
474             if (!AnchorRight)\r
475                 return;\r
476             if (_right == int.MinValue || _left != Left)\r
477             {\r
478                 _right = Right;\r
479                 _left = Left;\r
480                 return;\r
481             }\r
482             if (_right == Right)\r
483                 return;\r
484             _left -= Right - _right;\r
485             Location = new Point(_left, Top);\r
486         }\r
487 \r
488         public void Scale()\r
489         {\r
490             Scale(ScaleFactor);\r
491         }\r
492     }\r
493 }