soc.2009.vulture: 8aaab5ec: Most of the functionality for the buddy-...
gdick at soc.pidgin.im
gdick at soc.pidgin.im
Thu Aug 13 22:54:12 EDT 2009
-----------------------------------------------------------------
Revision: 8aaab5ecfd3fb6a9aca6f396c9a64127290589a0
Ancestor: e3d2126104f3a40c863b972aa2ec0fd47c937ed0
Author: gdick at soc.pidgin.im
Date: 2009-08-13T20:56:52
Branch: im.pidgin.soc.2009.vulture
URL: http://d.pidgin.im/viewmtn/revision/info/8aaab5ecfd3fb6a9aca6f396c9a64127290589a0
Modified files:
vulture/cmdline.c vulture/cmdline.h vulture/purplebicon.c
vulture/purplebicon.h vulture/purplemain.c
vulture/purplemain.h vulture/purplequeue.c
vulture/purplequeue.h vulture/resource.h
vulture/vulture-res.rc vulture/vulture.c vulture/vulture.h
vulture/vultureblist.c
ChangeLog:
Most of the functionality for the buddy-icon control in the status box. Also
added a command-line debug switch.
-------------- next part --------------
============================================================
--- vulture/cmdline.c 87aeb64a0a020b28219c272fca9fd6c2d39a786b
+++ vulture/cmdline.c d01c044e16452da9b761fa757c93c279772c70db
@@ -25,15 +25,18 @@
#include "vulture.h"
#include "cmdline.h"
+#include "purple.h"
/** Command-line options set by VultureParseCommandLine. */
static gchar *g_szCustomUserDir = NULL;
+static gboolean g_bDebug = FALSE;
static GOptionContext *g_lpgopcontext;
static GOptionEntry g_rggopentry[] =
{
{ "config", 'c', 0, G_OPTION_ARG_STRING, &g_szCustomUserDir, "use DIR for config files", "DIR" },
+ { "debug", 'd', 0, G_OPTION_ARG_NONE, &g_bDebug, "enable debug messages", NULL },
{ NULL, 0, 0, 0, NULL, NULL, NULL }
};
@@ -76,10 +79,20 @@ void VultureCommandLineCleanup(void)
/**
- * Retrieves the user directory specified on the command-line, or NULL if none
+ * Retrieves the user directory specified on the command line, or NULL if none
* set.
*/
gchar* VultureGetCustomUserDir(void)
{
return g_szCustomUserDir;
}
+
+
+/** Enables debug mode if specified on the command line. */
+void VultureSetDebugFromCmdLine(void)
+{
+ purple_debug_set_enabled(g_bDebug);
+
+ if(g_bDebug)
+ g_set_print_handler(VultureGPrintHandler);
+}
============================================================
--- vulture/cmdline.h 288d6762e77b8ccb8c131b3836fbfdba4b3caaa5
+++ vulture/cmdline.h c5b79eb75b7aae943ae88eac001c1e8003842a0b
@@ -28,5 +28,6 @@ gchar* VultureGetCustomUserDir(void);
void VultureParseCommandLine(void);
void VultureCommandLineCleanup(void);
gchar* VultureGetCustomUserDir(void);
+void VultureSetDebugFromCmdLine(void);
#endif
============================================================
--- vulture/purplebicon.c ca36daa9e4113e6b14d4c907c8c1154f6f0eba7d
+++ vulture/purplebicon.c 40a3abcb7c8a529a3feb4f80220cb0e3cdbbc279
@@ -23,14 +23,19 @@
#include <windows.h>
#include <glib.h>
+#include <glib/gstdio.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <string.h>
#include "vulture.h"
#include "purple.h"
#include "purplebicon.h"
+#include "purplemain.h"
static HBITMAP GetBuddyIcon(gconstpointer lpvBuddyIconData, size_t cbBuddyIconData, int cxMax, int cyMax);
+static gpointer ConvertAndScaleBuddyIcon(const gchar *szFilename, PurplePlugin *lpplugin, gsize *lpcbImage);
+static void SetGlobalBuddyIcon(const gchar *szFilename);
@@ -275,3 +280,213 @@ HBITMAP PurpleGetBlistNodeIcon(PurpleBli
return hbitmap;
}
+
+
+/**
+ * Retrieves contents of an image file, scaled and converted into a format
+ * suitable for a buddy icon. Based very closely on Pidgin's
+ * pidgin_convert_buddy_icon.
+ *
+ * @param szFilename Filename of buddy icon.
+ * @param lpplugin Prpl.
+ * @param[out] lpcbImage Returns the length of the data. May be
+ * clobbered even on error.
+ *
+ * @return Image data, or NULL on error.
+ */
+static gpointer ConvertAndScaleBuddyIcon(const gchar *szFilename, PurplePlugin *lpplugin, gsize *lpcbImage)
+{
+ gchar *szTempFile = NULL;
+ PurplePluginProtocolInfo *lpprplinfo;
+ int cx, cy;
+ int i, j;
+ gchar **rgszPixbufFormats, **rgszPrplFormats;
+ BOOL bFormatSupported, bNeedScale;
+ GdkPixbufFormat *lppixbufformat;
+ gchar *lpcImage;
+
+ lpprplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(lpplugin);
+ if(!lpprplinfo->icon_spec.format)
+ return NULL;
+
+ lppixbufformat = gdk_pixbuf_get_file_info(szFilename, &cx, &cy);
+ if(!lppixbufformat)
+ return NULL;
+
+ /* Which formats does the prpl support? */
+ rgszPrplFormats = g_strsplit(lpprplinfo->icon_spec.format, ",", 0);
+
+ /* Attempt to match supported formats against the format of the file we
+ * were given.
+ */
+
+ rgszPixbufFormats = gdk_pixbuf_format_get_extensions(lppixbufformat);
+
+ bFormatSupported = FALSE;
+ for(i = 0; rgszPixbufFormats[i] && !bFormatSupported; i++)
+ for(j = 0; rgszPrplFormats[j] && !bFormatSupported; j++)
+ if(!g_ascii_strcasecmp(rgszPixbufFormats[i], rgszPrplFormats[j]))
+ bFormatSupported = TRUE;
+
+ /* We need to scale iff the prpl requires it and we're out of range. */
+ bNeedScale = (lpprplinfo->icon_spec.scale_rules & PURPLE_ICON_SCALE_SEND) &&
+ (cx < lpprplinfo->icon_spec.min_width ||
+ cx > lpprplinfo->icon_spec.max_width ||
+ cy < lpprplinfo->icon_spec.min_height ||
+ cy > lpprplinfo->icon_spec.max_height);
+
+ /* Mangle the image, unless it's already very nice. */
+ if(!bFormatSupported || bNeedScale)
+ {
+ GdkPixbuf *lppixbuf, *lppixbufScaled;
+ GError *lpgerror = NULL;
+
+ lppixbuf = gdk_pixbuf_new_from_file(szFilename, &lpgerror);
+
+ if(lppixbuf)
+ {
+ int cxScaled = cx, cyScaled = cy;
+ BOOL bHaveCompression = gdk_pixbuf_major_version > 2 || (gdk_pixbuf_major_version == 2 && gdk_pixbuf_minor_version >= 8);
+
+ /* Get aspect-correct scaled size. */
+ purple_buddy_icon_get_scale_size(&lpprplinfo->icon_spec, &cxScaled, &cyScaled);
+
+ lppixbufScaled = gdk_pixbuf_scale_simple(lppixbuf, cxScaled, cyScaled, GDK_INTERP_HYPER);
+ g_object_unref(lppixbuf);
+
+ szFilename = NULL;
+
+ /* Attempt each of the supported formats. */
+ for(i = 0; rgszPrplFormats[i]; i++)
+ {
+ FILE *lpfile;
+
+ /* libpurple's routine is less fiddly than
+ * GetTempFileName here.
+ */
+ if(!(lpfile = purple_mkstemp(&szTempFile, TRUE)))
+ break;
+ fclose(lpfile);
+
+ if(bHaveCompression && strcmp(rgszPrplFormats[i], "png") == 0)
+ {
+ if (gdk_pixbuf_save(lppixbufScaled, szTempFile, rgszPrplFormats[i], &lpgerror, "compression", "9", NULL))
+ break;
+ }
+ else if(gdk_pixbuf_save(lppixbufScaled, szTempFile, rgszPrplFormats[i], &lpgerror, NULL))
+ break;
+
+ /* If we get here, we failed. */
+ if(lpgerror)
+ {
+ g_error_free(lpgerror);
+ lpgerror = NULL;
+ }
+
+ g_unlink(szTempFile);
+ g_free(szTempFile);
+ szTempFile = NULL;
+ }
+ }
+ else
+ {
+ g_error_free(lpgerror);
+ szFilename = NULL;
+ }
+ }
+
+ g_strfreev(rgszPixbufFormats);
+ g_strfreev(rgszPrplFormats);
+
+ if(szFilename || (szFilename = szTempFile))
+ g_file_get_contents(szFilename, &lpcImage, lpcbImage, NULL);
+
+ if(szTempFile)
+ {
+ g_unlink(szTempFile);
+ g_free(szTempFile);
+ }
+
+ /* Make sure we're not too big. */
+ if((lpprplinfo->icon_spec.max_filesize != 0) && (*lpcbImage > lpprplinfo->icon_spec.max_filesize))
+ {
+ g_free(lpcImage);
+ return NULL;
+ }
+
+ return lpcImage;
+}
+
+
+/**
+ * Sets the buddy icons for accounts using the global icon, and instructs the
+ * UI to show the new icon. Called by the preference hook.
+ *
+ * @param szFilename Filename of buddy icon.
+ */
+static void SetGlobalBuddyIcon(const gchar *szFilename)
+{
+ GList *lpglistAccounts;
+ PurpleStoredImage *lpstoredimg;
+
+ for(lpglistAccounts = purple_accounts_get_all(); lpglistAccounts; lpglistAccounts = lpglistAccounts->next)
+ {
+ PurpleAccount *lpaccount = lpglistAccounts->data;
+ PurplePlugin *lpplugin = purple_find_prpl(purple_account_get_protocol_id(lpaccount));
+
+ if(lpplugin)
+ {
+ PurplePluginProtocolInfo *lpprplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(lpplugin);
+
+ /* Set icon for accounts that don't override the global
+ * setting.
+ */
+ if(lpprplinfo && purple_account_get_bool(lpaccount, "use-global-buddyicon", TRUE) && lpprplinfo->icon_spec.format)
+ {
+ if(szFilename)
+ {
+ gsize cbImage;
+ gpointer lpvImage = ConvertAndScaleBuddyIcon(szFilename, lpplugin, &cbImage);
+
+ purple_buddy_icons_set_account_icon(lpaccount, lpvImage, cbImage);
+ }
+ else
+ purple_buddy_icons_set_account_icon(lpaccount, NULL, 0);
+
+ purple_account_set_buddy_icon_path(lpaccount, szFilename);
+ }
+ }
+ }
+
+ if(szFilename)
+ {
+ /* Make an HBITMAP and send it back to the UI for display in
+ * the main window.
+ */
+ if((lpstoredimg = purple_imgstore_new_from_file(szFilename)))
+ {
+ HBITMAP hbmIcon = GetBuddyIcon(purple_imgstore_get_data(lpstoredimg), purple_imgstore_get_size(lpstoredimg), 0, 0);
+ VulturePostUIMessage(VUIMSG_NEWGLOBALBICON, hbmIcon);
+ purple_imgstore_unref(lpstoredimg);
+ }
+ }
+ else
+ VulturePostUIMessage(VUIMSG_NEWGLOBALBICON, NULL);
+}
+
+
+/**
+ * Called when the global buddy-icon preference is set.
+ *
+ * @param szName Unused.
+ * @param preftype Unused.
+ * @param lpvValue Filename of buddy icon.
+ * @param lpvData Unused.
+ */
+void PurpleGlobalBuddyIconPrefChanged(const char *szName, PurplePrefType preftype, gconstpointer lpvValue, gpointer lpvData)
+{
+ UNREFERENCED_PARAMETER(szName);
+ UNREFERENCED_PARAMETER(preftype);
+ UNREFERENCED_PARAMETER(lpvData);
+ SetGlobalBuddyIcon(lpvValue);
+}
============================================================
--- vulture/purplebicon.h ae61a69f5ec73d830e00c73ac9e1ae909da47d33
+++ vulture/purplebicon.h fb88563160f2a5847c33cf19aabb7e5a107c82d1
@@ -31,6 +31,7 @@ HBITMAP PurpleGetBlistNodeIcon(PurpleBli
HBITMAP PurpleGetIMBuddyIcon(PurpleConversation *lpconv, int cxMax, int cyMax);
HBITMAP PurpleGetBlistNodeIcon(PurpleBlistNode *lpblistnode, int cxMax, int cyMax);
+void PurpleGlobalBuddyIconPrefChanged(const char *szName, PurplePrefType preftype, gconstpointer lpvValue, gpointer lpvData);
#endif
============================================================
--- vulture/purplemain.c c5fe877770c6e70a9fb3946d34ee4eb085b741e2
+++ vulture/purplemain.c 966a55819a18db2e46a1df71291294f355c5288a
@@ -50,15 +50,14 @@
#include "purpleconv.h"
#include "purplestatus.h"
#include "purpleacct.h"
+#include "purplebicon.h"
-#define VULTURE_PREFS_ROOT "/vulture"
-
-
static UINT CALLBACK PurpleThread(void *lpvData);
static int InitLibpurple(void);
static void InitUI(void);
static void Quitting(void);
+static void InitPrefs(void);
static void LoadFlags(void);
static void SaveFlags(void);
@@ -152,6 +151,8 @@ static int InitLibpurple(void)
gchar *szCustomUserDir;
+ VultureSetDebugFromCmdLine();
+
if((szCustomUserDir = VultureGetCustomUserDir()))
purple_util_set_user_dir(szCustomUserDir);
@@ -210,6 +211,7 @@ static void InitUI(void)
NULL, /* reserved */
};
+ InitPrefs();
LoadFlags();
purple_blist_set_ui_ops(&s_blistuiops);
@@ -221,6 +223,8 @@ static void InitUI(void)
purple_signal_connect(purple_blist_get_handle(), "buddy-icon-changed", GINT_TO_POINTER(VSH_BLIST), PURPLE_CALLBACK(PurpleBuddyIconChanged), NULL);
purple_signal_connect(purple_connections_get_handle(), "signed-on", GINT_TO_POINTER(VSH_BLIST), PURPLE_CALLBACK(PurpleAccountSignedOn), NULL);
+ purple_prefs_connect_callback(GINT_TO_POINTER(VSH_BICON), VULTURE_PREFS_ROOT "/accounts/buddyicon", PurpleGlobalBuddyIconPrefChanged, NULL);
+
/* Create and load libpurple's buddy-list. */
purple_set_blist(purple_blist_new());
purple_blist_load();
@@ -325,14 +329,22 @@ void PurpleInsertDynamicMenu(HMENU hmenu
}
-/** Loads flags from libpurple's preference store. */
-static void LoadFlags(void)
+/** Preference initialisation. Call before any other preference functions. */
+static void InitPrefs(void)
{
purple_prefs_add_none(VULTURE_PREFS_ROOT);
purple_prefs_add_none(VULTURE_PREFS_ROOT "/blist");
purple_prefs_add_bool(VULTURE_PREFS_ROOT "/blist/show_offline_buddies", FALSE);
purple_prefs_add_bool(VULTURE_PREFS_ROOT "/blist/show_empty_groups", TRUE);
+ purple_prefs_add_none(VULTURE_PREFS_ROOT "/accounts");
+ purple_prefs_add_path(VULTURE_PREFS_ROOT "/accounts/buddyicon", "");
+}
+
+
+/** Loads flags from libpurple's preference store. */
+static void LoadFlags(void)
+{
g_vflags.bShowOffline = purple_prefs_get_bool(VULTURE_PREFS_ROOT "/blist/show_offline_buddies");
g_vflags.bShowEmptyGroups = purple_prefs_get_bool(VULTURE_PREFS_ROOT "/blist/show_empty_groups");
}
============================================================
--- vulture/purplemain.h e12b252d7f0db0b03520757baec24498284e11b4
+++ vulture/purplemain.h 4e5cdc340a212e1052581277ba026e6b936133f4
@@ -49,6 +49,10 @@ typedef struct _VULTURE_MAKE_CONTEXT_MEN
} VULTURE_MAKE_CONTEXT_MENU;
+
+#define VULTURE_PREFS_ROOT "/vulture"
+
+
/* The significance of lpvParam is given for each message. */
enum ENUM_VULTURE_UI_MESSAGES
{
@@ -91,6 +95,9 @@ enum ENUM_VULTURE_UI_MESSAGES
/* (VULTURE_BLIST_NODE*) Node whose cache should be invalidated. */
VUIMSG_INVALIDATEICONCACHE,
+
+ /* (HBITMAP) */
+ VUIMSG_NEWGLOBALBICON,
};
/* HandlEs for reigistering signal handlERs. */
@@ -98,7 +105,8 @@ enum ENUM_VULTURE_SIGNAL_HANDLES
{
VSH_STATUS = 1,
VSH_CONV,
- VSH_BLIST
+ VSH_BLIST,
+ VSH_BICON
};
void VultureInitLibpurple(HANDLE *lphthread);
============================================================
--- vulture/purplequeue.c 03539c890a5b7bb241d33a7a0eb3760588c3abf1
+++ vulture/purplequeue.c 1298af63d37b82bc9a75ff52630fa68d343da8be
@@ -441,6 +441,18 @@ static void DispatchPurpleCall(PURPLE_CA
PurpleAddGroup(lppurplecall->lpvParam);
break;
+ case PC_SETGLOBALBICON:
+ if(lppurplecall->lpvParam)
+ {
+ gchar *szFilename = VultureTCHARToUTF8(lppurplecall->lpvParam);
+ purple_prefs_set_path(VULTURE_PREFS_ROOT "/accounts/buddyicon", szFilename);
+ g_free(szFilename);
+ }
+ else
+ purple_prefs_set_path(VULTURE_PREFS_ROOT "/accounts/buddyicon", NULL);
+
+ break;
+
case PC_QUIT:
purple_core_quit();
g_main_loop_quit(g_lpgmainloop);
============================================================
--- vulture/purplequeue.h 1ae1750c146a3d98504655978f404e67afe00b2d
+++ vulture/purplequeue.h dfd8e22f3d026cfbe4e52c003d53c9060732fc3f
@@ -129,6 +129,9 @@ enum PURPLE_CALL_ID
/* (LPTSTR) Group name. */
PC_ADDGROUP,
+
+ /* (LPTSTR) Icon filename. */
+ PC_SETGLOBALBICON,
};
============================================================
--- vulture/resource.h b3f60211860b0038910dc212efe4b516fffdf262
+++ vulture/resource.h 75c8f9d017bf7975a2f954220476faecf5183480
@@ -73,6 +73,10 @@
#define IDM_CONV 1003
#define IDM_CONV_CONV_CLOSE 40401
+#define IDM_BUDDYICON_CONTEXT 1004
+#define IDM_BUDDYICON_CONTEXT_SET 40601
+#define IDM_BUDDYICON_CONTEXT_REMOVE 40602
+
/* For dynamic menu items not sent as WM_COMMAND notifications. */
#define IDM_DYNAMIC_FIRST 50000
============================================================
--- vulture/vulture-res.rc cc94c74d7b211e3e438bbac44f0e2f4e833a7ebc
+++ vulture/vulture-res.rc 212f7e33b0eecf590cc4821ed9acfa7f54578d44
@@ -140,7 +140,18 @@ IDM_BLIST_CONTEXT MENUEX
}
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+IDM_BUDDYICON_CONTEXT MENUEX
+{
+ POPUP "Dummy Label"
+ {
+ MENUITEM "&Set buddy icon...", IDM_BUDDYICON_CONTEXT_SET, MFT_STRING, MFS_DEFAULT
+ MENUITEM "&Remove buddy icon", IDM_BUDDYICON_CONTEXT_REMOVE, MFT_STRING
+ }
+}
+
+
//
// Dialog resources
//
@@ -309,7 +320,7 @@ FONT 8, "Ms Shell Dlg"
STYLE DS_3DLOOK | DS_CENTER | DS_CONTROL | DS_SHELLFONT | WS_VISIBLE | WS_CHILDWINDOW
FONT 8, "Ms Shell Dlg"
{
- CONTROL "", IDC_BUDDY_ICON, "Static", WS_TABSTOP | SS_BLACKFRAME | SS_NOTIFY | SS_SUNKEN, 5, 5, 30, 30
+ CONTROL "", IDC_BUDDY_ICON, "Static", WS_TABSTOP | SS_BITMAP | SS_CENTERIMAGE | SS_SUNKEN | SS_NOTIFY, 5, 5, 30, 30
CONTROL "1", IDC_CBEX_STATUS, "ComboBoxEx32", 0x50000003, 40, 5, 115, 90
EDITTEXT IDC_EDIT_STATUSMSG, 40, 20, 115, 15, ES_AUTOHSCROLL
}
@@ -331,7 +342,7 @@ STRINGTABLE
IDS_QUERY_DELGROUP "Deleting this group will also delete everything contained in it. Do you wish to continue?"
IDS_QUERY_DELCONTACT "Deleting this contact will also delete all buddies associated with it. Do you wish to continue?"
IDS_BUDDYICON_FILTER "Image Files\t*.bmp;*.png;*.gif;*.jpg\tAll Files (*.*)\t*.*\t"
- IDS_BUDDYICON_TITLE "Choose Custom Icon"
+ IDS_BUDDYICON_TITLE "Choose Icon"
IDS_OFFLINE "Offline"
}
============================================================
--- vulture/vulture.c 3d1f361ef52adb86342fd470fdd08f6c25e6f112
+++ vulture/vulture.c 262cf079fcb75236b313d1a1d4df2b7e0f8413e7
@@ -355,3 +355,16 @@ void VultureLoadAndFormatFilterString(US
}
while(*(++szInFilter));
}
+
+
+/**
+ * Our g_print handler. Only used when debugging is enabled.
+ *
+ * @param sz String to print.
+ */
+void VultureGPrintHandler(const gchar *sz)
+{
+ LPTSTR szT = VultureUTF8ToTCHAR(sz);
+ OutputDebugString(szT);
+ g_free(szT);
+}
============================================================
--- vulture/vulture.h 1ffdbf86f02bdb38494deb08c4f36995d25f9e43
+++ vulture/vulture.h 74f168560d192dfefc8e6bf1565af6c794d0d09d
@@ -77,6 +77,7 @@ void VultureLoadAndFormatFilterString(US
int VultureGetMenuPosFromID(HMENU hmenu, UINT uiID);
int VultureCommDlgOpen(HWND hwnd, LPTSTR szFileNameReturn, UINT cchFileNameReturn, LPCTSTR szTitle, LPCTSTR szFilter, LPCTSTR szDefExt, LPCTSTR szInitFilename, int iFlags);
void VultureLoadAndFormatFilterString(USHORT unStringID, LPTSTR szFilter, UINT cchFilter);
+void VultureGPrintHandler(const gchar *sz);
============================================================
--- vulture/vultureblist.c 29a9b7b5a6ecdf75e3ae0d5a4e3e11dba9a35248
+++ vulture/vultureblist.c 0ed8d48df54645a70195293f7854a6b4d19bb1fc
@@ -45,6 +45,7 @@ typedef struct _STATUSDLGDATA
typedef struct _STATUSDLGDATA
{
WNDPROC wndprocStatusMsgOrig;
+ WNDPROC wndprocBuddyIconOrig;
} STATUSDLGDATA;
/* MinGW doesn't have NMTVKEYDOWN. */
@@ -76,6 +77,9 @@ static void RequestAddGroup(HWND hwndPar
static void RequestAddChat(HWND hwndParent, LPTSTR szAlias, LPTSTR szInitGroup);
static void RequestAddBuddy(HWND hwndParent, LPTSTR szUsername, LPTSTR szAlias, LPTSTR szInitGroup);
static void RequestAddGroup(HWND hwndParent);
+static LRESULT CALLBACK BuddyIconSubclassProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam);
+static void ChooseBuddyIcon(void);
+static void ClearBuddyIcon(void);
#define BLIST_MARGIN 6
@@ -258,6 +262,14 @@ static LRESULT CALLBACK MainWndProc(HWND
case IDM_BLIST_ACCOUNTS_MANAGE:
ManageAccounts(hwnd);
return 0;
+
+ case IDM_BUDDYICON_CONTEXT_SET:
+ ChooseBuddyIcon();
+ return 0;
+
+ case IDM_BUDDYICON_CONTEXT_REMOVE:
+ ClearBuddyIcon();
+ return 0;
}
break;
@@ -395,6 +407,78 @@ static LRESULT CALLBACK MainWndProc(HWND
break;
+ case VUIMSG_NEWGLOBALBICON:
+ {
+ HBITMAP hbmScaled = NULL;
+ HBITMAP hbmOld;
+
+ if(lParam)
+ {
+ RECT rcIconCtrl;
+ HDC hdcSrc, hdcDest;
+ HBITMAP hbmSrcOrig, hbmDestOrig;
+ int cxScaled, cyScaled, cxIconCtrl, cyIconCtrl;
+ BITMAP bitmap;
+
+ GetClientRect(GetDlgItem(g_hwndStatusDlg, IDC_BUDDY_ICON), &rcIconCtrl);
+
+ hdcSrc = CreateCompatibleDC(NULL);
+ hdcDest = CreateCompatibleDC(NULL);
+
+ GetObject((HBITMAP)lParam, sizeof(bitmap), &bitmap);
+
+ cxIconCtrl = rcIconCtrl.right - rcIconCtrl.left;
+ cyIconCtrl = rcIconCtrl.bottom - rcIconCtrl.top;
+
+ /* Scale if necessary. */
+ if(bitmap.bmWidth > cxIconCtrl || bitmap.bmHeight > cyIconCtrl)
+ {
+ if(bitmap.bmWidth * cyIconCtrl > bitmap.bmHeight * cxIconCtrl)
+ {
+ /* Scale to fit width. */
+ cxScaled = cxIconCtrl;
+ cyScaled = MulDiv(bitmap.bmHeight, cxIconCtrl, bitmap.bmWidth);
+ }
+ else
+ {
+ /* Scaled to fit height. */
+ cxScaled = MulDiv(bitmap.bmWidth, cyIconCtrl, bitmap.bmHeight);
+ cyScaled = cyIconCtrl;
+ }
+ }
+ else
+ {
+ cxScaled = bitmap.bmWidth;
+ cyScaled = bitmap.bmHeight;
+ }
+
+ /* CreateCompatibleBitmap must be called after the first SelectObject
+ * so that we don't end up with a monochrome bitmap.
+ */
+ hbmSrcOrig = SelectObject(hdcSrc, (HBITMAP)lParam);
+ hbmScaled = CreateCompatibleBitmap(hdcSrc, cxScaled, cyScaled);
+ hbmDestOrig = SelectObject(hdcDest, hbmScaled);
+
+ SetStretchBltMode(hdcDest, COLORONCOLOR);
+
+ StretchBlt(hdcDest, 0, 0, cxScaled, cyScaled, hdcSrc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY);
+
+ SelectObject(hdcDest, hbmDestOrig);
+ SelectObject(hdcSrc, hbmSrcOrig);
+
+ DeleteDC(hdcDest);
+ DeleteDC(hdcSrc);
+
+ DeleteObject((HBITMAP)lParam);
+ }
+
+ hbmOld = (HBITMAP)SendDlgItemMessage(g_hwndStatusDlg, IDC_BUDDY_ICON, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmScaled);
+ if(hbmOld)
+ DeleteObject(hbmOld);
+ }
+
+ break;
+
case VUIMSG_QUIT:
DestroyWindow(hwnd);
break;
@@ -467,6 +551,7 @@ static INT_PTR CALLBACK StatusDlgProc(HW
RECT rcIcon;
POINT ptIcon;
HWND hwndStatusMsg = GetDlgItem(hwndDlg, IDC_EDIT_STATUSMSG);
+ HWND hwndBuddyIcon = GetDlgItem(hwndDlg, IDC_BUDDY_ICON);
STATUSDLGDATA *lpsdd;
GetWindowRect(GetDlgItem(hwndDlg, IDC_BUDDY_ICON), &rcIcon);
@@ -479,11 +564,13 @@ static INT_PTR CALLBACK StatusDlgProc(HW
*/
SetWindowPos(GetDlgItem(hwndDlg, IDC_BUDDY_ICON), NULL, BLIST_MARGIN, ptIcon.y, rcIcon.bottom - rcIcon.top, rcIcon.bottom - rcIcon.top, SWP_NOACTIVATE | SWP_NOZORDER);
- /* Subclass status message box. */
+ /* Subclass controls. */
lpsdd = ProcHeapAlloc(sizeof(STATUSDLGDATA));
lpsdd->wndprocStatusMsgOrig = (WNDPROC)GetWindowLongPtr(hwndStatusMsg, GWLP_WNDPROC);
+ lpsdd->wndprocBuddyIconOrig = (WNDPROC)GetWindowLongPtr(hwndBuddyIcon, GWLP_WNDPROC);
SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG)lpsdd);
SetWindowLongPtr(hwndStatusMsg, GWLP_WNDPROC, (LONG)StatusMsgBoxSubclassProc);
+ SetWindowLongPtr(hwndBuddyIcon, GWLP_WNDPROC, (LONG)BuddyIconSubclassProc);
/* Set the combo's image-lists, and tell it to
* recalculate its size.
@@ -557,6 +644,15 @@ static INT_PTR CALLBACK StatusDlgProc(HW
}
break;
+
+ case IDC_BUDDY_ICON:
+ if(HIWORD(wParam) == STN_CLICKED)
+ {
+ ChooseBuddyIcon();
+ return TRUE;
+ }
+
+ break;
}
break;
@@ -1665,3 +1761,70 @@ static void RequestAddGroup(HWND hwndPar
ProcHeapFree(szGroup);
}
}
+
+
+/**
+ * Subclassing window procedure for buddy-icon control in status dialogue.
+ *
+ * @param hwnd Input box window handle.
+ * @param uiMsg Message ID.
+ * @param wParam Message-specific.
+ * @param lParam Message-specific.
+ *
+ * @return Message-specific.
+ */
+static LRESULT CALLBACK BuddyIconSubclassProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
+{
+ STATUSDLGDATA *lpsdd;
+ HWND hwndParent = GetParent(hwnd);
+
+ switch(uiMsg)
+ {
+ /* Need >= Win98/2000 for IDC_HAND. */
+#if WINVER >= 0x0410
+ case WM_SETCURSOR:
+ SetCursor(LoadCursor(NULL, IDC_HAND));
+ return TRUE;
+#endif
+ case WM_RBUTTONUP:
+ {
+ HMENU hmenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDM_BUDDYICON_CONTEXT));
+ HMENU hmenuSubmenu = GetSubMenu(hmenu, 0);
+
+ POINT ptMouse = {(short)LOWORD(lParam), (short)HIWORD(lParam)};
+ ClientToScreen(hwnd, &ptMouse);
+
+ EnableMenuItem(hmenuSubmenu, IDM_BUDDYICON_CONTEXT_REMOVE, SendMessage(hwnd, STM_GETIMAGE, 0, 0) ? MF_ENABLED : MF_DISABLED);
+
+ TrackPopupMenu(hmenuSubmenu, TPM_RIGHTBUTTON, ptMouse.x, ptMouse.y, 0, g_hwndMain, NULL);
+ DestroyMenu(hmenu);
+ }
+
+ return 0;
+ }
+
+ lpsdd = (STATUSDLGDATA*)GetWindowLongPtr(hwndParent, GWLP_USERDATA);
+ return CallWindowProc(lpsdd->wndprocBuddyIconOrig, hwnd, uiMsg, wParam, lParam);
+}
+
+
+/** Prompts the user to set the global buddy icon. */
+static void ChooseBuddyIcon(void)
+{
+ TCHAR szFilename[MAX_PATH];
+ TCHAR szFilter[256];
+ TCHAR szTitle[256];
+
+ VultureLoadAndFormatFilterString(IDS_BUDDYICON_FILTER, szFilter, NUM_ELEMENTS(szFilter));
+ LoadString(g_hInstance, IDS_BUDDYICON_TITLE, szTitle, NUM_ELEMENTS(szTitle));
+
+ if(VultureCommDlgOpen(g_hwndMain, szFilename, NUM_ELEMENTS(szFilename), szTitle, szFilter, TEXT("png"), NULL, OFN_FILEMUSTEXIST | OFN_HIDEREADONLY))
+ VultureSingleSyncPurpleCall(PC_SETGLOBALBICON, szFilename);
+}
+
+
+/** Clears the global buddy icon. */
+static void ClearBuddyIcon(void)
+{
+ VultureSingleSyncPurpleCall(PC_SETGLOBALBICON, NULL);
+}
More information about the Commits
mailing list