e16/src/iconify.c

623 lines
14 KiB
C

/*
* Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
* Copyright (C) 2004-2014 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 "container.h"
#include "desktops.h"
#include "emodule.h"
#include "eobj.h"
#include "ewins.h"
#include "hints.h"
#include "icons.h"
#include "timers.h"
#include "tooltips.h"
#include "xwin.h"
#include <math.h>
#define M_PI_F ((float)(M_PI))
#define M_2PI_F ((float)(2 * M_PI))
static Container *SelectIconboxForEwin(EWin * ewin);
/* Silly hack to avoid name clash warning when using -Wshadow */
#define y1 y1_
#define IB_ANIM_TIME Conf_containers.anim_time
#define IB_ANIM_STEP Conf.animation.step
static void
IB_Animate_Sleep(unsigned int t0, float a)
{
unsigned int t;
float dt;
t = GetTimeMs() - t0;
dt = 1e-3 * (t - a * IB_ANIM_TIME);
dt = 1e-3 * IB_ANIM_STEP - dt;
if (dt > 0)
usleep((unsigned long)(1e6 * dt));
}
static void
IB_Animate_A(char iconify, EWin * ewin, EWin * ibox)
{
EWin *fr, *to;
unsigned int t0;
float a, aa, spd;
int x, y, x1, y1, x2, y2, x3, y3, x4, y4, w, h;
int fx, fy, fw, fh, tx, ty, tw, th;
EX_Window root = WinGetXwin(VROOT);
GC gc;
XGCValues gcv;
/* Window: Extents, Iconbox: Center */
if (iconify)
{
fr = ewin;
to = ibox;
fw = EoGetW(fr) + 4;
fh = EoGetH(fr) + 4;
fx = EoGetX(fr) - 2;
fy = EoGetY(fr) - 2;
tw = 4;
th = 4;
tx = EoGetX(to) + EoGetW(to) / 2 - 2;
ty = EoGetY(to) + EoGetH(to) / 2 - 2;
}
else
{
fr = ibox;
to = ewin;
fw = 4;
fh = 4;
fx = EoGetX(fr) + EoGetW(fr) / 2 - 2;
fy = EoGetY(fr) + EoGetH(fr) / 2 - 2;
tw = EoGetW(to) + 4;
th = EoGetH(to) + 4;
tx = EoGetX(to) + 2;
ty = EoGetY(to) + 2;
}
fx += EoGetX(EoGetDesk(fr));
fy += EoGetY(EoGetDesk(fr));
tx += EoGetX(EoGetDesk(to));
ty += EoGetY(EoGetDesk(to));
gcv.subwindow_mode = IncludeInferiors;
gcv.function = GXxor;
gcv.line_width = 2;
gcv.foreground = Dpy.pixel_white;
if (gcv.foreground == 0)
gcv.foreground = Dpy.pixel_black;
gc = EXCreateGC(root,
GCFunction | GCForeground | GCSubwindowMode | GCLineWidth,
&gcv);
spd = (1.f * IB_ANIM_STEP) / IB_ANIM_TIME;
t0 = GetTimeMs();
for (a = 0.f; a < 1.f; a += spd)
{
aa = 1.f - a;
x = (int)((fx * aa) + (tx * a));
y = (int)((fy * aa) + (ty * a));
w = (int)((fw * aa) + (tw * a));
h = (int)((fh * aa) + (th * a));
x = (2 * x + w) / 2; /* x middle */
y = (2 * y + h) / 2; /* y middle */
w /= 2; /* width/2 */
h /= 2; /* height/2 */
x1 = (int)(x + w * (1.f - .5f * sinf(M_PI_F + a * M_2PI_F)));
y1 = (int)(y + h * cosf(a * M_2PI_F));
x2 = (int)(x + w * (1.f - .5f * sinf(a * M_2PI_F)));
y2 = (int)(y - h * cosf(a * M_2PI_F));
x3 = (int)(x - w * (1.f - .5f * sinf(M_PI_F + a * M_2PI_F)));
y3 = (int)(y - h * cosf(a * M_2PI_F));
x4 = (int)(x - w * (1.f - .5f * sinf(a * M_2PI_F)));
y4 = (int)(y + h * cosf(a * M_2PI_F));
XDrawLine(disp, root, gc, x1, y1, x2, y2);
XDrawLine(disp, root, gc, x2, y2, x3, y3);
XDrawLine(disp, root, gc, x3, y3, x4, y4);
XDrawLine(disp, root, gc, x4, y4, x1, y1);
ESync(0);
IB_Animate_Sleep(t0, a);
XDrawLine(disp, root, gc, x1, y1, x2, y2);
XDrawLine(disp, root, gc, x2, y2, x3, y3);
XDrawLine(disp, root, gc, x3, y3, x4, y4);
XDrawLine(disp, root, gc, x4, y4, x1, y1);
}
EXFreeGC(gc);
}
static void
IB_Animate_B(char iconify, EWin * ewin, EWin * ibox)
{
EWin *fr, *to;
unsigned int t0;
float a, spd;
int x, y, w, h;
int fx, fy, fw, fh, tx, ty, tw, th;
EX_Window root = WinGetXwin(VROOT);
GC gc;
XGCValues gcv;
if (iconify)
{
fr = ewin;
to = ibox;
}
else
{
fr = ibox;
to = ewin;
}
fx = EoGetX(fr) - 2;
fy = EoGetY(fr) - 2;
fw = EoGetW(fr) + 3;
fh = EoGetH(fr) + 3;
tx = EoGetX(to) - 2;
ty = EoGetY(to) - 2;
tw = EoGetW(to) + 3;
th = EoGetH(to) + 3;
fx += EoGetX(EoGetDesk(fr));
fy += EoGetY(EoGetDesk(fr));
tx += EoGetX(EoGetDesk(to));
ty += EoGetY(EoGetDesk(to));
gcv.subwindow_mode = IncludeInferiors;
gcv.function = GXxor;
gcv.fill_style = FillOpaqueStippled;
gcv.foreground = Dpy.pixel_white;
if (gcv.foreground == 0)
gcv.foreground = Dpy.pixel_black;
gc = EXCreateGC(root,
GCFunction | GCForeground | GCSubwindowMode | GCFillStyle,
&gcv);
XDrawLine(disp, root, gc, fx, fy, tx, ty);
XDrawLine(disp, root, gc, fx + fw, fy, tx + tw, ty);
XDrawLine(disp, root, gc, fx, fy + fh, tx, ty + th);
XDrawLine(disp, root, gc, fx + fw, fy + fh, tx + tw, ty + th);
XDrawRectangle(disp, root, gc, tx, ty, tw, th);
XDrawRectangle(disp, root, gc, fx, fy, fw, fh);
spd = (1.f * IB_ANIM_STEP) / IB_ANIM_TIME;
t0 = GetTimeMs();
for (a = 0.f; a < 1.f; a += spd)
{
x = (int)(fx + a * (tx - fx));
w = (int)(fw + a * (tw - fw));
y = (int)(fy + a * (ty - fy));
h = (int)(fh + a * (th - fh));
XDrawRectangle(disp, root, gc, x, y, w, h);
ESync(0);
IB_Animate_Sleep(t0, a);
XDrawRectangle(disp, root, gc, x, y, w, h);
}
XDrawLine(disp, root, gc, fx, fy, tx, ty);
XDrawLine(disp, root, gc, fx + fw, fy, tx + tw, ty);
XDrawLine(disp, root, gc, fx, fy + fh, tx, ty + th);
XDrawLine(disp, root, gc, fx + fw, fy + fh, tx + tw, ty + th);
XDrawRectangle(disp, root, gc, tx, ty, tw, th);
XDrawRectangle(disp, root, gc, fx, fy, fw, fh);
EXFreeGC(gc);
}
static void
IB_Animate(Container * ct, int iconify, EWin * ewin)
{
if (Mode.wm.startup || ct->anim_mode <= 0)
return;
if (Conf_containers.anim_time < 10 || Conf_containers.anim_time > 10000)
Conf_containers.anim_time = 250;
EobjsRepaint();
EGrabServer();
switch (ct->anim_mode)
{
default:
break;
case 1:
IB_Animate_A(iconify, ewin, ct->ewin);
break;
case 2:
IB_Animate_B(iconify, ewin, ct->ewin);
break;
}
EUngrabServer();
}
static int
IconboxObjEwinFind(Container * ct, EWin * ewin)
{
return ContainerObjectFind(ct, ewin);
}
static void
IconboxObjEwinAdd(Container * ct, EWin * ewin)
{
int i;
i = ContainerObjectAdd(ct, ewin);
if (i < 0)
return;
ct->objs[i].im = EwinIconImageGet(ewin, ct->iconsize, ct->icon_mode);
ContainerRedraw(ct);
}
static void
IconboxObjEwinDel(Container * ct, EWin * ewin)
{
int i;
i = IconboxObjEwinFind(ct, ewin);
if (i < 0)
return;
if (ct->objs[i].im)
EImageFree(ct->objs[i].im);
ContainerObjectDel(ct, ewin);
}
static void
IconboxesEwinIconify(EWin * ewin)
{
Container *ct;
ct = SelectIconboxForEwin(ewin);
if (!ct)
return;
SoundPlay(SOUND_ICONIFY);
if (EoIsShown(ewin) && ct->anim_mode && !ewin->state.showingdesk)
IB_Animate(ct, 1, ewin);
IconboxObjEwinAdd(ct, ewin);
}
static void
IconboxesEwinDeIconify(EWin * ewin)
{
Container *ct;
ct = SelectIconboxForEwin(ewin);
if (!ct)
return;
SoundPlay(SOUND_DEICONIFY);
if (ct->anim_mode && !ewin->state.showingdesk)
IB_Animate(ct, 0, ewin);
IconboxObjEwinDel(ct, ewin);
ContainerRedraw(ct);
EobjsRepaint();
}
static void
RemoveMiniIcon(EWin * ewin)
{
Container *ct;
ct = SelectIconboxForEwin(ewin);
if (!ct)
return;
IconboxObjEwinDel(ct, ewin);
ContainerRedraw(ct);
}
static int
IconboxFindEwin(Container * ct, void *data)
{
EWin *ewin = (EWin *) data;
return IconboxObjEwinFind(ct, ewin) >= 0;
}
static Container *
SelectIconboxForEwin(EWin * ewin)
{
/* find the appropriate iconbox from all available ones for this app */
/* if it is to be iconified, or if it is alreayd return which iconbox */
/* it's in */
Container *ct, *ib_sel = NULL;
if (!ewin)
return NULL;
if (ewin->state.iconified)
{
/* find the iconbox this window got iconifed into */
ib_sel = ContainersIterate(IconboxFindEwin, IB_TYPE_ICONBOX, ewin);
}
else
{
/* pick the closest iconbox physically on screen to put it in */
int min_dist;
int dx, dy, dist;
int i, num;
Container **lst;
lst = ContainersGetList(&num);
min_dist = 0x7fffffff;
for (i = 0; i < num; i++)
{
ct = lst[i];
if (!ct->ewin || ct->type != IB_TYPE_ICONBOX)
continue;
dx = (EoGetX(ct->ewin) + (EoGetW(ct->ewin) / 2)) -
(EoGetX(ewin) + (EoGetW(ewin) / 2));
dy = (EoGetY(ct->ewin) + (EoGetH(ct->ewin) / 2)) -
(EoGetY(ewin) + (EoGetH(ewin) / 2));
dist = (dx * dx) + (dy * dy);
if ((!EoIsSticky(ct->ewin)) &&
(EoGetDesk(ct->ewin) != EoGetDesk(ewin)))
dist += (WinGetW(VROOT) * WinGetW(VROOT)) +
(WinGetH(VROOT) * WinGetH(VROOT));
if (dist < min_dist)
{
min_dist = dist;
ib_sel = ct;
}
}
Efree(lst);
}
return ib_sel;
}
static void
IconboxUpdateEwinIcon(Container * ct, EWin * ewin, int icon_mode)
{
int i;
if (ct->icon_mode != icon_mode)
return;
i = IconboxObjEwinFind(ct, ewin);
if (i < 0)
return;
if (ct->objs[i].im)
EImageFree(ct->objs[i].im);
ct->objs[i].im = EwinIconImageGet(ewin, ct->iconsize, icon_mode);
ContainerRedraw(ct);
}
static void
IconboxesUpdateEwinIcon(EWin * ewin, int icon_mode)
{
int i, num;
Container **lst, *ct;
lst = ContainersGetList(&num);
for (i = 0; i < num; i++)
{
ct = lst[i];
IconboxUpdateEwinIcon(ct, ewin, icon_mode);
}
Efree(lst);
}
static void
IconboxFindIconSize(EImage * im, int *pw, int *ph, int size)
{
int w, h, minsz, maxwh;
EImageGetSize(im, &w, &h);
maxwh = (w > h) ? w : h;
if (maxwh <= 1)
goto done;
minsz = (size * 3) / 4;
if (maxwh < minsz || maxwh > size)
{
w = (w * size) / maxwh;
h = (h * size) / maxwh;
}
done:
*pw = w;
*ph = h;
}
static void
IconboxInit(Container * ct)
{
ct->wm_name = "Iconbox";
ct->menu_title = _("Iconbox Options");
ct->dlg_title = _("Iconbox Settings");
ct->iconsize = 48;
ct->anim_mode = 1;
}
static void
IconboxExit(Container * ct, int wm_exit)
{
while (ct->num_objs)
{
if (!wm_exit)
EwinDeIconify((EWin *) ct->objs[0].obj);
IconboxObjEwinDel(ct, (EWin *) ct->objs[0].obj);
}
}
static void
IconboxSighan(Container * ct __UNUSED__, int sig, void *prm)
{
EWin *ewin = (EWin *) prm;
switch (sig)
{
case ESIGNAL_EWIN_ICONIFY:
IconboxesEwinIconify(ewin);
break;
case ESIGNAL_EWIN_DEICONIFY:
IconboxesEwinDeIconify(ewin);
break;
case ESIGNAL_EWIN_DESTROY:
if (ewin->state.iconified)
RemoveMiniIcon(ewin);
break;
case ESIGNAL_EWIN_CHANGE_ICON:
if (ewin->state.iconified)
IconboxesUpdateEwinIcon(ewin, 1);
break;
}
}
static void
IconboxEvent(Container * ct, XEvent * ev)
{
static EWin *name_ewin = NULL;
ToolTip *tt;
EWin *ewin;
int x, y;
const char *name;
switch (ev->type)
{
case ButtonPress:
if (ev->xbutton.button == 1)
ct->icon_clicked = 1;
break;
case ButtonRelease:
if (!ct->icon_clicked)
break;
ct->icon_clicked = 0;
ewin =
(EWin *) ContainerObjectFindByXY(ct, ev->xbutton.x, ev->xbutton.y);
if (!ewin)
break;
tt = TooltipFind("ICONBOX");
if (tt)
TooltipHide(tt);
EwinOpIconify(ewin, OPSRC_USER, 0);
break;
case MotionNotify:
x = ev->xmotion.x;
y = ev->xmotion.y;
goto do_motion;
case EnterNotify:
x = ev->xcrossing.x;
y = ev->xcrossing.y;
goto do_motion;
do_motion:
if (!ct->shownames)
break;
ewin = (EWin *) ContainerObjectFindByXY(ct, x, y);
if (ewin == name_ewin)
break;
name_ewin = ewin;
tt = TooltipFind("ICONBOX");
if (!tt)
break;
TooltipHide(tt);
if (!ewin)
break;
name = EwinGetTitle(ewin);
if (name)
TooltipShow(tt, name, NULL, Mode.events.cx, Mode.events.cy);
break;
case LeaveNotify:
tt = TooltipFind("ICONBOX");
if (tt)
{
TooltipHide(tt);
name_ewin = NULL;
}
break;
}
}
static void
IconboxObjSizeCalc(Container * ct, ContainerObject * cto)
{
/* Inner size */
cto->wi = cto->hi = 8;
if (cto->im)
IconboxFindIconSize(cto->im, &cto->wi, &cto->hi, ct->iconsize);
}
static void
IconboxObjPlace(Container * ct __UNUSED__, ContainerObject * cto, EImage * im)
{
int w, h;
if (!cto->im)
return;
EImageGetSize(cto->im, &w, &h);
EImageBlend(im, cto->im, EIMAGE_BLEND | EIMAGE_ANTI_ALIAS, 0, 0, w, h,
cto->xi, cto->yi, cto->wi, cto->hi, 1);
}
extern const ContainerOps IconboxOps;
const ContainerOps IconboxOps = {
IconboxInit,
IconboxExit,
IconboxSighan,
IconboxEvent,
IconboxObjSizeCalc,
IconboxObjPlace,
};