/* * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors * Copyright (C) 2004-2023 Kim Woelders * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies of the Software, its documentation and marketing & publicity * materials, and acknowledgment shall be given in the documentation, materials * and software packages that this Software was used. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "E.h" #include "borders.h" #include "dialog.h" #include "emodule.h" #include "ewins.h" #include "groups.h" #include "list.h" #include "settings.h" #include "snaps.h" #define DEBUG_GROUPS 0 #if DEBUG_GROUPS #define Dprintf(fmt, ...) Eprintf("%s: " fmt, __func__, __VA_ARGS__) #else #define Dprintf(fmt...) #endif #define USE_GROUP_SHOWHIDE 1 /* Enable group borders */ #define GROUP_SELECT_ALL 0 #define GROUP_SELECT_EWIN_ONLY 1 #define GROUP_SELECT_ALL_EXCEPT_EWIN 2 #define SET_OFF 0 #define SET_ON 1 #define SET_TOGGLE 2 typedef struct _groupconfig { char iconify; char kill; char move; char raise; char set_border; char shade; char stick; } GroupConfig; struct _group { dlist_t list; int index; EWin **members; int num_members; GroupConfig cfg; char keep; /* Don't destroy when empty */ char used; /* Don't discard when saving */ }; static LIST_HEAD(group_list); static struct { GroupConfig dflt; } Conf_groups; static struct { Group *current; } Mode_groups; static void _GroupsSave(void); static Group * _GroupCreate(int gid, int cfg_update) { Group *g; g = ECALLOC(Group, 1); if (!g) return NULL; LIST_APPEND(Group, &group_list, g); if (gid == -1) { /* Create new group id */ /* ... using us time. Should really be checked for uniqueness. */ g->index = (int)GetTimeUs(); } else { /* Use given group id */ g->index = gid; } g->cfg.iconify = Conf_groups.dflt.iconify; g->cfg.kill = Conf_groups.dflt.kill; g->cfg.move = Conf_groups.dflt.move; g->cfg.raise = Conf_groups.dflt.raise; g->cfg.set_border = Conf_groups.dflt.set_border; g->cfg.stick = Conf_groups.dflt.stick; g->cfg.shade = Conf_groups.dflt.shade; Dprintf("%s: grp=%p gid=%d\n", __func__, g, g->index); if (cfg_update) _GroupsSave(); return g; } static void _GroupDestroy(Group *g, int cfg_update) { if (!g) return; Dprintf("%s: grp=%p gid=%d\n", __func__, g, g->index); LIST_REMOVE(Group, &group_list, g); if (g == Mode_groups.current) Mode_groups.current = NULL; Efree(g->members); Efree(g); if (cfg_update) _GroupsSave(); } static int _GroupMatchId(const void *data, const void *match) { return ((const Group *)data)->index != PTR2INT(match); } static Group * _GroupFind(int gid) { return LIST_FIND(Group, &group_list, _GroupMatchId, INT2PTR(gid)); } EWin *const * GroupGetMembers(const Group *g, int *num) { *num = g->num_members; return g->members; } int GroupGetIndex(const Group *g) { return g->index; } void GroupSetUsed(int gid) { Group *g; g = _GroupFind(gid); if (!g) return; g->used = 1; } int GroupMatchAction(const Group *g, int action) { int match; switch (action) { default: match = 0; break; case GROUP_ACTION_ANY: match = 1; break; case GROUP_ACTION_SET_WINDOW_BORDER: match = g->cfg.set_border; break; case GROUP_ACTION_ICONIFY: match = g->cfg.iconify; break; case GROUP_ACTION_MOVE: match = g->cfg.move; break; case GROUP_ACTION_STACKING: match = g->cfg.raise; break; case GROUP_ACTION_STICK: match = g->cfg.stick; break; case GROUP_ACTION_SHADE: match = g->cfg.shade; break; case GROUP_ACTION_KILL: match = g->cfg.kill; break; } return match; } static Group * _GroupFind2(const char *groupid) { int gid; if (groupid[0] == '*' || groupid[0] == '\0') return Mode_groups.current; #if ENABLE_DIALOGS #define GROUP_CHOOSE ((Group*)1) if (groupid[0] == '=') return GROUP_CHOOSE; #endif gid = 0; sscanf(groupid, "%d", &gid); if (gid == 0) return NULL; return _GroupFind(gid); } static int _EwinGroupIndex(const EWin *ewin, const Group *g) { int i; for (i = 0; i < ewin->num_groups; i++) if (ewin->groups[i] == g) return i; return -1; } static int _GroupEwinIndex(const Group *g, const EWin *ewin) { int i; for (i = 0; i < g->num_members; i++) if (g->members[i] == ewin) return i; return -1; } static void _GroupEwinAdd(Group *g, EWin *ewin, int snap_update) { if (!ewin || !g) return; if (_EwinGroupIndex(ewin, g) >= 0) return; /* Already there */ Dprintf("%s: gid=%8d: %s\n", __func__, g->index, EoGetName(ewin)); ewin->num_groups++; ewin->groups = EREALLOC(Group *, ewin->groups, ewin->num_groups); ewin->groups[ewin->num_groups - 1] = g; g->num_members++; g->members = EREALLOC(EWin *, g->members, g->num_members); g->members[g->num_members - 1] = ewin; if (snap_update) SnapshotEwinUpdate(ewin, SNAP_USE_GROUPS); } static void _GroupEwinRemove(Group *g, EWin *ewin, int snap_update) { int i, ie, ig; if (!ewin || !g) return; Dprintf("%s: gid=%8d: %s\n", __func__, g->index, EoGetName(ewin)); ie = _EwinGroupIndex(ewin, g); if (ie < 0) { /* Should not happen */ Dprintf("%s: g=%p gid=%8d: %s: Group not found?!?\n", __func__, g, g->index, EoGetName(ewin)); return; } ig = _GroupEwinIndex(g, ewin); if (ig < 0) { /* Should not happen */ Dprintf("%s: g=%p gid=%8d: %s: Ewin not found?!?\n", __func__, g, g->index, EoGetName(ewin)); return; } Dprintf("%s: gid=%8d index=%d/%d: %s\n", __func__, g->index, ie, ig, EoGetName(ewin)); /* remove it from the group */ g->num_members--; for (i = ig; i < g->num_members; i++) g->members[i] = g->members[i + 1]; if (g->num_members > 0) g->members = EREALLOC(EWin *, g->members, g->num_members); else if (g->keep) EFREE_NULL(g->members); else _GroupDestroy(g, snap_update); /* and remove the group from the groups that the window is in */ ewin->num_groups--; for (i = ie; i < ewin->num_groups; i++) ewin->groups[i] = ewin->groups[i + 1]; if (ewin->num_groups > 0) ewin->groups = EREALLOC(Group *, ewin->groups, ewin->num_groups); else EFREE_NULL(ewin->groups); if (snap_update) SnapshotEwinUpdate(ewin, SNAP_USE_GROUPS); } static void _GroupDelete(Group *g) { if (!g) return; Dprintf("%s: gid=%d\n", __func__, g->index); g->keep = 1; while (g->num_members > 0) _GroupEwinRemove(g, g->members[0], 1); _GroupDestroy(g, 1); } Group ** GroupsGetList(int *pnum) { return LIST_GET_ITEMS(Group, &group_list, pnum); } #if ENABLE_DIALOGS static Group ** _EwinListGroups(const EWin *ewin, char group_select, int *num) { Group **groups; Group **groups2; int i, j, n, killed; groups = NULL; *num = 0; switch (group_select) { case GROUP_SELECT_EWIN_ONLY: *num = n = ewin->num_groups; if (n <= 0) break; groups = EMALLOC(Group *, n); if (!groups) break; memcpy(groups, ewin->groups, n * sizeof(Group *)); break; case GROUP_SELECT_ALL_EXCEPT_EWIN: groups2 = GroupsGetList(num); if (!groups2) break; n = *num; for (i = killed = 0; i < n; i++) { for (j = 0; j < ewin->num_groups; j++) { if (ewin->groups[j] == groups2[i]) { groups2[i] = NULL; killed++; } } } if (n - killed > 0) { groups = EMALLOC(Group *, n - killed); if (groups) { for (i = j = 0; i < n; i++) if (groups2[i]) groups[j++] = groups2[i]; *num = n - killed; } } Efree(groups2); break; case GROUP_SELECT_ALL: default: groups = GroupsGetList(num); break; } return groups; } #endif /* ENABLE_DIALOGS */ /* Update groups on snapped window appearance */ void GroupsEwinAdd(EWin *ewin, const int *pgid, int ngid) { Group *g; int i, gid; for (i = 0; i < ngid; i++) { gid = pgid[i]; g = _GroupFind(gid); Dprintf("ewin=%p gid=%d grp=%p\n", ewin, gid, g); if (!g) { /* This should not happen, but may if group/snap configs are corrupted */ g = _GroupCreate(gid, 1); } _GroupEwinAdd(g, ewin, 0); } } /* Update groups on snapped window disappearance */ void GroupsEwinRemove(EWin *ewin) { while (ewin->num_groups > 0) _GroupEwinRemove(ewin->groups[0], ewin, 0); } Group * EwinsInGroup(const EWin *ewin1, const EWin *ewin2) { int i; if (ewin1 && ewin2) { for (i = 0; i < ewin1->num_groups; i++) { if (_GroupEwinIndex(ewin1->groups[i], ewin2) >= 0) return ewin1->groups[i]; } } return NULL; } #if ENABLE_DIALOGS static char ** _GrouplistMemberNames(Group **groups, int num) { int i, j, len; char **group_member_strings; const char *name; group_member_strings = ECALLOC(char *, num); if (!group_member_strings) return NULL; for (i = 0; i < num; i++) { group_member_strings[i] = EMALLOC(char, 1024); if (!group_member_strings[i]) break; len = 0; for (j = 0; j < groups[i]->num_members; j++) { name = EwinGetTitle(groups[i]->members[j]); if (!name) /* Should never happen */ continue; len += Esnprintf(group_member_strings[i] + len, 1024 - len, "%s\n", name); if (len >= 1024) break; } if (len == 0) snprintf(group_member_strings[i], 1024, "(empty)"); } return group_member_strings; } #endif /* ENABLE_DIALOGS */ #if USE_GROUP_SHOWHIDE static void _EwinGroupsShowHide(EWin *ewin, int group_index, char onoff) { EWin **gwins; int i, num; const Border *b = NULL; if (!ewin || group_index >= ewin->num_groups) return; if (group_index < 0) { gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_ANY, 0, &num); } else { gwins = ewin->groups[group_index]->members; num = ewin->groups[group_index]->num_members; } if (onoff == SET_TOGGLE) onoff = (ewin->border == ewin->normal_border) ? SET_ON : SET_OFF; for (i = 0; i < num; i++) { if (onoff == SET_ON) b = EwinBorderGetGroupBorder(gwins[i]); else b = gwins[i]->normal_border; EwinBorderChange(gwins[i], b, 0); } if (group_index < 0) Efree(gwins); } #else #define _EwinGroupsShowHide(ewin, group_index, onoff) #endif /* USE_GROUP_SHOWHIDE */ static void _GroupsSave(void) { Group *g; FILE *f; char s[1024]; Dprintf("%s\n", __func__); Esnprintf(s, sizeof(s), "%s.groups", EGetSavePrefix()); f = fopen(s, "w"); if (!f) return; LIST_FOR_EACH(Group, &group_list, g) { fprintf(f, "NEW: %i\n", g->index); fprintf(f, "ICONIFY: %i\n", g->cfg.iconify); fprintf(f, "KILL: %i\n", g->cfg.kill); fprintf(f, "MOVE: %i\n", g->cfg.move); fprintf(f, "RAISE: %i\n", g->cfg.raise); fprintf(f, "SET_BORDER: %i\n", g->cfg.set_border); fprintf(f, "STICK: %i\n", g->cfg.stick); fprintf(f, "SHADE: %i\n", g->cfg.shade); } fclose(f); } void GroupsPrune(void) { Group *g, *tmp; int pruned; pruned = 0; LIST_FOR_EACH_SAFE(Group, &group_list, g, tmp) { if (g->used) continue; if (g->members) continue; _GroupDestroy(g, 0); pruned += 1; } Dprintf("%s: Pruned=%d\n", __func__, pruned); if (pruned) _GroupsSave(); } static int _GroupsLoad(FILE *fs) { char s[1024]; Group *g = NULL; while (fgets(s, sizeof(s), fs)) { char ss[128]; int ii; if (strlen(s) > 0) s[strlen(s) - 1] = 0; ii = 0; sscanf(s, "%100s %d", ss, &ii); if (!strcmp(ss, "NEW:")) { g = _GroupCreate(ii, 0); continue; } if (!g) continue; if (!strcmp(ss, "ICONIFY:")) { g->cfg.iconify = ii; } else if (!strcmp(ss, "KILL:")) { g->cfg.kill = ii; } else if (!strcmp(ss, "MOVE:")) { g->cfg.move = ii; } else if (!strcmp(ss, "RAISE:")) { g->cfg.raise = ii; } else if (!strcmp(ss, "SET_BORDER:")) { g->cfg.set_border = ii; } else if (!strcmp(ss, "STICK:")) { g->cfg.stick = ii; } else if (!strcmp(ss, "SHADE:")) { g->cfg.shade = ii; } } return 0; } void GroupsLoad(void) { char s[4096]; Dprintf("%s\n", __func__); Esnprintf(s, sizeof(s), "%s.groups", EGetSavePrefix()); ConfigFileLoad(s, NULL, _GroupsLoad, 0); } #if ENABLE_DIALOGS #define GROUP_OP_ADD 1 #define GROUP_OP_DEL 2 #define GROUP_OP_BREAK 3 typedef struct { EWin *ewin; int action; const char *message; Group **groups; int group_num; int cur_grp; /* Current group */ int prv_grp; /* Previous group */ } GroupSelDlgData; static void _DlgApplyGroupChoose(Dialog *d, int val __UNUSED__, void *data __UNUSED__) { GroupSelDlgData *dd = DLG_DATA_GET(d, GroupSelDlgData); if (!dd->groups) return; switch (dd->action) { case GROUP_OP_ADD: _GroupEwinAdd(dd->groups[dd->cur_grp], dd->ewin, 1); break; case GROUP_OP_DEL: _GroupEwinRemove(dd->groups[dd->cur_grp], dd->ewin, 1); break; case GROUP_OP_BREAK: _GroupDelete(dd->groups[dd->cur_grp]); break; default: break; } } static void _DlgExitGroupChoose(Dialog *d) { GroupSelDlgData *dd = DLG_DATA_GET(d, GroupSelDlgData); if (!dd->groups) return; _EwinGroupsShowHide(dd->ewin, dd->cur_grp, SET_OFF); Efree(dd->groups); } static void _DlgSelectCbGroupChoose(Dialog *d, int val, void *data __UNUSED__) { GroupSelDlgData *dd = DLG_DATA_GET(d, GroupSelDlgData); /* val is equal to dd->cur_grp */ _EwinGroupsShowHide(dd->ewin, dd->prv_grp, SET_OFF); _EwinGroupsShowHide(dd->ewin, val, SET_ON); dd->prv_grp = val; } static void _DlgFillGroupChoose(Dialog *d, DItem *table, void *data) { GroupSelDlgData *dd = DLG_DATA_GET(d, GroupSelDlgData); DItem *di, *radio; int i, num_groups; char **group_member_strings; *dd = *(GroupSelDlgData *) data; DialogItemTableSetOptions(table, 1, 0, 0, 0); di = DialogAddItem(table, DITEM_TEXT); DialogItemSetText(di, dd->message); num_groups = dd->group_num; group_member_strings = _GrouplistMemberNames(dd->groups, num_groups); if (!group_member_strings) return; /* Silence clang - It should not be possible to go here */ radio = NULL; /* Avoid warning */ for (i = 0; i < num_groups; i++) { di = DialogAddItem(table, DITEM_RADIOBUTTON); if (i == 0) radio = di; DialogItemSetCallback(di, _DlgSelectCbGroupChoose, i, NULL); DialogItemSetText(di, group_member_strings[i]); DialogItemRadioButtonSetFirst(di, radio); DialogItemRadioButtonGroupSetVal(di, i); } DialogItemRadioButtonGroupSetValPtr(radio, &dd->cur_grp); StrlistFree(group_member_strings, num_groups); } static const DialogDef DlgGroupChoose = { "GROUP_SELECTION", NULL, N_("Window Group Selection"), sizeof(GroupSelDlgData), SOUND_SETTINGS_GROUP, "pix/group.png", N_("Enlightenment Window Group\n" "Selection Dialog"), _DlgFillGroupChoose, DLG_OC, _DlgApplyGroupChoose, _DlgExitGroupChoose }; static void _EwinGroupChooseDialog(EWin *ewin, int action) { int group_sel; GroupSelDlgData gsdd, *dd = &gsdd; if (!ewin) return; dd->ewin = ewin; dd->action = action; dd->cur_grp = dd->prv_grp = 0; switch (action) { default: return; case GROUP_OP_ADD: dd->message = _("Pick the group the window will belong to:"); group_sel = GROUP_SELECT_ALL_EXCEPT_EWIN; break; case GROUP_OP_DEL: dd->message = _("Select the group to remove the window from:"); group_sel = GROUP_SELECT_EWIN_ONLY; break; case GROUP_OP_BREAK: dd->message = _("Select the group to break:"); group_sel = GROUP_SELECT_EWIN_ONLY; break; } dd->groups = _EwinListGroups(dd->ewin, group_sel, &dd->group_num); if (!dd->groups) { if (action == GROUP_OP_BREAK || action == GROUP_OP_DEL) { DialogOK(_("Window Group Error"), _ ("This window currently does not belong to any groups.\n" "You can only destroy groups or remove windows from groups\n" "through a window that actually belongs to at least one group.")); return; } if (group_sel == GROUP_SELECT_ALL_EXCEPT_EWIN) { DialogOK(_("Window Group Error"), _("Currently, no groups exist or this window\n" "already belongs to all existing groups.\n" "You have to start other groups first.")); return; } DialogOK(_("Window Group Error"), _ ("Currently, no groups exist. You have to start a group first.")); return; } _EwinGroupsShowHide(dd->ewin, 0, SET_ON); DialogShowSimple(&DlgGroupChoose, dd); } typedef struct { EWin *ewin; GroupConfig cfg; /* Dialog data for current group */ GroupConfig *cfgs; /* Work copy of ewin group cfgs */ int ngrp; int cur_grp; /* Current group */ int prv_grp; /* Previous group */ } EwinGroupDlgData; static void _DlgApplyGroups(Dialog *d, int val __UNUSED__, void *data __UNUSED__) { EwinGroupDlgData *dd = DLG_DATA_GET(d, EwinGroupDlgData); EWin *ewin; int i; /* Check ewin */ ewin = EwinFindByPtr(dd->ewin); if (ewin && ewin->num_groups != dd->ngrp) ewin = NULL; if (ewin) { dd->cfgs[dd->cur_grp] = dd->cfg; for (i = 0; i < ewin->num_groups; i++) ewin->groups[i]->cfg = dd->cfgs[i]; } _GroupsSave(); } static void _DlgExitGroups(Dialog *d) { EwinGroupDlgData *dd = DLG_DATA_GET(d, EwinGroupDlgData); EWin *ewin; ewin = EwinFindByPtr(dd->ewin); _EwinGroupsShowHide(ewin, dd->cur_grp, SET_OFF); Efree(dd->cfgs); } static void _DlgSelectCbGroups(Dialog *d, int val, void *data __UNUSED__) { EwinGroupDlgData *dd = DLG_DATA_GET(d, EwinGroupDlgData); /* val is equal to dd->cur_grp */ dd->cfgs[dd->prv_grp] = dd->cfg; dd->cfg = dd->cfgs[val]; DialogRedraw(d); _EwinGroupsShowHide(dd->ewin, dd->prv_grp, SET_OFF); _EwinGroupsShowHide(dd->ewin, val, SET_ON); dd->prv_grp = val; } static void _DlgFillGroups(Dialog *d, DItem *table, void *data) { EwinGroupDlgData *dd = DLG_DATA_GET(d, EwinGroupDlgData); EWin *ewin = (EWin *) data; DItem *radio, *di; int i; char **group_member_strings; dd->ewin = ewin; dd->cfgs = EMALLOC(GroupConfig, ewin->num_groups); dd->ngrp = ewin->num_groups; dd->cur_grp = dd->prv_grp = 0; for (i = 0; i < ewin->num_groups; i++) dd->cfgs[i] = ewin->groups[i]->cfg; if (ewin->num_groups > 0) /* Avoid compiler warning */ dd->cfg = dd->cfgs[0]; _EwinGroupsShowHide(ewin, 0, SET_ON); DialogItemTableSetOptions(table, 2, 0, 0, 0); di = DialogAddItem(table, DITEM_TEXT); DialogItemSetColSpan(di, 2); DialogItemSetText(di, _("Pick the group to configure:")); group_member_strings = _GrouplistMemberNames(ewin->groups, ewin->num_groups); if (!group_member_strings) return; /* Silence clang - It should not be possible to go here */ radio = NULL; /* Avoid warning */ for (i = 0; i < ewin->num_groups; i++) { di = DialogAddItem(table, DITEM_RADIOBUTTON); if (i == 0) radio = di; DialogItemSetColSpan(di, 2); DialogItemSetCallback(di, _DlgSelectCbGroups, i, d); DialogItemSetText(di, group_member_strings[i]); DialogItemRadioButtonSetFirst(di, radio); DialogItemRadioButtonGroupSetVal(di, i); } DialogItemRadioButtonGroupSetValPtr(radio, &dd->cur_grp); StrlistFree(group_member_strings, ewin->num_groups); di = DialogAddItem(table, DITEM_SEPARATOR); DialogItemSetColSpan(di, 2); di = DialogAddItem(table, DITEM_TEXT); DialogItemSetColSpan(di, 2); DialogItemSetText(di, _("The following actions are\n" "applied to all group members:")); di = DialogAddItem(table, DITEM_CHECKBUTTON); DialogItemSetColSpan(di, 2); DialogItemSetText(di, _("Changing border style")); DialogItemCheckButtonSetPtr(di, &(dd->cfg.set_border)); di = DialogAddItem(table, DITEM_CHECKBUTTON); DialogItemSetColSpan(di, 2); DialogItemSetText(di, _("Iconifying")); DialogItemCheckButtonSetPtr(di, &(dd->cfg.iconify)); di = DialogAddItem(table, DITEM_CHECKBUTTON); DialogItemSetColSpan(di, 2); DialogItemSetText(di, _("Killing")); DialogItemCheckButtonSetPtr(di, &(dd->cfg.kill)); di = DialogAddItem(table, DITEM_CHECKBUTTON); DialogItemSetColSpan(di, 2); DialogItemSetText(di, _("Moving")); DialogItemCheckButtonSetPtr(di, &(dd->cfg.move)); di = DialogAddItem(table, DITEM_CHECKBUTTON); DialogItemSetColSpan(di, 2); DialogItemSetText(di, _("Raising/lowering")); DialogItemCheckButtonSetPtr(di, &(dd->cfg.raise)); di = DialogAddItem(table, DITEM_CHECKBUTTON); DialogItemSetColSpan(di, 2); DialogItemSetText(di, _("Sticking")); DialogItemCheckButtonSetPtr(di, &(dd->cfg.stick)); di = DialogAddItem(table, DITEM_CHECKBUTTON); DialogItemSetColSpan(di, 2); DialogItemSetText(di, _("Shading")); DialogItemCheckButtonSetPtr(di, &(dd->cfg.shade)); } static const DialogDef DlgGroups = { "CONFIGURE_GROUP", NULL, N_("Window Group Settings"), sizeof(EwinGroupDlgData), SOUND_SETTINGS_GROUP, "pix/group.png", N_("Enlightenment Window Group\n" "Settings Dialog"), _DlgFillGroups, DLG_OAC, _DlgApplyGroups, _DlgExitGroups }; static void _EwinGroupsConfig(EWin *ewin) { if (!ewin) return; if (ewin->num_groups == 0) { DialogOK(_("Window Group Error"), _("This window currently does not belong to any groups.")); return; } DialogShowSimple(&DlgGroups, ewin); } typedef struct { GroupConfig group_cfg; } GroupCfgDlgData; static void _DlgApplyGroupDefaults(Dialog *d, int val __UNUSED__, void *data __UNUSED__) { GroupCfgDlgData *dd = DLG_DATA_GET(d, GroupCfgDlgData); Conf_groups.dflt = dd->group_cfg; autosave(); } static void _DlgFillGroupDefaults(Dialog *d, DItem *table, void *data __UNUSED__) { GroupCfgDlgData *dd = DLG_DATA_GET(d, GroupCfgDlgData); DItem *di; dd->group_cfg = Conf_groups.dflt; DialogItemTableSetOptions(table, 2, 0, 0, 0); di = DialogAddItem(table, DITEM_TEXT); DialogItemSetColSpan(di, 2); DialogItemSetText(di, _("Per-group settings:")); di = DialogAddItem(table, DITEM_CHECKBUTTON); DialogItemSetColSpan(di, 2); DialogItemSetText(di, _("Changing border style")); DialogItemCheckButtonSetPtr(di, &dd->group_cfg.set_border); di = DialogAddItem(table, DITEM_CHECKBUTTON); DialogItemSetColSpan(di, 2); DialogItemSetText(di, _("Iconifying")); DialogItemCheckButtonSetPtr(di, &dd->group_cfg.iconify); di = DialogAddItem(table, DITEM_CHECKBUTTON); DialogItemSetColSpan(di, 2); DialogItemSetText(di, _("Killing")); DialogItemCheckButtonSetPtr(di, &dd->group_cfg.kill); di = DialogAddItem(table, DITEM_CHECKBUTTON); DialogItemSetColSpan(di, 2); DialogItemSetText(di, _("Moving")); DialogItemCheckButtonSetPtr(di, &dd->group_cfg.move); di = DialogAddItem(table, DITEM_CHECKBUTTON); DialogItemSetColSpan(di, 2); DialogItemSetText(di, _("Raising/lowering")); DialogItemCheckButtonSetPtr(di, &dd->group_cfg.raise); di = DialogAddItem(table, DITEM_CHECKBUTTON); DialogItemSetColSpan(di, 2); DialogItemSetText(di, _("Sticking")); DialogItemCheckButtonSetPtr(di, &dd->group_cfg.stick); di = DialogAddItem(table, DITEM_CHECKBUTTON); DialogItemSetColSpan(di, 2); DialogItemSetText(di, _("Shading")); DialogItemCheckButtonSetPtr(di, &dd->group_cfg.shade); } const DialogDef DlgGroupDefaults = { "CONFIGURE_DEFAULT_GROUP_CONTROL", N_("Groups"), N_("Default Group Control Settings"), sizeof(GroupCfgDlgData), SOUND_SETTINGS_GROUP, "pix/group.png", N_("Enlightenment Default\n" "Group Control Settings Dialog"), _DlgFillGroupDefaults, DLG_OAC, _DlgApplyGroupDefaults, NULL }; #endif /* ENABLE_DIALOGS */ /* * Groups module */ static void _GroupShowEwins(Group *g) { int i; IpcPrintf(" gid=%8d: ", g->index); for (i = 0; i < g->num_members; i++) IpcPrintf(" %s", EoGetName(g->members[i])); IpcPrintf("\n"); } static void _GroupsShowGroups(void) { Group *g; IpcPrintf("%s:\n", __func__); LIST_FOR_EACH(Group, &group_list, g) { _GroupShowEwins(g); } } static void _GroupsShow(void) { _GroupsShowGroups(); } static void _GroupShow(Group *g) { int j; for (j = 0; j < g->num_members; j++) IpcPrintf("%d: %s\n", g->index, EwinGetIcccmName(g->members[j])); IpcPrintf(" index: %d\n" " num_members: %d\n" " iconify: %d\n" " kill: %d\n" " move: %d\n" " raise: %d\n" " set_border: %d\n" " stick: %d\n" " shade: %d\n", g->index, g->num_members, g->cfg.iconify, g->cfg.kill, g->cfg.move, g->cfg.raise, g->cfg.set_border, g->cfg.stick, g->cfg.shade); } static void IPC_GroupOps(const char *params) { Group *group; char windowid[128]; char operation[128]; char groupid[128]; unsigned int win; EWin *ewin; if (!params) { IpcPrintf("Error: no window specified\n"); return; } windowid[0] = operation[0] = groupid[0] = '\0'; sscanf(params, "%100s %100s %100s", windowid, operation, groupid); win = 0; sscanf(windowid, "%x", &win); if (!operation[0]) { IpcPrintf("Error: no operation specified\n"); return; } ewin = EwinFindByExpr(windowid); if (!ewin) { IpcPrintf("Error: no such window: %s\n", windowid); return; } #if ENABLE_DIALOGS if (!strcmp(operation, "cfg")) { _EwinGroupsConfig(ewin); } else #endif if (!strcmp(operation, "start")) { group = _GroupCreate(-1, 1); Mode_groups.current = group; _GroupEwinAdd(group, ewin, 1); } else if (!strcmp(operation, "add")) { group = _GroupFind2(groupid); #if ENABLE_DIALOGS if (group == GROUP_CHOOSE) _EwinGroupChooseDialog(ewin, GROUP_OP_ADD); else _GroupEwinAdd(group, ewin, 1); #endif } else if (!strcmp(operation, "del")) { group = _GroupFind2(groupid); #if ENABLE_DIALOGS if (group == GROUP_CHOOSE) _EwinGroupChooseDialog(ewin, GROUP_OP_DEL); else #endif _GroupEwinRemove(group, ewin, 1); } else if (!strcmp(operation, "break")) { group = _GroupFind2(groupid); #if ENABLE_DIALOGS if (group == GROUP_CHOOSE) _EwinGroupChooseDialog(ewin, GROUP_OP_BREAK); else #endif _GroupDelete(group); } else if (!strcmp(operation, "showhide")) { _EwinGroupsShowHide(ewin, -1, SET_TOGGLE); } else { IpcPrintf("Error: no such operation: %s\n", operation); return; } } static void IPC_Group(const char *params) { char groupid[128]; char operation[128]; char param1[128]; Group *group; int onoff; if (!params) { IpcPrintf("Error: no operation specified\n"); return; } groupid[0] = operation[0] = param1[0] = '\0'; sscanf(params, "%100s %100s %100s", groupid, operation, param1); if (!strcmp(groupid, "info")) { IpcPrintf("Number of groups: %d\n", LIST_GET_COUNT(&group_list)); LIST_FOR_EACH(Group, &group_list, group) _GroupShow(group); return; } else if (!strcmp(groupid, "list")) { _GroupsShow(); return; } if (!operation[0]) { IpcPrintf("Error: no operation specified\n"); return; } group = _GroupFind2(groupid); if (!group) { IpcPrintf("Error: no such group: %s\n", groupid); return; } if (!strcmp(operation, "info")) { _GroupShow(group); return; } else if (!strcmp(operation, "del")) { _GroupDelete(group); return; } if (!param1[0]) { IpcPrintf("Error: no mode specified\n"); return; } onoff = -1; if (!strcmp(param1, "on")) onoff = 1; else if (!strcmp(param1, "off")) onoff = 0; if (onoff == -1 && strcmp(param1, "?")) { IpcPrintf("Error: unknown mode specified\n"); } else if (!strcmp(operation, "iconify")) { if (onoff >= 0) group->cfg.iconify = onoff; else onoff = group->cfg.iconify; } else if (!strcmp(operation, "kill")) { if (onoff >= 0) group->cfg.kill = onoff; else onoff = group->cfg.kill; } else if (!strcmp(operation, "move")) { if (onoff >= 0) group->cfg.move = onoff; else onoff = group->cfg.move; } else if (!strcmp(operation, "raise")) { if (onoff >= 0) group->cfg.raise = onoff; else onoff = group->cfg.raise; } else if (!strcmp(operation, "set_border")) { if (onoff >= 0) group->cfg.set_border = onoff; else onoff = group->cfg.set_border; } else if (!strcmp(operation, "stick")) { if (onoff >= 0) group->cfg.stick = onoff; else onoff = group->cfg.stick; } else if (!strcmp(operation, "shade")) { if (onoff >= 0) group->cfg.shade = onoff; else onoff = group->cfg.shade; } else { IpcPrintf("Error: no such operation: %s\n", operation); onoff = -1; } if (onoff == 1) IpcPrintf("%s: on\n", operation); else if (onoff == 0) IpcPrintf("%s: off\n", operation); if (onoff >= 0) _GroupsSave(); } static const IpcItem GroupsIpcArray[] = { { IPC_GroupOps, "group_op", "gop", "Group operations", "use \"group_op []\" to perform " "group operations on a window.\n" "Available group_op commands are:\n" " group_op cfg\n" " group_op start\n" " group_op add []\n" " group_op del []\n" " group_op break []\n" " group_op showhide\n" } , { IPC_Group, "group", "grp", "Group commands", "use \"group \" to set group properties.\n" "Available group commands are:\n" " group info\n" " group info\n" " group del\n" " group iconify \n" " group kill \n" " group move \n" " group raise \n" " group set_border \n" " group stick \n" " group shade \n" } , }; /* * Configuration items */ static const CfgItem GroupsCfgItems[] = { CFG_ITEM_BOOL(Conf_groups, dflt.iconify, 1), CFG_ITEM_BOOL(Conf_groups, dflt.kill, 0), CFG_ITEM_BOOL(Conf_groups, dflt.move, 1), CFG_ITEM_BOOL(Conf_groups, dflt.raise, 0), CFG_ITEM_BOOL(Conf_groups, dflt.set_border, 1), CFG_ITEM_BOOL(Conf_groups, dflt.stick, 1), CFG_ITEM_BOOL(Conf_groups, dflt.shade, 1), }; extern const EModule ModGroups; const EModule ModGroups = { "groups", "grp", NULL, MOD_ITEMS(GroupsIpcArray), MOD_ITEMS(GroupsCfgItems) };