From 78c1c90ea25c57d9f08a92b15cf5fe6167a18282 Mon Sep 17 00:00:00 2001
From: David Maciejak <david.maciejak@gmail.com>
Date: Wed, 20 Aug 2014 11:25:56 +0700
Subject: [PATCH] wmaker: add miniwindow apercu

This patch is adding miniwindow apercu when the mouse
is over the miniwindows.

To enable it you have to run WPref, in Miscellaneous Ergonomic
Preferences, check miniwindow apercus.
Then, you will be able to see a screenshot of the app when the mouse
is over the miniwindow.
---
 WINGs/wwindow.c          |  4 +--
 WPrefs.app/Preferences.c | 37 ++++++++++++----------
 src/WindowMaker.h        |  3 +-
 src/actions.c            | 34 ++++++++++++++++++++
 src/balloon.c            | 82 ++++++++++++++++++++++++++++++++++++++++++++++--
 src/defaults.c           |  4 ++-
 src/icon.c               | 20 ++++++++++++
 src/icon.h               |  4 +++
 8 files changed, 165 insertions(+), 23 deletions(-)

diff --git a/WINGs/wwindow.c b/WINGs/wwindow.c
index 5293f87..a04ab6a 100644
--- a/WINGs/wwindow.c
+++ b/WINGs/wwindow.c
@@ -606,8 +606,8 @@ void WMSetWindowMiniwindowPixmap(WMWindow * win, WMPixmap * pixmap)
 
 void WMSetWindowMiniwindowTitle(WMWindow * win, const char *title)
 {
-	if ((win->miniTitle && !title) || (!win->miniTitle && title)
-	    || (title && win->miniTitle && strcoll(title, win->miniTitle) != 0)) {
+	if (win && ((win->miniTitle && !title) || (!win->miniTitle && title)
+	    || (title && win->miniTitle && strcoll(title, win->miniTitle) != 0))) {
 		if (win->miniTitle)
 			wfree(win->miniTitle);
 
diff --git a/WPrefs.app/Preferences.c b/WPrefs.app/Preferences.c
index 7d77213..b6b7891 100644
--- a/WPrefs.app/Preferences.c
+++ b/WPrefs.app/Preferences.c
@@ -39,7 +39,7 @@ typedef struct _Panel {
 	WMPopUpButton *posiP;
 
 	WMFrame *ballF;
-	WMButton *ballB[4];
+	WMButton *ballB[5];
 
 	WMFrame *optF;
 	WMButton *bounceB;
@@ -136,8 +136,9 @@ static void showData(_Panel * panel)
 
 	WMSetButtonSelected(panel->ballB[0], GetBoolForKey("WindowTitleBalloons"));
 	WMSetButtonSelected(panel->ballB[1], GetBoolForKey("MiniwindowTitleBalloons"));
-	WMSetButtonSelected(panel->ballB[2], GetBoolForKey("AppIconBalloons"));
-	WMSetButtonSelected(panel->ballB[3], GetBoolForKey("HelpBalloons"));
+	WMSetButtonSelected(panel->ballB[2], GetBoolForKey("MiniwindowApercuBalloons"));
+	WMSetButtonSelected(panel->ballB[3], GetBoolForKey("AppIconBalloons"));
+	WMSetButtonSelected(panel->ballB[4], GetBoolForKey("HelpBalloons"));
 }
 
 static void storeData(_Panel * panel)
@@ -198,8 +199,9 @@ static void storeData(_Panel * panel)
 	SetBoolForKey(WMGetButtonSelected(panel->bounceRaisB), "RaiseAppIconsWhenBouncing");
 	SetBoolForKey(WMGetButtonSelected(panel->ballB[0]), "WindowTitleBalloons");
 	SetBoolForKey(WMGetButtonSelected(panel->ballB[1]), "MiniwindowTitleBalloons");
-	SetBoolForKey(WMGetButtonSelected(panel->ballB[2]), "AppIconBalloons");
-	SetBoolForKey(WMGetButtonSelected(panel->ballB[3]), "HelpBalloons");
+	SetBoolForKey(WMGetButtonSelected(panel->ballB[2]), "MiniwindowApercuBalloons");
+	SetBoolForKey(WMGetButtonSelected(panel->ballB[3]), "AppIconBalloons");
+	SetBoolForKey(WMGetButtonSelected(panel->ballB[4]), "HelpBalloons");
 }
 
 static void createPanel(Panel * p)
@@ -251,26 +253,27 @@ static void createPanel(Panel * p)
 
     /***************** Balloon Text ****************/
 	panel->ballF = WMCreateFrame(panel->box);
-	WMResizeWidget(panel->ballF, 240, 109);
+	WMResizeWidget(panel->ballF, 240, 126);
 	WMMoveWidget(panel->ballF, 265, 10);
-	WMSetFrameTitle(panel->ballF, _("Show balloon text for..."));
+	WMSetFrameTitle(panel->ballF, _("Show balloon for..."));
 
-	for (i = 0; i < 4; i++) {
+	for (i = 0; i < 5; i++) {
 		panel->ballB[i] = WMCreateSwitchButton(panel->ballF);
 		WMResizeWidget(panel->ballB[i], 210, 20);
 		WMMoveWidget(panel->ballB[i], 15, 16 + i * 22);
 	}
 	WMSetButtonText(panel->ballB[0], _("incomplete window titles"));
 	WMSetButtonText(panel->ballB[1], _("miniwindow titles"));
-	WMSetButtonText(panel->ballB[2], _("application/dock icons"));
-	WMSetButtonText(panel->ballB[3], _("internal help"));
+	WMSetButtonText(panel->ballB[2], _("miniwindow apercus"));
+	WMSetButtonText(panel->ballB[3], _("application/dock icons"));
+	WMSetButtonText(panel->ballB[4], _("internal help"));
 
 	WMMapSubwidgets(panel->ballF);
 
     /***************** Options ****************/
 	panel->optF = WMCreateFrame(panel->box);
-	WMResizeWidget(panel->optF, 240, 101);
-	WMMoveWidget(panel->optF, 265, 124);
+	WMResizeWidget(panel->optF, 240, 91);
+	WMMoveWidget(panel->optF, 265, 136);
 	WMSetFrameTitle(panel->optF, _("AppIcon bouncing"));
 
 	panel->bounceB = WMCreateSwitchButton(panel->optF);
@@ -279,21 +282,21 @@ static void createPanel(Panel * p)
 	WMSetButtonText(panel->bounceB, _("Disable AppIcon bounce."));
 
 	panel->bounceUrgB = WMCreateSwitchButton(panel->optF);
-	WMResizeWidget(panel->bounceUrgB, 210, 30);
-	WMMoveWidget(panel->bounceUrgB, 15, 39);
+	WMResizeWidget(panel->bounceUrgB, 210, 28);
+	WMMoveWidget(panel->bounceUrgB, 15, 37);
 	WMSetButtonText(panel->bounceUrgB, _("Bounce AppIcon when the application wants attention."));
 	WMSetButtonSelected(panel->bounceUrgB, True); /* defaults to true */
 
 	panel->bounceRaisB = WMCreateSwitchButton(panel->optF);
-	WMResizeWidget(panel->bounceRaisB, 210, 25);
-	WMMoveWidget(panel->bounceRaisB, 15, 70);
+	WMResizeWidget(panel->bounceRaisB, 210, 23);
+	WMMoveWidget(panel->bounceRaisB, 15, 65);
 	WMSetButtonText(panel->bounceRaisB, _("Raise AppIcons when bouncing."));
 
 	WMMapSubwidgets(panel->optF);
 
     /***************** Workspace border ****************/
 	panel->borderF = WMCreateFrame(panel->box);
-	WMResizeWidget(panel->borderF, 240, 80);
+	WMResizeWidget(panel->borderF, 240, 82);
 	WMMoveWidget(panel->borderF, 15, 145);
 	WMSetFrameTitle(panel->borderF, _("Workspace border"));
 
diff --git a/src/WindowMaker.h b/src/WindowMaker.h
index d021293..5553d5b 100644
--- a/src/WindowMaker.h
+++ b/src/WindowMaker.h
@@ -392,7 +392,8 @@ extern struct WPreferences {
 
     /* balloon text */
     char window_balloon;
-    char miniwin_balloon;
+    char miniwin_title_balloon;
+    char miniwin_apercu_balloon;
     char appicon_balloon;
     char help_balloon;
 
diff --git a/src/actions.c b/src/actions.c
index a767072..ae326a1 100644
--- a/src/actions.c
+++ b/src/actions.c
@@ -1094,6 +1094,40 @@ void wIconifyWindow(WWindow * wwin)
 
 		wwin->icon = icon_create_for_wwindow(wwin);
 		wwin->icon->mapped = 1;
+
+		/* extract the window screenshot everytime, as the option can be enable anytime */
+		if (wwin->client_win && wwin->flags.mapped) {
+			RImage *apercu;
+			XImage *pimg;
+			unsigned int w, h;
+			int x, y;
+			Window baz;
+
+			XRaiseWindow(dpy, wwin->frame->core->window);
+			XTranslateCoordinates(dpy, wwin->client_win, wwin->screen_ptr->root_win, 0, 0, &x, &y, &baz);
+
+			w = attribs.width;
+			h = attribs.height;
+
+			if (x - attribs.x + attribs.width > wwin->screen_ptr->scr_width)
+				w = wwin->screen_ptr->scr_width - x + attribs.x;
+
+			if (y - attribs.y + attribs.height > wwin->screen_ptr->scr_height)
+				h = wwin->screen_ptr->scr_height - y + attribs.y;
+
+			pimg = XGetImage(dpy, wwin->client_win, 0, 0, w, h, AllPlanes, ZPixmap);
+			if (pimg) {
+				apercu = RCreateImageFromXImage(wwin->screen_ptr->rcontext, pimg, NULL);
+				XDestroyImage(pimg);
+
+				if (apercu) {
+					set_icon_apercu(wwin->icon, apercu);
+					RReleaseImage(apercu);
+				} else {
+					wwarning("window apercu creation failed");
+				}
+			}
+		}
 	}
 
 	wwin->flags.miniaturized = 1;
diff --git a/src/balloon.c b/src/balloon.c
index b9c77bb..1fae947 100644
--- a/src/balloon.c
+++ b/src/balloon.c
@@ -2,6 +2,7 @@
  *  Window Maker window manager
  *
  *  Copyright (c) 1998-2003 Alfredo K. Kojima
+ *  Copyright (c) 2014 Window Maker Team
  *
  *  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
@@ -42,6 +43,7 @@
 #include "appicon.h"
 #include "workspace.h"
 #include "balloon.h"
+#include "misc.h"
 
 
 typedef struct _WBalloon {
@@ -59,6 +61,7 @@ typedef struct _WBalloon {
 	WMHandlerID timer;
 
 	Pixmap contents;
+	Pixmap apercu;
 
 	char mapped;
 	char ignoreTimer;
@@ -376,6 +379,71 @@ static void showText(WScreen *scr, int x, int y, int h, int w, const char *text)
 }
 #endif				/* !SHAPED_BALLOON */
 
+static void showApercu(WScreen *scr, int x, int y, int height, int width, char *title, Pixmap apercu)
+{
+	Pixmap pixmap;
+	WMFont *font = scr->info_text_font;
+	int titleHeight = 0;
+	char *shortenTitle = title;
+
+	if (scr->balloon->contents)
+		XFreePixmap(dpy, scr->balloon->contents);
+
+	if (wPreferences.miniwin_title_balloon) {
+		shortenTitle = ShrinkString(font, title, width - APERCU_BORDER);
+		titleHeight = countLines(shortenTitle) * WMFontHeight(font) + 4;
+		height += titleHeight;
+	}
+
+	if (x < 0)
+		x = 0;
+	else if (x + width > scr->scr_width - 1)
+		x = scr->scr_width - width - APERCU_BORDER;
+
+	if (y - height - 2 < 0) {
+		y += wPreferences.icon_size;
+		if (y < 0)
+			y = 0;
+	} else {
+		y -= height + 2;
+	}
+
+	if (scr->window_title_texture[0])
+		XSetForeground(dpy, scr->draw_gc, scr->window_title_texture[0]->any.color.pixel);
+	else
+		XSetForeground(dpy, scr->draw_gc, scr->light_pixel);
+
+	pixmap = XCreatePixmap(dpy, scr->root_win, width, height, scr->w_depth);
+	XFillRectangle(dpy, pixmap, scr->draw_gc, 0, 0, width, height);
+
+	if (shortenTitle && wPreferences.miniwin_title_balloon) {
+		drawMultiLineString(scr->wmscreen, pixmap, scr->window_title_color[0], font,
+						APERCU_BORDER, APERCU_BORDER, shortenTitle, strlen(shortenTitle));
+		wfree(shortenTitle);
+	}
+
+	XCopyArea(dpy, apercu, pixmap, scr->draw_gc,
+				0, 0, (wPreferences.icon_size - 1 - APERCU_BORDER) * 2,
+				(wPreferences.icon_size - 1 - APERCU_BORDER) * 2,
+				APERCU_BORDER, APERCU_BORDER + titleHeight);
+
+#ifdef SHAPED_BALLOON
+	XShapeCombineMask(dpy, scr->balloon->window, ShapeBounding, 0, 0, None, ShapeSet);
+#endif
+	XResizeWindow(dpy, scr->balloon->window, width, height);
+	XMoveWindow(dpy, scr->balloon->window, x, y);
+
+	XSetWindowBackgroundPixmap(dpy, scr->balloon->window, pixmap);
+
+	XClearWindow(dpy, scr->balloon->window);
+	XMapRaised(dpy, scr->balloon->window);
+
+
+	scr->balloon->contents = pixmap;
+
+	scr->balloon->mapped = 1;
+}
+
 static void showBalloon(WScreen * scr)
 {
 	int x, y;
@@ -389,7 +457,13 @@ static void showBalloon(WScreen * scr)
 		scr->balloon->prevType = 0;
 		return;
 	}
-	showText(scr, x, y, scr->balloon->h, w, scr->balloon->text);
+
+	if (wPreferences.miniwin_apercu_balloon && scr->balloon->apercu != None)
+		/* used to display either the apercu alone or the apercu and the title */
+		showApercu(scr, x, y, (wPreferences.icon_size - 1) * 2, (wPreferences.icon_size - 1) * 2,
+					scr->balloon->text, scr->balloon->apercu);
+	else
+		showText(scr, x, y, scr->balloon->h, w, scr->balloon->text);
 }
 
 static void frameBalloon(WObjDescriptor * object)
@@ -420,7 +494,9 @@ static void miniwindowBalloon(WObjDescriptor * object)
 	}
 	scr->balloon->h = icon->core->height;
 	scr->balloon->text = wstrdup(icon->icon_name);
+	scr->balloon->apercu = icon->apercu;
 	scr->balloon->objectWindow = icon->core->window;
+
 	if ((scr->balloon->prevType == object->parent_type || scr->balloon->prevType == WCLASS_APPICON)
 	    && scr->balloon->ignoreTimer) {
 		XUnmapWindow(dpy, scr->balloon->window);
@@ -522,6 +598,8 @@ void wBalloonEnteredObject(WScreen * scr, WObjDescriptor * object)
 		wfree(scr->balloon->text);
 	scr->balloon->text = NULL;
 
+	scr->balloon->apercu = None;
+
 	if (!object) {
 		wBalloonHide(scr);
 		balloon->ignoreTimer = 0;
@@ -538,7 +616,7 @@ void wBalloonEnteredObject(WScreen * scr, WObjDescriptor * object)
 			appiconBalloon(object);
 		break;
 	case WCLASS_MINIWINDOW:
-		if (wPreferences.miniwin_balloon)
+		if (wPreferences.miniwin_title_balloon || wPreferences.miniwin_apercu_balloon)
 			miniwindowBalloon(object);
 		break;
 	case WCLASS_APPICON:
diff --git a/src/defaults.c b/src/defaults.c
index 266684b..155fe6f 100644
--- a/src/defaults.c
+++ b/src/defaults.c
@@ -458,7 +458,9 @@ WDefaultEntry optionList[] = {
 	{"WindowTitleBalloons", "NO", NULL,
 	    &wPreferences.window_balloon, getBool, NULL, NULL, NULL},
 	{"MiniwindowTitleBalloons", "NO", NULL,
-	    &wPreferences.miniwin_balloon, getBool, NULL, NULL, NULL},
+	    &wPreferences.miniwin_title_balloon, getBool, NULL, NULL, NULL},
+	{"MiniwindowApercuBalloons", "NO", NULL,
+	    &wPreferences.miniwin_apercu_balloon, getBool, NULL, NULL, NULL},
 	{"AppIconBalloons", "NO", NULL,
 	    &wPreferences.appicon_balloon, getBool, NULL, NULL, NULL},
 	{"HelpBalloons", "NO", NULL,
diff --git a/src/icon.c b/src/icon.c
index b5bb308..1aa27ea 100644
--- a/src/icon.c
+++ b/src/icon.c
@@ -225,6 +225,9 @@ void wIconDestroy(WIcon *icon)
 	if (icon->pixmap)
 		XFreePixmap(dpy, icon->pixmap);
 
+	if (icon->apercu)
+		XFreePixmap(dpy, icon->apercu);
+
 	unset_icon_image(icon);
 
 	wCoreDestroy(icon->core);
@@ -582,6 +585,23 @@ void set_icon_image_from_image(WIcon *icon, RImage *image)
 	icon->file_image = image;
 }
 
+void set_icon_apercu(WIcon *icon, RImage *image)
+{
+	Pixmap tmp;
+	RImage *scaled_apercu;
+	WScreen *scr = icon->core->screen_ptr;
+
+	scaled_apercu = RSmoothScaleImage(image, (wPreferences.icon_size - 1 - APERCU_BORDER) * 2,
+						(wPreferences.icon_size - 1 - APERCU_BORDER) * 2 );
+
+	if (RConvertImage(scr->rcontext, scaled_apercu, &tmp)) {
+		if (icon->apercu != None)
+			XFreePixmap(dpy, icon->apercu);
+		icon->apercu = tmp;
+	}
+	RReleaseImage(scaled_apercu);
+}
+
 void wIconUpdate(WIcon *icon)
 {
 	WWindow *wwin = NULL;
diff --git a/src/icon.h b/src/icon.h
index 347814b..af82f5c 100644
--- a/src/icon.h
+++ b/src/icon.h
@@ -29,6 +29,8 @@
 #define TILE_CLIP	1
 #define TILE_DRAWER	2
 
+#define APERCU_BORDER 2
+
 typedef struct WIcon {
 	WCoreWindow 	*core;
 	WWindow 	*owner;		/* owner window */
@@ -48,6 +50,7 @@ typedef struct WIcon {
 	unsigned int	highlighted:1;
 
 	Pixmap 		pixmap;
+	Pixmap		apercu;
 
 	WMHandlerID	handlerID;	/* timer handler ID for cycling select
 					 * color */
@@ -74,5 +77,6 @@ char *get_name_for_instance_class(const char *wm_instance, const char *wm_class)
 
 void wIconSetHighlited(WIcon *icon, Bool flag);
 void set_icon_image_from_image(WIcon *icon, RImage *image);
+void set_icon_apercu(WIcon *icon, RImage *image);
 
 #endif /* WMICON_H_ */
-- 
1.8.3.2

