#include "wconfig.h" #include "WINGsP.h" #ifdef SHAPE #include #endif typedef struct W_Balloon { W_View *view; WMHashTable *table; /* Table from view ptr to text */ WMColor *backColor; WMColor *textColor; WMFont *font; WMHandlerID timer; /* timer for showing balloon */ WMHandlerID noDelayTimer; int delay; Window forWindow; /* window for which the balloon * is being show in the moment */ struct { WMAlignment alignment:2; unsigned enabled:1; unsigned noDelay:1; } flags; } Balloon; #define DEFAULT_WIDTH 60 #define DEFAULT_HEIGHT 14 #define DEFAULT_ALIGNMENT WALeft #define DEFAULT_DELAY 500 #define NO_DELAY_DELAY 150 static void destroyBalloon(Balloon * bPtr); static void handleEvents(XEvent * event, void *data); static void showText(Balloon * bPtr, int x, int y, int w, int h, char *text); struct W_Balloon *W_CreateBalloon(WMScreen * scr) { Balloon *bPtr; bPtr = wmalloc(sizeof(Balloon)); bPtr->view = W_CreateUnmanagedTopView(scr); if (!bPtr->view) { wfree(bPtr); return NULL; } bPtr->view->self = bPtr; bPtr->textColor = WMRetainColor(bPtr->view->screen->black); WMCreateEventHandler(bPtr->view, StructureNotifyMask, handleEvents, bPtr); W_ResizeView(bPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT); bPtr->flags.alignment = DEFAULT_ALIGNMENT; bPtr->table = WMCreateHashTable(WMIntHashCallbacks); bPtr->delay = DEFAULT_DELAY; bPtr->flags.enabled = 1; return bPtr; } void WMSetBalloonTextAlignment(WMScreen * scr, WMAlignment alignment) { scr->balloon->flags.alignment = alignment; } void WMSetBalloonTextForView(char *text, WMView * view) { char *oldText = NULL; WMScreen *scr = view->screen; if (text) { oldText = WMHashInsert(scr->balloon->table, view, wstrdup(text)); } else { oldText = WMHashGet(scr->balloon->table, view); WMHashRemove(scr->balloon->table, view); } if (oldText) { wfree(oldText); } } void WMSetBalloonFont(WMScreen * scr, WMFont * font) { Balloon *bPtr = scr->balloon; if (bPtr->font != NULL) WMReleaseFont(bPtr->font); if (font) bPtr->font = WMRetainFont(font); else bPtr->font = NULL; } void WMSetBalloonTextColor(WMScreen * scr, WMColor * color) { Balloon *bPtr = scr->balloon; if (bPtr->textColor) WMReleaseColor(bPtr->textColor); bPtr->textColor = WMRetainColor(color); } void WMSetBalloonDelay(WMScreen * scr, int delay) { scr->balloon->delay = delay; } void WMSetBalloonEnabled(WMScreen * scr, Bool flag) { scr->balloon->flags.enabled = ((flag == 0) ? 0 : 1); W_UnmapView(scr->balloon->view); } static void clearNoDelay(void *data) { Balloon *bPtr = (Balloon *) data; bPtr->flags.noDelay = 0; bPtr->noDelayTimer = NULL; } void W_BalloonHandleLeaveView(WMView * view) { Balloon *bPtr = view->screen->balloon; if (bPtr->forWindow == view->window) { if (bPtr->view->flags.mapped) { W_UnmapView(bPtr->view); bPtr->noDelayTimer = WMAddTimerHandler(NO_DELAY_DELAY, clearNoDelay, bPtr); } if (bPtr->timer) WMDeleteTimerHandler(bPtr->timer); bPtr->timer = NULL; bPtr->forWindow = None; } } /* * botar balao perto do cursor * so mapear balao se o mouse ficar parado pelo delay * */ static void showBalloon(void *data) { char *text; WMView *view = (WMView *) data; Balloon *bPtr = view->screen->balloon; int x, y; Window foo; bPtr->timer = NULL; text = WMHashGet(bPtr->table, view); if (!text) return; XTranslateCoordinates(view->screen->display, view->window, view->screen->rootWin, 0, 0, &x, &y, &foo); if (!bPtr->view->flags.realized) W_RealizeView(bPtr->view); showText(bPtr, x, y, view->size.width, view->size.height, text); bPtr->flags.noDelay = 1; } void W_BalloonHandleEnterView(WMView * view) { Balloon *bPtr = view->screen->balloon; char *text; if (!bPtr->flags.enabled) return; text = WMHashGet(bPtr->table, view); if (!text) { if (bPtr->view->flags.realized) W_UnmapView(bPtr->view); return; } if (bPtr->timer) WMDeleteTimerHandler(bPtr->timer); bPtr->timer = NULL; if (bPtr->noDelayTimer) WMDeleteTimerHandler(bPtr->noDelayTimer); bPtr->noDelayTimer = NULL; bPtr->forWindow = view->window; if (bPtr->flags.noDelay) { bPtr->timer = NULL; showBalloon(view); } else { bPtr->timer = WMAddTimerHandler(bPtr->delay, showBalloon, view); } } #define TOP 0 #define BOTTOM 1 #define LEFT 0 #define RIGHT 2 #define TLEFT (TOP|LEFT) #define TRIGHT (TOP|RIGHT) #define BLEFT (BOTTOM|LEFT) #define BRIGHT (BOTTOM|RIGHT) #define SPACE 12 static void drawBalloon(WMScreen * scr, Pixmap bitmap, Pixmap pix, int x, int y, int w, int h, int side) { Display *dpy = scr->display; WMColor *white = WMWhiteColor(scr); WMColor *black = WMBlackColor(scr); GC bgc = scr->monoGC; GC gc = WMColorGC(white); int rad = h * 3 / 10; XPoint pt[3], ipt[3]; int w1; /* outline */ XSetForeground(dpy, bgc, 1); XFillArc(dpy, bitmap, bgc, x, y, rad, rad, 90 * 64, 90 * 64); XFillArc(dpy, bitmap, bgc, x, y + h - 1 - rad, rad, rad, 180 * 64, 90 * 64); XFillArc(dpy, bitmap, bgc, x + w - 1 - rad, y, rad, rad, 0 * 64, 90 * 64); XFillArc(dpy, bitmap, bgc, x + w - 1 - rad, y + h - 1 - rad, rad, rad, 270 * 64, 90 * 64); XFillRectangle(dpy, bitmap, bgc, x, y + rad / 2, w, h - rad); XFillRectangle(dpy, bitmap, bgc, x + rad / 2, y, w - rad, h); /* interior */ XFillArc(dpy, pix, gc, x + 1, y + 1, rad, rad, 90 * 64, 90 * 64); XFillArc(dpy, pix, gc, x + 1, y + h - 2 - rad, rad, rad, 180 * 64, 90 * 64); XFillArc(dpy, pix, gc, x + w - 2 - rad, y + 1, rad, rad, 0 * 64, 90 * 64); XFillArc(dpy, pix, gc, x + w - 2 - rad, y + h - 2 - rad, rad, rad, 270 * 64, 90 * 64); XFillRectangle(dpy, pix, gc, x + 1, y + 1 + rad / 2, w - 2, h - 2 - rad); XFillRectangle(dpy, pix, gc, x + 1 + rad / 2, y + 1, w - 2 - rad, h - 2); if (side & BOTTOM) { pt[0].y = y + h - 1; pt[1].y = y + h - 1 + SPACE; pt[2].y = y + h - 1; ipt[0].y = pt[0].y - 1; ipt[1].y = pt[1].y - 1; ipt[2].y = pt[2].y - 1; } else { pt[0].y = y; pt[1].y = y - SPACE; pt[2].y = y; ipt[0].y = pt[0].y + 1; ipt[1].y = pt[1].y + 1; ipt[2].y = pt[2].y + 1; } /*w1 = WMAX(h, 24); */ w1 = WMAX(h, 21); if (side & RIGHT) { pt[0].x = x + w - w1 + 2 * w1 / 16; pt[1].x = x + w - w1 + 11 * w1 / 16; pt[2].x = x + w - w1 + 7 * w1 / 16; ipt[0].x = x + 1 + w - w1 + 2 * (w1 - 1) / 16; ipt[1].x = x + 1 + w - w1 + 11 * (w1 - 1) / 16; ipt[2].x = x + 1 + w - w1 + 7 * (w1 - 1) / 16; /*ipt[0].x = pt[0].x+1; ipt[1].x = pt[1].x; ipt[2].x = pt[2].x; */ } else { pt[0].x = x + w1 - 2 * w1 / 16; pt[1].x = x + w1 - 11 * w1 / 16; pt[2].x = x + w1 - 7 * w1 / 16; ipt[0].x = x - 1 + w1 - 2 * (w1 - 1) / 16; ipt[1].x = x - 1 + w1 - 11 * (w1 - 1) / 16; ipt[2].x = x - 1 + w1 - 7 * (w1 - 1) / 16; /*ipt[0].x = pt[0].x-1; ipt[1].x = pt[1].x; ipt[2].x = pt[2].x; */ } XFillPolygon(dpy, bitmap, bgc, pt, 3, Convex, CoordModeOrigin); XFillPolygon(dpy, pix, gc, ipt, 3, Convex, CoordModeOrigin); /* fix outline */ XDrawLines(dpy, pix, WMColorGC(black), pt, 3, CoordModeOrigin); if (side & RIGHT) { pt[0].x++; pt[2].x--; } else { pt[0].x--; pt[2].x++; } XDrawLines(dpy, pix, WMColorGC(black), pt, 3, CoordModeOrigin); WMReleaseColor(white); WMReleaseColor(black); } static Pixmap makePixmap(WMScreen * scr, int width, int height, int side, Pixmap * mask) { Display *dpy = WMScreenDisplay(scr); Pixmap bitmap; Pixmap pixmap; int x, y; WMColor *black = WMBlackColor(scr); bitmap = XCreatePixmap(dpy, scr->rootWin, width + SPACE, height + SPACE, 1); XSetForeground(dpy, scr->monoGC, 0); XFillRectangle(dpy, bitmap, scr->monoGC, 0, 0, width + SPACE, height + SPACE); pixmap = XCreatePixmap(dpy, scr->rootWin, width + SPACE, height + SPACE, scr->depth); XFillRectangle(dpy, pixmap, WMColorGC(black), 0, 0, width + SPACE, height + SPACE); if (side & BOTTOM) { y = 0; } else { y = SPACE; } x = 0; drawBalloon(scr, bitmap, pixmap, x, y, width, height, side); *mask = bitmap; WMReleaseColor(black); return pixmap; } static void showText(Balloon * bPtr, int x, int y, int w, int h, char *text) { WMScreen *scr = bPtr->view->screen; Display *dpy = WMScreenDisplay(scr); int width; int height; Pixmap pixmap; Pixmap mask; WMFont *font = bPtr->font ? bPtr->font : scr->normalFont; int textHeight; int side = 0; int ty; int bx, by; { int w; char *ptr, *ptr2; ptr2 = ptr = text; width = 0; while (ptr && ptr2) { ptr2 = strchr(ptr, '\n'); if (ptr2) { w = WMWidthOfString(font, ptr, ptr2 - ptr); } else { w = WMWidthOfString(font, ptr, strlen(ptr)); } if (w > width) width = w; ptr = ptr2 + 1; } } width += 16; textHeight = W_GetTextHeight(font, text, width, False); height = textHeight + 4; if (height < 16) height = 16; if (width < height) width = height; if (x + width > scr->rootView->size.width) { side = RIGHT; bx = x - width + w / 2; if (bx < 0) bx = 0; } else { side = LEFT; bx = x + w / 2; } if (bx + width > scr->rootView->size.width) bx = scr->rootView->size.width - width; if (y - (height + SPACE) < 0) { side |= TOP; by = y + h - 1; ty = SPACE; } else { side |= BOTTOM; by = y - (height + SPACE); ty = 0; } pixmap = makePixmap(scr, width, height, side, &mask); W_PaintText(bPtr->view, pixmap, font, 8, ty + (height - textHeight) / 2, width, bPtr->flags.alignment, bPtr->textColor ? bPtr->textColor : scr->black, False, text, strlen(text)); XSetWindowBackgroundPixmap(dpy, bPtr->view->window, pixmap); W_ResizeView(bPtr->view, width, height + SPACE); XFreePixmap(dpy, pixmap); #ifdef SHAPE XShapeCombineMask(dpy, bPtr->view->window, ShapeBounding, 0, 0, mask, ShapeSet); #endif XFreePixmap(dpy, mask); W_MoveView(bPtr->view, bx, by); W_MapView(bPtr->view); } static void handleEvents(XEvent * event, void *data) { Balloon *bPtr = (Balloon *) data; switch (event->type) { case DestroyNotify: destroyBalloon(bPtr); break; } } static void destroyBalloon(Balloon * bPtr) { WMHashEnumerator e; char *str; e = WMEnumerateHashTable(bPtr->table); while ((str = WMNextHashEnumeratorItem(&e))) { wfree(str); } WMFreeHashTable(bPtr->table); if (bPtr->textColor) WMReleaseColor(bPtr->textColor); if (bPtr->font) WMReleaseFont(bPtr->font); wfree(bPtr); }