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