From c0ea59aed73b5425ad78e6bdb6f8f12e2b44567e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= Date: Wed, 15 Sep 2010 18:40:20 +0200 Subject: Added Snarl notification support diff --git a/3rdParty/Snarl/SConscript b/3rdParty/Snarl/SConscript new file mode 100644 index 0000000..99900f7 --- /dev/null +++ b/3rdParty/Snarl/SConscript @@ -0,0 +1,17 @@ +Import("env") + +################################################################################ +# Flags +################################################################################ +if env.get("HAVE_SNARL", True) : + if env["SCONS_STAGE"] == "flags" : + env["SNARL_FLAGS"] = { + "CPPPATH": [Dir(".")], + "LIBPATH": [Dir(".")], + "LIBS": ["Snarl"], + } + + elif env["SCONS_STAGE"] == "build" : + myenv = env.Clone() + myenv.Replace(CCFLAGS = [flag for flag in env["CCFLAGS"] if flag not in ["-W", "-Wall"]]) + myenv.StaticLibrary("Snarl", ["SnarlInterface.cpp"], CPPPATH = ["."]) diff --git a/3rdParty/Snarl/SnarlInterface.cpp b/3rdParty/Snarl/SnarlInterface.cpp new file mode 100644 index 0000000..f287b8e --- /dev/null +++ b/3rdParty/Snarl/SnarlInterface.cpp @@ -0,0 +1,812 @@ +// About: +// Snarl C++ interface implementation +// To understand what the different functions do and what they return, please +// have a look at the API on http://www.fullphat.net/dev/api.htm. +// Also have a look at mSnarl_i.bas found in the CVS/SVN repository for Snarl. +// +// The functions in SnarlInterface both have ANSI(UTF8) and UNICODE versions. +// If the LPCWSTR (unicode) version of the functions are called, the strings +// are converted to UTF8 by SnarlInterface before sent to Snarl. So using the +// ANSI/UTF8/LPCSTR versions of the functions are faster! +// +// +// Difference to VB implementation: +// Please note that string functions return NULL when they fail and not an +// empty string. So check for NULL... +// Function names doesn't have the pre "sn". +// +// +// Authors: +// Written and maintained by Toke Noer Nøttrup +// Original C++ version by "Whitman" +// +// License etc. : +// Feel free to use this code and class as you see fit. +// If you improve the code, it would be nice of you to take contact to the +// authors, so all can get the benifit. +// +// There is no guarantee that the code is correct or functional. +// USE AT YOUR OWN RISK +//----------------------------------------------------------------------------- + +// History +// 2008/12/31 : Implemented V39 API +// : Moved SnarlInterface into new Snarl namespace and moved enums etc. out of class +// : Added WCHAR overloads for all functions +// 2008/08/27 : Fixed return value of IsMessageVisible and HideMessage (returns false on failure now) +// : Fixed critical error in the new overloaded UpdateMessage() function +// 2008/08/27 : x64 compiler fix +// 2008/08/24 : Renamed all functions to not have prepended "sn". +// : Memory allocation functions added. (Use FreeString to free strings returned by Snarl) +// : Added m_nLastMessageId to the class. (Use GetLastMessageId() to get it) +// : Overloaded a few functions, so one don't have include the message id. (UpdateMessage f.i.) + +// 2008/06/20 : Fixed snShowMessageEx so it actually sends the extended version - oops +// : Fixed compiler warning C4800: forcing value to bool 'true' or 'false' (performance warning) + +// 2008/05/19 : uSend and uSendEx would always set return value to M_OK on successfull call +// 2008/04/14 : Updated to follow (what should be) the final Snarl 2.0 API +// 2008/03/28 : Few fixes for Snarl 2.0 +// 2007/05/23 : snGetGlobalMsg & snGetSnarlWindow made static +// 2007/03/25 : 1.6 RC1 fixup +// 2007/03/04 : Added - snGetAppPath, snGetIconsPath, snGetVersionEx, +// snSetTimeout, uSendEx + + +#include "SnarlInterface.h" + +using namespace Snarl; + + +//----------------------------------------------------------------------------- +// Constructor/Destructor +//----------------------------------------------------------------------------- +SnarlInterface::SnarlInterface() +: m_hwndFrom(NULL), m_nLastMessageId(0) +{ + +} + +SnarlInterface::~SnarlInterface() +{ + +} + + +//----------------------------------------------------------------------------- +// snShowMessage() + +/// Displays a message with Title and Text. Timeout controls how long the +/// message is displayed for (in seconds) (omitting this value means the message +/// is displayed indefinately). IconPath specifies the location of a PNG image +/// which will be displayed alongside the message text. +/// Message Id on success or M_RESULT on failure + +LONG32 SnarlInterface::ShowMessage(LPCSTR szTitle, LPCSTR szText, LONG32 timeout, LPCSTR szIconPath, HWND hWndReply, WPARAM uReplyMsg) +{ + SNARLSTRUCT ss; + ZeroMemory((void*)&ss, sizeof(ss)); + + ss.Cmd = SNARL_SHOW; + StringCbCopyA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, szTitle); + StringCbCopyA((LPSTR)&ss.Text, SNARL_STRING_LENGTH, szText); + StringCbCopyA((LPSTR)&ss.Icon, SNARL_STRING_LENGTH, szIconPath); + ss.Timeout = timeout; + + ss.LngData2 = reinterpret_cast(hWndReply); + ss.Id = static_cast(uReplyMsg); + + m_nLastMessageId = Send(ss); + return m_nLastMessageId; +} + +LONG32 SnarlInterface::ShowMessage(LPCWSTR szTitle, LPCWSTR szText, LONG32 timeout, LPCWSTR szIconPath, HWND hWndReply, WPARAM uReplyMsg) +{ + LPSTR szUTF8Title = WideToUTF8(szTitle); + LPSTR szUTF8Text = WideToUTF8(szText); + LPSTR szUFT8IconPath = WideToUTF8(szIconPath); + + LONG32 result = ShowMessage(szUTF8Title, szUTF8Text, timeout, szUFT8IconPath, hWndReply, uReplyMsg); + + delete [] szUTF8Title; + delete [] szUTF8Text; + delete [] szUFT8IconPath; + + return result; +} + +//----------------------------------------------------------------------------- +// snShowMessageEx() + +/// Displays a notification. This function is identical to snShowMessage() +/// except that Class specifies an alert previously registered with +/// snRegisterAlert() and SoundFile can optionally specify a WAV sound to play +/// when the notification is displayed on screen. + +/// Message Id on success or M_RESULT on failure + +LONG32 SnarlInterface::ShowMessageEx(LPCSTR szClass, LPCSTR szTitle, LPCSTR szText, LONG32 timeout, LPCSTR szIconPath, HWND hWndReply, WPARAM uReplyMsg, LPCSTR szSoundFile) +{ + SNARLSTRUCTEX ssex; + ZeroMemory((void*)&ssex, sizeof(ssex)); + + ssex.Cmd = SNARL_EX_SHOW; + ssex.Timeout = timeout; + ssex.LngData2 = reinterpret_cast(hWndReply); + ssex.Id = static_cast(uReplyMsg); + + StringCbCopyA((LPSTR)&ssex.Class, SNARL_STRING_LENGTH, szClass); + StringCbCopyA((LPSTR)&ssex.Title, SNARL_STRING_LENGTH, szTitle); + StringCbCopyA((LPSTR)&ssex.Text, SNARL_STRING_LENGTH, szText); + StringCbCopyA((LPSTR)&ssex.Icon, SNARL_STRING_LENGTH, szIconPath); + StringCbCopyA((LPSTR)&ssex.Extra, SNARL_STRING_LENGTH, szSoundFile); + + m_nLastMessageId = Send(ssex); + return m_nLastMessageId; +} + +LONG32 SnarlInterface::ShowMessageEx(LPCWSTR szClass, LPCWSTR szTitle, LPCWSTR szText, LONG32 timeout, LPCWSTR szIconPath, HWND hWndReply, WPARAM uReplyMsg, LPCWSTR szSoundFile) +{ + LPSTR szUTF8Class = WideToUTF8(szClass); + LPSTR szUTF8Title = WideToUTF8(szTitle); + LPSTR szUTF8Text = WideToUTF8(szText); + LPSTR szUFT8IconPath = WideToUTF8(szIconPath); + LPSTR szUFT8SoundFile = WideToUTF8(szSoundFile); + + LONG32 result = ShowMessageEx(szUTF8Class, szUTF8Title, szUTF8Text, timeout, szUFT8IconPath, hWndReply, uReplyMsg, szUFT8SoundFile); + + delete [] szUTF8Class; + delete [] szUTF8Title; + delete [] szUTF8Text; + delete [] szUFT8IconPath; + delete [] szUFT8SoundFile; + + return result; +} + +//----------------------------------------------------------------------------- +// snHideMessage() + +/// Hides the notification specified by Id. Id is the value returned by +/// snShowMessage() or snShowMessageEx() when the notification was initially +/// created. This function returns True if the notification was successfully +/// hidden or False otherwise (for example, the notification may no longer exist). + +BOOL SnarlInterface::HideMessage(LONG32 Id) +{ + SNARLSTRUCT ss; + ss.Cmd = SNARL_HIDE; + ss.Id = Id; + + LONG32 n = Send(ss); + return (n == -1 || n == 1) ? TRUE : FALSE; +} + +BOOL SnarlInterface::HideMessage() +{ + return HideMessage(m_nLastMessageId); +} + +//----------------------------------------------------------------------------- +// snIsMessageVisible() + +/// Returns True if the notification specified by Id is still visible, or +/// False if not. Id is the value returned by snShowMessage() or +/// snShowMessageEx() when the notification was initially created. + +BOOL SnarlInterface::IsMessageVisible(LONG32 Id) +{ + SNARLSTRUCT ss; + ss.Cmd = SNARL_IS_VISIBLE; + ss.Id = Id; + + // We are getting -1 when true, checking for 1 just in case. We don't want to return true for the other M_RESULT returns + LONG32 n = Send(ss); + return (n == -1 || n == 1) ? TRUE : FALSE; +} + +BOOL SnarlInterface::IsMessageVisible() +{ + if (m_nLastMessageId == 0) + return FALSE; + + return IsMessageVisible(m_nLastMessageId); +} + +//----------------------------------------------------------------------------- +// snUpdateMessage() + +/// Changes the title and text in the message specified by Id to the values +/// specified by Title and Text respectively. Id is the value returned by +/// snShowMessage() or snShowMessageEx() when the notification was originally +/// created. To change the timeout parameter of a notification, use snSetTimeout() + +M_RESULT SnarlInterface::UpdateMessage(LONG32 id, LPCSTR szTitle, LPCSTR szText, LPCSTR szIconPath) +{ + SNARLSTRUCT ss; + ZeroMemory((void*)&ss, sizeof(ss)); + + ss.Cmd = SNARL_UPDATE; + ss.Id = id; + + StringCbCopyA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, szTitle); + StringCbCopyA((LPSTR)&ss.Text, SNARL_STRING_LENGTH, szText); + StringCbCopyA((LPSTR)&ss.Icon, SNARL_STRING_LENGTH, szIconPath); + + return static_cast(Send(ss)); +} + +M_RESULT SnarlInterface::UpdateMessage(LONG32 id, LPCWSTR szTitle, LPCWSTR szText, LPCWSTR szIconPath) +{ + LPSTR szParam1 = WideToUTF8(szTitle); + LPSTR szParam2 = WideToUTF8(szText); + LPSTR szParam3 = WideToUTF8(szIconPath); + + M_RESULT result = UpdateMessage(id, szParam1, szParam2, szParam3); + + delete [] szParam1; + delete [] szParam2; + delete [] szParam3; + + return result; +} + +M_RESULT SnarlInterface::UpdateMessage(LPCSTR szTitle, LPCSTR szText, LPCSTR szIconPath) +{ + return UpdateMessage(m_nLastMessageId, szTitle, szText, szIconPath); +} + +M_RESULT SnarlInterface::UpdateMessage(LPCWSTR szTitle, LPCWSTR szText, LPCWSTR szIconPath) +{ + return UpdateMessage(m_nLastMessageId, szTitle, szText, szIconPath); +} + +//----------------------------------------------------------------------------- +// snRegisterConfig + +/// Registers an application's configuration interface with Snarl. +/// AppName is the text that's displayed in the Applications list so it should +/// be people friendly ("My cool app" rather than "my_cool_app"). + +M_RESULT SnarlInterface::RegisterConfig(HWND hWnd, LPCSTR szAppName, LONG32 replyMsg) +{ + return RegisterConfig2(hWnd, szAppName, replyMsg, ""); +} + +M_RESULT SnarlInterface::RegisterConfig(HWND hWnd, LPCWSTR szAppName, LONG32 replyMsg) +{ + return RegisterConfig2(hWnd, szAppName, replyMsg, L""); +} + +//----------------------------------------------------------------------------- +// snRegisterConfig2 + +/// Registers an application's configuration interface with Snarl. +/// This function is identical to snRegisterConfig() except that Icon can be +/// used to specify a PNG image which will be displayed against the +/// application's entry in Snarl's Preferences panel. + +M_RESULT SnarlInterface::RegisterConfig2(HWND hWnd, LPCSTR szAppName, LONG32 replyMsg, LPCSTR szIcon) +{ + if (!szAppName || !szIcon) + return M_BAD_POINTER; + + SNARLSTRUCT ss; + + m_hwndFrom = hWnd; + + ss.Cmd = SNARL_REGISTER_CONFIG_WINDOW_2; + ss.LngData2 = reinterpret_cast(hWnd); + ss.Id = replyMsg; + StringCbCopyA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, szAppName); + StringCbCopyA((LPSTR)&ss.Icon, SNARL_STRING_LENGTH, szIcon); + + return static_cast(Send(ss)); +} + +M_RESULT SnarlInterface::RegisterConfig2(HWND hWnd, LPCWSTR szAppName, LONG32 replyMsg, LPCWSTR szIcon) +{ + LPSTR szParam1 = WideToUTF8(szAppName); + LPSTR szParam2 = WideToUTF8(szIcon); + + M_RESULT result = RegisterConfig2(hWnd, szParam1, replyMsg, szParam2); + + delete [] szParam1; + delete [] szParam2; + + return result; +} + + +//----------------------------------------------------------------------------- +// snRevokeConfig + +/// Removes the application previously registered using snRegisterConfig() or +/// snRegisterConfig2(). hWnd should be the same as that used during registration. + +M_RESULT SnarlInterface::RevokeConfig(HWND hWnd) +{ + SNARLSTRUCT ss; + + m_hwndFrom = NULL; + + ss.Cmd = SNARL_REVOKE_CONFIG_WINDOW; + ss.LngData2 = reinterpret_cast(hWnd); + + return static_cast(Send(ss)); +} + + +//----------------------------------------------------------------------------- +// snGetVersion() + +/// Checks if Snarl is currently running and, if it is, retrieves the major and +/// minor release version numbers in Major and Minor respectively. +/// Returns True if Snarl is running, False otherwise. + +BOOL SnarlInterface::GetVersion(WORD* Major, WORD* Minor) +{ + SNARLSTRUCT ss; + ss.Cmd = SNARL_GET_VERSION; + LONG32 versionInfo = Send(ss); + if (versionInfo > 0 && versionInfo != M_FAILED && versionInfo != M_TIMED_OUT) { + *Major = HIWORD(versionInfo); + *Minor = LOWORD(versionInfo); + return TRUE; + } + return FALSE; +} + + +//----------------------------------------------------------------------------- +// snGetVersionEx + +/// Returns the Snarl system version number. This is an integer value which +/// represents the system build number and can be used to identify the specific +/// version of Snarl running + +LONG32 SnarlInterface::GetVersionEx() +{ + SNARLSTRUCT ss; + ss.Cmd = SNARL_GET_VERSION_EX; + return Send(ss); +} + + +//----------------------------------------------------------------------------- +// snSetTimeout() + +/// Sets the timeout of existing notification Id to Timeout seconds. Id is the +/// value returned by snShowMessage() or snShowMessageEx() when the notification +/// was first created. + +M_RESULT SnarlInterface::SetTimeout(LONG32 Id, LONG32 Timeout) +{ + SNARLSTRUCT ss; + ss.Cmd = SNARL_SET_TIMEOUT; + ss.Id = Id; + ss.LngData2 = Timeout; + + return static_cast(Send(ss)); +} + +M_RESULT SnarlInterface::SetTimeout(LONG32 Timeout) +{ + return SetTimeout(m_nLastMessageId, Timeout); +} + +//----------------------------------------------------------------------------- +// snRegisterAlert() + +/// Registers an alert of Class for application AppName which must have previously +/// been registered with either snRegisterConfig() or snRegisterConfig2(). + +M_RESULT SnarlInterface::RegisterAlert(LPCSTR szAppName, LPCSTR szClass) +{ + SNARLSTRUCT ss; + ss.Cmd = SNARL_REGISTER_ALERT; + StringCbCopyA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, szAppName); + StringCbCopyA((LPSTR)&ss.Text, SNARL_STRING_LENGTH, szClass); + + return static_cast(Send(ss)); +} + +M_RESULT SnarlInterface::RegisterAlert(LPCWSTR szAppName, LPCWSTR szClass) +{ + LPSTR szParam1 = WideToUTF8(szAppName); + LPSTR szParam2 = WideToUTF8(szClass); + + M_RESULT result = RegisterAlert(szParam1, szParam2); + + delete [] szParam1; + delete [] szParam2; + + return result; +} + +//----------------------------------------------------------------------------- +// snGetGlobalMsg() + +/// Returns the atom that corresponds to the "SnarlGlobalEvent" registered +/// Windows message. This message is sent by Snarl when it is first starts and +/// when it shuts down. + +LONG32 SnarlInterface::GetGlobalMsg() +{ + return RegisterWindowMessage(SNARL_GLOBAL_MSG); +} + + +//----------------------------------------------------------------------------- +// snGetSnarlWindow + +HWND SnarlInterface::GetSnarlWindow() +{ + return FindWindow(NULL, _T("Snarl")); +} + + +//----------------------------------------------------------------------------- +// snGetAppPath() + +/// Returns a pointer to the path. +/// ** Remember to call FreeString + +LPCTSTR SnarlInterface::GetAppPath() +{ + HWND hWnd = GetSnarlWindow(); + if (hWnd) + { + HWND hWndPath = FindWindowEx(hWnd, 0, _T("static"), NULL); + if (hWndPath) + { + TCHAR strTmp[MAX_PATH] = {0}; + int nReturn = GetWindowText(hWndPath, strTmp, MAX_PATH); + if (nReturn > 0) { + TCHAR* strReturn = AllocateString(nReturn + 1); + StringCchCopy(strReturn, nReturn + 1, strTmp); + return strReturn; + } + } + } + return NULL; +} + + +//----------------------------------------------------------------------------- +// snGetIconsPath() + +/// Returns a pointer to the iconpath. +/// ** Remember to call FreeString when done with the string + +LPCTSTR SnarlInterface::GetIconsPath() +{ + TCHAR* szIconPath = NULL; + LPCTSTR szPath = GetAppPath(); + if (!szPath) + return NULL; + + size_t nLen = 0; + if (SUCCEEDED(StringCbLength(szPath, MAX_PATH, &nLen))) + { + nLen += 10 + 1; // etc\\icons\\ + NULL + szIconPath = AllocateString(nLen); + + StringCbCopy(szIconPath, nLen * sizeof(TCHAR), szPath); + StringCbCat(szIconPath, nLen * sizeof(TCHAR), _T("etc\\icons\\")); + } + + FreeString(szPath); + + return szIconPath; +} + + +//----------------------------------------------------------------------------- +// snSetAsSnarlApp() + +/// Identifies an application as a Snarl App. (V39) + +void SnarlInterface::SetAsSnarlApp(HWND hWndOwner, SNARL_APP_FLAGS Flags) +{ + if (IsWindow(hWndOwner)) { + SetProp(hWndOwner, _T("snarl_app"), reinterpret_cast(1)); + SetProp(hWndOwner, _T("snarl_app_flags"), reinterpret_cast(Flags)); + } +} + + +//----------------------------------------------------------------------------- +// snGetAppMsg() + +/// Returns the global Snarl Application message (V39) + +LONG32 SnarlInterface::GetAppMsg() +{ + return RegisterWindowMessage(SNARL_APP_MSG); +} + + +//----------------------------------------------------------------------------- +// snRegisterApp() + +/// Registers an application with Snarl (V39) + +M_RESULT SnarlInterface::RegisterApp(LPCSTR Application, LPCSTR SmallIcon, LPCSTR LargeIcon, HWND hWnd, LONG32 ReplyMsg) +{ + m_hwndFrom = hWnd; + + SNARLSTRUCT ss; + ss.Cmd = SNARL_REGISTER_APP; + + StringCbCopyA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, Application); + StringCbCopyA((LPSTR)&ss.Icon, SNARL_STRING_LENGTH, SmallIcon); + StringCbCopyA((LPSTR)&ss.Text, SNARL_STRING_LENGTH, LargeIcon); + + ss.LngData2 = reinterpret_cast(hWnd); + ss.Id = ReplyMsg; + ss.Timeout = GetCurrentProcessId(); + + return static_cast(Send(ss)); +} + +M_RESULT SnarlInterface::RegisterApp(LPCWSTR Application, LPCWSTR SmallIcon, LPCWSTR LargeIcon, HWND hWnd, LONG32 ReplyMsg) +{ + LPSTR szParam1 = WideToUTF8(Application); + LPSTR szParam2 = WideToUTF8(SmallIcon); + LPSTR szParam3 = WideToUTF8(LargeIcon); + + M_RESULT result = RegisterApp(szParam1, szParam2, szParam3, hWnd, ReplyMsg); + + delete [] szParam1; + delete [] szParam2; + delete [] szParam3; + + return result; +} + + +//----------------------------------------------------------------------------- +// snUnregisterApp() + +/// Unregisters an application with Snarl (V39) + +M_RESULT SnarlInterface::UnregisterApp() +{ + SNARLSTRUCT ss; + ss.Cmd = SNARL_UNREGISTER_APP; + ss.LngData2 = GetCurrentProcessId(); + + m_hwndFrom = NULL; + + return static_cast(Send(ss)); +} + + +//----------------------------------------------------------------------------- +// snShowNotification() + +/// Displays a Snarl notification using registered class (V39) +/// Message Id on success or M_RESULT on failure + +LONG32 SnarlInterface::ShowNotification(LPCSTR Class, LPCSTR Title, LPCSTR Text, LONG32 Timeout, LPCSTR Icon, HWND hWndReply, LONG32 uReplyMsg, LPCSTR Sound) +{ + SNARLSTRUCTEX ssex; + ssex.Cmd = SNARL_SHOW_NOTIFICATION; + + StringCbCopyExA((LPSTR)&ssex.Title, SNARL_STRING_LENGTH, Title, NULL, NULL, STRSAFE_IGNORE_NULLS); + StringCbCopyExA((LPSTR)&ssex.Text, SNARL_STRING_LENGTH, Text, NULL, NULL, STRSAFE_IGNORE_NULLS); + StringCbCopyExA((LPSTR)&ssex.Icon, SNARL_STRING_LENGTH, Icon, NULL, NULL, STRSAFE_IGNORE_NULLS); + + ssex.Timeout = Timeout; + ssex.LngData2 = reinterpret_cast(hWndReply); + ssex.Id = uReplyMsg; + + StringCbCopyExA((LPSTR)&ssex.Extra, SNARL_STRING_LENGTH, Sound, NULL, NULL, STRSAFE_IGNORE_NULLS); + StringCbCopyA((LPSTR)&ssex.Class, SNARL_STRING_LENGTH, Class); + + ssex.Reserved1 = GetCurrentProcessId(); + + m_nLastMessageId = Send(ssex); + return m_nLastMessageId; +} + +LONG32 SnarlInterface::ShowNotification(LPCWSTR Class, LPCWSTR Title, LPCWSTR Text, LONG32 Timeout, LPCWSTR Icon, HWND hWndReply, LONG32 uReplyMsg, LPCWSTR Sound) +{ + LPSTR szParam1 = WideToUTF8(Class); + LPSTR szParam2 = WideToUTF8(Title); + LPSTR szParam3 = WideToUTF8(Text); + LPSTR szParam4 = WideToUTF8(Icon); + LPSTR szParam5 = WideToUTF8(Sound); + + LONG32 result = ShowNotification(szParam1, szParam2, szParam3, Timeout, szParam4, hWndReply, uReplyMsg, szParam5); + + delete [] szParam1; + delete [] szParam2; + delete [] szParam3; + delete [] szParam4; + delete [] szParam5; + + return result; +} + + +//----------------------------------------------------------------------------- +// snChangeAttribute() + +/// (V39) + +M_RESULT SnarlInterface::ChangeAttribute(LONG32 Id, SNARL_ATTRIBUTES Attr, LPCSTR Value) +{ + SNARLSTRUCT ss; + ss.Cmd = SNARL_CHANGE_ATTR; + ss.Id = Id; + ss.LngData2 = Attr; + + StringCbCopyExA((LPSTR)&ss.Text, SNARL_STRING_LENGTH, Value, NULL, NULL, STRSAFE_IGNORE_NULLS); + + return static_cast(Send(ss)); +} + +M_RESULT SnarlInterface::ChangeAttribute(LONG32 Id, SNARL_ATTRIBUTES Attr, LPCWSTR Value) +{ + LPSTR szParam1 = WideToUTF8(Value); + + M_RESULT result = ChangeAttribute(Id, Attr, szParam1); + + delete [] szParam1; + + return result; +} + +M_RESULT SnarlInterface::ChangeAttribute(SNARL_ATTRIBUTES Attr, LPCSTR Value) +{ + return ChangeAttribute(m_nLastMessageId, Attr, Value); +} + +M_RESULT SnarlInterface::ChangeAttribute(SNARL_ATTRIBUTES Attr, LPCWSTR Value) +{ + return ChangeAttribute(m_nLastMessageId, Attr, Value); +} + + +//----------------------------------------------------------------------------- +// snSetClassDefault() + +/// Sets the default value for an alert class (V39) + +M_RESULT SnarlInterface::SetClassDefault(LPCSTR Class, SNARL_ATTRIBUTES Attr, LPCSTR Value) +{ + SNARLSTRUCT ss; + ss.Cmd = SNARL_SET_CLASS_DEFAULT; + ss.LngData2 = Attr; + ss.Timeout = GetCurrentProcessId(); + + StringCbCopyExA((LPSTR)&ss.Text, SNARL_STRING_LENGTH, Class, NULL, NULL, STRSAFE_IGNORE_NULLS); + StringCbCopyExA((LPSTR)&ss.Icon, SNARL_STRING_LENGTH, Value, NULL, NULL, STRSAFE_IGNORE_NULLS); + + return static_cast(Send(ss)); +} + +M_RESULT SnarlInterface::SetClassDefault(LPCWSTR Class, SNARL_ATTRIBUTES Attr, LPCWSTR Value) +{ + LPSTR szParam1 = WideToUTF8(Class); + LPSTR szParam2 = WideToUTF8(Value); + + M_RESULT result = SetClassDefault(szParam1, Attr, szParam2); + + delete [] szParam1; + delete [] szParam2; + + return result; +} + + +//----------------------------------------------------------------------------- +// snGetRevision() + +/// Gets the current Snarl revision (build) number (V39) +/// Returns the build version number, or M_RESULT on failure. + +LONG32 SnarlInterface::GetRevision() +{ + SNARLSTRUCT ss; + ss.Cmd = SNARL_GET_REVISION; + ss.LngData2 = 0xFFFE; + + return Send(ss); +} + + +//----------------------------------------------------------------------------- +// snAddClass() + +/// (V39) + +M_RESULT SnarlInterface::AddClass(LPCSTR Class, LPCSTR Description, SNARL_CLASS_FLAGS Flags, LPCSTR DefaultTitle, LPCSTR DefaultIcon, LONG32 DefaultTimeout) +{ + SNARLSTRUCT ss; + ss.Cmd = SNARL_ADD_CLASS; + ss.LngData2 = Flags; + ss.Timeout = GetCurrentProcessId(); + + StringCbCopyExA((LPSTR)&ss.Text, SNARL_STRING_LENGTH, Class, NULL, NULL, STRSAFE_IGNORE_NULLS); + StringCbCopyExA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, Description, NULL, NULL, STRSAFE_IGNORE_NULLS); + + LONG32 result = Send(ss); + + if (static_cast(result) == M_OK) + { + SetClassDefault(Class, SNARL_ATTRIBUTE_TITLE, DefaultTitle); + SetClassDefault(Class, SNARL_ATTRIBUTE_ICON, DefaultIcon); + if (DefaultTimeout > 0) { + char str[64] = {0}; + StringCbPrintfA((LPSTR)&str, sizeof(str), "%d", DefaultTimeout); + SetClassDefault(Class, SNARL_ATTRIBUTE_TIMEOUT, str); + } + + return M_OK; + } + else + return M_FAILED; +} + +M_RESULT SnarlInterface::AddClass(LPCWSTR Class, LPCWSTR Description, SNARL_CLASS_FLAGS Flags, LPCWSTR DefaultTitle, LPCWSTR DefaultIcon, LONG32 DefaultTimeout) +{ + LPCSTR szClass = WideToUTF8(Class); + LPCSTR szDescription = WideToUTF8(Description); + LPCSTR szDefaultTitle = WideToUTF8(DefaultTitle); + LPCSTR szDefaultIcon = WideToUTF8(DefaultIcon); + + M_RESULT result = AddClass(szClass, szDescription, Flags, szDefaultTitle, szDefaultIcon, DefaultTimeout); + + delete [] szClass; + delete [] szDescription; + delete [] szDefaultTitle; + delete [] szDefaultIcon; + + return result; +} + +//----------------------------------------------------------------------------- +// Private functions +//----------------------------------------------------------------------------- + +template +LONG32 SnarlInterface::Send(T ss) +{ + DWORD_PTR nReturn = M_FAILED; + + HWND hWnd = GetSnarlWindow(); + if (IsWindow(hWnd)) + { + COPYDATASTRUCT cds; + cds.dwData = 2; + cds.cbData = sizeof(ss); + cds.lpData = &ss; + + if (SendMessageTimeout(hWnd, WM_COPYDATA, (WPARAM)m_hwndFrom, (LPARAM)&cds, SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 1000, &nReturn) == 0) + { + if (GetLastError() == ERROR_TIMEOUT) + nReturn = M_TIMED_OUT; + } + } + + return static_cast(nReturn); +} + +//----------------------------------------------------------------------------- + +// Remember to : delete [] returned string + +LPSTR SnarlInterface::WideToUTF8(LPCWSTR szWideStr) +{ + if (szWideStr == NULL) + return NULL; + + int nSize = WideCharToMultiByte(CP_UTF8, 0, szWideStr, -1, NULL, 0, NULL, NULL); + LPSTR szUTF8 = new char[nSize]; + WideCharToMultiByte(CP_UTF8, 0, szWideStr, -1, szUTF8, nSize, NULL, NULL); + + return szUTF8; +} \ No newline at end of file diff --git a/3rdParty/Snarl/SnarlInterface.h b/3rdParty/Snarl/SnarlInterface.h new file mode 100644 index 0000000..137d597 --- /dev/null +++ b/3rdParty/Snarl/SnarlInterface.h @@ -0,0 +1,218 @@ +#ifndef SNARL_INTERFACE +#define SNARL_INTERFACE + +#include +#include +#include + + +namespace Snarl { + + static const LPCTSTR SNARL_GLOBAL_MSG = _T("SnarlGlobalEvent"); + static const LPCTSTR SNARL_APP_MSG = _T("SnarlAppMessage"); + + static const int SNARL_STRING_LENGTH = 1024; + static const int SNARL_UNICODE_LENGTH = SNARL_STRING_LENGTH / 2; + + static const LONG32 SNARL_LAUNCHED = 1; // Snarl has just started running + static const LONG32 SNARL_QUIT = 2; // Snarl is about to stop running + static const LONG32 SNARL_ASK_APPLET_VER = 3; // (R1.5) Reserved for future use + static const LONG32 SNARL_SHOW_APP_UI = 4; // (R1.6) Application should show its UI + + static const LONG32 SNARL_NOTIFICATION_CLICKED = 32; // notification was right-clicked by user + static const LONG32 SNARL_NOTIFICATION_TIMED_OUT = 33; + static const LONG32 SNARL_NOTIFICATION_ACK = 34; // notification was left-clicked by user + static const LONG32 SNARL_NOTIFICATION_MENU = 35; // V39 - menu item selected + static const LONG32 SNARL_NOTIFICATION_MIDDLE_BUTTON = 36; // V39 - notification middle-clicked by user + static const LONG32 SNARL_NOTIFICATION_CLOSED = 37; // V39 - user clicked the close gadget + + static const LONG32 SNARL_NOTIFICATION_CANCELLED = SNARL_NOTIFICATION_CLICKED; // Added in R1.6 + + static const DWORD WM_SNARLTEST = WM_USER + 237; // note hardcoded WM_USER value! + + // -------------------------------------------------------------------- + + typedef enum M_RESULT { + M_ABORTED = 0x80000007, + M_ACCESS_DENIED = 0x80000009, + M_ALREADY_EXISTS = 0x8000000C, + M_BAD_HANDLE = 0x80000006, + M_BAD_POINTER = 0x80000005, + M_FAILED = 0x80000008, + M_INVALID_ARGS = 0x80000003, + M_NO_INTERFACE = 0x80000004, + M_NOT_FOUND = 0x8000000B, + M_NOT_IMPLEMENTED = 0x80000001, + M_OK = 0x00000000, + M_OUT_OF_MEMORY = 0x80000002, + M_TIMED_OUT = 0x8000000A + }; + + enum SNARL_COMMANDS { + SNARL_SHOW = 1, + SNARL_HIDE, + SNARL_UPDATE, + SNARL_IS_VISIBLE, + SNARL_GET_VERSION, + SNARL_REGISTER_CONFIG_WINDOW, + SNARL_REVOKE_CONFIG_WINDOW, + + /* R1.6 onwards */ + SNARL_REGISTER_ALERT, + SNARL_REVOKE_ALERT, // for future use + SNARL_REGISTER_CONFIG_WINDOW_2, + SNARL_GET_VERSION_EX, + SNARL_SET_TIMEOUT, + + /* following introduced in Snarl V39 (R2.1) */ + SNARL_SET_CLASS_DEFAULT, + SNARL_CHANGE_ATTR, + SNARL_REGISTER_APP, + SNARL_UNREGISTER_APP, + SNARL_ADD_CLASS, + + /* extended commands (all use SNARLSTRUCTEX) */ + SNARL_EX_SHOW = 0x20, + SNARL_SHOW_NOTIFICATION // V39 + }; + + static const SNARL_COMMANDS SNARL_GET_REVISION = SNARL_REVOKE_ALERT; + + typedef enum SNARL_APP_FLAGS { + SNARL_APP_HAS_PREFS = 1, + SNARL_APP_HAS_ABOUT = 2 + }; + + static const LONG32 SNARL_APP_PREFS = 1; + static const LONG32 SNARL_APP_ABOUT = 2; + + + /* --------------- V39 additions --------------- */ + + /* snAddClass() flags */ + enum SNARL_CLASS_FLAGS { + SNARL_CLASS_ENABLED = 0, + SNARL_CLASS_DISABLED = 1, + SNARL_CLASS_NO_DUPLICATES = 2, // means Snarl will suppress duplicate notifications + SNARL_CLASS_DELAY_DUPLICATES = 4 // means Snarl will suppress duplicate notifications within a pre-set time period + }; + + /* Class attributes */ + typedef enum SNARL_ATTRIBUTES { + SNARL_ATTRIBUTE_TITLE = 1, + SNARL_ATTRIBUTE_TEXT, + SNARL_ATTRIBUTE_ICON, + SNARL_ATTRIBUTE_TIMEOUT, + SNARL_ATTRIBUTE_SOUND, + SNARL_ATTRIBUTE_ACK, // file to run on ACK + SNARL_ATTRIBUTE_MENU + }; + + /* ------------------- end of V39 additions ------------------ */ + + struct SNARLSTRUCT { + SNARL_COMMANDS Cmd; + LONG32 Id; + LONG32 Timeout; + LONG32 LngData2; + char Title[SNARL_STRING_LENGTH]; + char Text[SNARL_STRING_LENGTH]; + char Icon[SNARL_STRING_LENGTH]; + }; + + struct SNARLSTRUCTEX { + SNARL_COMMANDS Cmd; + LONG32 Id; + LONG32 Timeout; + LONG32 LngData2; + char Title[SNARL_STRING_LENGTH]; + char Text[SNARL_STRING_LENGTH]; + char Icon[SNARL_STRING_LENGTH]; + + char Class[SNARL_STRING_LENGTH]; + char Extra[SNARL_STRING_LENGTH]; + char Extra2[SNARL_STRING_LENGTH]; + LONG32 Reserved1; + LONG32 Reserved2; + }; + + + + // ------------------------------------------------------------------------ + // SnarlInterface class definition + // ------------------------------------------------------------------------ + + class SnarlInterface { + public: + SnarlInterface(); + ~SnarlInterface(); + + static HWND GetSnarlWindow(); + static LONG32 GetGlobalMsg(); + + + LPTSTR AllocateString(size_t n) { return new TCHAR[n]; } + void FreeString(LPCTSTR str) { delete [] str; str = NULL; } + + + LONG32 ShowMessage(LPCSTR szTitle, LPCSTR szText, LONG32 timeout = 0, LPCSTR szIconPath = "", HWND hWndReply = NULL, WPARAM uReplyMsg = 0); + LONG32 ShowMessage(LPCWSTR szTitle, LPCWSTR szText, LONG32 timeout = 0, LPCWSTR szIconPath = L"", HWND hWndReply = NULL, WPARAM uReplyMsg = 0); + LONG32 ShowMessageEx(LPCSTR szClass, LPCSTR szTitle, LPCSTR szText, LONG32 timeout = 0, LPCSTR szIconPath = "", HWND hWndReply = NULL, WPARAM uReplyMsg = 0, LPCSTR szSoundFile = ""); + LONG32 ShowMessageEx(LPCWSTR szClass, LPCWSTR szTitle, LPCWSTR szText, LONG32 timeout = 0, LPCWSTR szIconPath = L"", HWND hWndReply = NULL, WPARAM uReplyMsg = 0, LPCWSTR szSoundFile = L""); + + LPCTSTR GetAppPath(); // ** Remember to FreeString when finished with the string ! + LPCTSTR GetIconsPath(); // ** Remember to FreeString when finished with the string ! + + BOOL GetVersion(WORD* Major, WORD* Minor); + LONG32 GetVersionEx(); + BOOL HideMessage(); + BOOL HideMessage(LONG32 Id); + BOOL IsMessageVisible(); + BOOL IsMessageVisible(LONG32 Id); + M_RESULT RegisterAlert(LPCSTR szAppName, LPCSTR szClass); + M_RESULT RegisterAlert(LPCWSTR szAppName, LPCWSTR szClass); + M_RESULT RegisterConfig(HWND hWnd, LPCSTR szAppName, LONG32 replyMsg); + M_RESULT RegisterConfig(HWND hWnd, LPCWSTR szAppName, LONG32 replyMsg); + M_RESULT RegisterConfig2(HWND hWnd, LPCSTR szAppName, LONG32 replyMsg, LPCSTR szIcon); + M_RESULT RegisterConfig2(HWND hWnd, LPCWSTR szAppName, LONG32 replyMsg, LPCWSTR szIcon); + M_RESULT RevokeConfig(HWND hWnd); + M_RESULT SetTimeout(LONG32 Timeout); + M_RESULT SetTimeout(LONG32 Id, LONG32 Timeout); + M_RESULT UpdateMessage(LPCSTR szTitle, LPCSTR szText, LPCSTR szIconPath = ""); + M_RESULT UpdateMessage(LPCWSTR szTitle, LPCWSTR szText, LPCWSTR szIconPath = L""); + M_RESULT UpdateMessage(LONG32 Id, LPCSTR szTitle, LPCSTR szText, LPCSTR szIconPath = ""); + M_RESULT UpdateMessage(LONG32 Id, LPCWSTR szTitle, LPCWSTR szText, LPCWSTR szIconPath = L""); + + /* V39 */ + M_RESULT AddClass(LPCSTR Class, LPCSTR Description = NULL, SNARL_CLASS_FLAGS Flags = SNARL_CLASS_ENABLED, LPCSTR DefaultTitle = NULL, LPCSTR DefaultIcon = NULL, LONG32 DefaultTimeout = 0); + M_RESULT AddClass(LPCWSTR Class, LPCWSTR Description = NULL, SNARL_CLASS_FLAGS Flags = SNARL_CLASS_ENABLED, LPCWSTR DefaultTitle = NULL, LPCWSTR DefaultIcon = NULL, LONG32 DefaultTimeout = 0); + M_RESULT ChangeAttribute(SNARL_ATTRIBUTES Attr, LPCSTR Value); + M_RESULT ChangeAttribute(SNARL_ATTRIBUTES Attr, LPCWSTR Value); + M_RESULT ChangeAttribute(LONG32 Id, SNARL_ATTRIBUTES Attr, LPCSTR Value); + M_RESULT ChangeAttribute(LONG32 Id, SNARL_ATTRIBUTES Attr, LPCWSTR Value); + LONG32 GetAppMsg(); + LONG32 GetRevision(); + + M_RESULT RegisterApp(LPCSTR Application, LPCSTR SmallIcon, LPCSTR LargeIcon, HWND hWnd = 0, LONG32 ReplyMsg = 0); + M_RESULT RegisterApp(LPCWSTR Application, LPCWSTR SmallIcon, LPCWSTR LargeIcon, HWND hWnd = 0, LONG32 ReplyMsg = 0); + void SetAsSnarlApp(HWND hWndOwner, SNARL_APP_FLAGS Flags = (SNARL_APP_FLAGS)(SNARL_APP_HAS_ABOUT | SNARL_APP_HAS_PREFS)); + M_RESULT SetClassDefault(LPCSTR Class, SNARL_ATTRIBUTES Attr, LPCSTR Value); + M_RESULT SetClassDefault(LPCWSTR Class, SNARL_ATTRIBUTES Attr, LPCWSTR Value); + LONG32 ShowNotification(LPCSTR Class, LPCSTR Title = NULL, LPCSTR Text = NULL, LONG32 Timeout = 0, LPCSTR Icon = NULL, HWND hWndReply = NULL, LONG32 uReplyMsg = 0, LPCSTR Sound = NULL); + LONG32 ShowNotification(LPCWSTR Class, LPCWSTR Title = NULL, LPCWSTR Text = NULL, LONG32 Timeout = 0, LPCWSTR Icon = NULL, HWND hWndReply = NULL, LONG32 uReplyMsg = 0, LPCWSTR Sound = NULL); + M_RESULT UnregisterApp(); + + LONG32 GetLastMessageId() { return m_nLastMessageId; } + + private: + template LONG32 Send(T ss); + LPSTR WideToUTF8(LPCWSTR szWideStr); + + LONG32 m_nLastMessageId; + HWND m_hwndFrom; // set during snRegisterConfig() or snRegisterConfig2() + + }; + +} + +#endif // SNARL_INTERFACE diff --git a/BuildTools/SCons/SConstruct b/BuildTools/SCons/SConstruct index 5a8791b..220ccf1 100644 --- a/BuildTools/SCons/SConstruct +++ b/BuildTools/SCons/SConstruct @@ -379,6 +379,10 @@ if env["PLATFORM"] == "darwin" : env["GROWL_FRAMEWORK"] = "/Library/Frameworks/Growl.framework" conf.Finish() +# Snarl +if env["PLATFORM"] == "win32" : + env["HAVE_SNARL"] = True + # LibXML conf = Configure(conf_env) if conf.CheckCHeader("libxml/parser.h") and conf.CheckLib("xml2") : diff --git a/SwifTools/Notifier/GrowlNotifier.cpp b/SwifTools/Notifier/GrowlNotifier.cpp index 0527ef8..0660910 100644 --- a/SwifTools/Notifier/GrowlNotifier.cpp +++ b/SwifTools/Notifier/GrowlNotifier.cpp @@ -71,7 +71,10 @@ GrowlNotifier::GrowlNotifier(const String& name) { Growl_SetDelegate(&delegate_); } -void GrowlNotifier::showMessage(Type type, const String& subject, const String& description, const ByteArray& picture, boost::function callback) { +void GrowlNotifier::showMessage(Type type, const String& subject, const String& description, const boost::filesystem::path& picturePath, boost::function callback) { + ByteArray picture; + picture.readFromFile(picturePath.string()); + CFStringRef cfSubject = SWIFTEN_STRING_TO_CFSTRING(subject); CFStringRef cfDescription = SWIFTEN_STRING_TO_CFSTRING(description); CFStringRef cfName = SWIFTEN_STRING_TO_CFSTRING(typeToString(type)); @@ -92,16 +95,4 @@ void GrowlNotifier::showMessage(Type type, const String& subject, const String& CFRelease(cfSubject); } -String GrowlNotifier::typeToString(Type type) { - switch (type) { - case ContactAvailable: return "Contact Becomes Available"; - case ContactUnavailable: return "Contact Becomes Unavailable"; - case ContactStatusChange: return "Contact Changes Status"; - case IncomingMessage: return "Incoming Message"; - case SystemMessage: return "System Message"; - } - assert(false); - return ""; -} - } diff --git a/SwifTools/Notifier/GrowlNotifier.h b/SwifTools/Notifier/GrowlNotifier.h index fab3b5e..5d618e6 100644 --- a/SwifTools/Notifier/GrowlNotifier.h +++ b/SwifTools/Notifier/GrowlNotifier.h @@ -8,6 +8,7 @@ #include #include +#include #include "SwifTools/Notifier/Notifier.h" @@ -23,12 +24,9 @@ namespace Swift { public: GrowlNotifier(const String& name); - virtual void showMessage(Type type, const String& subject, const String& description, const ByteArray& picture, boost::function callback); + virtual void showMessage(Type type, const String& subject, const String& description, const boost::filesystem::path& picture, boost::function callback); private: - String typeToString(Type type); - - private: Growl_Delegate delegate_; }; } diff --git a/SwifTools/Notifier/LoggingNotifier.h b/SwifTools/Notifier/LoggingNotifier.h index 93349d9..eea07ef 100644 --- a/SwifTools/Notifier/LoggingNotifier.h +++ b/SwifTools/Notifier/LoggingNotifier.h @@ -12,16 +12,16 @@ namespace Swift { class LoggingNotifier : public Notifier { public: - virtual void showMessage(Type type, const String& subject, const String& description, const ByteArray& picture, boost::function callback) { + virtual void showMessage(Type type, const String& subject, const String& description, const boost::filesystem::path& picture, boost::function callback) { notifications.push_back(Notification(type, subject, description, picture, callback)); } struct Notification { - Notification(Type type, const String& subject, const String& description, const ByteArray& picture, boost::function callback) : type(type), subject(subject), description(description), picture(picture), callback(callback) {} + Notification(Type type, const String& subject, const String& description, const boost::filesystem::path& picture, boost::function callback) : type(type), subject(subject), description(description), picture(picture), callback(callback) {} Type type; String subject; String description; - ByteArray picture; + boost::filesystem::path picture; boost::function callback; }; diff --git a/SwifTools/Notifier/Notifier.cpp b/SwifTools/Notifier/Notifier.cpp index 2c2cfe5..a976486 100644 --- a/SwifTools/Notifier/Notifier.cpp +++ b/SwifTools/Notifier/Notifier.cpp @@ -11,4 +11,34 @@ namespace Swift { Notifier::~Notifier() { } +String Notifier::typeToString(Type type) { + switch (type) { + case ContactAvailable: return "Contact Becomes Available"; + case ContactUnavailable: return "Contact Becomes Unavailable"; + case ContactStatusChange: return "Contact Changes Status"; + case IncomingMessage: return "Incoming Message"; + case SystemMessage: return "System Message"; + } + assert(false); + return ""; +} + +std::vector Notifier::getAllTypes() { + std::vector result; + result.push_back(ContactAvailable); + result.push_back(ContactUnavailable); + result.push_back(ContactStatusChange); + result.push_back(IncomingMessage); + result.push_back(SystemMessage); + return result; +} + +std::vector Notifier::getDefaultTypes() { + std::vector result; + result.push_back(ContactAvailable); + result.push_back(IncomingMessage); + result.push_back(SystemMessage); + return result; +} + } diff --git a/SwifTools/Notifier/Notifier.h b/SwifTools/Notifier/Notifier.h index 3e3da8a..f1a89ef 100644 --- a/SwifTools/Notifier/Notifier.h +++ b/SwifTools/Notifier/Notifier.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include "Swiften/Base/String.h" @@ -24,7 +25,12 @@ namespace Swift { Type type, const String& subject, const String& description, - const ByteArray& picture, + const boost::filesystem::path& picture, boost::function callback) = 0; + + protected: + String typeToString(Type type); + static std::vector getAllTypes(); + static std::vector getDefaultTypes(); }; } diff --git a/SwifTools/Notifier/SConscript b/SwifTools/Notifier/SConscript index 89fc31b..9ad2fd7 100644 --- a/SwifTools/Notifier/SConscript +++ b/SwifTools/Notifier/SConscript @@ -1,5 +1,7 @@ Import("swiftools_env") +myenv = swiftools_env.Clone() + sources = [ "Notifier.cpp", ] @@ -8,6 +10,11 @@ if swiftools_env.get("HAVE_GROWL", False) : sources += [ "GrowlNotifier.cpp", ] - -objects = swiftools_env.StaticObject(sources) +if swiftools_env.get("HAVE_SNARL", False) : + myenv.MergeFlags(myenv["SNARL_FLAGS"]) + sources += [ + "SnarlNotifier.cpp", + ] + +objects = myenv.StaticObject(sources) swiftools_env.Append(SWIFTOOLS_OBJECTS = objects) diff --git a/SwifTools/Notifier/SnarlNotifier.cpp b/SwifTools/Notifier/SnarlNotifier.cpp new file mode 100644 index 0000000..86069c9 --- /dev/null +++ b/SwifTools/Notifier/SnarlNotifier.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "SwifTools/Notifier/SnarlNotifier.h" + +#include +#include +#include + +#include "Swiften/Base/foreach.h" +#include "SwifTools/Notifier/Win32NotifierWindow.h" + +#define SWIFT_SNARLNOTIFIER_MESSAGE_ID 0x4567 // Sounds sick to pick a number, but this is windows + +namespace Swift { + +SnarlNotifier::SnarlNotifier(const String& name, Win32NotifierWindow* window) : window(window) { + window->onMessageReceived.connect(boost::bind(&SnarlNotifier::handleMessageReceived, this, _1)); + snarl.RegisterConfig(window->getID(), name.getUTF8Data(), 0); + foreach(Notifier::Type type, getAllTypes()) { + snarl.RegisterAlert(name.getUTF8Data(), typeToString(type).getUTF8Data()); + } +} + +SnarlNotifier::~SnarlNotifier() { + snarl.RevokeConfig(window->getID()); + window->onMessageReceived.disconnect(boost::bind(&SnarlNotifier::handleMessageReceived, this, _1)); + if (!notifications.empty()) { + std::cerr << "Warning: " << notifications.size() << " Snarl notifications pending" << std::endl; + } +} + +void SnarlNotifier::showMessage(Type type, const String& subject, const String& description, const boost::filesystem::path& picture, boost::function callback) { + int notificationID = snarl.ShowMessageEx(typeToString(type).getUTF8Data(), subject.getUTF8Data(), description.getUTF8Data(), timeout, picture.string().c_str(), window->getID(), SWIFT_SNARLNOTIFIER_MESSAGE_ID); + if (notificationID > 0) { + notifications.insert(std::make_pair(notificationID, callback)); + } +} + +void SnarlNotifier::handleMessageReceived(MSG* message) { + if (message->message == SWIFT_SNARLNOTIFIER_MESSAGE_ID) { + int action = message->wParam; + if (action == Snarl::SNARL_NOTIFICATION_TIMED_OUT || action == Snarl::SNARL_NOTIFICATION_ACK || action == Snarl::SNARL_NOTIFICATION_CLOSED) { + int notificationID = message->lParam; + NotificationsMap::iterator i = notifications.find(notificationID); + if (i != notifications.end()) { + if (action == Snarl::SNARL_NOTIFICATION_ACK) { + i->second(); + } + notifications.erase(i); + } + else { + std::cerr << "Warning: Orphaned Snarl notification received"; + } + } + } +} + +} diff --git a/SwifTools/Notifier/SnarlNotifier.h b/SwifTools/Notifier/SnarlNotifier.h new file mode 100644 index 0000000..45408ec --- /dev/null +++ b/SwifTools/Notifier/SnarlNotifier.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include + +#include "SwifTools/Notifier/Notifier.h" +#include "SnarlInterface.h" + +namespace Swift { + class Win32NotifierWindow; + + class SnarlNotifier : public Notifier { + public: + SnarlNotifier(const String& name, Win32NotifierWindow* window); + ~SnarlNotifier(); + + virtual void showMessage(Type type, const String& subject, const String& description, const boost::filesystem::path& picture, boost::function callback); + + private: + void handleMessageReceived(MSG* message); + + private: + static const int timeout = 3; + Snarl::SnarlInterface snarl; + Win32NotifierWindow* window; + typedef std::map > NotificationsMap; + NotificationsMap notifications; + }; +} diff --git a/SwifTools/Notifier/Win32NotifierWindow.h b/SwifTools/Notifier/Win32NotifierWindow.h new file mode 100644 index 0000000..7ac149b --- /dev/null +++ b/SwifTools/Notifier/Win32NotifierWindow.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +//#include + +#include "Swiften/Base/boost_bsignals.h" + +namespace Swift { + class Win32NotifierWindow { + public: + virtual ~Win32NotifierWindow() {} + + virtual HWND getID() const = 0; + + boost::signal onMessageReceived; + }; +} diff --git a/Swift/Controllers/PresenceNotifier.cpp b/Swift/Controllers/PresenceNotifier.cpp index ce7ae40..0c46f9b 100644 --- a/Swift/Controllers/PresenceNotifier.cpp +++ b/Swift/Controllers/PresenceNotifier.cpp @@ -90,7 +90,7 @@ void PresenceNotifier::showNotification(const JID& jid, Notifier::Type type) { } String title = name + " (" + getStatusType(jid) + ")"; String message = getStatusMessage(jid); - notifier->showMessage(type, title, message, avatarManager->getAvatar(jid), boost::bind(&PresenceNotifier::handleNotificationActivated, this, jid)); + notifier->showMessage(type, title, message, avatarManager->getAvatarPath(jid), boost::bind(&PresenceNotifier::handleNotificationActivated, this, jid)); } void PresenceNotifier::handleNotificationActivated(JID jid) { diff --git a/Swift/Controllers/UnitTest/PresenceNotifierTest.cpp b/Swift/Controllers/UnitTest/PresenceNotifierTest.cpp index 85433f3..a410665 100644 --- a/Swift/Controllers/UnitTest/PresenceNotifierTest.cpp +++ b/Swift/Controllers/UnitTest/PresenceNotifierTest.cpp @@ -145,7 +145,7 @@ class PresenceNotifierTest : public CppUnit::TestFixture { sendPresence(user1, StatusShow::Online); CPPUNIT_ASSERT_EQUAL(1, static_cast(notifier->notifications.size())); - CPPUNIT_ASSERT_EQUAL(ByteArray("abcdef"), notifier->notifications[0].picture); + CPPUNIT_ASSERT_EQUAL(boost::filesystem::path("/avatars/user1@bar.com/bla"), notifier->notifications[0].picture); } void testNotificationActivationEmitsSignal() { diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp index ee97fc6..0118416 100644 --- a/Swift/QtUI/QtSwift.cpp +++ b/Swift/QtUI/QtSwift.cpp @@ -38,6 +38,9 @@ #include "SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h" #if defined(HAVE_GROWL) #include "SwifTools/Notifier/GrowlNotifier.h" +#elif defined(HAVE_SNARL) +#include "QtWin32NotifierWindow.h" +#include "SwifTools/Notifier/SnarlNotifier.h" #else #include "SwifTools/Notifier/NullNotifier.h" #endif @@ -97,6 +100,9 @@ QtSwift::QtSwift(po::variables_map options) : autoUpdater_(NULL) { soundPlayer_ = new QtSoundPlayer(applicationPathProvider_); #if defined(HAVE_GROWL) notifier_ = new GrowlNotifier(SWIFT_APPLICATION_NAME); +#elif defined(HAVE_SNARL) + notifierWindow_ = new QtWin32NotifierWindow(); + notifier_ = new SnarlNotifier(SWIFT_APPLICATION_NAME, notifierWindow_); #else notifier_ = new NullNotifier(); #endif @@ -155,6 +161,7 @@ QtSwift::QtSwift(po::variables_map options) : autoUpdater_(NULL) { } QtSwift::~QtSwift() { + delete notifier_; delete autoUpdater_; delete chatWindowFactory_; foreach (QtMainWindowFactory* factory, rosterWindowFactories_) { diff --git a/Swift/QtUI/QtSwift.h b/Swift/QtUI/QtSwift.h index abc8c75..092c45e 100644 --- a/Swift/QtUI/QtSwift.h +++ b/Swift/QtUI/QtSwift.h @@ -20,6 +20,9 @@ #if defined(SWIFTEN_PLATFORM_MACOSX) #include "Swiften/Application/CocoaApplication.h" #endif +#if defined(HAVE_SNARL) +#include "SwifTools/Notifier/Win32NotifierWindow.h" +#endif namespace po = boost::program_options; @@ -75,6 +78,9 @@ namespace Swift { #if defined(SWIFTEN_PLATFORM_MACOSX) CocoaApplication cocoaApplication_; #endif +#if defined(HAVE_SNARL) + Win32NotifierWindow* notifierWindow_; +#endif }; } diff --git a/Swift/QtUI/QtWin32NotifierWindow.h b/Swift/QtUI/QtWin32NotifierWindow.h new file mode 100644 index 0000000..b8d9c77 --- /dev/null +++ b/Swift/QtUI/QtWin32NotifierWindow.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include + +#include "SwifTools/Notifier/Win32NotifierWindow.h" + +namespace Swift { + class QtWin32NotifierWindow : public QWidget, public Win32NotifierWindow { + public: + QtWin32NotifierWindow(QWidget* parent = NULL) { + setVisible(false); + } + + bool winEvent (MSG* message, long* result ) { + onMessageReceived(message); + return false; + } + + virtual HWND getID() const { + return winId(); + } + }; +} diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index 634207c..933133d 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -38,6 +38,9 @@ myenv.MergeFlags(env.get("EXPAT_FLAGS", "")) if myenv.get("HAVE_GROWL", False) : myenv.MergeFlags(myenv["GROWL_FLAGS"]) myenv.Append(CPPDEFINES = ["HAVE_GROWL"]) +if myenv.get("HAVE_SNARL", False) : + myenv.MergeFlags(myenv["SNARL_FLAGS"]) + myenv.Append(CPPDEFINES = ["HAVE_SNARL"]) myenv.MergeFlags(myenv["PLATFORM_FLAGS"]) myenv.Tool("qt4", toolpath = ["#/BuildTools/SCons/Tools"]) diff --git a/Swiften/Avatars/AvatarMemoryStorage.h b/Swiften/Avatars/AvatarMemoryStorage.h index 13fbd9b..6f1ba49 100644 --- a/Swiften/Avatars/AvatarMemoryStorage.h +++ b/Swiften/Avatars/AvatarMemoryStorage.h @@ -22,8 +22,8 @@ namespace Swift { return i == avatars.end() ? ByteArray() : i->second; } - virtual boost::filesystem::path getAvatarPath(const String& /*hash*/) const { - return boost::filesystem::path(); + virtual boost::filesystem::path getAvatarPath(const String& hash) const { + return boost::filesystem::path("/avatars") / hash.getUTF8String(); } private: diff --git a/Swiften/Avatars/DummyAvatarManager.h b/Swiften/Avatars/DummyAvatarManager.h index db63b05..ec5068e 100644 --- a/Swiften/Avatars/DummyAvatarManager.h +++ b/Swiften/Avatars/DummyAvatarManager.h @@ -13,8 +13,8 @@ namespace Swift { class DummyAvatarManager : public AvatarManager { public: - virtual boost::filesystem::path getAvatarPath(const JID&) const { - return boost::filesystem::path(); + virtual boost::filesystem::path getAvatarPath(const JID& j) const { + return boost::filesystem::path("/avatars") / j.toString().getUTF8String(); } virtual ByteArray getAvatar(const JID& jid) const { -- cgit v0.10.2-6-g49f6