OSDN Git Service

エラーメッセージのword wrapを止める
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / SwipeScrollify.cs
1 // Copyright (C) 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 //\r
3 // This program is part of KancolleSniffer.\r
4 //\r
5 // KancolleSniffer is free software: you can redistribute it and/or modify\r
6 // it under the terms of the GNU General Public License as published by\r
7 // the Free Software Foundation, either version 3 of the License, or\r
8 // (at your option) any later version.\r
9 //\r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 //\r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, see <http://www.gnu.org/licenses/>.\r
17 \r
18 using System;\r
19 using System.Drawing;\r
20 using System.Runtime.InteropServices;\r
21 using System.Windows.Forms;\r
22 using static System.Math;\r
23 \r
24 namespace KancolleSniffer\r
25 {\r
26     public class SwipeScrollify\r
27     {\r
28         private readonly MouseFilter _filter;\r
29 \r
30         public SwipeScrollify()\r
31         {\r
32             _filter = new MouseFilter();\r
33             Application.AddMessageFilter(_filter);\r
34         }\r
35 \r
36         public void AddPanel(Panel panel)\r
37         {\r
38             var handler = new PanelHandler(panel);\r
39             _filter.MouseDown += handler.MouseDown;\r
40             _filter.MouseMove += handler.MouseMove;\r
41             _filter.MouseUp += handler.MouseUp;\r
42         }\r
43 \r
44         public void AddTreeView(TreeView treeView)\r
45         {\r
46             var handler = new TreeViewHandler(treeView);\r
47             _filter.MouseDown += handler.MouseDown;\r
48             _filter.MouseMove += handler.MouseMove;\r
49             _filter.MouseUp += handler.MouseUp;\r
50         }\r
51 \r
52         private class MouseFilter : IMessageFilter\r
53         {\r
54             public delegate void MouseHandler(IntPtr handle, ref bool handled);\r
55 \r
56             public event MouseHandler MouseMove , MouseDown , MouseUp;\r
57 \r
58             // ReSharper disable InconsistentNaming\r
59             private const int WM_MOUSEMOVE = 0x0200;\r
60             private const int WM_LBUTTONDOWN = 0x0201;\r
61             private const int WM_LBUTTONUP = 0x0202;\r
62             // ReSharper restore InconsistentNaming\r
63 \r
64             public bool PreFilterMessage(ref Message m)\r
65             {\r
66                 var handled = false;\r
67                 switch (m.Msg)\r
68                 {\r
69                     case WM_LBUTTONDOWN:\r
70                         MouseDown?.Invoke(m.HWnd, ref handled);\r
71                         break;\r
72                     case WM_MOUSEMOVE:\r
73                         MouseMove?.Invoke(m.HWnd, ref handled);\r
74                         break;\r
75                     case WM_LBUTTONUP:\r
76                         MouseUp?.Invoke(m.HWnd, ref handled);\r
77                         break;\r
78                 }\r
79                 return handled;\r
80             }\r
81         }\r
82 \r
83         private class PanelHandler\r
84         {\r
85             private readonly Panel _panel;\r
86             private bool _touch;\r
87             private Point _mouseStart;\r
88             private Point _panelStart;\r
89 \r
90             public PanelHandler(Panel panel)\r
91             {\r
92                 _panel = panel;\r
93             }\r
94 \r
95             public void MouseDown(IntPtr handle, ref bool handled)\r
96             {\r
97                 if (!_mouseStart.IsEmpty)\r
98                     return;\r
99                 if (!_panel.RectangleToScreen(_panel.ClientRectangle).Contains(Control.MousePosition))\r
100                     return;\r
101                 var found = false;\r
102                 for (var control = Control.FromHandle(handle); control != null; control = control.Parent)\r
103                 {\r
104                     if (control != _panel)\r
105                         continue;\r
106                     found = true;\r
107                     break;\r
108                 }\r
109                 if (!found)\r
110                     return;\r
111                 _mouseStart = Control.MousePosition;\r
112                 _panelStart = _panel.AutoScrollPosition;\r
113             }\r
114 \r
115             public void MouseMove(IntPtr handle, ref bool handled)\r
116             {\r
117                 if (_mouseStart.IsEmpty)\r
118                     return;\r
119                 var cur = Control.MousePosition;\r
120                 var dx = cur.X - _mouseStart.X;\r
121                 var dy = cur.Y - _mouseStart.Y;\r
122                 if (_touch)\r
123                     _panel.AutoScrollPosition = new Point(-_panelStart.X - dx, -_panelStart.Y - dy);\r
124                 else if (Abs(dx) > 5 || Abs(dy) > 5)\r
125                     _touch = true;\r
126             }\r
127 \r
128             public void MouseUp(IntPtr handle, ref bool handled)\r
129             {\r
130                 if (_touch && !_panelStart.IsEmpty && _panelStart != _panel.AutoScrollPosition)\r
131                     handled = true;\r
132                 _touch = false;\r
133                 _mouseStart = _panelStart = Point.Empty;\r
134             }\r
135         }\r
136 \r
137         private class TreeViewHandler\r
138         {\r
139             private readonly TreeView _treeView;\r
140             private bool _touch;\r
141             private Point _mouseStart;\r
142             private Point _panelStart;\r
143 \r
144             [DllImport("user32.dll")]\r
145             private static extern int GetScrollPos(IntPtr hWnd, int nBar);\r
146 \r
147             [DllImport("user32.dll")]\r
148             private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);\r
149 \r
150             // ReSharper disable InconsistentNaming\r
151             private const int GWL_STYLE = -16;\r
152             private const int WS_HSCROLL = 0x00100000;\r
153             private const int WS_VSCROLL = 0x00200000;\r
154             // ReSharper restore InconsistentNaming\r
155 \r
156             [DllImport("user32.dll")]\r
157             private static extern int GetWindowLong(IntPtr hWnd, int nIndex);\r
158 \r
159             public TreeViewHandler(TreeView treeView)\r
160             {\r
161                 _treeView = treeView;\r
162             }\r
163 \r
164             public void MouseDown(IntPtr handle, ref bool handled)\r
165             {\r
166                 if (!_mouseStart.IsEmpty)\r
167                     return;\r
168                 var control = Control.FromHandle(handle);\r
169                 if (control == null || control != _treeView)\r
170                     return;\r
171                 if (!_treeView.RectangleToScreen(_treeView.ClientRectangle).Contains(Control.MousePosition))\r
172                     return;\r
173                 var loc = _treeView.HitTest(_treeView.PointToClient(Control.MousePosition)).Location;\r
174                 // アイテムをクリックするとWM_LBUTTONUPが来ないので避ける\r
175                 if (loc == TreeViewHitTestLocations.Label || loc == TreeViewHitTestLocations.Image)\r
176                     return;\r
177                 _mouseStart = Control.MousePosition;\r
178                 _panelStart = ScrollPosition();\r
179             }\r
180 \r
181             public void MouseMove(IntPtr handle, ref bool handled)\r
182             {\r
183                 if (_mouseStart.IsEmpty)\r
184                     return;\r
185                 if (!_treeView.RectangleToScreen(_treeView.ClientRectangle).Contains(Control.MousePosition))\r
186                 {\r
187                     // TreeViewではうまく動かないので外に出たら止める\r
188                     _touch = false;\r
189                     _mouseStart = _panelStart = Point.Empty;\r
190                     return;\r
191                 }\r
192                 var cur = Control.MousePosition;\r
193                 var dx = cur.X - _mouseStart.X;\r
194                 var dy = cur.Y - _mouseStart.Y;\r
195                 var style = GetWindowLong(_treeView.Handle, GWL_STYLE);\r
196                 if (_touch)\r
197                 {\r
198                     _treeView.BeginUpdate();\r
199                     if ((style & WS_HSCROLL) != 0)\r
200                         SetScrollPos(_treeView.Handle, 0, _panelStart.X - dx, true);\r
201                     if ((style & WS_VSCROLL) != 0)\r
202                         SetScrollPos(_treeView.Handle, 1, _panelStart.Y - dy / _treeView.ItemHeight, true);\r
203                     _treeView.EndUpdate();\r
204                     handled = true;\r
205                 }\r
206                 else if (Abs(dx) > 5 || Abs(dy) > 5)\r
207                     _touch = true;\r
208             }\r
209 \r
210             public void MouseUp(IntPtr handle, ref bool handled)\r
211             {\r
212                 if (_touch && !_panelStart.IsEmpty && _panelStart != ScrollPosition())\r
213                     handled = true;\r
214                 _touch = false;\r
215                 _mouseStart = _panelStart = Point.Empty;\r
216             }\r
217 \r
218             private Point ScrollPosition()\r
219             {\r
220                 return new Point(GetScrollPos(_treeView.Handle, 0), GetScrollPos(_treeView.Handle, 1));\r
221             }\r
222         }\r
223     }\r
224 }