- fixes for AA fonts
[wmaker-crm.git] / src / balloon.c
blob6ff4db352c0b02228fd0a2ff0213fd1f4e16e2f8
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 int side = 0;
176 int ty;
177 int bx, by;
179 if (scr->balloon->contents)
180 XFreePixmap(dpy, scr->balloon->contents);
182 width = WMWidthOfString(font, text, strlen(text))+16;
183 height = WMFontHeight(font) + 4;
185 if (height < 16)
186 height = 16;
187 if (width < height)
188 width = height;
191 if (x + width > scr->scr_width) {
192 side = RIGHT;
193 bx = x - width + w/2;
194 if (bx < 0)
195 bx = 0;
196 } else {
197 side = LEFT;
198 bx = x + w/2;
200 if (bx + width > scr->scr_width)
201 bx = scr->scr_width - width;
203 if (y - (height + SPACE) < 0) {
204 side |= TOP;
205 by = y+h-1;
206 ty = SPACE;
207 } else {
208 side |= BOTTOM;
209 by = y - (height + SPACE);
210 ty = 0;
212 pixmap = makePixmap(scr, width, height, side, &mask);
214 WMDrawString(scr->wmscreen, pixmap, scr->black, font, 8,
215 ty + (height - WMFontHeight(font))/2,
216 text, strlen(text));
218 XSetWindowBackgroundPixmap(dpy, scr->balloon->window, pixmap);
219 scr->balloon->contents = pixmap;
221 XResizeWindow(dpy, scr->balloon->window, width, height+SPACE);
222 XShapeCombineMask(dpy, scr->balloon->window, ShapeBounding, 0, 0, mask,
223 ShapeSet);
224 XFreePixmap(dpy, mask);
225 XMoveWindow(dpy, scr->balloon->window, bx, by);
226 XMapRaised(dpy, scr->balloon->window);
229 scr->balloon->mapped = 1;
231 #else /* !SHAPED_BALLOON */
232 static void
233 showText(WScreen *scr, int x, int y, int h, int w, char *text)
235 int width;
236 int height;
237 Pixmap pixmap;
238 WMFont *font = scr->info_text_font;
240 if (scr->balloon->contents)
241 XFreePixmap(dpy, scr->balloon->contents);
243 width = WMWidthOfString(font, text, strlen(text))+8;
244 height = WMFontHeight(font) + 4;
246 if (x < 0)
247 x = 0;
248 else if (x + width > scr->scr_width-1)
249 x = scr->scr_width - width;
251 if (y - height - 2 < 0) {
252 y += h;
253 if (y < 0)
254 y = 0;
255 } else {
256 y -= height + 2;
259 if (scr->window_title_texture[0])
260 XSetForeground(dpy, scr->draw_gc,
261 scr->window_title_texture[0]->any.color.pixel);
262 else
263 XSetForeground(dpy, scr->draw_gc, scr->light_pixel);
265 pixmap = XCreatePixmap(dpy, scr->root_win, width, height, scr->w_depth);
266 XFillRectangle(dpy, pixmap, scr->draw_gc, 0, 0, width, height);
268 WMDrawString(scr->wmscreen, pixmap, scr->window_title_color[0], font, 4, 2,
269 text, strlen(text));
271 XResizeWindow(dpy, scr->balloon->window, width, height);
272 XMoveWindow(dpy, scr->balloon->window, x, y);
274 XSetWindowBackgroundPixmap(dpy, scr->balloon->window, pixmap);
275 XClearWindow(dpy, scr->balloon->window);
276 XMapRaised(dpy, scr->balloon->window);
278 scr->balloon->contents = pixmap;
280 scr->balloon->mapped = 1;
282 #endif /* !SHAPED_BALLOON */
285 static void
286 showBalloon(WScreen *scr)
288 int x, y;
289 Window foow;
290 unsigned foo, w;
292 if (scr->balloon) {
293 scr->balloon->timer = NULL;
294 scr->balloon->ignoreTimer = 1;
297 if (!XGetGeometry(dpy, scr->balloon->objectWindow, &foow, &x, &y,
298 &w, &foo, &foo, &foo)) {
299 scr->balloon->prevType = 0;
300 return;
302 showText(scr, x, y, scr->balloon->h, w, scr->balloon->text);
307 static void
308 frameBalloon(WObjDescriptor *object)
310 WFrameWindow *fwin = (WFrameWindow*)object->parent;
311 WScreen *scr = fwin->core->screen_ptr;
313 if (fwin->titlebar != object->self
314 || !fwin->flags.is_client_window_frame) {
315 wBalloonHide(scr);
316 return;
318 if (fwin->title && fwin->flags.incomplete_title) {
319 scr->balloon->h = (fwin->titlebar ? fwin->titlebar->height : 0);
320 scr->balloon->text = wstrdup(fwin->title);
321 scr->balloon->objectWindow = fwin->core->window;
322 scr->balloon->timer = WMAddTimerHandler(BALLOON_DELAY,
323 (WMCallback*)showBalloon, scr);
328 static void
329 miniwindowBalloon(WObjDescriptor *object)
331 WIcon *icon = (WIcon*)object->parent;
332 WScreen *scr = icon->core->screen_ptr;
334 if (!icon->icon_name) {
335 wBalloonHide(scr);
336 return;
338 scr->balloon->h = icon->core->height;
339 scr->balloon->text = wstrdup(icon->icon_name);
340 scr->balloon->objectWindow = icon->core->window;
341 if ((scr->balloon->prevType == object->parent_type
342 || scr->balloon->prevType == WCLASS_APPICON)
343 && scr->balloon->ignoreTimer) {
344 XUnmapWindow(dpy, scr->balloon->window);
345 showBalloon(scr);
346 } else {
347 scr->balloon->timer = WMAddTimerHandler(BALLOON_DELAY,
348 (WMCallback*)showBalloon, scr);
354 static void
355 appiconBalloon(WObjDescriptor *object)
357 WAppIcon *aicon = (WAppIcon*)object->parent;
358 WScreen *scr = aicon->icon->core->screen_ptr;
359 char *tmp;
361 if (aicon->command && aicon->wm_class) {
362 int len = strlen(aicon->command)+strlen(aicon->wm_class)+8;
363 tmp = wmalloc(len);
364 snprintf(tmp, len, "%s (%s)", aicon->wm_class, aicon->command);
365 scr->balloon->text = tmp;
366 } else if (aicon->command) {
367 scr->balloon->text = wstrdup(aicon->command);
368 } else if (aicon->wm_class) {
369 scr->balloon->text = wstrdup(aicon->wm_class);
370 } else {
371 wBalloonHide(scr);
372 return;
374 scr->balloon->h = aicon->icon->core->height-2;
376 scr->balloon->objectWindow = aicon->icon->core->window;
377 if ((scr->balloon->prevType == object->parent_type
378 || scr->balloon->prevType == WCLASS_MINIWINDOW)
379 && scr->balloon->ignoreTimer) {
380 XUnmapWindow(dpy, scr->balloon->window);
381 showBalloon(scr);
382 } else {
383 scr->balloon->timer = WMAddTimerHandler(BALLOON_DELAY,
384 (WMCallback*)showBalloon, scr);
390 void
391 wBalloonInitialize(WScreen *scr)
393 WBalloon *bal;
394 XSetWindowAttributes attribs;
395 unsigned long vmask;
397 bal = wmalloc(sizeof(WBalloon));
398 memset(bal, 0, sizeof(WBalloon));
400 scr->balloon = bal;
402 vmask = CWSaveUnder|CWOverrideRedirect|CWColormap|CWBackPixel
403 |CWBorderPixel;
404 attribs.save_under = True;
405 attribs.override_redirect = True;
406 attribs.colormap = scr->w_colormap;
407 attribs.background_pixel = scr->icon_back_texture->normal.pixel;
408 attribs.border_pixel = 0; /* do not care */
410 bal->window = XCreateWindow(dpy, scr->root_win, 1, 1, 10, 10, 1,
411 scr->w_depth, CopyFromParent,
412 scr->w_visual, vmask, &attribs);
413 #if 0
414 /* select EnterNotify to so that the balloon will be unmapped
415 * when the pointer is moved over it */
416 XSelectInput(dpy, bal->window, EnterWindowMask);
417 #endif
422 void
423 wBalloonEnteredObject(WScreen *scr, WObjDescriptor *object)
425 WBalloon *balloon = scr->balloon;
427 if (balloon->timer) {
428 WMDeleteTimerHandler(balloon->timer);
429 balloon->timer = NULL;
430 balloon->ignoreTimer = 0;
433 if (scr->balloon->text)
434 wfree(scr->balloon->text);
435 scr->balloon->text = NULL;
437 if (!object) {
438 wBalloonHide(scr);
439 balloon->ignoreTimer = 0;
440 return;
442 switch (object->parent_type) {
443 case WCLASS_FRAME:
444 if (wPreferences.window_balloon) {
445 frameBalloon(object);
447 break;
449 case WCLASS_DOCK_ICON:
450 if (object->parent != scr->clip_icon && wPreferences.appicon_balloon)
451 appiconBalloon(object);
452 else
453 wBalloonHide(scr);
454 break;
456 case WCLASS_MINIWINDOW:
457 if (wPreferences.miniwin_balloon) {
458 miniwindowBalloon(object);
460 break;
461 case WCLASS_APPICON:
462 if (wPreferences.appicon_balloon)
463 appiconBalloon(object);
464 break;
465 default:
466 wBalloonHide(scr);
467 break;
469 scr->balloon->prevType = object->parent_type;
474 void
475 wBalloonHide(WScreen *scr)
477 if (scr) {
478 if (scr->balloon->mapped) {
479 XUnmapWindow(dpy, scr->balloon->window);
480 scr->balloon->mapped = 0;
481 } else if (scr->balloon->timer) {
482 WMDeleteTimerHandler(scr->balloon->timer);
483 scr->balloon->timer = NULL;
485 scr->balloon->prevType = 0;
488 #endif