strncpy's third argument should be the length of the dest buffer, not the source.
[wmaker-crm.git] / WINGs / wballoon.c
blobc87b6c9e419c2d3ccc1a2c8c559c183c47c11c41
2 #include "wconfig.h"
3 #include "WINGsP.h"
5 #ifdef USE_XSHAPE
6 #include <X11/extensions/shape.h>
7 #endif
9 typedef struct W_Balloon {
10 W_View *view;
12 WMHashTable *table; /* Table from view ptr to text */
14 WMColor *backColor;
15 WMColor *textColor;
16 WMFont *font;
18 WMHandlerID timer; /* timer for showing balloon */
20 WMHandlerID noDelayTimer;
22 int delay;
24 Window forWindow; /* window for which the balloon
25 * is being show in the moment */
27 struct {
28 WMAlignment alignment:2;
29 unsigned enabled:1;
31 unsigned noDelay:1;
32 } flags;
33 } Balloon;
35 #define DEFAULT_WIDTH 60
36 #define DEFAULT_HEIGHT 14
37 #define DEFAULT_ALIGNMENT WALeft
38 #define DEFAULT_DELAY 500
40 #define NO_DELAY_DELAY 150
42 static void destroyBalloon(Balloon * bPtr);
44 static void handleEvents(XEvent * event, void *data);
46 static void showText(Balloon * bPtr, int x, int y, int w, int h, const char *text);
48 struct W_Balloon *W_CreateBalloon(WMScreen * scr)
50 Balloon *bPtr;
52 bPtr = wmalloc(sizeof(Balloon));
54 bPtr->view = W_CreateUnmanagedTopView(scr);
55 if (!bPtr->view) {
56 wfree(bPtr);
57 return NULL;
59 bPtr->view->self = bPtr;
61 bPtr->textColor = WMRetainColor(bPtr->view->screen->black);
63 WMCreateEventHandler(bPtr->view, StructureNotifyMask, handleEvents, bPtr);
65 W_ResizeView(bPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
66 bPtr->flags.alignment = DEFAULT_ALIGNMENT;
68 bPtr->table = WMCreateHashTable(WMIntHashCallbacks);
70 bPtr->delay = DEFAULT_DELAY;
72 bPtr->flags.enabled = 1;
74 return bPtr;
77 void WMSetBalloonTextAlignment(WMScreen * scr, WMAlignment alignment)
79 scr->balloon->flags.alignment = alignment;
83 void WMSetBalloonTextForView(const char *text, WMView * view)
85 char *oldText = NULL;
86 WMScreen *scr = view->screen;
88 if (text) {
89 oldText = WMHashInsert(scr->balloon->table, view, wstrdup(text));
90 } else {
91 oldText = WMHashGet(scr->balloon->table, view);
93 WMHashRemove(scr->balloon->table, view);
96 if (oldText) {
97 wfree(oldText);
101 void WMSetBalloonFont(WMScreen * scr, WMFont * font)
103 Balloon *bPtr = scr->balloon;
105 if (bPtr->font != NULL)
106 WMReleaseFont(bPtr->font);
108 if (font)
109 bPtr->font = WMRetainFont(font);
110 else
111 bPtr->font = NULL;
114 void WMSetBalloonTextColor(WMScreen * scr, WMColor * color)
116 Balloon *bPtr = scr->balloon;
118 if (bPtr->textColor)
119 WMReleaseColor(bPtr->textColor);
121 bPtr->textColor = WMRetainColor(color);
124 void WMSetBalloonDelay(WMScreen * scr, int delay)
126 scr->balloon->delay = delay;
129 void WMSetBalloonEnabled(WMScreen * scr, Bool flag)
131 scr->balloon->flags.enabled = ((flag == 0) ? 0 : 1);
133 W_UnmapView(scr->balloon->view);
136 static void clearNoDelay(void *data)
138 Balloon *bPtr = (Balloon *) data;
140 bPtr->flags.noDelay = 0;
141 bPtr->noDelayTimer = NULL;
144 void W_BalloonHandleLeaveView(WMView * view)
146 Balloon *bPtr = view->screen->balloon;
148 if (bPtr->forWindow == view->window) {
149 if (bPtr->view->flags.mapped) {
150 W_UnmapView(bPtr->view);
151 bPtr->noDelayTimer = WMAddTimerHandler(NO_DELAY_DELAY, clearNoDelay, bPtr);
153 if (bPtr->timer)
154 WMDeleteTimerHandler(bPtr->timer);
156 bPtr->timer = NULL;
158 bPtr->forWindow = None;
163 * botar balao perto do cursor
164 * so mapear balao se o mouse ficar parado pelo delay
168 static void showBalloon(void *data)
170 char *text;
171 WMView *view = (WMView *) data;
172 Balloon *bPtr = view->screen->balloon;
173 int x, y;
174 Window foo;
176 bPtr->timer = NULL;
178 text = WMHashGet(bPtr->table, view);
179 if (!text)
180 return;
182 XTranslateCoordinates(view->screen->display, view->window, view->screen->rootWin, 0, 0, &x, &y, &foo);
184 if (!bPtr->view->flags.realized)
185 W_RealizeView(bPtr->view);
187 showText(bPtr, x, y, view->size.width, view->size.height, text);
189 bPtr->flags.noDelay = 1;
192 void W_BalloonHandleEnterView(WMView * view)
194 Balloon *bPtr = view->screen->balloon;
195 char *text;
197 if (!bPtr->flags.enabled)
198 return;
200 text = WMHashGet(bPtr->table, view);
201 if (!text) {
202 if (bPtr->view->flags.realized)
203 W_UnmapView(bPtr->view);
205 return;
208 if (bPtr->timer)
209 WMDeleteTimerHandler(bPtr->timer);
210 bPtr->timer = NULL;
212 if (bPtr->noDelayTimer)
213 WMDeleteTimerHandler(bPtr->noDelayTimer);
214 bPtr->noDelayTimer = NULL;
216 bPtr->forWindow = view->window;
218 if (bPtr->flags.noDelay) {
219 bPtr->timer = NULL;
221 showBalloon(view);
222 } else {
223 bPtr->timer = WMAddTimerHandler(bPtr->delay, showBalloon, view);
227 #define TOP 0
228 #define BOTTOM 1
229 #define LEFT 0
230 #define RIGHT 2
232 #define SPACE 12
234 static void drawBalloon(WMScreen * scr, Pixmap bitmap, Pixmap pix, int x, int y, int w, int h, int side)
236 Display *dpy = scr->display;
237 WMColor *white = WMWhiteColor(scr);
238 WMColor *black = WMBlackColor(scr);
239 GC bgc = scr->monoGC;
240 GC gc = WMColorGC(white);
241 int rad = h * 3 / 10;
242 XPoint pt[3], ipt[3];
243 int w1;
245 /* outline */
246 XSetForeground(dpy, bgc, 1);
248 XFillArc(dpy, bitmap, bgc, x, y, rad, rad, 90 * 64, 90 * 64);
249 XFillArc(dpy, bitmap, bgc, x, y + h - 1 - rad, rad, rad, 180 * 64, 90 * 64);
251 XFillArc(dpy, bitmap, bgc, x + w - 1 - rad, y, rad, rad, 0 * 64, 90 * 64);
252 XFillArc(dpy, bitmap, bgc, x + w - 1 - rad, y + h - 1 - rad, rad, rad, 270 * 64, 90 * 64);
254 XFillRectangle(dpy, bitmap, bgc, x, y + rad / 2, w, h - rad);
255 XFillRectangle(dpy, bitmap, bgc, x + rad / 2, y, w - rad, h);
257 /* interior */
258 XFillArc(dpy, pix, gc, x + 1, y + 1, rad, rad, 90 * 64, 90 * 64);
259 XFillArc(dpy, pix, gc, x + 1, y + h - 2 - rad, rad, rad, 180 * 64, 90 * 64);
261 XFillArc(dpy, pix, gc, x + w - 2 - rad, y + 1, rad, rad, 0 * 64, 90 * 64);
262 XFillArc(dpy, pix, gc, x + w - 2 - rad, y + h - 2 - rad, rad, rad, 270 * 64, 90 * 64);
264 XFillRectangle(dpy, pix, gc, x + 1, y + 1 + rad / 2, w - 2, h - 2 - rad);
265 XFillRectangle(dpy, pix, gc, x + 1 + rad / 2, y + 1, w - 2 - rad, h - 2);
267 if (side & BOTTOM) {
268 pt[0].y = y + h - 1;
269 pt[1].y = y + h - 1 + SPACE;
270 pt[2].y = y + h - 1;
271 ipt[0].y = pt[0].y - 1;
272 ipt[1].y = pt[1].y - 1;
273 ipt[2].y = pt[2].y - 1;
274 } else {
275 pt[0].y = y;
276 pt[1].y = y - SPACE;
277 pt[2].y = y;
278 ipt[0].y = pt[0].y + 1;
279 ipt[1].y = pt[1].y + 1;
280 ipt[2].y = pt[2].y + 1;
283 /*w1 = WMAX(h, 24); */
284 w1 = WMAX(h, 21);
286 if (side & RIGHT) {
287 pt[0].x = x + w - w1 + 2 * w1 / 16;
288 pt[1].x = x + w - w1 + 11 * w1 / 16;
289 pt[2].x = x + w - w1 + 7 * w1 / 16;
290 ipt[0].x = x + 1 + w - w1 + 2 * (w1 - 1) / 16;
291 ipt[1].x = x + 1 + w - w1 + 11 * (w1 - 1) / 16;
292 ipt[2].x = x + 1 + w - w1 + 7 * (w1 - 1) / 16;
293 /*ipt[0].x = pt[0].x+1;
294 ipt[1].x = pt[1].x;
295 ipt[2].x = pt[2].x; */
296 } else {
297 pt[0].x = x + w1 - 2 * w1 / 16;
298 pt[1].x = x + w1 - 11 * w1 / 16;
299 pt[2].x = x + w1 - 7 * w1 / 16;
300 ipt[0].x = x - 1 + w1 - 2 * (w1 - 1) / 16;
301 ipt[1].x = x - 1 + w1 - 11 * (w1 - 1) / 16;
302 ipt[2].x = x - 1 + w1 - 7 * (w1 - 1) / 16;
303 /*ipt[0].x = pt[0].x-1;
304 ipt[1].x = pt[1].x;
305 ipt[2].x = pt[2].x; */
308 XFillPolygon(dpy, bitmap, bgc, pt, 3, Convex, CoordModeOrigin);
309 XFillPolygon(dpy, pix, gc, ipt, 3, Convex, CoordModeOrigin);
311 /* fix outline */
312 XDrawLines(dpy, pix, WMColorGC(black), pt, 3, CoordModeOrigin);
313 if (side & RIGHT) {
314 pt[0].x++;
315 pt[2].x--;
316 } else {
317 pt[0].x--;
318 pt[2].x++;
320 XDrawLines(dpy, pix, WMColorGC(black), pt, 3, CoordModeOrigin);
322 WMReleaseColor(white);
323 WMReleaseColor(black);
326 static Pixmap makePixmap(WMScreen * scr, int width, int height, int side, Pixmap * mask)
328 Display *dpy = WMScreenDisplay(scr);
329 Pixmap bitmap;
330 Pixmap pixmap;
331 int x, y;
332 WMColor *black = WMBlackColor(scr);
334 bitmap = XCreatePixmap(dpy, scr->rootWin, width + SPACE, height + SPACE, 1);
336 XSetForeground(dpy, scr->monoGC, 0);
337 XFillRectangle(dpy, bitmap, scr->monoGC, 0, 0, width + SPACE, height + SPACE);
339 pixmap = XCreatePixmap(dpy, scr->rootWin, width + SPACE, height + SPACE, scr->depth);
341 XFillRectangle(dpy, pixmap, WMColorGC(black), 0, 0, width + SPACE, height + SPACE);
343 if (side & BOTTOM) {
344 y = 0;
345 } else {
346 y = SPACE;
348 x = 0;
350 drawBalloon(scr, bitmap, pixmap, x, y, width, height, side);
352 *mask = bitmap;
354 WMReleaseColor(black);
356 return pixmap;
359 static void showText(Balloon * bPtr, int x, int y, int w, int h, const char *text)
361 WMScreen *scr = bPtr->view->screen;
362 Display *dpy = WMScreenDisplay(scr);
363 int width;
364 int height;
365 Pixmap pixmap;
366 Pixmap mask;
367 WMFont *font = bPtr->font ? bPtr->font : scr->normalFont;
368 int textHeight;
369 int side = 0;
370 int ty;
371 int bx, by;
374 int w;
375 const char *ptr, *ptr2;
377 ptr2 = ptr = text;
378 width = 0;
379 while (ptr && ptr2) {
380 ptr2 = strchr(ptr, '\n');
381 if (ptr2) {
382 w = WMWidthOfString(font, ptr, ptr2 - ptr);
383 } else {
384 w = WMWidthOfString(font, ptr, strlen(ptr));
386 if (w > width)
387 width = w;
388 ptr = ptr2 + 1;
392 width += 16;
394 textHeight = W_GetTextHeight(font, text, width, False);
396 height = textHeight + 4;
398 if (height < 16)
399 height = 16;
400 if (width < height)
401 width = height;
403 if (x + width > scr->rootView->size.width) {
404 side = RIGHT;
405 bx = x - width + w / 2;
406 if (bx < 0)
407 bx = 0;
408 } else {
409 side = LEFT;
410 bx = x + w / 2;
412 if (bx + width > scr->rootView->size.width)
413 bx = scr->rootView->size.width - width;
415 if (y - (height + SPACE) < 0) {
416 side |= TOP;
417 by = y + h - 1;
418 ty = SPACE;
419 } else {
420 side |= BOTTOM;
421 by = y - (height + SPACE);
422 ty = 0;
424 pixmap = makePixmap(scr, width, height, side, &mask);
426 W_PaintText(bPtr->view, pixmap, font, 8, ty + (height - textHeight) / 2,
427 width, bPtr->flags.alignment,
428 bPtr->textColor ? bPtr->textColor : scr->black, False, text, strlen(text));
430 XSetWindowBackgroundPixmap(dpy, bPtr->view->window, pixmap);
432 W_ResizeView(bPtr->view, width, height + SPACE);
434 XFreePixmap(dpy, pixmap);
436 #ifdef USE_XSHAPE
437 XShapeCombineMask(dpy, bPtr->view->window, ShapeBounding, 0, 0, mask, ShapeSet);
438 #endif
439 XFreePixmap(dpy, mask);
441 W_MoveView(bPtr->view, bx, by);
443 W_MapView(bPtr->view);
446 static void handleEvents(XEvent * event, void *data)
448 Balloon *bPtr = (Balloon *) data;
450 switch (event->type) {
451 case DestroyNotify:
452 destroyBalloon(bPtr);
453 break;
457 static void destroyBalloon(Balloon * bPtr)
459 WMHashEnumerator e;
460 char *str;
462 e = WMEnumerateHashTable(bPtr->table);
464 while ((str = WMNextHashEnumeratorItem(&e))) {
465 wfree(str);
467 WMFreeHashTable(bPtr->table);
469 if (bPtr->textColor)
470 WMReleaseColor(bPtr->textColor);
472 if (bPtr->font)
473 WMReleaseFont(bPtr->font);
475 wfree(bPtr);