OSDN Git Service

連合艦隊表示でも艦娘名のクリックで一覧をジャンプできるようにする
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / View / ItemTreeView.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.Collections;\r
17 using System.Collections.Generic;\r
18 using System.Linq;\r
19 using System.Runtime.InteropServices;\r
20 using System.Windows.Forms;\r
21 using KancolleSniffer.Model;\r
22 \r
23 namespace KancolleSniffer.View\r
24 {\r
25     public class ItemTreeView : TreeView\r
26     {\r
27         private ItemStatus[] _prevItemList;\r
28 \r
29         public void SetNodes(ItemStatus[] itemList)\r
30         {\r
31             if (_prevItemList != null && _prevItemList.SequenceEqual(itemList, new ItemStatusComparer()))\r
32                 return;\r
33             _prevItemList = itemList.Select(CloneItemStatus).ToArray();\r
34             SetNodes(CreateItemNodes(itemList));\r
35         }\r
36 \r
37         private TreeNode CreateItemNodes(IEnumerable<ItemStatus> itemList)\r
38         {\r
39             var grouped = from item in itemList\r
40                 where !item.Spec.Empty\r
41                 orderby item.Spec.Type, item.Spec.Id, item.Alv, item.Level\r
42                 group item by item.Spec.Type\r
43                 into byTypeGroup\r
44                 from bySpec in (from item in byTypeGroup\r
45                     group item by item.Spec.Id\r
46                     into bySpecGroup\r
47                     from byParam in (from item in bySpecGroup\r
48                         group item by new {item.Alv, item.Level}\r
49                         into byParamGroup\r
50                         from byHolder in (from item in byParamGroup group item by item.Holder.Id)\r
51                         group byHolder by byParamGroup.Key)\r
52                     group byParam by bySpecGroup.Key)\r
53                 group bySpec by byTypeGroup.Key;\r
54 \r
55             var root = new TreeNode();\r
56             foreach (var byType in grouped)\r
57             {\r
58                 var typeName = byType.First().First().First().First().Spec.TypeName;\r
59                 var typeNode = new TreeNode();\r
60                 typeNode.Name = typeNode.Text = typeName;\r
61                 root.Nodes.Add(typeNode);\r
62                 foreach (var bySpec in byType)\r
63                 {\r
64                     var item = bySpec.First().First().First();\r
65                     var itemNode = new TreeNode();\r
66                     itemNode.Name = itemNode.Text = item.Spec.Name + "x" +\r
67                                                     bySpec.SelectMany(spec => spec).SelectMany(param => param).Count();\r
68                     typeNode.Nodes.Add(itemNode);\r
69                     foreach (var byParam in bySpec)\r
70                     {\r
71                         TreeNode paramNode;\r
72                         if (bySpec.Count() == 1 && byParam.Key.Alv == 0 && byParam.Key.Level == 0)\r
73                         {\r
74                             paramNode = itemNode;\r
75                         }\r
76                         else\r
77                         {\r
78                             paramNode = new TreeNode();\r
79                             item = byParam.First().First();\r
80                             paramNode.Name = paramNode.Text =\r
81                                 (item.Spec.IsAircraft ? "+" + item.Alv : "") + "★" + item.Level + "x" +\r
82                                 byParam.SelectMany(param => param).Count();\r
83                             itemNode.Nodes.Add(paramNode);\r
84                         }\r
85                         foreach (var byShip in byParam)\r
86                         {\r
87                             var ship = byShip.First().Holder;\r
88                             var name = ship.Empty\r
89                                 ? "未装備x" + byShip.Count()\r
90                                 : (ship.Fleet == null ? "" : ship.Fleet.Number + 1 + " ") +\r
91                                   ship.Name + (ship.Level > 0 ? "Lv" + ship.Level : "") + "x" + byShip.Count();\r
92                             paramNode.Nodes.Add(name, name);\r
93                         }\r
94                     }\r
95                 }\r
96             }\r
97             return root;\r
98         }\r
99 \r
100         private ItemStatus CloneItemStatus(ItemStatus org)\r
101         {\r
102             return new ItemStatus\r
103             {\r
104                 Level = org.Level,\r
105                 Spec = org.Spec,\r
106                 Holder = new ShipStatus {Id = org.Holder.Id, Fleet = org.Holder.Fleet}\r
107             };\r
108         }\r
109 \r
110         private class ItemStatusComparer : IEqualityComparer<ItemStatus>\r
111         {\r
112             public bool Equals(ItemStatus x, ItemStatus y)\r
113                 // ReSharper disable PossibleNullReferenceException\r
114                 => x.Level == y.Level && x.Spec == y.Spec && x.Holder.Id == y.Holder.Id &&\r
115                    x.Holder.Fleet == y.Holder.Fleet;\r
116             // ReSharper restore PossibleNullReferenceException\r
117 \r
118             public int GetHashCode(ItemStatus obj) => obj.Level + obj.Spec.GetHashCode() + obj.Holder.GetHashCode();\r
119         }\r
120 \r
121         [DllImport("user32.dll")]\r
122         private static extern int GetScrollPos(IntPtr hWnd, int nBar);\r
123 \r
124         [DllImport("user32.dll")]\r
125         private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);\r
126 \r
127         private void SetNodes(TreeNode root)\r
128         {\r
129             var y = GetScrollPos(Handle, 1);\r
130             BeginUpdate();\r
131             var save = SaveTreeViewState(Nodes);\r
132             Nodes.Clear();\r
133             foreach (TreeNode child in root.Nodes)\r
134                 Nodes.Add(child);\r
135             RestoreTreeViewState(Nodes, save.Nodes);\r
136             EndUpdate();\r
137             SetScrollPos(Handle, 1, y, true);\r
138         }\r
139 \r
140         private TreeNode SaveTreeViewState(IEnumerable nodes)\r
141         {\r
142             var result = new TreeNode();\r
143             foreach (TreeNode child in nodes)\r
144             {\r
145                 var copy = SaveTreeViewState(child.Nodes);\r
146                 if (child.IsExpanded)\r
147                     copy.Expand();\r
148                 copy.Name = child.Name;\r
149                 result.Nodes.Add(copy);\r
150             }\r
151             return result;\r
152         }\r
153 \r
154         private void RestoreTreeViewState(TreeNodeCollection dst, TreeNodeCollection src)\r
155         {\r
156             foreach (TreeNode d in dst)\r
157             {\r
158                 var s = src[d.Name];\r
159                 if (s == null)\r
160                     continue;\r
161                 if (s.IsExpanded)\r
162                     d.Expand();\r
163                 RestoreTreeViewState(d.Nodes, s.Nodes);\r
164             }\r
165         }\r
166 \r
167         // ReSharper disable InconsistentNaming\r
168         // ReSharper disable IdentifierTypo\r
169 \r
170         private const int TV_FIRST = 0x1100;\r
171 \r
172         private const int TVM_SETEXTENDEDSTYLE = TV_FIRST + 44;\r
173 \r
174         private const int TVS_EX_DOUBLEBUFFER = 0x0004;\r
175 \r
176         // ReSharper restore IdentifierTypo\r
177         // ReSharper restore InconsistentNaming\r
178 \r
179         [DllImport("user32.dll")]\r
180         private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);\r
181 \r
182         protected override void OnHandleCreated(EventArgs e)\r
183         {\r
184             base.OnHandleCreated(e);\r
185             // Enable double buffer\r
186             SendMessage(Handle, TVM_SETEXTENDEDSTYLE, (IntPtr)TVS_EX_DOUBLEBUFFER, (IntPtr)TVS_EX_DOUBLEBUFFER);\r
187         }\r
188     }\r
189 }