chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / src / snaps.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 "desktops.h"
27 #include "dialog.h"
28 #include "e16-ecore_list.h"
29 #include "ewins.h"
30 #include "file.h"
31 #include "groups.h"
32 #include "ipc.h"
33 #include "settings.h"
34 #include "snaps.h"
35 #include "timers.h"
36 #include "xwin.h"
37
38 struct _snapshot {
39    char               *name;
40    char               *win_title;
41    char               *win_name;
42    char               *win_class;
43    char               *win_role;
44    Window              win;
45    EWin               *used;
46    unsigned int        startup_id;
47    char                track_changes;
48    unsigned int        match_flags;
49    unsigned int        use_flags;
50
51    char               *border_name;
52    int                 desktop;
53    int                 area_x, area_y;
54    int                 x, y;
55    int                 w, h;
56    int                 layer;
57    char                sticky;
58    char                shaded;
59    unsigned int        flags[2];
60    char               *cmd;
61    int                *groups;
62    int                 num_groups;
63    char                skiptask;
64    char                skipfocus;
65    char                skipwinlist;
66 #if USE_COMPOSITE
67    int                 opacity;
68    int                 focused_opacity;
69    char                shadow;
70 #endif
71 };
72
73 static Ecore_List  *ss_list = NULL;
74 static Timer       *ss_timer = NULL;
75
76 static Snapshot    *
77 _SnapCreate(const char *name)
78 {
79    Snapshot           *sn;
80
81    sn = ECALLOC(Snapshot, 1);
82    if (!sn)
83       return NULL;
84
85    if (!ss_list)
86       ss_list = ecore_list_new();
87    ecore_list_append(ss_list, sn);
88
89    sn->name = Estrdup(name);
90
91    return sn;
92 }
93
94 static void
95 _SnapDestroy(Snapshot * sn)
96 {
97    /* Just making sure */
98    sn = (Snapshot *) ecore_list_node_remove(ss_list, sn);
99    if (!sn)
100       return;
101
102    if (sn->used)
103       sn->used->snap = NULL;
104
105    Efree(sn->name);
106    Efree(sn->win_title);
107    Efree(sn->win_name);
108    Efree(sn->win_class);
109    Efree(sn->win_role);
110    Efree(sn->border_name);
111    Efree(sn->cmd);
112    Efree(sn->groups);
113
114    Efree(sn);
115 }
116
117 /*
118  * Stupid hack to fix apps that set WM_WINDOW_ROLE to
119  * a <name>-<pid>-<something>-<time> like thing.
120  * Is this even ICCCM compliant?
121  */
122 static char        *
123 _ParseRole(const char *role, char *buf, int len)
124 {
125    int                 l1, l2;
126
127    if (!role)
128       return NULL;
129
130    l1 = strlen(role);
131    if (l1 >= len)
132       l1 = len - 1;
133
134    for (l2 = l1; l2 > 0; l2--)
135      {
136         if (role[l2 - 1] != '-' &&
137             !(role[l2 - 1] >= '0' && role[l2 - 1] <= '9'))
138            break;
139      }
140    if (l1 - l2 > 8)
141       l1 = l2;
142    memcpy(buf, role, l1);
143    buf[l1] = '\0';
144
145    return buf;
146 }
147
148 #define SEQ(s1, s2) ((s1) && (s2) && !strcmp(s1, s2))
149
150 static int
151 _SnapEwinMatch(const Snapshot * sn, const EWin * ewin)
152 {
153    char                buf[256], *s;
154
155    /* Don't allow matching anything */
156    if (!sn->match_flags)
157       return 0;
158
159    if (ewin->state.identified)
160       return sn->win == EwinGetClientXwin(ewin);
161
162    if (sn->startup_id && !sn->cmd)
163       return 0;
164
165    if (sn->match_flags & SNAP_MATCH_TITLE
166        && !SEQ(sn->win_title, EwinGetIcccmName(ewin)))
167       return 0;
168
169    if (sn->match_flags & SNAP_MATCH_NAME
170        && !SEQ(sn->win_name, EwinGetIcccmCName(ewin)))
171       return 0;
172
173    if (sn->match_flags & SNAP_MATCH_CLASS
174        && !SEQ(sn->win_class, EwinGetIcccmClass(ewin)))
175       return 0;
176
177    if (sn->match_flags & SNAP_MATCH_ROLE)
178      {
179         s = _ParseRole(ewin->icccm.wm_role, buf, sizeof(buf));
180         if (!SEQ(sn->win_role, s))
181            return 0;
182      }
183
184    /* Match! */
185    return 1;
186 }
187
188 static int
189 _SnapEwinFindMatchCmd(const void *data, const void *match)
190 {
191    const Snapshot     *sn = (Snapshot *) data;
192    const EWin         *ewin = (EWin *) match;
193
194    return sn->used ||
195       !(sn->startup_id && SEQ(sn->cmd, ewin->icccm.wm_command) &&
196         _SnapEwinMatch(sn, ewin));
197 }
198
199 static int
200 _SnapEwinFindMatch(const void *data, const void *match)
201 {
202    const Snapshot     *sn = (Snapshot *) data;
203    const EWin         *ewin = (EWin *) match;
204
205    return sn->used || !_SnapEwinMatch(sn, ewin);
206 }
207
208 /* find a snapshot state that applies to this ewin */
209 static Snapshot    *
210 _SnapEwinFind(EWin * ewin)
211 {
212    Snapshot           *sn;
213
214    if (ewin->snap)
215       return ewin->snap;
216
217    if (ecore_list_count(ss_list) <= 0)
218       return NULL;
219
220    /* If exec'ed by snap try matching command exactly */
221    sn = (Snapshot *) ecore_list_find(ss_list, _SnapEwinFindMatchCmd, ewin);
222
223    if (!sn)
224       sn = (Snapshot *) ecore_list_find(ss_list, _SnapEwinFindMatch, ewin);
225
226    if (sn && !(sn->match_flags & SNAP_MATCH_MULTIPLE))
227      {
228         sn->used = ewin;
229         ewin->snap = sn;
230      }
231
232    return sn;
233 }
234
235 #define ST(s) ((s) ? (s) : "")
236
237 /* find a snapshot state that applies to this ewin Or if that doesnt exist */
238 /* create a new one */
239 static Snapshot    *
240 _SnapEwinGet(EWin * ewin, unsigned int match_flags)
241 {
242    Snapshot           *sn;
243    char                buf[1024], *s;
244
245    sn = _SnapEwinFind(ewin);
246    if (sn)
247       return sn;
248
249    if ((match_flags & SNAP_MATCH_TITLE) && !EwinGetIcccmName(ewin))
250       match_flags ^= SNAP_MATCH_TITLE;
251    if ((match_flags & SNAP_MATCH_NAME) && !EwinGetIcccmCName(ewin))
252       match_flags ^= SNAP_MATCH_NAME;
253    if ((match_flags & SNAP_MATCH_CLASS) && !EwinGetIcccmClass(ewin))
254       match_flags ^= SNAP_MATCH_CLASS;
255    if ((match_flags & SNAP_MATCH_ROLE) && !ewin->icccm.wm_role)
256       match_flags ^= SNAP_MATCH_ROLE;
257    if (match_flags == 0)
258      {
259         if (!EwinGetIcccmName(ewin))
260            return NULL;
261         match_flags = SNAP_MATCH_TITLE;
262      }
263
264    sn = _SnapCreate(NULL);
265    if (!sn)
266       return NULL;
267
268    sn->match_flags = match_flags;
269    if (match_flags & SNAP_MATCH_TITLE)
270       sn->win_title = Estrdup(EwinGetIcccmName(ewin));
271    if (match_flags & SNAP_MATCH_NAME)
272       sn->win_name = Estrdup(EwinGetIcccmCName(ewin));
273    if (match_flags & SNAP_MATCH_CLASS)
274       sn->win_class = Estrdup(EwinGetIcccmClass(ewin));
275    if (match_flags & SNAP_MATCH_ROLE)
276      {
277         s = _ParseRole(ewin->icccm.wm_role, buf, sizeof(buf));
278         sn->win_role = Estrdup(s);
279      }
280
281    /* Set the snap name. Has no particular significance. */
282    if ((sn->win_name || sn->win_class) && sn->win_role)
283       Esnprintf(buf, sizeof(buf), "%s.%s:%s", ST(sn->win_name),
284                 ST(sn->win_class), sn->win_role);
285    else if (sn->win_name || sn->win_class)
286       Esnprintf(buf, sizeof(buf), "%s.%s", ST(sn->win_name), ST(sn->win_class));
287    else if (sn->win_title)
288       Esnprintf(buf, sizeof(buf), "TITLE.%s", sn->win_title);
289    else                         /* We should not go here */
290       Esnprintf(buf, sizeof(buf), "TITLE.%s", EwinGetIcccmName(ewin));
291    sn->name = Estrdup(buf);
292
293    if (!(sn->match_flags & SNAP_MATCH_MULTIPLE))
294      {
295         sn->used = ewin;
296         ewin->snap = sn;
297      }
298
299    return sn;
300 }
301
302 /* record info about this Ewin's attributes */
303
304 static void
305 _SnapUpdateEwinBorder(Snapshot * sn, const EWin * ewin)
306 {
307    Efree(sn->border_name);
308    sn->border_name = Estrdup(BorderGetName(ewin->normal_border));
309 }
310
311 static void
312 _SnapUpdateEwinDesktop(Snapshot * sn, const EWin * ewin)
313 {
314    sn->desktop = EoGetDeskNum(ewin);
315 }
316
317 static void
318 _SnapUpdateEwinSize(Snapshot * sn, const EWin * ewin)
319 {
320    sn->w = ewin->client.w;
321    sn->h = ewin->client.h;
322 }
323
324 static void
325 _SnapUpdateEwinLocation(Snapshot * sn, const EWin * ewin)
326 {
327    int                 ax, ay;
328
329    sn->x = EoGetX(ewin);
330    sn->y = EoGetY(ewin);
331    sn->area_x = ewin->area_x;
332    sn->area_y = ewin->area_y;
333    if (!EoIsSticky(ewin))
334      {
335         DeskGetArea(EoGetDesk(ewin), &ax, &ay);
336         sn->x += ((ax - sn->area_x) * WinGetW(VROOT));
337         sn->y += ((ay - sn->area_y) * WinGetH(VROOT));
338      }
339 }
340
341 static void
342 _SnapUpdateEwinLayer(Snapshot * sn, const EWin * ewin)
343 {
344    sn->layer = EoGetLayer(ewin);
345 }
346
347 static void
348 _SnapUpdateEwinSticky(Snapshot * sn, const EWin * ewin)
349 {
350    sn->sticky = EoIsSticky(ewin);
351 }
352
353 static void
354 _SnapUpdateEwinShade(Snapshot * sn, const EWin * ewin)
355 {
356    sn->shaded = ewin->state.shaded;
357 }
358
359 static void
360 _SnapUpdateEwinSkipLists(Snapshot * sn, const EWin * ewin)
361 {
362    sn->skiptask = ewin->props.skip_ext_task;
363    sn->skipwinlist = ewin->props.skip_winlist;
364    sn->skipfocus = ewin->props.skip_focuslist;
365 }
366
367 static void
368 _SnapUpdateEwinFlags(Snapshot * sn, const EWin * ewin)
369 {
370    EwinFlagsEncode(ewin, sn->flags);
371 }
372
373 static void
374 _SnapUpdateEwinCmd(Snapshot * sn, const EWin * ewin)
375 {
376    if (ewin->icccm.wm_machine &&
377        strcmp(ewin->icccm.wm_machine, Mode.wm.machine_name))
378       return;
379
380    Efree(sn->cmd);
381    sn->cmd = Estrdup(ewin->icccm.wm_command);
382 }
383
384 static void
385 _SnapUpdateEwinGroups(Snapshot * sn, const EWin * ewin, char onoff)
386 {
387    EWin              **gwins = NULL;
388    Group              *const *groups;
389    int                 i, j, num, num_groups;
390
391    if (!ewin)
392       return;
393
394    if (!ewin->groups)
395      {
396         Efree(sn->groups);
397         sn->groups = NULL;
398         sn->num_groups = 0;
399         return;
400      }
401
402    gwins =
403       ListWinGroupMembersForEwin(ewin, GROUP_ACTION_ANY, Mode.nogroup, &num);
404    for (i = 0; i < num; i++)
405      {
406         if (onoff)
407           {
408              groups = EwinGetGroups(gwins[i], &num_groups);
409              if (!groups)
410                 continue;
411
412              sn = gwins[i]->snap;
413              if (!sn)
414                 sn = _SnapEwinGet(gwins[i], SNAP_MATCH_DEFAULT);
415              if (!sn)
416                 continue;
417
418              sn->num_groups = num_groups;
419              Efree(sn->groups);
420              sn->groups = EMALLOC(int, num_groups);
421
422              for (j = 0; j < num_groups; j++)
423                {
424                   sn->groups[j] = groups[j]->index;
425                   groups[j]->save = 1;
426                }
427           }
428         else
429           {
430              if (ewin->snap)
431                {
432                   sn = gwins[i]->snap;
433                   if (sn)
434                     {
435                        Efree(sn->groups);
436                        sn->groups = NULL;
437                        sn->num_groups = 0;
438                     }
439                }
440           }
441      }
442    Efree(gwins);
443 }
444
445 #if USE_COMPOSITE
446
447 static void
448 _SnapUpdateEwinOpacity(Snapshot * sn, const EWin * ewin)
449 {
450    sn->opacity = OpacityToPercent(ewin->ewmh.opacity);
451    sn->focused_opacity = OpacityToPercent(ewin->props.focused_opacity);
452 }
453
454 static void
455 _SnapUpdateEwinShadow(Snapshot * sn, const EWin * ewin)
456 {
457    sn->shadow = EoGetShadow(ewin);
458 }
459
460 #endif
461
462 static void
463 _SnapUpdateEwin(Snapshot * sn, const EWin * ewin, unsigned int flags)
464 {
465    /* FIXME - We should check if anything is actually changed */
466
467    if (flags & SNAP_USE_BORDER)
468       _SnapUpdateEwinBorder(sn, ewin);
469    if (flags & SNAP_USE_COMMAND)
470       _SnapUpdateEwinCmd(sn, ewin);
471    if (flags & SNAP_USE_DESK)
472       _SnapUpdateEwinDesktop(sn, ewin);
473    if (flags & SNAP_USE_POS)
474       _SnapUpdateEwinLocation(sn, ewin);
475    if (flags & SNAP_USE_SIZE)
476       _SnapUpdateEwinSize(sn, ewin);
477    if (flags & SNAP_USE_LAYER)
478       _SnapUpdateEwinLayer(sn, ewin);
479    if (flags & SNAP_USE_STICKY)
480       _SnapUpdateEwinSticky(sn, ewin);
481    if (flags & SNAP_USE_SHADED)
482       _SnapUpdateEwinShade(sn, ewin);
483    if (flags & SNAP_USE_SKIP_LISTS)
484       _SnapUpdateEwinSkipLists(sn, ewin);
485    if (flags & SNAP_USE_FLAGS)
486       _SnapUpdateEwinFlags(sn, ewin);
487 #if USE_COMPOSITE
488    if (flags & SNAP_USE_OPACITY)
489       _SnapUpdateEwinOpacity(sn, ewin);
490    if (flags & SNAP_USE_SHADOW)
491       _SnapUpdateEwinShadow(sn, ewin);
492 #endif
493    if (flags & SNAP_USE_GROUPS)
494       _SnapUpdateEwinGroups(sn, ewin, ewin->num_groups);
495
496    SnapshotsSave();
497 }
498
499 static void
500 _EwinSnapSet(EWin * ewin, unsigned int match_flags, unsigned int use_flags)
501 {
502    Snapshot           *sn;
503
504    /* Quit if nothing to be saved */
505    if (!match_flags || !(use_flags & SNAP_USE_ALL))
506       return;
507
508    sn = _SnapEwinGet(ewin, match_flags);
509    if (!sn)
510       return;
511
512    if (use_flags & SNAP_AUTO)
513       sn->track_changes = 1;
514
515    sn->use_flags = use_flags & SNAP_USE_ALL;
516
517    _SnapUpdateEwin(sn, ewin, use_flags);
518 }
519
520 void
521 SnapshotEwinUpdate(const EWin * ewin, unsigned int flags)
522 {
523    Snapshot           *sn;
524
525    sn = ewin->snap;
526    if (!sn || !sn->track_changes)
527       return;
528
529 #if 0
530    Eprintf("SnapshotEwinUpdate %s: %#x\n", EwinGetTitle(ewin), flags);
531 #endif
532
533    if (flags & sn->use_flags)
534       _SnapUpdateEwin(sn, ewin, flags);
535 }
536
537 /* unsnapshot any saved info about this ewin */
538 static void
539 _EwinSnapRemove(EWin * ewin)
540 {
541    if (ewin->snap)
542       _SnapDestroy(ewin->snap);
543    ewin->snap = NULL;
544 }
545
546 #if ENABLE_DIALOGS
547 /*
548  * Snapshot dialogs
549  */
550 typedef struct {
551    Window              client;
552
553    struct {
554       char                title;
555       char                name;
556       char                clss;
557       char                role;
558    } match;
559
560    char                track_changes;
561    char                snap_border;
562    char                snap_desktop;
563    char                snap_size;
564    char                snap_location;
565    char                snap_layer;
566    char                snap_sticky;
567    char                snap_icon;
568    char                snap_shaded;
569    char                snap_cmd;
570    char                snap_group;
571    char                snap_skiplists;
572    char                snap_flags;
573
574 #if USE_COMPOSITE
575    char                snap_opacity;
576    char                snap_shadow;
577 #endif
578 } SnapDlgData;
579
580 static void
581 CB_ApplySnap(Dialog * d, int val, void *data __UNUSED__)
582 {
583    EWin               *ewin;
584    SnapDlgData        *sd = (SnapDlgData *) DialogGetData(d);
585    unsigned int        match_flags, use_flags;
586
587    if (val >= 2 || !sd)
588       goto done;
589
590    ewin = EwinFindByClient(sd->client);
591    if (!ewin)
592       goto done;
593
594    _EwinSnapRemove(ewin);
595
596    match_flags = 0;
597    if (sd->match.title)
598       match_flags |= SNAP_MATCH_TITLE;
599    if (sd->match.name)
600       match_flags |= SNAP_MATCH_NAME;
601    if (sd->match.clss)
602       match_flags |= SNAP_MATCH_CLASS;
603    if (sd->match.role)
604       match_flags |= SNAP_MATCH_ROLE;
605
606    if (!match_flags)
607       goto done;
608
609    use_flags = 0;
610    if (sd->track_changes)
611       use_flags |= SNAP_AUTO;
612    if (sd->snap_border)
613       use_flags |= SNAP_USE_BORDER;
614    if (sd->snap_cmd)
615       use_flags |= SNAP_USE_COMMAND;
616    if (sd->snap_desktop)
617       use_flags |= SNAP_USE_DESK;
618    if (sd->snap_location)
619       use_flags |= SNAP_USE_POS;
620    if (sd->snap_size)
621       use_flags |= SNAP_USE_SIZE;
622    if (sd->snap_layer)
623       use_flags |= SNAP_USE_LAYER;
624    if (sd->snap_sticky)
625       use_flags |= SNAP_USE_STICKY;
626    if (sd->snap_shaded)
627       use_flags |= SNAP_USE_SHADED;
628    if (sd->snap_skiplists)
629       use_flags |= SNAP_USE_SKIP_LISTS;
630    if (sd->snap_flags)
631       use_flags |= SNAP_USE_FLAGS;
632 #if USE_COMPOSITE
633    if (sd->snap_opacity)
634       use_flags |= SNAP_USE_OPACITY;
635    if (sd->snap_shadow)
636       use_flags |= SNAP_USE_SHADOW;
637 #endif
638    if (sd->snap_group)
639       use_flags |= SNAP_USE_GROUPS;
640
641    if (!use_flags)
642       goto done;
643
644    _EwinSnapSet(ewin, match_flags, use_flags);
645
646  done:
647    if (sd && val == 2)
648      {
649         Efree(sd);
650         DialogSetData(d, NULL);
651      }
652
653    SnapshotsSave();
654 }
655
656 static void
657 _DlgFillSnap(Dialog * d, DItem * table, void *data)
658 {
659    DItem              *di;
660    Snapshot           *sn;
661    SnapDlgData        *sd;
662    char                s[1024];
663    const EWin         *ewin = (EWin *) data;
664
665    sd = ECALLOC(SnapDlgData, 1);
666    DialogSetData(d, sd);
667    sd->client = EwinGetClientXwin(ewin);
668
669    sn = ewin->snap;
670    if (sn)
671      {
672         sd->match.title = (sn->match_flags & SNAP_MATCH_TITLE) != 0;
673         sd->match.name = (sn->match_flags & SNAP_MATCH_NAME) != 0;
674         sd->match.clss = (sn->match_flags & SNAP_MATCH_CLASS) != 0;
675         sd->match.role = (sn->match_flags & SNAP_MATCH_ROLE) != 0;
676
677         if (sn->track_changes)
678            sd->track_changes = 1;
679         if (sn->use_flags & SNAP_USE_BORDER)
680            sd->snap_border = 1;
681         if (sn->use_flags & SNAP_USE_COMMAND)
682            sd->snap_cmd = 1;
683         if (sn->use_flags & SNAP_USE_DESK)
684            sd->snap_desktop = 1;
685         if (sn->use_flags & SNAP_USE_POS)
686            sd->snap_location = 1;
687         if (sn->use_flags & SNAP_USE_SIZE)
688            sd->snap_size = 1;
689         if (sn->use_flags & SNAP_USE_LAYER)
690            sd->snap_layer = 1;
691         if (sn->use_flags & SNAP_USE_STICKY)
692            sd->snap_sticky = 1;
693         if (sn->use_flags & SNAP_USE_SHADED)
694            sd->snap_shaded = 1;
695         if (sn->use_flags & SNAP_USE_SKIP_LISTS)
696            sd->snap_skiplists = 1;
697         if (sn->use_flags & SNAP_USE_FLAGS)
698            sd->snap_flags = 1;
699 #if USE_COMPOSITE
700         if (sn->use_flags & SNAP_USE_OPACITY)
701            sd->snap_opacity = 1;
702         if (sn->use_flags & SNAP_USE_SHADOW)
703            sd->snap_shadow = 1;
704 #endif
705         if (sn->use_flags & SNAP_USE_GROUPS)
706            sd->snap_group = 1;
707      }
708    else
709      {
710         if (EwinGetIcccmCName(ewin))
711           {
712              sd->match.name = 1;
713              sd->match.clss = 1;
714              sd->match.role = ewin->icccm.wm_role != NULL;
715           }
716         else
717           {
718              sd->match.title = EwinGetIcccmName(ewin) != NULL;
719           }
720      }
721
722    table = DialogAddItem(table, DITEM_TABLE);
723    DialogItemTableSetOptions(table, 4, 0, 0, 0);
724
725    di = DialogAddItem(table, DITEM_CHECKBUTTON);
726    DialogItemSetAlign(di, 0, 512);
727    DialogItemSetText(di, _("Title:"));
728    DialogItemCheckButtonSetPtr(di, &sd->match.title);
729
730    di = DialogAddItem(table, DITEM_TEXT);
731    DialogItemSetColSpan(di, 3);
732    DialogItemSetAlign(di, 1024, 512);
733    DialogItemSetText(di, EwinGetIcccmName(ewin));
734
735    if (EwinGetIcccmCName(ewin))
736      {
737         di = DialogAddItem(table, DITEM_CHECKBUTTON);
738         DialogItemSetAlign(di, 0, 512);
739         DialogItemSetText(di, _("Name:"));
740         DialogItemCheckButtonSetPtr(di, &sd->match.name);
741
742         di = DialogAddItem(table, DITEM_TEXT);
743         DialogItemSetColSpan(di, 3);
744         DialogItemSetAlign(di, 1024, 512);
745         DialogItemSetText(di, EwinGetIcccmCName(ewin));
746      }
747
748    if (EwinGetIcccmClass(ewin))
749      {
750         di = DialogAddItem(table, DITEM_CHECKBUTTON);
751         DialogItemSetAlign(di, 0, 512);
752         DialogItemSetText(di, _("Class:"));
753         DialogItemCheckButtonSetPtr(di, &sd->match.clss);
754
755         di = DialogAddItem(table, DITEM_TEXT);
756         DialogItemSetColSpan(di, 3);
757         DialogItemSetAlign(di, 1024, 512);
758         DialogItemSetText(di, EwinGetIcccmClass(ewin));
759      }
760
761    if (ewin->icccm.wm_role)
762      {
763         di = DialogAddItem(table, DITEM_CHECKBUTTON);
764         DialogItemSetAlign(di, 0, 512);
765         DialogItemSetText(di, _("Role:"));
766         DialogItemCheckButtonSetPtr(di, &sd->match.role);
767
768         di = DialogAddItem(table, DITEM_TEXT);
769         DialogItemSetColSpan(di, 3);
770         DialogItemSetAlign(di, 1024, 512);
771         DialogItemSetText(di, ewin->icccm.wm_role);
772      }
773
774    if (ewin->icccm.wm_command)
775      {
776         di = DialogAddItem(table, DITEM_TEXT);
777         DialogItemSetAlign(di, 0, 512);
778         DialogItemSetText(di, _("Command:"));
779
780         di = DialogAddItem(table, DITEM_TEXT);
781         DialogItemSetColSpan(di, 3);
782         DialogItemSetAlign(di, 1024, 512);
783
784         /* if the command is long, cut in into slices of about 80 characters */
785         if (strlen(ewin->icccm.wm_command) > 80)
786           {
787              int                 i = 0, slice, last;
788
789              s[0] = 0;
790              slice = 64;
791              while ((i <= (int)strlen(ewin->icccm.wm_command))
792                     && (i < (int)(sizeof(s) / 4)))
793                {
794                   last = i;
795                   i += 64;
796                   slice = 64;
797                   /* and make sure that we don't cut in the middle of a word. */
798                   while ((ewin->icccm.wm_command[i++] != ' ')
799                          && (i < (int)(sizeof(s) / 4)))
800                      slice++;
801                   strncat(s, ewin->icccm.wm_command + last, slice);
802                   if (i < (int)(sizeof(s) / 4))
803                      strcat(s, "\n");
804                   else
805                      strcat(s, "...\n");
806                }
807              DialogItemSetText(di, s);
808           }
809         else
810            DialogItemSetText(di, ewin->icccm.wm_command);
811      }
812
813    di = DialogAddItem(table, DITEM_SEPARATOR);
814    DialogItemSetColSpan(di, 4);
815
816    di = DialogAddItem(table, DITEM_CHECKBUTTON);
817    DialogItemSetColSpan(di, 4);
818    DialogItemSetText(di, _("Track Changes"));
819    DialogItemCheckButtonSetPtr(di, &sd->track_changes);
820
821    di = DialogAddItem(table, DITEM_CHECKBUTTON);
822    DialogItemSetColSpan(di, 2);
823    DialogItemSetText(di, _("Location"));
824    DialogItemCheckButtonSetPtr(di, &sd->snap_location);
825
826    di = DialogAddItem(table, DITEM_CHECKBUTTON);
827    DialogItemSetColSpan(di, 2);
828    DialogItemSetText(di, _("Border style"));
829    DialogItemCheckButtonSetPtr(di, &sd->snap_border);
830
831    di = DialogAddItem(table, DITEM_CHECKBUTTON);
832    DialogItemSetColSpan(di, 2);
833    DialogItemSetText(di, _("Size"));
834    DialogItemCheckButtonSetPtr(di, &sd->snap_size);
835
836    di = DialogAddItem(table, DITEM_CHECKBUTTON);
837    DialogItemSetColSpan(di, 2);
838    DialogItemSetText(di, _("Desktop"));
839    DialogItemCheckButtonSetPtr(di, &sd->snap_desktop);
840
841    di = DialogAddItem(table, DITEM_CHECKBUTTON);
842    DialogItemSetColSpan(di, 2);
843    DialogItemSetText(di, _("Shaded state"));
844    DialogItemCheckButtonSetPtr(di, &sd->snap_shaded);
845
846    di = DialogAddItem(table, DITEM_CHECKBUTTON);
847    DialogItemSetColSpan(di, 2);
848    DialogItemSetText(di, _("Sticky state"));
849    DialogItemCheckButtonSetPtr(di, &sd->snap_sticky);
850
851    di = DialogAddItem(table, DITEM_CHECKBUTTON);
852    DialogItemSetColSpan(di, 2);
853    DialogItemSetText(di, _("Stacking layer"));
854    DialogItemCheckButtonSetPtr(di, &sd->snap_layer);
855
856    di = DialogAddItem(table, DITEM_CHECKBUTTON);
857    DialogItemSetColSpan(di, 2);
858    DialogItemSetText(di, _("Window List Skip"));
859    DialogItemCheckButtonSetPtr(di, &sd->snap_skiplists);
860
861 #if USE_COMPOSITE
862    di = DialogAddItem(table, DITEM_CHECKBUTTON);
863    DialogItemSetColSpan(di, 2);
864    DialogItemSetText(di, _("Opacity"));
865    DialogItemCheckButtonSetPtr(di, &sd->snap_opacity);
866
867    di = DialogAddItem(table, DITEM_CHECKBUTTON);
868    DialogItemSetColSpan(di, 2);
869    DialogItemSetText(di, _("Shadowing"));
870    DialogItemCheckButtonSetPtr(di, &sd->snap_shadow);
871 #endif
872
873    di = DialogAddItem(table, DITEM_CHECKBUTTON);
874    DialogItemSetColSpan(di, 2);
875    DialogItemSetText(di, _("Flags"));
876    DialogItemCheckButtonSetPtr(di, &sd->snap_flags);
877
878    di = DialogAddItem(table, DITEM_NONE);
879    DialogItemSetColSpan(di, 2);
880
881    if (ewin->icccm.wm_command)
882      {
883         char                ok = 1;
884
885         if (ewin->icccm.wm_machine)
886           {
887              if (strcmp(ewin->icccm.wm_machine, Mode.wm.machine_name))
888                 ok = 0;
889           }
890         if (ok)
891           {
892              di = DialogAddItem(table, DITEM_CHECKBUTTON);
893              DialogItemSetColSpan(di, 4);
894              DialogItemSetText(di, _("Restart application on login"));
895              DialogItemCheckButtonSetPtr(di, &sd->snap_cmd);
896           }
897         else
898           {
899              di = DialogAddItem(table, DITEM_NONE);
900              DialogItemSetColSpan(di, 4);
901           }
902      }
903    else
904      {
905         di = DialogAddItem(table, DITEM_NONE);
906         DialogItemSetColSpan(di, 4);
907      }
908
909    if (ewin->groups)
910      {
911         di = DialogAddItem(table, DITEM_CHECKBUTTON);
912         DialogItemSetColSpan(di, 4);
913         DialogItemSetText(di, _("Remember this window's group(s)"));
914         DialogItemCheckButtonSetPtr(di, &sd->snap_group);
915      }
916 }
917
918 static const DialogDef DlgSnap = {
919    NULL,
920    NULL,
921    N_("Remembered Application Attributes"),
922    SOUND_NONE,
923    "pix/snapshots.png",
924    N_("Select the attributes of this\n"
925       "window you wish to Remember\n" "from now on\n"),
926    _DlgFillSnap,
927    DLG_OAC, CB_ApplySnap,
928 };
929
930 static void
931 _EwinSnapDialog(const EWin * ewin)
932 {
933    char                s[1024];
934
935    Esnprintf(s, sizeof(s), "SNAPSHOT_WINDOW-%#lx", EwinGetClientXwin(ewin));
936
937    DialogShowSimpleWithName(&DlgSnap, s, (void *)ewin);
938 }
939
940 /* list of remembered items for the remember dialog -- it's either
941  * _another_ global var, or a wrapper struct to pass data to the 
942  * callback funcs besides the dialog itself -- this is much easier */
943
944 typedef struct _remwinlist {
945    Snapshot           *snap;
946    char                remove;
947 } RememberWinList;
948
949 static RememberWinList *rd_ewin_list;
950
951 static void
952 CB_ApplyRemember(Dialog * d __UNUSED__, int val, void *data __UNUSED__)
953 {
954    int                 i;
955
956    if (val < 2 && rd_ewin_list)
957      {
958         for (i = 0; rd_ewin_list[i].snap; i++)
959           {
960              if (!rd_ewin_list[i].remove)
961                 continue;
962
963              _SnapDestroy(rd_ewin_list[i].snap);
964           }
965         /* save snapshot info to disk */
966         SnapshotsSave();
967      }
968
969    if (((val == 0) || (val == 2)) && rd_ewin_list)
970      {
971         Efree(rd_ewin_list);
972         rd_ewin_list = NULL;
973      }
974 }
975
976 static void
977 CB_RememberWindowSettings(Dialog * d __UNUSED__, int val __UNUSED__, void *data)
978 {
979    RememberWinList    *rd;
980    Snapshot           *sn;
981
982    if (!data)
983       return;
984    rd = (RememberWinList *) data;
985
986    /* Make sure its still there */
987    sn = (Snapshot *) ecore_list_goto(ss_list, rd->snap);
988
989    if (!sn || !sn->used)
990       return;
991    _EwinSnapDialog(sn->used);
992 }
993
994 static void
995 _DlgFillRemember(Dialog * d __UNUSED__, DItem * table, void *data __UNUSED__)
996 {
997    DItem              *di;
998    Snapshot           *sn;
999    int                 i, num;
1000    char                buf[128];
1001    const char         *s;
1002
1003    DialogItemTableSetOptions(table, 3, 0, 0, 0);
1004
1005    num = ecore_list_count(ss_list);
1006    rd_ewin_list = EMALLOC(RememberWinList, num + 1);
1007
1008    if (num > 0)
1009      {
1010         di = DialogAddItem(table, DITEM_TEXT);
1011         DialogItemSetColSpan(di, 3);
1012         DialogItemSetFill(di, 0, 0);
1013         DialogItemSetAlign(di, 0, 512);
1014         DialogItemSetText(di, _("Delete"));
1015      }
1016
1017    i = 0;
1018    ECORE_LIST_FOR_EACH(ss_list, sn)
1019    {
1020       rd_ewin_list[i].snap = sn;
1021       rd_ewin_list[i].remove = 0;
1022
1023       di = DialogAddItem(table, DITEM_CHECKBUTTON);
1024       DialogItemSetColSpan(di, 2);
1025       DialogItemSetAlign(di, 0, 512);
1026       if (sn->used)
1027          s = EwinGetTitle(sn->used);
1028       else if (sn->win_title)
1029          s = sn->win_title;
1030       else
1031         {
1032            Esnprintf(buf, sizeof(buf), "%s.%s", sn->win_name, sn->win_class);
1033            s = buf;
1034         }
1035       DialogItemSetText(di, s);
1036       DialogItemCheckButtonSetPtr(di, &(rd_ewin_list[i].remove));
1037
1038       if (sn->used)
1039         {
1040            di = DialogAddItem(table, DITEM_BUTTON);
1041            DialogItemSetAlign(di, 0, 512);
1042            DialogItemSetText(di, _("Remembered Settings..."));
1043            DialogItemSetCallback(di, CB_RememberWindowSettings, 0,
1044                                  (char *)(&rd_ewin_list[i]));
1045         }
1046       else
1047         {
1048            di = DialogAddItem(table, DITEM_TEXT);
1049            DialogItemSetText(di, _("Unused"));
1050         }
1051       i++;
1052    }
1053    rd_ewin_list[num].snap = NULL;
1054
1055    /* finish remember window */
1056    if (!num)
1057      {
1058         di = DialogAddItem(table, DITEM_TEXT);
1059         DialogItemSetColSpan(di, 3);
1060         DialogItemSetText(di,
1061                           _
1062                           ("There are no active windows with remembered attributes."));
1063      }
1064 }
1065
1066 const DialogDef     DlgRemember = {
1067    "CONFIGURE_PAGER",
1068    N_("Remember"),
1069    N_("Remembered Windows Settings"),
1070    SOUND_SETTINGS_PAGER,
1071    "pix/snapshots.png",
1072    N_("Enlightenment Remembered\n" "Windows Settings Dialog\n"),
1073    _DlgFillRemember,
1074    DLG_OC, CB_ApplyRemember,
1075 };
1076 #endif /* ENABLE_DIALOGS */
1077
1078 /* ... combine writes, only save after a timeout */
1079 void
1080 SnapshotsSave(void)
1081 {
1082    TIMER_DEL(ss_timer);
1083    TIMER_ADD(ss_timer, 5.0, SnapshotsSaveReal, NULL);
1084 }
1085
1086 /* save out all snapped info to disk */
1087 int
1088 SnapshotsSaveReal(void *data __UNUSED__)
1089 {
1090    Snapshot           *sn;
1091    int                 j;
1092    char                buf[4096], s[4096];
1093    FILE               *f;
1094
1095    if (!Mode.wm.save_ok)
1096       goto done;
1097
1098    Etmp(s);
1099    f = fopen(s, "w");
1100    if (!f)
1101       goto done;
1102
1103    ECORE_LIST_FOR_EACH(ss_list, sn)
1104    {
1105       fprintf(f, "NEW: %s\n", sn->name);
1106       if (sn->used)
1107          fprintf(f, "WIN: %#lx\n", EwinGetClientXwin(sn->used));
1108       if ((sn->match_flags & SNAP_MATCH_TITLE) && sn->win_title)
1109          fprintf(f, "TITLE: %s\n", sn->win_title);
1110       if ((sn->match_flags & SNAP_MATCH_NAME) && sn->win_name)
1111          fprintf(f, "NAME: %s\n", sn->win_name);
1112       if ((sn->match_flags & SNAP_MATCH_CLASS) && sn->win_class)
1113          fprintf(f, "CLASS: %s\n", sn->win_class);
1114       if ((sn->match_flags & SNAP_MATCH_ROLE) && sn->win_role)
1115          fprintf(f, "ROLE: %s\n", sn->win_role);
1116       if (sn->track_changes)
1117          fprintf(f, "AUTO: yes\n");
1118       if ((sn->use_flags & SNAP_USE_BORDER) && sn->border_name)
1119          fprintf(f, "BORDER: %s\n", sn->border_name);
1120       if ((sn->use_flags & SNAP_USE_COMMAND) && sn->cmd)
1121          fprintf(f, "CMD: %s\n", sn->cmd);
1122       if (sn->use_flags & SNAP_USE_DESK)
1123          fprintf(f, "DESKTOP: %i\n", sn->desktop);
1124       if (sn->use_flags & SNAP_USE_POS)
1125          fprintf(f, "RES: %i %i\n", WinGetW(VROOT), WinGetH(VROOT));
1126       if (sn->use_flags & SNAP_USE_SIZE)
1127          fprintf(f, "WH: %i %i\n", sn->w, sn->h);
1128       if (sn->use_flags & SNAP_USE_POS)
1129          fprintf(f, "XY: %i %i %i %i\n", sn->x, sn->y, sn->area_x, sn->area_y);
1130       if (sn->use_flags & SNAP_USE_LAYER)
1131          fprintf(f, "LAYER: %i\n", sn->layer);
1132       if (sn->use_flags & SNAP_USE_STICKY)
1133          fprintf(f, "STICKY: %i\n", sn->sticky);
1134       if (sn->use_flags & SNAP_USE_SHADED)
1135          fprintf(f, "SHADE: %i\n", sn->shaded);
1136       if (sn->use_flags & SNAP_USE_SKIP_LISTS)
1137         {
1138            fprintf(f, "SKIPTASK: %i\n", sn->skiptask);
1139            fprintf(f, "SKIPWINLIST: %i\n", sn->skipwinlist);
1140            fprintf(f, "SKIPFOCUS: %i\n", sn->skipfocus);
1141         }
1142       if (sn->use_flags & SNAP_USE_FLAGS)
1143          fprintf(f, "FLAGS: %#x %#x\n", sn->flags[0], sn->flags[1]);
1144 #if USE_COMPOSITE
1145       if (sn->use_flags & SNAP_USE_OPACITY)
1146          fprintf(f, "OPACITY: %i %i\n", sn->opacity, sn->focused_opacity);
1147       if (sn->use_flags & SNAP_USE_SHADOW)
1148          fprintf(f, "SHADOW: %i\n", sn->shadow);
1149 #endif
1150       if (sn->groups)
1151         {
1152            for (j = 0; j < sn->num_groups; j++)
1153               fprintf(f, "GROUP: %i\n", sn->groups[j]);
1154         }
1155       fprintf(f, "\n");
1156    }
1157
1158    fclose(f);
1159
1160    Esnprintf(buf, sizeof(buf), "%s.snapshots", EGetSavePrefix());
1161
1162    if (EDebug(EDBUG_TYPE_SNAPS))
1163       Eprintf("SnapshotsSaveReal: %s\n", buf);
1164    E_mv(s, buf);
1165    if (!isfile(buf))
1166       Alert(_("Error saving snaps file\n"));
1167
1168    GroupsSave();
1169
1170  done:
1171    TIMER_DEL(ss_timer);
1172    return 0;
1173 }
1174
1175 void
1176 SnapshotsSpawn(void)
1177 {
1178    Snapshot           *sn;
1179
1180    ECORE_LIST_FOR_EACH(ss_list, sn)
1181    {
1182       if ((sn->use_flags & SNAP_USE_COMMAND) && (sn->cmd) &&
1183           !sn->used && !(sn->match_flags & SNAP_MATCH_MULTIPLE))
1184         {
1185            sn->startup_id = ++Mode.apps.startup_id;
1186            EspawnCmd(sn->cmd);
1187         }
1188    }
1189 }
1190
1191 /* load all snapped info */
1192 void
1193 SnapshotsLoad(void)
1194 {
1195    Snapshot           *sn = NULL;
1196    char                buf[4096], *s;
1197    FILE               *f;
1198    int                 res_w, res_h, a, b, c, d;
1199
1200    GroupsLoad();
1201
1202    Esnprintf(buf, sizeof(buf), "%s.snapshots", EGetSavePrefix());
1203    f = fopen(buf, "r");
1204    if (!f)
1205       return;
1206
1207    res_w = WinGetW(VROOT);
1208    res_h = WinGetH(VROOT);
1209    while (fgets(buf, sizeof(buf), f))
1210      {
1211         s = strchr(buf, ':');
1212         if (!s)
1213            continue;
1214         *s++ = '\0';
1215         s = Estrtrim(s);
1216         if (!buf[0] || !s[0])
1217            continue;
1218         if (!strcmp(buf, "NEW"))
1219           {
1220              res_w = WinGetW(VROOT);
1221              res_h = WinGetH(VROOT);
1222              sn = _SnapCreate(s);
1223           }
1224         else if (sn)
1225           {
1226              if (!strcmp(buf, "WIN"))
1227                {
1228                   sn->win = strtoul(s, NULL, 0);
1229                }
1230              else if (!strcmp(buf, "TITLE"))
1231                {
1232                   sn->win_title = Estrdup(s);
1233                   sn->match_flags |= SNAP_MATCH_TITLE;
1234                }
1235              else if (!strcmp(buf, "NAME"))
1236                {
1237                   sn->win_name = Estrdup(s);
1238                   sn->match_flags |= SNAP_MATCH_NAME;
1239                }
1240              else if (!strcmp(buf, "CLASS"))
1241                {
1242                   sn->win_class = Estrdup(s);
1243                   sn->match_flags |= SNAP_MATCH_CLASS;
1244                }
1245              else if (!strcmp(buf, "ROLE"))
1246                {
1247                   sn->win_role = Estrdup(s);
1248                   sn->match_flags |= SNAP_MATCH_ROLE;
1249                }
1250              else if (!strcmp(buf, "AUTO"))
1251                {
1252                   sn->track_changes = 1;
1253                }
1254              else if (!strcmp(buf, "BORDER"))
1255                {
1256                   sn->use_flags |= SNAP_USE_BORDER;
1257                   sn->border_name = Estrdup(s);
1258                }
1259              else if (!strcmp(buf, "CMD"))
1260                {
1261                   sn->use_flags |= SNAP_USE_COMMAND;
1262                   sn->cmd = Estrdup(s);
1263                }
1264              else if (!strcmp(buf, "DESKTOP"))
1265                {
1266                   sn->use_flags |= SNAP_USE_DESK;
1267                   sn->desktop = atoi(s);
1268                }
1269              else if (!strcmp(buf, "RES"))
1270                {
1271                   if (sscanf(s, "%u %u", &a, &b) < 2)
1272                      continue;
1273                   if (a <= 0 || b <= 0)
1274                      continue;
1275                   res_w = a;
1276                   res_h = b;
1277                }
1278              else if (!strcmp(buf, "WH"))
1279                {
1280                   if (sscanf(s, "%u %u", &a, &b) < 2)
1281                      continue;
1282                   if (a <= 0 || b <= 0)
1283                      continue;
1284                   sn->use_flags |= SNAP_USE_SIZE;
1285                   sn->w = a;
1286                   sn->h = b;
1287                }
1288              else if (!strcmp(buf, "XY"))
1289                {
1290                   if (sscanf(s, "%d %d %u %u", &a, &b, &c, &d) < 4)
1291                      continue;
1292                   sn->use_flags |= SNAP_USE_POS;
1293                   sn->x = a;
1294                   sn->y = b;
1295                   /* we changed reses since we last used this snapshot file */
1296                   if (res_w != WinGetW(VROOT))
1297                     {
1298                        if (sn->use_flags & SNAP_USE_SIZE)
1299                          {
1300                             if ((res_w - sn->w) <= 0)
1301                                sn->x = 0;
1302                             else
1303                                sn->x =
1304                                   (sn->x * (WinGetW(VROOT) - sn->w)) /
1305                                   (res_w - sn->w);
1306                          }
1307                        else
1308                          {
1309                             if (sn->x >= WinGetW(VROOT))
1310                                sn->x = WinGetW(VROOT) - 32;
1311                          }
1312                     }
1313                   if (res_h != WinGetH(VROOT))
1314                     {
1315                        if (sn->use_flags & SNAP_USE_SIZE)
1316                          {
1317                             if ((res_h - sn->h) <= 0)
1318                                sn->y = 0;
1319                             else
1320                                sn->y =
1321                                   (sn->y * (WinGetH(VROOT) - sn->h)) /
1322                                   (res_h - sn->h);
1323                          }
1324                        else
1325                          {
1326                             if (sn->y >= WinGetH(VROOT))
1327                                sn->y = WinGetH(VROOT) - 32;
1328                          }
1329                     }
1330                   sn->area_x = c;
1331                   sn->area_y = d;
1332                }
1333              else if (!strcmp(buf, "LAYER"))
1334                {
1335                   sn->use_flags |= SNAP_USE_LAYER;
1336                   sn->layer = atoi(s);
1337                }
1338              else if (!strcmp(buf, "STICKY"))
1339                {
1340                   sn->use_flags |= SNAP_USE_STICKY;
1341                   sn->sticky = atoi(s);
1342                }
1343              else if (!strcmp(buf, "SHADE"))
1344                {
1345                   sn->use_flags |= SNAP_USE_SHADED;
1346                   sn->shaded = atoi(s);
1347                }
1348              else if (!strcmp(buf, "SKIPFOCUS"))
1349                {
1350                   sn->use_flags |= SNAP_USE_SKIP_LISTS;
1351                   sn->skipfocus = atoi(s);
1352                }
1353              else if (!strcmp(buf, "SKIPTASK"))
1354                {
1355                   sn->use_flags |= SNAP_USE_SKIP_LISTS;
1356                   sn->skiptask = atoi(s);
1357                }
1358              else if (!strcmp(buf, "SKIPWINLIST"))
1359                {
1360                   sn->use_flags |= SNAP_USE_SKIP_LISTS;
1361                   sn->skipwinlist = atoi(s);
1362                }
1363              else if (!strcmp(buf, "FLAGS"))
1364                {
1365                   sn->use_flags |= SNAP_USE_FLAGS;
1366                   sn->flags[0] = sn->flags[1] = 0;
1367                   sscanf(s, "%i %i", sn->flags, sn->flags + 1);
1368                }
1369              else if (!strcmp(buf, "GROUP"))
1370                {
1371                   sn->use_flags |= SNAP_USE_GROUPS;
1372                   sn->num_groups++;
1373                   sn->groups = EREALLOC(int, sn->groups, sn->num_groups);
1374
1375                   sn->groups[sn->num_groups - 1] = atoi(s);
1376                   GroupRemember(sn->groups[sn->num_groups - 1]);
1377                }
1378 #if USE_COMPOSITE
1379              else if (!strcmp(buf, "OPACITY"))
1380                {
1381                   sn->use_flags |= SNAP_USE_OPACITY;
1382                   a = 100;
1383                   b = 100;
1384                   sscanf(s, "%i %i", &a, &b);
1385                   if (b == 1)
1386                      b = 100;   /* BW compat - focused is opaque */
1387                   sn->opacity = a;
1388                   sn->focused_opacity = b;
1389                }
1390              else if (!strcmp(buf, "SHADOW"))
1391                {
1392                   sn->use_flags |= SNAP_USE_SHADOW;
1393                   sn->shadow = atoi(s);
1394                }
1395 #endif
1396           }
1397      }
1398    fclose(f);
1399 }
1400
1401 /* make a client window conform to snapshot info */
1402 void
1403 SnapshotEwinApply(EWin * ewin)
1404 {
1405    Snapshot           *sn;
1406    int                 ax, ay;
1407    unsigned int        use_flags;
1408
1409    _SnapEwinFind(ewin);         /* Find a saved settings match */
1410
1411    sn = ewin->snap;
1412    if (!sn)
1413      {
1414         if (ewin->props.autosave)
1415            _EwinSnapSet(ewin, SNAP_MATCH_DEFAULT, SNAP_USE_ALL | SNAP_AUTO);
1416         return;
1417      }
1418
1419    if (ewin->props.autosave)
1420       sn->track_changes = 1;
1421
1422    use_flags = sn->use_flags;
1423    /* If restarting don't override stuff set in attributes/properties */
1424    if (ewin->state.identified)
1425       use_flags &= SNAP_USE_LAYER | SNAP_USE_SHADOW | SNAP_USE_GROUPS |
1426          SNAP_USE_OPACITY;
1427
1428    if (use_flags & SNAP_USE_STICKY)
1429       EoSetSticky(ewin, sn->sticky);
1430
1431    if (use_flags & SNAP_USE_DESK)
1432       EoSetDesk(ewin, DeskGet(sn->desktop));
1433
1434    if (use_flags & SNAP_USE_SIZE)
1435      {
1436         ewin->client.w = sn->w;
1437         ewin->client.h = sn->h;
1438         ewin->state.maximized_horz = ewin->state.maximized_vert = 0;
1439      }
1440
1441    if (use_flags & SNAP_USE_POS)
1442      {
1443         ewin->state.placed = 1;
1444         ewin->client.x = sn->x;
1445         ewin->client.y = sn->y;
1446 #if 0                           /* No, do later in EwinDetermineArea() */
1447         ewin->area_x = sn->area_x;
1448         ewin->area_y = sn->area_y;
1449 #endif
1450         if (!EoIsSticky(ewin))
1451           {
1452              DeskGetArea(EoGetDesk(ewin), &ax, &ay);
1453              ewin->client.x += ((sn->area_x - ax) * WinGetW(VROOT));
1454              ewin->client.y += ((sn->area_y - ay) * WinGetH(VROOT));
1455           }
1456      }
1457
1458    if (use_flags & SNAP_USE_LAYER)
1459       EoSetLayer(ewin, sn->layer);
1460
1461    if (use_flags & SNAP_USE_SKIP_LISTS)
1462      {
1463         ewin->props.skip_focuslist = sn->skipfocus;
1464         ewin->props.skip_ext_task = sn->skiptask;
1465         ewin->props.skip_winlist = sn->skipwinlist;
1466      }
1467
1468    if (use_flags & SNAP_USE_FLAGS)
1469       EwinFlagsDecode(ewin, sn->flags);
1470
1471    if (use_flags & SNAP_USE_SHADED)
1472       ewin->state.shaded = sn->shaded;
1473
1474    if (use_flags & SNAP_USE_BORDER)
1475       EwinBorderSetInitially(ewin, sn->border_name);
1476
1477    if (use_flags & SNAP_USE_GROUPS)
1478       GroupsEwinAdd(ewin, sn->groups, sn->num_groups);
1479
1480 #if USE_COMPOSITE
1481    if (use_flags & SNAP_USE_OPACITY)
1482      {
1483         sn->opacity = OpacityFix(sn->opacity, 0);
1484         sn->focused_opacity = OpacityFix(sn->focused_opacity, 0);
1485         ewin->ewmh.opacity = OpacityFromPercent(sn->opacity);
1486         ewin->props.focused_opacity = OpacityFromPercent(sn->focused_opacity);
1487      }
1488
1489    if (use_flags & SNAP_USE_SHADOW)
1490       EoSetShadow(ewin, sn->shadow);
1491 #endif
1492
1493    if (EDebug(EDBUG_TYPE_SNAPS))
1494       Eprintf("Snap get snap  %#lx: %4d+%4d %4dx%4d: %s\n",
1495               EwinGetClientXwin(ewin), ewin->client.x, ewin->client.y,
1496               ewin->client.w, ewin->client.h, EwinGetTitle(ewin));
1497 }
1498
1499 /* Detach snapshot from ewin */
1500 void
1501 SnapshotEwinUnmatch(EWin * ewin)
1502 {
1503    Snapshot           *sn;
1504
1505    sn = ewin->snap;
1506    if (sn == NULL)
1507       return;
1508
1509    ewin->snap = NULL;
1510    sn->used = NULL;
1511 }
1512
1513 void
1514 SnapshotEwinParse(EWin * ewin, const char *params)
1515 {
1516    char                param[1024];
1517    const char         *p;
1518    int                 len;
1519    unsigned int        match_flags, use_flags;
1520
1521    p = params;
1522    if (!p)
1523       return;
1524
1525    match_flags = SNAP_MATCH_DEFAULT;
1526    use_flags = 0;
1527
1528    for (;;)
1529      {
1530         param[0] = '\0';
1531         len = 0;
1532         sscanf(p, "%s %n", param, &len);
1533         if (len <= 0)
1534            break;
1535         p += len;
1536
1537         if (!strcmp(param, "all"))
1538           {
1539              use_flags = SNAP_USE_ALL;
1540           }
1541 #if ENABLE_DIALOGS
1542         else if (!strcmp(param, "dialog"))
1543           {
1544              _EwinSnapDialog(ewin);
1545              break;
1546           }
1547 #endif
1548         else if (!strcmp(param, "none"))
1549            _EwinSnapRemove(ewin);
1550         else if (!strcmp(param, "auto"))
1551            use_flags |= SNAP_AUTO;
1552         else if (!strcmp(param, "border"))
1553            use_flags |= SNAP_USE_BORDER;
1554         else if (!strcmp(param, "command"))
1555            use_flags |= SNAP_USE_COMMAND;
1556         else if (!strcmp(param, "desktop"))
1557            use_flags |= SNAP_USE_DESK;
1558         else if (!strcmp(param, "location"))
1559            use_flags |= SNAP_USE_POS;
1560         else if (!strcmp(param, "size"))
1561            use_flags |= SNAP_USE_SIZE;
1562         else if (!strcmp(param, "layer"))
1563            use_flags |= SNAP_USE_LAYER;
1564         else if (!strcmp(param, "shade"))
1565            use_flags |= SNAP_USE_SHADED;
1566         else if (!strcmp(param, "sticky"))
1567            use_flags |= SNAP_USE_STICKY;
1568 #if USE_COMPOSITE
1569         else if (!strcmp(param, "opacity"))
1570            use_flags |= SNAP_USE_OPACITY;
1571         else if (!strcmp(param, "shadow"))
1572            use_flags |= SNAP_USE_SHADOW;
1573 #endif
1574         else if (!strcmp(param, "group"))
1575            use_flags |= SNAP_USE_GROUPS;
1576      }
1577
1578    if (ewin->snap)
1579      {
1580         match_flags = ewin->snap->match_flags;
1581         use_flags |= ewin->snap->use_flags;
1582      }
1583
1584    _EwinSnapSet(ewin, match_flags, use_flags);
1585
1586    SnapshotsSave();
1587 }
1588
1589 /*
1590  * IPC functions
1591  * A bit ugly...
1592  */
1593 const char          SnapshotsIpcText[] =
1594    "usage:\n" "  list_remember [full]\n"
1595    "  Retrieve a list of remembered windows.  with full, the list\n"
1596    "  includes the window's remembered attributes\n";
1597
1598 #define SS(s) ((s) ? (s) : NoText)
1599 static const char   NoText[] = "-NONE-";
1600
1601 static void
1602 _SnapShow(void *data, void *prm)
1603 {
1604    Snapshot           *sn = (Snapshot *) data;
1605    int                 full = prm != NULL;
1606    char                buf[FILEPATH_LEN_MAX];
1607    const char         *name;
1608
1609    name = (sn->name) ? sn->name : "???";
1610
1611    if (!full)
1612      {
1613         if (sn->used)
1614            IpcPrintf("%s\n", name);
1615         else
1616            IpcPrintf("%s (unused)\n", name);
1617         return;
1618      }
1619
1620 #define SU(sn, item) ((sn->match_flags & item) ? '>' : ':')
1621
1622    if (sn->used)
1623       Esnprintf(buf, sizeof(buf), "In use - %#lx", EwinGetClientXwin(sn->used));
1624    else
1625       Esnprintf(buf, sizeof(buf), "*** Unused ***");
1626    IpcPrintf(" Snapshot  Name: %s    %s\n", name, buf);
1627    if (sn->win_title)
1628       IpcPrintf("   Window Title%c %s\n", SU(sn, SNAP_MATCH_TITLE),
1629                 sn->win_title);
1630    if (sn->win_name)
1631       IpcPrintf("   Window  Name%c %s\n", SU(sn, SNAP_MATCH_NAME),
1632                 sn->win_name);
1633    if (sn->win_class)
1634       IpcPrintf("   Window Class%c %s\n", SU(sn, SNAP_MATCH_CLASS),
1635                 sn->win_class);
1636    if (sn->win_role)
1637       IpcPrintf("   Window  Role%c %s\n", SU(sn, SNAP_MATCH_ROLE),
1638                 sn->win_role);
1639
1640    if (sn->track_changes)
1641       IpcPrintf("      Tracking changes\n");
1642    if (sn->use_flags & SNAP_USE_BORDER)
1643       IpcPrintf("      Border Name: %s\n", SS(sn->border_name));
1644    if (sn->use_flags & SNAP_USE_DESK)
1645       IpcPrintf("          desktop: %d\n", sn->desktop);
1646    if (sn->use_flags & SNAP_USE_POS)
1647       IpcPrintf("           (x, y): %d, %d    area (x, y): %d, %d\n",
1648                 sn->x, sn->y, sn->area_x, sn->area_y);
1649    if (sn->use_flags & SNAP_USE_SIZE)
1650       IpcPrintf("           (w, h): %d, %d\n", sn->w, sn->h);
1651    if (sn->use_flags & SNAP_USE_LAYER)
1652       IpcPrintf("            layer: %d\n", sn->layer);
1653    if (sn->use_flags & SNAP_USE_STICKY)
1654       IpcPrintf("           sticky: %d\n", sn->sticky);
1655    if (sn->use_flags & SNAP_USE_SHADED)
1656       IpcPrintf("            shade: %d\n", sn->shaded);
1657    if (sn->use_flags & SNAP_USE_COMMAND)
1658       IpcPrintf("          command: %s\n", SS(sn->cmd));
1659    if (sn->use_flags & SNAP_USE_SKIP_LISTS)
1660       IpcPrintf
1661          ("         skiptask: %d    skipfocus: %d    skipwinlist: %d\n",
1662           sn->skiptask, sn->skipfocus, sn->skipwinlist);
1663    if (sn->use_flags & SNAP_USE_FLAGS)
1664       IpcPrintf("            flags: %#x %#x\n", sn->flags[0], sn->flags[1]);
1665    IpcPrintf("\n");
1666 }
1667
1668 void
1669 SnapshotsIpcFunc(const char *params)
1670 {
1671    const char         *p;
1672    char                cmd[128], prm[4096];
1673    int                 len;
1674
1675    cmd[0] = prm[0] = '\0';
1676    p = params;
1677    if (p)
1678      {
1679         len = 0;
1680         sscanf(p, "%100s %4000s %n", cmd, prm, &len);
1681         p += len;
1682      }
1683
1684    if (ecore_list_count(ss_list) <= 0)
1685      {
1686         IpcPrintf("No remembered windows\n");
1687         return;
1688      }
1689
1690    if (!p || cmd[0] == '?')
1691      {
1692         ecore_list_for_each(ss_list, _SnapShow, NULL);
1693      }
1694    else
1695      {
1696         ecore_list_for_each(ss_list, _SnapShow, (void *)1L);
1697      }
1698 }