chiark / gitweb /
New option: movres.ignore_transience.
[e16] / src / iconify.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2008 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 "container.h"
26 #include "desktops.h"
27 #include "e16-ecore_hints.h"
28 #include "emodule.h"
29 #include "eobj.h"
30 #include "ewins.h"
31 #include "hints.h"
32 #include "icons.h"
33 #include "timers.h"
34 #include "tooltips.h"
35 #include "xwin.h"
36 #include <math.h>
37
38 static Container   *SelectIconboxForEwin(EWin * ewin);
39
40 /* Silly hack to avoid name clash warning when using -Wshadow */
41 #define y1 y1_
42
43 #define IB_ANIM_TIME    Conf_containers.anim_time
44 #define IB_ANIM_STEP    Conf.animation.step
45
46 static void
47 IB_Animate_Sleep(double t0, double a)
48 {
49    double              t, dt;
50
51    t = GetTime();
52    dt = t - t0 - a * 1e-3 * IB_ANIM_TIME;
53    dt = 1e-3 * IB_ANIM_STEP - dt;
54    if (dt > 0)
55       usleep((unsigned long)(1e6 * dt));
56 }
57
58 static void
59 IB_Animate_A(char iconify, EWin * ewin, EWin * ibox)
60 {
61    EWin               *fr, *to;
62    double              a, aa, spd, t0;
63    int                 x, y, x1, y1, x2, y2, x3, y3, x4, y4, w, h;
64    int                 fx, fy, fw, fh, tx, ty, tw, th;
65    Window              root = WinGetXwin(VROOT);
66    GC                  gc;
67    XGCValues           gcv;
68
69    /* Window: Extents, Iconbox: Center */
70    if (iconify)
71      {
72         fr = ewin;
73         to = ibox;
74         fw = EoGetW(fr) + 4;
75         fh = EoGetH(fr) + 4;
76         fx = EoGetX(fr) - 2;
77         fy = EoGetY(fr) - 2;
78         tw = 4;
79         th = 4;
80         tx = EoGetX(to) + EoGetW(to) / 2 - 2;
81         ty = EoGetY(to) + EoGetH(to) / 2 - 2;
82      }
83    else
84      {
85         fr = ibox;
86         to = ewin;
87         fw = 4;
88         fh = 4;
89         fx = EoGetX(fr) + EoGetW(fr) / 2 - 2;
90         fy = EoGetY(fr) + EoGetH(fr) / 2 - 2;
91         tw = EoGetW(to) + 4;
92         th = EoGetH(to) + 4;
93         tx = EoGetX(to) + 2;
94         ty = EoGetY(to) + 2;
95      }
96    fx += EoGetX(EoGetDesk(fr));
97    fy += EoGetY(EoGetDesk(fr));
98    tx += EoGetX(EoGetDesk(to));
99    ty += EoGetY(EoGetDesk(to));
100
101    gcv.subwindow_mode = IncludeInferiors;
102    gcv.function = GXxor;
103    gcv.line_width = 2;
104    gcv.foreground = Dpy.pixel_white;
105    if (gcv.foreground == 0)
106       gcv.foreground = Dpy.pixel_black;
107    gc = EXCreateGC(root,
108                    GCFunction | GCForeground | GCSubwindowMode | GCLineWidth,
109                    &gcv);
110
111    spd = (1. * IB_ANIM_STEP) / IB_ANIM_TIME;
112
113    t0 = GetTime();
114    for (a = 0.0; a < 1.0; a += spd)
115      {
116         aa = 1.0 - a;
117
118         x = (int)((fx * aa) + (tx * a));
119         y = (int)((fy * aa) + (ty * a));
120         w = (int)((fw * aa) + (tw * a));
121         h = (int)((fh * aa) + (th * a));
122
123         x = (2 * x + w) / 2;    /* x middle */
124         y = (2 * y + h) / 2;    /* y middle */
125         w /= 2;                 /* width/2 */
126         h /= 2;                 /* height/2 */
127
128         x1 = (int)(x + w * (1 - .5 * sin(3.14159 + a * 6.2831853072)));
129         y1 = (int)(y + h * cos(a * 6.2831853072));
130         x2 = (int)(x + w * (1 - .5 * sin(a * 6.2831853072)));
131         y2 = (int)(y - h * cos(a * 6.2831853072));
132         x3 = (int)(x - w * (1 - .5 * sin(3.14159 + a * 6.2831853072)));
133         y3 = (int)(y - h * cos(a * 6.2831853072));
134         x4 = (int)(x - w * (1 - .5 * sin(a * 6.2831853072)));
135         y4 = (int)(y + h * cos(a * 6.2831853072));
136
137         XDrawLine(disp, root, gc, x1, y1, x2, y2);
138         XDrawLine(disp, root, gc, x2, y2, x3, y3);
139         XDrawLine(disp, root, gc, x3, y3, x4, y4);
140         XDrawLine(disp, root, gc, x4, y4, x1, y1);
141
142         ESync(0);
143         IB_Animate_Sleep(t0, a);
144
145         XDrawLine(disp, root, gc, x1, y1, x2, y2);
146         XDrawLine(disp, root, gc, x2, y2, x3, y3);
147         XDrawLine(disp, root, gc, x3, y3, x4, y4);
148         XDrawLine(disp, root, gc, x4, y4, x1, y1);
149      }
150
151    EXFreeGC(gc);
152 }
153
154 static void
155 IB_Animate_B(char iconify, EWin * ewin, EWin * ibox)
156 {
157    EWin               *fr, *to;
158    double              a, spd, t0;
159    int                 x, y, w, h;
160    int                 fx, fy, fw, fh, tx, ty, tw, th;
161    Window              root = WinGetXwin(VROOT);
162    GC                  gc;
163    XGCValues           gcv;
164
165    if (iconify)
166      {
167         fr = ewin;
168         to = ibox;
169      }
170    else
171      {
172         fr = ibox;
173         to = ewin;
174      }
175
176    fx = EoGetX(fr) - 2;
177    fy = EoGetY(fr) - 2;
178    fw = EoGetW(fr) + 3;
179    fh = EoGetH(fr) + 3;
180
181    tx = EoGetX(to) - 2;
182    ty = EoGetY(to) - 2;
183    tw = EoGetW(to) + 3;
184    th = EoGetH(to) + 3;
185
186    fx += EoGetX(EoGetDesk(fr));
187    fy += EoGetY(EoGetDesk(fr));
188    tx += EoGetX(EoGetDesk(to));
189    ty += EoGetY(EoGetDesk(to));
190
191    gcv.subwindow_mode = IncludeInferiors;
192    gcv.function = GXxor;
193    gcv.fill_style = FillOpaqueStippled;
194    gcv.foreground = Dpy.pixel_white;
195    if (gcv.foreground == 0)
196       gcv.foreground = Dpy.pixel_black;
197    gc = EXCreateGC(root,
198                    GCFunction | GCForeground | GCSubwindowMode | GCFillStyle,
199                    &gcv);
200
201    XDrawLine(disp, root, gc, fx, fy, tx, ty);
202    XDrawLine(disp, root, gc, fx + fw, fy, tx + tw, ty);
203    XDrawLine(disp, root, gc, fx, fy + fh, tx, ty + th);
204    XDrawLine(disp, root, gc, fx + fw, fy + fh, tx + tw, ty + th);
205    XDrawRectangle(disp, root, gc, tx, ty, tw, th);
206    XDrawRectangle(disp, root, gc, fx, fy, fw, fh);
207
208    spd = (1. * IB_ANIM_STEP) / IB_ANIM_TIME;
209
210    t0 = GetTime();
211    for (a = 0.0; a < 1.0; a += spd)
212      {
213         x = (int)(fx + a * (tx - fx));
214         w = (int)(fw + a * (tw - fw));
215         y = (int)(fy + a * (ty - fy));
216         h = (int)(fh + a * (th - fh));
217         XDrawRectangle(disp, root, gc, x, y, w, h);
218
219         ESync(0);
220         IB_Animate_Sleep(t0, a);
221
222         XDrawRectangle(disp, root, gc, x, y, w, h);
223      }
224
225    XDrawLine(disp, root, gc, fx, fy, tx, ty);
226    XDrawLine(disp, root, gc, fx + fw, fy, tx + tw, ty);
227    XDrawLine(disp, root, gc, fx, fy + fh, tx, ty + th);
228    XDrawLine(disp, root, gc, fx + fw, fy + fh, tx + tw, ty + th);
229    XDrawRectangle(disp, root, gc, tx, ty, tw, th);
230    XDrawRectangle(disp, root, gc, fx, fy, fw, fh);
231
232    EXFreeGC(gc);
233 }
234
235 static void
236 IB_Animate(Container * ct, int iconify, EWin * ewin)
237 {
238    if (Mode.wm.startup || ct->anim_mode <= 0)
239       return;
240
241    if (Conf_containers.anim_time < 10 || Conf_containers.anim_time > 10000)
242       Conf_containers.anim_time = 250;
243
244    EobjsRepaint();
245    EGrabServer();
246
247    switch (ct->anim_mode)
248      {
249      default:
250         break;
251      case 1:
252         IB_Animate_A(iconify, ewin, ct->ewin);
253         break;
254      case 2:
255         IB_Animate_B(iconify, ewin, ct->ewin);
256         break;
257      }
258
259    EUngrabServer();
260 }
261
262 static int
263 IconboxObjEwinFind(Container * ct, EWin * ewin)
264 {
265    return ContainerObjectFind(ct, ewin);
266 }
267
268 static void
269 IconboxObjEwinAdd(Container * ct, EWin * ewin)
270 {
271    int                 i;
272
273    i = ContainerObjectAdd(ct, ewin);
274    if (i < 0)
275       return;
276
277    ct->objs[i].im = EwinIconImageGet(ewin, ct->iconsize, ct->icon_mode);
278    ContainerRedraw(ct);
279 }
280
281 static void
282 IconboxObjEwinDel(Container * ct, EWin * ewin)
283 {
284    int                 i;
285
286    i = IconboxObjEwinFind(ct, ewin);
287    if (i < 0)
288       return;
289
290    if (ct->objs[i].im)
291       EImageFree(ct->objs[i].im);
292
293    ContainerObjectDel(ct, ewin);
294 }
295
296 static void
297 IconboxesEwinIconify(EWin * ewin)
298 {
299    Container          *ct;
300
301    ct = SelectIconboxForEwin(ewin);
302    if (!ct)
303       return;
304
305    SoundPlay(SOUND_ICONIFY);
306
307    if (EoIsShown(ewin) && ct->anim_mode && !ewin->state.showingdesk)
308       IB_Animate(ct, 1, ewin);
309
310    IconboxObjEwinAdd(ct, ewin);
311 }
312
313 static void
314 IconboxesEwinDeIconify(EWin * ewin)
315 {
316    Container          *ct;
317
318    ct = SelectIconboxForEwin(ewin);
319    if (!ct)
320       return;
321
322    SoundPlay(SOUND_DEICONIFY);
323
324    if (ct->anim_mode && !ewin->state.showingdesk)
325       IB_Animate(ct, 0, ewin);
326
327    IconboxObjEwinDel(ct, ewin);
328    ContainerRedraw(ct);
329    EobjsRepaint();
330 }
331
332 static void
333 RemoveMiniIcon(EWin * ewin)
334 {
335    Container          *ct;
336
337    ct = SelectIconboxForEwin(ewin);
338    if (!ct)
339       return;
340
341    IconboxObjEwinDel(ct, ewin);
342    ContainerRedraw(ct);
343 }
344
345 static int
346 IconboxFindEwin(Container * ct, void *data)
347 {
348    EWin               *ewin = (EWin *) data;
349
350    return IconboxObjEwinFind(ct, ewin) >= 0;
351
352 }
353
354 static Container   *
355 SelectIconboxForEwin(EWin * ewin)
356 {
357    /* find the appropriate iconbox from all available ones for this app */
358    /* if it is to be iconified, or if it is alreayd return which iconbox */
359    /* it's in */
360    Container          *ct, *ib_sel = NULL;
361
362    if (!ewin)
363       return NULL;
364
365    if (ewin->state.iconified)
366      {
367         /* find the iconbox this window got iconifed into */
368         ib_sel = ContainersIterate(IconboxFindEwin, IB_TYPE_ICONBOX, ewin);
369      }
370    else
371      {
372         /* pick the closest iconbox physically on screen to put it in */
373         int                 min_dist;
374         int                 dx, dy, dist;
375         int                 i, num;
376         Container         **lst;
377
378         lst = ContainersGetList(&num);
379         min_dist = 0x7fffffff;
380         for (i = 0; i < num; i++)
381           {
382              ct = lst[i];
383              if (ct->ewin == NULL || ct->type != IB_TYPE_ICONBOX)
384                 continue;
385
386              dx = (EoGetX(ct->ewin) + (EoGetW(ct->ewin) / 2)) -
387                 (EoGetX(ewin) + (EoGetW(ewin) / 2));
388              dy = (EoGetY(ct->ewin) + (EoGetH(ct->ewin) / 2)) -
389                 (EoGetY(ewin) + (EoGetH(ewin) / 2));
390              dist = (dx * dx) + (dy * dy);
391              if ((!EoIsSticky(ct->ewin)) &&
392                  (EoGetDesk(ct->ewin) != EoGetDesk(ewin)))
393                 dist += (WinGetW(VROOT) * WinGetW(VROOT)) +
394                    (WinGetH(VROOT) * WinGetH(VROOT));
395              if (dist < min_dist)
396                {
397                   min_dist = dist;
398                   ib_sel = ct;
399                }
400           }
401         Efree(lst);
402      }
403
404    return ib_sel;
405 }
406
407 static void
408 IconboxUpdateEwinIcon(Container * ct, EWin * ewin, int icon_mode)
409 {
410    int                 i;
411
412    if (ct->icon_mode != icon_mode)
413       return;
414
415    i = IconboxObjEwinFind(ct, ewin);
416    if (i < 0)
417       return;
418
419    if (ct->objs[i].im)
420       EImageFree(ct->objs[i].im);
421    ct->objs[i].im = EwinIconImageGet(ewin, ct->iconsize, icon_mode);
422
423    ContainerRedraw(ct);
424 }
425
426 static void
427 IconboxesUpdateEwinIcon(EWin * ewin, int icon_mode)
428 {
429    int                 i, num;
430    Container         **lst, *ct;
431
432    lst = ContainersGetList(&num);
433    for (i = 0; i < num; i++)
434      {
435         ct = lst[i];
436         IconboxUpdateEwinIcon(ct, ewin, icon_mode);
437      }
438    Efree(lst);
439 }
440
441 static void
442 IconboxFindIconSize(EImage * im, int *pw, int *ph, int size)
443 {
444    int                 w, h, minsz, maxwh;
445
446    EImageGetSize(im, &w, &h);
447
448    maxwh = (w > h) ? w : h;
449    if (maxwh <= 1)
450       goto done;
451
452    minsz = (size * 3) / 4;
453
454    if (maxwh < minsz || maxwh > size)
455      {
456         w = (w * size) / maxwh;
457         h = (h * size) / maxwh;
458      }
459
460  done:
461    *pw = w;
462    *ph = h;
463 }
464
465 static void
466 IconboxInit(Container * ct)
467 {
468    ct->wm_name = "Iconbox";
469    ct->menu_title = _("Iconbox Options");
470    ct->dlg_title = _("Iconbox Settings");
471    ct->iconsize = 48;
472    ct->anim_mode = 1;
473 }
474
475 static void
476 IconboxExit(Container * ct, int wm_exit)
477 {
478    while (ct->num_objs)
479      {
480         if (!wm_exit)
481            EwinDeIconify((EWin *) ct->objs[0].obj);
482         IconboxObjEwinDel(ct, (EWin *) ct->objs[0].obj);
483      }
484 }
485
486 static void
487 IconboxSighan(Container * ct __UNUSED__, int sig, void *prm)
488 {
489    EWin               *ewin;
490
491    switch (sig)
492      {
493      case ESIGNAL_EWIN_ICONIFY:
494         ewin = (EWin *) prm;
495         IconboxesEwinIconify(ewin);
496         break;
497      case ESIGNAL_EWIN_DEICONIFY:
498         ewin = (EWin *) prm;
499         IconboxesEwinDeIconify(ewin);
500         break;
501      case ESIGNAL_EWIN_DESTROY:
502         ewin = (EWin *) prm;
503         if (ewin->state.iconified)
504            RemoveMiniIcon(ewin);
505         break;
506      case ESIGNAL_EWIN_CHANGE_ICON:
507         ewin = (EWin *) prm;
508         if (ewin->state.iconified)
509            IconboxesUpdateEwinIcon(ewin, 1);
510         break;
511      }
512 }
513
514 static void
515 IconboxEvent(Container * ct, XEvent * ev)
516 {
517    static EWin        *name_ewin = NULL;
518    ToolTip            *tt;
519    EWin               *ewin;
520    int                 x, y;
521    const char         *name;
522
523    switch (ev->type)
524      {
525      case ButtonPress:
526         if (ev->xbutton.button == 1)
527            ct->icon_clicked = 1;
528         break;
529
530      case ButtonRelease:
531         if (!ct->icon_clicked)
532            break;
533         ct->icon_clicked = 0;
534
535         ewin =
536            (EWin *) ContainerObjectFindByXY(ct, ev->xbutton.x, ev->xbutton.y);
537         if (!ewin)
538            break;
539
540         tt = TooltipFind("ICONBOX");
541         if (tt)
542            TooltipHide(tt);
543
544         EwinOpIconify(ewin, OPSRC_USER, 0);
545         break;
546
547      case MotionNotify:
548         x = ev->xmotion.x;
549         y = ev->xmotion.y;
550         goto do_motion;
551
552      case EnterNotify:
553         x = ev->xcrossing.x;
554         y = ev->xcrossing.y;
555         goto do_motion;
556
557       do_motion:
558         if (!ct->shownames)
559            break;
560
561         ewin = (EWin *) ContainerObjectFindByXY(ct, x, y);
562         if (ewin == name_ewin)
563            break;
564         name_ewin = ewin;
565
566         tt = TooltipFind("ICONBOX");
567         if (!tt)
568            break;
569
570         TooltipHide(tt);
571         if (!ewin)
572            break;
573
574         name = EwinGetIconName(ewin);
575         if (name)
576            TooltipShow(tt, name, NULL, Mode.events.cx, Mode.events.cy);
577         break;
578
579      case LeaveNotify:
580         tt = TooltipFind("ICONBOX");
581         if (tt)
582           {
583              TooltipHide(tt);
584              name_ewin = NULL;
585           }
586         break;
587      }
588 }
589
590 static void
591 IconboxObjSizeCalc(Container * ct, ContainerObject * cto)
592 {
593    /* Inner size */
594    cto->wi = cto->hi = 8;
595    if (cto->im)
596       IconboxFindIconSize(cto->im, &cto->wi, &cto->hi, ct->iconsize);
597 }
598
599 static void
600 IconboxObjPlace(Container * ct __UNUSED__, ContainerObject * cto, EImage * im)
601 {
602    int                 w, h;
603
604    if (!cto->im)
605       return;
606
607    EImageGetSize(cto->im, &w, &h);
608    EImageBlend(im, cto->im, EIMAGE_BLEND | EIMAGE_ANTI_ALIAS, 0, 0, w, h,
609                cto->xi, cto->yi, cto->wi, cto->hi, 1);
610 }
611
612 extern const ContainerOps IconboxOps;
613 const ContainerOps  IconboxOps = {
614    IconboxInit,
615    IconboxExit,
616    IconboxSighan,
617    IconboxEvent,
618    IconboxObjSizeCalc,
619    IconboxObjPlace,
620 };