- Fixed crashing bug in menu.c
[wmaker-crm.git] / src / balloon.c
blob2ba090771ae039e8a9b950c38c8f2beea0e13b96
1 /*
2 * Window Maker window manager
3 *
4 * Copyright (c) 1998-2003 Alfredo K. Kojima
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 * USA.
22 #include "wconfig.h"
24 #ifdef BALLOON_TEXT
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
28 #ifdef SHAPED_BALLOON
29 #include <X11/extensions/shape.h>
30 #endif
32 #include <stdlib.h>
33 #include <string.h>
35 #include <wraster.h>
37 #include "WindowMaker.h"
38 #include "screen.h"
39 #include "texture.h"
40 #include "wcore.h"
41 #include "framewin.h"
42 #include "icon.h"
43 #include "appicon.h"
44 #include "funcs.h"
45 #include "workspace.h"
46 #include "balloon.h"
50 extern WPreferences wPreferences;
52 typedef struct _WBalloon {
53 Window window;
55 #ifdef SHAPED_BALLOON
56 GC monoGC;
57 #endif
58 int prevType;
60 Window objectWindow;
61 char *text;
62 int h;
64 WMHandlerID timer;
66 Pixmap contents;
68 char mapped;
69 char ignoreTimer;
70 } WBalloon;
73 #define TOP 0
74 #define BOTTOM 1
75 #define LEFT 0
76 #define RIGHT 2
78 #define TLEFT (TOP|LEFT)
79 #define TRIGHT (TOP|RIGHT)
80 #define BLEFT (BOTTOM|LEFT)
81 #define BRIGHT (BOTTOM|RIGHT)
84 #ifdef SHAPED_BALLOON
86 #define SPACE 12
89 static void
90 drawBalloon(WScreen *scr, Pixmap bitmap, Pixmap pix, int x, int y, int w,
91 int h, int side)
93 GC bgc = scr->balloon->monoGC;
94 GC gc = scr->draw_gc;
95 int rad = h*3/10;
96 XPoint pt[3], ipt[3];
97 int w1;
99 /* outline */
100 XSetForeground(dpy, bgc, 1);
102 XFillArc(dpy, bitmap, bgc, x, y, rad, rad, 90*64, 90*64);
103 XFillArc(dpy, bitmap, bgc, x, y+h-1-rad, rad, rad, 180*64, 90*64);
105 XFillArc(dpy, bitmap, bgc, x+w-1-rad, y, rad, rad, 0*64, 90*64);
106 XFillArc(dpy, bitmap, bgc, x+w-1-rad, y+h-1-rad, rad, rad, 270*64, 90*64);
108 XFillRectangle(dpy, bitmap, bgc, x, y+rad/2, w, h-rad);
109 XFillRectangle(dpy, bitmap, bgc, x+rad/2, y, w-rad, h);
111 /* interior */
112 XSetForeground(dpy, gc, scr->white_pixel);
114 XFillArc(dpy, pix, gc, x+1, y+1, rad, rad, 90*64, 90*64);
115 XFillArc(dpy, pix, gc, x+1, y+h-2-rad, rad, rad, 180*64, 90*64);
117 XFillArc(dpy, pix, gc, x+w-2-rad, y+1, rad, rad, 0*64, 90*64);
118 XFillArc(dpy, pix, gc, x+w-2-rad, y+h-2-rad, rad, rad, 270*64, 90*64);
120 XFillRectangle(dpy, pix, gc, x+1, y+1+rad/2, w-2, h-2-rad);
121 XFillRectangle(dpy, pix, gc, x+1+rad/2, y+1, w-2-rad, h-2);
123 if (side & BOTTOM) {
124 pt[0].y = y+h-1;
125 pt[1].y = y+h-1+SPACE;
126 pt[2].y = y+h-1;
127 ipt[0].y = pt[0].y-1;
128 ipt[1].y = pt[1].y-1;
129 ipt[2].y = pt[2].y-1;
130 } else {
131 pt[0].y = y;
132 pt[1].y = y-SPACE;
133 pt[2].y = y;
134 ipt[0].y = pt[0].y+1;
135 ipt[1].y = pt[1].y+1;
136 ipt[2].y = pt[2].y+1;
139 /*w1 = WMAX(h, 24);*/
140 w1 = WMAX(h, 21);
142 if (side & RIGHT) {
143 pt[0].x = x+w-w1+2*w1/16;
144 pt[1].x = x+w-w1+11*w1/16;
145 pt[2].x = x+w-w1+7*w1/16;
146 ipt[0].x = x+1+w-w1+2*(w1-1)/16;
147 ipt[1].x = x+1+w-w1+11*(w1-1)/16;
148 ipt[2].x = x+1+w-w1+7*(w1-1)/16;
149 /*ipt[0].x = pt[0].x+1;
150 ipt[1].x = pt[1].x;
151 ipt[2].x = pt[2].x;*/
152 } else {
153 pt[0].x = x+w1-2*w1/16;
154 pt[1].x = x+w1-11*w1/16;
155 pt[2].x = x+w1-7*w1/16;
156 ipt[0].x = x-1+w1-2*(w1-1)/16;
157 ipt[1].x = x-1+w1-11*(w1-1)/16;
158 ipt[2].x = x-1+w1-7*(w1-1)/16;
159 /*ipt[0].x = pt[0].x-1;
160 ipt[1].x = pt[1].x;
161 ipt[2].x = pt[2].x;*/
164 XFillPolygon(dpy, bitmap, bgc, pt, 3, Convex, CoordModeOrigin);
165 XFillPolygon(dpy, pix, gc, ipt, 3, Convex, CoordModeOrigin);
167 /* fix outline */
168 XSetForeground(dpy, gc, scr->black_pixel);
170 XDrawLines(dpy, pix, gc, pt, 3, CoordModeOrigin);
171 if (side & RIGHT) {
172 pt[0].x++;
173 pt[2].x--;
174 } else {
175 pt[0].x--;
176 pt[2].x++;
178 XDrawLines(dpy, pix, gc, pt, 3, CoordModeOrigin);
182 static Pixmap
183 makePixmap(WScreen *scr, int width, int height, int side, Pixmap *mask)
185 WBalloon *bal = scr->balloon;
186 Pixmap bitmap;
187 Pixmap pixmap;
188 int x, y;
190 bitmap = XCreatePixmap(dpy, scr->root_win, width+SPACE, height+SPACE, 1);
192 if (!bal->monoGC) {
193 bal->monoGC = XCreateGC(dpy, bitmap, 0, NULL);
195 XSetForeground(dpy, bal->monoGC, 0);
196 XFillRectangle(dpy, bitmap, bal->monoGC, 0, 0, width+SPACE, height+SPACE);
198 pixmap = XCreatePixmap(dpy, scr->root_win, width+SPACE, height+SPACE,
199 scr->w_depth);
200 XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
201 XFillRectangle(dpy, pixmap, scr->draw_gc, 0, 0, width+SPACE, height+SPACE);
203 if (side & BOTTOM) {
204 y = 0;
205 } else {
206 y = SPACE;
208 x = 0;
210 drawBalloon(scr, bitmap, pixmap, x, y, width, height, side);
212 *mask = bitmap;
214 return pixmap;
218 static void
219 showText(WScreen *scr, int x, int y, int h, int w, char *text)
221 int width;
222 int height;
223 Pixmap pixmap;
224 Pixmap mask;
225 WMFont *font = scr->info_text_font;
226 int side = 0;
227 int ty;
228 int bx, by;
230 if (scr->balloon->contents)
231 XFreePixmap(dpy, scr->balloon->contents);
233 width = WMWidthOfString(font, text, strlen(text))+16;
234 height = WMFontHeight(font) + 4;
236 if (height < 16)
237 height = 16;
238 if (width < height)
239 width = height;
242 if (x + width > scr->scr_width) {
243 side = RIGHT;
244 bx = x - width + w/2;
245 if (bx < 0)
246 bx = 0;
247 } else {
248 side = LEFT;
249 bx = x + w/2;
251 if (bx + width > scr->scr_width)
252 bx = scr->scr_width - width;
254 if (y - (height + SPACE) < 0) {
255 side |= TOP;
256 by = y+h-1;
257 ty = SPACE;
258 } else {
259 side |= BOTTOM;
260 by = y - (height + SPACE);
261 ty = 0;
263 pixmap = makePixmap(scr, width, height, side, &mask);
265 WMDrawString(scr->wmscreen, pixmap, scr->black, font, 8,
266 ty + (height - WMFontHeight(font))/2,
267 text, strlen(text));
269 XSetWindowBackgroundPixmap(dpy, scr->balloon->window, pixmap);
270 scr->balloon->contents = pixmap;
272 XResizeWindow(dpy, scr->balloon->window, width, height+SPACE);
273 XShapeCombineMask(dpy, scr->balloon->window, ShapeBounding, 0, 0, mask,
274 ShapeSet);
275 XFreePixmap(dpy, mask);
276 XMoveWindow(dpy, scr->balloon->window, bx, by);
277 XMapRaised(dpy, scr->balloon->window);
280 scr->balloon->mapped = 1;
282 #else /* !SHAPED_BALLOON */
283 static void
284 showText(WScreen *scr, int x, int y, int h, int w, char *text)
286 int width;
287 int height;
288 Pixmap pixmap;
289 WMFont *font = scr->info_text_font;
291 if (scr->balloon->contents)
292 XFreePixmap(dpy, scr->balloon->contents);
294 width = WMWidthOfString(font, text, strlen(text))+8;
295 height = WMFontHeight(font) + 4;
297 if (x < 0)
298 x = 0;
299 else if (x + width > scr->scr_width-1)
300 x = scr->scr_width - width;
302 if (y - height - 2 < 0) {
303 y += h;
304 if (y < 0)
305 y = 0;
306 } else {
307 y -= height + 2;
310 if (scr->window_title_texture[0])
311 XSetForeground(dpy, scr->draw_gc,
312 scr->window_title_texture[0]->any.color.pixel);
313 else
314 XSetForeground(dpy, scr->draw_gc, scr->light_pixel);
316 pixmap = XCreatePixmap(dpy, scr->root_win, width, height, scr->w_depth);
317 XFillRectangle(dpy, pixmap, scr->draw_gc, 0, 0, width, height);
319 WMDrawString(scr->wmscreen, pixmap, scr->window_title_color[0], font, 4, 2,
320 text, strlen(text));
322 XResizeWindow(dpy, scr->balloon->window, width, height);
323 XMoveWindow(dpy, scr->balloon->window, x, y);
325 XSetWindowBackgroundPixmap(dpy, scr->balloon->window, pixmap);
326 XClearWindow(dpy, scr->balloon->window);
327 XMapRaised(dpy, scr->balloon->window);
329 scr->balloon->contents = pixmap;
331 scr->balloon->mapped = 1;
333 #endif /* !SHAPED_BALLOON */
336 static void
337 showBalloon(WScreen *scr)
339 int x, y;
340 Window foow;
341 unsigned foo, w;
343 if (scr->balloon) {
344 scr->balloon->timer = NULL;
345 scr->balloon->ignoreTimer = 1;
348 if (!XGetGeometry(dpy, scr->balloon->objectWindow, &foow, &x, &y,
349 &w, &foo, &foo, &foo)) {
350 scr->balloon->prevType = 0;
351 return;
353 showText(scr, x, y, scr->balloon->h, w, scr->balloon->text);
358 static void
359 frameBalloon(WObjDescriptor *object)
361 WFrameWindow *fwin = (WFrameWindow*)object->parent;
362 WScreen *scr = fwin->core->screen_ptr;
364 if (fwin->titlebar != object->self
365 || !fwin->flags.is_client_window_frame) {
366 wBalloonHide(scr);
367 return;
369 if (fwin->title && fwin->flags.incomplete_title) {
370 scr->balloon->h = (fwin->titlebar ? fwin->titlebar->height : 0);
371 scr->balloon->text = wstrdup(fwin->title);
372 scr->balloon->objectWindow = fwin->core->window;
373 scr->balloon->timer = WMAddTimerHandler(BALLOON_DELAY,
374 (WMCallback*)showBalloon, scr);
379 static void
380 miniwindowBalloon(WObjDescriptor *object)
382 WIcon *icon = (WIcon*)object->parent;
383 WScreen *scr = icon->core->screen_ptr;
385 if (!icon->icon_name) {
386 wBalloonHide(scr);
387 return;
389 scr->balloon->h = icon->core->height;
390 scr->balloon->text = wstrdup(icon->icon_name);
391 scr->balloon->objectWindow = icon->core->window;
392 if ((scr->balloon->prevType == object->parent_type
393 || scr->balloon->prevType == WCLASS_APPICON)
394 && scr->balloon->ignoreTimer) {
395 XUnmapWindow(dpy, scr->balloon->window);
396 showBalloon(scr);
397 } else {
398 scr->balloon->timer = WMAddTimerHandler(BALLOON_DELAY,
399 (WMCallback*)showBalloon, scr);
405 static void
406 appiconBalloon(WObjDescriptor *object)
408 WAppIcon *aicon = (WAppIcon*)object->parent;
409 WScreen *scr = aicon->icon->core->screen_ptr;
410 char *tmp;
412 if (aicon->command && aicon->wm_class) {
413 int len = strlen(aicon->command)+strlen(aicon->wm_class)+8;
414 tmp = wmalloc(len);
415 snprintf(tmp, len, "%s (%s)", aicon->wm_class, aicon->command);
416 scr->balloon->text = tmp;
417 } else if (aicon->command) {
418 scr->balloon->text = wstrdup(aicon->command);
419 } else if (aicon->wm_class) {
420 scr->balloon->text = wstrdup(aicon->wm_class);
421 } else {
422 wBalloonHide(scr);
423 return;
425 scr->balloon->h = aicon->icon->core->height-2;
427 scr->balloon->objectWindow = aicon->icon->core->window;
428 if ((scr->balloon->prevType == object->parent_type
429 || scr->balloon->prevType == WCLASS_MINIWINDOW)
430 && scr->balloon->ignoreTimer) {
431 XUnmapWindow(dpy, scr->balloon->window);
432 showBalloon(scr);
433 } else {
434 scr->balloon->timer = WMAddTimerHandler(BALLOON_DELAY,
435 (WMCallback*)showBalloon, scr);
441 void
442 wBalloonInitialize(WScreen *scr)
444 WBalloon *bal;
445 XSetWindowAttributes attribs;
446 unsigned long vmask;
448 bal = wmalloc(sizeof(WBalloon));
449 memset(bal, 0, sizeof(WBalloon));
451 scr->balloon = bal;
453 vmask = CWSaveUnder|CWOverrideRedirect|CWColormap|CWBackPixel
454 |CWBorderPixel;
455 attribs.save_under = True;
456 attribs.override_redirect = True;
457 attribs.colormap = scr->w_colormap;
458 attribs.background_pixel = scr->icon_back_texture->normal.pixel;
459 attribs.border_pixel = 0; /* do not care */
461 bal->window = XCreateWindow(dpy, scr->root_win, 1, 1, 10, 10, 1,
462 scr->w_depth, CopyFromParent,
463 scr->w_visual, vmask, &attribs);
464 #if 0
465 /* select EnterNotify to so that the balloon will be unmapped
466 * when the pointer is moved over it */
467 XSelectInput(dpy, bal->window, EnterWindowMask);
468 #endif
473 void
474 wBalloonEnteredObject(WScreen *scr, WObjDescriptor *object)
476 WBalloon *balloon = scr->balloon;
478 if (balloon->timer) {
479 WMDeleteTimerHandler(balloon->timer);
480 balloon->timer = NULL;
481 balloon->ignoreTimer = 0;
484 if (scr->balloon->text)
485 wfree(scr->balloon->text);
486 scr->balloon->text = NULL;
488 if (!object) {
489 wBalloonHide(scr);
490 balloon->ignoreTimer = 0;
491 return;
493 switch (object->parent_type) {
494 case WCLASS_FRAME:
495 if (wPreferences.window_balloon) {
496 frameBalloon(object);
498 break;
500 case WCLASS_DOCK_ICON:
501 if (object->parent != scr->clip_icon && wPreferences.appicon_balloon)
502 appiconBalloon(object);
503 else
504 wBalloonHide(scr);
505 break;
507 case WCLASS_MINIWINDOW:
508 if (wPreferences.miniwin_balloon) {
509 miniwindowBalloon(object);
511 break;
512 case WCLASS_APPICON:
513 if (wPreferences.appicon_balloon)
514 appiconBalloon(object);
515 break;
516 default:
517 wBalloonHide(scr);
518 break;
520 scr->balloon->prevType = object->parent_type;
525 void
526 wBalloonHide(WScreen *scr)
528 if (scr) {
529 if (scr->balloon->mapped) {
530 XUnmapWindow(dpy, scr->balloon->window);
531 scr->balloon->mapped = 0;
532 } else if (scr->balloon->timer) {
533 WMDeleteTimerHandler(scr->balloon->timer);
534 scr->balloon->timer = NULL;
536 scr->balloon->prevType = 0;
539 #endif