From d7551d0e53dd0408b30cbd8e45f44389831dd2a3 Mon Sep 17 00:00:00 2001
From: David Maciejak <david.maciejak@gmail.com>
Date: Thu, 14 Aug 2014 12:32:11 +0700
Subject: [PATCH] wmaker: add miniwindow apercu

This patch is adding miniwindow apercu when the mouse
is over the miniwindows.
---
 WINGs/wwindow.c          |  4 +--
 WPrefs.app/Preferences.c | 37 ++++++++++----------
 src/WindowMaker.h        |  3 +-
 src/actions.c            | 34 +++++++++++++++++++
 src/balloon.c            | 88 ++++++++++++++++++++++++++++++++++++++++++++++--
 src/defaults.c           |  4 ++-
 src/icon.c               | 20 +++++++++++
 src/icon.h               |  4 +++
 8 files changed, 171 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..769a346 100644
--- a/src/balloon.c
+++ b/src/balloon.c
@@ -42,6 +42,7 @@
 #include "appicon.h"
 #include "workspace.h"
 #include "balloon.h"
+#include "misc.h"
 
 
 typedef struct _WBalloon {
@@ -59,6 +60,7 @@ typedef struct _WBalloon {
 	WMHandlerID timer;
 
 	Pixmap contents;
+	Pixmap apercu;
 
 	char mapped;
 	char ignoreTimer;
@@ -376,6 +378,77 @@ 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 h, int w, char *title, Pixmap apercu)
+{
+	int width;
+	int height;
+	Pixmap pixmap;
+	WMFont *font = scr->info_text_font;
+	int titleHeight = 0;
+	char *shortenTitle = title;
+
+	if (scr->balloon->contents)
+		XFreePixmap(dpy, scr->balloon->contents);
+
+	height = h;
+	width  = w;
+
+	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 +462,14 @@ 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
+		if (wPreferences.miniwin_title_balloon)
+			showText(scr, x, y, scr->balloon->h, w, scr->balloon->text);
 }
 
 static void frameBalloon(WObjDescriptor * object)
@@ -420,7 +500,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 +604,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 +622,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

