some documentation updates
[wmaker-crm.git] / src / balloon.c
blobf751508153674799b415ccb0b16c733c7e2f0f0f
1 /*
2 * Window Maker window manager
4 * Copyright (c) 1998-2003 Alfredo K. Kojima
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 static int
85 countLines(const char *text)
87 const char *p = text;
88 int h = 1;
90 while (*p) {
91 if (*p == '\n' && p[1] != 0)
92 h++;
93 p++;
95 return h;
99 static int
100 getMaxStringWidth(WMFont *font, char *text)
102 char *p = text;
103 char *pb = p;
104 int pos = 0;
105 int w = 0, wt;
107 while (*p) {
108 if (*p == '\n') {
109 wt = WMWidthOfString(font, pb, pos);
110 if (wt > w)
111 w = wt;
112 pos = 0;
113 pb = p + 1;
114 } else {
115 pos++;
117 p++;
119 if (pos > 0) {
120 wt = WMWidthOfString(font, pb, pos);
121 if (wt > w)
122 w = wt;
124 return w;
128 static void
129 drawMultiLineString(WMScreen *scr, Pixmap pixmap, WMColor *color,
130 WMFont *font, int x, int y, char *text, int len)
132 char *p = text;
133 char *pb = p;
134 int l = 0, pos = 0;
135 int height = WMFontHeight(font);
137 while (*p && p - text < len) {
138 if (*p == '\n') {
139 WMDrawString(scr, pixmap, color, font, x, y + l * height, pb,
140 pos);
141 l++;
142 pos = 0;
143 pb = p + 1;
144 } else {
145 pos++;
147 p++;
149 if (pos > 0) {
150 WMDrawString(scr, pixmap, color, font, x, y + l * height, pb, pos);
157 #ifdef SHAPED_BALLOON
159 #define SPACE 12
162 static void
163 drawBalloon(WScreen *scr, Pixmap bitmap, Pixmap pix, int x, int y, int w,
164 int h, int side)
166 GC bgc = scr->balloon->monoGC;
167 GC gc = scr->draw_gc;
168 int rad = h * 3 / 10;
169 XPoint pt[3], ipt[3];
170 int w1;
172 /* outline */
173 XSetForeground(dpy, bgc, 1);
175 XFillArc(dpy, bitmap, bgc, x, y, rad, rad, 90 * 64, 90 * 64);
176 XFillArc(dpy, bitmap, bgc, x, y + h - 1 - rad, rad, rad, 180 * 64,
177 90 * 64);
179 XFillArc(dpy, bitmap, bgc, x + w - 1 - rad, y, rad, rad, 0 * 64,
180 90 * 64);
181 XFillArc(dpy, bitmap, bgc, x + w - 1 - rad, y + h - 1 - rad, rad, rad,
182 270 * 64, 90 * 64);
184 XFillRectangle(dpy, bitmap, bgc, x, y + rad / 2, w, h - rad);
185 XFillRectangle(dpy, bitmap, bgc, x + rad / 2, y, w - rad, h);
187 /* interior */
188 XSetForeground(dpy, gc, scr->white_pixel);
190 XFillArc(dpy, pix, gc, x + 1, y + 1, rad, rad, 90 * 64, 90 * 64);
191 XFillArc(dpy, pix, gc, x + 1, y + h - 2 - rad, rad, rad, 180 * 64,
192 90 * 64);
194 XFillArc(dpy, pix, gc, x + w - 2 - rad, y + 1, rad, rad, 0 * 64,
195 90 * 64);
196 XFillArc(dpy, pix, gc, x + w - 2 - rad, y + h - 2 - rad, rad, rad,
197 270 * 64, 90 * 64);
199 XFillRectangle(dpy, pix, gc, x + 1, y + 1 + rad / 2, w - 2,
200 h - 2 - rad);
201 XFillRectangle(dpy, pix, gc, x + 1 + rad / 2, y + 1, w - 2 - rad,
202 h - 2);
204 if (side & BOTTOM) {
205 pt[0].y = y + h - 1;
206 pt[1].y = y + h - 1 + SPACE;
207 pt[2].y = y + h - 1;
208 ipt[0].y = pt[0].y - 1;
209 ipt[1].y = pt[1].y - 1;
210 ipt[2].y = pt[2].y - 1;
211 } else {
212 pt[0].y = y;
213 pt[1].y = y - SPACE;
214 pt[2].y = y;
215 ipt[0].y = pt[0].y + 1;
216 ipt[1].y = pt[1].y + 1;
217 ipt[2].y = pt[2].y + 1;
220 /*w1 = WMAX(h, 24); */
221 w1 = WMAX(h, 21);
223 if (side & RIGHT) {
224 pt[0].x = x + w - w1 + 2 * w1 / 16;
225 pt[1].x = x + w - w1 + 11 * w1 / 16;
226 pt[2].x = x + w - w1 + 7 * w1 / 16;
227 ipt[0].x = x + 1 + w - w1 + 2 * (w1 - 1) / 16;
228 ipt[1].x = x + 1 + w - w1 + 11 * (w1 - 1) / 16;
229 ipt[2].x = x + 1 + w - w1 + 7 * (w1 - 1) / 16;
230 /*ipt[0].x = pt[0].x+1;
231 ipt[1].x = pt[1].x;
232 ipt[2].x = pt[2].x; */
233 } else {
234 pt[0].x = x + w1 - 2 * w1 / 16;
235 pt[1].x = x + w1 - 11 * w1 / 16;
236 pt[2].x = x + w1 - 7 * w1 / 16;
237 ipt[0].x = x - 1 + w1 - 2 * (w1 - 1) / 16;
238 ipt[1].x = x - 1 + w1 - 11 * (w1 - 1) / 16;
239 ipt[2].x = x - 1 + w1 - 7 * (w1 - 1) / 16;
240 /*ipt[0].x = pt[0].x-1;
241 ipt[1].x = pt[1].x;
242 ipt[2].x = pt[2].x; */
245 XFillPolygon(dpy, bitmap, bgc, pt, 3, Convex, CoordModeOrigin);
246 XFillPolygon(dpy, pix, gc, ipt, 3, Convex, CoordModeOrigin);
248 /* fix outline */
249 XSetForeground(dpy, gc, scr->black_pixel);
251 XDrawLines(dpy, pix, gc, pt, 3, CoordModeOrigin);
252 if (side & RIGHT) {
253 pt[0].x++;
254 pt[2].x--;
255 } else {
256 pt[0].x--;
257 pt[2].x++;
259 XDrawLines(dpy, pix, gc, pt, 3, CoordModeOrigin);
263 static Pixmap
264 makePixmap(WScreen *scr, int width, int height, int side, Pixmap *mask)
266 WBalloon *bal = scr->balloon;
267 Pixmap bitmap;
268 Pixmap pixmap;
269 int x, y;
271 bitmap =
272 XCreatePixmap(dpy, scr->root_win, width + SPACE, height + SPACE,
275 if (!bal->monoGC) {
276 bal->monoGC = XCreateGC(dpy, bitmap, 0, NULL);
278 XSetForeground(dpy, bal->monoGC, 0);
279 XFillRectangle(dpy, bitmap, bal->monoGC, 0, 0, width + SPACE,
280 height + SPACE);
282 pixmap =
283 XCreatePixmap(dpy, scr->root_win, width + SPACE, height + SPACE,
284 scr->w_depth);
285 XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
286 XFillRectangle(dpy, pixmap, scr->draw_gc, 0, 0, width + SPACE,
287 height + SPACE);
289 if (side & BOTTOM) {
290 y = 0;
291 } else {
292 y = SPACE;
294 x = 0;
296 drawBalloon(scr, bitmap, pixmap, x, y, width, height, side);
298 *mask = bitmap;
300 return pixmap;
304 static void
305 showText(WScreen *scr, int x, int y, int h, int w, char *text)
307 int width;
308 int height;
309 Pixmap pixmap;
310 Pixmap mask;
311 WMFont *font = scr->info_text_font;
312 int side = 0;
313 int ty;
314 int bx, by;
316 if (scr->balloon->contents)
317 XFreePixmap(dpy, scr->balloon->contents);
319 width = getMaxStringWidth(font, text) + 16;
320 height = countLines(text) * WMFontHeight(font) + 4;
322 if (height < 16)
323 height = 16;
324 if (width < height)
325 width = height;
328 if (x + width > scr->scr_width) {
329 side = RIGHT;
330 bx = x - width + w / 2;
331 if (bx < 0)
332 bx = 0;
333 } else {
334 side = LEFT;
335 bx = x + w / 2;
337 if (bx + width > scr->scr_width)
338 bx = scr->scr_width - width;
340 if (y - (height + SPACE) < 0) {
341 side |= TOP;
342 by = y + h - 1;
343 ty = SPACE;
344 } else {
345 side |= BOTTOM;
346 by = y - (height + SPACE);
347 ty = 0;
349 pixmap = makePixmap(scr, width, height, side, &mask);
351 drawMultiLineString(scr->wmscreen, pixmap, scr->black, font, 8, ty + 2,
352 text, strlen(text));
354 XSetWindowBackgroundPixmap(dpy, scr->balloon->window, pixmap);
355 scr->balloon->contents = pixmap;
357 XResizeWindow(dpy, scr->balloon->window, width, height + SPACE);
358 XShapeCombineMask(dpy, scr->balloon->window, ShapeBounding, 0, 0, mask,
359 ShapeSet);
360 XFreePixmap(dpy, mask);
361 XMoveWindow(dpy, scr->balloon->window, bx, by);
362 XMapRaised(dpy, scr->balloon->window);
365 scr->balloon->mapped = 1;
367 #else /* !SHAPED_BALLOON */
370 static void
371 showText(WScreen *scr, int x, int y, int h, int w, char *text)
373 int width;
374 int height;
375 Pixmap pixmap;
376 WMFont *font = scr->info_text_font;
378 if (scr->balloon->contents)
379 XFreePixmap(dpy, scr->balloon->contents);
381 width = getMaxStringWidth(font, text) + 8;
382 /*width = WMWidthOfString(font, text, strlen(text))+8;*/
383 height = countLines(text) * WMFontHeight(font) + 4;
385 if (x < 0)
386 x = 0;
387 else if (x + width > scr->scr_width - 1)
388 x = scr->scr_width - width;
390 if (y - height - 2 < 0) {
391 y += h;
392 if (y < 0)
393 y = 0;
394 } else {
395 y -= height + 2;
398 if (scr->window_title_texture[0])
399 XSetForeground(dpy, scr->draw_gc,
400 scr->window_title_texture[0]->any.color.pixel);
401 else
402 XSetForeground(dpy, scr->draw_gc, scr->light_pixel);
404 pixmap =
405 XCreatePixmap(dpy, scr->root_win, width, height, scr->w_depth);
406 XFillRectangle(dpy, pixmap, scr->draw_gc, 0, 0, width, height);
408 drawMultiLineString(scr->wmscreen, pixmap, scr->window_title_color[0],
409 font, 4, 2, text, strlen(text));
411 XResizeWindow(dpy, scr->balloon->window, width, height);
412 XMoveWindow(dpy, scr->balloon->window, x, y);
414 XSetWindowBackgroundPixmap(dpy, scr->balloon->window, pixmap);
415 XClearWindow(dpy, scr->balloon->window);
416 XMapRaised(dpy, scr->balloon->window);
418 scr->balloon->contents = pixmap;
420 scr->balloon->mapped = 1;
422 #endif /* !SHAPED_BALLOON */
425 static void
426 showBalloon(WScreen *scr)
428 int x, y;
429 Window foow;
430 unsigned foo, w;
432 if (scr->balloon) {
433 scr->balloon->timer = NULL;
434 scr->balloon->ignoreTimer = 1;
437 if (!XGetGeometry(dpy, scr->balloon->objectWindow, &foow, &x, &y,
438 &w, &foo, &foo, &foo)) {
439 scr->balloon->prevType = 0;
440 return;
442 showText(scr, x, y, scr->balloon->h, w, scr->balloon->text);
446 static void
447 frameBalloon(WObjDescriptor *object)
449 WFrameWindow *fwin = (WFrameWindow *) object->parent;
450 WScreen *scr = fwin->core->screen_ptr;
452 if (fwin->titlebar != object->self
453 || !fwin->flags.is_client_window_frame) {
454 wBalloonHide(scr);
455 return;
457 if (fwin->title && fwin->flags.incomplete_title) {
458 scr->balloon->h = (fwin->titlebar ? fwin->titlebar->height : 0);
459 scr->balloon->text = wstrdup(fwin->title);
460 scr->balloon->objectWindow = fwin->core->window;
461 scr->balloon->timer = WMAddTimerHandler(BALLOON_DELAY,
462 (WMCallback *) showBalloon,
463 scr);
468 static void
469 miniwindowBalloon(WObjDescriptor *object)
471 WIcon *icon = (WIcon *) object->parent;
472 WScreen *scr = icon->core->screen_ptr;
474 if (!icon->icon_name) {
475 wBalloonHide(scr);
476 return;
478 scr->balloon->h = icon->core->height;
479 scr->balloon->text = wstrdup(icon->icon_name);
480 scr->balloon->objectWindow = icon->core->window;
481 if ((scr->balloon->prevType == object->parent_type
482 || scr->balloon->prevType == WCLASS_APPICON)
483 && scr->balloon->ignoreTimer) {
484 XUnmapWindow(dpy, scr->balloon->window);
485 showBalloon(scr);
486 } else {
487 scr->balloon->timer = WMAddTimerHandler(BALLOON_DELAY,
488 (WMCallback *) showBalloon,
489 scr);
494 static void
495 appiconBalloon(WObjDescriptor *object)
497 WAppIcon *aicon = (WAppIcon *) object->parent;
498 WScreen *scr = aicon->icon->core->screen_ptr;
499 char *tmp;
501 if (aicon->command && aicon->wm_class) {
502 int len = strlen(aicon->command) + strlen(aicon->wm_class) + 8;
503 tmp = wmalloc(len);
504 snprintf(tmp, len, "%s\n(%s)", aicon->wm_class, aicon->command);
505 scr->balloon->text = tmp;
506 } else if (aicon->command) {
507 scr->balloon->text = wstrdup(aicon->command);
508 } else if (aicon->wm_class) {
509 scr->balloon->text = wstrdup(aicon->wm_class);
510 } else {
511 wBalloonHide(scr);
512 return;
514 scr->balloon->h = aicon->icon->core->height - 2;
516 scr->balloon->objectWindow = aicon->icon->core->window;
517 if ((scr->balloon->prevType == object->parent_type
518 || scr->balloon->prevType == WCLASS_MINIWINDOW)
519 && scr->balloon->ignoreTimer) {
520 XUnmapWindow(dpy, scr->balloon->window);
521 showBalloon(scr);
522 } else {
523 scr->balloon->timer = WMAddTimerHandler(BALLOON_DELAY,
524 (WMCallback *) showBalloon,
525 scr);
531 void
532 wBalloonInitialize(WScreen *scr)
534 WBalloon *bal;
535 XSetWindowAttributes attribs;
536 unsigned long vmask;
538 bal = wmalloc(sizeof(WBalloon));
539 memset(bal, 0, sizeof(WBalloon));
541 scr->balloon = bal;
543 vmask = CWSaveUnder | CWOverrideRedirect | CWColormap | CWBackPixel
544 | CWBorderPixel;
545 attribs.save_under = True;
546 attribs.override_redirect = True;
547 attribs.colormap = scr->w_colormap;
548 attribs.background_pixel = scr->icon_back_texture->normal.pixel;
549 attribs.border_pixel = 0; /* do not care */
551 bal->window = XCreateWindow(dpy, scr->root_win, 1, 1, 10, 10, 1,
552 scr->w_depth, CopyFromParent,
553 scr->w_visual, vmask, &attribs);
554 #if 0
555 /* select EnterNotify to so that the balloon will be unmapped
556 * when the pointer is moved over it */
557 XSelectInput(dpy, bal->window, EnterWindowMask);
558 #endif
562 void
563 wBalloonEnteredObject(WScreen *scr, WObjDescriptor *object)
565 WBalloon *balloon = scr->balloon;
567 if (balloon->timer) {
568 WMDeleteTimerHandler(balloon->timer);
569 balloon->timer = NULL;
570 balloon->ignoreTimer = 0;
573 if (scr->balloon->text)
574 wfree(scr->balloon->text);
575 scr->balloon->text = NULL;
577 if (!object) {
578 wBalloonHide(scr);
579 balloon->ignoreTimer = 0;
580 return;
582 switch (object->parent_type) {
583 case WCLASS_FRAME:
584 if (wPreferences.window_balloon) {
585 frameBalloon(object);
587 break;
589 case WCLASS_DOCK_ICON:
590 if (object->parent != scr->clip_icon
591 && wPreferences.appicon_balloon)
592 appiconBalloon(object);
593 else
594 wBalloonHide(scr);
595 break;
597 case WCLASS_MINIWINDOW:
598 if (wPreferences.miniwin_balloon) {
599 miniwindowBalloon(object);
601 break;
602 case WCLASS_APPICON:
603 if (wPreferences.appicon_balloon)
604 appiconBalloon(object);
605 break;
606 default:
607 wBalloonHide(scr);
608 break;
610 scr->balloon->prevType = object->parent_type;
615 void
616 wBalloonHide(WScreen *scr)
618 if (scr) {
619 if (scr->balloon->mapped) {
620 XUnmapWindow(dpy, scr->balloon->window);
621 scr->balloon->mapped = 0;
622 } else if (scr->balloon->timer) {
623 WMDeleteTimerHandler(scr->balloon->timer);
624 scr->balloon->timer = NULL;
626 scr->balloon->prevType = 0;
630 #endif