OSDN Git Service

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