Avoid icon change to default on winspector save
[wmaker-crm.git] / WINGs / wballoon.c
blobde8c5026646e1024a9ec31b7b8f79100f5fd7ee3
2 #include "wconfig.h"
3 #include "WINGsP.h"
5 #ifdef SHAPE
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, 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(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 TLEFT (TOP|LEFT)
233 #define TRIGHT (TOP|RIGHT)
234 #define BLEFT (BOTTOM|LEFT)
235 #define BRIGHT (BOTTOM|RIGHT)
237 #define SPACE 12
239 static void drawBalloon(WMScreen * scr, Pixmap bitmap, Pixmap pix, int x, int y, int w, int h, int side)
241 Display *dpy = scr->display;
242 WMColor *white = WMWhiteColor(scr);
243 WMColor *black = WMBlackColor(scr);
244 GC bgc = scr->monoGC;
245 GC gc = WMColorGC(white);
246 int rad = h * 3 / 10;
247 XPoint pt[3], ipt[3];
248 int w1;
250 /* outline */
251 XSetForeground(dpy, bgc, 1);
253 XFillArc(dpy, bitmap, bgc, x, y, rad, rad, 90 * 64, 90 * 64);
254 XFillArc(dpy, bitmap, bgc, x, y + h - 1 - rad, rad, rad, 180 * 64, 90 * 64);
256 XFillArc(dpy, bitmap, bgc, x + w - 1 - rad, y, rad, rad, 0 * 64, 90 * 64);
257 XFillArc(dpy, bitmap, bgc, x + w - 1 - rad, y + h - 1 - rad, rad, rad, 270 * 64, 90 * 64);
259 XFillRectangle(dpy, bitmap, bgc, x, y + rad / 2, w, h - rad);
260 XFillRectangle(dpy, bitmap, bgc, x + rad / 2, y, w - rad, h);
262 /* interior */
263 XFillArc(dpy, pix, gc, x + 1, y + 1, rad, rad, 90 * 64, 90 * 64);
264 XFillArc(dpy, pix, gc, x + 1, y + h - 2 - rad, rad, rad, 180 * 64, 90 * 64);
266 XFillArc(dpy, pix, gc, x + w - 2 - rad, y + 1, rad, rad, 0 * 64, 90 * 64);
267 XFillArc(dpy, pix, gc, x + w - 2 - rad, y + h - 2 - rad, rad, rad, 270 * 64, 90 * 64);
269 XFillRectangle(dpy, pix, gc, x + 1, y + 1 + rad / 2, w - 2, h - 2 - rad);
270 XFillRectangle(dpy, pix, gc, x + 1 + rad / 2, y + 1, w - 2 - rad, h - 2);
272 if (side & BOTTOM) {
273 pt[0].y = y + h - 1;
274 pt[1].y = y + h - 1 + SPACE;
275 pt[2].y = y + h - 1;
276 ipt[0].y = pt[0].y - 1;
277 ipt[1].y = pt[1].y - 1;
278 ipt[2].y = pt[2].y - 1;
279 } else {
280 pt[0].y = y;
281 pt[1].y = y - SPACE;
282 pt[2].y = y;
283 ipt[0].y = pt[0].y + 1;
284 ipt[1].y = pt[1].y + 1;
285 ipt[2].y = pt[2].y + 1;
288 /*w1 = WMAX(h, 24); */
289 w1 = WMAX(h, 21);
291 if (side & RIGHT) {
292 pt[0].x = x + w - w1 + 2 * w1 / 16;
293 pt[1].x = x + w - w1 + 11 * w1 / 16;
294 pt[2].x = x + w - w1 + 7 * w1 / 16;
295 ipt[0].x = x + 1 + w - w1 + 2 * (w1 - 1) / 16;
296 ipt[1].x = x + 1 + w - w1 + 11 * (w1 - 1) / 16;
297 ipt[2].x = x + 1 + w - w1 + 7 * (w1 - 1) / 16;
298 /*ipt[0].x = pt[0].x+1;
299 ipt[1].x = pt[1].x;
300 ipt[2].x = pt[2].x; */
301 } else {
302 pt[0].x = x + w1 - 2 * w1 / 16;
303 pt[1].x = x + w1 - 11 * w1 / 16;
304 pt[2].x = x + w1 - 7 * w1 / 16;
305 ipt[0].x = x - 1 + w1 - 2 * (w1 - 1) / 16;
306 ipt[1].x = x - 1 + w1 - 11 * (w1 - 1) / 16;
307 ipt[2].x = x - 1 + w1 - 7 * (w1 - 1) / 16;
308 /*ipt[0].x = pt[0].x-1;
309 ipt[1].x = pt[1].x;
310 ipt[2].x = pt[2].x; */
313 XFillPolygon(dpy, bitmap, bgc, pt, 3, Convex, CoordModeOrigin);
314 XFillPolygon(dpy, pix, gc, ipt, 3, Convex, CoordModeOrigin);
316 /* fix outline */
317 XDrawLines(dpy, pix, WMColorGC(black), pt, 3, CoordModeOrigin);
318 if (side & RIGHT) {
319 pt[0].x++;
320 pt[2].x--;
321 } else {
322 pt[0].x--;
323 pt[2].x++;
325 XDrawLines(dpy, pix, WMColorGC(black), pt, 3, CoordModeOrigin);
327 WMReleaseColor(white);
328 WMReleaseColor(black);
331 static Pixmap makePixmap(WMScreen * scr, int width, int height, int side, Pixmap * mask)
333 Display *dpy = WMScreenDisplay(scr);
334 Pixmap bitmap;
335 Pixmap pixmap;
336 int x, y;
337 WMColor *black = WMBlackColor(scr);
339 bitmap = XCreatePixmap(dpy, scr->rootWin, width + SPACE, height + SPACE, 1);
341 XSetForeground(dpy, scr->monoGC, 0);
342 XFillRectangle(dpy, bitmap, scr->monoGC, 0, 0, width + SPACE, height + SPACE);
344 pixmap = XCreatePixmap(dpy, scr->rootWin, width + SPACE, height + SPACE, scr->depth);
346 XFillRectangle(dpy, pixmap, WMColorGC(black), 0, 0, width + SPACE, height + SPACE);
348 if (side & BOTTOM) {
349 y = 0;
350 } else {
351 y = SPACE;
353 x = 0;
355 drawBalloon(scr, bitmap, pixmap, x, y, width, height, side);
357 *mask = bitmap;
359 WMReleaseColor(black);
361 return pixmap;
364 static void showText(Balloon * bPtr, int x, int y, int w, int h, char *text)
366 WMScreen *scr = bPtr->view->screen;
367 Display *dpy = WMScreenDisplay(scr);
368 int width;
369 int height;
370 Pixmap pixmap;
371 Pixmap mask;
372 WMFont *font = bPtr->font ? bPtr->font : scr->normalFont;
373 int textHeight;
374 int side = 0;
375 int ty;
376 int bx, by;
379 int w;
380 char *ptr, *ptr2;
382 ptr2 = ptr = text;
383 width = 0;
384 while (ptr && ptr2) {
385 ptr2 = strchr(ptr, '\n');
386 if (ptr2) {
387 w = WMWidthOfString(font, ptr, ptr2 - ptr);
388 } else {
389 w = WMWidthOfString(font, ptr, strlen(ptr));
391 if (w > width)
392 width = w;
393 ptr = ptr2 + 1;
397 width += 16;
399 textHeight = W_GetTextHeight(font, text, width, False);
401 height = textHeight + 4;
403 if (height < 16)
404 height = 16;
405 if (width < height)
406 width = height;
408 if (x + width > scr->rootView->size.width) {
409 side = RIGHT;
410 bx = x - width + w / 2;
411 if (bx < 0)
412 bx = 0;
413 } else {
414 side = LEFT;
415 bx = x + w / 2;
417 if (bx + width > scr->rootView->size.width)
418 bx = scr->rootView->size.width - width;
420 if (y - (height + SPACE) < 0) {
421 side |= TOP;
422 by = y + h - 1;
423 ty = SPACE;
424 } else {
425 side |= BOTTOM;
426 by = y - (height + SPACE);
427 ty = 0;
429 pixmap = makePixmap(scr, width, height, side, &mask);
431 W_PaintText(bPtr->view, pixmap, font, 8, ty + (height - textHeight) / 2,
432 width, bPtr->flags.alignment,
433 bPtr->textColor ? bPtr->textColor : scr->black, False, text, strlen(text));
435 XSetWindowBackgroundPixmap(dpy, bPtr->view->window, pixmap);
437 W_ResizeView(bPtr->view, width, height + SPACE);
439 XFreePixmap(dpy, pixmap);
441 #ifdef SHAPE
442 XShapeCombineMask(dpy, bPtr->view->window, ShapeBounding, 0, 0, mask, ShapeSet);
443 #endif
444 XFreePixmap(dpy, mask);
446 W_MoveView(bPtr->view, bx, by);
448 W_MapView(bPtr->view);
451 static void handleEvents(XEvent * event, void *data)
453 Balloon *bPtr = (Balloon *) data;
455 switch (event->type) {
456 case DestroyNotify:
457 destroyBalloon(bPtr);
458 break;
462 static void destroyBalloon(Balloon * bPtr)
464 WMHashEnumerator e;
465 char *str;
467 e = WMEnumerateHashTable(bPtr->table);
469 while ((str = WMNextHashEnumeratorItem(&e))) {
470 wfree(str);
472 WMFreeHashTable(bPtr->table);
474 if (bPtr->textColor)
475 WMReleaseColor(bPtr->textColor);
477 if (bPtr->font)
478 WMReleaseFont(bPtr->font);
480 wfree(bPtr);