- API change in WINGs for WMDraw*String().
[wmaker-crm.git] / src / balloon.c
blob176fae2a590d25d0be89f5fc7566f7d3f4324a4a
1 /*
2 * Window Maker window manager
3 *
4 * Copyright (c) 1998-2002 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 <WINGs/WINGsP.h>
39 #include "WindowMaker.h"
40 #include "screen.h"
41 #include "texture.h"
42 #include "wcore.h"
43 #include "framewin.h"
44 #include "icon.h"
45 #include "appicon.h"
46 #include "funcs.h"
47 #include "workspace.h"
48 #include "balloon.h"
52 extern WPreferences wPreferences;
54 typedef struct _WBalloon {
55 Window window;
57 #ifdef SHAPED_BALLOON
58 GC monoGC;
59 #endif
60 int prevType;
62 Window objectWindow;
63 char *text;
64 int h;
66 WMHandlerID timer;
68 Pixmap contents;
70 char mapped;
71 char ignoreTimer;
72 } WBalloon;
75 #define TOP 0
76 #define BOTTOM 1
77 #define LEFT 0
78 #define RIGHT 2
80 #define TLEFT (TOP|LEFT)
81 #define TRIGHT (TOP|RIGHT)
82 #define BLEFT (BOTTOM|LEFT)
83 #define BRIGHT (BOTTOM|RIGHT)
86 #ifdef SHAPED_BALLOON
88 #define SPACE 12
91 static void
92 drawBalloon(Pixmap pix, GC gc, int x, int y, int w, int h, int side)
94 int rad = h*3/10;
95 XPoint pt[3];
97 XFillArc(dpy, pix, gc, x, y, rad, rad, 90*64, 90*64);
98 XFillArc(dpy, pix, gc, x, y+h-1-rad, rad, rad, 180*64, 90*64);
100 XFillArc(dpy, pix, gc, x+w-1-rad, y, rad, rad, 0*64, 90*64);
101 XFillArc(dpy, pix, gc, x+w-1-rad, y+h-1-rad, rad, rad, 270*64, 90*64);
103 XFillRectangle(dpy, pix, gc, x, y+rad/2, w, h-rad);
104 XFillRectangle(dpy, pix, gc, x+rad/2, y, w-rad, h);
106 if (side & BOTTOM) {
107 pt[0].y = y+h-1;
108 pt[1].y = y+h-1+SPACE;
109 pt[2].y = y+h-1;
110 } else {
111 pt[0].y = y;
112 pt[1].y = y-SPACE;
113 pt[2].y = y;
115 if (side & RIGHT) {
116 pt[0].x = x+w-h+2*h/16;
117 pt[1].x = x+w-h+11*h/16;
118 pt[2].x = x+w-h+7*h/16;
119 } else {
120 pt[0].x = x+h-2*h/16;
121 pt[1].x = x+h-11*h/16;
122 pt[2].x = x+h-7*h/16;
124 XFillPolygon(dpy, pix, gc, pt, 3, Convex, CoordModeOrigin);
128 static Pixmap
129 makePixmap(WScreen *scr, int width, int height, int side, Pixmap *mask)
131 WBalloon *bal = scr->balloon;
132 Pixmap bitmap;
133 Pixmap pixmap;
134 int x, y;
136 bitmap = XCreatePixmap(dpy, scr->root_win, width+SPACE, height+SPACE, 1);
138 if (!bal->monoGC) {
139 bal->monoGC = XCreateGC(dpy, bitmap, 0, NULL);
141 XSetForeground(dpy, bal->monoGC, 0);
142 XFillRectangle(dpy, bitmap, bal->monoGC, 0, 0, width+SPACE, height+SPACE);
144 pixmap = XCreatePixmap(dpy, scr->root_win, width+SPACE, height+SPACE,
145 scr->w_depth);
146 XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
147 XFillRectangle(dpy, pixmap, scr->draw_gc, 0, 0, width+SPACE, height+SPACE);
149 if (side & BOTTOM) {
150 y = 0;
151 } else {
152 y = SPACE;
154 x = 0;
156 XSetForeground(dpy, bal->monoGC, 1);
157 drawBalloon(bitmap, bal->monoGC, x, y, width, height, side);
158 XSetForeground(dpy, scr->draw_gc, scr->white_pixel);
159 drawBalloon(pixmap, scr->draw_gc, x+1, y+1, width-2, height-2, side);
161 *mask = bitmap;
163 return pixmap;
167 static void
168 showText(WScreen *scr, int x, int y, int h, int w, char *text)
170 int width;
171 int height;
172 Pixmap pixmap;
173 Pixmap mask;
174 WMFont *font = scr->info_text_font;
175 WMColor *color;
176 int side = 0;
177 int ty;
178 int bx, by;
180 if (scr->balloon->contents)
181 XFreePixmap(dpy, scr->balloon->contents);
183 width = WMWidthOfString(font, text, strlen(text))+16;
184 height = WMFontHeight(font) + 4;
186 if (height < 16)
187 height = 16;
188 if (width < height)
189 width = height;
192 if (x + width > scr->scr_width) {
193 side = RIGHT;
194 bx = x - width + w/2;
195 if (bx < 0)
196 bx = 0;
197 } else {
198 side = LEFT;
199 bx = x + w/2;
201 if (bx + width > scr->scr_width)
202 bx = scr->scr_width - width;
204 if (y - (height + SPACE) < 0) {
205 side |= TOP;
206 by = y+h-1;
207 ty = SPACE;
208 } else {
209 side |= BOTTOM;
210 by = y - (height + SPACE);
211 ty = 0;
213 pixmap = makePixmap(scr, width, height, side, &mask);
215 color = WMBlackColor(scr->wmscreen);
217 WMDrawString(scr->wmscreen, pixmap, color, font, 8,
218 ty + (height - WMFontHeight(font))/2,
219 text, strlen(text));
221 WMReleaseColor(color);
223 XSetWindowBackgroundPixmap(dpy, scr->balloon->window, pixmap);
224 scr->balloon->contents = pixmap;
226 XResizeWindow(dpy, scr->balloon->window, width, height+SPACE);
227 XShapeCombineMask(dpy, scr->balloon->window, ShapeBounding, 0, 0, mask,
228 ShapeSet);
229 XFreePixmap(dpy, mask);
230 XMoveWindow(dpy, scr->balloon->window, bx, by);
231 XMapRaised(dpy, scr->balloon->window);
234 scr->balloon->mapped = 1;
236 #else /* !SHAPED_BALLOON */
237 static void
238 showText(WScreen *scr, int x, int y, int h, int w, char *text)
240 int width;
241 int height;
242 Pixmap pixmap;
243 WMFont *font = scr->info_text_font;
244 WMColor *color;
245 WMPixel pixel;
247 if (scr->balloon->contents)
248 XFreePixmap(dpy, scr->balloon->contents);
250 width = WMWidthOfString(font, text, strlen(text))+8;
251 height = WMFontHeight(font) + 4;
253 if (x < 0)
254 x = 0;
255 else if (x + width > scr->scr_width-1)
256 x = scr->scr_width - width;
258 if (y - height - 2 < 0) {
259 y += h;
260 if (y < 0)
261 y = 0;
262 } else {
263 y -= height + 2;
266 if (scr->window_title_texture[0])
267 XSetForeground(dpy, scr->draw_gc,
268 scr->window_title_texture[0]->any.color.pixel);
269 else
270 XSetForeground(dpy, scr->draw_gc, scr->light_pixel);
272 pixmap = XCreatePixmap(dpy, scr->root_win, width, height, scr->w_depth);
273 XFillRectangle(dpy, pixmap, scr->draw_gc, 0, 0, width, height);
275 /* ugly hack */
276 color = WMBlackColor(scr->wmscreen);
277 pixel = color->color.pixel;
278 color->color.pixel = scr->window_title_pixel[0];
279 WMDrawString(scr->wmscreen, pixmap, color, font, 4, 2, text, strlen(text));
280 color->color.pixel = pixel;
281 WMReleaseColor(color);
283 XResizeWindow(dpy, scr->balloon->window, width, height);
284 XMoveWindow(dpy, scr->balloon->window, x, y);
286 XSetWindowBackgroundPixmap(dpy, scr->balloon->window, pixmap);
287 XClearWindow(dpy, scr->balloon->window);
288 XMapRaised(dpy, scr->balloon->window);
290 scr->balloon->contents = pixmap;
292 scr->balloon->mapped = 1;
294 #endif /* !SHAPED_BALLOON */
297 static void
298 showBalloon(WScreen *scr)
300 int x, y;
301 Window foow;
302 unsigned foo, w;
304 if (scr->balloon) {
305 scr->balloon->timer = NULL;
306 scr->balloon->ignoreTimer = 1;
309 if (!XGetGeometry(dpy, scr->balloon->objectWindow, &foow, &x, &y,
310 &w, &foo, &foo, &foo)) {
311 scr->balloon->prevType = 0;
312 return;
314 showText(scr, x, y, scr->balloon->h, w, scr->balloon->text);
319 static void
320 frameBalloon(WObjDescriptor *object)
322 WFrameWindow *fwin = (WFrameWindow*)object->parent;
323 WScreen *scr = fwin->core->screen_ptr;
325 if (fwin->titlebar != object->self
326 || !fwin->flags.is_client_window_frame) {
327 wBalloonHide(scr);
328 return;
330 if (fwin->title && fwin->flags.incomplete_title) {
331 scr->balloon->h = (fwin->titlebar ? fwin->titlebar->height : 0);
332 scr->balloon->text = wstrdup(fwin->title);
333 scr->balloon->objectWindow = fwin->core->window;
334 scr->balloon->timer = WMAddTimerHandler(BALLOON_DELAY,
335 (WMCallback*)showBalloon, scr);
340 static void
341 miniwindowBalloon(WObjDescriptor *object)
343 WIcon *icon = (WIcon*)object->parent;
344 WScreen *scr = icon->core->screen_ptr;
346 if (!icon->icon_name) {
347 wBalloonHide(scr);
348 return;
350 scr->balloon->h = icon->core->height;
351 scr->balloon->text = wstrdup(icon->icon_name);
352 scr->balloon->objectWindow = icon->core->window;
353 if ((scr->balloon->prevType == object->parent_type
354 || scr->balloon->prevType == WCLASS_APPICON)
355 && scr->balloon->ignoreTimer) {
356 XUnmapWindow(dpy, scr->balloon->window);
357 showBalloon(scr);
358 } else {
359 scr->balloon->timer = WMAddTimerHandler(BALLOON_DELAY,
360 (WMCallback*)showBalloon, scr);
366 static void
367 appiconBalloon(WObjDescriptor *object)
369 WAppIcon *aicon = (WAppIcon*)object->parent;
370 WScreen *scr = aicon->icon->core->screen_ptr;
371 char *tmp;
373 if (aicon->command && aicon->wm_class) {
374 int len = strlen(aicon->command)+strlen(aicon->wm_class)+8;
375 tmp = wmalloc(len);
376 snprintf(tmp, len, "%s (%s)", aicon->wm_class, aicon->command);
377 scr->balloon->text = tmp;
378 } else if (aicon->command) {
379 scr->balloon->text = wstrdup(aicon->command);
380 } else if (aicon->wm_class) {
381 scr->balloon->text = wstrdup(aicon->wm_class);
382 } else {
383 wBalloonHide(scr);
384 return;
386 scr->balloon->h = aicon->icon->core->height-2;
388 scr->balloon->objectWindow = aicon->icon->core->window;
389 if ((scr->balloon->prevType == object->parent_type
390 || scr->balloon->prevType == WCLASS_MINIWINDOW)
391 && scr->balloon->ignoreTimer) {
392 XUnmapWindow(dpy, scr->balloon->window);
393 showBalloon(scr);
394 } else {
395 scr->balloon->timer = WMAddTimerHandler(BALLOON_DELAY,
396 (WMCallback*)showBalloon, scr);
402 void
403 wBalloonInitialize(WScreen *scr)
405 WBalloon *bal;
406 XSetWindowAttributes attribs;
407 unsigned long vmask;
409 bal = wmalloc(sizeof(WBalloon));
410 memset(bal, 0, sizeof(WBalloon));
412 scr->balloon = bal;
414 vmask = CWSaveUnder|CWOverrideRedirect|CWColormap|CWBackPixel
415 |CWBorderPixel;
416 attribs.save_under = True;
417 attribs.override_redirect = True;
418 attribs.colormap = scr->w_colormap;
419 attribs.background_pixel = scr->icon_back_texture->normal.pixel;
420 attribs.border_pixel = 0; /* do not care */
422 bal->window = XCreateWindow(dpy, scr->root_win, 1, 1, 10, 10, 1,
423 scr->w_depth, CopyFromParent,
424 scr->w_visual, vmask, &attribs);
425 #if 0
426 /* select EnterNotify to so that the balloon will be unmapped
427 * when the pointer is moved over it */
428 XSelectInput(dpy, bal->window, EnterWindowMask);
429 #endif
434 void
435 wBalloonEnteredObject(WScreen *scr, WObjDescriptor *object)
437 WBalloon *balloon = scr->balloon;
439 if (balloon->timer) {
440 WMDeleteTimerHandler(balloon->timer);
441 balloon->timer = NULL;
442 balloon->ignoreTimer = 0;
445 if (scr->balloon->text)
446 wfree(scr->balloon->text);
447 scr->balloon->text = NULL;
449 if (!object) {
450 wBalloonHide(scr);
451 balloon->ignoreTimer = 0;
452 return;
454 switch (object->parent_type) {
455 case WCLASS_FRAME:
456 if (wPreferences.window_balloon) {
457 frameBalloon(object);
459 break;
461 case WCLASS_DOCK_ICON:
462 if (object->parent != scr->clip_icon && wPreferences.appicon_balloon)
463 appiconBalloon(object);
464 else
465 wBalloonHide(scr);
466 break;
468 case WCLASS_MINIWINDOW:
469 if (wPreferences.miniwin_balloon) {
470 miniwindowBalloon(object);
472 break;
473 case WCLASS_APPICON:
474 if (wPreferences.appicon_balloon)
475 appiconBalloon(object);
476 break;
477 default:
478 wBalloonHide(scr);
479 break;
481 scr->balloon->prevType = object->parent_type;
486 void
487 wBalloonHide(WScreen *scr)
489 if (scr) {
490 if (scr->balloon->mapped) {
491 XUnmapWindow(dpy, scr->balloon->window);
492 scr->balloon->mapped = 0;
493 } else if (scr->balloon->timer) {
494 WMDeleteTimerHandler(scr->balloon->timer);
495 scr->balloon->timer = NULL;
497 scr->balloon->prevType = 0;
500 #endif