diff --git a/WPrefs.app/Expert.c b/WPrefs.app/Expert.c index f7b7c7ce..6ef6307d 100644 --- a/WPrefs.app/Expert.c +++ b/WPrefs.app/Expert.c @@ -78,6 +78,9 @@ static struct expert_option { { N_("Ignore minimized windows when cycling."), /* default: */ False, OPTION_WMAKER, "CycleIgnoreMinimized" }, + { N_("Show app icons in window list."), + /* default: */ False, OPTION_WMAKER, "WindowListAppIcons" }, + { N_("Show switch panel when cycling windows."), /* default: */ True, OPTION_WMAKER_ARRAY, "SwitchPanelImages" }, diff --git a/WindowMaker/Defaults/WindowMaker.in b/WindowMaker/Defaults/WindowMaker.in index 0cc0f654..49cd181f 100644 --- a/WindowMaker/Defaults/WindowMaker.in +++ b/WindowMaker/Defaults/WindowMaker.in @@ -254,4 +254,5 @@ CycleActiveHeadOnly = NO; CycleAllWorkspaces = NO; CycleIgnoreMinimized = NO; + WindowListAppIcons = NO; } diff --git a/src/WindowMaker.h b/src/WindowMaker.h index 23ca8741..75464fdf 100644 --- a/src/WindowMaker.h +++ b/src/WindowMaker.h @@ -479,6 +479,7 @@ extern struct WPreferences { int hot_corner_delay; /* Delay after which the hot corner is triggered */ int hot_corner_edge; /* Hot corner edge size */ char *hot_corner_actions[4]; /* Action of each corner */ + char window_list_app_icons; /* Show app icons in window list */ struct { #ifdef USE_ICCCM_WMREPLACE diff --git a/src/defaults.c b/src/defaults.c index 8720b4ac..43860599 100644 --- a/src/defaults.c +++ b/src/defaults.c @@ -534,6 +534,8 @@ WDefaultEntry optionList[] = { &wPreferences.hot_corner_edge, getInt, NULL, NULL, NULL}, {"HotCornerActions", "(\"None\", \"None\", \"None\", \"None\")", &wPreferences, NULL, getPropList, setHotCornerActions, NULL, NULL}, + {"WindowListAppIcons", "NO", NULL, + &wPreferences.window_list_app_icons, getBool, NULL, NULL, NULL}, /* style options */ diff --git a/src/menu.c b/src/menu.c index e84b1caa..7d3b69db 100644 --- a/src/menu.c +++ b/src/menu.c @@ -267,6 +267,7 @@ WMenuEntry *wMenuInsertCallback(WMenu *menu, int index, const char *text, entry = wmalloc(sizeof(WMenuEntry)); entry->flags.enabled = 1; entry->text = wstrdup(text); + entry->icon = NULL; entry->cascade = -1; entry->clientdata = clientdata; entry->callback = callback; @@ -369,6 +370,9 @@ void wMenuRemoveItem(WMenu * menu, int index) if (menu->entries[index]->text) wfree(menu->entries[index]->text); + if (menu->entries[index]->icon) + wPixmapDestroy(menu->entries[index]->icon); + if (menu->entries[index]->rtext) wfree(menu->entries[index]->rtext); @@ -499,6 +503,10 @@ void wMenuRealize(WMenu * menu) text = menu->entries[i]->text; width = WMWidthOfString(scr->menu_entry_font, text, strlen(text)) + 10; + if (wPreferences.window_list_app_icons && menu->entries[i]->icon) { + width += menu->entries[i]->icon->width + 4; + } + if (menu->entries[i]->flags.indicator) { width += MENU_INDICATOR_SPACE; } @@ -562,6 +570,9 @@ void wMenuDestroy(WMenu * menu, int recurse) wfree(menu->entries[i]->text); + if (menu->entries[i]->icon) + wPixmapDestroy(menu->entries[i]->icon); + if (menu->entries[i]->rtext) wfree(menu->entries[i]->rtext); #ifdef USER_MENU @@ -711,6 +722,26 @@ static void paintEntry(WMenu * menu, int index, int selected) if (entry->flags.indicator) x += MENU_INDICATOR_SPACE + 2; + if (wPreferences.window_list_app_icons && entry->icon && entry->icon->image != None) { + int ix = x; + int iy = y + (h - entry->icon->height) / 2; + + if (entry->icon->mask != None) { + XSetClipMask(dpy, scr->copy_gc, entry->icon->mask); + XSetClipOrigin(dpy, scr->copy_gc, ix, iy); + } else { + XSetClipMask(dpy, scr->copy_gc, None); + XSetClipOrigin(dpy, scr->copy_gc, 0, 0); + } + + XCopyArea(dpy, entry->icon->image, win, scr->copy_gc, + 0, 0, entry->icon->width, entry->icon->height, ix, iy); + XSetClipMask(dpy, scr->copy_gc, None); + XSetClipOrigin(dpy, scr->copy_gc, 0, 0); + + x += entry->icon->width + 4; + } + WMDrawString(scr->wmscreen, win, color, scr->menu_entry_font, x, 3 + y + wPreferences.menu_text_clearance, entry->text, strlen(entry->text)); diff --git a/src/menu.h b/src/menu.h index 60ff9555..036403bd 100644 --- a/src/menu.h +++ b/src/menu.h @@ -46,6 +46,7 @@ typedef struct WMenuEntry { int order; char *text; /* entry text */ char *rtext; /* text to show in the right part */ + struct WPixmap *icon; /* optional icon displayed before the text */ void (*callback)(struct WMenu *menu, struct WMenuEntry *entry); void (*free_cdata)(void *data); /* proc to be used to free clientdata */ void *clientdata; /* data to pass to callback */ diff --git a/src/switchmenu.c b/src/switchmenu.c index 551b4e58..a8c3937f 100644 --- a/src/switchmenu.c +++ b/src/switchmenu.c @@ -48,6 +48,50 @@ static int initialized = 0; static void observer(void *self, WMNotification * notif); static void wsobserver(void *self, WMNotification * notif); +static WPixmap *switchMenuIconForWindow(WScreen *scr, WWindow *wwin) +{ + RImage *image = NULL; + WPixmap *pix; + WApplication *wapp; + int max_size; + + if (!scr || !wwin) + return NULL; + + max_size = WMFontHeight(scr->menu_entry_font) + 2; + if (max_size < 12) + max_size = 12; + + /* Prefer the actual appicon image when available */ + wapp = wApplicationOf(wwin->main_window); + if (wapp && wapp->app_icon && wapp->app_icon->icon && wapp->app_icon->icon->file_image) { + image = RRetainImage(wapp->app_icon->icon->file_image); + } + + /* Fall back to _NET_WM_ICON, then the default icon */ + if (!image && !WFLAGP(wwin, always_user_icon) && wwin->net_icon_image) + image = RRetainImage(wwin->net_icon_image); + if (!image) + image = get_icon_image(scr, wwin->wm_instance, wwin->wm_class, max_size); + + if (!image) + return NULL; + + image = wIconValidateIconSize(image, max_size); + if (!image) + return NULL; + + pix = wmalloc(sizeof(WPixmap)); + memset(pix, 0, sizeof(WPixmap)); + RConvertImageMask(scr->rcontext, image, &pix->image, &pix->mask, 128); + pix->width = image->width; + pix->height = image->height; + pix->depth = scr->w_depth; + + RReleaseImage(image); + return pix; +} + /* * FocusWindow * @@ -216,6 +260,8 @@ void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action) entry = wMenuInsertCallback(switchmenu, idx, t, focusWindow, wwin); wfree(t); + entry->icon = switchMenuIconForWindow(scr, wwin); + entry->flags.indicator = 1; entry->rtext = wmalloc(MAX_WORKSPACENAME_WIDTH + 8); if (IS_OMNIPRESENT(wwin)) @@ -273,6 +319,7 @@ void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action) if (entry->rtext) { int idx = -1; char *t, *rt; + WPixmap *ipix; int it, ion; if (IS_OMNIPRESENT(wwin)) { @@ -285,6 +332,8 @@ void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action) rt = entry->rtext; entry->rtext = NULL; + ipix = entry->icon; + entry->icon = NULL; t = entry->text; entry->text = NULL; @@ -300,6 +349,7 @@ void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action) entry = wMenuInsertCallback(switchmenu, idx, t, focusWindow, wwin); wfree(t); entry->rtext = rt; + entry->icon = ipix; entry->flags.indicator = 1; entry->flags.indicator_type = it; entry->flags.indicator_on = ion;