chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / src / groups.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 "borders.h"
26 #include "dialog.h"
27 #include "e16-ecore_list.h"
28 #include "emodule.h"
29 #include "ewins.h"
30 #include "groups.h"
31 #include "settings.h"
32 #include "snaps.h"
33 #include "timers.h"
34 #include <math.h>
35
36 #define DEBUG_GROUPS 0
37 #if DEBUG_GROUPS
38 #define Dprintf(fmt, ...) Eprintf("%s: " fmt, __func__, __VA_ARGS__)
39 #else
40 #define Dprintf(fmt...)
41 #endif
42
43 #define USE_GROUP_SHOWHIDE 1    /* Enable group borders */
44
45 #define SET_OFF    0
46 #define SET_ON     1
47 #define SET_TOGGLE 2
48
49 static Ecore_List  *group_list = NULL;
50
51 static struct {
52    GroupConfig         dflt;
53    char                swapmove;
54 } Conf_groups;
55
56 static struct {
57    Group              *current;
58 } Mode_groups;
59
60 static void         AddEwinToGroup(EWin * ewin, Group * g);
61 static void         RemoveEwinFromGroup(EWin * ewin, Group * g);
62
63 int
64 GroupsGetSwapmove(void)
65 {
66    return Conf_groups.swapmove;
67 }
68
69 static Group       *
70 GroupCreate(int gid)
71 {
72    Group              *g;
73    double              t;
74
75    g = ECALLOC(Group, 1);
76    if (!g)
77       return NULL;
78
79    if (!group_list)
80       group_list = ecore_list_new();
81    ecore_list_append(group_list, g);
82
83    if (gid == -1)
84      {
85         /* Create new group id */
86         t = GetTime();
87         g->index = (int)((GetTime() - (floor(t / 1000) * 1000)) * 10000);
88         /* g->index = (int)(GetTime() * 100); */
89      }
90    else
91      {
92         /* Use given group id */
93         g->index = gid;
94      }
95    g->cfg.iconify = Conf_groups.dflt.iconify;
96    g->cfg.kill = Conf_groups.dflt.kill;
97    g->cfg.move = Conf_groups.dflt.move;
98    g->cfg.raise = Conf_groups.dflt.raise;
99    g->cfg.set_border = Conf_groups.dflt.set_border;
100    g->cfg.stick = Conf_groups.dflt.stick;
101    g->cfg.shade = Conf_groups.dflt.shade;
102
103    Dprintf("grp=%p gid=%d\n", g, g->index);
104    return g;
105 }
106
107 static void
108 GroupDestroy(Group * g)
109 {
110    if (!g)
111       return;
112
113    Dprintf("grp=%p gid=%d\n", g, g->index);
114    ecore_list_node_remove(group_list, g);
115
116    if (g == Mode_groups.current)
117       Mode_groups.current = NULL;
118    Efree(g->members);
119
120    Efree(g);
121 }
122
123 static int
124 GroupMatchId(const void *data, const void *match)
125 {
126    return ((const Group *)data)->index != (int)(long)match;
127 }
128
129 static Group       *
130 GroupFind(int gid)
131 {
132    return (Group *) ecore_list_find(group_list, GroupMatchId,
133                                     (void *)(long)gid);
134 }
135
136 void
137 GroupRemember(int gid)
138 {
139    Group              *g;
140
141    g = GroupFind(gid);
142    if (!g)
143       return;
144
145    g->save = 1;
146 }
147
148 static Group       *
149 GroupFind2(const char *groupid)
150 {
151    int                 gid;
152
153    if (groupid[0] == '*' || groupid[0] == '\0')
154       return Mode_groups.current;
155
156    gid = -1;
157    sscanf(groupid, "%d", &gid);
158    if (gid <= 0)
159       return NULL;
160
161    return GroupFind(gid);
162 }
163
164 #if ENABLE_DIALOGS
165 static void
166 CopyGroupConfig(GroupConfig * src, GroupConfig * dest)
167 {
168    if (!(src && dest))
169       return;
170
171    memcpy(dest, src, sizeof(GroupConfig));
172 }
173 #endif /* ENABLE_DIALOGS */
174
175 static void
176 BreakWindowGroup(EWin * ewin, Group * g)
177 {
178    int                 i, j;
179    EWin               *ewin2;
180    Group              *g2;
181
182    Dprintf("ewin=%p group=%p gid=%d\n", ewin, g, g->index);
183    if (!ewin || !ewin->groups)
184       return;
185
186    for (j = 0; j < ewin->num_groups; j++)
187      {
188         g2 = ewin->groups[j];
189         if (g && g != g2)
190            continue;
191
192         for (i = 0; i < g2->num_members; i++)
193           {
194              ewin2 = g2->members[0];
195              RemoveEwinFromGroup(ewin2, g2);
196              SnapshotEwinUpdate(ewin2, SNAP_USE_GROUPS);
197           }
198      }
199 }
200
201 static void
202 BuildWindowGroup(EWin ** ewins, int num, int gid)
203 {
204    int                 i;
205    Group              *group;
206
207    Mode_groups.current = group = GroupCreate(gid);
208
209    for (i = 0; i < num; i++)
210       AddEwinToGroup(ewins[i], group);
211 }
212
213 Group             **
214 GroupsGetList(int *pnum)
215 {
216    return (Group **) ecore_list_items_get(group_list, pnum);
217 }
218
219 Group              *const *
220 EwinGetGroups(const EWin * ewin, int *num)
221 {
222    *num = ewin->num_groups;
223    return ewin->groups;
224 }
225
226 #if ENABLE_DIALOGS
227 static Group      **
228 ListWinGroups(const EWin * ewin, char group_select, int *num)
229 {
230    Group             **groups = NULL;
231    Group             **groups2 = NULL;
232    int                 i, j, killed = 0;
233
234    switch (group_select)
235      {
236      case GROUP_SELECT_EWIN_ONLY:
237         groups = EMALLOC(Group *, ewin->num_groups);
238         if (!groups)
239            break;
240         memcpy(groups, ewin->groups, sizeof(Group *) * ewin->num_groups);
241         *num = ewin->num_groups;
242         break;
243      case GROUP_SELECT_ALL_EXCEPT_EWIN:
244         groups2 = GroupsGetList(num);
245         if (!groups2)
246            break;
247
248         for (i = 0; i < (*num); i++)
249           {
250              for (j = 0; j < ewin->num_groups; j++)
251                {
252                   if (ewin->groups[j] == groups2[i])
253                     {
254                        groups2[i] = NULL;
255                        killed++;
256                     }
257                }
258           }
259         groups = EMALLOC(Group *, *num - killed);
260         if (groups)
261           {
262              j = 0;
263              for (i = 0; i < (*num); i++)
264                 if (groups2[i])
265                    groups[j++] = groups2[i];
266              (*num) -= killed;
267           }
268         Efree(groups2);
269         break;
270      case GROUP_SELECT_ALL:
271      default:
272         groups = GroupsGetList(num);
273         break;
274      }
275
276    return groups;
277 }
278 #endif /* ENABLE_DIALOGS */
279
280 static void
281 _GroupAddEwin(Group * g, EWin * ewin)
282 {
283    int                 i;
284
285    for (i = 0; i < ewin->num_groups; i++)
286       if (ewin->groups[i] == g)
287          return;
288
289    ewin->num_groups++;
290    ewin->groups = EREALLOC(Group *, ewin->groups, ewin->num_groups);
291    ewin->groups[ewin->num_groups - 1] = g;
292    g->num_members++;
293    g->members = EREALLOC(EWin *, g->members, g->num_members);
294    g->members[g->num_members - 1] = ewin;
295 }
296
297 static void
298 AddEwinToGroup(EWin * ewin, Group * g)
299 {
300    if (!ewin || !g)
301       return;
302
303    _GroupAddEwin(g, ewin);
304    SnapshotEwinUpdate(ewin, SNAP_USE_GROUPS);
305 }
306
307 void
308 GroupsEwinAdd(EWin * ewin, const int *pgid, int ngid)
309 {
310    Group              *g;
311    int                 i, gid;
312
313    for (i = 0; i < ngid; i++)
314      {
315         gid = pgid[i];
316         g = GroupFind(gid);
317         Dprintf("ewin=%p gid=%d grp=%p\n", ewin, gid, g);
318         if (!g)
319           {
320              /* This should not happen, but may if group/snap configs are corrupted */
321              BuildWindowGroup(&ewin, 1, gid);
322           }
323         else
324           {
325              _GroupAddEwin(g, ewin);
326           }
327      }
328    SnapshotEwinUpdate(ewin, SNAP_USE_GROUPS);
329 }
330
331 static int
332 EwinInGroup(const EWin * ewin, const Group * g)
333 {
334    int                 i;
335
336    if (ewin && g)
337      {
338         for (i = 0; i < g->num_members; i++)
339           {
340              if (g->members[i] == ewin)
341                 return 1;
342           }
343      }
344    return 0;
345 }
346
347 Group              *
348 EwinsInGroup(const EWin * ewin1, const EWin * ewin2)
349 {
350    int                 i;
351
352    if (ewin1 && ewin2)
353      {
354         for (i = 0; i < ewin1->num_groups; i++)
355           {
356              if (EwinInGroup(ewin2, ewin1->groups[i]))
357                 return ewin1->groups[i];
358           }
359      }
360    return NULL;
361 }
362
363 void
364 RemoveEwinFromGroup(EWin * ewin, Group * g)
365 {
366    int                 i, j, k, i2;
367
368    if (!ewin || !g)
369       return;
370
371    for (k = 0; k < ewin->num_groups; k++)
372      {
373         /* is the window actually part of the given group */
374         if (ewin->groups[k] != g)
375            continue;
376
377         for (i = 0; i < g->num_members; i++)
378           {
379              if (g->members[i] != ewin)
380                 continue;
381
382              /* remove it from the group */
383              for (j = i; j < g->num_members - 1; j++)
384                 g->members[j] = g->members[j + 1];
385              g->num_members--;
386              if (g->num_members > 0)
387                 g->members = EREALLOC(EWin *, g->members, g->num_members);
388              else if (g->save)
389                {
390                   Efree(g->members);
391                   g->members = NULL;
392                }
393              else
394                {
395                   GroupDestroy(g);
396                }
397
398              /* and remove the group from the groups that the window is in */
399              for (i2 = k; i2 < ewin->num_groups - 1; i2++)
400                 ewin->groups[i2] = ewin->groups[i2 + 1];
401              ewin->num_groups--;
402              if (ewin->num_groups <= 0)
403                {
404                   Efree(ewin->groups);
405                   ewin->groups = NULL;
406                   ewin->num_groups = 0;
407                }
408              else
409                 ewin->groups =
410                    EREALLOC(Group *, ewin->groups, ewin->num_groups);
411
412              GroupsSave();
413              return;
414           }
415      }
416 }
417
418 void
419 GroupsEwinRemove(EWin * ewin)
420 {
421    int                 num, i;
422
423    num = ewin->num_groups;
424    for (i = 0; i < num; i++)
425       RemoveEwinFromGroup(ewin, ewin->groups[0]);
426 }
427
428 #if ENABLE_DIALOGS
429 static char       **
430 GetWinGroupMemberNames(Group ** groups, int num)
431 {
432    int                 i, j, len;
433    char              **group_member_strings;
434    const char         *name;
435
436    group_member_strings = ECALLOC(char *, num);
437
438    if (!group_member_strings)
439       return NULL;
440
441    for (i = 0; i < num; i++)
442      {
443         group_member_strings[i] = EMALLOC(char, 1024);
444
445         if (!group_member_strings[i])
446            break;
447
448         len = 0;
449         for (j = 0; j < groups[i]->num_members; j++)
450           {
451              name = EwinGetTitle(groups[i]->members[j]);
452              if (!name)         /* Should never happen */
453                 continue;
454              len += Esnprintf(group_member_strings[i] + len, 1024 - len,
455                               "%s\n", name);
456              if (len >= 1024)
457                 break;
458           }
459      }
460
461    return group_member_strings;
462 }
463 #endif /* ENABLE_DIALOGS */
464
465 #if USE_GROUP_SHOWHIDE
466 static void
467 ShowHideWinGroups(EWin * ewin, int group_index, char onoff)
468 {
469    EWin              **gwins;
470    int                 i, num;
471    const Border       *b = NULL;
472
473    if (!ewin || group_index >= ewin->num_groups)
474       return;
475
476    if (group_index < 0)
477      {
478         gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_ANY, 0, &num);
479      }
480    else
481      {
482         gwins = ewin->groups[group_index]->members;
483         num = ewin->groups[group_index]->num_members;
484      }
485
486    if (onoff == SET_TOGGLE)
487       onoff = (ewin->border == ewin->normal_border) ? SET_ON : SET_OFF;
488
489    for (i = 0; i < num; i++)
490      {
491         if (onoff == SET_ON)
492            b = BorderFind(gwins[i]->border->group_border_name);
493         else
494            b = gwins[i]->normal_border;
495
496         EwinBorderChange(gwins[i], b, 0);
497      }
498    if (group_index < 0)
499       Efree(gwins);
500 }
501 #else
502
503 #define ShowHideWinGroups(ewin, group_index, onoff)
504
505 #endif /* USE_GROUP_SHOWHIDE */
506
507 void
508 GroupsSave(void)
509 {
510    Group              *g;
511    FILE               *f;
512    char                s[1024];
513
514    if (ecore_list_count(group_list) <= 0)
515       return;
516
517    Esnprintf(s, sizeof(s), "%s.groups", EGetSavePrefix());
518    f = fopen(s, "w");
519    if (!f)
520       return;
521
522    ECORE_LIST_FOR_EACH(group_list, g)
523    {
524       if (!g->save)
525          continue;
526
527       fprintf(f, "NEW: %i\n", g->index);
528       fprintf(f, "ICONIFY: %i\n", g->cfg.iconify);
529       fprintf(f, "KILL: %i\n", g->cfg.kill);
530       fprintf(f, "MOVE: %i\n", g->cfg.move);
531       fprintf(f, "RAISE: %i\n", g->cfg.raise);
532       fprintf(f, "SET_BORDER: %i\n", g->cfg.set_border);
533       fprintf(f, "STICK: %i\n", g->cfg.stick);
534       fprintf(f, "SHADE: %i\n", g->cfg.shade);
535    }
536
537    fclose(f);
538 }
539
540 void
541 GroupsLoad(void)
542 {
543    FILE               *f;
544    char                s[1024];
545    Group              *g = NULL;
546
547    Esnprintf(s, sizeof(s), "%s.groups", EGetSavePrefix());
548    f = fopen(s, "r");
549    if (!f)
550       return;
551
552    while (fgets(s, sizeof(s), f))
553      {
554         char                ss[128];
555         int                 ii;
556
557         if (strlen(s) > 0)
558            s[strlen(s) - 1] = 0;
559         ii = 0;
560         sscanf(s, "%100s %d", ss, &ii);
561
562         if (!strcmp(ss, "NEW:"))
563           {
564              g = GroupCreate(ii);
565              continue;
566           }
567         if (!g)
568            continue;
569
570         if (!strcmp(ss, "ICONIFY:"))
571           {
572              g->cfg.iconify = ii;
573           }
574         else if (!strcmp(ss, "KILL:"))
575           {
576              g->cfg.kill = ii;
577           }
578         else if (!strcmp(ss, "MOVE:"))
579           {
580              g->cfg.move = ii;
581           }
582         else if (!strcmp(ss, "RAISE:"))
583           {
584              g->cfg.raise = ii;
585           }
586         else if (!strcmp(ss, "SET_BORDER:"))
587           {
588              g->cfg.set_border = ii;
589           }
590         else if (!strcmp(ss, "STICK:"))
591           {
592              g->cfg.stick = ii;
593           }
594         else if (!strcmp(ss, "SHADE:"))
595           {
596              g->cfg.shade = ii;
597           }
598      }
599    fclose(f);
600 }
601
602 #if ENABLE_DIALOGS
603
604 #define GROUP_OP_ADD    1
605 #define GROUP_OP_DEL    2
606 #define GROUP_OP_BREAK  3
607
608 static int          tmp_group_index;
609 static int          tmp_index;
610 static EWin        *tmp_ewin;
611 static Group      **tmp_groups;
612 static int          tmp_group_num;
613 static int          tmp_action;
614
615 static void
616 ChooseGroup(Dialog * d __UNUSED__, int val, void *data __UNUSED__)
617 {
618    if (((val == 0) || (val == 2)) && tmp_groups)
619      {
620         ShowHideWinGroups(tmp_ewin, tmp_index, SET_OFF);
621      }
622    if (val == 0)
623      {
624         if (tmp_groups)
625           {
626              switch (tmp_action)
627                {
628                case GROUP_OP_ADD:
629                   AddEwinToGroup(tmp_ewin, tmp_groups[tmp_group_index]);
630                   break;
631                case GROUP_OP_DEL:
632                   RemoveEwinFromGroup(tmp_ewin, tmp_groups[tmp_group_index]);
633                   break;
634                case GROUP_OP_BREAK:
635                   BreakWindowGroup(tmp_ewin, tmp_groups[tmp_group_index]);
636                   break;
637                default:
638                   break;
639                }
640           }
641      }
642    if (((val == 0) || (val == 2)) && tmp_groups)
643      {
644         Efree(tmp_groups);
645         tmp_groups = NULL;
646
647         GroupsSave();
648      }
649 }
650
651 static void
652 GroupCallback(Dialog * d __UNUSED__, int val, void *data __UNUSED__)
653 {
654    ShowHideWinGroups(tmp_ewin, tmp_index, SET_OFF);
655    ShowHideWinGroups(tmp_ewin, val, SET_ON);
656    tmp_index = val;
657 }
658
659 static void
660 _DlgFillGroupChoose(Dialog * d, DItem * table, void *data)
661 {
662    DItem              *di, *radio;
663    int                 i, num_groups;
664    char              **group_member_strings;
665    const char         *message = (const char *)data;
666
667    DialogItemTableSetOptions(table, 2, 0, 0, 0);
668
669    di = DialogAddItem(table, DITEM_TEXT);
670    DialogItemSetColSpan(di, 2);
671    DialogItemSetAlign(di, 0, 512);
672    DialogItemSetText(di, message);
673
674    num_groups = tmp_group_num;
675    group_member_strings = GetWinGroupMemberNames(tmp_groups, num_groups);
676
677    radio = di = DialogAddItem(table, DITEM_RADIOBUTTON);
678    DialogItemSetColSpan(di, 2);
679    DialogItemSetCallback(di, GroupCallback, 0, (void *)d);
680    DialogItemSetText(di, group_member_strings[0]);
681    DialogItemRadioButtonSetFirst(di, radio);
682    DialogItemRadioButtonGroupSetVal(di, 0);
683
684    for (i = 1; i < num_groups; i++)
685      {
686         di = DialogAddItem(table, DITEM_RADIOBUTTON);
687         DialogItemSetColSpan(di, 2);
688         DialogItemSetCallback(di, GroupCallback, i, NULL);
689         DialogItemSetText(di, group_member_strings[i]);
690         DialogItemRadioButtonSetFirst(di, radio);
691         DialogItemRadioButtonGroupSetVal(di, i);
692      }
693    DialogItemRadioButtonGroupSetValPtr(radio, &tmp_group_index);
694
695    StrlistFree(group_member_strings, num_groups);
696 }
697
698 static const DialogDef DlgGroupChoose = {
699    "GROUP_SELECTION",
700    NULL,
701    N_("Window Group Selection"),
702    SOUND_SETTINGS_GROUP,
703    "pix/group.png",
704    N_("Enlightenment Window Group\n" "Selection Dialog\n"),
705    _DlgFillGroupChoose,
706    DLG_OC, ChooseGroup,
707 };
708
709 static void
710 ChooseGroupDialog(EWin * ewin, const char *message, char group_select,
711                   int action)
712 {
713    int                 num_groups;
714
715    if (!ewin)
716       return;
717
718    tmp_ewin = ewin;
719    tmp_group_index = tmp_index = 0;
720    tmp_action = action;
721    tmp_groups = ListWinGroups(ewin, group_select, &num_groups);
722    tmp_group_num = num_groups;
723
724    if ((num_groups == 0)
725        && (action == GROUP_OP_BREAK || action == GROUP_OP_DEL))
726      {
727         DialogOK(_("Window Group Error"),
728                  _
729                  ("\n  This window currently does not belong to any groups.  \n"
730                   "  You can only destroy groups or remove windows from groups  \n"
731                   "  through a window that actually belongs to at least one group.\n\n"));
732         return;
733      }
734    if ((num_groups == 0) && (group_select == GROUP_SELECT_ALL_EXCEPT_EWIN))
735      {
736         DialogOK(_("Window Group Error"),
737                  _("\n  Currently, no groups exist or this window  \n"
738                    "  already belongs to all existing groups.  \n"
739                    "  You have to start other groups first.  \n\n"));
740         return;
741      }
742    if (!tmp_groups)
743      {
744         DialogOK(_("Window Group Error"),
745                  _
746                  ("\n  Currently, no groups exist. You have to start a group first.\n\n"));
747         return;
748      }
749
750    ShowHideWinGroups(ewin, 0, SET_ON);
751
752    DialogShowSimple(&DlgGroupChoose, (void *)message);
753 }
754
755 typedef struct {
756    EWin               *ewin;
757    GroupConfig         cfg;     /* Dialog data for current group */
758    GroupConfig        *cfgs;    /* Work copy of ewin group cfgs */
759    int                 ngrp;
760    unsigned int        current;
761 } EwinGroupDlgData;
762
763 static void
764 CB_ConfigureGroup(Dialog * d, int val, void *data __UNUSED__)
765 {
766    EwinGroupDlgData   *dd = (EwinGroupDlgData *) DialogGetData(d);
767    EWin               *ewin;
768    int                 i;
769
770    if (!dd)
771       return;
772
773    /* Check ewin */
774    ewin = EwinFindByPtr(dd->ewin);
775    if (ewin && ewin->num_groups != dd->ngrp)
776       ewin = NULL;
777
778    if (val < 2 && ewin)
779      {
780         CopyGroupConfig(&(dd->cfg), &(dd->cfgs[dd->current]));
781         for (i = 0; i < ewin->num_groups; i++)
782            CopyGroupConfig(dd->cfgs + i, &(ewin->groups[i]->cfg));
783      }
784    if ((val == 0) || (val == 2))
785      {
786         ShowHideWinGroups(ewin, dd->current, SET_OFF);
787         Efree(dd->cfgs);
788         Efree(dd);
789         DialogSetData(d, NULL);
790      }
791    autosave();
792 }
793
794 static void
795 GroupSelectCallback(Dialog * d, int val, void *data __UNUSED__)
796 {
797    EwinGroupDlgData   *dd = (EwinGroupDlgData *) DialogGetData(d);
798
799    CopyGroupConfig(&(dd->cfg), &(dd->cfgs[dd->current]));
800    CopyGroupConfig(&(dd->cfgs[val]), &(dd->cfg));
801    DialogRedraw(d);
802    ShowHideWinGroups(dd->ewin, dd->current, SET_OFF);
803    ShowHideWinGroups(dd->ewin, val, SET_ON);
804    dd->current = val;
805 }
806
807 static void
808 _DlgFillGroups(Dialog * d, DItem * table, void *data)
809 {
810    EWin               *ewin = (EWin *) data;
811    DItem              *radio, *di;
812    int                 i;
813    char              **group_member_strings;
814    EwinGroupDlgData   *dd;
815
816    dd = ECALLOC(EwinGroupDlgData, 1);
817    if (!dd)
818       return;
819
820    dd->ewin = ewin;
821    dd->cfgs = EMALLOC(GroupConfig, ewin->num_groups);
822    dd->ngrp = ewin->num_groups;
823    dd->current = 0;
824    for (i = 0; i < ewin->num_groups; i++)
825       CopyGroupConfig(&(ewin->groups[i]->cfg), dd->cfgs + i);
826    CopyGroupConfig(dd->cfgs, &(dd->cfg));
827    DialogSetData(d, dd);
828
829    ShowHideWinGroups(ewin, 0, SET_ON);
830
831    DialogItemTableSetOptions(table, 2, 0, 0, 0);
832
833    di = DialogAddItem(table, DITEM_TEXT);
834    DialogItemSetColSpan(di, 2);
835    DialogItemSetAlign(di, 0, 512);
836    DialogItemSetText(di, _("   Pick the group to configure:   "));
837
838    group_member_strings =
839       GetWinGroupMemberNames(ewin->groups, ewin->num_groups);
840
841    radio = di = DialogAddItem(table, DITEM_RADIOBUTTON);
842    DialogItemSetColSpan(di, 2);
843    DialogItemSetCallback(di, GroupSelectCallback, 0, d);
844    DialogItemSetText(di, group_member_strings[0]);
845    DialogItemRadioButtonSetFirst(di, radio);
846    DialogItemRadioButtonGroupSetVal(di, 0);
847
848    for (i = 1; i < ewin->num_groups; i++)
849      {
850         di = DialogAddItem(table, DITEM_RADIOBUTTON);
851         DialogItemSetColSpan(di, 2);
852         DialogItemSetCallback(di, GroupSelectCallback, i, d);
853         DialogItemSetText(di, group_member_strings[i]);
854         DialogItemRadioButtonSetFirst(di, radio);
855         DialogItemRadioButtonGroupSetVal(di, i);
856      }
857    DialogItemRadioButtonGroupSetValPtr(radio, &tmp_index);
858
859    StrlistFree(group_member_strings, ewin->num_groups);
860
861    di = DialogAddItem(table, DITEM_SEPARATOR);
862    DialogItemSetColSpan(di, 2);
863
864    di = DialogAddItem(table, DITEM_TEXT);
865    DialogItemSetColSpan(di, 2);
866    DialogItemSetAlign(di, 0, 512);
867    DialogItemSetText(di, _("  The following actions are  \n"
868                            "  applied to all group members:  "));
869
870    di = DialogAddItem(table, DITEM_CHECKBUTTON);
871    DialogItemSetColSpan(di, 2);
872    DialogItemSetText(di, _("Changing Border Style"));
873    DialogItemCheckButtonSetPtr(di, &(dd->cfg.set_border));
874
875    di = DialogAddItem(table, DITEM_CHECKBUTTON);
876    DialogItemSetColSpan(di, 2);
877    DialogItemSetText(di, _("Iconifying"));
878    DialogItemCheckButtonSetPtr(di, &(dd->cfg.iconify));
879
880    di = DialogAddItem(table, DITEM_CHECKBUTTON);
881    DialogItemSetColSpan(di, 2);
882    DialogItemSetText(di, _("Killing"));
883    DialogItemCheckButtonSetPtr(di, &(dd->cfg.kill));
884
885    di = DialogAddItem(table, DITEM_CHECKBUTTON);
886    DialogItemSetColSpan(di, 2);
887    DialogItemSetText(di, _("Moving"));
888    DialogItemCheckButtonSetPtr(di, &(dd->cfg.move));
889
890    di = DialogAddItem(table, DITEM_CHECKBUTTON);
891    DialogItemSetColSpan(di, 2);
892    DialogItemSetText(di, _("Raising/Lowering"));
893    DialogItemCheckButtonSetPtr(di, &(dd->cfg.raise));
894
895    di = DialogAddItem(table, DITEM_CHECKBUTTON);
896    DialogItemSetColSpan(di, 2);
897    DialogItemSetText(di, _("Sticking"));
898    DialogItemCheckButtonSetPtr(di, &(dd->cfg.stick));
899
900    di = DialogAddItem(table, DITEM_CHECKBUTTON);
901    DialogItemSetColSpan(di, 2);
902    DialogItemSetText(di, _("Shading"));
903    DialogItemCheckButtonSetPtr(di, &(dd->cfg.shade));
904 }
905
906 static const DialogDef DlgGroups = {
907    "CONFIGURE_GROUP",
908    NULL,
909    N_("Window Group Settings"),
910    SOUND_SETTINGS_GROUP,
911    "pix/group.png",
912    N_("Enlightenment Window Group\n" "Settings Dialog\n"),
913    _DlgFillGroups,
914    DLG_OAC, CB_ConfigureGroup,
915 };
916
917 static void
918 SettingsGroups(EWin * ewin)
919 {
920    if (!ewin)
921       return;
922
923    if (ewin->num_groups == 0)
924      {
925         DialogOK(_("Window Group Error"),
926                  _
927                  ("\n  This window currently does not belong to any groups.  \n\n"));
928         return;
929      }
930
931    DialogShowSimple(&DlgGroups, ewin);
932 }
933
934 static GroupConfig  tmp_group_cfg;
935 static char         tmp_group_swap;
936 static void
937 CB_ConfigureDefaultGroupSettings(Dialog * d __UNUSED__, int val,
938                                  void *data __UNUSED__)
939 {
940    if (val < 2)
941      {
942         CopyGroupConfig(&tmp_group_cfg, &(Conf_groups.dflt));
943         Conf_groups.swapmove = tmp_group_swap;
944      }
945    autosave();
946 }
947
948 static void
949 _DlgFillGroupDefaults(Dialog * d __UNUSED__, DItem * table,
950                       void *data __UNUSED__)
951 {
952    DItem              *di;
953
954    CopyGroupConfig(&(Conf_groups.dflt), &tmp_group_cfg);
955    tmp_group_swap = Conf_groups.swapmove;
956
957    DialogItemTableSetOptions(table, 2, 0, 0, 0);
958
959    di = DialogAddItem(table, DITEM_TEXT);
960    DialogItemSetColSpan(di, 2);
961    DialogItemSetAlign(di, 0, 512);
962    DialogItemSetText(di, _(" Per-group settings: "));
963
964    di = DialogAddItem(table, DITEM_SEPARATOR);
965    DialogItemSetColSpan(di, 2);
966
967    di = DialogAddItem(table, DITEM_CHECKBUTTON);
968    DialogItemSetColSpan(di, 2);
969    DialogItemSetText(di, _("Changing Border Style"));
970    DialogItemCheckButtonSetPtr(di, &(tmp_group_cfg.set_border));
971
972    di = DialogAddItem(table, DITEM_CHECKBUTTON);
973    DialogItemSetColSpan(di, 2);
974    DialogItemSetText(di, _("Iconifying"));
975    DialogItemCheckButtonSetPtr(di, &(tmp_group_cfg.iconify));
976
977    di = DialogAddItem(table, DITEM_CHECKBUTTON);
978    DialogItemSetColSpan(di, 2);
979    DialogItemSetText(di, _("Killing"));
980    DialogItemCheckButtonSetPtr(di, &(tmp_group_cfg.kill));
981
982    di = DialogAddItem(table, DITEM_CHECKBUTTON);
983    DialogItemSetColSpan(di, 2);
984    DialogItemSetText(di, _("Moving"));
985    DialogItemCheckButtonSetPtr(di, &(tmp_group_cfg.move));
986
987    di = DialogAddItem(table, DITEM_CHECKBUTTON);
988    DialogItemSetColSpan(di, 2);
989    DialogItemSetText(di, _("Raising/Lowering"));
990    DialogItemCheckButtonSetPtr(di, &(tmp_group_cfg.raise));
991
992    di = DialogAddItem(table, DITEM_CHECKBUTTON);
993    DialogItemSetColSpan(di, 2);
994    DialogItemSetText(di, _("Sticking"));
995    DialogItemCheckButtonSetPtr(di, &(tmp_group_cfg.stick));
996
997    di = DialogAddItem(table, DITEM_CHECKBUTTON);
998    DialogItemSetColSpan(di, 2);
999    DialogItemSetText(di, _("Shading"));
1000    DialogItemCheckButtonSetPtr(di, &(tmp_group_cfg.shade));
1001
1002    di = DialogAddItem(table, DITEM_SEPARATOR);
1003    DialogItemSetColSpan(di, 2);
1004
1005    di = DialogAddItem(table, DITEM_TEXT);
1006    DialogItemSetColSpan(di, 2);
1007    DialogItemSetAlign(di, 0, 512);
1008    DialogItemSetText(di, _(" Global settings: "));
1009
1010    di = DialogAddItem(table, DITEM_CHECKBUTTON);
1011    DialogItemSetColSpan(di, 2);
1012    DialogItemSetText(di, _("Swap Window Locations"));
1013    DialogItemCheckButtonSetPtr(di, &(tmp_group_swap));
1014 }
1015
1016 const DialogDef     DlgGroupDefaults = {
1017    "CONFIGURE_DEFAULT_GROUP_CONTROL",
1018    N_("Groups"),
1019    N_("Default Group Control Settings"),
1020    SOUND_SETTINGS_GROUP,
1021    "pix/group.png",
1022    N_("Enlightenment Default\n" "Group Control Settings Dialog\n"),
1023    _DlgFillGroupDefaults,
1024    DLG_OAC, CB_ConfigureDefaultGroupSettings,
1025 };
1026
1027 static void
1028 GroupsConfigure(const char *params)
1029 {
1030    char                s[128];
1031    const char         *p;
1032    int                 l;
1033    EWin               *ewin;
1034
1035    p = params;
1036    l = 0;
1037    s[0] = '\0';
1038    sscanf(p, "%100s %n", s, &l);
1039    p += l;
1040
1041    ewin = GetContextEwin();
1042
1043    if (!strcmp(s, "group"))
1044      {
1045         SettingsGroups(ewin);
1046      }
1047    else if (!strcmp(s, "add"))
1048      {
1049         ChooseGroupDialog(ewin,
1050                           _("Pick the group the window will belong to:"),
1051                           GROUP_SELECT_ALL_EXCEPT_EWIN, GROUP_OP_ADD);
1052      }
1053    else if (!strcmp(s, "del"))
1054      {
1055         ChooseGroupDialog(ewin,
1056                           _("Select the group to remove the window from:"),
1057                           GROUP_SELECT_EWIN_ONLY, GROUP_OP_DEL);
1058      }
1059    else if (!strcmp(s, "break"))
1060      {
1061         ChooseGroupDialog(ewin, _("Select the group to break:"),
1062                           GROUP_SELECT_EWIN_ONLY, GROUP_OP_BREAK);
1063      }
1064 }
1065 #endif /* ENABLE_DIALOGS */
1066
1067 /*
1068  * Groups module
1069  */
1070
1071 static void
1072 GroupShow(Group * g)
1073 {
1074    int                 j;
1075
1076    for (j = 0; j < g->num_members; j++)
1077       IpcPrintf("%d: %s\n", g->index, EwinGetIcccmName(g->members[j]));
1078
1079    IpcPrintf("        index: %d\n" "  num_members: %d\n"
1080              "      iconify: %d\n" "         kill: %d\n"
1081              "         move: %d\n" "        raise: %d\n"
1082              "   set_border: %d\n" "        stick: %d\n"
1083              "        shade: %d\n",
1084              g->index, g->num_members,
1085              g->cfg.iconify, g->cfg.kill,
1086              g->cfg.move, g->cfg.raise,
1087              g->cfg.set_border, g->cfg.stick, g->cfg.shade);
1088 }
1089
1090 static void
1091 IPC_GroupInfo(const char *params)
1092 {
1093    Group              *group;
1094
1095    if (params)
1096      {
1097         group = GroupFind2(params);
1098         if (group)
1099            GroupShow(group);
1100         else
1101            IpcPrintf("Error: no such group: %s", params);
1102      }
1103    else
1104      {
1105         IpcPrintf("Number of groups: %d\n", ecore_list_count(group_list));
1106         ECORE_LIST_FOR_EACH(group_list, group) GroupShow(group);
1107      }
1108 }
1109
1110 static void
1111 IPC_GroupOps(const char *params)
1112 {
1113    Group              *group = Mode_groups.current;
1114    char                windowid[128];
1115    char                operation[128];
1116    char                groupid[128];
1117    unsigned int        win;
1118    EWin               *ewin;
1119
1120    if (!params)
1121      {
1122         IpcPrintf("Error: no window specified");
1123         return;
1124      }
1125
1126    windowid[0] = operation[0] = groupid[0] = '\0';
1127    sscanf(params, "%100s %100s %100s", windowid, operation, groupid);
1128    win = 0;
1129    sscanf(windowid, "%x", &win);
1130
1131    if (!operation[0])
1132      {
1133         IpcPrintf("Error: no operation specified");
1134         return;
1135      }
1136
1137    ewin = EwinFindByExpr(windowid);
1138    if (!ewin)
1139      {
1140         IpcPrintf("Error: no such window: %s", windowid);
1141         return;
1142      }
1143
1144    if (!strcmp(operation, "start"))
1145      {
1146         BuildWindowGroup(&ewin, 1, -1);
1147         IpcPrintf("start %8x", win);
1148      }
1149    else if (!strcmp(operation, "add"))
1150      {
1151         group = GroupFind2(groupid);
1152         AddEwinToGroup(ewin, group);
1153         IpcPrintf("add %8x", win);
1154      }
1155    else if (!strcmp(operation, "del"))
1156      {
1157         group = GroupFind2(groupid);
1158         RemoveEwinFromGroup(ewin, group);
1159         IpcPrintf("del %8x", win);
1160      }
1161    else if (!strcmp(operation, "break"))
1162      {
1163         group = GroupFind2(groupid);
1164         BreakWindowGroup(ewin, group);
1165         IpcPrintf("break %8x", win);
1166      }
1167    else if (!strcmp(operation, "showhide"))
1168      {
1169         ShowHideWinGroups(ewin, -1, SET_TOGGLE);
1170         IpcPrintf("showhide %8x", win);
1171      }
1172    else
1173      {
1174         IpcPrintf("Error: no such operation: %s", operation);
1175         return;
1176      }
1177    GroupsSave();
1178 }
1179
1180 static void
1181 IPC_Group(const char *params)
1182 {
1183    char                groupid[128];
1184    char                operation[128];
1185    char                param1[128];
1186    Group              *group;
1187    int                 onoff;
1188
1189    if (!params)
1190      {
1191         IpcPrintf("Error: no group specified");
1192         return;
1193      }
1194
1195    groupid[0] = operation[0] = param1[0] = '\0';
1196    sscanf(params, "%100s %100s %100s", groupid, operation, param1);
1197
1198    if (!operation[0])
1199      {
1200         IpcPrintf("Error: no operation specified");
1201         return;
1202      }
1203
1204    group = GroupFind2(groupid);
1205    if (!group)
1206      {
1207         IpcPrintf("Error: no such group: %s", groupid);
1208         return;
1209      }
1210
1211    if (!param1[0])
1212      {
1213         IpcPrintf("Error: no mode specified");
1214         return;
1215      }
1216
1217    onoff = -1;
1218    if (!strcmp(param1, "on"))
1219       onoff = 1;
1220    else if (!strcmp(param1, "off"))
1221       onoff = 0;
1222
1223    if (onoff == -1 && strcmp(param1, "?"))
1224      {
1225         IpcPrintf("Error: unknown mode specified");
1226      }
1227    else if (!strcmp(operation, "num_members"))
1228      {
1229         IpcPrintf("num_members: %d", group->num_members);
1230         onoff = -1;
1231      }
1232    else if (!strcmp(operation, "iconify"))
1233      {
1234         if (onoff >= 0)
1235            group->cfg.iconify = onoff;
1236         else
1237            onoff = group->cfg.iconify;
1238      }
1239    else if (!strcmp(operation, "kill"))
1240      {
1241         if (onoff >= 0)
1242            group->cfg.kill = onoff;
1243         else
1244            onoff = group->cfg.kill;
1245      }
1246    else if (!strcmp(operation, "move"))
1247      {
1248         if (onoff >= 0)
1249            group->cfg.move = onoff;
1250         else
1251            onoff = group->cfg.move;
1252      }
1253    else if (!strcmp(operation, "raise"))
1254      {
1255         if (onoff >= 0)
1256            group->cfg.raise = onoff;
1257         else
1258            onoff = group->cfg.raise;
1259      }
1260    else if (!strcmp(operation, "set_border"))
1261      {
1262         if (onoff >= 0)
1263            group->cfg.set_border = onoff;
1264         else
1265            onoff = group->cfg.set_border;
1266      }
1267    else if (!strcmp(operation, "stick"))
1268      {
1269         if (onoff >= 0)
1270            group->cfg.stick = onoff;
1271         else
1272            onoff = group->cfg.stick;
1273      }
1274    else if (!strcmp(operation, "shade"))
1275      {
1276         if (onoff >= 0)
1277            group->cfg.shade = onoff;
1278         else
1279            onoff = group->cfg.shade;
1280      }
1281    else
1282      {
1283         IpcPrintf("Error: no such operation: %s", operation);
1284         onoff = -1;
1285      }
1286
1287    if (onoff == 1)
1288       IpcPrintf("%s: on", operation);
1289    else if (onoff == 0)
1290       IpcPrintf("%s: off", operation);
1291 }
1292
1293 #if ENABLE_DIALOGS
1294 static void
1295 GroupsIpc(const char *params)
1296 {
1297    const char         *p;
1298    char                cmd[128], prm[128];
1299    int                 len;
1300
1301    cmd[0] = prm[0] = '\0';
1302    p = params;
1303    if (p)
1304      {
1305         len = 0;
1306         sscanf(p, "%100s %100s %n", cmd, prm, &len);
1307         p += len;
1308      }
1309
1310    if (!p || cmd[0] == '?')
1311      {
1312         /* Show groups */
1313      }
1314    else if (!strncmp(cmd, "cfg", 2))
1315      {
1316         GroupsConfigure(prm);
1317      }
1318 }
1319 #endif /* ENABLE_DIALOGS */
1320
1321 static const IpcItem GroupsIpcArray[] = {
1322 #if ENABLE_DIALOGS
1323    {
1324     GroupsIpc,
1325     "groups", "grp",
1326     "Configure window groups",
1327     "  groups cfg           Configure groups\n"}
1328    ,
1329 #endif /* ENABLE_DIALOGS */
1330    {
1331     IPC_GroupInfo,
1332     "group_info", "gl",
1333     "Retrieve some info on groups",
1334     "use \"group_info [group_index]\"\n"}
1335    ,
1336    {
1337     IPC_GroupOps,
1338     "group_op", "gop",
1339     "Group operations",
1340     "use \"group_op <windowid> <property> [<value>]\" to perform "
1341     "group operations on a window.\n" "Available group_op commands are:\n"
1342     "  group_op <windowid> start\n"
1343     "  group_op <windowid> add [<group_index>]\n"
1344     "  group_op <windowid> del [<group_index>]\n"
1345     "  group_op <windowid> break [<group_index>]\n"
1346     "  group_op <windowid> showhide\n"}
1347    ,
1348    {
1349     IPC_Group,
1350     "group", "gc",
1351     "Group commands",
1352     "use \"group <groupid> <property> <value>\" to set group properties.\n"
1353     "Available group commands are:\n"
1354     "  group <groupid> num_members <on/off/?>\n"
1355     "  group <groupid> iconify <on/off/?>\n"
1356     "  group <groupid> kill <on/off/?>\n" "  group <groupid> move <on/off/?>\n"
1357     "  group <groupid> raise <on/off/?>\n"
1358     "  group <groupid> set_border <on/off/?>\n"
1359     "  group <groupid> stick <on/off/?>\n"
1360     "  group <groupid> shade <on/off/?>\n"}
1361    ,
1362 };
1363 #define N_IPC_FUNCS (sizeof(GroupsIpcArray)/sizeof(IpcItem))
1364
1365 /*
1366  * Configuration items
1367  */
1368 static const CfgItem GroupsCfgItems[] = {
1369    CFG_ITEM_BOOL(Conf_groups, dflt.iconify, 1),
1370    CFG_ITEM_BOOL(Conf_groups, dflt.kill, 0),
1371    CFG_ITEM_BOOL(Conf_groups, dflt.move, 1),
1372    CFG_ITEM_BOOL(Conf_groups, dflt.raise, 0),
1373    CFG_ITEM_BOOL(Conf_groups, dflt.set_border, 1),
1374    CFG_ITEM_BOOL(Conf_groups, dflt.stick, 1),
1375    CFG_ITEM_BOOL(Conf_groups, dflt.shade, 1),
1376    CFG_ITEM_BOOL(Conf_groups, swapmove, 1),
1377 };
1378 #define N_CFG_ITEMS (sizeof(GroupsCfgItems)/sizeof(CfgItem))
1379
1380 extern const EModule ModGroups;
1381 const EModule       ModGroups = {
1382    "groups", "grp",
1383    NULL,
1384    {N_IPC_FUNCS, GroupsIpcArray},
1385    {N_CFG_ITEMS, GroupsCfgItems}
1386 };