Large nasty commit in the form of a 5000 line patch chock-full of completely
[ardour.git] / gtk2_ardour / keyboard.cc
1 /*
2     Copyright (C) 2001 Paul Davis 
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 2 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, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18     $Id$
19 */
20
21 #include "ardour_ui.h"
22
23 #include <algorithm>
24 #include <fstream>
25
26 #include <ctype.h>
27
28 #include <X11/keysymdef.h>
29 #include <gdk/gdkx.h>
30 #include <gdk/gdkkeysyms.h>
31 #include <pbd/error.h>
32
33 #include "keyboard.h"
34 #include "gui_thread.h"
35
36 #include "i18n.h"
37
38 using namespace PBD;
39
40 #define KBD_DEBUG 1
41 bool debug_keyboard = false;
42
43 guint Keyboard::edit_but = 3;
44 guint Keyboard::edit_mod = GDK_CONTROL_MASK;
45 guint Keyboard::delete_but = 3;
46 guint Keyboard::delete_mod = GDK_SHIFT_MASK;
47 guint Keyboard::snap_mod = GDK_MOD3_MASK;
48
49 uint32_t Keyboard::Control = GDK_CONTROL_MASK;
50 uint32_t Keyboard::Shift = GDK_SHIFT_MASK;
51 uint32_t Keyboard::Alt = GDK_MOD1_MASK;
52 uint32_t Keyboard::Meta = GDK_MOD2_MASK;
53
54 Keyboard* Keyboard::_the_keyboard = 0;
55
56 /* set this to initially contain the modifiers we care about, then track changes in ::set_edit_modifier() etc. */
57
58 GdkModifierType Keyboard::RelevantModifierKeyMask = 
59                                GdkModifierType (GDK_SHIFT_MASK|GDK_CONTROL_MASK|GDK_MOD1_MASK|GDK_MOD3_MASK);
60
61
62 Keyboard::Keyboard ()
63 {
64         if (_the_keyboard == 0) {
65                 _the_keyboard = this;
66         }
67
68         collecting_prefix = false;
69
70         get_modifier_masks ();
71
72         snooper_id = gtk_key_snooper_install (_snooper, (gpointer) this);
73
74         XMLNode* node = ARDOUR_UI::instance()->keyboard_settings();
75         set_state (*node);
76 }
77
78 Keyboard::~Keyboard ()
79 {
80         gtk_key_snooper_remove (snooper_id);
81         delete [] modifier_masks;
82 }
83
84 XMLNode& 
85 Keyboard::get_state (void)
86 {
87         XMLNode* node = new XMLNode ("Keyboard");
88         char buf[32];
89
90         snprintf (buf, sizeof (buf), "%d", edit_but);
91         node->add_property ("edit-button", buf);
92         snprintf (buf, sizeof (buf), "%d", edit_mod);
93         node->add_property ("edit-modifier", buf);
94         snprintf (buf, sizeof (buf), "%d", delete_but);
95         node->add_property ("delete-button", buf);
96         snprintf (buf, sizeof (buf), "%d", delete_mod);
97         node->add_property ("delete-modifier", buf);
98         snprintf (buf, sizeof (buf), "%d", snap_mod);
99         node->add_property ("snap-modifier", buf);
100
101         return *node;
102 }
103
104 int 
105 Keyboard::set_state (const XMLNode& node)
106 {
107         const XMLProperty* prop;
108
109         if ((prop = node.property ("edit-button")) != 0) {
110                 sscanf (prop->value().c_str(), "%d", &edit_but);
111         } 
112
113         if ((prop = node.property ("edit-modifier")) != 0) {
114                 sscanf (prop->value().c_str(), "%d", &edit_mod);
115         } 
116
117         if ((prop = node.property ("delete-button")) != 0) {
118                 sscanf (prop->value().c_str(), "%d", &delete_but);
119         } 
120
121         if ((prop = node.property ("delete-modifier")) != 0) {
122                 sscanf (prop->value().c_str(), "%d", &delete_mod);
123         } 
124
125         if ((prop = node.property ("snap-modifier")) != 0) {
126                 sscanf (prop->value().c_str(), "%d", &snap_mod);
127         } 
128
129         return 0;
130 }
131
132 gint
133 Keyboard::_snooper (GtkWidget *widget, GdkEventKey *event, gpointer data)
134 {
135         return ((Keyboard *) data)->snooper (widget, event);
136 }
137
138 gint
139 Keyboard::snooper (GtkWidget *widget, GdkEventKey *event)
140 {
141         uint32_t keyval;
142
143 #if KBD_DEBUG
144         if (debug_keyboard) {
145                 cerr << "snoop widget " << widget << " key " << event->keyval << " type: " << event->type 
146                      << endl;
147         }
148 #endif
149
150         if (event->keyval == GDK_Shift_R) {
151                 keyval = GDK_Shift_L;
152
153         } else  if (event->keyval == GDK_Control_R) {
154                 keyval = GDK_Control_L;
155
156         } else {
157                 keyval = event->keyval;
158         }
159                 
160         if (event->type == GDK_KEY_PRESS) {
161                 bool was_prefix = false;
162
163                 if (collecting_prefix) {
164                         switch (keyval) {
165                         case GDK_0:
166                                 current_prefix += '0';
167                                 was_prefix = true;
168                                 break;
169                         case GDK_1:
170                                 current_prefix += '1';
171                                 was_prefix = true;
172                                 break;
173                         case GDK_2:
174                                 current_prefix += '2';
175                                 was_prefix = true;
176                                 break;
177                         case GDK_3:
178                                 current_prefix += '3';
179                                 was_prefix = true;
180                                 break;
181                         case GDK_4:
182                                 current_prefix += '4';
183                                 was_prefix = true;
184                                 break;
185                         case GDK_5:
186                                 current_prefix += '5';
187                                 was_prefix = true;
188                                 break;
189                         case GDK_6:
190                                 current_prefix += '6';
191                                 was_prefix = true;
192                                 break;
193                         case GDK_7:
194                                 current_prefix += '7';
195                                 was_prefix = true;
196                                 break;
197                         case GDK_8:
198                                 current_prefix += '8';
199                                 was_prefix = true;
200                                 break;
201                         case GDK_9:
202                                 current_prefix += '9';
203                                 was_prefix = true;
204                                 break;
205                         case GDK_period:
206                                 current_prefix += '.';
207                                 was_prefix = true;
208                                 break;
209                         default:
210                                 was_prefix = false;
211                                 collecting_prefix = false;
212                                 break;
213                         }
214                 }
215
216                 if (find (state.begin(), state.end(), keyval) == state.end()) {
217                         state.push_back (keyval);
218                         sort (state.begin(), state.end());
219                 }
220
221         } else if (event->type == GDK_KEY_RELEASE) {
222
223                 State::iterator i;
224                 
225                 if ((i = find (state.begin(), state.end(), keyval)) != state.end()) {
226                         state.erase (i);
227                         sort (state.begin(), state.end());
228                 } 
229
230         }
231
232         return false;
233 }
234
235 bool
236 Keyboard::key_is_down (uint32_t keyval)
237 {
238         return find (state.begin(), state.end(), keyval) != state.end();
239 }
240
241 Keyboard::State
242 Keyboard::translate_key_name (const string& name)
243
244 {
245         string::size_type i;
246         string::size_type len;
247         bool at_end;
248         string::size_type hyphen;
249         string keyname;
250         string whatevers_left;
251         State result;
252         guint keycode;
253         
254         i = 0;
255         len = name.length();
256         at_end = (len == 0);
257
258         while (!at_end) {
259
260                 whatevers_left = name.substr (i);
261
262                 if ((hyphen = whatevers_left.find_first_of ('-')) == string::npos) {
263                         
264                         /* no hyphen, so use the whole thing */
265                         
266                         keyname = whatevers_left;
267                         at_end = true;
268
269                 } else {
270
271                         /* There is a hyphen. */
272                         
273                         if (hyphen == 0 && whatevers_left.length() == 1) {
274                                 /* its the first and only character */
275                         
276                                 keyname = "-";
277                                 at_end = true;
278
279                         } else {
280
281                                 /* use the text before the hypen */
282                                 
283                                 keyname = whatevers_left.substr (0, hyphen);
284                                 
285                                 if (hyphen == len - 1) {
286                                         at_end = true;
287                                 } else {
288                                         i += hyphen + 1;
289                                         at_end = (i >= len);
290                                 }
291                         }
292                 }
293                 
294                 if (keyname.length() == 1 && isupper (keyname[0])) {
295                         result.push_back (GDK_Shift_L);
296                 }
297                 
298                 if ((keycode = gdk_keyval_from_name(get_real_keyname (keyname).c_str())) == GDK_VoidSymbol) {
299                         error << string_compose(_("KeyboardTarget: keyname \"%1\" is unknown."), keyname) << endmsg;
300                         result.clear();
301                         return result;
302                 }
303                 
304                 result.push_back (keycode);
305         }
306
307         sort (result.begin(), result.end());
308
309         return result;
310 }
311
312 string
313 Keyboard::get_real_keyname (const string& name)
314 {
315
316         if (name == "Control" || name == "Ctrl") {
317                 return "Control_L";
318         } 
319         if (name == "Meta" || name == "MetaL") {
320                 return "Meta_L";
321         } 
322         if (name == "MetaR") {
323                 return "Meta_R";
324         } 
325         if (name == "Alt" || name == "AltL") {
326                 return "Alt_L";
327         } 
328         if (name == "AltR") {
329                 return "Alt_R";
330         } 
331         if (name == "Shift") {
332                 return "Shift_L";
333         }
334         if (name == "Shift_R") {
335                 return "Shift_L";
336         }
337         if (name == " ") {
338                 return "space";
339         }
340         if (name == "!") {
341                 return "exclam";
342         }
343         if (name == "\"") {
344                 return "quotedbl";
345         }
346         if (name == "#") {
347                 return "numbersign";
348         }
349         if (name == "$") {
350                 return "dollar";
351         }
352         if (name == "%") {
353                 return "percent";
354         }
355         if (name == "&") {
356                 return "ampersand";
357         }
358         if (name == "'") {
359                 return "apostrophe";
360         }
361         if (name == "'") {
362                 return "quoteright";
363         }
364         if (name == "(") {
365                 return "parenleft";
366         }
367         if (name == ")") {
368                 return "parenright";
369         }
370         if (name == "*") {
371                 return "asterisk";
372         }
373         if (name == "+") {
374                 return "plus";
375         }
376         if (name == ",") {
377                 return "comma";
378         }
379         if (name == "-") {
380                 return "minus";
381         }
382         if (name == ".") {
383                 return "period";
384         }
385         if (name == "/") {
386                 return "slash";
387         }
388         if (name == ":") {
389                 return "colon";
390         }
391         if (name == ";") {
392                 return "semicolon";
393         }
394         if (name == "<") {
395                 return "less";
396         }
397         if (name == "=") {
398                 return "equal";
399         }
400         if (name == ">") {
401                 return "greater";
402         }
403         if (name == "?") {
404                 return "question";
405         }
406         if (name == "@") {
407                 return "at";
408         }
409         if (name == "[") {
410                 return "bracketleft";
411         }
412         if (name == "\\") {
413                 return "backslash";
414         }
415         if (name == "]") {
416                 return "bracketright";
417         }
418         if (name == "^") {
419                 return "asciicircum";
420         }
421         if (name == "_") {
422                 return "underscore";
423         }
424         if (name == "`") {
425                 return "grave";
426         }
427         if (name == "`") {
428                 return "quoteleft";
429         }
430         if (name == "{") {
431                 return "braceleft";
432         }
433         if (name == "|") {
434                 return "bar";
435         }
436         if (name == "}") {
437                 return "braceright";
438         }
439         if (name == "~") {
440                 return "asciitilde";
441         }
442
443         return name;
444 }
445
446 int
447 Keyboard::get_prefix (float& val, bool& was_floating)
448 {
449         if (current_prefix.length()) {
450                 if (current_prefix.find ('.') != string::npos) {
451                         was_floating = true;
452                 } else {
453                         was_floating = false;
454                 }
455                 if (sscanf (current_prefix.c_str(), "%f", &val) == 1) {
456                         return 0;
457                 }
458                 current_prefix = "";
459         }
460         return -1;
461 }
462
463 void
464 Keyboard::start_prefix ()
465 {
466         collecting_prefix = true;
467         current_prefix = "";
468 }
469
470 void
471 Keyboard::clear_modifier_state ()
472 {
473         modifier_mask = 0;
474 }
475
476 void
477 Keyboard::check_modifier_state ()
478 {
479         char keys[32];
480         int i, j;
481
482         clear_modifier_state ();
483         XQueryKeymap (GDK_DISPLAY(), keys);
484
485         for (i = 0; i < 32; ++i) {
486                 for (j = 0; j < 8; ++j) {
487
488                         if (keys[i] & (1<<j)) {
489                                 modifier_mask |= modifier_masks[(i*8)+j];
490                         }
491                 }
492         }
493 }
494
495 void
496 Keyboard::check_meta_numlock (char keycode, guint mod, string modname)
497 {
498         guint alternate_meta_mod;
499         string alternate_meta_modname;
500
501         if (mod == Meta) {
502                 
503                 guint keysym = XKeycodeToKeysym  (GDK_DISPLAY(), keycode, 0);
504                 
505                 if (keysym == GDK_Num_Lock) {
506
507                         switch (mod) {
508                         case GDK_MOD2_MASK:
509                                 alternate_meta_mod = GDK_MOD3_MASK;
510                                 alternate_meta_modname = "Mod3";
511                                 break;
512                         case GDK_MOD3_MASK:
513                                 alternate_meta_mod = GDK_MOD2_MASK;
514                                 alternate_meta_modname = "Mod2";
515                                 break;
516                         case GDK_MOD4_MASK:
517                                 alternate_meta_mod = GDK_MOD2_MASK;
518                                 alternate_meta_modname = "Mod2";
519                                 break;
520                         case GDK_MOD5_MASK:
521                                 alternate_meta_mod = GDK_MOD2_MASK;
522                                 alternate_meta_modname = "Mod2";
523                                 break;
524                         default:
525                                 error << string_compose (_("Your system is completely broken - NumLock uses \"%1\""
526                                                     "as its modifier. This is madness - see the man page "
527                                                     "for xmodmap to find out how to fix this."),
528                                                   modname)
529                                       << endmsg;
530                                 return;
531                         }
532
533                         warning << string_compose (_("Your system generates \"%1\" when the NumLock key "
534                                               "is pressed. This can cause problems when editing "
535                                               "so Ardour will use %2 to mean Meta rather than %1"),
536                                             modname, alternate_meta_modname)
537                                 << endmsg;
538
539                         set_meta_modifier (alternate_meta_mod);
540                 }
541         }
542 }
543
544 void
545 Keyboard::get_modifier_masks ()
546 {
547         XModifierKeymap *modifiers;
548         KeyCode *keycode;
549         int i;
550         int bound;
551
552         XDisplayKeycodes (GDK_DISPLAY(), &min_keycode, &max_keycode);
553
554         /* This function builds a lookup table to provide rapid answers to
555            the question: what, if any, modmask, is associated with a given
556            keycode ?
557         */
558         
559         modifiers = XGetModifierMapping (GDK_DISPLAY());
560         
561         modifier_masks = new int32_t [max_keycode+1];
562         
563         keycode = modifiers->modifiermap;
564
565         for (i = 0; i < modifiers->max_keypermod; ++i) { /* shift */
566                 if (*keycode) {
567                         modifier_masks[*keycode] = GDK_SHIFT_MASK;
568                         // cerr << "Shift = " << XKeysymToString (XKeycodeToKeysym  (GDK_DISPLAY(), *keycode, 0)) << endl;
569                 }
570                 keycode++;
571         }
572     
573         for (i = 0; i < modifiers->max_keypermod; ++i) keycode++; /* skip lock */
574     
575         for (i = 0; i < modifiers->max_keypermod; ++i) { /* control */
576                 if (*keycode) {
577                         modifier_masks[*keycode] = GDK_CONTROL_MASK;
578                         // cerr << "Control = " << XKeysymToString (XKeycodeToKeysym  (GDK_DISPLAY(), *keycode, 0)) << endl;
579                 }
580                 keycode++;
581         }
582
583         bound = 0;
584         for (i = 0; i < modifiers->max_keypermod; ++i) { /* mod 1 */
585                 if (*keycode) {
586                         modifier_masks[*keycode] = GDK_MOD1_MASK;
587                         // cerr << "Mod1 = " << XKeysymToString (XKeycodeToKeysym  (GDK_DISPLAY(), *keycode, 0)) << endl;
588                         bound++;
589                 }
590                 keycode++;
591         }
592 #ifdef WARN_ABOUT_DUPLICATE_MODIFIERS
593         if (bound > 1) {
594                 warning << string_compose (_("You have %1 keys bound to \"mod1\""), bound) << endmsg;
595         }
596 #endif
597         bound = 0;
598         for (i = 0; i < modifiers->max_keypermod; ++i) { /* mod2 */
599                 if (*keycode) {
600                         modifier_masks[*keycode] = GDK_MOD2_MASK;
601                         check_meta_numlock (*keycode, GDK_MOD2_MASK, "Mod2");
602                         //cerr << "Mod2 = " << std::hex << (int) *keycode << std::dec << " = " << XKeysymToString (XKeycodeToKeysym  (GDK_DISPLAY(), *keycode, 0)) << endl;
603                         bound++;
604                 }
605                 keycode++; 
606         }
607 #ifdef WARN_ABOUT_DUPLICATE_MODIFIERS
608         if (bound > 1) {
609                 warning << string_compose (_("You have %1 keys bound to \"mod2\""), bound) << endmsg;
610         }
611 #endif
612         bound = 0;
613         for (i = 0; i < modifiers->max_keypermod; ++i) { /* mod3 */
614                 if (*keycode) {
615                         modifier_masks[*keycode] = GDK_MOD3_MASK;
616                         check_meta_numlock (*keycode, GDK_MOD3_MASK, "Mod3");
617                         // cerr << "Mod3 = " << XKeysymToString (XKeycodeToKeysym  (GDK_DISPLAY(), *keycode, 0)) << endl;
618                         bound++;
619                 }
620                 keycode++; 
621         }
622 #ifdef WARN_ABOUT_DUPLICATE_MODIFIERS
623         if (bound > 1) {
624                 warning << string_compose (_("You have %1 keys bound to \"mod3\""), bound) << endmsg;
625         }
626 #endif
627         bound = 0;
628         for (i = 0; i < modifiers->max_keypermod; ++i) { /* mod 4 */
629                 if (*keycode) {
630                         modifier_masks[*keycode] = GDK_MOD4_MASK;
631                         check_meta_numlock (*keycode, GDK_MOD4_MASK, "Mod4");
632                         // cerr << "Mod4 = " << XKeysymToString (XKeycodeToKeysym  (GDK_DISPLAY(), *keycode, 0)) << endl;
633                         bound++;
634                 }
635                 keycode++;
636         }
637 #ifdef WARN_ABOUT_DUPLICATE_MODIFIERS
638         if (bound > 1) {
639                 warning << string_compose (_("You have %1 keys bound to \"mod4\""), bound) << endmsg;
640         }
641 #endif
642         bound = 0;
643         for (i = 0; i < modifiers->max_keypermod; ++i) { /* mod 5 */
644                 if (*keycode) {
645                         modifier_masks[*keycode] = GDK_MOD5_MASK;
646                         check_meta_numlock (*keycode, GDK_MOD5_MASK, "Mod5");
647                         // cerr << "Mod5 = " << XKeysymToString (XKeycodeToKeysym  (GDK_DISPLAY(), *keycode, 0)) << endl;
648                         bound++;
649                 }
650                 keycode++;
651         }
652 #ifdef WARN_ABOUT_DUPLICATE_MODIFIERS
653         if (bound > 1) {
654                 warning << string_compose (_("You have %1 keys bound to \"mod5\""), bound) << endmsg;
655         }
656 #endif
657
658         XFreeModifiermap (modifiers);
659 }
660
661 bool
662 Keyboard::enter_window (GdkEventCrossing *ev, Gtk::Window* win)
663 {
664         switch (ev->detail) {
665         case GDK_NOTIFY_INFERIOR:
666                 break;
667
668         case GDK_NOTIFY_VIRTUAL:
669                 /* fallthru */
670
671         default:
672                 check_modifier_state ();
673         }
674
675         return FALSE;
676 }
677
678 bool
679 Keyboard::leave_window (GdkEventCrossing *ev, Gtk::Window* win)
680 {
681         switch (ev->detail) {
682         case GDK_NOTIFY_INFERIOR:
683                 if (debug_keyboard) {
684                         cerr << "INFERIOR crossing ... out\n";
685                 }
686                 break;
687
688         case GDK_NOTIFY_VIRTUAL:
689                 if (debug_keyboard) {
690                         cerr << "VIRTUAL crossing ... out\n";
691                 }
692                 /* fallthru */
693
694         default:
695                 if (debug_keyboard) {
696                         cerr << "REAL CROSSING ... out\n";
697                         cerr << "clearing current target\n";
698                 }
699                 state.clear ();
700                 clear_modifier_state ();
701         }
702         return FALSE;
703
704 }
705
706 void
707 Keyboard::set_edit_button (guint but)
708 {
709         edit_but = but;
710 }
711
712 void
713 Keyboard::set_edit_modifier (guint mod)
714 {
715         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~edit_mod);
716         edit_mod = mod;
717         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | edit_mod);
718 }
719
720 void
721 Keyboard::set_delete_button (guint but)
722 {
723         delete_but = but;
724 }
725
726 void
727 Keyboard::set_delete_modifier (guint mod)
728 {
729         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~delete_mod);
730         delete_mod = mod;
731         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | delete_mod);
732 }
733
734 void
735 Keyboard::set_meta_modifier (guint mod)
736 {
737         /* we don't include Meta in the RelevantModifierKeyMask because its not used
738            in the same way as snap_mod, delete_mod etc. the only reason we allow it to be
739            set at all is that X Window has no convention for the keyboard modifier
740            that Meta should use. Some Linux distributions bind NumLock to Mod2, which
741            is our default Meta modifier, and this causes severe problems.
742         */
743         Meta = mod;
744 }
745
746 void
747 Keyboard::set_snap_modifier (guint mod)
748 {
749         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~snap_mod);
750         snap_mod = mod;
751         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | snap_mod);
752 }
753
754 bool
755 Keyboard::is_edit_event (GdkEventButton *ev)
756 {
757         return (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_BUTTON_RELEASE) && 
758                 (ev->button == Keyboard::edit_button()) && 
759                 ((ev->state & RelevantModifierKeyMask) == Keyboard::edit_modifier());
760 }
761
762 bool
763 Keyboard::is_delete_event (GdkEventButton *ev)
764 {
765         return (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_BUTTON_RELEASE) && 
766                 (ev->button == Keyboard::delete_button()) && 
767                 ((ev->state & RelevantModifierKeyMask) == Keyboard::delete_modifier());
768 }
769
770 bool
771 Keyboard::is_context_menu_event (GdkEventButton *ev)
772 {
773         return (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_BUTTON_RELEASE) && 
774                 (ev->button == 3) && 
775                 ((ev->state & RelevantModifierKeyMask) == 0);
776 }
777
778 bool 
779 Keyboard::no_modifiers_active (guint state)
780 {
781         return (state & RelevantModifierKeyMask) == 0;
782 }
783
784 bool
785 Keyboard::modifier_state_contains (guint state, ModifierMask mask)
786 {
787         return (state & mask) == (guint) mask;
788 }
789
790 bool
791 Keyboard::modifier_state_equals (guint state, ModifierMask mask)
792 {
793         return (state & RelevantModifierKeyMask) == (guint) mask;
794 }
795
796 Selection::Operation
797 Keyboard::selection_type (guint state)
798 {
799         if (modifier_state_equals (state, Shift)) {
800                 return Selection::Extend;
801         } else if (modifier_state_equals (state, Control)) {
802                 return Selection::Toggle;
803         } else {
804                 return Selection::Set;
805         }
806 }