/* -*-c-*- */ /* Copyright (C) 2004 Olivier Chapuis */ /* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* ---------------------------- included header files ---------------------- */ #include "config.h" #include #include #include "libs/ftime.h" #include #include #include "defaults.h" #include "fvwmlib.h" #include "Strings.h" #include "PictureBase.h" #include "Flocale.h" #include "Graphics.h" #include "Colorset.h" #include "libs/FScreen.h" #include "FTips.h" /* ---------------------------- local definitions -------------------------- */ #define FVWM_TIPS_NOTHING 0 #define FVWM_TIPS_WAITING 1 #define FVWM_TIPS_MAPPED 2 /* ---------------------------- local macros ------------------------------- */ /* ---------------------------- imports ------------------------------------ */ /* ---------------------------- included code files ------------------------ */ /* ---------------------------- local types -------------------------------- */ /* ---------------------------- forward declarations ----------------------- */ /* ---------------------------- local variables ---------------------------- */ static Window win = None; static GC gc = None; static Window win_for; static ftips_config *current_config, *default_config; static char *label; static int state = FVWM_TIPS_NOTHING; static void *boxID = NULL; static FlocaleWinString fwin_string; static rectangle box; static unsigned long timeOut; static unsigned long onTime; static Atom _net_um_for = None; /* ---------------------------- exported variables (globals) --------------- */ /* ---------------------------- local functions ---------------------------- */ static unsigned long __get_time(void) { struct timeval t; gettimeofday(&t, NULL); return (1000*t.tv_sec + t.tv_usec/1000); } static void __initialize_window(Display *dpy) { XGCValues xgcv; unsigned long valuemask; XSetWindowAttributes attributes; Atom _net_um_window_type; long _net_um_window_type_tooltips; valuemask = CWOverrideRedirect | CWEventMask | CWColormap; attributes.override_redirect = True; attributes.event_mask = ExposureMask; attributes.colormap = Pcmap; win = XCreateWindow( dpy, DefaultRootWindow(dpy), 0, 0, 1, 1, current_config->border_width, Pdepth, InputOutput, Pvisual, valuemask, &attributes); _net_um_window_type = XInternAtom( dpy, "_NET_UM_WINDOW_TYPE", False); _net_um_window_type_tooltips = XInternAtom( dpy, "_NET_UM_WINDOW_TYPE_TOOLTIPS", False); XChangeProperty( dpy, win, _net_um_window_type, XA_ATOM, 32, PropModeReplace, (unsigned char *) &_net_um_window_type_tooltips, 1); _net_um_for = XInternAtom(dpy, "_NET_UM_FOR", False); gc = fvwmlib_XCreateGC(dpy, win, 0, &xgcv); return; } static void __setup_cs(Display *dpy) { XSetWindowAttributes xswa; unsigned long valuemask = 0; if (current_config->colorset > -1) { xswa.border_pixel = Colorset[current_config->colorset].fg; xswa.background_pixel = Colorset[current_config->colorset].bg; if (Colorset[current_config->colorset].pixmap) { /* set later */ xswa.background_pixmap = None; valuemask = CWBackPixmap | CWBorderPixel;; } else { valuemask = CWBackPixel | CWBorderPixel; } } else { xswa.border_pixel = current_config->border_pixel; xswa.background_pixel = current_config->bg; valuemask = CWBackPixel | CWBorderPixel; } XChangeWindowAttributes(dpy, win, valuemask, &xswa); } static void __setup_gc(Display *dpy) { XGCValues xgcv; unsigned long valuemask; valuemask = GCForeground; if (current_config->colorset > -1) { xgcv.foreground = Colorset[current_config->colorset].fg; } else { xgcv.foreground = current_config->fg; } if (current_config->Ffont && current_config->Ffont->font != NULL) { xgcv.font = current_config->Ffont->font->fid; valuemask |= GCFont; } XChangeGC(dpy, gc, valuemask, &xgcv); return; } static void __draw(Display *dpy) { if (!current_config->Ffont) { return; } fwin_string.str = label; fwin_string.win = win; fwin_string.gc = gc; if (current_config->colorset > -1) { fwin_string.colorset = &Colorset[current_config->colorset]; fwin_string.flags.has_colorset = True; } else { fwin_string.flags.has_colorset = False; } fwin_string.x = 2; fwin_string.y = current_config->Ffont->ascent; FlocaleDrawString(dpy, current_config->Ffont, &fwin_string, 0); return; } static void __map_window(Display *dpy) { rectangle new_g; rectangle screen_g; Window dummy; fscreen_scr_arg *fsarg = NULL; /* for now no xinerama support */ ftips_placement_t placement; int x,y; static int border_width = 1; static Window win_f = None; if (border_width != current_config->border_width) { XSetWindowBorderWidth(dpy, win, current_config->border_width); border_width = current_config->border_width; } FScreenGetScrRect( fsarg, FSCREEN_GLOBAL, &screen_g.x, &screen_g.y, &screen_g.width, &screen_g.height); new_g.height = ((current_config->Ffont)? current_config->Ffont->height:0) + 1; new_g.width = 4; if (label && current_config->Ffont) { new_g.width += FlocaleTextWidth( current_config->Ffont, label, strlen(label)); } if (current_config->placement != FTIPS_PLACEMENT_AUTO_UPDOWN && current_config->placement != FTIPS_PLACEMENT_AUTO_LEFTRIGHT) { placement = current_config->placement; } else { XTranslateCoordinates( dpy, win_for, DefaultRootWindow(dpy), box.x, box.y, &x, &y, &dummy); if (current_config->placement == FTIPS_PLACEMENT_AUTO_UPDOWN) { if (y + box.height/2 >= screen_g.height/2) { placement = FTIPS_PLACEMENT_UP; } else { placement = FTIPS_PLACEMENT_DOWN; } } else { if (x + box.width/2 >= screen_g.width/2) { placement = FTIPS_PLACEMENT_LEFT; } else { placement = FTIPS_PLACEMENT_RIGHT; } } } if (placement == FTIPS_PLACEMENT_RIGHT || placement == FTIPS_PLACEMENT_LEFT) { if (current_config->justification == FTIPS_JUSTIFICATION_CENTER) { y = box.y + (box.height / 2) - (new_g.height / 2) - current_config->border_width; } else if (current_config->justification == FTIPS_JUSTIFICATION_RIGHT_DOWN) { y = box.y + box.height - new_g.height - (2 * current_config->border_width) - current_config->justification_offset; } else /* LEFT_UP */ { y = box.y + current_config->justification_offset; } } else /* placement == FTIPS_PLACEMENT_DOWN || placement == FTIPS_PLACEMENT_UP */ { if (current_config->justification == FTIPS_JUSTIFICATION_CENTER) { x = box.x + (box.width / 2) - (new_g.width / 2) - current_config->border_width; } else if (current_config->justification == FTIPS_JUSTIFICATION_RIGHT_DOWN) { x = box.x + box.width - new_g.width - (2 * current_config->border_width) - current_config->justification_offset; } else /* LEFT_UP */ { x = box.x + current_config->justification_offset; } } if (placement == FTIPS_PLACEMENT_RIGHT) { x = box.x + box.width + current_config->placement_offset + 1; } else if (placement == FTIPS_PLACEMENT_LEFT) { x = box.x - current_config->placement_offset - new_g.width - (2 * current_config->border_width) - 1; } else if (placement == FTIPS_PLACEMENT_DOWN) { y = box.y + box.height + current_config->placement_offset - 0; } else /* UP */ { y = box.y - current_config->placement_offset - new_g.height + 0 - (2 * current_config->border_width); } XTranslateCoordinates( dpy, win_for, DefaultRootWindow(dpy), x, y, &new_g.x, &new_g.y, &dummy); if (placement == FTIPS_PLACEMENT_RIGHT || placement == FTIPS_PLACEMENT_LEFT) { int x1,y1,l1,l2; if (new_g.x < 2) { x = box.x + box.width + current_config->placement_offset + 1; XTranslateCoordinates( dpy, win_for, DefaultRootWindow(dpy), x, y, &x1, &y1, &dummy); /* */ l1 = new_g.width + new_g.x - 2; l2 = screen_g.width - (x1 + new_g.width) - current_config->border_width - 2; if (l2 > l1) { new_g.x = x1; } } else if (new_g.x + new_g.width > screen_g.width - (2 * current_config->border_width) - 2) { x = box.x - current_config->placement_offset - new_g.width - (2 * current_config->border_width) - 1; XTranslateCoordinates( dpy, win_for, DefaultRootWindow(dpy), x, y, &x1, &y1, &dummy); /* */ l1 = new_g.width + x1 - 2; l2 = screen_g.width - (new_g.x + new_g.width) - (2 * current_config->border_width) - 2; if (l1 > l2) { new_g.x = x1; } } if ( new_g.y < 2 ) { new_g.y = 2; } else if (new_g.y + new_g.height > screen_g.height - (2 * current_config->border_width) - 2) { new_g.y = screen_g.height - new_g.height - (2 * current_config->border_width) - 2; } } else /* placement == FTIPS_PLACEMENT_DOWN || placement == FTIPS_PLACEMENT_UP */ { if (new_g.y < 2) { y = box.y + box.height + current_config->placement_offset - 0; XTranslateCoordinates( dpy, win_for, DefaultRootWindow(dpy), x, y, &new_g.x, &new_g.y, &dummy); } else if (new_g.y + new_g.height > screen_g.height - (2 * current_config->border_width) - 2) { y = box.y - current_config->placement_offset - new_g.height + 0 - (2 * current_config->border_width); XTranslateCoordinates( dpy, win_for, DefaultRootWindow(dpy), x, y, &new_g.x, &new_g.y, &dummy); } if ( new_g.x < 2 ) { new_g.x = 2; } else if (new_g.x + new_g.width > screen_g.width - (2 * current_config->border_width) - 2) { new_g.x = screen_g.width - new_g.width - (2 * current_config->border_width) - 2; } } /* make changes to window */ XMoveResizeWindow( dpy, win, new_g.x, new_g.y, new_g.width, new_g.height); __setup_gc(dpy); if (current_config->colorset > -1) { SetWindowBackground( dpy, win, new_g.width, new_g.height, &Colorset[current_config->colorset], Pdepth, gc, True); } else { XSetWindowBackground (dpy, win, current_config->bg); } if (current_config->border_width > 0) { XSetWindowBorder( dpy, win, Colorset[current_config->colorset].fg); } if (state != FVWM_TIPS_MAPPED && win_f != win_for) { long l_win_for; l_win_for = win_for; XChangeProperty( dpy, win, _net_um_for, XA_WINDOW, 32, PropModeReplace, (unsigned char *) &l_win_for, 1); win_f = win_for; } XMapRaised(dpy, win); state = FVWM_TIPS_MAPPED; return; } /* ---------------------------- interface functions ------------------------ */ Bool FTipsInit(Display *dpy) { current_config = default_config = FTipsNewConfig(); __initialize_window(dpy); if (gc == None || win == None) { return False; } __setup_cs(dpy); __setup_gc(dpy); memset(&fwin_string, 0, sizeof(fwin_string)); return True; } ftips_config *FTipsNewConfig(void) { ftips_config *fc; fc = (ftips_config *)safemalloc(sizeof(ftips_config)); memset(fc, 0, sizeof(ftips_config)); /* use colorset 0 as default */ fc->border_width = FTIPS_DEFAULT_BORDER_WIDTH; fc->placement = FTIPS_DEFAULT_PLACEMENT; fc->justification = FTIPS_DEFAULT_JUSTIFICATION; fc->placement_offset = FTIPS_DEFAULT_PLACEMENT_OFFSET; fc->justification_offset = FTIPS_DEFAULT_JUSTIFICATION_OFFSET; fc->delay = 1000; fc->mapped_delay = 300; return fc; } void FTipsOn( Display *dpy, Window win_f, ftips_config *fc, void *id, char *str, int x, int y, int w, int h) { unsigned long delay; box.x = x; box.y = y; box.width = w; box.height = h; if (fc == NULL) { fc = default_config; } current_config = fc; if (label != NULL) { free(label); } CopyString(&label, str); win_for = win_f; if (id == boxID) { if (id && state == FVWM_TIPS_WAITING) { FTipsCheck(dpy); } return; } onTime = __get_time(); if (state == FVWM_TIPS_MAPPED) { FTipsCancel(dpy); delay = fc->mapped_delay; } else { delay = fc->delay; } boxID = id; timeOut = onTime + delay; state = FVWM_TIPS_WAITING; if (delay == 0) { FTipsCheck(dpy); } return; } void FTipsCancel(Display *dpy) { if (state == FVWM_TIPS_MAPPED && win != None) { XUnmapWindow(dpy, win); } boxID = 0; state = FVWM_TIPS_NOTHING; } unsigned long FTipsCheck(Display *dpy) { unsigned long ct; if (state != FVWM_TIPS_WAITING || win == None) { return 0; } ct = __get_time(); if (ct >= timeOut) { __map_window(dpy); XFlush(dpy); state = FVWM_TIPS_MAPPED; return 0; } else { XFlush(dpy); return timeOut - ct; } } Bool FTipsExpose(Display *dpy, XEvent *ev) { int ex,ey,ex2,ey2; if (win == None || ev->xany.window != win) { return False; } ex = ev->xexpose.x; ey = ev->xexpose.y; ex2 = ev->xexpose.x + ev->xexpose.width; ey2= ev->xexpose.y + ev->xexpose.height; while (FCheckTypedWindowEvent(dpy, ev->xany.window, Expose, ev)) { ex = min(ex, ev->xexpose.x); ey = min(ey, ev->xexpose.y); ex2 = max(ex2, ev->xexpose.x + ev->xexpose.width); ey2= max(ey2 , ev->xexpose.y + ev->xexpose.height); } #if 0 fprintf ( stderr, "\tExpose: %i,%i,%i,%i %i\n", ex, ey, ex2-ex, ey2-ey, ev->xexpose.count); #endif __draw(dpy); return True; } Bool FTipsHandleEvents(Display *dpy, XEvent *ev) { if (ev->xany.window != win) { return False; } switch(ev->type) { case Expose: FTipsExpose(dpy, ev); break; default: break; } return True; } void FTipsUpdateLabel(Display *dpy, char *str) { if (state != FVWM_TIPS_MAPPED && state != FVWM_TIPS_WAITING) { return; } if (label) { free(label); } CopyString(&label, str); if (state == FVWM_TIPS_MAPPED) { __map_window(dpy); __draw(dpy); } } void FTipsColorsetChanged(Display *dpy, int cs) { if (state != FVWM_TIPS_MAPPED || cs != current_config->colorset) { return; } __map_window(dpy); __draw(dpy); }