Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / wballoon.c
1
2 #include "wconfig.h"
3 #include "WINGsP.h"
4
5 #ifdef SHAPE
6 #include <X11/extensions/shape.h>
7 #endif
8
9 typedef struct W_Balloon {
10         W_View *view;
11
12         WMHashTable *table;     /* Table from view ptr to text */
13
14         WMColor *backColor;
15         WMColor *textColor;
16         WMFont *font;
17
18         WMHandlerID timer;      /* timer for showing balloon */
19
20         WMHandlerID noDelayTimer;
21
22         int delay;
23
24         Window forWindow;       /* window for which the balloon
25                                  * is being show in the moment */
26
27         struct {
28                 WMAlignment alignment:2;
29                 unsigned enabled:1;
30
31                 unsigned noDelay:1;
32         } flags;
33 } Balloon;
34
35 #define DEFAULT_WIDTH           60
36 #define DEFAULT_HEIGHT          14
37 #define DEFAULT_ALIGNMENT       WALeft
38 #define DEFAULT_DELAY           500
39
40 #define NO_DELAY_DELAY          150
41
42 static void destroyBalloon(Balloon * bPtr);
43
44 static void handleEvents(XEvent * event, void *data);
45
46 static void showText(Balloon * bPtr, int x, int y, int w, int h, char *text);
47
48 struct W_Balloon *W_CreateBalloon(WMScreen * scr)
49 {
50         Balloon *bPtr;
51
52         bPtr = wmalloc(sizeof(Balloon));
53         memset(bPtr, 0, sizeof(Balloon));
54
55         bPtr->view = W_CreateUnmanagedTopView(scr);
56         if (!bPtr->view) {
57                 wfree(bPtr);
58                 return NULL;
59         }
60         bPtr->view->self = bPtr;
61
62         bPtr->textColor = WMRetainColor(bPtr->view->screen->black);
63
64         WMCreateEventHandler(bPtr->view, StructureNotifyMask, handleEvents, bPtr);
65
66         W_ResizeView(bPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
67         bPtr->flags.alignment = DEFAULT_ALIGNMENT;
68
69         bPtr->table = WMCreateHashTable(WMIntHashCallbacks);
70
71         bPtr->delay = DEFAULT_DELAY;
72
73         bPtr->flags.enabled = 1;
74
75         return bPtr;
76 }
77
78 void WMSetBalloonTextAlignment(WMScreen * scr, WMAlignment alignment)
79 {
80         scr->balloon->flags.alignment = alignment;
81
82 }
83
84 void WMSetBalloonTextForView(char *text, WMView * view)
85 {
86         char *oldText = NULL;
87         WMScreen *scr = view->screen;
88
89         if (text) {
90                 oldText = WMHashInsert(scr->balloon->table, view, wstrdup(text));
91         } else {
92                 oldText = WMHashGet(scr->balloon->table, view);
93
94                 WMHashRemove(scr->balloon->table, view);
95         }
96
97         if (oldText) {
98                 wfree(oldText);
99         }
100 }
101
102 void WMSetBalloonFont(WMScreen * scr, WMFont * font)
103 {
104         Balloon *bPtr = scr->balloon;
105
106         if (bPtr->font != NULL)
107                 WMReleaseFont(bPtr->font);
108
109         if (font)
110                 bPtr->font = WMRetainFont(font);
111         else
112                 bPtr->font = NULL;
113 }
114
115 void WMSetBalloonTextColor(WMScreen * scr, WMColor * color)
116 {
117         Balloon *bPtr = scr->balloon;
118
119         if (bPtr->textColor)
120                 WMReleaseColor(bPtr->textColor);
121
122         bPtr->textColor = WMRetainColor(color);
123 }
124
125 void WMSetBalloonDelay(WMScreen * scr, int delay)
126 {
127         scr->balloon->delay = delay;
128 }
129
130 void WMSetBalloonEnabled(WMScreen * scr, Bool flag)
131 {
132         scr->balloon->flags.enabled = ((flag == 0) ? 0 : 1);
133
134         W_UnmapView(scr->balloon->view);
135 }
136
137 static void clearNoDelay(void *data)
138 {
139         Balloon *bPtr = (Balloon *) data;
140
141         bPtr->flags.noDelay = 0;
142         bPtr->noDelayTimer = NULL;
143 }
144
145 void W_BalloonHandleLeaveView(WMView * view)
146 {
147         Balloon *bPtr = view->screen->balloon;
148
149         if (bPtr->forWindow == view->window) {
150                 if (bPtr->view->flags.mapped) {
151                         W_UnmapView(bPtr->view);
152                         bPtr->noDelayTimer = WMAddTimerHandler(NO_DELAY_DELAY, clearNoDelay, bPtr);
153                 }
154                 if (bPtr->timer)
155                         WMDeleteTimerHandler(bPtr->timer);
156
157                 bPtr->timer = NULL;
158
159                 bPtr->forWindow = None;
160         }
161 }
162
163 /*
164  * botar balao perto do cursor
165  * so mapear balao se o mouse ficar parado pelo delay
166  *
167  */
168
169 static void showBalloon(void *data)
170 {
171         char *text;
172         WMView *view = (WMView *) data;
173         Balloon *bPtr = view->screen->balloon;
174         int x, y;
175         Window foo;
176
177         bPtr->timer = NULL;
178
179         text = WMHashGet(bPtr->table, view);
180         if (!text)
181                 return;
182
183         XTranslateCoordinates(view->screen->display, view->window, view->screen->rootWin, 0, 0, &x, &y, &foo);
184
185         if (!bPtr->view->flags.realized)
186                 W_RealizeView(bPtr->view);
187
188         showText(bPtr, x, y, view->size.width, view->size.height, text);
189
190         bPtr->flags.noDelay = 1;
191 }
192
193 void W_BalloonHandleEnterView(WMView * view)
194 {
195         Balloon *bPtr = view->screen->balloon;
196         char *text;
197
198         if (!bPtr->flags.enabled)
199                 return;
200
201         text = WMHashGet(bPtr->table, view);
202         if (!text) {
203                 if (bPtr->view->flags.realized)
204                         W_UnmapView(bPtr->view);
205
206                 return;
207         }
208
209         if (bPtr->timer)
210                 WMDeleteTimerHandler(bPtr->timer);
211         bPtr->timer = NULL;
212
213         if (bPtr->noDelayTimer)
214                 WMDeleteTimerHandler(bPtr->noDelayTimer);
215         bPtr->noDelayTimer = NULL;
216
217         bPtr->forWindow = view->window;
218
219         if (bPtr->flags.noDelay) {
220                 bPtr->timer = NULL;
221
222                 showBalloon(view);
223         } else {
224                 bPtr->timer = WMAddTimerHandler(bPtr->delay, showBalloon, view);
225         }
226 }
227
228 #define TOP     0
229 #define BOTTOM  1
230 #define LEFT    0
231 #define RIGHT   2
232
233 #define TLEFT   (TOP|LEFT)
234 #define TRIGHT  (TOP|RIGHT)
235 #define BLEFT   (BOTTOM|LEFT)
236 #define BRIGHT  (BOTTOM|RIGHT)
237
238 #define         SPACE   12
239
240 static void drawBalloon(WMScreen * scr, Pixmap bitmap, Pixmap pix, int x, int y, int w, int h, int side)
241 {
242         Display *dpy = scr->display;
243         WMColor *white = WMWhiteColor(scr);
244         WMColor *black = WMBlackColor(scr);
245         GC bgc = scr->monoGC;
246         GC gc = WMColorGC(white);
247         int rad = h * 3 / 10;
248         XPoint pt[3], ipt[3];
249         int w1;
250
251         /* outline */
252         XSetForeground(dpy, bgc, 1);
253
254         XFillArc(dpy, bitmap, bgc, x, y, rad, rad, 90 * 64, 90 * 64);
255         XFillArc(dpy, bitmap, bgc, x, y + h - 1 - rad, rad, rad, 180 * 64, 90 * 64);
256
257         XFillArc(dpy, bitmap, bgc, x + w - 1 - rad, y, rad, rad, 0 * 64, 90 * 64);
258         XFillArc(dpy, bitmap, bgc, x + w - 1 - rad, y + h - 1 - rad, rad, rad, 270 * 64, 90 * 64);
259
260         XFillRectangle(dpy, bitmap, bgc, x, y + rad / 2, w, h - rad);
261         XFillRectangle(dpy, bitmap, bgc, x + rad / 2, y, w - rad, h);
262
263         /* interior */
264         XFillArc(dpy, pix, gc, x + 1, y + 1, rad, rad, 90 * 64, 90 * 64);
265         XFillArc(dpy, pix, gc, x + 1, y + h - 2 - rad, rad, rad, 180 * 64, 90 * 64);
266
267         XFillArc(dpy, pix, gc, x + w - 2 - rad, y + 1, rad, rad, 0 * 64, 90 * 64);
268         XFillArc(dpy, pix, gc, x + w - 2 - rad, y + h - 2 - rad, rad, rad, 270 * 64, 90 * 64);
269
270         XFillRectangle(dpy, pix, gc, x + 1, y + 1 + rad / 2, w - 2, h - 2 - rad);
271         XFillRectangle(dpy, pix, gc, x + 1 + rad / 2, y + 1, w - 2 - rad, h - 2);
272
273         if (side & BOTTOM) {
274                 pt[0].y = y + h - 1;
275                 pt[1].y = y + h - 1 + SPACE;
276                 pt[2].y = y + h - 1;
277                 ipt[0].y = pt[0].y - 1;
278                 ipt[1].y = pt[1].y - 1;
279                 ipt[2].y = pt[2].y - 1;
280         } else {
281                 pt[0].y = y;
282                 pt[1].y = y - SPACE;
283                 pt[2].y = y;
284                 ipt[0].y = pt[0].y + 1;
285                 ipt[1].y = pt[1].y + 1;
286                 ipt[2].y = pt[2].y + 1;
287         }
288
289         /*w1 = WMAX(h, 24); */
290         w1 = WMAX(h, 21);
291
292         if (side & RIGHT) {
293                 pt[0].x = x + w - w1 + 2 * w1 / 16;
294                 pt[1].x = x + w - w1 + 11 * w1 / 16;
295                 pt[2].x = x + w - w1 + 7 * w1 / 16;
296                 ipt[0].x = x + 1 + w - w1 + 2 * (w1 - 1) / 16;
297                 ipt[1].x = x + 1 + w - w1 + 11 * (w1 - 1) / 16;
298                 ipt[2].x = x + 1 + w - w1 + 7 * (w1 - 1) / 16;
299                 /*ipt[0].x = pt[0].x+1;
300                    ipt[1].x = pt[1].x;
301                    ipt[2].x = pt[2].x; */
302         } else {
303                 pt[0].x = x + w1 - 2 * w1 / 16;
304                 pt[1].x = x + w1 - 11 * w1 / 16;
305                 pt[2].x = x + w1 - 7 * w1 / 16;
306                 ipt[0].x = x - 1 + w1 - 2 * (w1 - 1) / 16;
307                 ipt[1].x = x - 1 + w1 - 11 * (w1 - 1) / 16;
308                 ipt[2].x = x - 1 + w1 - 7 * (w1 - 1) / 16;
309                 /*ipt[0].x = pt[0].x-1;
310                    ipt[1].x = pt[1].x;
311                    ipt[2].x = pt[2].x; */
312         }
313
314         XFillPolygon(dpy, bitmap, bgc, pt, 3, Convex, CoordModeOrigin);
315         XFillPolygon(dpy, pix, gc, ipt, 3, Convex, CoordModeOrigin);
316
317         /* fix outline */
318         XDrawLines(dpy, pix, WMColorGC(black), pt, 3, CoordModeOrigin);
319         if (side & RIGHT) {
320                 pt[0].x++;
321                 pt[2].x--;
322         } else {
323                 pt[0].x--;
324                 pt[2].x++;
325         }
326         XDrawLines(dpy, pix, WMColorGC(black), pt, 3, CoordModeOrigin);
327
328         WMReleaseColor(white);
329         WMReleaseColor(black);
330 }
331
332 static Pixmap makePixmap(WMScreen * scr, int width, int height, int side, Pixmap * mask)
333 {
334         Display *dpy = WMScreenDisplay(scr);
335         Pixmap bitmap;
336         Pixmap pixmap;
337         int x, y;
338         WMColor *black = WMBlackColor(scr);
339
340         bitmap = XCreatePixmap(dpy, scr->rootWin, width + SPACE, height + SPACE, 1);
341
342         XSetForeground(dpy, scr->monoGC, 0);
343         XFillRectangle(dpy, bitmap, scr->monoGC, 0, 0, width + SPACE, height + SPACE);
344
345         pixmap = XCreatePixmap(dpy, scr->rootWin, width + SPACE, height + SPACE, scr->depth);
346
347         XFillRectangle(dpy, pixmap, WMColorGC(black), 0, 0, width + SPACE, height + SPACE);
348
349         if (side & BOTTOM) {
350                 y = 0;
351         } else {
352                 y = SPACE;
353         }
354         x = 0;
355
356         drawBalloon(scr, bitmap, pixmap, x, y, width, height, side);
357
358         *mask = bitmap;
359
360         WMReleaseColor(black);
361
362         return pixmap;
363 }
364
365 static void showText(Balloon * bPtr, int x, int y, int w, int h, char *text)
366 {
367         WMScreen *scr = bPtr->view->screen;
368         Display *dpy = WMScreenDisplay(scr);
369         int width;
370         int height;
371         Pixmap pixmap;
372         Pixmap mask;
373         WMFont *font = bPtr->font ? bPtr->font : scr->normalFont;
374         int textHeight;
375         int side = 0;
376         int ty;
377         int bx, by;
378
379         {
380                 int w;
381                 char *ptr, *ptr2;
382
383                 ptr2 = ptr = text;
384                 width = 0;
385                 while (ptr && ptr2) {
386                         ptr2 = strchr(ptr, '\n');
387                         if (ptr2) {
388                                 w = WMWidthOfString(font, ptr, ptr2 - ptr);
389                         } else {
390                                 w = WMWidthOfString(font, ptr, strlen(ptr));
391                         }
392                         if (w > width)
393                                 width = w;
394                         ptr = ptr2 + 1;
395                 }
396         }
397
398         width += 16;
399
400         textHeight = W_GetTextHeight(font, text, width, False);
401
402         height = textHeight + 4;
403
404         if (height < 16)
405                 height = 16;
406         if (width < height)
407                 width = height;
408
409         if (x + width > scr->rootView->size.width) {
410                 side = RIGHT;
411                 bx = x - width + w / 2;
412                 if (bx < 0)
413                         bx = 0;
414         } else {
415                 side = LEFT;
416                 bx = x + w / 2;
417         }
418         if (bx + width > scr->rootView->size.width)
419                 bx = scr->rootView->size.width - width;
420
421         if (y - (height + SPACE) < 0) {
422                 side |= TOP;
423                 by = y + h - 1;
424                 ty = SPACE;
425         } else {
426                 side |= BOTTOM;
427                 by = y - (height + SPACE);
428                 ty = 0;
429         }
430         pixmap = makePixmap(scr, width, height, side, &mask);
431
432         W_PaintText(bPtr->view, pixmap, font, 8, ty + (height - textHeight) / 2,
433                     width, bPtr->flags.alignment,
434                     bPtr->textColor ? bPtr->textColor : scr->black, False, text, strlen(text));
435
436         XSetWindowBackgroundPixmap(dpy, bPtr->view->window, pixmap);
437
438         W_ResizeView(bPtr->view, width, height + SPACE);
439
440         XFreePixmap(dpy, pixmap);
441
442 #ifdef SHAPE
443         XShapeCombineMask(dpy, bPtr->view->window, ShapeBounding, 0, 0, mask, ShapeSet);
444 #endif
445         XFreePixmap(dpy, mask);
446
447         W_MoveView(bPtr->view, bx, by);
448
449         W_MapView(bPtr->view);
450 }
451
452 static void handleEvents(XEvent * event, void *data)
453 {
454         Balloon *bPtr = (Balloon *) data;
455
456         switch (event->type) {
457         case DestroyNotify:
458                 destroyBalloon(bPtr);
459                 break;
460         }
461 }
462
463 static void destroyBalloon(Balloon * bPtr)
464 {
465         WMHashEnumerator e;
466         char *str;
467
468         e = WMEnumerateHashTable(bPtr->table);
469
470         while ((str = WMNextHashEnumeratorItem(&e))) {
471                 wfree(str);
472         }
473         WMFreeHashTable(bPtr->table);
474
475         if (bPtr->textColor)
476                 WMReleaseColor(bPtr->textColor);
477
478         if (bPtr->font)
479                 WMReleaseFont(bPtr->font);
480
481         wfree(bPtr);
482 }