bug fix for balloons and some other minor things
[wmaker-crm.git] / WINGs / wballoon.c
blobc3be98a63c791a056f6fd61f5e2066835ab3ae4a
5 #include "WINGsP.h"
7 #include <X11/extensions/shape.h>
10 typedef struct W_Balloon {
11 W_View *view;
13 WMHashTable *table; /* Table from view ptr to text */
15 WMColor *backColor;
16 WMColor *textColor;
17 WMFont *font;
19 WMHandlerID timer; /* timer for showing balloon */
21 WMHandlerID noDelayTimer;
23 int delay;
25 Window forWindow; /* window for which the balloon
26 * is being show in the moment */
28 struct {
29 WMAlignment alignment:2;
30 unsigned enabled:1;
31 unsigned noDelay:1;
32 } flags;
33 } Balloon;
37 #define DEFAULT_WIDTH 60
38 #define DEFAULT_HEIGHT 14
39 #define DEFAULT_ALIGNMENT WALeft
40 #define DEFAULT_DELAY 500
42 #define NO_DELAY_DELAY 150
45 static void destroyBalloon(Balloon *bPtr);
48 static void handleEvents(XEvent *event, void *data);
50 static void showText(Balloon *bPtr, int x, int y, int w, int h, char *text);
53 struct W_Balloon*
54 W_CreateBalloon(WMScreen *scr)
56 Balloon *bPtr;
58 bPtr = wmalloc(sizeof(Balloon));
59 memset(bPtr, 0, sizeof(Balloon));
61 bPtr->view = W_CreateTopView(scr);
62 if (!bPtr->view) {
63 free(bPtr);
64 return NULL;
66 bPtr->view->self = bPtr;
68 bPtr->view->attribFlags |= CWOverrideRedirect;
69 bPtr->view->attribs.override_redirect = True;
71 bPtr->textColor = WMRetainColor(bPtr->view->screen->black);
73 WMCreateEventHandler(bPtr->view, StructureNotifyMask, handleEvents, bPtr);
75 W_ResizeView(bPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
76 bPtr->flags.alignment = DEFAULT_ALIGNMENT;
78 bPtr->table = WMCreateHashTable(WMIntHashCallbacks);
80 bPtr->delay = DEFAULT_DELAY;
82 bPtr->flags.enabled = 1;
84 return bPtr;
89 void
90 WMSetBalloonTextAlignment(WMScreen *scr, WMAlignment alignment)
92 scr->balloon->flags.alignment = alignment;
97 void
98 WMSetBalloonTextForView(char *text, WMView *view)
100 char *oldText = NULL;
101 WMScreen *scr = view->screen;
103 if (text) {
104 oldText = WMHashInsert(scr->balloon->table, view, wstrdup(text));
105 } else {
106 oldText = WMHashGet(scr->balloon->table, view);
108 WMHashRemove(scr->balloon->table, view);
111 if (oldText) {
112 free(oldText);
117 void
118 WMSetBalloonFont(WMScreen *scr, WMFont *font)
120 Balloon *bPtr = scr->balloon;
122 if (bPtr->font!=NULL)
123 WMReleaseFont(bPtr->font);
125 if (font)
126 bPtr->font = WMRetainFont(font);
127 else
128 bPtr->font = NULL;
132 void
133 WMSetBalloonTextColor(WMScreen *scr, WMColor *color)
135 Balloon *bPtr = scr->balloon;
137 if (bPtr->textColor)
138 WMReleaseColor(bPtr->textColor);
140 bPtr->textColor = WMRetainColor(color);
144 void
145 WMSetBalloonDelay(WMScreen *scr, int delay)
147 scr->balloon->delay = delay;
151 void
152 WMSetBalloonEnabled(WMScreen *scr, Bool flag)
154 scr->balloon->flags.enabled = flag;
156 W_UnmapView(scr->balloon->view);
160 static void
161 clearNoDelay(void *data)
163 Balloon *bPtr = (Balloon*)data;
165 bPtr->flags.noDelay = 0;
166 bPtr->noDelayTimer = NULL;
170 void
171 W_BalloonHandleLeaveView(WMView *view)
173 Balloon *bPtr = view->screen->balloon;
175 if (bPtr->view->flags.mapped && bPtr->forWindow == view->window) {
176 W_UnmapView(bPtr->view);
178 if (bPtr->timer)
179 WMDeleteTimerHandler(bPtr->timer);
181 bPtr->timer = NULL;
183 bPtr->noDelayTimer = WMAddTimerHandler(NO_DELAY_DELAY, clearNoDelay,
184 bPtr);
186 bPtr->forWindow = None;
190 static void
191 showBalloon(void *data)
193 char *text;
194 WMView *view = (WMView*)data;
195 Balloon *bPtr = view->screen->balloon;
196 int x, y;
197 Window foo;
199 bPtr->timer = NULL;
201 text = WMHashGet(bPtr->table, view);
202 if (!text)
203 return;
205 XTranslateCoordinates(view->screen->display, view->window,
206 view->screen->rootWin, 0, 0, &x, &y, &foo);
208 if (!bPtr->view->flags.realized)
209 W_RealizeView(bPtr->view);
211 showText(bPtr, x, y, view->size.width, view->size.height, text);
213 bPtr->forWindow = view->window;
215 bPtr->flags.noDelay = 1;
220 void
221 W_BalloonHandleEnterView(WMView *view)
223 Balloon *bPtr = view->screen->balloon;
224 char *text;
226 if (!bPtr->flags.enabled)
227 return;
229 text = WMHashGet(bPtr->table, view);
230 if (!text) {
231 bPtr->forWindow = None;
233 if (bPtr->view->flags.realized)
234 W_UnmapView(bPtr->view);
236 return;
239 if (bPtr->timer)
240 WMDeleteTimerHandler(bPtr->timer);
242 if (bPtr->noDelayTimer)
243 WMDeleteTimerHandler(bPtr->noDelayTimer);
244 bPtr->noDelayTimer = NULL;
246 if (bPtr->flags.noDelay) {
247 bPtr->timer = NULL;
249 showBalloon(view);
250 } else {
251 bPtr->timer = WMAddTimerHandler(bPtr->delay, showBalloon, view);
256 #define TOP 0
257 #define BOTTOM 1
258 #define LEFT 0
259 #define RIGHT 2
261 #define TLEFT (TOP|LEFT)
262 #define TRIGHT (TOP|RIGHT)
263 #define BLEFT (BOTTOM|LEFT)
264 #define BRIGHT (BOTTOM|RIGHT)
268 #define SPACE 12
271 static void
272 drawBalloon(Display *dpy, Pixmap pix, GC gc, int x, int y, int w, int h,
273 int side)
275 int rad = h*3/10;
276 XPoint pt[3];
278 XFillArc(dpy, pix, gc, x, y, rad, rad, 90*64, 90*64);
279 XFillArc(dpy, pix, gc, x, y+h-1-rad, rad, rad, 180*64, 90*64);
281 XFillArc(dpy, pix, gc, x+w-1-rad, y, rad, rad, 0*64, 90*64);
282 XFillArc(dpy, pix, gc, x+w-1-rad, y+h-1-rad, rad, rad, 270*64, 90*64);
284 XFillRectangle(dpy, pix, gc, x, y+rad/2, w, h-rad);
285 XFillRectangle(dpy, pix, gc, x+rad/2, y, w-rad, h);
287 if (side & BOTTOM) {
288 pt[0].y = y+h-1;
289 pt[1].y = y+h-1+SPACE;
290 pt[2].y = y+h-1;
291 } else {
292 pt[0].y = y;
293 pt[1].y = y-SPACE;
294 pt[2].y = y;
296 if (side & RIGHT) {
297 pt[0].x = x+w-h+2*h/16;
298 pt[1].x = x+w-h+11*h/16;
299 pt[2].x = x+w-h+7*h/16;
300 } else {
301 pt[0].x = x+h-2*h/16;
302 pt[1].x = x+h-11*h/16;
303 pt[2].x = x+h-7*h/16;
305 XFillPolygon(dpy, pix, gc, pt, 3, Convex, CoordModeOrigin);
309 static Pixmap
310 makePixmap(WMScreen *scr, int width, int height, int side, Pixmap *mask)
312 Display *dpy = WMScreenDisplay(scr);
313 Pixmap bitmap;
314 Pixmap pixmap;
315 int x, y;
316 WMColor *black = WMBlackColor(scr);
317 WMColor *white = WMWhiteColor(scr);
319 bitmap = XCreatePixmap(dpy, scr->rootWin, width+SPACE, height+SPACE, 1);
321 XSetForeground(dpy, scr->monoGC, 0);
322 XFillRectangle(dpy, bitmap, scr->monoGC, 0, 0, width+SPACE, height+SPACE);
324 pixmap = XCreatePixmap(dpy, scr->rootWin, width+SPACE, height+SPACE,
325 scr->depth);
327 XFillRectangle(dpy, pixmap, WMColorGC(black), 0, 0,
328 width+SPACE, height+SPACE);
330 if (side & BOTTOM) {
331 y = 0;
332 } else {
333 y = SPACE;
335 x = 0;
337 XSetForeground(dpy, scr->monoGC, 1);
338 drawBalloon(dpy, bitmap, scr->monoGC, x, y, width, height, side);
339 drawBalloon(dpy, pixmap, WMColorGC(white), x+1, y+1, width-2, height-2,
340 side);
342 *mask = bitmap;
344 WMReleaseColor(black);
345 WMReleaseColor(white);
347 return pixmap;
351 static void
352 showText(Balloon *bPtr, int x, int y, int h, int w, char *text)
354 WMScreen *scr = bPtr->view->screen;
355 Display *dpy = WMScreenDisplay(scr);
356 int width;
357 int height;
358 Pixmap pixmap;
359 Pixmap mask;
360 WMFont *font = bPtr->font ? bPtr->font : scr->normalFont;
361 int textHeight;
362 int side = 0;
363 int ty;
364 int bx, by;
367 int w;
368 char *ptr, *ptr2;
370 ptr2 = ptr = text;
371 width = 0;
372 while (ptr && ptr2) {
373 ptr2 = strchr(ptr, '\n');
374 if (ptr2) {
375 w = WMWidthOfString(font, ptr, ptr2 - ptr);
376 } else {
377 w = WMWidthOfString(font, ptr, strlen(ptr));
379 if (w > width)
380 width = w;
381 ptr = ptr2 + 1;
385 width += 16;
387 textHeight = W_GetTextHeight(font, text, width, False);
389 height = textHeight + 4;
391 if (height < 16)
392 height = 16;
393 if (width < height)
394 width = height;
396 if (x + width > scr->rootView->size.width) {
397 side = RIGHT;
398 bx = x - width + w/2;
399 if (bx < 0)
400 bx = 0;
401 } else {
402 side = LEFT;
403 bx = x + w/2;
405 if (bx + width > scr->rootView->size.width)
406 bx = scr->rootView->size.width - width;
408 if (y - (height + SPACE) < 0) {
409 side |= TOP;
410 by = y+h-1;
411 ty = SPACE;
412 } else {
413 side |= BOTTOM;
414 by = y - (height + SPACE);
415 ty = 0;
417 pixmap = makePixmap(scr, width, height, side, &mask);
419 W_PaintText(bPtr->view, pixmap, font, 8, ty + (height - textHeight)/2,
420 width, bPtr->flags.alignment,
421 WMColorGC(bPtr->textColor ? bPtr->textColor : scr->black),
422 False, text, strlen(text));
424 XSetWindowBackgroundPixmap(dpy, bPtr->view->window, pixmap);
426 W_ResizeView(bPtr->view, width, height+SPACE);
428 XFreePixmap(dpy, pixmap);
430 XShapeCombineMask(dpy, bPtr->view->window, ShapeBounding, 0, 0, mask,
431 ShapeSet);
432 XFreePixmap(dpy, mask);
434 W_MoveView(bPtr->view, bx, by);
436 W_MapView(bPtr->view);
440 static void
441 handleEvents(XEvent *event, void *data)
443 Balloon *bPtr = (Balloon*)data;
445 switch (event->type) {
446 case DestroyNotify:
447 destroyBalloon(bPtr);
448 break;
453 static void
454 destroyBalloon(Balloon *bPtr)
456 WMHashEnumerator e;
457 char *str;
459 e = WMEnumerateHashTable(bPtr->table);
461 while ((str = WMNextHashEnumeratorItem(&e))) {
462 free(str);
464 WMFreeHashTable(bPtr->table);
466 if (bPtr->textColor)
467 WMReleaseColor(bPtr->textColor);
469 if (bPtr->font)
470 WMReleaseFont(bPtr->font);
472 free(bPtr);