1 /* BDevices.hpp
2  * Copyright (C) 2019  Sven Jähnichen
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #ifndef BWIDGETS_BDEVICES_HPP_
19 #define BWIDGETS_BDEVICES_HPP_
20 
21 #include <set>
22 #include <list>
23 #include <chrono>
24 #include <algorithm>
25 #include "../BUtilities/Point.hpp"
26 
27 namespace BWidgets
28 {
29 class Widget;	// Forward declaration
30 }
31 
32 namespace BDevices
33 {
34 
35 /**
36  * Enumeration of mouse buttons as input device for event handling
37  */
38 enum ButtonCode
39 {
40 	NO_BUTTON	= 0,
41 	LEFT_BUTTON	= 1,
42 	MIDDLE_BUTTON	= 2,
43 	RIGHT_BUTTON	= 3,
44 	NR_OF_BUTTONS	= 4
45 };
46 
47 enum KeyCode {
48 	KEY_F1 = 0xE000,
49 	KEY_F2,
50 	KEY_F3,
51 	KEY_F4,
52 	KEY_F5,
53 	KEY_F6,
54 	KEY_F7,
55 	KEY_F8,
56 	KEY_F9,
57 	KEY_F10,
58 	KEY_F11,
59 	KEY_F12,
60 	KEY_LEFT,
61 	KEY_UP,
62 	KEY_RIGHT,
63 	KEY_DOWN,
64 	KEY_PAGE_UP,
65 	KEY_PAGE_DOWN,
66 	KEY_HOME,
67 	KEY_END,
68 	KEY_INSERT,
69 	KEY_SHIFT,
70 	KEY_CTRL,
71 	KEY_ALT,
72 	KEY_SUPER
73 };
74 
75 /**
76  * Class BDevices::DeviceGrab<T>
77  *
78  * Links a BWidgets::Widget to a std::set<T> of devices
79  */
80 template<typename T> class DeviceGrab
81 {
82 protected:
83 	BWidgets::Widget* widget_;
84 	std::set<T> devices_;
85 
86 public:
DeviceGrab()87 	DeviceGrab () : DeviceGrab (nullptr, std::set<T> {}) {}
DeviceGrab(BWidgets::Widget * widget)88 	DeviceGrab (BWidgets::Widget* widget) :
89 			DeviceGrab (widget, std::set<T> {}) {}
DeviceGrab(BWidgets::Widget * widget,const T & device)90 	DeviceGrab (BWidgets::Widget* widget, const T& device) :
91 			DeviceGrab (widget, std::set<T> {device}) {}
DeviceGrab(BWidgets::Widget * widget,const std::set<T> & devices)92 	DeviceGrab (BWidgets::Widget* widget, const std::set<T>& devices) :
93 			widget_ (widget), devices_ (devices) {}
94 
95 	/* Get (the pointer to) the widget of this DeviceGrab
96 	 * @return	Pointer to widget
97 	 */
getWidget() const98 	BWidgets::Widget* getWidget () const {return widget_;}
99 
100 	/* Gets the devices of this DeviceGrab
101 	 * @return	std::set<T> of devices
102 	 */
getDevices() const103 	std::set<T> getDevices () const {return devices_;}
104 
105 	/* Gets infomation whether this DeviceGrab contains a given device or
106 	 * not.
107 	 * @param device	<T> of device
108 	 * @return		True if device is in this DeviceGrab, otherwise
109 	 *			false
110 	 */
contains(const T & device) const111 	bool contains (const T& device) const
112 	{
113 		if (devices_.empty()) return true;	// Empty devices_ used as joker
114 		return (devices_.find (device) != devices_.end());
115 	}
116 
117 };
118 
119 template<typename T> class DeviceGrabStack : std::list<DeviceGrab<T>>
120 {
121 protected:
122 	std::list<DeviceGrab<T>> stack_;
123 
contains(BWidgets::Widget * widget)124 	bool contains (BWidgets::Widget* widget)
125 	{
126 		for (typename std::list<DeviceGrab<T>>::iterator it = stack_.begin(); it != stack_.end(); ++it)
127 		{
128 			DeviceGrab<T>& dg = *it;
129 			if (dg.getWidget() == widget) return true;
130 		}
131 		return false;
132 	}
133 
getDevices(BWidgets::Widget * widget)134 	std::set<T> getDevices (BWidgets::Widget* widget)
135 	{
136 		std::set<T> devices {};
137 		for (typename std::list<DeviceGrab<T>>::iterator it = stack_.begin(); it != stack_.end(); ++it)
138 		{
139 			DeviceGrab<T>& dg = *it;
140 			if (dg.getWidget() == widget)
141 			{
142 				std::set<T> d = dg.getDevices ();
143 				devices.insert (d.begin(), d.end());
144 			}
145 		}
146 
147 		return devices;
148 	}
149 
150 public:
151 	using std::list<DeviceGrab<T>>::clear;
152 
153 	/* Removes DeviceGrab devices from the stack. If the DeviceGrab devices
154 	 * are completely depleted, the empty DeviceGrab is removed from the
155 	 * stack.
156 	 * @param widget	All DeviceGrabs with this widget will be removed
157 	 *			from the stack.
158 	 * @param device 	Devices device from all DeviceGrabs of the stack
159 	 *			will be removed.
160 	 * @param devices	All given devices will be removed from all
161 	 *			DeviceGrabs of the stack.
162 	 * @param deviceGrab	For all DeviceGrabs of the stack that match with
163 	 *			deviceGrab.widget all deviceGrab.devices will be
164 	 *			removed.
165 	 */
remove(BWidgets::Widget * widget)166 	void remove (BWidgets::Widget* widget) {remove (DeviceGrab<T> (widget));}
remove(const T & device)167 	void remove (const T& device) {remove (DeviceGrab<T> (nullptr, device));}
remove(const std::set<T> & devices)168 	void remove (const std::set<T>& devices) {remove (DeviceGrab<T> (nullptr, devices));}
remove(const DeviceGrab<T> & deviceGrab)169 	void remove (const DeviceGrab<T>& deviceGrab)
170 	{
171 		bool done = true;
172 		std::set<T> devices = deviceGrab.getDevices();
173 		BWidgets::Widget* widget = deviceGrab.getWidget();
174 
175 		do
176 		{
177 			done = true;
178 
179 			for (typename std::list<DeviceGrab<T>>::iterator it = stack_.begin(); it != stack_.end(); ++it)
180 			{
181 				DeviceGrab<T>& dg = *it;
182 				if ((!widget) || (dg.getWidget() == widget))
183 				{
184 					// Erase list item if joker (std::set<T>{}) used
185 					if (devices.empty())
186 					{
187 						stack_.erase (it);
188 						done = false;
189 						break;
190 					}
191 
192 					std::set<T> stackDevices = dg.getDevices();
193 
194 					// Deletion of individual devices is not allowed
195 					// if joker (std::set<T>{}) is set
196 					if (stackDevices.empty()) {}
197 
198 					else
199 					{
200 						// Build difference
201 						std::set<T> diff = {};
202 						std::set_difference
203 						(
204 							stackDevices.begin(), stackDevices.end(),
205 							devices.begin(), devices.end(),
206 							std::inserter (diff, diff.begin())
207 						);
208 
209 						// Erase list item if all devices are deleted
210 						if (diff.empty())
211 						{
212 							stack_.erase (it);
213 							done = false;
214 							break;
215 						}
216 
217 						// Otherwise replace list item
218 						else dg = DeviceGrab<T> (dg.getWidget(), diff);
219 					}
220 				}
221 			}
222 		} while (!done);
223 	}
224 
225 	/* Adds a widget to the top of DeviceGrab stack. If the widget is
226 	 * already inside the stack, it is moved to the top and the linked
227 	 * devices are combined.
228 	 * @param deviceGrab	DeviceGrab
229 	 */
add(BWidgets::Widget * widget)230 	void add (BWidgets::Widget* widget) {add (DeviceGrab<T> (widget, std::set<T>{}));}
add(const DeviceGrab<T> & deviceGrab)231 	void add (const DeviceGrab<T>& deviceGrab)
232 	{
233 		BWidgets::Widget* widget = deviceGrab.getWidget();
234 		std::set<T> devices = deviceGrab.getDevices ();
235 
236 		if (contains(widget))
237 		{
238 			std::set<T> d2 = getDevices (widget);
239 			if (devices.empty() || d2.empty()) devices.clear();
240 			else devices.insert (d2.begin(), d2.end());
241 			remove (deviceGrab.getWidget());
242 		}
243 
244 		stack_.push_back (DeviceGrab<T> (widget, devices));
245 	}
246 
247 	/* Gets (the pointer to) the DeviceGrab containing the respective
248 	 * device. Starts from the top of the DeviceGrab stack.
249 	 * @param device	<T> of the respective device.
250 	 * @return		Pointer to the respective DeviceGrab or nullptr.
251 	 */
getGrab(const T & device)252 	DeviceGrab<T>* getGrab (const T& device)
253 	{
254 		for (typename std::list<DeviceGrab<T>>::reverse_iterator rit = stack_.rbegin (); rit != stack_.rend (); ++rit)
255 		{
256 			DeviceGrab<T>& dg = *rit;
257 			if (dg.contains (device)) return &dg;
258 		}
259 
260 		return nullptr;
261 	}
262 
263 };
264 
265 class MouseDevice
266 {
267 public:
268 	ButtonCode button;
269 	BUtilities::Point position;
270 
271 protected:
272 	std::chrono::steady_clock::time_point time_;
273 
274 public:
MouseDevice()275 	MouseDevice () : MouseDevice (NO_BUTTON, BUtilities::Point ()) {}
MouseDevice(const ButtonCode but)276 	MouseDevice (const ButtonCode but) : MouseDevice (but, BUtilities::Point ()) {}
MouseDevice(const ButtonCode but,const BUtilities::Point & pos)277 	MouseDevice (const ButtonCode but, const BUtilities::Point& pos) :
278 			button (but), position (pos),
279 			time_ (std::chrono::steady_clock::now()) {}
280 
getTime() const281 	std::chrono::steady_clock::time_point getTime () const {return time_;}
282 
operator <(const MouseDevice & lhs,const MouseDevice & rhs)283 	friend inline bool operator< (const MouseDevice& lhs, const MouseDevice& rhs)
284 	{
285 		if( lhs.button < rhs.button ) return true;
286 		return false;
287 	}
288 
operator ==(const MouseDevice & lhs,const MouseDevice & rhs)289 	friend inline bool operator== (const MouseDevice& lhs, const MouseDevice& rhs)
290 	{
291 		if( lhs.button == rhs.button ) return true;
292 		return false;
293 	}
294 
operator >(const MouseDevice & lhs,const MouseDevice & rhs)295 	friend inline bool operator> (const MouseDevice& lhs, const MouseDevice& rhs) {return rhs < lhs;}
operator <=(const MouseDevice & lhs,const MouseDevice & rhs)296 	friend inline bool operator<=(const MouseDevice& lhs, const MouseDevice& rhs) {return !(lhs > rhs);}
operator >=(const MouseDevice & lhs,const MouseDevice & rhs)297 	friend inline bool operator>=(const MouseDevice& lhs, const MouseDevice& rhs) {return !(lhs < rhs);}
operator !=(const MouseDevice & lhs,const MouseDevice & rhs)298 	friend inline bool operator!=(const MouseDevice& lhs, const MouseDevice& rhs) {return !(lhs==rhs);}
299 };
300 
301 
302 }
303 
304 #endif /*BWIDGETS_BDEVICES_HPP_*/
305