/* icon.c - window icon and dock and appicon parent * * Window Maker window manager * * Copyright (c) 1997, 1998 Alfredo K. Kojima * * 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. */ #include "wconfig.h" #include #include #include #include #include #include #include #include #include #include "WindowMaker.h" #include "wcore.h" #include "texture.h" #include "window.h" #include "icon.h" #include "actions.h" #include "funcs.h" #include "stacking.h" #include "application.h" #include "defaults.h" #include "appicon.h" /**** Global variables ****/ extern WPreferences wPreferences; #define MOD_MASK wPreferences.modifier_mask extern Cursor wCursor[WCUR_LAST]; static void miniwindowExpose(WObjDescriptor *desc, XEvent *event); static void miniwindowMouseDown(WObjDescriptor *desc, XEvent *event); static void miniwindowDblClick(WObjDescriptor *desc, XEvent *event); /****** Notification Observers ******/ static void appearanceObserver(void *self, WMNotification *notif) { WIcon *icon = (WIcon*)self; int flags = (int)WMGetNotificationClientData(notif); if (flags & WTextureSettings) { icon->force_paint = 1; } if (flags & WFontSettings) { icon->force_paint = 1; } /* if (flags & WColorSettings) { } */ wIconPaint(icon); /* so that the appicon expose handlers will paint the appicon specific * stuff */ XClearArea(dpy, icon->core->window, 0, 0, 1, 1, True); } static void tileObserver(void *self, WMNotification *notif) { WIcon *icon = (WIcon*)self; icon->force_paint = 1; wIconPaint(icon); XClearArea(dpy, icon->core->window, 0, 0, 1, 1, True); } /************************************/ INLINE static void getSize(Drawable d, unsigned int *w, unsigned int *h, unsigned int *dep) { Window rjunk; int xjunk, yjunk; unsigned int bjunk; XGetGeometry(dpy, d, &rjunk, &xjunk, &yjunk, w, h, &bjunk, dep); } WIcon* wIconCreate(WWindow *wwin) { WScreen *scr=wwin->screen_ptr; WIcon *icon; char *file; unsigned long vmask = 0; XSetWindowAttributes attribs; icon = wmalloc(sizeof(WIcon)); memset(icon, 0, sizeof(WIcon)); icon->core = wCoreCreateTopLevel(scr, wwin->icon_x, wwin->icon_y, wPreferences.icon_size, wPreferences.icon_size, 0); if (wPreferences.use_saveunders) { vmask |= CWSaveUnder; attribs.save_under = True; } /* a white border for selecting it */ vmask |= CWBorderPixel; attribs.border_pixel = scr->white_pixel; XChangeWindowAttributes(dpy, icon->core->window, vmask, &attribs); /* will be overriden if this is an application icon */ icon->core->descriptor.handle_mousedown = miniwindowMouseDown; icon->core->descriptor.handle_expose = miniwindowExpose; icon->core->descriptor.parent_type = WCLASS_MINIWINDOW; icon->core->descriptor.parent = icon; icon->core->stacking = wmalloc(sizeof(WStacking)); icon->core->stacking->above = NULL; icon->core->stacking->under = NULL; icon->core->stacking->window_level = NORMAL_ICON_LEVEL; icon->core->stacking->child_of = NULL; icon->owner = wwin; if (wwin->wm_hints && (wwin->wm_hints->flags & IconWindowHint)) { if (wwin->client_win == wwin->main_window) { WApplication *wapp; /* do not let miniwindow steal app-icon's icon window */ wapp = wApplicationOf(wwin->client_win); if (!wapp || wapp->app_icon==NULL) icon->icon_win = wwin->wm_hints->icon_window; } else { icon->icon_win = wwin->wm_hints->icon_window; } } #ifdef NO_MINIWINDOW_TITLES icon->show_title = 0; #else icon->show_title = 1; #endif icon->image = wDefaultGetImage(scr, wwin->wm_instance, wwin->wm_class); file = wDefaultGetIconFile(scr, wwin->wm_instance, wwin->wm_class, False); if (file) { icon->file = wstrdup(file); } wGetIconName(dpy, wwin->client_win, &icon->icon_name); icon->tile_type = TILE_NORMAL; wIconUpdate(icon); XFlush(dpy); WMAddNotificationObserver(appearanceObserver, icon, WNIconAppearanceSettingsChanged, icon); WMAddNotificationObserver(tileObserver, icon, WNIconTileSettingsChanged, icon); return icon; } WIcon* wIconCreateWithIconFile(WScreen *scr, char *iconfile, int tile) { WIcon *icon; unsigned long vmask = 0; XSetWindowAttributes attribs; icon = wmalloc(sizeof(WIcon)); memset(icon, 0, sizeof(WIcon)); icon->core = wCoreCreateTopLevel(scr, 0, 0, wPreferences.icon_size, wPreferences.icon_size, 0); if (wPreferences.use_saveunders) { vmask = CWSaveUnder; attribs.save_under = True; } /* a white border for selecting it */ vmask |= CWBorderPixel; attribs.border_pixel = scr->white_pixel; XChangeWindowAttributes(dpy, icon->core->window, vmask, &attribs); /* will be overriden if this is a application icon */ icon->core->descriptor.handle_mousedown = miniwindowMouseDown; icon->core->descriptor.handle_expose = miniwindowExpose; icon->core->descriptor.parent_type = WCLASS_MINIWINDOW; icon->core->descriptor.parent = icon; icon->core->stacking = wmalloc(sizeof(WStacking)); icon->core->stacking->above = NULL; icon->core->stacking->under = NULL; icon->core->stacking->window_level = NORMAL_ICON_LEVEL; icon->core->stacking->child_of = NULL; if (iconfile) { icon->image = RLoadImage(scr->rcontext, iconfile, 0); if (!icon->image) { wwarning(_("error loading image file \"%s\""), iconfile, RMessageForError(RErrorCode)); } icon->image = wIconValidateIconSize(scr, icon->image); icon->file = wstrdup(iconfile); } icon->tile_type = tile; wIconUpdate(icon); WMAddNotificationObserver(tileObserver, icon, WNIconTileSettingsChanged, icon); return icon; } void wIconDestroy(WIcon *icon) { WCoreWindow *core = icon->core; WScreen *scr = core->screen_ptr; WMRemoveNotificationObserver(icon); if (icon->handlerID) WMDeleteTimerHandler(icon->handlerID); if (icon->icon_win) { int x=0, y=0; if (icon->owner) { x = icon->owner->icon_x; y = icon->owner->icon_y; } XUnmapWindow(dpy, icon->icon_win); XReparentWindow(dpy, icon->icon_win, scr->root_win, x, y); } if (icon->icon_name) XFree(icon->icon_name); if (icon->pixmap) XFreePixmap(dpy, icon->pixmap); if (icon->file) free(icon->file); if (icon->image!=NULL) RDestroyImage(icon->image); wCoreDestroy(icon->core); free(icon); } static void drawIconTitle(WScreen *scr, Pixmap pixmap, int height) { XFillRectangle(dpy, pixmap, scr->icon_title_texture->normal_gc, 0, 0, wPreferences.icon_size, height+1); XDrawLine(dpy, pixmap, scr->icon_title_texture->light_gc, 0, 0, wPreferences.icon_size, 0); XDrawLine(dpy, pixmap, scr->icon_title_texture->light_gc, 0, 0, 0, height+1); XDrawLine(dpy, pixmap, scr->icon_title_texture->dim_gc, wPreferences.icon_size-1, 0, wPreferences.icon_size-1, height+1); } static Pixmap makeIcon(WScreen *scr, RImage *icon, int titled, int shadowed, int tileType) { RImage *tile; Pixmap pixmap; int x, y, sx, sy; unsigned w, h; int theight = scr->icon_title_font->height; if (tileType == TILE_NORMAL) tile = RCloneImage(scr->icon_tile); else { assert(scr->clip_tile); tile = RCloneImage(scr->clip_tile); } if (icon) { w = (icon->width > wPreferences.icon_size) ? wPreferences.icon_size : icon->width; x = (wPreferences.icon_size - w) / 2; sx = (icon->width - w)/2; if (!titled) { h = (icon->height > wPreferences.icon_size) ? wPreferences.icon_size : icon->height; y = (wPreferences.icon_size - h) / 2; sy = (icon->height - h)/2; } else { h = (icon->height+theight > wPreferences.icon_size ? wPreferences.icon_size-theight : icon->height); y = theight+((int)wPreferences.icon_size-theight-h)/2; sy = (icon->height - h)/2; } RCombineArea(tile, icon, sx, sy, w, h, x, y); } if (shadowed) { RColor color; color.red = scr->icon_back_texture->light.red >> 8; color.green = scr->icon_back_texture->light.green >> 8; color.blue = scr->icon_back_texture->light.blue >> 8; color.alpha = 150; /* about 60% */ RClearImage(tile, &color); } if (!RConvertImage(scr->rcontext, tile, &pixmap)) { wwarning(_("error rendering image:%s"), RMessageForError(RErrorCode)); } RDestroyImage(tile); if (titled) drawIconTitle(scr, pixmap, theight); return pixmap; } void wIconChangeTitle(WIcon *icon, char *new_title) { int changed; changed = (new_title==NULL && icon->icon_name!=NULL) || (new_title!=NULL && icon->icon_name==NULL); if (icon->icon_name!=NULL) XFree(icon->icon_name); icon->icon_name = new_title; if (changed) icon->force_paint = 1; wIconPaint(icon); } void wIconChangeImage(WIcon *icon, RImage *new_image) { assert(icon != NULL); if (icon->image) RDestroyImage(icon->image); icon->image = wIconValidateIconSize(icon->core->screen_ptr, new_image); wIconUpdate(icon); } RImage* wIconValidateIconSize(WScreen *scr, RImage *icon) { RImage *tmp; int w, h; if (!icon) return NULL; if (icon->width > wPreferences.icon_size || icon->height > wPreferences.icon_size) { if (icon->width > icon->height) { w = wPreferences.icon_size - 4; h = w*icon->height/icon->width; } else { h = wPreferences.icon_size - 4; w = h*icon->width/icon->height; } tmp = RScaleImage(icon, w, h); RDestroyImage(icon); icon = tmp; } return icon; } Bool wIconChangeImageFile(WIcon *icon, char *file) { WScreen *scr = icon->core->screen_ptr; RImage *image; char *path; int error = 0; if (!file) { wIconChangeImage(icon, NULL); return True; } path = FindImage(wPreferences.icon_path, file); if (path && (image = RLoadImage(scr->rcontext, path, 0))) { wIconChangeImage(icon, image); } else { error = 1; } if (path) free(path); return !error; } static char* getnameforicon(WWindow *wwin) { char *prefix, *suffix; char *path; int len; if (wwin->wm_class && wwin->wm_instance) { suffix = wmalloc(strlen(wwin->wm_class)+strlen(wwin->wm_instance)+2); sprintf(suffix, "%s.%s", wwin->wm_instance, wwin->wm_class); } else if (wwin->wm_class) { suffix = wmalloc(strlen(wwin->wm_class)+1); strcpy(suffix, wwin->wm_class); } else if (wwin->wm_instance) { suffix = wmalloc(strlen(wwin->wm_instance)+1); strcpy(suffix, wwin->wm_instance); } else { return NULL; } prefix = wusergnusteppath(); len = strlen(prefix)+64+strlen(suffix); path = wmalloc(len+1); sprintf(path, "%s/.AppInfo", prefix); if (access(path, F_OK)!=0) { if (mkdir(path, S_IRUSR|S_IWUSR|S_IXUSR)) { wsyserror(_("could not create directory %s"), path); free(path); free(suffix); return NULL; } } strcat(path, "/WindowMaker"); if (access(path, F_OK)!=0) { if (mkdir(path, S_IRUSR|S_IWUSR|S_IXUSR)!=0) { wsyserror(_("could not create directory %s"), path); free(path); free(suffix); return NULL; } } strcat(path, "/"); strcat(path, suffix); strcat(path, ".xpm"); free(suffix); return path; } /* * wIconStore-- * Stores the client supplied icon at ~/GNUstep/.AppInfo/WindowMaker * and returns the path for that icon. Returns NULL if there is no * client supplied icon or on failure. * * Side effects: * New directories might be created. */ char* wIconStore(WIcon *icon) { char *path; RImage *image; WWindow *wwin = icon->owner; if (!wwin || !wwin->wm_hints || !(wwin->wm_hints->flags & IconPixmapHint) || wwin->wm_hints->icon_pixmap == None) return NULL; path = getnameforicon(wwin); if (!path) return NULL; image = RCreateImageFromDrawable(icon->core->screen_ptr->rcontext, wwin->wm_hints->icon_pixmap, (wwin->wm_hints->flags & IconMaskHint) ? wwin->wm_hints->icon_mask : None); if (!image) { free(path); return NULL; } if (!RSaveImage(image, path, "XPM")) { free(path); path = NULL; } RDestroyImage(image); return path; } /* void wIconChangeIconWindow(WIcon *icon, Window new_window); */ static void cycleColor(void *data) { WIcon *icon = (WIcon*)data; WScreen *scr = icon->core->screen_ptr; XGCValues gcv; icon->step--; gcv.dash_offset = icon->step; XChangeGC(dpy, scr->icon_select_gc, GCDashOffset, &gcv); XDrawRectangle(dpy, icon->core->window, scr->icon_select_gc, 0, 0, icon->core->width-1, icon->core->height-1); icon->handlerID = WMAddTimerHandler(COLOR_CYCLE_DELAY, cycleColor, icon); } void wIconSetHighlited(WIcon *icon, Bool flag) { if (icon->highlighted == flag) { return; } icon->highlighted = flag; wIconPaint(icon); } void wIconSelect(WIcon *icon) { WScreen *scr = icon->core->screen_ptr; icon->selected = !icon->selected; if (icon->selected) { icon->step = 0; if (!wPreferences.dont_blink) icon->handlerID = WMAddTimerHandler(10, cycleColor, icon); else XDrawRectangle(dpy, icon->core->window, scr->icon_select_gc, 0, 0, icon->core->width-1, icon->core->height-1); } else { if (icon->handlerID) { WMDeleteTimerHandler(icon->handlerID); icon->handlerID = NULL; } XClearArea(dpy, icon->core->window, 0, 0, icon->core->width, icon->core->height, True); } } void wIconUpdate(WIcon *icon) { WScreen *scr = icon->core->screen_ptr; int title_height = scr->icon_title_font->height; WWindow *wwin = icon->owner; assert(scr->icon_tile!=NULL); if (icon->pixmap!=None) XFreePixmap(dpy, icon->pixmap); icon->pixmap = None; if (wwin && WFLAGP(wwin, always_user_icon)) goto user_icon; /* use client specified icon window */ if (icon->icon_win!=None) { XWindowAttributes attr; int resize=0; int width, height, depth; int theight; Pixmap pixmap; getSize(icon->icon_win, &width, &height, &depth); if (width > wPreferences.icon_size) { resize = 1; width = wPreferences.icon_size; } if (height > wPreferences.icon_size) { resize = 1; height = wPreferences.icon_size; } if (icon->show_title && (height+title_height < wPreferences.icon_size)) { pixmap = XCreatePixmap(dpy, scr->w_win, wPreferences.icon_size, wPreferences.icon_size, scr->w_depth); XSetClipMask(dpy, scr->copy_gc, None); XCopyArea(dpy, scr->icon_tile_pixmap, pixmap, scr->copy_gc, 0, 0, wPreferences.icon_size, wPreferences.icon_size, 0, 0); drawIconTitle(scr, pixmap, title_height); theight = title_height; } else { pixmap = None; theight = 0; XSetWindowBackgroundPixmap(dpy, icon->core->window, scr->icon_tile_pixmap); } XSetWindowBorderWidth(dpy, icon->icon_win, 0); XReparentWindow(dpy, icon->icon_win, icon->core->window, (wPreferences.icon_size-width)/2, theight+(wPreferences.icon_size-height-theight)/2); if (resize) XResizeWindow(dpy, icon->icon_win, width, height); XMapWindow(dpy, icon->icon_win); XAddToSaveSet(dpy, icon->icon_win); icon->pixmap = pixmap; if (XGetWindowAttributes(dpy, icon->icon_win, &attr)) { if (attr.all_event_masks & ButtonPressMask) { wHackedGrabButton(Button1, MOD_MASK, icon->core->window, True, ButtonPressMask, GrabModeSync, GrabModeAsync, None, wCursor[WCUR_ARROW]); } } } else if (wwin && wwin->wm_hints && (wwin->wm_hints->flags & IconPixmapHint)) { int x, y; unsigned int w, h; Window jw; int ji, dotitle; unsigned int ju, d; Pixmap pixmap; if (!XGetGeometry(dpy, wwin->wm_hints->icon_pixmap, &jw, &ji, &ji, &w, &h, &ju, &d)) { icon->owner->wm_hints->flags &= ~IconPixmapHint; goto user_icon; } pixmap = XCreatePixmap(dpy, icon->core->window, wPreferences.icon_size, wPreferences.icon_size, scr->w_depth); XSetClipMask(dpy, scr->copy_gc, None); XCopyArea(dpy, scr->icon_tile_pixmap, pixmap, scr->copy_gc, 0, 0, wPreferences.icon_size, wPreferences.icon_size, 0, 0); if (w > wPreferences.icon_size) w = wPreferences.icon_size; x = (wPreferences.icon_size-w)/2; if (icon->show_title && (title_height < wPreferences.icon_size)) { drawIconTitle(scr, pixmap, title_height); dotitle = 1; if (h > wPreferences.icon_size - title_height - 2) { h = wPreferences.icon_size - title_height - 2; y = title_height + 1; } else { y = (wPreferences.icon_size-h-title_height)/2+title_height + 1; } } else { dotitle = 0; if (w > wPreferences.icon_size) w = wPreferences.icon_size; y = (wPreferences.icon_size-h)/2; } if (wwin->wm_hints->flags & IconMaskHint) XSetClipMask(dpy, scr->copy_gc, wwin->wm_hints->icon_mask); XSetClipOrigin(dpy, scr->copy_gc, x, y); if (d != scr->w_depth) { XSetForeground(dpy, scr->copy_gc, scr->black_pixel); XSetBackground(dpy, scr->copy_gc, scr->white_pixel); XCopyPlane(dpy, wwin->wm_hints->icon_pixmap, pixmap, scr->copy_gc, 0, 0, w, h, x, y, 1); } else { XCopyArea(dpy, wwin->wm_hints->icon_pixmap, pixmap, scr->copy_gc, 0, 0, w, h, x, y); } XSetClipOrigin(dpy, scr->copy_gc, 0, 0); icon->pixmap = pixmap; } else { user_icon: if (icon->image) { icon->pixmap = makeIcon(scr, icon->image, icon->show_title, icon->shadowed, icon->tile_type); } else { /* make default icons */ if (!scr->def_icon_pixmap) { RImage *image = NULL; char *path; char *file; file = wDefaultGetIconFile(scr, NULL, NULL, False); if (file) { path = FindImage(wPreferences.icon_path, file); if (!path) { wwarning(_("could not find default icon \"%s\""),file); goto make_icons; } image = RLoadImage(scr->rcontext, path, 0); if (!image) { wwarning(_("could not load default icon \"%s\":%s"), file, RMessageForError(RErrorCode)); } free(path); } make_icons: image = wIconValidateIconSize(scr, image); scr->def_icon_pixmap = makeIcon(scr, image, False, False, icon->tile_type); scr->def_ticon_pixmap = makeIcon(scr, image, True, False, icon->tile_type); if (image) RDestroyImage(image); } if (icon->show_title) { XSetWindowBackgroundPixmap(dpy, icon->core->window, scr->def_ticon_pixmap); } else { XSetWindowBackgroundPixmap(dpy, icon->core->window, scr->def_icon_pixmap); } icon->pixmap = None; } } if (icon->pixmap != None) { XSetWindowBackgroundPixmap(dpy, icon->core->window, icon->pixmap); } XClearWindow(dpy, icon->core->window); wIconPaint(icon); } void wIconPaint(WIcon *icon) { WScreen *scr=icon->core->screen_ptr; GC gc = scr->icon_title_gc; int x; char *tmp; if (icon->force_paint) { icon->force_paint = 0; wIconUpdate(icon); return; } XClearWindow(dpy, icon->core->window); /* draw the icon title */ if (icon->show_title && icon->icon_name!=NULL) { int l; int w; tmp = ShrinkString(scr->icon_title_font, icon->icon_name, wPreferences.icon_size-4); w = wTextWidth(scr->icon_title_font->font, tmp, l=strlen(tmp)); if (w > icon->core->width - 4) x = (icon->core->width - 4) - w; else x = (icon->core->width - w)/2; wDrawString(icon->core->window, scr->icon_title_font, gc, x, 1 + scr->icon_title_font->y, tmp, l); free(tmp); } if (icon->selected) XDrawRectangle(dpy, icon->core->window, scr->icon_select_gc, 0, 0, icon->core->width-1, icon->core->height-1); } /******************************************************************/ static void miniwindowExpose(WObjDescriptor *desc, XEvent *event) { wIconPaint(desc->parent); } static void miniwindowDblClick(WObjDescriptor *desc, XEvent *event) { WIcon *icon = desc->parent; assert(icon->owner!=NULL); wDeiconifyWindow(icon->owner); } static void miniwindowMouseDown(WObjDescriptor *desc, XEvent *event) { WIcon *icon = desc->parent; WWindow *wwin = icon->owner; XEvent ev; int x=wwin->icon_x, y=wwin->icon_y; int dx=event->xbutton.x, dy=event->xbutton.y; int grabbed=0; int clickButton=event->xbutton.button; if (WCHECK_STATE(WSTATE_MODAL)) return; if (IsDoubleClick(icon->core->screen_ptr, event)) { miniwindowDblClick(desc, event); return; } #ifdef DEBUG puts("Moving miniwindow"); #endif if (event->xbutton.button == Button1) { if (event->xbutton.state & MOD_MASK) wLowerFrame(icon->core); else wRaiseFrame(icon->core); if (event->xbutton.state & ShiftMask) { wIconSelect(icon); wSelectWindow(icon->owner, !wwin->flags.selected); } } else if (event->xbutton.button == Button3) { WObjDescriptor *desc; OpenMiniwindowMenu(wwin, event->xbutton.x_root, event->xbutton.y_root); /* allow drag select of menu */ desc = &wwin->screen_ptr->window_menu->menu->descriptor; event->xbutton.send_event = True; (*desc->handle_mousedown)(desc, event); return; } if (XGrabPointer(dpy, icon->core->window, False, ButtonMotionMask |ButtonReleaseMask|ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) { #ifdef DEBUG0 wwarning("pointer grab failed for icon move"); #endif } while(1) { WMMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask |ButtonMotionMask|ExposureMask, &ev); switch (ev.type) { case Expose: WMHandleEvent(&ev); break; case MotionNotify: if (!grabbed) { if (abs(dx-ev.xmotion.x)>=MOVE_THRESHOLD || abs(dy-ev.xmotion.y)>=MOVE_THRESHOLD) { XChangeActivePointerGrab(dpy, ButtonMotionMask |ButtonReleaseMask|ButtonPressMask, wCursor[WCUR_MOVE], CurrentTime); grabbed=1; } else { break; } } x = ev.xmotion.x_root - dx; y = ev.xmotion.y_root - dy; XMoveWindow(dpy, icon->core->window, x, y); break; case ButtonPress: break; case ButtonRelease: if (ev.xbutton.button != clickButton) break; if (wwin->icon_x!=x || wwin->icon_y!=y) wwin->flags.icon_moved = 1; XMoveWindow(dpy, icon->core->window, x, y); wwin->icon_x = x; wwin->icon_y = y; #ifdef DEBUG puts("End miniwindow move"); #endif XUngrabPointer(dpy, CurrentTime); if (wPreferences.auto_arrange_icons) wArrangeIcons(wwin->screen_ptr, True); return; } } }