chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / src / aclass.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2009 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "E.h"
25 #include "aclass.h"
26 #include "conf.h"
27 #include "cursors.h"
28 #include "e16-ecore_list.h"
29 #include "emodule.h"
30 #include "ewins.h"
31 #include "file.h"
32 #include "grabs.h"
33 #include "timers.h"
34 #include <ctype.h>
35
36 typedef struct _actiontype {
37    char               *params;
38    struct _actiontype *next;
39 } ActionType;
40
41 struct _action {
42    char                event;
43    char                anymodifier;
44    int                 modifiers;
45    char                anybutton;
46    int                 button;
47    char                anykey;
48    KeyCode             key;
49    char               *key_str;
50    char               *tooltipstring;
51    ActionType         *action;
52 };
53
54 struct _actionclass {
55    char               *name;
56    int                 num;
57    Action            **list;
58    char               *tooltipstring;
59    unsigned int        ref_count;
60    char global;
61 };
62
63 static void         UnGrabActionKey(Action * aa);
64 static void         GrabActionKey(Action * aa);
65
66 static Ecore_List  *aclass_list = NULL;
67 static Ecore_List  *aclass_list_global = NULL;
68
69 static char         mode_action_destroy = 0;
70
71 static void
72 RemoveActionType(ActionType * ActionTypeToRemove)
73 {
74    ActionType         *ptr, *pp;
75
76    ptr = ActionTypeToRemove;
77    while (ptr)
78      {
79         Efree(ptr->params);
80         pp = ptr;
81         ptr = ptr->next;
82         Efree(pp);
83      }
84 }
85
86 Action             *
87 ActionCreate(char event, char anymod, int mod, int anybut, int but,
88              char anykey, const char *key, const char *tooltipstring)
89 {
90    Action             *aa;
91
92    aa = EMALLOC(Action, 1);
93    aa->action = NULL;
94    aa->event = event;
95    aa->anymodifier = anymod;
96    aa->modifiers = mod;
97    aa->anybutton = anybut;
98    aa->button = but;
99    aa->anykey = anykey;
100    if (!key || !key[0] || (event != EVENT_KEY_DOWN && event != EVENT_KEY_UP))
101       aa->key = 0;
102    else
103       aa->key = EKeynameToKeycode(key);
104    aa->key_str = (aa->key) ? Estrdup(key) : NULL;
105    aa->tooltipstring =
106       (tooltipstring) ? Estrdup((tooltipstring[0]) ? tooltipstring : "?!?") :
107       NULL;
108
109    return aa;
110 }
111
112 static void
113 ActionDestroy(Action * aa)
114 {
115    if (!aa)
116       return;
117
118    if ((aa->event == EVENT_KEY_DOWN) || (aa->event == EVENT_KEY_UP))
119       UnGrabActionKey(aa);
120    if (aa->action)
121       RemoveActionType(aa->action);
122    Efree(aa->tooltipstring);
123    Efree(aa->key_str);
124    Efree(aa);
125 }
126
127 void
128 ActionAddTo(Action * aa, const char *params)
129 {
130    ActionType         *pptr, *ptr, *at;
131
132    at = EMALLOC(ActionType, 1);
133    if (!at)
134       return;
135
136    at->next = NULL;
137    at->params = (params && *params) ? Estrdup(params) : NULL;
138    if (!aa->action)
139      {
140         aa->action = at;
141      }
142    else
143      {
144         pptr = NULL;
145         ptr = aa->action;
146         while (ptr)
147           {
148              pptr = ptr;
149              ptr = ptr->next;
150           }
151         if (pptr)
152            pptr->next = at;
153      }
154 }
155
156 void
157 ActionclassAddAction(ActionClass * ac, Action * aa)
158 {
159    ac->num++;
160    ac->list = EREALLOC(Action *, ac->list, ac->num);
161    ac->list[ac->num - 1] = aa;
162 }
163
164 ActionClass        *
165 ActionclassCreate(const char *name, int global)
166 {
167    ActionClass        *ac;
168
169    ac = ECALLOC(ActionClass, 1);
170    ac->name = Estrdup(name);
171
172    if (global)
173      {
174         if (!aclass_list_global)
175            aclass_list_global = ecore_list_new();
176         ecore_list_prepend(aclass_list_global, ac);
177         ac->global = 1;
178      }
179    else
180      {
181         if (!aclass_list)
182            aclass_list = ecore_list_new();
183         ecore_list_prepend(aclass_list, ac);
184      }
185
186    return ac;
187 }
188
189 static void
190 ActionclassEmpty(ActionClass * ac)
191 {
192    int                 i;
193
194    for (i = 0; i < ac->num; i++)
195       ActionDestroy(ac->list[i]);
196    ac->num = 0;
197    _EFREE(ac->list);
198    _EFREE(ac->tooltipstring);
199 }
200
201 static void
202 ActionclassDestroy(ActionClass * ac)
203 {
204    if (!ac)
205       return;
206
207    if (ac->ref_count > 0)
208      {
209         DialogOK("ActionClass Error!", _("%u references remain\n"),
210                  ac->ref_count);
211         return;
212      }
213
214    ecore_list_node_remove(aclass_list, ac);
215
216    ActionclassEmpty(ac);
217    Efree(ac->name);
218
219    Efree(ac);
220    mode_action_destroy = 1;
221 }
222
223 static int
224 _ActionclassMatchName(const void *data, const void *match)
225 {
226    return strcmp(((const ActionClass *)data)->name, (const char *)match);
227 }
228
229 static ActionClass *
230 ActionclassFindGlobal(const char *name)
231 {
232    return (ActionClass *) ecore_list_find(aclass_list_global,
233                                           _ActionclassMatchName, name);
234 }
235
236 ActionClass        *
237 ActionclassFind(const char *name)
238 {
239    if (!name)
240       return NULL;
241    return (ActionClass *) ecore_list_find(aclass_list, _ActionclassMatchName,
242                                           name);
243 }
244
245 static ActionClass *
246 ActionclassFindAny(const char *name)
247 {
248    ActionClass        *ac;
249
250    ac = (ActionClass *) ecore_list_find(aclass_list_global,
251                                         _ActionclassMatchName, name);
252    if (ac)
253       return ac;
254    return (ActionClass *) ecore_list_find(aclass_list, _ActionclassMatchName,
255                                           name);
256 }
257
258 int
259 AclassConfigLoad(FILE * fs)
260 {
261    int                 err = 0;
262    ActionClass        *ac = NULL;
263    Action             *aa = NULL;
264    char                s[FILEPATH_LEN_MAX];
265    char                s2[FILEPATH_LEN_MAX];
266    char               *p2;
267    int                 i1, i2;
268    char                event = 0;
269    char                anymod = 0;
270    int                 mod = 0;
271    int                 anybut = 0;
272    int                 but = 0;
273    int                 first = 1;
274    char                anykey = 0;
275    char                key[64];
276    char               *aclass_tooltipstring = NULL;
277    char               *action_tooltipstring = NULL;
278    char global = 0;
279
280    key[0] = '\0';
281
282    while (GetLine(s, sizeof(s), fs))
283      {
284         i1 = ConfigParseline1(s, s2, &p2, NULL);
285         i2 = atoi(s2);
286         switch (i1)
287           {
288           case CONFIG_VERSION:
289              break;
290           case CONFIG_ACTIONCLASS:
291              err = -1;
292              if (i2 != CONFIG_OPEN)
293                 goto done;
294              ac = NULL;
295              aa = NULL;
296              event = 0;
297              anymod = anybut = anykey = 0;
298              mod = 0;
299              but = 0;
300              first = 1;
301              key[0] = '\0';
302              break;
303           case CONFIG_CLOSE:
304              ac->tooltipstring =
305                 (aclass_tooltipstring) ? Estrdup((aclass_tooltipstring[0]) ?
306                                                  aclass_tooltipstring :
307                                                  "?!?") : NULL;
308              err = 0;
309              goto done;
310
311           case CONFIG_CLASSNAME:
312           case ACLASS_NAME:
313              ac = ActionclassFindAny(s2);
314              if (ac)
315                {
316                   if (!strcmp(s2, "KEYBINDINGS"))
317                      Mode.keybinds_changed = 1;
318                   ActionclassEmpty(ac);
319                }
320              else
321                {
322                   ac = ActionclassCreate(s2, 0);
323                }
324              break;
325           case CONFIG_TYPE:
326           case ACLASS_TYPE:
327              if (i2 == ACLASS_TYPE_ACLASS)
328                 break;
329              ecore_list_node_remove(aclass_list, ActionclassFind(s2));
330              ecore_list_prepend(aclass_list_global, ac);
331              global = 1;
332
333              break;
334           case CONFIG_MODIFIER:
335           case ACLASS_MODIFIER:
336              /* These are the defines that I have listed...
337               * These, therefore, are the ones that I am 
338               * going to accept by default.
339               * REMINDER: add and'ing in future!!!!
340               * #define ShiftMask       (1<<0)
341               * #define LockMask        (1<<1)
342               * #define ControlMask     (1<<2)
343               * #define Mod1Mask        (1<<3)
344               * #define Mod2Mask        (1<<4)
345               * #define Mod3Mask        (1<<5)
346               * #define Mod4Mask        (1<<6)
347               * #define Mod5Mask        (1<<7)
348               */
349              switch (i2)
350                {
351                case MASK_NONE:
352                   mod = 0;
353                   break;
354                case MASK_SHIFT:
355                   mod |= ShiftMask;
356                   break;
357                case MASK_LOCK:
358                   mod |= LockMask;
359                   break;
360                case MASK_CTRL:
361                   mod |= ControlMask;
362                   break;
363                case MASK_MOD1:
364                   mod |= Mod1Mask;
365                   break;
366                case MASK_MOD2:
367                   mod |= Mod2Mask;
368                   break;
369                case MASK_MOD3:
370                   mod |= Mod3Mask;
371                   break;
372                case MASK_MOD4:
373                   mod |= Mod4Mask;
374                   break;
375                case MASK_MOD5:
376                   mod |= Mod5Mask;
377                   break;
378                case MASK_CTRL_ALT:
379                   mod |= ControlMask | Mod1Mask;
380                   break;
381                case MASK_SHIFT_ALT:
382                   mod |= ShiftMask | Mod1Mask;
383                   break;
384                case MASK_CTRL_SHIFT:
385                   mod |= ShiftMask | ControlMask;
386                   break;
387                case MASK_CTRL_SHIFT_ALT:
388                   mod |= ShiftMask | ControlMask | Mod1Mask;
389                   break;
390                case MASK_SHIFT_META4:
391                   mod |= Mod4Mask | ShiftMask;
392                   break;
393                case MASK_CTRL_META4:
394                   mod |= Mod4Mask | ControlMask;
395                   break;
396                case MASK_CTRL_META4_SHIFT:
397                   mod |= Mod4Mask | ControlMask | ShiftMask;
398                   break;
399                case MASK_SHIFT_META5:
400                   mod |= Mod5Mask | ShiftMask;
401                   break;
402                case MASK_CTRL_META5:
403                   mod |= Mod5Mask | ControlMask;
404                   break;
405                case MASK_CTRL_META5_SHIFT:
406                   mod |= Mod5Mask | ControlMask | ShiftMask;
407                   break;
408                case MASK_WINDOWS_SHIFT:
409                   mod |= Mod2Mask | ShiftMask;
410                   break;
411                case MASK_WINDOWS_CTRL:
412                   mod |= Mod2Mask | ControlMask;
413                   break;
414                case MASK_WINDOWS_ALT:
415                   mod |= Mod2Mask | Mod1Mask;
416                   break;
417                default:
418                   break;
419                }
420              break;
421           case CONFIG_ANYMOD:
422           case ACLASS_ANYMOD:
423              anymod = i2;
424              break;
425           case CONFIG_ANYBUT:
426           case ACLASS_ANYBUT:
427              anybut = i2;
428              break;
429           case CONFIG_BUTTON:
430           case ACLASS_BUT:
431              but = i2;
432              break;
433           case CONFIG_ANYKEY:
434           case ACLASS_ANYKEY:
435              anykey = i2;
436              break;
437           case ACLASS_KEY:
438              STRCPY(key, s2);
439              break;
440           case ACLASS_EVENT_TRIGGER:
441              event = i2;
442              break;
443           case CONFIG_NEXT:
444              mod = 0;
445              anymod = 0;
446              anybut = 0;
447              first = 1;
448              break;
449           case CONFIG_ACTION:
450              if (first)
451                {
452                   aa = ActionCreate(event, anymod, mod, anybut, but, anykey,
453                                     key, action_tooltipstring);
454                   /* the correct place to grab an action key */
455                   Efree(action_tooltipstring);
456                   action_tooltipstring = NULL;
457                   key[0] = '\0';
458                   if (global)
459                      GrabActionKey(aa);
460                   ActionclassAddAction(ac, aa);
461                   first = 0;
462                }
463              ActionAddTo(aa, p2);
464              break;
465           case CONFIG_ACTION_TOOLTIP:
466              action_tooltipstring = Estrdupcat2(action_tooltipstring, "\n", p2);
467              break;
468           case CONFIG_TOOLTIP:
469              aclass_tooltipstring = Estrdupcat2(aclass_tooltipstring, "\n", p2);
470              break;
471           default:
472              ConfigParseError("ActionClass", s);
473              break;
474           }
475      }
476
477    if (ac && err)
478       ActionclassDestroy(ac);
479
480  done:
481    Efree(aclass_tooltipstring);
482    Efree(action_tooltipstring);
483
484    return err;
485 }
486
487 static Action      *
488 ActionDecode(const char *line)
489 {
490    Action             *aa;
491    char                ev[16], mod[16], key[128], *s;
492    int                 len, event, modifiers, button;
493    char                anymod, anybut, anykey;
494
495    len = -1;
496    sscanf(line, "%15s %15s %127s %n", ev, mod, key, &len);
497    if (len <= 0)
498       return NULL;
499
500    event = -1;
501    if (!strcmp(ev, "KeyDown"))
502       event = EVENT_KEY_DOWN;
503    else if (!strcmp(ev, "MouseDown"))
504       event = EVENT_MOUSE_DOWN;
505    else if (!strcmp(ev, "KeyUp"))
506       event = EVENT_KEY_UP;
507    else if (!strcmp(ev, "MouseUp"))
508       event = EVENT_MOUSE_UP;
509    else if (!strcmp(ev, "MouseDouble"))
510       event = EVENT_DOUBLE_DOWN;
511    else if (!strcmp(ev, "MouseIn"))
512       event = EVENT_MOUSE_ENTER;
513    else if (!strcmp(ev, "MouseOut"))
514       event = EVENT_MOUSE_LEAVE;
515    else if (!strcmp(ev, "FocusIn"))
516       event = EVENT_FOCUS_IN;
517    else if (!strcmp(ev, "FocusOut"))
518       event = EVENT_FOCUS_OUT;
519
520    anymod = anybut = anykey = 0;
521    button = 0;
522
523    modifiers = 0;
524    for (s = mod; *s; s++)
525      {
526         switch (*s)
527           {
528           case '*':
529              anymod = 1;
530              break;
531           case 'C':
532              modifiers |= ControlMask;
533              break;
534           case 'S':
535              modifiers |= ShiftMask;
536              break;
537           case 'A':
538              modifiers |= Mod1Mask;
539              break;
540           case '1':
541              modifiers |= Mod1Mask;
542              break;
543           case '2':
544              modifiers |= Mod2Mask;
545              break;
546           case '3':
547              modifiers |= Mod3Mask;
548              break;
549           case '4':
550              modifiers |= Mod4Mask;
551              break;
552           case '5':
553              modifiers |= Mod5Mask;
554              break;
555           }
556      }
557
558    switch (event)
559      {
560      case EVENT_MOUSE_DOWN:
561      case EVENT_MOUSE_UP:
562      case EVENT_DOUBLE_DOWN:
563      case EVENT_MOUSE_ENTER:
564      case EVENT_MOUSE_LEAVE:
565         if (key[0] == '*')
566            anybut = 1;
567         else if (isdigit(key[0]))
568            button = atoi(key);
569         if (!anybut && button == 0)
570            return NULL;         /* Invalid */
571         key[0] = '\0';
572         break;
573      }
574
575    aa =
576       ActionCreate(event, anymod, modifiers, anybut, button, anykey, key, NULL);
577    ActionAddTo(aa, line + len);
578
579    return aa;
580 }
581
582 static int
583 ActionEncode(Action * aa, char *buf, int len)
584 {
585    const char         *event;
586    char               *p, mod[32], btn[32];
587
588    if (!aa || !aa->action)
589       return 0;
590
591    p = mod;
592    if (aa->anymodifier)
593       *p++ = '*';
594    if (aa->modifiers & ControlMask)
595       *p++ = 'C';
596    if (aa->modifiers & ShiftMask)
597       *p++ = 'S';
598    if (aa->modifiers & Mod1Mask)
599       *p++ = 'A';
600    if (aa->modifiers & Mod2Mask)
601       *p++ = '2';
602    if (aa->modifiers & Mod3Mask)
603       *p++ = '3';
604    if (aa->modifiers & Mod4Mask)
605       *p++ = '4';
606    if (aa->modifiers & Mod5Mask)
607       *p++ = '5';
608    if (p == mod)
609       *p++ = '-';
610    *p++ = '\0';
611
612    switch (aa->event)
613      {
614      default:
615         return 0;
616      case EVENT_KEY_DOWN:
617         event = "KeyDown";
618         goto encode_kb;
619      case EVENT_KEY_UP:
620         event = "KeyUp";
621         goto encode_kb;
622       encode_kb:
623         if (!aa->key_str)
624            return 0;
625         len = Esnprintf(buf, len, "%-7s %4s %8s %s\n", event, mod, aa->key_str,
626                         (aa->action->params) ? aa->action->params : "");
627         break;
628
629      case EVENT_MOUSE_DOWN:
630         event = "MouseDown";
631         goto encode_mb;
632      case EVENT_MOUSE_UP:
633         event = "MouseUp";
634         goto encode_mb;
635      case EVENT_DOUBLE_DOWN:
636         event = "MouseDouble";
637         goto encode_mb;
638      case EVENT_MOUSE_ENTER:
639         event = "MouseIn";
640         goto encode_mb;
641      case EVENT_MOUSE_LEAVE:
642         event = "MouseOut";
643         goto encode_mb;
644       encode_mb:
645         if (aa->anybutton)
646            strcpy(btn, "*");
647         else
648            sprintf(btn, "%u", aa->button);
649         len = Esnprintf(buf, len, "%-11s %4s %s %s\n", event, mod, btn,
650                         (aa->action->params) ? aa->action->params : "");
651         break;
652
653      case EVENT_FOCUS_IN:
654         event = "FocusIn";
655         goto encode_fc;
656      case EVENT_FOCUS_OUT:
657         event = "FocusOut";
658         goto encode_fc;
659       encode_fc:
660         break;
661      }
662
663    return len;
664 }
665
666 static int
667 AclassEncodeTT(const char *str, char *buf, int len)
668 {
669    char              **lst;
670    int                 i, num, l, nw;
671
672    lst = StrlistFromString(str, '\n', &num);
673    nw = 0;
674    for (i = 0; i < num; i++)
675      {
676         l = Esnprintf(buf, len, "Tooltip %s\n", lst[i]);
677         nw += l;
678         len -= l;
679         buf += l;
680      }
681    StrlistFree(lst, num);
682    return nw;
683 }
684
685 static void
686 AclassConfigLoad2(FILE * fs)
687 {
688    char                s[FILEPATH_LEN_MAX], *ss;
689    char                prm1[128], prm2[128], prm3[128];
690    ActionClass        *ac = NULL;
691    Action             *aa = NULL;
692    int                 len, len2;
693
694    for (;;)
695      {
696         ss = fgets(s, sizeof(s), fs);
697         if (!ss)
698            break;
699
700         len = strcspn(s, "#\r\n");
701         if (len <= 0)
702            continue;
703         s[len] = '\0';
704
705         prm3[0] = '\0';
706         len2 = 0;
707         len = sscanf(s, "%16s %n%128s %16s", prm1, &len2, prm2, prm3);
708         if (len < 2)
709            continue;
710
711         if (!strcmp(prm1, "Aclass"))
712           {
713              if (!strcmp(prm2, "KEYBINDINGS_UNCHANGABLE"))
714                {
715                   /* No more "unchangable" keybindings. */
716                   ac = ActionclassFindGlobal("KEYBINDINGS");
717                   prm2[11] = '\0';
718                }
719              else
720                {
721                   ac = ActionclassFindAny(prm2);
722                   if (ac)
723                      ActionclassEmpty(ac);
724                }
725
726              if (!ac)
727                 ac = ActionclassCreate(prm2, prm3[0] == 'g');
728
729              aa = NULL;
730           }
731         else if (!strncmp(prm1, "Key", 3) || !strncmp(prm1, "Mouse", 5))
732           {
733              if (!ac)
734                 continue;
735
736              aa = ActionDecode(s);
737              if (!aa)
738                 continue;
739
740              ActionclassAddAction(ac, aa);
741              GrabActionKey(aa);
742           }
743         else if (!strcmp(prm1, "Tooltip"))
744           {
745              /* FIXME - Multiple line strings may break */
746              if (aa)
747                {
748                   aa->tooltipstring =
749                      Estrdupcat2(aa->tooltipstring, "\n", s + len2);
750                }
751              else if (ac)
752                {
753                   ac->tooltipstring =
754                      Estrdupcat2(ac->tooltipstring, "\n", s + len2);
755                }
756           }
757      }
758 }
759
760 static void
761 AclassConfigLoadConfig(const char *name)
762 {
763    char               *file;
764    FILE               *fs;
765
766    file = ConfigFileFind(name, NULL, 0);
767    if (!file)
768       return;
769
770    fs = fopen(file, "r");
771    Efree(file);
772    if (!fs)
773       return;
774
775    AclassConfigLoad2(fs);
776
777    fclose(fs);
778 }
779
780 static void
781 AclassConfigWrite(const ActionClass * ac, void (*prf) (const char *fmt, ...))
782 {
783    char                s[FILEPATH_LEN_MAX];
784    Action             *aa;
785    int                 i, len;
786
787    if (!ac || ac->num <= 0)
788       return;
789
790    prf("Aclass %s %s\n", ac->name, (ac->global)? "global" : "normal");
791    if (ac->tooltipstring)
792      {
793         len = AclassEncodeTT(ac->tooltipstring, s, sizeof(s));
794         prf(s);
795      }
796    for (i = 0; i < ac->num; i++)
797      {
798         aa = ac->list[i];
799         len = ActionEncode(aa, s, sizeof(s));
800         if (len <= 0)
801            continue;
802         prf(s);
803         if (aa->tooltipstring)
804           {
805              len = AclassEncodeTT(aa->tooltipstring, s, sizeof(s));
806              prf(s);
807           }
808      }
809 }
810
811 static FILE        *_ac_fs = NULL;      /* Ugly! Yeah well... */
812
813 static void
814 _ac_prf(const char *fmt, ...)
815 {
816    va_list             args;
817    int                 len;
818
819    va_start(args, fmt);
820    len = vfprintf(_ac_fs, fmt, args);
821    va_end(args);
822 }
823
824 static void
825 BindingsSave(void)
826 {
827    char                s[FILEPATH_LEN_MAX], ss[FILEPATH_LEN_MAX];
828    FILE               *fs;
829
830    if (!Mode.keybinds_changed)
831       return;
832
833    Etmp(ss);
834    fs = fopen(ss, "w");
835    if (!fs)
836       return;
837    _ac_fs = fs;
838
839    AclassConfigWrite(ActionclassFind("BUTTONBINDINGS"), _ac_prf);
840    AclassConfigWrite(ActionclassFind("DESKBINDINGS"), _ac_prf);
841    AclassConfigWrite(ActionclassFindGlobal("KEYBINDINGS"), _ac_prf);
842    AclassConfigWrite(ActionclassFindGlobal("KEYBINDINGS_UNCHANGABLE"), _ac_prf);
843
844    fclose(fs);
845    _ac_fs = NULL;
846
847    Esnprintf(s, sizeof(s), "%s/bindings.cfg", EDirUser());
848    E_mv(ss, s);
849 }
850
851 void
852 ActionclassSetTooltipString(ActionClass * ac, const char *tts)
853 {
854    _EFDUP(ac->tooltipstring, tts);
855 }
856
857 ActionClass        *
858 ActionclassAlloc(const char *name)
859 {
860    ActionClass        *ac;
861
862    if (!name || !name[0])
863       return NULL;
864
865    ac = ActionclassFind(name);
866    if (ac)
867       ac->ref_count++;
868
869    return ac;
870 }
871
872 void
873 ActionclassFree(ActionClass * ac)
874 {
875    if (ac)
876       ac->ref_count--;
877 }
878
879 const char         *
880 ActionclassGetName(ActionClass * ac)
881 {
882    return (ac) ? ac->name : NULL;
883 }
884
885 const char         *
886 ActionclassGetTooltipString(ActionClass * ac)
887 {
888    return (ac) ? ac->tooltipstring : NULL;
889 }
890
891 int
892 ActionclassGetActionCount(ActionClass * ac)
893 {
894    return (ac) ? ac->num : 0;
895 }
896
897 Action             *
898 ActionclassGetAction(ActionClass * ac, int ix)
899 {
900    return (ac && ix < ac->num) ? ac->list[ix] : NULL;
901 }
902
903 const char         *
904 ActionGetTooltipString(Action * aa)
905 {
906    return (aa) ? aa->tooltipstring : NULL;
907 }
908
909 int
910 ActionGetEvent(Action * aa)
911 {
912    return (aa) ? aa->event : 0;
913 }
914
915 int
916 ActionGetAnybutton(Action * aa)
917 {
918    return (aa) ? aa->anybutton : 0;
919 }
920
921 int
922 ActionGetButton(Action * aa)
923 {
924    return (aa) ? aa->button : 0;
925 }
926
927 int
928 ActionGetModifiers(Action * aa)
929 {
930    return (aa) ? aa->modifiers : 0;
931 }
932
933 static void
934 handleAction(EWin * ewin, ActionType * action)
935 {
936    if (ewin)
937       ewin->state.in_action = 1;
938    EFunc(ewin, action->params);
939    if (ewin)
940       ewin->state.in_action = 0;
941
942    /* Did we just hose ourselves? if so, we'd best not stick around here */
943    if (mode_action_destroy)
944       return;
945
946    /* If there is another action in this series, (now that
947     * we're sure we didn't already die) perform it
948     */
949    if (action->next)
950       handleAction(ewin, action->next);
951 }
952
953 int
954 ActionclassEvent(ActionClass * ac, XEvent * ev, EWin * ewin)
955 {
956    KeyCode             key;
957    int                 i, type, button, modifiers, ok, mouse, mask, val = 0;
958    Action             *aa;
959
960    if (Mode.action_inhibit || (ewin && ewin->state.inhibit_actions))
961       return 0;
962
963    key = type = button = modifiers = mouse = 0;
964
965    mask = Mode.masks.mod_key_mask;
966
967    switch (ev->type)
968      {
969      case KeyPress:
970         type = EVENT_KEY_DOWN;
971         key = ev->xkey.keycode;
972         modifiers = ev->xbutton.state & mask;
973         mouse = 0;
974         break;
975      case KeyRelease:
976         type = EVENT_KEY_UP;
977         key = ev->xkey.keycode;
978         modifiers = ev->xbutton.state & mask;
979         mouse = 0;
980         break;
981      case ButtonPress:
982         if (Mode.events.double_click)
983            type = EVENT_DOUBLE_DOWN;
984         else
985            type = EVENT_MOUSE_DOWN;
986         button = ev->xbutton.button;
987         modifiers = ev->xbutton.state & mask;
988         mouse = 1;
989         break;
990      case ButtonRelease:
991         type = EVENT_MOUSE_UP;
992         button = ev->xbutton.button;
993         modifiers = ev->xbutton.state & mask;
994         mouse = 1;
995         break;
996      case EnterNotify:
997         type = EVENT_MOUSE_ENTER;
998         button = -1;
999         modifiers = ev->xcrossing.state & mask;
1000         mouse = 1;
1001         break;
1002      case LeaveNotify:
1003         /* If frame window, quit if pointer is still inside */
1004         if (ewin && ev->xcrossing.window == EoGetXwin(ewin) &&
1005             (ev->xcrossing.x >= 0 && ev->xcrossing.x < EoGetW(ewin) &&
1006              ev->xcrossing.y >= 0 && ev->xcrossing.y < EoGetH(ewin)))
1007            return 0;
1008         type = EVENT_MOUSE_LEAVE;
1009         button = -1;
1010         modifiers = ev->xcrossing.state & mask;
1011         mouse = 1;
1012         break;
1013      case FocusIn:
1014         type = EVENT_FOCUS_IN;
1015         button = -1;
1016         mouse = 1;
1017         break;
1018      case FocusOut:
1019         type = EVENT_FOCUS_OUT;
1020         button = -1;
1021         mouse = 1;
1022         break;
1023      default:
1024         break;
1025      }
1026
1027    mode_action_destroy = 0;
1028
1029    for (i = 0; i < ac->num; i++)
1030      {
1031         if (!mode_action_destroy)
1032           {
1033              aa = ac->list[i];
1034              ok = 0;
1035              if ((aa->event == type) && (aa->action))
1036                {
1037                   if (mouse)
1038                     {
1039                        if (button < 0)
1040                          {
1041                             if (aa->anymodifier)
1042                                ok = 1;
1043                             else if (aa->modifiers == modifiers)
1044                                ok = 1;
1045                          }
1046                        else
1047                          {
1048                             if (aa->anymodifier)
1049                               {
1050                                  if (aa->anybutton)
1051                                     ok = 1;
1052                                  else if (aa->button == button)
1053                                     ok = 1;
1054                               }
1055                             else if (aa->modifiers == modifiers)
1056                               {
1057                                  if (aa->anybutton)
1058                                     ok = 1;
1059                                  else if (aa->button == button)
1060                                     ok = 1;
1061                               }
1062                          }
1063                     }
1064                   else
1065                     {
1066                        if (aa->anymodifier)
1067                          {
1068                             if (aa->anykey)
1069                                ok = 1;
1070                             else if (aa->key == key)
1071                                ok = 1;
1072                          }
1073                        else if (aa->modifiers == modifiers)
1074                          {
1075                             if (aa->anykey)
1076                                ok = 1;
1077                             else if (aa->key == key)
1078                                ok = 1;
1079                          }
1080                     }
1081                   if (ok)
1082                     {
1083                        handleAction(ewin, aa->action);
1084                        val = 1;
1085                     }
1086                }
1087           }
1088         if (mode_action_destroy)
1089            break;
1090      }
1091
1092    mode_action_destroy = 0;
1093
1094    return val;
1095 }
1096
1097 int
1098 ActionclassesGlobalEvent(XEvent * ev)
1099 {
1100    ActionClass        *ac;
1101    int                 match;
1102
1103    match = 0;
1104    ECORE_LIST_FOR_EACH(aclass_list_global, ac)
1105       match |= ActionclassEvent(ac, ev, GetFocusEwin());
1106
1107    return match;
1108 }
1109
1110 static Timer       *ac_reload_timer = NULL;
1111
1112 static int
1113 _ac_reload(void *data __UNUSED__)
1114 {
1115    AclassConfigLoadConfig("bindings.cfg");
1116    ac_reload_timer = NULL;
1117    return 0;
1118 }
1119
1120 void
1121 ActionclassesReload(void)
1122 {
1123    TIMER_DEL(ac_reload_timer);
1124    TIMER_ADD(ac_reload_timer, 0.2, _ac_reload, NULL);
1125 }
1126
1127 /*
1128  * Actions module
1129  */
1130
1131 static void
1132 AclassSighan(int sig, void *prm __UNUSED__)
1133 {
1134    switch (sig)
1135      {
1136      case ESIGNAL_INIT:
1137         AclassConfigLoadConfig("bindings.cfg");
1138         break;
1139      }
1140 }
1141
1142 static void
1143 AclassIpc(const char *params)
1144 {
1145    const char         *p;
1146    char                cmd[128], prm[4096];
1147    int                 len;
1148    ActionClass        *ac;
1149
1150    cmd[0] = prm[0] = '\0';
1151    p = params;
1152    if (p)
1153      {
1154         len = 0;
1155         sscanf(p, "%100s %4000s %n", cmd, prm, &len);
1156         p += len;
1157      }
1158
1159    if (!p || cmd[0] == '\0' || cmd[0] == '?')
1160      {
1161      }
1162    else if (!strncmp(cmd, "kb", 2))
1163      {
1164         AclassConfigWrite(ActionclassFindGlobal("KEYBINDINGS"), IpcPrintf);
1165      }
1166    else if (!strncmp(cmd, "list", 2))
1167      {
1168         if (prm[0] == '\0')
1169           {
1170              IpcPrintf("Normal:\n");
1171              ECORE_LIST_FOR_EACH(aclass_list, ac) IpcPrintf("%s\n", ac->name);
1172              IpcPrintf("Global:\n");
1173              ECORE_LIST_FOR_EACH(aclass_list_global, ac) IpcPrintf("%s\n",
1174                                                                    ac->name);
1175           }
1176         else if (!strcmp(prm, "all"))
1177           {
1178              ECORE_LIST_FOR_EACH(aclass_list, ac)
1179              {
1180                 IpcPrintf("\n");
1181                 AclassConfigWrite(ac, IpcPrintf);
1182              }
1183              ECORE_LIST_FOR_EACH(aclass_list_global, ac)
1184              {
1185                 IpcPrintf("\n");
1186                 AclassConfigWrite(ac, IpcPrintf);
1187              }
1188           }
1189         else
1190           {
1191              AclassConfigWrite(ActionclassFindAny(prm), IpcPrintf);
1192           }
1193      }
1194    else if (!strcmp(cmd, "load"))
1195      {
1196         if (*prm == '\0')
1197            AclassConfigLoadConfig("bindings.cfg");
1198         else
1199            AclassConfigLoadConfig(prm);
1200      }
1201 }
1202
1203 /* Should only be used via e16keyedit */
1204 static void
1205 IPC_KeybindingsGet(const char *params __UNUSED__)
1206 {
1207    ActionClass        *ac;
1208    Action             *aa;
1209    int                 i, mod;
1210
1211    ac = ActionclassFindGlobal("KEYBINDINGS");
1212    if (!ac)
1213       return;
1214
1215    for (i = 0; i < ac->num; i++)
1216      {
1217         aa = ac->list[i];
1218         if ((aa) && (aa->action) && (aa->event == EVENT_KEY_DOWN))
1219           {
1220              const char         *key;
1221
1222              key = EKeycodeToString(aa->key, 0);
1223              if (!key)
1224                 continue;
1225
1226              mod = 0;
1227              if (aa->modifiers == (ControlMask))
1228                 mod = 1;
1229              else if (aa->modifiers == (Mod1Mask))
1230                 mod = 2;
1231              else if (aa->modifiers == (ShiftMask))
1232                 mod = 3;
1233              else if (aa->modifiers == (ControlMask | Mod1Mask))
1234                 mod = 4;
1235              else if (aa->modifiers == (ShiftMask | ControlMask))
1236                 mod = 5;
1237              else if (aa->modifiers == (ShiftMask | Mod1Mask))
1238                 mod = 6;
1239              else if (aa->modifiers == (ShiftMask | ControlMask | Mod1Mask))
1240                 mod = 7;
1241              else if (aa->modifiers == (Mod2Mask))
1242                 mod = 8;
1243              else if (aa->modifiers == (Mod3Mask))
1244                 mod = 9;
1245              else if (aa->modifiers == (Mod4Mask))
1246                 mod = 10;
1247              else if (aa->modifiers == (Mod5Mask))
1248                 mod = 11;
1249              else if (aa->modifiers == (Mod2Mask | ShiftMask))
1250                 mod = 12;
1251              else if (aa->modifiers == (Mod2Mask | ControlMask))
1252                 mod = 13;
1253              else if (aa->modifiers == (Mod2Mask | Mod1Mask))
1254                 mod = 14;
1255              else if (aa->modifiers == (Mod4Mask | ShiftMask))
1256                 mod = 15;
1257              else if (aa->modifiers == (Mod4Mask | ControlMask))
1258                 mod = 16;
1259              else if (aa->modifiers == (Mod4Mask | ControlMask | ShiftMask))
1260                 mod = 17;
1261              else if (aa->modifiers == (Mod5Mask | ShiftMask))
1262                 mod = 18;
1263              else if (aa->modifiers == (Mod5Mask | ControlMask))
1264                 mod = 19;
1265              else if (aa->modifiers == (Mod5Mask | ControlMask | ShiftMask))
1266                 mod = 20;
1267
1268              if (aa->action->params)
1269                 IpcPrintf("%s %i %i %s\n", key, mod, 0, aa->action->params);
1270              else
1271                 IpcPrintf("%s %i %i\n", key, mod, 0);
1272           }
1273      }
1274 }
1275
1276 /* Should only be used via e16keyedit */
1277 static void
1278 IPC_KeybindingsSet(const char *params)
1279 {
1280    ActionClass        *ac;
1281    Action             *aa;
1282    int                 i, l;
1283    char                buf[FILEPATH_LEN_MAX];
1284    const char         *sp, *ss;
1285
1286    ss = params;
1287    if (!ss || !ss[0])
1288       return;
1289
1290    Mode.keybinds_changed = 1;
1291
1292    ac = ActionclassFindGlobal("KEYBINDINGS");
1293    if (ac)
1294       ActionclassEmpty(ac);
1295    else
1296       ac = ActionclassCreate("KEYBINDINGS", 1);
1297    if (!ac)
1298       return;
1299
1300    i = 0;
1301    l = strlen(ss);
1302    while (i < l)
1303      {
1304         char                key[256];
1305         int                 mod = 0;
1306         int                 act_id = 0;
1307         int                 j = 0, len;
1308
1309         /* put line in buf */
1310         sp = &(ss[i]);
1311         while ((sp[j]) && (sp[j] != '\n'))
1312           {
1313              buf[j] = sp[j];
1314              j++;
1315           }
1316         buf[j] = 0;
1317         if (sp[j] == '\n')
1318            j++;
1319         i += j;
1320
1321         /* parse the line */
1322         len = 0;
1323         sscanf(buf, "%250s %i %i %n", key, &mod, &act_id, &len);
1324         if (mod == 0)
1325            mod = 0;
1326         else if (mod == 1)
1327            mod = ControlMask;
1328         else if (mod == 2)
1329            mod = Mod1Mask;
1330         else if (mod == 3)
1331            mod = ShiftMask;
1332         else if (mod == 4)
1333            mod = ControlMask | Mod1Mask;
1334         else if (mod == 5)
1335            mod = ShiftMask | ControlMask;
1336         else if (mod == 6)
1337            mod = ShiftMask | Mod1Mask;
1338         else if (mod == 7)
1339            mod = ShiftMask | ControlMask | Mod1Mask;
1340         else if (mod == 8)
1341            mod = Mod2Mask;
1342         else if (mod == 9)
1343            mod = Mod3Mask;
1344         else if (mod == 10)
1345            mod = Mod4Mask;
1346         else if (mod == 11)
1347            mod = Mod5Mask;
1348         else if (mod == 12)
1349            mod = Mod2Mask | ShiftMask;
1350         else if (mod == 13)
1351            mod = Mod2Mask | ControlMask;
1352         else if (mod == 14)
1353            mod = Mod2Mask | Mod1Mask;
1354         else if (mod == 15)
1355            mod = Mod4Mask | ShiftMask;
1356         else if (mod == 16)
1357            mod = Mod4Mask | ControlMask;
1358         else if (mod == 17)
1359            mod = Mod4Mask | ControlMask | ShiftMask;
1360         else if (mod == 18)
1361            mod = Mod5Mask | ShiftMask;
1362         else if (mod == 19)
1363            mod = Mod5Mask | ControlMask;
1364         else if (mod == 20)
1365            mod = Mod5Mask | ControlMask | ShiftMask;
1366
1367         aa = ActionCreate(4, 0, mod, 0, 0, 0, key, NULL);
1368         ActionclassAddAction(ac, aa);
1369         ActionAddTo(aa, buf + len);
1370         GrabActionKey(aa);
1371      }
1372
1373    BindingsSave();
1374 }
1375
1376 static const IpcItem AclassIpcArray[] = {
1377    {
1378     AclassIpc,
1379     "aclass", "ac",
1380     "Action class functions",
1381     "  aclass kb                 List key bindings\n"
1382     "  aclass list [name/all]    List action class[es]\n"
1383     "  aclass load [name]        Reload action classes (default is bindings.cfg)\n"}
1384    ,
1385    {
1386     IPC_KeybindingsGet, "get_keybindings", NULL,
1387     "List keybindings (deprecated)", NULL}
1388    ,
1389    {
1390     IPC_KeybindingsSet, "set_keybindings", NULL, "Set keybindings (deprecated)",
1391     NULL}
1392    ,
1393 };
1394 #define N_IPC_FUNCS (sizeof(AclassIpcArray)/sizeof(IpcItem))
1395
1396 /*
1397  * Module descriptor
1398  */
1399 extern const EModule ModAclass;
1400 const EModule       ModAclass = {
1401    "aclass", "ac",
1402    AclassSighan,
1403    {N_IPC_FUNCS, AclassIpcArray},
1404    {0, NULL}
1405 };
1406
1407 void
1408 GrabButtonGrabs(Win win)
1409 {
1410    ActionClass        *ac;
1411    int                 j;
1412    Action             *aa;
1413    unsigned int        mod, button, mask;
1414
1415    ac = ActionclassFind("BUTTONBINDINGS");
1416    if (!ac)
1417       return;
1418
1419    ac->ref_count++;
1420    for (j = 0; j < ac->num; j++)
1421      {
1422         aa = ac->list[j];
1423         if ((!aa) || ((aa->event != EVENT_MOUSE_DOWN) &&
1424                       (aa->event != EVENT_MOUSE_UP)))
1425            continue;
1426
1427         mod = (aa->anymodifier) ? AnyModifier : aa->modifiers;
1428         button = (aa->anybutton) ? AnyButton : aa->button;
1429         mask = ButtonPressMask | ButtonReleaseMask;
1430
1431         GrabButtonSet(button, mod, win, mask, ECSR_PGRAB, 1);
1432      }
1433 }
1434
1435 void
1436 UnGrabButtonGrabs(Win win)
1437 {
1438    ActionClass        *ac;
1439    int                 j;
1440    Action             *aa;
1441    unsigned int        mod, button;
1442
1443    ac = ActionclassFind("BUTTONBINDINGS");
1444    if (!ac)
1445       return;
1446
1447    ac->ref_count--;
1448    for (j = 0; j < ac->num; j++)
1449      {
1450         aa = ac->list[j];
1451         if ((!aa) || ((aa->event != EVENT_MOUSE_DOWN)
1452                       && (aa->event != EVENT_MOUSE_UP)))
1453            continue;
1454
1455         mod = (aa->anymodifier) ? AnyModifier : aa->modifiers;
1456         button = (aa->anybutton) ? AnyButton : aa->button;
1457
1458         GrabButtonRelease(button, mod, win);
1459      }
1460 }
1461
1462 static void
1463 GrabActionKey(Action * aa)
1464 {
1465    int                 mod;
1466
1467    if (!aa->key)
1468       return;
1469
1470    mod = (aa->anymodifier) ? AnyModifier : aa->modifiers;
1471
1472    GrabKeySet(aa->key, mod, VROOT);
1473 }
1474
1475 static void
1476 UnGrabActionKey(Action * aa)
1477 {
1478    int                 mod;
1479
1480    if (!aa->key)
1481       return;
1482
1483    mod = (aa->anymodifier) ? AnyModifier : aa->modifiers;
1484
1485    GrabKeyRelease(aa->key, mod, VROOT);
1486 }