Propagate Layer changes via Style command on-the-fly.
[fvwm.git] / libs / FTips.c
blob5490e8fe5126f842b58165292c259159f5627266
1 /* -*-c-*- */
2 /* Copyright (C) 2004 Olivier Chapuis */
3 /* This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 /* ---------------------------- included header files ---------------------- */
20 #include "config.h"
22 #include <stdio.h>
23 #include <unistd.h>
24 #include "libs/ftime.h"
26 #include <X11/Xlib.h>
27 #include <X11/Xatom.h>
29 #include "defaults.h"
30 #include "fvwmlib.h"
31 #include "Strings.h"
32 #include "PictureBase.h"
33 #include "Flocale.h"
34 #include "Graphics.h"
35 #include "Colorset.h"
36 #include "libs/FScreen.h"
37 #include "FTips.h"
39 /* ---------------------------- local definitions -------------------------- */
41 #define FVWM_TIPS_NOTHING 0
42 #define FVWM_TIPS_WAITING 1
43 #define FVWM_TIPS_MAPPED 2
45 /* ---------------------------- local macros ------------------------------- */
47 /* ---------------------------- imports ------------------------------------ */
49 /* ---------------------------- included code files ------------------------ */
51 /* ---------------------------- local types -------------------------------- */
53 /* ---------------------------- forward declarations ----------------------- */
55 /* ---------------------------- local variables ---------------------------- */
57 static Window win = None;
58 static GC gc = None;
59 static Window win_for;
61 static ftips_config *current_config, *default_config;
63 static char *label;
64 static int state = FVWM_TIPS_NOTHING;
65 static void *boxID = NULL;
67 static FlocaleWinString fwin_string;
68 static rectangle box;
70 static unsigned long timeOut;
71 static unsigned long onTime;
73 static Atom _net_um_for = None;
75 /* ---------------------------- exported variables (globals) --------------- */
77 /* ---------------------------- local functions ---------------------------- */
79 static
80 unsigned long __get_time(void)
82 struct timeval t;
84 gettimeofday(&t, NULL);
85 return (1000*t.tv_sec + t.tv_usec/1000);
88 static
89 void __initialize_window(Display *dpy)
91 XGCValues xgcv;
92 unsigned long valuemask;
93 XSetWindowAttributes attributes;
94 Atom _net_um_window_type;
95 long _net_um_window_type_tooltips;
97 valuemask = CWOverrideRedirect | CWEventMask | CWColormap;
98 attributes.override_redirect = True;
99 attributes.event_mask = ExposureMask;
100 attributes.colormap = Pcmap;
101 win = XCreateWindow(
102 dpy, DefaultRootWindow(dpy), 0, 0, 1, 1,
103 current_config->border_width, Pdepth, InputOutput, Pvisual,
104 valuemask, &attributes);
106 _net_um_window_type = XInternAtom(
107 dpy, "_NET_UM_WINDOW_TYPE", False);
108 _net_um_window_type_tooltips = XInternAtom(
109 dpy, "_NET_UM_WINDOW_TYPE_TOOLTIPS", False);
111 XChangeProperty(
112 dpy, win, _net_um_window_type, XA_ATOM, 32, PropModeReplace,
113 (unsigned char *) &_net_um_window_type_tooltips, 1);
115 _net_um_for = XInternAtom(dpy, "_NET_UM_FOR", False);
117 gc = fvwmlib_XCreateGC(dpy, win, 0, &xgcv);
118 return;
121 static
122 void __setup_cs(Display *dpy)
124 XSetWindowAttributes xswa;
125 unsigned long valuemask = 0;
128 if (current_config->colorset > -1)
130 xswa.border_pixel = Colorset[current_config->colorset].fg;
131 xswa.background_pixel = Colorset[current_config->colorset].bg;
132 if (Colorset[current_config->colorset].pixmap)
134 /* set later */
135 xswa.background_pixmap = None;
136 valuemask = CWBackPixmap | CWBorderPixel;;
138 else
140 valuemask = CWBackPixel | CWBorderPixel;
143 else
145 xswa.border_pixel = current_config->border_pixel;
146 xswa.background_pixel = current_config->bg;
147 valuemask = CWBackPixel | CWBorderPixel;
149 XChangeWindowAttributes(dpy, win, valuemask, &xswa);
152 static
153 void __setup_gc(Display *dpy)
155 XGCValues xgcv;
156 unsigned long valuemask;
158 valuemask = GCForeground;
159 if (current_config->colorset > -1)
161 xgcv.foreground = Colorset[current_config->colorset].fg;
163 else
165 xgcv.foreground = current_config->fg;
167 if (current_config->Ffont && current_config->Ffont->font != NULL)
169 xgcv.font = current_config->Ffont->font->fid;
170 valuemask |= GCFont;
172 XChangeGC(dpy, gc, valuemask, &xgcv);
174 return;
177 static
178 void __draw(Display *dpy)
180 if (!current_config->Ffont)
182 return;
185 fwin_string.str = label;
186 fwin_string.win = win;
187 fwin_string.gc = gc;
188 if (current_config->colorset > -1)
190 fwin_string.colorset = &Colorset[current_config->colorset];
191 fwin_string.flags.has_colorset = True;
193 else
195 fwin_string.flags.has_colorset = False;
197 fwin_string.x = 2;
198 fwin_string.y = current_config->Ffont->ascent;
199 FlocaleDrawString(dpy, current_config->Ffont, &fwin_string, 0);
201 return;
204 static
205 void __map_window(Display *dpy)
207 rectangle new_g;
208 rectangle screen_g;
209 Window dummy;
210 fscreen_scr_arg *fsarg = NULL; /* for now no xinerama support */
211 ftips_placement_t placement;
212 int x,y;
213 static int border_width = 1;
214 static Window win_f = None;
216 if (border_width != current_config->border_width)
218 XSetWindowBorderWidth(dpy, win, current_config->border_width);
219 border_width = current_config->border_width;
222 FScreenGetScrRect(
223 fsarg, FSCREEN_GLOBAL, &screen_g.x, &screen_g.y,
224 &screen_g.width, &screen_g.height);
226 new_g.height = ((current_config->Ffont)? current_config->Ffont->height:0)
227 + 1;
228 new_g.width = 4;
230 if (label && current_config->Ffont)
232 new_g.width += FlocaleTextWidth(
233 current_config->Ffont, label, strlen(label));
236 if (current_config->placement != FTIPS_PLACEMENT_AUTO_UPDOWN &&
237 current_config->placement != FTIPS_PLACEMENT_AUTO_LEFTRIGHT)
239 placement = current_config->placement;
241 else
243 XTranslateCoordinates(
244 dpy, win_for, DefaultRootWindow(dpy), box.x, box.y,
245 &x, &y, &dummy);
246 if (current_config->placement == FTIPS_PLACEMENT_AUTO_UPDOWN)
248 if (y + box.height/2 >= screen_g.height/2)
250 placement = FTIPS_PLACEMENT_UP;
252 else
254 placement = FTIPS_PLACEMENT_DOWN;
257 else
259 if (x + box.width/2 >= screen_g.width/2)
261 placement = FTIPS_PLACEMENT_LEFT;
263 else
265 placement = FTIPS_PLACEMENT_RIGHT;
270 if (placement == FTIPS_PLACEMENT_RIGHT ||
271 placement == FTIPS_PLACEMENT_LEFT)
273 if (current_config->justification == FTIPS_JUSTIFICATION_CENTER)
275 y = box.y + (box.height / 2) - (new_g.height / 2) -
276 current_config->border_width;
278 else if (current_config->justification ==
279 FTIPS_JUSTIFICATION_RIGHT_DOWN)
281 y = box.y + box.height - new_g.height -
282 (2 * current_config->border_width) -
283 current_config->justification_offset;
285 else /* LEFT_UP */
287 y = box.y + current_config->justification_offset;
291 else /* placement == FTIPS_PLACEMENT_DOWN ||
292 placement == FTIPS_PLACEMENT_UP */
294 if (current_config->justification == FTIPS_JUSTIFICATION_CENTER)
296 x = box.x + (box.width / 2) - (new_g.width / 2) -
297 current_config->border_width;
299 else if (current_config->justification ==
300 FTIPS_JUSTIFICATION_RIGHT_DOWN)
302 x = box.x + box.width - new_g.width -
303 (2 * current_config->border_width) -
304 current_config->justification_offset;
306 else /* LEFT_UP */
308 x = box.x + current_config->justification_offset;
312 if (placement == FTIPS_PLACEMENT_RIGHT)
314 x = box.x + box.width + current_config->placement_offset + 1;
316 else if (placement == FTIPS_PLACEMENT_LEFT)
318 x = box.x - current_config->placement_offset - new_g.width
319 - (2 * current_config->border_width) - 1;
321 else if (placement == FTIPS_PLACEMENT_DOWN)
323 y = box.y + box.height + current_config->placement_offset - 0;
325 else /* UP */
327 y = box.y - current_config->placement_offset - new_g.height
328 + 0 - (2 * current_config->border_width);
331 XTranslateCoordinates(
332 dpy, win_for, DefaultRootWindow(dpy), x, y,
333 &new_g.x, &new_g.y, &dummy);
335 if (placement == FTIPS_PLACEMENT_RIGHT ||
336 placement == FTIPS_PLACEMENT_LEFT)
338 int x1,y1,l1,l2;
340 if (new_g.x < 2)
342 x = box.x + box.width + current_config->placement_offset
343 + 1;
344 XTranslateCoordinates(
345 dpy, win_for, DefaultRootWindow(dpy), x, y,
346 &x1, &y1, &dummy);
347 /* */
348 l1 = new_g.width + new_g.x - 2;
349 l2 = screen_g.width - (x1 + new_g.width) -
350 current_config->border_width - 2;
351 if (l2 > l1)
353 new_g.x = x1;
356 else if (new_g.x + new_g.width >
357 screen_g.width - (2 * current_config->border_width) - 2)
359 x = box.x - current_config->placement_offset - new_g.width
360 - (2 * current_config->border_width) - 1;
361 XTranslateCoordinates(
362 dpy, win_for, DefaultRootWindow(dpy), x, y,
363 &x1, &y1, &dummy);
364 /* */
365 l1 = new_g.width + x1 - 2;
366 l2 = screen_g.width - (new_g.x + new_g.width) -
367 (2 * current_config->border_width) - 2;
368 if (l1 > l2)
370 new_g.x = x1;
373 if ( new_g.y < 2 )
375 new_g.y = 2;
377 else if (new_g.y + new_g.height >
378 screen_g.height - (2 * current_config->border_width) - 2)
380 new_g.y = screen_g.height - new_g.height -
381 (2 * current_config->border_width) - 2;
384 else /* placement == FTIPS_PLACEMENT_DOWN ||
385 placement == FTIPS_PLACEMENT_UP */
387 if (new_g.y < 2)
389 y = box.y + box.height +
390 current_config->placement_offset - 0;
391 XTranslateCoordinates(
392 dpy, win_for, DefaultRootWindow(dpy),
393 x, y, &new_g.x, &new_g.y, &dummy);
395 else if (new_g.y + new_g.height >
396 screen_g.height - (2 * current_config->border_width) - 2)
398 y = box.y - current_config->placement_offset -
399 new_g.height + 0
400 - (2 * current_config->border_width);
401 XTranslateCoordinates(
402 dpy, win_for, DefaultRootWindow(dpy),
403 x, y, &new_g.x, &new_g.y, &dummy);
405 if ( new_g.x < 2 )
407 new_g.x = 2;
409 else if (new_g.x + new_g.width >
410 screen_g.width - (2 * current_config->border_width) - 2)
412 new_g.x = screen_g.width - new_g.width -
413 (2 * current_config->border_width) - 2;
417 /* make changes to window */
418 XMoveResizeWindow(
419 dpy, win, new_g.x, new_g.y, new_g.width, new_g.height);
420 __setup_gc(dpy);
421 if (current_config->colorset > -1)
423 SetWindowBackground(
424 dpy, win, new_g.width, new_g.height,
425 &Colorset[current_config->colorset], Pdepth, gc, True);
427 else
429 XSetWindowBackground (dpy, win, current_config->bg);
431 if (current_config->border_width > 0)
433 XSetWindowBorder(
434 dpy, win, Colorset[current_config->colorset].fg);
437 if (state != FVWM_TIPS_MAPPED && win_f != win_for)
439 long l_win_for;
441 l_win_for = win_for;
442 XChangeProperty(
443 dpy, win, _net_um_for, XA_WINDOW, 32, PropModeReplace,
444 (unsigned char *) &l_win_for, 1);
445 win_f = win_for;
447 XMapRaised(dpy, win);
448 state = FVWM_TIPS_MAPPED;
450 return;
453 /* ---------------------------- interface functions ------------------------ */
455 Bool FTipsInit(Display *dpy)
458 current_config = default_config = FTipsNewConfig();
459 __initialize_window(dpy);
461 if (gc == None || win == None)
463 return False;
466 __setup_cs(dpy);
467 __setup_gc(dpy);
469 memset(&fwin_string, 0, sizeof(fwin_string));
471 return True;
474 ftips_config *FTipsNewConfig(void)
476 ftips_config *fc;
478 fc = (ftips_config *)safemalloc(sizeof(ftips_config));
480 memset(fc, 0, sizeof(ftips_config));
482 /* use colorset 0 as default */
483 fc->border_width = FTIPS_DEFAULT_BORDER_WIDTH;
484 fc->placement = FTIPS_DEFAULT_PLACEMENT;
485 fc->justification = FTIPS_DEFAULT_JUSTIFICATION;
486 fc->placement_offset = FTIPS_DEFAULT_PLACEMENT_OFFSET;
487 fc->justification_offset = FTIPS_DEFAULT_JUSTIFICATION_OFFSET;
488 fc->delay = 1000;
489 fc->mapped_delay = 300;
491 return fc;
494 void FTipsOn(
495 Display *dpy, Window win_f, ftips_config *fc, void *id, char *str,
496 int x, int y, int w, int h)
498 unsigned long delay;
500 box.x = x;
501 box.y = y;
502 box.width = w;
503 box.height = h;
505 if (fc == NULL)
507 fc = default_config;
509 current_config = fc;
511 if (label != NULL)
513 free(label);
515 CopyString(&label, str);
516 win_for = win_f;
518 if (id == boxID)
520 if (id && state == FVWM_TIPS_WAITING)
522 FTipsCheck(dpy);
524 return;
526 onTime = __get_time();
527 if (state == FVWM_TIPS_MAPPED)
529 FTipsCancel(dpy);
530 delay = fc->mapped_delay;
532 else
534 delay = fc->delay;
536 boxID = id;
537 timeOut = onTime + delay;
538 state = FVWM_TIPS_WAITING;
539 if (delay == 0)
541 FTipsCheck(dpy);
544 return;
547 void FTipsCancel(Display *dpy)
549 if (state == FVWM_TIPS_MAPPED && win != None)
551 XUnmapWindow(dpy, win);
553 boxID = 0;
554 state = FVWM_TIPS_NOTHING;
557 unsigned long FTipsCheck(Display *dpy)
559 unsigned long ct;
561 if (state != FVWM_TIPS_WAITING || win == None)
563 return 0;
566 ct = __get_time();
568 if (ct >= timeOut)
570 __map_window(dpy);
571 XFlush(dpy);
572 state = FVWM_TIPS_MAPPED;
573 return 0;
575 else
577 XFlush(dpy);
578 return timeOut - ct;
582 Bool FTipsExpose(Display *dpy, XEvent *ev)
584 int ex,ey,ex2,ey2;
586 if (win == None || ev->xany.window != win)
588 return False;
590 ex = ev->xexpose.x;
591 ey = ev->xexpose.y;
592 ex2 = ev->xexpose.x + ev->xexpose.width;
593 ey2= ev->xexpose.y + ev->xexpose.height;
595 while (FCheckTypedWindowEvent(dpy, ev->xany.window, Expose, ev))
597 ex = min(ex, ev->xexpose.x);
598 ey = min(ey, ev->xexpose.y);
599 ex2 = max(ex2, ev->xexpose.x + ev->xexpose.width);
600 ey2= max(ey2 , ev->xexpose.y + ev->xexpose.height);
602 #if 0
603 fprintf (
604 stderr, "\tExpose: %i,%i,%i,%i %i\n",
605 ex, ey, ex2-ex, ey2-ey, ev->xexpose.count);
606 #endif
607 __draw(dpy);
609 return True;
612 Bool FTipsHandleEvents(Display *dpy, XEvent *ev)
614 if (ev->xany.window != win)
616 return False;
619 switch(ev->type)
621 case Expose:
622 FTipsExpose(dpy, ev);
623 break;
624 default:
625 break;
627 return True;
630 void FTipsUpdateLabel(Display *dpy, char *str)
632 if (state != FVWM_TIPS_MAPPED && state != FVWM_TIPS_WAITING)
634 return;
637 if (label)
639 free(label);
641 CopyString(&label, str);
642 if (state == FVWM_TIPS_MAPPED)
644 __map_window(dpy);
645 __draw(dpy);
649 void FTipsColorsetChanged(Display *dpy, int cs)
651 if (state != FVWM_TIPS_MAPPED || cs != current_config->colorset)
653 return;
655 __map_window(dpy);
656 __draw(dpy);