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 }