OSDN Git Service

装備の種別が不明でも例外にならないようにする
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / SwipeScrollify.cs
1 // Copyright (C) 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.Runtime.InteropServices;\r
18 using System.Windows.Forms;\r
19 using static System.Math;\r
20 \r
21 namespace KancolleSniffer\r
22 {\r
23     public class SwipeScrollify\r
24     {\r
25         private readonly MouseFilter _filter;\r
26 \r
27         public SwipeScrollify()\r
28         {\r
29             _filter = new MouseFilter();\r
30             Application.AddMessageFilter(_filter);\r
31         }\r
32 \r
33         public void AddPanel(Panel panel)\r
34         {\r
35             var handler = new PanelHandler(panel);\r
36             _filter.MouseDown += handler.MouseDown;\r
37             _filter.MouseMove += handler.MouseMove;\r
38             _filter.MouseUp += handler.MouseUp;\r
39         }\r
40 \r
41         public void AddTreeView(TreeView treeView)\r
42         {\r
43             var handler = new TreeViewHandler(treeView);\r
44             _filter.MouseDown += handler.MouseDown;\r
45             _filter.MouseMove += handler.MouseMove;\r
46             _filter.MouseUp += handler.MouseUp;\r
47         }\r
48 \r
49         private class MouseFilter : IMessageFilter\r
50         {\r
51             public delegate void MouseHandler(IntPtr handle, ref bool handled);\r
52 \r
53             public event MouseHandler MouseMove , MouseDown , MouseUp;\r
54 \r
55             // ReSharper disable InconsistentNaming\r
56             private const int WM_MOUSEMOVE = 0x0200;\r
57             private const int WM_LBUTTONDOWN = 0x0201;\r
58             private const int WM_LBUTTONUP = 0x0202;\r
59             // ReSharper restore InconsistentNaming\r
60 \r
61             public bool PreFilterMessage(ref Message m)\r
62             {\r
63                 var handled = false;\r
64                 switch (m.Msg)\r
65                 {\r
66                     case WM_LBUTTONDOWN:\r
67                         MouseDown?.Invoke(m.HWnd, ref handled);\r
68                         break;\r
69                     case WM_MOUSEMOVE:\r
70                         MouseMove?.Invoke(m.HWnd, ref handled);\r
71                         break;\r
72                     case WM_LBUTTONUP:\r
73                         MouseUp?.Invoke(m.HWnd, ref handled);\r
74                         break;\r
75                 }\r
76                 return handled;\r
77             }\r
78         }\r
79 \r
80         private class PanelHandler\r
81         {\r
82             private readonly Panel _panel;\r
83             private bool _touch;\r
84             private Point _mouseStart;\r
85             private Point _panelStart;\r
86             private Point _scrollStart;\r
87             private const int ScrollCount = 6;\r
88 \r
89             public PanelHandler(Panel panel)\r
90             {\r
91                 _panel = panel;\r
92             }\r
93 \r
94             public void MouseDown(IntPtr handle, ref bool handled)\r
95             {\r
96                 if (!_mouseStart.IsEmpty)\r
97                     return;\r
98                 if (!_panel.RectangleToScreen(_panel.ClientRectangle).Contains(Control.MousePosition))\r
99                     return;\r
100                 var found = false;\r
101                 for (var control = Control.FromHandle(handle); control != null; control = control.Parent)\r
102                 {\r
103                     if (control != _panel)\r
104                         continue;\r
105                     found = true;\r
106                     break;\r
107                 }\r
108                 if (!found)\r
109                     return;\r
110                 _mouseStart = _scrollStart = Control.MousePosition;\r
111                 _panelStart = _panel.AutoScrollPosition;\r
112             }\r
113 \r
114             public void MouseMove(IntPtr handle, ref bool handled)\r
115             {\r
116                 if (_mouseStart.IsEmpty)\r
117                     return;\r
118                 var cur = Control.MousePosition;\r
119                 var dx = cur.X - _mouseStart.X;\r
120                 var dy = cur.Y - _mouseStart.Y;\r
121                 if (!_touch)\r
122                 {\r
123                     if (!(Abs(dx) > ScrollCount || Abs(dy) > ScrollCount))\r
124                         return;\r
125                    _touch = true;\r
126                 }\r
127                 if (Abs(_scrollStart.X - cur.X) > ScrollCount || Abs(_scrollStart.Y - cur.Y) > ScrollCount)\r
128                 {\r
129                     _panel.AutoScrollPosition = new Point(-_panelStart.X - dx, -_panelStart.Y - dy);\r
130                     _scrollStart = cur;\r
131                 }\r
132             }\r
133 \r
134             public void MouseUp(IntPtr handle, ref bool handled)\r
135             {\r
136                 if (_touch && !_panelStart.IsEmpty && _panelStart != _panel.AutoScrollPosition)\r
137                     handled = true;\r
138                 _touch = false;\r
139                 _mouseStart = _panelStart = Point.Empty;\r
140             }\r
141         }\r
142 \r
143         private class TreeViewHandler\r
144         {\r
145             private readonly TreeView _treeView;\r
146             private bool _touch;\r
147             private Point _mouseStart;\r
148             private Point _panelStart;\r
149 \r
150             [DllImport("user32.dll")]\r
151             private static extern int GetScrollPos(IntPtr hWnd, int nBar);\r
152 \r
153             [DllImport("user32.dll")]\r
154             private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);\r
155 \r
156             // ReSharper disable InconsistentNaming\r
157             private const int GWL_STYLE = -16;\r
158             private const int WS_HSCROLL = 0x00100000;\r
159             private const int WS_VSCROLL = 0x00200000;\r
160             // ReSharper restore InconsistentNaming\r
161 \r
162             [DllImport("user32.dll")]\r
163             private static extern int GetWindowLong(IntPtr hWnd, int nIndex);\r
164 \r
165             public TreeViewHandler(TreeView treeView)\r
166             {\r
167                 _treeView = treeView;\r
168             }\r
169 \r
170             public void MouseDown(IntPtr handle, ref bool handled)\r
171             {\r
172                 if (!_mouseStart.IsEmpty)\r
173                     return;\r
174                 var control = Control.FromHandle(handle);\r
175                 if (control == null || control != _treeView)\r
176                     return;\r
177                 if (!_treeView.RectangleToScreen(_treeView.ClientRectangle).Contains(Control.MousePosition))\r
178                     return;\r
179                 var loc = _treeView.HitTest(_treeView.PointToClient(Control.MousePosition)).Location;\r
180                 // アイテムをクリックするとWM_LBUTTONUPが来ないので避ける\r
181                 if (loc == TreeViewHitTestLocations.Label || loc == TreeViewHitTestLocations.Image)\r
182                     return;\r
183                 _mouseStart = Control.MousePosition;\r
184                 _panelStart = ScrollPosition();\r
185             }\r
186 \r
187             public void MouseMove(IntPtr handle, ref bool handled)\r
188             {\r
189                 if (_mouseStart.IsEmpty)\r
190                     return;\r
191                 if (!_treeView.RectangleToScreen(_treeView.ClientRectangle).Contains(Control.MousePosition))\r
192                 {\r
193                     // TreeViewではうまく動かないので外に出たら止める\r
194                     _touch = false;\r
195                     _mouseStart = _panelStart = Point.Empty;\r
196                     return;\r
197                 }\r
198                 var cur = Control.MousePosition;\r
199                 var dx = cur.X - _mouseStart.X;\r
200                 var dy = cur.Y - _mouseStart.Y;\r
201                 var style = GetWindowLong(_treeView.Handle, GWL_STYLE);\r
202                 if (_touch)\r
203                 {\r
204                     _treeView.BeginUpdate();\r
205                     if ((style & WS_HSCROLL) != 0)\r
206                         SetScrollPos(_treeView.Handle, 0, _panelStart.X - dx, true);\r
207                     if ((style & WS_VSCROLL) != 0)\r
208                         SetScrollPos(_treeView.Handle, 1, _panelStart.Y - dy / _treeView.ItemHeight, true);\r
209                     _treeView.EndUpdate();\r
210                     handled = true;\r
211                 }\r
212                 else if (Abs(dx) > 5 || Abs(dy) > 5)\r
213                     _touch = true;\r
214             }\r
215 \r
216             public void MouseUp(IntPtr handle, ref bool handled)\r
217             {\r
218                 if (_touch && !_panelStart.IsEmpty && _panelStart != ScrollPosition())\r
219                     handled = true;\r
220                 _touch = false;\r
221                 _mouseStart = _panelStart = Point.Empty;\r
222             }\r
223 \r
224             private Point ScrollPosition()\r
225             {\r
226                 return new Point(GetScrollPos(_treeView.Handle, 0), GetScrollPos(_treeView.Handle, 1));\r
227             }\r
228         }\r
229     }\r
230 }