summaryrefslogtreecommitdiff
path: root/plugins/AssocMgr/assoclist.cpp
diff options
context:
space:
mode:
authorKirill Volinsky <mataes2007@gmail.com>2012-06-06 20:31:36 +0000
committerKirill Volinsky <mataes2007@gmail.com>2012-06-06 20:31:36 +0000
commit01b90c2d693230916ebc4d868f5868c6e32b58a0 (patch)
tree9132a6e673a6973be4dfd2d3825dd288bc7adc83 /plugins/AssocMgr/assoclist.cpp
parent6259dc6f9fcd6ee828555bdebd9a344fa62b0801 (diff)
AssocMgr files renaming
git-svn-id: http://svn.miranda-ng.org/main/trunk@339 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/AssocMgr/assoclist.cpp')
-rw-r--r--plugins/AssocMgr/assoclist.cpp1102
1 files changed, 1102 insertions, 0 deletions
diff --git a/plugins/AssocMgr/assoclist.cpp b/plugins/AssocMgr/assoclist.cpp
new file mode 100644
index 0000000000..26a72a43da
--- /dev/null
+++ b/plugins/AssocMgr/assoclist.cpp
@@ -0,0 +1,1102 @@
+/*
+
+'File Association Manager'-Plugin for Miranda IM
+
+Copyright (C) 2005-2007 H. Herkenrath
+
+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 the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program (AssocMgr-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "common.h"
+
+/* Options */
+extern HINSTANCE hInst;
+static HANDLE hHookOptInit;
+/* Services */
+static HANDLE hServiceAddFile, hServiceRemoveFile, hServiceAddUrl, hServiceRemoveUrl;
+
+/************************* Assoc List *****************************/
+
+typedef struct {
+ char *pszClassName; /* class name as used in registry and db */
+ TCHAR *pszDescription;
+ HINSTANCE hInstance; /* allowed to be NULL for miranda32.exe */
+ WORD nIconResID;
+ char *pszService;
+ WORD flags; /* set of FTDF_* and UTDF_* flags */
+ char *pszFileExt; /* file type: NULL for url type*/
+ char *pszMimeType; /* file type: allowed to be NULL */
+ TCHAR *pszVerbDesc; /* file type: allowed to be NULL */
+} ASSOCDATA;
+
+static ASSOCDATA *pAssocList; /* protected by csAssocList */
+static int nAssocListCount; /* protected by csAssocList */
+static CRITICAL_SECTION csAssocList;
+
+/************************* Assoc Enabled **************************/
+
+static BOOL IsAssocEnabled(const ASSOCDATA *assoc)
+{
+ char szSetting[MAXMODULELABELLENGTH];
+ mir_snprintf(szSetting, sizeof(szSetting), "enabled_%s", assoc->pszClassName);
+ return DBGetContactSettingByte(NULL, "AssocMgr", szSetting, (BYTE)!(assoc->flags&FTDF_DEFAULTDISABLED))!= 0;
+}
+
+static void SetAssocEnabled(const ASSOCDATA *assoc, BOOL fEnabled)
+{
+ char szSetting[MAXMODULELABELLENGTH];
+ TCHAR szDLL[MAX_PATH], szBuf[MAX_PATH];
+ mir_snprintf(szSetting, sizeof(szSetting), "enabled_%s", assoc->pszClassName);
+ DBWriteContactSettingByte(NULL, "AssocMgr", szSetting, (BYTE)fEnabled);
+ /* dll name for uninstall */
+ if(assoc->hInstance!= NULL && assoc->hInstance!= hInst && assoc->hInstance!= GetModuleHandle(NULL))
+ if(GetModuleFileName(assoc->hInstance, szBuf, SIZEOF(szBuf)))
+ if(CallService(MS_UTILS_PATHTORELATIVET, (WPARAM)szBuf, (LPARAM)szDLL)) {
+ mir_snprintf(szSetting, sizeof(szSetting), "module_%s", assoc->pszClassName);
+ DBWriteContactSettingTString(NULL, "AssocMgr", szSetting, szDLL);
+ }
+}
+
+static void DeleteAssocEnabledSetting(const ASSOCDATA *assoc)
+{
+ char szSetting[MAXMODULELABELLENGTH];
+ mir_snprintf(szSetting, sizeof(szSetting), "enabled_%s", assoc->pszClassName);
+ DBDeleteContactSetting(NULL, "AssocMgr", szSetting);
+ /* dll name for uninstall */
+ mir_snprintf(szSetting, sizeof(szSetting), "module_%s", assoc->pszClassName);
+ DBDeleteContactSetting(NULL, "AssocMgr", szSetting);
+}
+
+void CleanupAssocEnabledSettings(void)
+{
+ int nSettingsCount;
+ char **ppszSettings, *pszSuffix;
+ DBVARIANT dbv;
+ int i;
+ HANDLE hFile;
+ TCHAR szDLL[MAX_PATH];
+ char szSetting[MAXMODULELABELLENGTH];
+
+ /* delete old enabled_* settings if associated plugin no longer present */
+ if(EnumDbPrefixSettings("AssocMgr", "enabled_", &ppszSettings, &nSettingsCount)) {
+ EnterCriticalSection(&csAssocList);
+ for(i = 0;i<nSettingsCount;++i) {
+ pszSuffix = &ppszSettings[i][8];
+ mir_snprintf(szSetting, sizeof(szSetting), "module_%s", pszSuffix);
+ if (!DBGetContactSettingTString(NULL, "AssocMgr", szSetting, &dbv)) {
+ if(CallService(MS_UTILS_PATHTOABSOLUTET, (WPARAM)dbv.ptszVal, (LPARAM)szDLL)) {
+ /* file still exists? */
+ hFile = CreateFile(szDLL, 0, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
+ if(hFile == INVALID_HANDLE_VALUE) {
+ DBDeleteContactSetting(NULL, "AssocMgr", ppszSettings[i]);
+ DBDeleteContactSetting(NULL, "AssocMgr", szSetting);
+ } else CloseHandle(hFile);
+ }
+ mir_free(dbv.ptszVal);
+ }
+ mir_free(ppszSettings[i]);
+ }
+ LeaveCriticalSection(&csAssocList);
+ mir_free(ppszSettings); /* does NULL check */
+ }
+}
+
+/************************* Mime Reg *******************************/
+
+static __inline void RememberMimeTypeAdded(const char *pszMimeType, const char *pszFileExt, BYTE fAdded)
+{
+ char szSetting[MAXMODULELABELLENGTH];
+ mir_snprintf(szSetting, sizeof(szSetting), "mime_%s", pszMimeType);
+ if(fAdded) DBWriteContactSettingString(NULL, "AssocMgr", szSetting, pszFileExt);
+ else DBDeleteContactSetting(NULL, "AssocMgr", szSetting);
+}
+
+static __inline BOOL WasMimeTypeAdded(const char *pszMimeType)
+{
+ char szSetting[MAXMODULELABELLENGTH];
+ DBVARIANT dbv;
+ BOOL fAdded = FALSE;
+ mir_snprintf(szSetting, sizeof(szSetting), "mime_%s", pszMimeType);
+ if (!DBGetContactSetting(NULL, "AssocMgr", szSetting, &dbv)) fAdded = TRUE;
+ else DBFreeVariant(&dbv);
+ return fAdded;
+}
+
+void CleanupMimeTypeAddedSettings(void)
+{
+ int nSettingsCount;
+ char **ppszSettings, *pszSuffix;
+ DBVARIANT dbv;
+ int i, j;
+
+ /* delete old mime_* settings and unregister the associated mime type */
+ if(EnumDbPrefixSettings("AssocMgr", "mime_", &ppszSettings, &nSettingsCount)) {
+ EnterCriticalSection(&csAssocList);
+ for(i = 0;i<nSettingsCount;++i) {
+ pszSuffix = &ppszSettings[i][5];
+ for(j = 0;j<nAssocListCount;++j)
+ if (!lstrcmpA(pszSuffix, pAssocList[j].pszMimeType))
+ break; /* mime type in current list */
+ if(j == nAssocListCount) { /* mime type not in current list */
+ if (!DBGetContactSetting(NULL, "AssocMgr", ppszSettings[i], &dbv)) {
+ if(dbv.type == DBVT_ASCIIZ)
+ RemoveRegMimeType(pszSuffix, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ DBDeleteContactSetting(NULL, "AssocMgr", ppszSettings[i]);
+ }
+ mir_free(ppszSettings[i]);
+ }
+ LeaveCriticalSection(&csAssocList);
+ mir_free(ppszSettings);
+ }
+}
+
+/************************* Shell Notify ***************************/
+
+#define SHELLNOTIFY_DELAY 3000 /* time for which assoc changes are buffered */
+
+static UINT nNotifyTimerID; /* protected by csNotifyTimer */
+static CRITICAL_SECTION csNotifyTimer;
+
+static void CALLBACK NotifyTimerProc(HWND hwnd, UINT msg, UINT_PTR nTimerID, DWORD dwTime)
+{
+ UNREFERENCED_PARAMETER(msg);
+ UNREFERENCED_PARAMETER(dwTime);
+ EnterCriticalSection(&csNotifyTimer);
+ KillTimer(hwnd, nTimerID);
+ if(nNotifyTimerID == nTimerID) /* might be stopped previously */
+ SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST|SHCNF_FLUSHNOWAIT, NULL, NULL);
+ nNotifyTimerID = 0;
+ LeaveCriticalSection(&csNotifyTimer);
+}
+
+static void NotifyAssocChange(BOOL fNow)
+{
+ EnterCriticalSection(&csNotifyTimer);
+ if(fNow) {
+ nNotifyTimerID = 0; /* stop previous timer */
+ SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST|SHCNF_FLUSH, NULL, NULL);
+ }
+ else nNotifyTimerID = SetTimer(NULL, nNotifyTimerID, SHELLNOTIFY_DELAY, NotifyTimerProc);
+ LeaveCriticalSection(&csNotifyTimer);
+}
+
+/************************* Assoc List Utils ***********************/
+
+// this function assumes it has got the csAssocList mutex
+static int FindAssocItem(const char *pszClassName)
+{
+ int i;
+ for(i = 0;i<nAssocListCount;++i)
+ if (!lstrcmpA(pszClassName, pAssocList[i].pszClassName))
+ return i;
+ return -1;
+}
+
+BOOL IsRegisteredAssocItem(const char *pszClassName)
+{
+ int index;
+ EnterCriticalSection(&csAssocList);
+ index = FindAssocItem(pszClassName);
+ LeaveCriticalSection(&csAssocList);
+ return index!= -1;
+}
+
+
+// this function assumes it has got the csAssocList mutex
+static ASSOCDATA* CopyAssocItem(const ASSOCDATA *assoc)
+{
+ ASSOCDATA *assoc2;
+ assoc2 = (ASSOCDATA*)mir_alloc(sizeof(ASSOCDATA));
+ if(assoc2 == NULL) return NULL;
+ assoc2->pszClassName = mir_strdup(assoc->pszClassName);
+ assoc2->pszDescription = mir_tstrdup(assoc->pszDescription);
+ assoc2->hInstance = assoc->hInstance;
+ assoc2->nIconResID = assoc->nIconResID;
+ assoc2->pszService = mir_strdup(assoc->pszService);
+ assoc2->flags = assoc->flags;
+ assoc2->pszFileExt = mir_strdup(assoc->pszFileExt);
+ assoc2->pszMimeType = mir_strdup(assoc->pszMimeType);
+ assoc2->pszVerbDesc = mir_tstrdup(assoc->pszVerbDesc);
+ if(assoc2->pszClassName == NULL || assoc2->pszDescription == NULL ||
+ (assoc2->pszFileExt == NULL && assoc->pszFileExt!= NULL)) {
+ mir_free(assoc2->pszClassName); /* does NULL check */
+ mir_free(assoc2->pszDescription); /* does NULL check */
+ mir_free(assoc2->pszService); /* does NULL check */
+ mir_free(assoc2->pszFileExt); /* does NULL check */
+ mir_free(assoc2->pszMimeType); /* does NULL check */
+ mir_free(assoc2->pszVerbDesc); /* does NULL check */
+ mir_free(assoc2);
+ return NULL;
+ }
+ return assoc2;
+}
+
+// this function assumes it has got the csAssocList mutex
+// this function assumes CoInitialize() has been called before
+static int ReplaceImageListAssocIcon(HIMAGELIST himl, const ASSOCDATA *assoc, int iPrevIndex)
+{
+ HICON hIcon = NULL;
+ int index;
+ if(himl == NULL) return -1;
+
+ /* load icon */
+ hIcon = LoadRegClassSmallIcon(assoc->pszClassName);
+ if(hIcon == NULL) {
+ SHFILEINFOA sfi;
+ if(SHGetFileInfoA((assoc->pszFileExt!= NULL)?assoc->pszFileExt:"", FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(sfi), SHGFI_ICON|SHGFI_SMALLICON|SHGFI_USEFILEATTRIBUTES))
+ hIcon = sfi.hIcon; /* WinXP: this icon is not updated until the process exits */
+ }
+ /* add icon */
+ if(hIcon == NULL) return -1;
+ index = ImageList_ReplaceIcon(himl, iPrevIndex, hIcon);
+ DestroyIcon(hIcon);
+ return index;
+}
+
+// the return value does not need to be freed
+// this function assumes it has got the csAssocList mutex
+static TCHAR* GetAssocTypeDesc(const ASSOCDATA *assoc)
+{
+ static TCHAR szDesc[32];
+ if(assoc->pszFileExt == NULL) mir_sntprintf(szDesc, SIZEOF(szDesc), TranslateT("%hs:"), assoc->pszClassName);
+ else mir_sntprintf(szDesc, SIZEOF(szDesc), TranslateT("%hs files"), assoc->pszFileExt);
+ return szDesc;
+}
+
+// this function assumes it has got the csAssocList mutex
+static BOOL IsAssocRegistered(const ASSOCDATA *assoc)
+{
+ BOOL fSuccess = FALSE, fIsUrl, fUseMainCmdLine;
+ TCHAR *pszRunCmd;
+
+ fIsUrl = (assoc->pszFileExt == NULL);
+ fUseMainCmdLine = (assoc->pszService == NULL);
+
+ /* class */
+ pszRunCmd = MakeRunCommand(fUseMainCmdLine, !fUseMainCmdLine);
+ if(pszRunCmd!= NULL)
+ fSuccess = IsRegClass(assoc->pszClassName, pszRunCmd);
+ mir_free(pszRunCmd); /* does NULL check */
+ /* file ext */
+ if (!fIsUrl)
+ fSuccess = IsRegFileExt(assoc->pszFileExt, assoc->pszClassName);
+
+ return fSuccess;
+}
+
+// this function assumes it has got the csAssocList mutex
+// call GetLastError() on error to get more error details
+static BOOL EnsureAssocRegistered(const ASSOCDATA *assoc)
+{
+ BOOL fSuccess = FALSE, fIsUrl, fUseMainCmdLine;
+ TCHAR *pszIconLoc, *pszRunCmd, *pszDdeCmd, *pszAppFileName;
+
+ fIsUrl = (assoc->pszFileExt == NULL);
+ fUseMainCmdLine = (assoc->pszService == NULL);
+
+ pszRunCmd = MakeRunCommand(fUseMainCmdLine, !fUseMainCmdLine);
+ if(pszRunCmd!= NULL) {
+ fSuccess = TRUE; /* tentatively */
+ /* do not overwrite user customized settings */
+ if (!IsRegClass(assoc->pszClassName, pszRunCmd)) {
+ /* class icon */
+ if (!assoc->nIconResID && fIsUrl) pszIconLoc = MakeIconLocation(NULL, 0); /* miranda logo */
+ else if (!assoc->nIconResID) pszIconLoc = MakeIconLocation(hInst, IDI_MIRANDAFILE); /* generic file */
+ else pszIconLoc = MakeIconLocation(assoc->hInstance, assoc->nIconResID);
+ /* register class */
+ if(fUseMainCmdLine) pszDdeCmd = NULL;
+ else pszDdeCmd = fIsUrl?DDEURLCMD:DDEFILECMD;
+ fSuccess = AddRegClass(assoc->pszClassName, assoc->pszDescription, pszIconLoc, _T(MIRANDANAME), pszRunCmd, pszDdeCmd, DDEAPP, DDETOPIC, assoc->pszVerbDesc, assoc->flags&FTDF_BROWSERAUTOOPEN, fIsUrl, assoc->flags&FTDF_ISSHORTCUT);
+ mir_free(pszIconLoc); /* does NULL check */
+ /* file type */
+ if(fSuccess && !fIsUrl) {
+ /* register mime type */
+ if(assoc->pszMimeType!= NULL)
+ if(AddRegMimeType(assoc->pszMimeType, assoc->pszFileExt, assoc->pszDescription))
+ RememberMimeTypeAdded(assoc->pszMimeType, assoc->pszFileExt, TRUE);
+ /* register file ext */
+ fSuccess = AddRegFileExt(assoc->pszFileExt, assoc->pszClassName, assoc->pszMimeType, assoc->flags&FTDF_ISTEXT);
+ /* register open-with */
+ pszAppFileName = MakeAppFileName(fUseMainCmdLine);
+ if(pszAppFileName!= NULL)
+ AddRegOpenWithExtEntry(pszAppFileName, assoc->pszFileExt, assoc->pszDescription);
+ mir_free(pszAppFileName); /* does NULL check */
+ }
+ }
+ mir_free(pszRunCmd);
+ }
+ else SetLastError(ERROR_OUTOFMEMORY);
+ return fSuccess;
+}
+
+// this function assumes it has got the csAssocList mutex
+// call GetLastError() on error to get more error details
+static BOOL UnregisterAssoc(const ASSOCDATA *assoc)
+{
+ BOOL fIsUrl, fUseMainCmdLine;
+ TCHAR *pszAppFileName, *pszRunCmd;
+
+ fIsUrl = (assoc->pszFileExt == NULL);
+ fUseMainCmdLine = (assoc->pszService == NULL);
+
+ /* class might have been registered by another instance */
+ pszRunCmd = MakeRunCommand(fUseMainCmdLine, !fUseMainCmdLine);
+ if(pszRunCmd!= NULL && !IsRegClass(assoc->pszClassName, pszRunCmd)) {
+ mir_free(pszRunCmd);
+ return TRUE; /* succeed anyway */
+ }
+ mir_free(pszRunCmd); /* does NULL check */
+
+ /* file type */
+ if (!fIsUrl) {
+ /* file extension */
+ RemoveRegFileExt(assoc->pszFileExt, assoc->pszClassName);
+ /* mime type */
+ if(assoc->pszMimeType!= NULL)
+ if(WasMimeTypeAdded(assoc->pszMimeType)) {
+ RemoveRegMimeType(assoc->pszMimeType, assoc->pszFileExt);
+ RememberMimeTypeAdded(assoc->pszMimeType, assoc->pszFileExt, FALSE);
+ }
+ /* open-with entry */
+ pszAppFileName = MakeAppFileName(fUseMainCmdLine);
+ if(pszAppFileName!= NULL)
+ RemoveRegOpenWithExtEntry(pszAppFileName, assoc->pszFileExt);
+ mir_free(pszAppFileName); /* does NULL check */
+ }
+ return RemoveRegClass(assoc->pszClassName);
+}
+
+/************************* Assoc List Workers *********************/
+
+/* this structure represents the head of both
+ * FILETYPEDESC and URLTYPEDESC structures.
+ * the head is identical for both structures. */
+typedef struct {
+ int cbSize; /* either sizeof(FILETYPEDESC) or sizeof(URLTYPEDESC) */
+ const void *pszDescription;
+ HINSTANCE hInstance;
+ UINT nIconResID;
+ const char *pszService;
+ DWORD flags;
+} TYPEDESCHEAD;
+
+// ownership of pszClassName, pszFileExt, pszVerbDesc and pszMimeType is transfered
+// to the storage list on success
+static BOOL AddNewAssocItem_Worker(char *pszClassName, const TYPEDESCHEAD *tdh, char *pszFileExt, TCHAR *pszVerbDesc, char *pszMimeType)
+{
+ ASSOCDATA *pAssocListBuf, *assoc;
+ int index;
+
+ /* is already in list? */
+ EnterCriticalSection(&csAssocList);
+ index = FindAssocItem(pszClassName);
+ if(index!= -1) return FALSE;
+
+ /* resize storage array */
+ pAssocListBuf = (ASSOCDATA*)mir_realloc(pAssocList, (nAssocListCount+1)*sizeof(ASSOCDATA));
+ if(pAssocListBuf == NULL) {
+ LeaveCriticalSection(&csAssocList);
+ return FALSE;
+ }
+ pAssocList = pAssocListBuf;
+
+ /* init new item */
+ assoc = &pAssocList[nAssocListCount];
+ assoc->pszClassName = pszClassName; /* no dup here */
+ assoc->pszDescription = s2t(tdh->pszDescription, tdh->flags&FTDF_UNICODE, TRUE); /* does NULL check */
+ assoc->hInstance = tdh->hInstance; /* hInstance is allowed to be NULL for miranda32.exe */
+ assoc->nIconResID = (WORD)tdh->nIconResID; /* default icon selected later on */
+ assoc->pszService = mir_strdup(tdh->pszService); /* does NULL check */
+ assoc->flags = (WORD)tdh->flags;
+ assoc->pszFileExt = pszFileExt;
+ assoc->pszMimeType = pszMimeType;
+ assoc->pszVerbDesc = pszVerbDesc;
+
+ /* error check */
+ if(assoc->pszDescription == NULL || (assoc->pszService == NULL && tdh->pszService!= NULL)) {
+ mir_free(assoc->pszService); /* does NULL check */
+ mir_free(assoc->pszDescription); /* does NULL check */
+ LeaveCriticalSection(&csAssocList);
+ return FALSE;
+ }
+
+ /* add registry keys */
+ if(IsAssocEnabled(assoc))
+ EnsureAssocRegistered(assoc);
+
+ ++nAssocListCount;
+ NotifyAssocChange(FALSE);
+ return TRUE;
+}
+
+// ownership of pszClassName is *not* transferd to storage list
+static BOOL RemoveAssocItem_Worker(const char *pszClassName)
+{
+ ASSOCDATA *pAssocListBuf, *assoc;
+ int index;
+
+ /* find index */
+ EnterCriticalSection(&csAssocList);
+ index = FindAssocItem(pszClassName);
+ if(index == -1) {
+ LeaveCriticalSection(&csAssocList);
+ return FALSE;
+ }
+ assoc = &pAssocList[index];
+
+ /* delete registry keys and db setting */
+ UnregisterAssoc(assoc);
+ if(assoc->pszMimeType!= NULL)
+ RememberMimeTypeAdded(assoc->pszMimeType, assoc->pszFileExt, FALSE);
+ DeleteAssocEnabledSetting(assoc);
+
+ /* free memory */
+ mir_free(assoc->pszClassName);
+ mir_free(assoc->pszDescription);
+ mir_free(assoc->pszService);
+ mir_free(assoc->pszFileExt); /* does NULL check */
+ mir_free(assoc->pszVerbDesc); /* does NULL check */
+ mir_free(assoc->pszMimeType); /* does NULL check */
+
+ /* resize storage array */
+ if ((index+1)<nAssocListCount)
+ MoveMemory(assoc, &pAssocList[index+1], ((nAssocListCount-index-1)*sizeof(ASSOCDATA)));
+ pAssocListBuf = (ASSOCDATA*)mir_realloc(pAssocList, (nAssocListCount-1)*sizeof(ASSOCDATA));
+ if(pAssocListBuf!= NULL) pAssocList = pAssocListBuf;
+ --nAssocListCount;
+ LeaveCriticalSection(&csAssocList);
+
+ NotifyAssocChange(FALSE);
+ return TRUE;
+}
+
+/************************* Services *******************************/
+
+static INT_PTR ServiceAddNewFileType(WPARAM wParam, LPARAM lParam)
+{
+ const FILETYPEDESC *ftd = (FILETYPEDESC*)lParam;
+ char *pszClassName, *pszFileExt, *pszMimeType;
+ TCHAR *pszVerbDesc;
+ UNREFERENCED_PARAMETER(wParam);
+
+ if(ftd->cbSize<sizeof(FILETYPEDESC))
+ return 1;
+ if(ftd->pszFileExt == NULL || ftd->pszFileExt[0]!= '.')
+ return 2;
+
+ pszFileExt = mir_strdup(ftd->pszFileExt);
+ pszClassName = MakeFileClassName(ftd->pszFileExt);
+ if(pszFileExt!= NULL && pszClassName!= NULL) {
+ pszVerbDesc = s2t(ftd->ptszVerbDesc, ftd->flags&FTDF_UNICODE, TRUE); /* does NULL check */
+ pszMimeType = mir_strdup(ftd->pszMimeType); /* does NULL check */
+ if(AddNewAssocItem_Worker(pszClassName, (TYPEDESCHEAD*)ftd, pszFileExt, pszVerbDesc, pszMimeType))
+ /* no need to free pszClassName, pszFileExt, pszVerbDesc and pszMimeType,
+ * as their ownership got transfered to storage list */
+ return 0;
+ }
+ mir_free(pszClassName); /* does NULL check */
+ mir_free(pszFileExt); /* does NULL check */
+ return 3;
+}
+
+static INT_PTR ServiceRemoveFileType(WPARAM wParam, LPARAM lParam)
+{
+ char *pszClassName;
+ UNREFERENCED_PARAMETER(wParam);
+
+ if ((char*)lParam == NULL) return 2;
+ pszClassName = MakeFileClassName((char*)lParam);
+ if(pszClassName!= NULL)
+ if(RemoveAssocItem_Worker(pszClassName)) {
+ mir_free(pszClassName);
+ return 0;
+ }
+ mir_free(pszClassName); /* does NULL check */
+ return 3;
+}
+
+static INT_PTR ServiceAddNewUrlType(WPARAM wParam, LPARAM lParam)
+{
+ const URLTYPEDESC *utd = (URLTYPEDESC*)lParam;
+ char *pszClassName;
+ UNREFERENCED_PARAMETER(wParam);
+
+ if(utd->cbSize < sizeof(URLTYPEDESC))
+ return 1;
+ if(utd->pszService == NULL)
+ return 2;
+ if(utd->pszProtoPrefix == NULL || utd->pszProtoPrefix[lstrlenA(utd->pszProtoPrefix)-1]!= ':')
+ return 2;
+
+ pszClassName = MakeUrlClassName(utd->pszProtoPrefix);
+ if(pszClassName!= NULL)
+ if(AddNewAssocItem_Worker(pszClassName, (TYPEDESCHEAD*)utd, NULL, NULL, NULL))
+ /* no need to free pszClassName, as its
+ * ownership got transfered to storage list */
+ return 0;
+ mir_free(pszClassName); /* does NULL check */
+ return 3;
+}
+
+static INT_PTR ServiceRemoveUrlType(WPARAM wParam, LPARAM lParam)
+{
+ char *pszClassName;
+ UNREFERENCED_PARAMETER(wParam);
+
+ if ((char*)lParam == NULL) return 2;
+ pszClassName = MakeUrlClassName((char*)lParam);
+ if(pszClassName!= NULL)
+ if(RemoveAssocItem_Worker(pszClassName)) {
+ mir_free(pszClassName);
+ return 0;
+ }
+ mir_free(pszClassName); /* does NULL check */
+ return 3;
+}
+
+/************************* Open Handler ***************************/
+
+static BOOL InvokeHandler_Worker(const char *pszClassName, const TCHAR *pszParam, INT_PTR *res)
+{
+ void *pvParam;
+ char *pszService;
+ int index;
+ ASSOCDATA *assoc;
+
+ /* find it in list */
+ EnterCriticalSection(&csAssocList);
+ index = FindAssocItem(pszClassName);
+ if(index == -1) {
+ LeaveCriticalSection(&csAssocList);
+ return FALSE;
+ }
+ assoc = &pAssocList[index];
+ /* no service specified? correct registry to use main commandline */
+ if(assoc->pszService == NULL) {
+ EnsureAssocRegistered(assoc);
+ NotifyAssocChange(FALSE);
+ /* try main command line */
+ if ((int)ShellExecute(NULL, NULL, pszParam, NULL, NULL, SW_SHOWNORMAL) >= 32)
+ *res = 0; /* success */
+ return TRUE;
+ }
+ /* get params */
+ pszService = mir_strdup(assoc->pszService);
+ pvParam = t2s(pszParam, assoc->flags&FTDF_UNICODE, FALSE);
+ LeaveCriticalSection(&csAssocList);
+
+ /* call service */
+ if(pszService!= NULL && pvParam!= NULL)
+ *res = CallService(pszService, 0, (LPARAM)pvParam);
+ mir_free(pszService); /* does NULL check */
+ mir_free(pvParam); /* does NULL check */
+ return TRUE;
+}
+
+INT_PTR InvokeFileHandler(const TCHAR *pszFileName)
+{
+ char *pszClassName, *pszFileExt;
+ TCHAR *p;
+ INT_PTR res = CALLSERVICE_NOTFOUND;
+
+ /* find extension */
+ p = _tcsrchr(pszFileName, _T('.'));
+ if(p!= NULL) {
+ pszFileExt = t2a(p);
+ if(pszFileExt!= NULL) {
+ /* class name */
+ pszClassName = MakeFileClassName(pszFileExt);
+ if(pszClassName!= NULL)
+ if (!InvokeHandler_Worker(pszClassName, pszFileName, &res)) {
+ /* correct registry on error (no longer in list) */
+ RemoveRegFileExt(pszFileExt, pszClassName);
+ RemoveRegClass(pszClassName);
+ }
+ mir_free(pszClassName); /* does NULL check */
+ mir_free(pszFileExt);
+ }
+ }
+ return res;
+}
+
+INT_PTR InvokeUrlHandler(const TCHAR *pszUrl)
+{
+ char *pszClassName, *pszProtoPrefix, *p;
+ INT_PTR res = CALLSERVICE_NOTFOUND;
+
+ /* find prefix */
+ pszProtoPrefix = t2a(pszUrl);
+ if(pszProtoPrefix!= NULL) {
+ p = strchr(pszProtoPrefix, _T(':'));
+ if(p!= NULL) {
+ *(++p) = 0; /* remove trailing : */
+ /* class name */
+ pszClassName = MakeUrlClassName(pszProtoPrefix);
+ if(pszClassName!= NULL)
+ if (!InvokeHandler_Worker(pszClassName, pszUrl, &res))
+ /* correct registry on error (no longer in list) */
+ RemoveRegClass(pszClassName);
+ mir_free(pszClassName); /* does NULL check */
+ }
+ mir_free(pszProtoPrefix);
+ }
+ return res;
+}
+
+/************************* Options ********************************/
+
+static int CALLBACK ListViewSortDesc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+{
+ int cmp;
+ if (((ASSOCDATA*)lParam1)->pszFileExt!= NULL && ((ASSOCDATA*)lParam2)->pszFileExt!= NULL)
+ cmp = CompareStringA((LCID)lParamSort, 0, ((ASSOCDATA*)lParam1)->pszFileExt, -1, ((ASSOCDATA*)lParam2)->pszFileExt, -1);
+ else if (((ASSOCDATA*)lParam1)->pszFileExt == ((ASSOCDATA*)lParam2)->pszFileExt) /* both NULL */
+ cmp = CompareStringA((LCID)lParamSort, 0, ((ASSOCDATA*)lParam1)->pszClassName, -1, ((ASSOCDATA*)lParam2)->pszClassName, -1);
+ else /* different types, incomparable */
+ cmp = (((ASSOCDATA*)lParam1)->pszFileExt == NULL)?CSTR_LESS_THAN:CSTR_GREATER_THAN;
+ if(cmp == CSTR_EQUAL)
+ cmp = CompareString((LCID)lParamSort, 0, ((ASSOCDATA*)lParam1)->pszDescription, -1, ((ASSOCDATA*)lParam2)->pszDescription, -1);
+ if(cmp!= 0) cmp -= 2; /* maintain CRT conventions */
+ return cmp;
+}
+
+#define M_REFRESH_ICONS (WM_APP+1)
+static INT_PTR CALLBACK AssocListOptDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+ case WM_INITDIALOG:
+ { HWND hwndList;
+ HIMAGELIST himl;
+ LVITEM lvi;
+ ASSOCDATA *assoc;
+ int i;
+ TranslateDialogDefault(hwndDlg);
+ CoInitialize(NULL);
+ hwndList = GetDlgItem(hwndDlg, IDC_ASSOCLIST);
+#if defined(_UNICODE)
+ ListView_SetUnicodeFormat(hwndList, TRUE);
+#endif
+ SendDlgItemMessage(hwndDlg, IDC_HEADERTEXT, WM_SETFONT, SendMessage(GetParent(hwndDlg), PSM_GETBOLDFONT, 0, 0), 0);
+ /* checkboxes won't show up on Win95 without IE3+ or 4.70 (plugin opts uses the same) */
+ ListView_SetExtendedListViewStyle(hwndList, LVS_EX_CHECKBOXES|LVS_EX_FULLROWSELECT|LVS_EX_LABELTIP);
+ /* columns */
+ { LVCOLUMN lvc;
+ lvc.mask = LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM;
+ lvc.pszText = TranslateT("Type");
+ lvc.cx = 170;
+ ListView_InsertColumn(hwndList, lvc.iSubItem = 0, &lvc);
+ lvc.pszText = TranslateT("Description");
+ ListView_InsertColumn(hwndList, lvc.iSubItem = 1, &lvc);
+ }
+ /* create image storage */
+ EnterCriticalSection(&csAssocList);
+ { HDC hdc;
+ hdc = GetDC(hwndList);
+ if(hdc!= NULL) { /* BITSPIXEL is compatible with ILC_COLOR flags */
+ himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), GetDeviceCaps(hdc, BITSPIXEL)|ILC_MASK, nAssocListCount, 0);
+ ReleaseDC(hwndList, hdc);
+ } else himl = NULL;
+ }
+ ListView_SetImageList(hwndList, himl, LVSIL_SMALL); /* autodestroyed */
+ /* enum assoc list */
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_TEXT|LVIF_PARAM|LVIF_IMAGE;
+ for(i = 0;i<nAssocListCount;++i) {
+ assoc = &pAssocList[i];
+ lvi.iItem = 0;
+ lvi.lParam = (LPARAM)CopyAssocItem(assoc);
+ lvi.pszText = GetAssocTypeDesc(assoc);
+ lvi.iImage = ReplaceImageListAssocIcon(himl, assoc, -1);
+ lvi.iItem = ListView_InsertItem(hwndList, &lvi);
+ if(lvi.iItem!= -1) {
+ ListView_SetItemText(hwndList, lvi.iItem, 1, assoc->pszDescription);
+ ListView_SetCheckState(hwndList, lvi.iItem, IsAssocEnabled(assoc) && IsAssocRegistered(assoc));
+ }
+ }
+ /* sort items (before moving to groups) */
+ ListView_SortItems(hwndList, ListViewSortDesc, (LPARAM)CallService(MS_LANGPACK_GETLOCALE, 0, 0));
+ /* groups */
+ if(ListView_EnableGroupView(hwndList, TRUE) == 1) { /* returns 0 on pre WinXP or if commctls6 are disabled */
+ LVGROUP lvg;
+ int iItem;
+ /* dummy item for group */
+ lvi.iItem = ListView_GetItemCount(hwndList)-1;
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM|LVIF_IMAGE;
+ lvi.iImage = -1;
+ lvi.lParam = 0;
+ /* insert groups */
+ lvg.cbSize = sizeof(lvg);
+ lvg.mask = LVGF_HEADER|LVGF_GROUPID;
+ lvg.iGroupId = 2;
+ lvg.pszHeader = (WCHAR*)TranslateW(L"URLs on Websites");
+ lvi.iItem = ListView_InsertItem(hwndList, &lvi);
+ if(lvi.iItem!= -1) {
+ ListView_InsertGroup(hwndList, lvi.iItem, &lvg);
+ lvg.iGroupId = 1;
+ lvg.pszHeader = (WCHAR*)TranslateW(L"File Types");
+ iItem = lvi.iItem = ListView_InsertItem(hwndList, &lvi);
+ if(lvi.iItem!= -1)
+ ListView_InsertGroup(hwndList, lvi.iItem, &lvg);
+ else ListView_DeleteItem(hwndList, iItem);
+ }
+ /* move to group */
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM|LVIF_GROUPID;
+ for(lvi.iItem = 0;ListView_GetItem(hwndList, &lvi);++lvi.iItem) {
+ assoc = (ASSOCDATA*)lvi.lParam;
+ if(assoc == NULL) continue; /* groups */
+ lvi.iGroupId = (assoc->pszFileExt == NULL)+1;
+ ListView_SetItem(hwndList, &lvi);
+ }
+ }
+ LeaveCriticalSection(&csAssocList);
+ lvi.iItem = ListView_GetTopIndex(hwndList);
+ ListView_SetItemState(hwndList, lvi.iItem, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
+ ListView_SetColumnWidth(hwndList, 1, LVSCW_AUTOSIZE_USEHEADER); /* size to fit window */
+ /* only while running */
+ CheckDlgButton(hwndDlg, IDC_ONLYWHILERUNNING, (BOOL)DBGetContactSettingByte(NULL, "AssocMgr", "OnlyWhileRunning", SETTING_ONLYWHILERUNNING_DEFAULT));
+ /* autostart */
+ { TCHAR *pszRunCmd;
+ pszRunCmd = MakeRunCommand(TRUE, TRUE);
+ if(pszRunCmd!= NULL) {
+ CheckDlgButton(hwndDlg, IDC_AUTOSTART, IsRegRunEntry(_T("MirandaIM"), pszRunCmd));
+ mir_free(pszRunCmd);
+ }
+ }
+ return TRUE;
+ }
+ case WM_SETTINGCHANGE:
+ case M_REFRESH_ICONS:
+ { LVITEM lvi;
+ HIMAGELIST himl;
+ ASSOCDATA *assoc;
+ HWND hwndList;
+ hwndList = GetDlgItem(hwndDlg, IDC_ASSOCLIST);
+ himl = ListView_GetImageList(hwndList, LVSIL_SMALL);
+ /* enum items */
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM|LVIF_IMAGE;
+ for(lvi.iItem = 0;ListView_GetItem(hwndList, &lvi);++lvi.iItem) {
+ assoc = (ASSOCDATA*)lvi.lParam;
+ if(assoc == NULL) continue; /* groups */
+ lvi.iImage = ReplaceImageListAssocIcon(himl, assoc, lvi.iImage);
+ ListView_SetItem(hwndList, &lvi);
+ }
+ if(lvi.iItem) { /* ListView_Update() blinks */
+ ListView_RedrawItems(hwndList, 0, lvi.iItem-1);
+ UpdateWindow(hwndList);
+ }
+ return TRUE;
+ }
+ case WM_CTLCOLORSTATIC:
+ /* use same text color for header as for group boxes (WinXP+) */
+ if(GetDlgCtrlID((HWND)lParam) == IDC_HEADERTEXT) {
+ HMODULE hUxThemeDLL;
+ HBRUSH hBrush;
+ hUxThemeDLL = LoadLibraryA("UXTHEME"); /* all ascii, already loaded */
+ lParam = (LPARAM)GetDlgItem(hwndDlg, IDC_MISCLABEL);
+ hBrush = (HBRUSH)SendMessage(hwndDlg, msg, wParam, lParam);
+ if(hUxThemeDLL!= NULL) {
+ HTHEME (WINAPI *pfnGetWindowTheme)(HWND);
+ HRESULT (WINAPI *pfnGetThemeColor)(HTHEME, int, int, int, COLORREF*);
+ COLORREF clr;
+ *(PROC*)&pfnGetWindowTheme = GetProcAddress(hUxThemeDLL, "GetWindowTheme");
+ *(PROC*)&pfnGetThemeColor = GetProcAddress(hUxThemeDLL, "GetThemeColor");
+ if(pfnGetWindowTheme!= NULL && pfnGetThemeColor!= NULL) {
+ HTHEME hTheme;
+ hTheme = pfnGetWindowTheme((HWND)lParam);
+ if(hTheme!= NULL)
+ if (!pfnGetThemeColor(hTheme, BP_GROUPBOX, GBS_NORMAL, TMT_TEXTCOLOR, &clr)) {
+ SetBkMode((HDC)wParam, TRANSPARENT);
+ SetTextColor((HDC)wParam, clr);
+ }
+ }
+ FreeLibrary(hUxThemeDLL);
+ }
+ return (BOOL)hBrush;
+ }
+ break;
+ case WM_NCDESTROY:
+ CoUninitialize();
+ return TRUE;
+ case WM_COMMAND:
+ /* enable apply */
+ PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+#if defined(_UNICODE)
+ case WM_NOTIFYFORMAT:
+ SetWindowLong(hwndDlg, DWLP_MSGRESULT, NFR_UNICODE);
+ return TRUE;
+#endif
+ case WM_NOTIFY:
+ { NMHDR *nmhdr = (NMHDR*)lParam;
+ switch(nmhdr->idFrom) {
+ case IDC_ASSOCLIST:
+ switch(nmhdr->code) {
+ case LVN_DELETEITEM: /* also called on WM_DESTROY */
+ { LVITEM lvi;
+ lvi.mask = LVIF_PARAM;
+ lvi.iSubItem = 0;
+ lvi.iItem = ((NMLISTVIEW*)lParam)->iItem;
+ /* free memory */
+ if(ListView_GetItem(nmhdr->hwndFrom, &lvi))
+ mir_free((ASSOCDATA*)lvi.lParam); /* does NULL check */
+ return TRUE;
+ }
+ case LVN_ITEMCHANGED:
+ /* enable apply (not while loading) */
+ if(IsWindowVisible(nmhdr->hwndFrom))
+ PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ return TRUE;
+ case LVN_KEYDOWN:
+ /* workaround for WinXP (ListView with groups):
+ * eat keyboard navigation that goes beyond the first item in list
+ * as it would scroll out of scope in this case
+ * bug should not be present using WinVista and higher */
+ switch(((NMLVKEYDOWN*)lParam)->wVKey) {
+ case VK_UP:
+ { LVITEM lvi;
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ListView_GetNextItem(nmhdr->hwndFrom, -1, LVNI_FOCUSED);
+ lvi.iItem = ListView_GetNextItem(nmhdr->hwndFrom, lvi.iItem, LVNI_ABOVE);
+ if(lvi.iItem!= -1)
+ if(ListView_GetItem(nmhdr->hwndFrom, &lvi))
+ if ((ASSOCDATA*)lvi.lParam == NULL) /* groups */
+ lvi.iItem = -1;
+ if(lvi.iItem == -1) {
+ SetWindowLong(hwndDlg, DWLP_MSGRESULT, TRUE); /* eat it */
+ return TRUE;
+ }
+ break;
+ }
+ case VK_PRIOR:
+ { LVITEM lvi;
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ListView_GetNextItem(nmhdr->hwndFrom, -1, LVNI_FOCUSED);
+ lvi.iItem-= ListView_GetCountPerPage(nmhdr->hwndFrom);
+ if(lvi.iItem>= 0)
+ if(ListView_GetItem(nmhdr->hwndFrom, &lvi))
+ if ((ASSOCDATA*)lvi.lParam == NULL) /* groups */
+ lvi.iItem = -1;
+ if(lvi.iItem<0) {
+ ListView_SetItemState(nmhdr->hwndFrom, 0, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
+ SetWindowLong(hwndDlg, DWLP_MSGRESULT, TRUE); /* eat it */
+ return TRUE;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ break;
+ case 0:
+ switch(nmhdr->code) {
+ case PSN_APPLY:
+ { HWND hwndList;
+ LVITEM lvi;
+ BOOL fEnabled, fRegFailed = FALSE;
+ ASSOCDATA *assoc;
+
+ /* only while running */
+ DBWriteContactSettingByte(NULL, "AssocMgr", "OnlyWhileRunning", (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_ONLYWHILERUNNING)!= 0));
+
+ /* save enabled assoc items */
+ hwndList = GetDlgItem(hwndDlg, IDC_ASSOCLIST);
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM;
+ EnterCriticalSection(&csAssocList);
+ for(lvi.iItem = 0;ListView_GetItem(hwndList, &lvi);++lvi.iItem) {
+ assoc = (ASSOCDATA*)lvi.lParam;
+ if(assoc == NULL) continue; /* groups */
+ fEnabled = ListView_GetCheckState(hwndList, lvi.iItem);
+ SetAssocEnabled(assoc, fEnabled);
+
+ /* re-register registery keys */
+ if(fEnabled?!EnsureAssocRegistered(assoc):!UnregisterAssoc(assoc)) {
+ char *pszErr;
+ pszErr = GetWinErrorDescription(GetLastError());
+ ShowInfoMessage(NIIF_ERROR, Translate("File Association Error"), Translate("There was an error writing to the registry to modify the file/url associations.\nReason: %s"), (pszErr!= NULL)?pszErr:Translate("Unknown"));
+ mir_free(pszErr); /* does NULL check */
+ fRegFailed = TRUE; /* just show one time */
+ }
+ }
+ LeaveCriticalSection(&csAssocList);
+ NotifyAssocChange(TRUE);
+ PostMessage(hwndDlg, M_REFRESH_ICONS, 0, 0);
+ if(fRegFailed) {
+ }
+ /* autostart */
+ { TCHAR *pszRunCmd;
+ pszRunCmd = MakeRunCommand(TRUE, TRUE);
+ fRegFailed = FALSE;
+ if(pszRunCmd!= NULL) {
+ fEnabled = IsDlgButtonChecked(hwndDlg, IDC_AUTOSTART);
+ if(fEnabled?!AddRegRunEntry(_T("MirandaIM"), pszRunCmd):!RemoveRegRunEntry(_T("MirandaIM"), pszRunCmd)) {
+ char *pszErr;
+ pszErr = GetWinErrorDescription(GetLastError());
+ ShowInfoMessage(NIIF_ERROR, Translate("Autostart Error"), Translate("There was an error writing to the registry to modify the autostart list.\n\nReason: %s"), (pszErr!= NULL)?pszErr:Translate("Unknown"));
+ mir_free(pszErr); /* does NULL check */
+ fRegFailed = TRUE; /* just show one time */
+ }
+ mir_free(pszRunCmd);
+ }
+ }
+ return TRUE;
+ }
+ } /* code */
+ break;
+ } /* idFrom */
+ break;
+ } /* WM_NOTIFY */
+ }
+ return FALSE;
+}
+
+static int AssocListOptInit(WPARAM wParam, LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_ASSOCLIST);
+ odp.position = 900000100; /* network opts = 900000000 */
+ odp.ptszGroup = _T("Services"); /* autotranslated */
+ odp.ptszTitle = _T("Associations"); /* autotranslated */
+ odp.flags = ODPF_BOLDGROUPS|ODPF_EXPERTONLY|ODPF_TCHAR;
+ odp.pfnDlgProc = AssocListOptDlgProc;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+ return 0;
+}
+
+/************************* Misc ***********************************/
+
+void InitAssocList(void)
+{
+ /* Options */
+ INITCOMMONCONTROLSEX icc;
+ icc.dwSize = sizeof(icc);
+ icc.dwICC = ICC_LISTVIEW_CLASSES;
+ InitCommonControlsEx(&icc);
+ hHookOptInit = HookEvent(ME_OPT_INITIALISE, AssocListOptInit);
+
+ /* Assoc List */
+ pAssocList = NULL;
+ nAssocListCount = 0;
+ InitializeCriticalSection(&csAssocList);
+
+ /* Services */
+ hServiceAddFile = CreateServiceFunction(MS_ASSOCMGR_ADDNEWFILETYPE, ServiceAddNewFileType);
+ hServiceRemoveFile = CreateServiceFunction(MS_ASSOCMGR_REMOVEFILETYPE, ServiceRemoveFileType);
+ hServiceAddUrl = CreateServiceFunction(MS_ASSOCMGR_ADDNEWURLTYPE, ServiceAddNewUrlType);
+ hServiceRemoveUrl = CreateServiceFunction(MS_ASSOCMGR_REMOVEURLTYPE, ServiceRemoveUrlType);
+
+ /* Notify Shell */
+ nNotifyTimerID = 0;
+ InitializeCriticalSection(&csNotifyTimer);
+
+ /* register open-with app */
+ { TCHAR *pszAppFileName, *pszIconLoc, *pszRunCmd;
+ pszIconLoc = MakeIconLocation(NULL, 0);
+
+ // miranda32.exe
+ pszAppFileName = MakeAppFileName(TRUE);
+ pszRunCmd = MakeRunCommand(TRUE, FALSE);
+ if(pszAppFileName!= NULL && pszRunCmd!= NULL)
+ AddRegOpenWith(pszAppFileName, FALSE, _T(MIRANDANAME), pszIconLoc, pszRunCmd, NULL, NULL, NULL);
+ mir_free(pszRunCmd); /* does NULL check */
+ mir_free(pszAppFileName); /* does NULL check */
+ // assocmgr.dll
+ pszAppFileName = MakeAppFileName(FALSE);
+ pszRunCmd = MakeRunCommand(FALSE, TRUE);
+ if(pszAppFileName!= NULL && pszRunCmd!= NULL)
+ AddRegOpenWith(pszAppFileName, TRUE, _T(MIRANDANAME), pszIconLoc, pszRunCmd, DDEFILECMD, DDEAPP, DDETOPIC);
+ mir_free(pszRunCmd); /* does NULL check */
+ mir_free(pszAppFileName); /* does NULL check */
+
+ mir_free(pszIconLoc); /* does NULL check */
+ }
+
+ /* default items */
+ { FILETYPEDESC ftd;
+ ftd.cbSize = sizeof(FILETYPEDESC);
+ ftd.pszFileExt = ".dat";
+ ftd.pszMimeType = NULL;
+ ftd.ptszDescription = TranslateT("Miranda IM Database");
+ ftd.hInstance = hInst;
+ ftd.nIconResID = IDI_MIRANDAFILE;
+ ftd.ptszVerbDesc = NULL;
+ ftd.pszService = NULL;
+ ftd.flags = FTDF_DEFAULTDISABLED|FTDF_TCHAR;
+ ServiceAddNewFileType(0, (LPARAM)&ftd);
+ }
+}
+
+void UninitAssocList(void)
+{
+ BYTE fOnlyWhileRunning;
+ ASSOCDATA *assoc;
+ int i;
+
+ /* Options */
+ UnhookEvent(hHookOptInit);
+
+ /* Services */
+ DestroyServiceFunction(hServiceAddFile);
+ DestroyServiceFunction(hServiceRemoveFile);
+ DestroyServiceFunction(hServiceAddUrl);
+ DestroyServiceFunction(hServiceRemoveUrl);
+
+ /* Assoc List */
+ fOnlyWhileRunning = DBGetContactSettingByte(NULL, "AssocMgr", "OnlyWhileRunning", SETTING_ONLYWHILERUNNING_DEFAULT);
+ for(i = 0;i<nAssocListCount;++i) {
+ assoc = &pAssocList[i];
+
+ /* remove registry keys */
+ if(fOnlyWhileRunning)
+ UnregisterAssoc(assoc);
+
+ mir_free(assoc->pszClassName);
+ mir_free(assoc->pszDescription);
+ mir_free(assoc->pszService);
+ mir_free(assoc->pszFileExt); /* does NULL check */
+ mir_free(assoc->pszVerbDesc); /* does NULL check */
+ mir_free(assoc->pszMimeType); /* does NULL check */
+ }
+ mir_free(pAssocList);
+ DeleteCriticalSection(&csAssocList);
+
+ /* Notify Shell */
+ if(fOnlyWhileRunning && nAssocListCount)
+ NotifyAssocChange(TRUE);
+ DeleteCriticalSection(&csNotifyTimer);
+
+ /* unregister open-with app */
+ if(fOnlyWhileRunning) {
+ TCHAR *pszAppFileName;
+ // miranda32.exe
+ pszAppFileName = MakeAppFileName(TRUE);
+ if(pszAppFileName!= NULL)
+ RemoveRegOpenWith(pszAppFileName);
+ pszAppFileName = MakeAppFileName(FALSE);
+ // assocmgr.dll
+ if(pszAppFileName!= NULL)
+ RemoveRegOpenWith(pszAppFileName);
+ mir_free(pszAppFileName); /* does NULL check */
+ }
+} \ No newline at end of file