diff options
author | Goraf <goraf@miranda-ng.org> | 2016-02-05 22:40:46 +0000 |
---|---|---|
committer | Goraf <goraf@miranda-ng.org> | 2016-02-05 22:40:46 +0000 |
commit | bacf5e0272c566c30e591152084daf2f684aebe8 (patch) | |
tree | 99a7100a05892d1a7c39782f91039dc9a37df7f3 /plugins/ContextHelp/src | |
parent | 1d862b873baa48f43b4698fb95640f20dab3d4f8 (diff) |
ContextHelp: initial commit (adopted)
* working
* 32/64-bit compilable
git-svn-id: http://svn.miranda-ng.org/main/trunk@16225 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/ContextHelp/src')
-rw-r--r-- | plugins/ContextHelp/src/datastore.cpp | 590 | ||||
-rw-r--r-- | plugins/ContextHelp/src/dlgboxsubclass.cpp | 492 | ||||
-rw-r--r-- | plugins/ContextHelp/src/help.h | 146 | ||||
-rw-r--r-- | plugins/ContextHelp/src/helpdlg.cpp | 735 | ||||
-rw-r--r-- | plugins/ContextHelp/src/helppack.cpp | 520 | ||||
-rw-r--r-- | plugins/ContextHelp/src/main.cpp | 128 | ||||
-rw-r--r-- | plugins/ContextHelp/src/options.cpp | 797 | ||||
-rw-r--r-- | plugins/ContextHelp/src/resource.h | 61 | ||||
-rw-r--r-- | plugins/ContextHelp/src/stdafx.cxx | 18 | ||||
-rw-r--r-- | plugins/ContextHelp/src/stdafx.h | 52 | ||||
-rw-r--r-- | plugins/ContextHelp/src/streaminout.cpp | 771 | ||||
-rw-r--r-- | plugins/ContextHelp/src/utils.cpp | 430 | ||||
-rw-r--r-- | plugins/ContextHelp/src/version.h | 53 |
13 files changed, 4793 insertions, 0 deletions
diff --git a/plugins/ContextHelp/src/datastore.cpp b/plugins/ContextHelp/src/datastore.cpp new file mode 100644 index 0000000000..84c7474e21 --- /dev/null +++ b/plugins/ContextHelp/src/datastore.cpp @@ -0,0 +1,590 @@ +/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 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 (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "stdafx.h"
+
+
+extern HWND hwndHelpDlg;
+
+struct DlgControlData {
+ int id;
+ int type;
+ TCHAR *szTitle;
+ char *szText; // ANSI or UTF-8 depending on _UNICODE defined (for RichEdit)
+};
+
+struct DialogData {
+ char *szId;
+ char *szModule;
+ struct DlgControlData *control;
+ int controlCount;
+ DWORD timeLoaded, timeLastUsed;
+ int changes;
+ LCID locale;
+ UINT defaultCodePage;
+ int isLocaleRTL;
+};
+
+static struct DialogData *dialogCache = NULL;
+static int dialogCacheCount = 0;
+static CRITICAL_SECTION csDialogCache;
+static HANDLE hServiceFileChange, hFileChange;
+
+#define DIALOGCACHEEXPIRY 10*60*1000 // delete from cache after those milliseconds
+
+static INT_PTR ServiceFileChanged(WPARAM wParam, LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(lParam);
+
+ EnterCriticalSection(&csDialogCache);
+ for (int i = 0; i < dialogCacheCount; i++)
+ dialogCache[i].timeLastUsed = 0;
+ LeaveCriticalSection(&csDialogCache);
+
+ if ((HANDLE)wParam != NULL)
+ FindNextChangeNotification((HANDLE)wParam);
+
+ return 0;
+}
+
+void InitDialogCache(void)
+{
+ TCHAR szFilePath[MAX_PATH], *p;
+
+ InitializeCriticalSection(&csDialogCache);
+
+ hServiceFileChange = CreateServiceFunction("Help/HelpPackChanged", ServiceFileChanged);
+ hFileChange = INVALID_HANDLE_VALUE;
+ if (GetModuleFileName(NULL, szFilePath, sizeof(szFilePath))) {
+ p = _tcsrchr(szFilePath, _T('\\'));
+ if (p != NULL)
+ *(p + 1) = _T('\0');
+ hFileChange = FindFirstChangeNotification(szFilePath, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);
+ if (hFileChange != INVALID_HANDLE_VALUE)
+ CallService(MS_SYSTEM_WAITONHANDLE, (WPARAM)hFileChange, (LPARAM)"Help/HelpPackChanged");
+ }
+}
+
+static void FreeDialogCacheEntry(struct DialogData *entry)
+{
+ for (int i = 0; i < entry->controlCount; i++) {
+ mir_free(entry->control[i].szText); // does NULL check
+ mir_free(entry->control[i].szTitle); // does NULL check
+ }
+ mir_free(entry->control); // does NULL check
+ mir_free(entry->szId); // does NULL check
+ mir_free(entry->szModule); // does NULL check
+}
+
+void FreeDialogCache(void)
+{
+ if (hFileChange != INVALID_HANDLE_VALUE) {
+ CallService(MS_SYSTEM_REMOVEWAIT, (WPARAM)hFileChange, 0);
+ FindCloseChangeNotification(hFileChange);
+ }
+ DestroyServiceFunction(hServiceFileChange);
+
+ DeleteCriticalSection(&csDialogCache);
+ for (int i = 0; i < dialogCacheCount; i++)
+ FreeDialogCacheEntry(&dialogCache[i]);
+ dialogCacheCount = 0;
+ mir_free(dialogCache); // does NULL check
+ dialogCache = NULL;
+}
+
+/**************************** LOAD HELP ***************************/
+
+struct LoaderThreadStartParams {
+ HWND hwndCtl;
+ char *szDlgId;
+ char *szModule;
+ int ctrlId;
+};
+
+static void LoaderThread(void *arg)
+{
+ LoaderThreadStartParams *dtsp = (LoaderThreadStartParams *)arg;
+
+ FILE *fp;
+ char line[4096];
+ char *pszLine, *pszColon, *pszBuf;
+ int startOfLine = 0;
+
+ WIN32_FIND_DATA wfd;
+ HANDLE hFind;
+ TCHAR szDir[MAX_PATH];
+ TCHAR szSearch[MAX_PATH];
+ TCHAR *p;
+ int success = 0;
+
+ struct DialogData dialog;
+ struct DlgControlData *control;
+ void *buf;
+ ZeroMemory(&dialog, sizeof(dialog));
+
+ if (GetModuleFileName(NULL, szDir, sizeof(szDir))) {
+ p = _tcsrchr(szDir, _T('\\'));
+ if (p)
+ *p = _T('\0');
+ mir_sntprintf(szSearch, _T("%s\\helppack_*.txt"), szDir);
+
+ hFind = FindFirstFile(szSearch, &wfd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ if (lstrlen(wfd.cFileName)<4 || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.'))
+ continue;
+ mir_sntprintf(szSearch, _T("%s\\%s"), szDir, wfd.cFileName);
+ success = 1;
+ break;
+ } while (FindNextFile(hFind, &wfd));
+ FindClose(hFind);
+ }
+ }
+ if (!success) {
+ if (!Miranda_Terminated() && IsWindow(hwndHelpDlg))
+ PostMessage(hwndHelpDlg, M_HELPLOADFAILED, 0, (LPARAM)dtsp->hwndCtl);
+ return;
+ }
+
+ fp = _tfopen(szSearch, _T("rt"));
+ if (fp == NULL) {
+ if (!Miranda_Terminated() && IsWindow(hwndHelpDlg))
+ PostMessage(hwndHelpDlg, M_HELPLOADFAILED, 0, (LPARAM)dtsp->hwndCtl);
+ return;
+ }
+ fgets(line, sizeof(line), fp);
+ TrimString(line);
+ if (lstrcmpA(line, "Miranda Help Pack Version 1")) {
+ fclose(fp);
+ if (!Miranda_Terminated() && IsWindow(hwndHelpDlg))
+ PostMessage(hwndHelpDlg, M_HELPLOADFAILED, 0, (LPARAM)dtsp->hwndCtl);
+ return;
+ }
+
+ // headers
+ dialog.locale = LOCALE_USER_DEFAULT;
+ dialog.defaultCodePage = CP_ACP;
+ while (!feof(fp)) {
+ startOfLine = ftell(fp);
+ if (fgets(line, sizeof(line), fp) == NULL)
+ break;
+ TrimString(line);
+ if (IsEmpty(line) || line[0] == ';' || line[0] == '\0')
+ continue;
+ if (line[0] == '[')
+ break;
+ pszColon = strchr(line, ':');
+ if (pszColon == NULL) {
+ fclose(fp);
+ if (!Miranda_Terminated() && IsWindow(hwndHelpDlg))
+ PostMessage(hwndHelpDlg, M_HELPLOADFAILED, 0, (LPARAM)dtsp->hwndCtl);
+ return;
+ }
+ *pszColon = '\0';
+
+ // locale
+ if (!lstrcmpA(line, "Locale")) {
+ char szCP[6];
+ TrimString(pszColon + 1);
+ dialog.locale = MAKELCID((USHORT)strtol(pszColon + 1, NULL, 16), SORT_DEFAULT);
+ // codepage
+ if (GetLocaleInfoA(dialog.locale, LOCALE_IDEFAULTANSICODEPAGE, szCP, sizeof(szCP))) {
+ szCP[5] = '\0'; // codepages have 5 digits at max
+ dialog.defaultCodePage = atoi(szCP);
+ }
+ }
+ }
+
+ // RTL flag
+ // see also: http://blogs.msdn.com/michkap/archive/2006/03/03/542963.aspx
+ {
+ LOCALESIGNATURE sig;
+ dialog.isLocaleRTL = 0;
+ if (GetLocaleInfo(dialog.locale, LOCALE_FONTSIGNATURE, (LPTSTR)&sig, sizeof(sig) / sizeof(TCHAR)))
+ dialog.isLocaleRTL = (sig.lsUsb[3] & 0x8000000); // Win2000+: testing for 'Layout progress: horizontal from right to left' bit
+ switch (PRIMARYLANGID(LANGIDFROMLCID(dialog.locale))) { // prior to Win2000
+ case LANG_ARABIC:
+ case LANG_HEBREW:
+ case LANG_FARSI:
+ dialog.isLocaleRTL = 1;
+ }
+ }
+
+ // body
+ fseek(fp, startOfLine, SEEK_SET);
+ success = 1;
+ control = NULL;
+ while (!feof(fp)) {
+ if (fgets(line, sizeof(line), fp) == NULL)
+ break;
+ if (IsEmpty(line) || line[0] == ';' || line[0] == '\0')
+ continue;
+ TrimStringSimple(line);
+
+ if (line[0] == '[' && line[lstrlenA(line) - 1] == ']') {
+ pszLine = line + 1;
+ line[lstrlenA(line) - 1] = '\0';
+
+ // module
+ pszColon = strrchr(pszLine, ':');
+ if (pszColon == NULL)
+ continue;
+ *pszColon = '\0';
+ pszColon++;
+ TrimString(pszLine);
+ if (lstrcmpiA(dtsp->szModule, pszLine))
+ continue;
+ pszBuf = pszLine;
+
+ // dlgid
+ pszLine = pszColon;
+ pszColon = strrchr(pszLine, '@');
+ if (pszColon == NULL)
+ continue;
+ *pszColon = '\0';
+ pszColon++;
+ TrimString(pszColon);
+ if (lstrcmpA(dtsp->szDlgId, pszColon))
+ continue;
+
+ if (dialog.szModule == NULL && dialog.szId == NULL) {
+ dialog.szModule = mir_strdup(pszBuf);
+ dialog.szId = mir_strdup(pszColon);
+ if (dialog.szId == NULL || dialog.szModule == NULL) {
+ success = 0;
+ break;
+ }
+ }
+ buf = (struct DlgControlData*)mir_realloc(dialog.control, sizeof(struct DlgControlData)*(dialog.controlCount + 1));
+ if (buf == NULL) {
+ success = 0;
+ break;
+ }
+ dialog.controlCount++;
+ dialog.control = (struct DlgControlData*)buf;
+ control = &dialog.control[dialog.controlCount - 1];
+ ZeroMemory(control, sizeof(*control));
+
+ // ctlid
+ TrimString(pszLine);
+ control->id = atoi(pszLine);
+ }
+ else if (control != NULL) {
+ pszLine = line;
+
+ // ctltext
+ pszColon = strchr(pszLine, '=');
+ if (pszColon == NULL)
+ continue;
+ *pszColon = '\0';
+ pszColon++;
+ TrimString(pszColon);
+ TrimString(pszLine);
+ if (*pszColon == '\0' || *pszLine == '\0')
+ continue;
+ int size = lstrlenA(pszLine) + 1;
+ control->szTitle = (WCHAR*)mir_alloc(size*sizeof(WCHAR));
+ if (control->szTitle != NULL) {
+ *control->szTitle = _T('\0');
+ MultiByteToWideChar(dialog.defaultCodePage, 0, pszLine, -1, control->szTitle, size);
+ }
+
+ // text
+ control->szText = mir_utf8encodecp(pszColon, dialog.defaultCodePage);
+ control = NULL; // control done
+ }
+ }
+ fclose(fp);
+
+ if (success) {
+ int i, dialogInserted = 0;
+
+ dialog.timeLoaded = dialog.timeLastUsed = GetTickCount();
+ EnterCriticalSection(&csDialogCache);
+ for (i = 0; i < dialogCacheCount; i++) {
+ if (dialogCache[i].timeLastUsed && dialogCache[i].timeLastUsed<(dialog.timeLoaded - DIALOGCACHEEXPIRY)) {
+ FreeDialogCacheEntry(&dialogCache[i]);
+ if (dialogInserted || !dialog.controlCount) {
+ MoveMemory(dialogCache + i, dialogCache + i + 1, sizeof(struct DialogData)*(dialogCacheCount - i - 1));
+ dialogCacheCount--;
+ buf = (struct DialogData*)mir_realloc(dialogCache, sizeof(struct DialogData)*dialogCacheCount);
+ if (buf != NULL)
+ dialogCache = (struct DialogData*)buf;
+ else if (!dialogCacheCount)
+ dialogCache = NULL;
+ }
+ else {
+ dialogInserted = 1;
+ dialogCache[i] = dialog;
+ }
+ }
+ }
+ if (dialog.controlCount && !dialogInserted) {
+ buf = (struct DialogData*)mir_realloc(dialogCache, sizeof(struct DialogData)*(dialogCacheCount + 1));
+ if (buf != NULL) {
+ dialogCacheCount++;
+ dialogCache = (struct DialogData*)buf;
+ dialogCache[dialogCacheCount - 1] = dialog;
+ dialogInserted = 1;
+ }
+ }
+ LeaveCriticalSection(&csDialogCache);
+
+ if (!dialogInserted) {
+ mir_free(dialog.szId); // does NULL check
+ mir_free(dialog.szModule); // does NULL check
+ mir_free(dialog.control); // does NULL check
+ }
+ if (!Miranda_Terminated() && IsWindow(hwndHelpDlg))
+ PostMessage(hwndHelpDlg, M_HELPLOADED, 0, (LPARAM)dtsp->hwndCtl);
+ }
+ if (!success) {
+ mir_free(dialog.szId); // does NULL check
+ mir_free(dialog.szModule); // does NULL check
+ mir_free(dialog.control); // does NULL check
+ if (!Miranda_Terminated() && IsWindow(hwndHelpDlg))
+ PostMessage(hwndHelpDlg, M_HELPLOADFAILED, 0, (LPARAM)dtsp->hwndCtl);
+ }
+
+ mir_free(dtsp->szDlgId);
+ mir_free(dtsp->szModule);
+ mir_free(dtsp);
+}
+
+// mir_free() the return value
+char *CreateControlIdentifier(const char *pszDlgId, const char *pszModule, int ctrlId, HWND hwndCtl)
+{
+ int size;
+ char *szId;
+ TCHAR szDefCtlText[128];
+ GetControlTitle(hwndCtl, szDefCtlText, sizeof(szDefCtlText));
+ size = lstrlenA(pszModule) + lstrlenA(pszDlgId) + sizeof(szDefCtlText) + 22;
+ szId = (char*)mir_alloc(size);
+ mir_snprintf(szId, size, "[%s:%i@%s]\r\n%S%s", pszModule, ctrlId, pszDlgId, szDefCtlText, szDefCtlText[0] ? "=" : "");
+
+ return szId;
+}
+
+int GetControlHelp(HWND hwndCtl, const char *pszDlgId, const char *pszModule, int ctrlId, TCHAR **ppszTitle, char **ppszText, int *pType, LCID *pLocaleID, UINT *pCodePage, BOOL *pIsRTL, DWORD flags)
+{
+ int i, j, useNext = 0;
+ struct LoaderThreadStartParams *dtsp;
+
+ EnterCriticalSection(&csDialogCache);
+ for (i = 0; i < dialogCacheCount; i++) {
+ if (!(flags&GCHF_DONTLOAD)) {
+ if (!dialogCache[i].timeLastUsed || dialogCache[i].timeLastUsed<(GetTickCount() - DIALOGCACHEEXPIRY)) {
+ struct DialogData *buf;
+ FreeDialogCacheEntry(&dialogCache[i]);
+ MoveMemory(dialogCache + i, dialogCache + i + 1, sizeof(struct DialogData)*(dialogCacheCount - i - 1));
+ dialogCacheCount--;
+ buf = (struct DialogData*)mir_realloc(dialogCache, sizeof(struct DialogData)*dialogCacheCount);
+ if (buf != NULL)
+ dialogCache = (struct DialogData*)buf;
+ else if (!dialogCacheCount)
+ dialogCache = NULL;
+ i--;
+ continue;
+ }
+ }
+ if (lstrcmpA(pszDlgId, dialogCache[i].szId))
+ break;
+ for (j = 0; j < dialogCache[i].controlCount; j++) {
+ if (ctrlId == dialogCache[i].control[j].id || useNext || dialogCache[i].control[j].id == 0) {
+ if (dialogCache[i].control[j].szTitle == NULL) {
+ useNext = 1;
+ continue;
+ }
+ if (ppszTitle)
+ *ppszTitle = dialogCache[i].control[j].szTitle;
+ if (ppszText)
+ *ppszText = dialogCache[i].control[j].szText;
+ if (pType)
+ *pLocaleID = dialogCache[i].locale;
+ if (pCodePage)
+ *pCodePage = CP_UTF8;
+ if (pIsRTL)
+ *pIsRTL = dialogCache[i].isLocaleRTL;
+ if (dialogCache[i].control[j].id != ctrlId && !useNext)
+ continue;
+ dialogCache[i].timeLastUsed = GetTickCount();
+ LeaveCriticalSection(&csDialogCache);
+ return 0;
+ }
+ }
+ break;
+ }
+
+ if (ppszTitle)
+ *ppszTitle = NULL;
+ if (ppszText)
+ *ppszText = NULL;
+ if (pType)
+ *pType = CTLTYPE_UNKNOWN;
+ if (pLocaleID)
+ *pLocaleID = LOCALE_USER_DEFAULT;
+ if (pCodePage)
+ *pCodePage = CP_ACP;
+
+ if (!(flags&GCHF_DONTLOAD)) {
+ dtsp = (struct LoaderThreadStartParams*)mir_alloc(sizeof(struct LoaderThreadStartParams));
+ if (dtsp == NULL) {
+ LeaveCriticalSection(&csDialogCache);
+ return 0;
+ }
+ dtsp->szDlgId = mir_strdup(pszDlgId);
+ dtsp->szModule = mir_strdup(pszModule);
+ if (dtsp->szDlgId == NULL || dtsp->szModule == NULL) {
+ mir_free(dtsp->szDlgId); // does NULL check
+ mir_free(dtsp->szModule); // does NULL check
+ mir_free(dtsp);
+ LeaveCriticalSection(&csDialogCache);
+ return 0;
+ }
+ dtsp->ctrlId = ctrlId;
+ dtsp->hwndCtl = hwndCtl;
+ mir_forkthread(LoaderThread, dtsp);
+ }
+ LeaveCriticalSection(&csDialogCache);
+
+ return 1;
+}
+
+/**************************** SAVE HELP ***************************/
+
+#ifdef EDITOR
+void SetControlHelp(const char *pszDlgId, const char *pszModule, int ctrlId, TCHAR *pszTitle, char *pszText, int type)
+{
+ int i, j;
+ int found = 0;
+ void *buf;
+
+ EnterCriticalSection(&csDialogCache);
+ j = 0;
+ for (i = 0; i < dialogCacheCount; i++) {
+ if (lstrcmpA(pszDlgId, dialogCache[i].szId))
+ continue;
+ for (j = 0; j < dialogCache[i].controlCount; j++) {
+ if (ctrlId == dialogCache[i].control[j].id) {
+ mir_free(dialogCache[i].control[j].szTitle); // does NULL check
+ mir_free(dialogCache[i].control[j].szText); // does NULL check
+ dialogCache[i].control[j].szTitle = NULL;
+ dialogCache[i].control[j].szText = NULL;
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ buf = (struct DlgControlData*)mir_realloc(dialogCache[i].control, sizeof(struct DlgControlData)*(dialogCache[i].controlCount + 1));
+ if (buf == NULL) {
+ LeaveCriticalSection(&csDialogCache);
+ return;
+ }
+ dialogCache[i].control = (struct DlgControlData*)buf;
+ j = dialogCache[i].controlCount++;
+ found = 1;
+ }
+ break;
+ }
+ if (!found) {
+ buf = (struct DialogData*)mir_realloc(dialogCache, sizeof(struct DialogData)*(dialogCacheCount + 1));
+ if (buf == NULL) {
+ LeaveCriticalSection(&csDialogCache);
+ return;
+ }
+ dialogCache = (struct DialogData*)buf;
+ dialogCache[i].control = (struct DlgControlData*)mir_alloc(sizeof(struct DlgControlData));
+ if (dialogCache[i].control == NULL) {
+ LeaveCriticalSection(&csDialogCache);
+ return;
+ }
+ dialogCache[i].controlCount = 1;
+ i = dialogCacheCount;
+ j = 0;
+
+ dialogCache[i].szId = mir_strdup(pszDlgId);
+ dialogCache[i].szModule = mir_strdup(pszModule);
+ if (dialogCache[i].szId == NULL || dialogCache[i].szModule == NULL) {
+ mir_free(dialogCache[i].szId); // does NULL check
+ mir_free(dialogCache[i].szModule); // does NULL check
+ LeaveCriticalSection(&csDialogCache);
+ return;
+ }
+ dialogCacheCount++;
+ dialogCache[i].timeLoaded = 0;
+ }
+ dialogCache[i].control[j].szTitle = mir_tstrdup(pszTitle); // does NULL arg check
+ dialogCache[i].control[j].szText = mir_strdup(pszText); // does NULL arg check
+ dialogCache[i].control[j].type = type;
+ dialogCache[i].control[j].id = ctrlId;
+ dialogCache[i].timeLastUsed = GetTickCount();
+ dialogCache[i].changes = 1;
+ LeaveCriticalSection(&csDialogCache);
+}
+
+static void DialogCacheSaveThread(void *unused)
+{
+ int success = 0;
+ UNREFERENCED_PARAMETER(unused);
+
+ // TODO: port the following code to write to the helppack file instead
+ // (netlib code already removed)
+ /*
+ { WCHAR *szDecoded;
+ char *szEncoded=mir_strdup(dialogCache[i].control[j].szText);
+ if (mir_utf8decode(szEncoded, &szDecoded)) {
+ [...]
+ mir_free(szDecoded)
+ }
+ mir_free(szEncoded); // does NULL check
+ }
+
+
+ int i, j;
+ struct ResizableCharBuffer data={0}, dlgId={0};
+
+ for (i = 0; i < dialogCacheCount; i++) {
+ if(!dialogCache[i].changes) continue;
+
+ AppendToCharBuffer(&data, "t <dialog id=\"%s\" module=\"%s\">\r\n", dialogCache[i].szId, dialogCache[i].szModule);
+ for (j = 0; j < dialogCache[i].controlCount; j++) {
+ AppendToCharBuffer(&data, "<control id=%d type=%d>\r\n", dialogCache[i].control[j].id, dialogCache[i].control[j].type);
+ if (dialogCache[i].control[j].szTitle)
+ AppendToCharBuffer(&data, "<title>%s</title>\r\n", dialogCache[i].control[j].szTitle);
+ if (dialogCache[i].control[j].szText)
+ AppendToCharBuffer(&data, "<text>%s</text>\r\n", dialogCache[i].control[j].szText);
+ AppendToCharBuffer(&data, "</control>\r\n");
+ }
+ AppendToCharBuffer(&data, "</dialog>");
+ }
+
+ if(success) {
+ dialogCache[i].changes = 0;
+ dialogCache[i].timeLoaded = GetTickCount();
+ }
+ */
+ MessageBoxEx(NULL, success ? TranslateT("Help saving complete.") : TranslateT("Help saving failed!"), TranslateT("Help Editor"), (success ? MB_ICONINFORMATION : MB_ICONERROR) | MB_OK | MB_SETFOREGROUND | MB_TOPMOST | MB_TASKMODAL, LANGIDFROMLCID(Langpack_GetDefaultLocale()));
+}
+
+void SaveDialogCache(void)
+{
+ mir_forkthread(DialogCacheSaveThread, 0);
+}
+#endif // defined EDITOR
diff --git a/plugins/ContextHelp/src/dlgboxsubclass.cpp b/plugins/ContextHelp/src/dlgboxsubclass.cpp new file mode 100644 index 0000000000..ab0a63f4dd --- /dev/null +++ b/plugins/ContextHelp/src/dlgboxsubclass.cpp @@ -0,0 +1,492 @@ +/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 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 (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "stdafx.h"
+
+
+#define SC_CONTEXTHELP_SEPARATOR SC_SEPARATOR+1
+#define SC_CONTEXTHELP_DIALOG SC_CONTEXTHELP+1
+
+#define PROP_CONTEXTSTATE _T("HelpPlugin_ContextState")
+#define PROPF_MENUFORCED 0x01 // always show help context menu for ctl (override default)
+#define PROPF_MENUDISABLED 0x02 // never show help context menu for ctl
+#define PROPF_AUTOTIPFORCED 0x04 // always show autotip for ctl (override default)
+#define PROPF_AUTOTIPDISABLED 0x08 // never show autotip for ctl
+
+extern HINSTANCE hInst;
+extern HWND hwndHelpDlg;
+extern WORD settingAutoTipDelay;
+static HHOOK hMessageHook, hKeyboardHook, hEatNextMouseHook = NULL;
+static HANDLE hServiceShowHelp, hServiceSetContext;
+
+struct DlgBoxSubclassData {
+ HWND hwndDlg;
+ WNDPROC pfnOldWndProc;
+ DWORD flags;
+} static *dlgBoxSubclass = NULL;
+static int dlgBoxSubclassCount = 0;
+static CRITICAL_SECTION csDlgBoxSubclass;
+
+#define DBSDF_MINIMIZABLE 0x01 // WS_MINIMIZEBOX style was set on hooked window
+#define DBSDF_MAXIMIZABLE 0x02 // WS_MAXIMIZEBOX style was set on hooked window
+
+struct FindChildAtPointData {
+ HWND hwnd;
+ POINT pt;
+ int bestArea;
+};
+
+// ChildWindowFromPoint() messes up with group boxes
+static INT_PTR CALLBACK FindChildAtPointEnumProc(HWND hwnd, LPARAM lParam)
+{
+ if (IsWindowVisible(hwnd)) {
+ struct FindChildAtPointData *fcap = (struct FindChildAtPointData*)lParam;
+ RECT rcVisible, rc, rcParent;
+ GetWindowRect(hwnd, &rc);
+ GetWindowRect(GetParent(hwnd), &rcParent);
+ IntersectRect(&rcVisible, &rcParent, &rc);
+ if (PtInRect(&rcVisible, fcap->pt)) {
+ int thisArea = (rc.bottom - rc.top)*(rc.right - rc.left);
+ if (thisArea && (thisArea<fcap->bestArea || fcap->bestArea == 0)) {
+ fcap->bestArea = thisArea;
+ fcap->hwnd = hwnd;
+ }
+ }
+ }
+ return TRUE;
+}
+
+// IsChild() messes up with owned windows
+int IsRealChild(HWND hwndParent, HWND hwnd)
+{
+ while (hwnd != NULL) {
+ if (hwnd == hwndParent)
+ return 1;
+ if (hwndParent == GetWindow(hwnd, GW_OWNER))
+ return 0;
+ hwnd = GetParent(hwnd);
+ }
+
+ return 0;
+}
+
+static BOOL CALLBACK RemovePropForAllChildsEnumProc(HWND hwnd, LPARAM lParam)
+{
+ RemoveProp(hwnd, (TCHAR*)lParam);
+
+ return TRUE;
+}
+
+static HWND hwndMouseMoveDlg = NULL;
+static UINT idMouseMoveTimer = 0;
+static LONG cursorPos = MAKELONG(-1, -1);
+static int openedAutoTip = 0;
+
+static void CALLBACK NoMouseMoveForDelayTimerProc(HWND hwnd, UINT msg, UINT idTimer, DWORD time)
+{
+ POINT pt;
+ HWND hwndCtl;
+ struct FindChildAtPointData fcap;
+ UNREFERENCED_PARAMETER(msg);
+ UNREFERENCED_PARAMETER(time);
+
+ KillTimer(hwnd, idTimer);
+ if (idMouseMoveTimer != idTimer)
+ return;
+ idMouseMoveTimer = 0;
+ if (!settingAutoTipDelay || !IsWindow(hwndMouseMoveDlg))
+ return;
+
+ ZeroMemory(&fcap, sizeof(fcap));
+ if (!GetCursorPos(&pt))
+ return;
+ // ChildWindowFromPoint() messes up with group boxes
+ fcap.hwnd = NULL;
+ fcap.pt = pt;
+ EnumChildWindows(hwndMouseMoveDlg, (WNDENUMPROC)FindChildAtPointEnumProc, (LPARAM)&fcap);
+ hwndCtl = fcap.hwnd;
+ if (hwndCtl == NULL) {
+ ScreenToClient(hwndMouseMoveDlg, &pt);
+ hwndCtl = ChildWindowFromPointEx(hwndMouseMoveDlg, pt, CWP_SKIPINVISIBLE);
+ if (hwndCtl == NULL)
+ return;
+ }
+
+ LONG flags = (LONG)GetProp(hwndCtl, PROP_CONTEXTSTATE);
+ if (flags&PROPF_AUTOTIPDISABLED)
+ return;
+ flags = SendMessage(hwndCtl, WM_GETDLGCODE, (WPARAM)VK_RETURN, (LPARAM)NULL);
+ if (flags&DLGC_HASSETSEL || flags&DLGC_WANTALLKEYS)
+ return; // autotips on edits are annoying
+
+ CURSORINFO ci;
+ BOOL(WINAPI *pfnGetCursorInfo)(CURSORINFO*);
+ ci.cbSize = sizeof(ci);
+ *(FARPROC*)&pfnGetCursorInfo = GetProcAddress(GetModuleHandleA("USER32"), "GetCursorInfo");
+ // if(pfnGetCursorInfo && IsWinVer2000Plus()) // call not safe for WinNT4
+ if (pfnGetCursorInfo(&ci) && !(ci.flags&CURSOR_SHOWING))
+ return;
+
+ if (IsRealChild(hwndMouseMoveDlg, hwndCtl) && hwndHelpDlg == NULL) {
+ hwndHelpDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_HELP), NULL, HelpDlgProc);
+ if (hwndHelpDlg == NULL)
+ return;
+ openedAutoTip = 1;
+ PostMessage(hwndHelpDlg, M_CHANGEHELPCONTROL, 0, (LPARAM)hwndCtl);
+ }
+}
+
+static LRESULT CALLBACK KeyboardInputHookProc(int code, WPARAM wParam, LPARAM lParam)
+{
+ if (code == HC_ACTION && idMouseMoveTimer != 0) {
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+ }
+ return CallNextHookEx(hKeyboardHook, code, wParam, lParam);
+}
+
+// workaround for WM_HELP (SC_CONTEXTHELP causes an additional WM_LBUTTONUP when selecting)
+static LRESULT CALLBACK EatNextMouseButtonUpHookProc(int code, WPARAM wParam, LPARAM lParam)
+{
+ if (code >= 0 && wParam == WM_LBUTTONUP) {
+ UnhookWindowsHookEx(hEatNextMouseHook); // unhook ourselves
+ hEatNextMouseHook = NULL;
+ return -1;
+ }
+ return CallNextHookEx(hEatNextMouseHook, code, wParam, lParam);
+}
+
+static LRESULT CALLBACK DialogBoxSubclassProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ WNDPROC pfnWndProc;
+ DWORD flags;
+ int i;
+
+ EnterCriticalSection(&csDlgBoxSubclass);
+ for (i = 0; i<dlgBoxSubclassCount; i++)
+ if (dlgBoxSubclass[i].hwndDlg == hwndDlg)
+ break;
+ if (i == dlgBoxSubclassCount) {
+ LeaveCriticalSection(&csDlgBoxSubclass);
+ return 0;
+ }
+ pfnWndProc = dlgBoxSubclass[i].pfnOldWndProc;
+ flags = dlgBoxSubclass[i].flags;
+ if (msg == WM_NCDESTROY) {
+ struct DlgBoxSubclassData *buf;
+ MoveMemory(dlgBoxSubclass + i, dlgBoxSubclass + i + 1, sizeof(struct DlgBoxSubclassData)*(dlgBoxSubclassCount - i - 1));
+ dlgBoxSubclassCount--;
+ buf = (struct DlgBoxSubclassData*)mir_realloc(dlgBoxSubclass, sizeof(struct DlgBoxSubclassData)*dlgBoxSubclassCount);
+ if (buf != NULL)
+ dlgBoxSubclass = buf;
+ else if (!dlgBoxSubclassCount)
+ dlgBoxSubclass = NULL;
+ }
+ LeaveCriticalSection(&csDlgBoxSubclass);
+
+ switch (msg) {
+ case WM_INITMENUPOPUP:
+ if (flags&DBSDF_MINIMIZABLE || flags&DBSDF_MAXIMIZABLE) {
+ HMENU hMenu = GetSystemMenu(hwndDlg, FALSE);
+ if ((HMENU)wParam != hMenu)
+ break;
+ int isMin = IsIconic(hwndDlg);
+ int isMax = IsZoomed(hwndDlg);
+ EnableMenuItem(hMenu, SC_RESTORE, MF_BYCOMMAND | (isMin || isMax) ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(hMenu, SC_MINIMIZE, MF_BYCOMMAND | (flags&DBSDF_MINIMIZABLE && !isMin) ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(hMenu, SC_MAXIMIZE, MF_BYCOMMAND | (flags&DBSDF_MAXIMIZABLE && !isMax) ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(hMenu, SC_SIZE, MF_BYCOMMAND | (GetWindowLongPtr(hwndDlg, GWL_STYLE)&WS_THICKFRAME && !isMin && !isMax) ? MF_ENABLED : MF_GRAYED);
+ }
+ break;
+ case WM_MOUSEMOVE: // TrackMouseEvent() would disturb too much
+ if (!settingAutoTipDelay)
+ break;
+ if (cursorPos == lParam)
+ break;
+ cursorPos = lParam;
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MOUSEWHEEL:
+ if (!settingAutoTipDelay)
+ break;
+ if (msg != WM_MOUSEMOVE && !idMouseMoveTimer)
+ break;
+ if (openedAutoTip && IsWindow(hwndHelpDlg))
+ DestroyWindow(hwndHelpDlg);
+ openedAutoTip = 0;
+ hwndMouseMoveDlg = hwndDlg;
+ if (hwndHelpDlg == NULL)
+ idMouseMoveTimer = SetTimer(NULL, idMouseMoveTimer, settingAutoTipDelay, (TIMERPROC)NoMouseMoveForDelayTimerProc);
+ break;
+ case WM_CAPTURECHANGED:
+ if ((HWND)lParam == hwndDlg)
+ break;
+ case WM_SHOWWINDOW:
+ case WM_WINDOWPOSCHANGING:
+ case WM_MOVING:
+ case WM_SIZING:
+ case WM_CANCELMODE:
+ case WM_CHILDACTIVATE:
+ case WM_MOUSEACTIVATE:
+ case WM_ACTIVATEAPP:
+ case WM_ACTIVATE:
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+ break;
+ case WM_SYSCOMMAND:
+ if ((UINT)wParam == SC_CONTEXTHELP_DIALOG) { // alt. "What's this Dialog?"
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+ if (hwndHelpDlg == NULL) {
+ hwndHelpDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_HELP), NULL, HelpDlgProc);
+ if (hwndHelpDlg == NULL)
+ break;
+ }
+ SendMessage(hwndHelpDlg, M_CHANGEHELPCONTROL, 0, (LPARAM)hwndDlg);
+ return 0;
+ }
+ break;
+ case WM_CONTEXTMENU:
+ {
+ POINT pt;
+ HWND hwndCtl;
+ struct FindChildAtPointData fcap;
+
+ // workaround for badly coded plugins that do display a context menu
+ // and pass the message to DefWindowProc afterwards (doing a "break;").
+ if (GetTickCount() - GetMessageTime()>10)
+ return 0;
+
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+
+ ZeroMemory(&fcap, sizeof(fcap));
+ POINTSTOPOINT(pt, MAKEPOINTS(lParam));
+ // ChildWindowFromPoint() messes up with group boxes
+ fcap.hwnd = NULL;
+ fcap.pt = pt;
+ EnumChildWindows(hwndDlg, (WNDENUMPROC)FindChildAtPointEnumProc, (LPARAM)&fcap);
+ hwndCtl = fcap.hwnd;
+ if (hwndCtl == NULL) {
+ ScreenToClient(hwndDlg, &pt);
+ hwndCtl = ChildWindowFromPointEx(hwndDlg, pt, CWP_SKIPINVISIBLE);
+ if (hwndCtl == NULL)
+ break;
+ POINTSTOPOINT(pt, MAKEPOINTS(lParam));
+ }
+ {
+ LONG flags = (LONG)GetProp(hwndCtl, PROP_CONTEXTSTATE);
+ if (flags&PROPF_MENUDISABLED)
+ break;
+ else if (!(flags&PROPF_MENUFORCED)) {
+ int type = GetControlType(hwndCtl);
+ // showing a context menu on these looks silly (multi components)
+ if (type == CTLTYPE_TOOLBAR || type == CTLTYPE_LISTVIEW || type == CTLTYPE_TREEVIEW || type == CTLTYPE_STATUSBAR || type == CTLTYPE_CLC)
+ break;
+ }
+ }
+ if (IsRealChild(hwndDlg, hwndCtl)) {
+ HMENU hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, MF_STRING, SC_CONTEXTHELP, (hwndCtl == hwndDlg) ? TranslateT("&What's this Dialog?") : TranslateT("&What's this?"));
+ if (TrackPopupMenuEx(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_HORPOSANIMATION | TPM_VERPOSANIMATION | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, pt.x, pt.y, hwndDlg, NULL)) {
+ if (hwndHelpDlg == NULL) {
+ hwndHelpDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_HELP), NULL, HelpDlgProc);
+ if (hwndHelpDlg == NULL) {
+ DestroyMenu(hMenu);
+ break;
+ }
+ }
+ SendMessage(hwndHelpDlg, M_CHANGEHELPCONTROL, 0, (LPARAM)hwndCtl);
+ }
+ DestroyMenu(hMenu);
+ }
+ return 0;
+ }
+ case WM_HELP:
+ {
+ HELPINFO *hi = (HELPINFO*)lParam;
+ if (hi->iContextType != HELPINFO_WINDOW) break;
+ // fix for SHBrowseForFolder() dialog, which sends unhandled help to parent
+ if (!IsRealChild(hwndDlg, (HWND)hi->hItemHandle))
+ break;
+
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+
+ if (!IsWindow(hwndHelpDlg)) {
+ hwndHelpDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_HELP), NULL, HelpDlgProc);
+ if (hwndHelpDlg == NULL)
+ break;
+ }
+ SendMessage(hwndHelpDlg, M_CHANGEHELPCONTROL, 0, (LPARAM)hi->hItemHandle);
+ // we need to eat the next WM_LBUTTONDOWN (if invoked by mouse)
+ if (GetKeyState(GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON) & 0x8000 && hEatNextMouseHook == NULL)
+ hEatNextMouseHook = SetWindowsHookEx(WH_MOUSE, EatNextMouseButtonUpHookProc, NULL, GetCurrentThreadId());
+ return TRUE;
+ }
+ case WM_NCDESTROY:
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+ EnumChildWindows(hwndDlg, RemovePropForAllChildsEnumProc, (LPARAM)PROP_CONTEXTSTATE);
+ {
+ TCHAR text[64];
+ mir_sntprintf(text, sizeof(text), _T("unhooked window 0x%X for context help\n"), hwndDlg);
+ OutputDebugString(text);
+ }
+ SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)pfnWndProc);
+ break;
+ }
+ return CallWindowProc(pfnWndProc, hwndDlg, msg, wParam, lParam);
+}
+
+static LRESULT CALLBACK HelpSendMessageHookProc(int code, WPARAM wParam, LPARAM lParam)
+{
+ if (code >= 0) {
+ CWPSTRUCT *msg = (CWPSTRUCT*)lParam;
+ switch (msg->message) {
+ case WM_INITDIALOG: // dialogs and message boxes
+ if (GetClassLong(msg->hwnd, GCW_ATOM) != 32770) // class="#32770"
+ break;
+ if (msg->hwnd == hwndHelpDlg || (DLGPROC)GetWindowLongPtr(msg->hwnd, DWLP_DLGPROC) == HelpDlgProc)
+ break;
+#ifndef EDITOR
+ if ((DLGPROC)GetWindowLongPtr(msg->hwnd, DWLP_DLGPROC) == ShadowDlgProc)
+ break;
+#endif
+ {
+ LONG_PTR style, exStyle;
+ struct DlgBoxSubclassData *buf;
+
+ exStyle = GetWindowLongPtr(msg->hwnd, GWL_EXSTYLE);
+ if (exStyle&WS_EX_CONTEXTHELP)
+ break;
+ style = GetWindowLongPtr(msg->hwnd, GWL_STYLE);
+
+ EnterCriticalSection(&csDlgBoxSubclass);
+ buf = (struct DlgBoxSubclassData*)mir_realloc(dlgBoxSubclass, sizeof(struct DlgBoxSubclassData)*(dlgBoxSubclassCount + 1));
+ if (buf == NULL) {
+ LeaveCriticalSection(&csDlgBoxSubclass);
+ break;
+ }
+ dlgBoxSubclass = buf;
+ dlgBoxSubclass[dlgBoxSubclassCount].hwndDlg = msg->hwnd;
+ dlgBoxSubclass[dlgBoxSubclassCount].pfnOldWndProc = (WNDPROC)SetWindowLongPtr(msg->hwnd, GWLP_WNDPROC, (LONG_PTR)DialogBoxSubclassProc);
+ dlgBoxSubclass[dlgBoxSubclassCount].flags = 0;
+
+ // WS_EX_CONTEXTHELP cannot be used in conjunction WS_MINIMIZEBOX or WS_MAXIMIZEBOX
+ // solution: switch off WS_MINIMIZEBOX or WS_MAXIMIZEBOX when only one of them is present
+ if (!(style & WS_MINIMIZEBOX) || !(style & WS_MAXIMIZEBOX)) {
+ if (style & WS_MINIMIZEBOX)
+ dlgBoxSubclass[dlgBoxSubclassCount].flags |= DBSDF_MINIMIZABLE;
+ if (style & WS_MAXIMIZEBOX)
+ dlgBoxSubclass[dlgBoxSubclassCount].flags |= DBSDF_MAXIMIZABLE;
+ SetWindowLongPtr(msg->hwnd, GWL_STYLE, style&(~(WS_MINIMIZEBOX | WS_MAXIMIZEBOX)));
+ SetWindowLongPtr(msg->hwnd, GWL_EXSTYLE, (LONG_PTR)exStyle | WS_EX_CONTEXTHELP);
+ }
+ dlgBoxSubclassCount++;
+ LeaveCriticalSection(&csDlgBoxSubclass);
+ }
+ {
+ HMENU hMenu;
+ hMenu = GetSystemMenu(msg->hwnd, FALSE);
+ if (hMenu != NULL && AppendMenu(hMenu, MF_SEPARATOR, SC_CONTEXTHELP_SEPARATOR, NULL)) {
+ AppendMenu(hMenu, MF_STRING, SC_CONTEXTHELP, TranslateT("&What's this?"));
+ AppendMenu(hMenu, MF_STRING, SC_CONTEXTHELP_DIALOG, TranslateT("&What's this Dialog?"));
+ }
+ }
+ {
+ TCHAR text[64];
+ mir_sntprintf(text, sizeof(text), _T("hooked window 0x%X for context help\n"), msg->hwnd);
+ OutputDebugString(text);
+ }
+ break;
+ }
+ }
+ return CallNextHookEx(hMessageHook, code, wParam, lParam);
+}
+
+static INT_PTR ServiceShowHelp(WPARAM wParam, LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(lParam);
+ if (!IsWindow((HWND)wParam))
+ return 1;
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+ if (hwndHelpDlg == NULL) {
+ hwndHelpDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_HELP), NULL, HelpDlgProc);
+ if (hwndHelpDlg == NULL)
+ return 2;
+ }
+ PostMessage(hwndHelpDlg, M_CHANGEHELPCONTROL, 0, (LPARAM)wParam);
+
+ return 0;
+}
+
+static INT_PTR ServiceSetContextState(WPARAM wParam, LPARAM lParam)
+{
+ int i;
+ LONG flags;
+ EnterCriticalSection(&csDlgBoxSubclass);
+ for (i = 0; i < dlgBoxSubclassCount; i++)
+ if (IsRealChild(dlgBoxSubclass[i].hwndDlg, (HWND)wParam))
+ break;
+ if (i == dlgBoxSubclassCount) {
+ LeaveCriticalSection(&csDlgBoxSubclass);
+
+ return 2;
+ }
+ LeaveCriticalSection(&csDlgBoxSubclass);
+ flags = (lParam&HCSF_CONTEXTMENU) ? PROPF_MENUFORCED : PROPF_MENUDISABLED;
+ flags |= (lParam&HCSF_AUTOTIP) ? PROPF_AUTOTIPFORCED : PROPF_AUTOTIPDISABLED;
+
+ return !SetProp((HWND)wParam, PROP_CONTEXTSTATE, (HANDLE)flags);
+}
+
+int InstallDialogBoxHook(void)
+{
+ InitializeCriticalSection(&csDlgBoxSubclass);
+ hServiceShowHelp = CreateServiceFunction(MS_HELP_SHOWHELP, ServiceShowHelp);
+ hServiceSetContext = CreateServiceFunction(MS_HELP_SETCONTEXTSTATE, ServiceSetContextState);
+ hMessageHook = SetWindowsHookEx(WH_CALLWNDPROC, HelpSendMessageHookProc, NULL, GetCurrentThreadId()); // main thread
+ hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardInputHookProc, NULL, GetCurrentThreadId()); // main thread
+
+ return hMessageHook == NULL;
+}
+
+int RemoveDialogBoxHook(void)
+{
+ DestroyServiceFunction(hServiceShowHelp); // does NULL check
+ DestroyServiceFunction(hServiceSetContext); // does NULL check
+ UnhookWindowsHookEx(hMessageHook);
+ if (hKeyboardHook)
+ UnhookWindowsHookEx(hMessageHook);
+ if (hEatNextMouseHook)
+ UnhookWindowsHookEx(hEatNextMouseHook);
+ DeleteCriticalSection(&csDlgBoxSubclass);
+ for (int i = 0; i<dlgBoxSubclassCount; i++)
+ SetWindowLongPtr(dlgBoxSubclass[i].hwndDlg, GWLP_WNDPROC, (LONG_PTR)dlgBoxSubclass[i].pfnOldWndProc);
+ mir_free(dlgBoxSubclass); // does NULL check
+
+ return 0;
+}
diff --git a/plugins/ContextHelp/src/help.h b/plugins/ContextHelp/src/help.h new file mode 100644 index 0000000000..cd6e095e7e --- /dev/null +++ b/plugins/ContextHelp/src/help.h @@ -0,0 +1,146 @@ +/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 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 (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef __CONTEXTHELP_HELP_H__
+#define __CONTEXTHELP_HELP_H__
+
+// dlgboxsubclass.c
+int InstallDialogBoxHook(void);
+int RemoveDialogBoxHook(void);
+
+// utils.c
+#define CTLTYPE_UNKNOWN 0
+#define CTLTYPE_DIALOG 1
+#define CTLTYPE_BUTTON 2
+#define CTLTYPE_CHECKBOX 3
+#define CTLTYPE_RADIO 4
+#define CTLTYPE_TEXT 5
+#define CTLTYPE_IMAGE 6
+#define CTLTYPE_EDIT 7
+#define CTLTYPE_GROUP 8
+#define CTLTYPE_COMBO 9
+#define CTLTYPE_LIST 10
+#define CTLTYPE_SPINEDIT 11
+#define CTLTYPE_PROGRESS 12
+#define CTLTYPE_SLIDER 13
+#define CTLTYPE_LISTVIEW 14
+#define CTLTYPE_TREEVIEW 15
+#define CTLTYPE_DATETIME 16
+#define CTLTYPE_IP 17
+#define CTLTYPE_STATUSBAR 18
+#define CTLTYPE_HYPERLINK 19
+#define CTLTYPE_CLC 20
+#define CTLTYPE_SCROLL 21
+#define CTLTYPE_ANIMATION 22
+#define CTLTYPE_HOTKEY 23
+#define CTLTYPE_TABS 24
+#define CTLTYPE_COLOUR 25
+#define CTLTYPE_TOOLBAR 26
+#define CTLTYPE_SIZEGRIP 27
+extern const TCHAR *szControlTypeNames[];
+int GetControlType(HWND hwndCtl);
+HWND GetControlDialog(HWND hwndCtl);
+int GetControlTitle(HWND hwndCtl, TCHAR *pszTitle, int cchTitle);
+char *GetControlModuleName(HWND hwndCtl);
+int GetControlID(HWND hwndCtl);
+char *CreateDialogIdString(HWND hwndDlg);
+struct ResizableCharBuffer {
+ char *sz;
+ int iEnd, cbAlloced;
+};
+void AppendCharToCharBuffer(struct ResizableCharBuffer *rcb, char c);
+void AppendToCharBuffer(struct ResizableCharBuffer *rcb, const char *fmt, ...);
+
+// helpdlg.c
+#define M_CHANGEHELPCONTROL (WM_APP+0x100)
+#define M_HELPLOADED (WM_APP+0x101)
+#ifdef EDITOR
+#define M_SAVECOMPLETE (WM_APP+0x102)
+#endif
+#define M_LOADHELP (WM_APP+0x103)
+#define M_HELPLOADFAILED (WM_APP+0x104)
+#define M_CLIPBOARDCOPY (WM_APP+0x105)
+INT_PTR CALLBACK HelpDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK ShadowDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+// streaminout.c
+void StreamInHtml(HWND hwndEdit, const char *szHtml, UINT codepage, COLORREF clrBkgrnd);
+#ifdef EDITOR
+char *StreamOutHtml(HWND hwndEdit);
+#endif
+#define TEXTSIZE_BIG 18 // in half points
+#define TEXTSIZE_NORMAL 16
+#define TEXTSIZE_SMALL 13
+#ifndef EDITOR
+void FreeHyperlinkData(void);
+int IsHyperlink(LONG cpPos, LONG *pcpMin, LONG *pcpMax, char **ppszLink);
+#endif
+
+// datastore.c
+void InitDialogCache(void);
+void FreeDialogCache(void);
+#define GCHF_DONTLOAD 1
+int GetControlHelp(HWND hwndCtl, const char *pszDlgId, const char *pszModule, int ctrlId, TCHAR **ppszTitle, char **ppszText, int *pType, LCID *pLocaleID, UINT *pCodePage, BOOL *pIsRTL, DWORD flags);
+#ifdef EDITOR
+void SetControlHelp(const char *pszDlgId, const char *pszModule, int ctrlId, TCHAR *pszTitle, char *pszText, int type);
+void SaveDialogCache(void);
+#else
+char *CreateControlIdentifier(const char *pszDlgId, const char *pszModule, int ctrlId, HWND hwndCtl);
+#endif
+void RegisterFileChange(void);
+void CloseFileChange(void);
+
+// options.c
+void ReloadLangOptList(void);
+void InitOptions(void);
+void UninitOptions(void);
+
+// helppack.c
+void TrimStringSimple(char *str);
+void TrimString(char *str);
+BOOL IsEmpty(const char *str);
+typedef struct {
+ TCHAR szLanguage[64];
+ LCID Locale;
+ WORD codepage;
+ char szAuthors[1024];
+ char szAuthorEmail[128];
+ char szLastModifiedUsing[64];
+ char szPluginsIncluded[4080];
+ char szVersion[21];
+ char szFLName[128];
+ FILETIME ftFileDate;
+ TCHAR szFileName[MAX_PATH]; /* just the file name itself */
+ BYTE flags; /* see HPIF_* flags */
+} HELPPACK_INFO;
+#define HPF_ENABLED 0x01 // pack is enabled
+#define HPF_NOLOCALE 0x02 // pack has no valid locale
+#define HPF_DEFAULT 0x04 // pack is english default
+BOOL GetPackPath(TCHAR *pszPath, int nSize, BOOL fEnabledPacks, const TCHAR *pszFile);
+typedef INT_PTR(CALLBACK *ENUM_PACKS_CALLBACK)(HELPPACK_INFO *pack, WPARAM wParam, LPARAM lParam);
+BOOL EnumPacks(ENUM_PACKS_CALLBACK callback, const TCHAR *pszFilePattern, const char *pszFileVersionHeader, WPARAM wParam, LPARAM lParam);
+BOOL IsPluginIncluded(const HELPPACK_INFO *pack, char *pszFileBaseName);
+BOOL EnablePack(const HELPPACK_INFO *pack, const TCHAR *pszFilePattern);
+void CorrectPacks(const TCHAR *pszFilePattern, const TCHAR *pszDefaultFile, BOOL fDisableAll);
+
+/* update.c
+INT_PTR ServiceShowLangDialog(WPARAM wParam, LPARAM lPARAM);
+void InitUpdate(void);
+void UninitUpdate(void);*/
+
+#endif // __CONTEXTHELP_HELP_H__
\ No newline at end of file diff --git a/plugins/ContextHelp/src/helpdlg.cpp b/plugins/ContextHelp/src/helpdlg.cpp new file mode 100644 index 0000000000..b035777a0f --- /dev/null +++ b/plugins/ContextHelp/src/helpdlg.cpp @@ -0,0 +1,735 @@ +/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 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 (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "stdafx.h"
+
+
+#include <richedit.h>
+
+extern HINSTANCE hInst;
+HWND hwndHelpDlg;
+
+static int HelpDialogResize(HWND hwndDlg, LPARAM lParam, UTILRESIZECONTROL *urc)
+{
+ UNREFERENCED_PARAMETER(hwndDlg);
+ UNREFERENCED_PARAMETER(lParam);
+ switch (urc->wId) {
+ case IDC_CTLTEXT:
+#ifdef EDITOR
+ case IDC_DLGID:
+ case IDC_MODULE:
+#endif
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP;
+ case IDC_TEXT:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT;
+ }
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+}
+
+#ifndef EDITOR
+INT_PTR CALLBACK ShadowDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(wParam);
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ BOOL(WINAPI *pfnSetLayeredWindowAttributes)(HWND, COLORREF, BYTE, DWORD);
+ *(FARPROC*)&pfnSetLayeredWindowAttributes = GetProcAddress(GetModuleHandleA("USER32"), "SetLayeredWindowAttributes");
+ if (!pfnSetLayeredWindowAttributes) {
+ *(HANDLE*)lParam = NULL; // hwndShadowDlg reset
+ DestroyWindow(hwndDlg);
+ return FALSE;
+ }
+ EnableWindow(hwndDlg, FALSE);
+ SetWindowLongPtr(hwndDlg, GWL_EXSTYLE, GetWindowLongPtr(hwndDlg, GWL_EXSTYLE) | WS_EX_LAYERED);
+ pfnSetLayeredWindowAttributes(hwndDlg, RGB(0, 0, 0), 96, LWA_ALPHA);
+ return FALSE;
+ }
+ case WM_CTLCOLORDLG:
+ return (INT_PTR)GetSysColorBrush(COLOR_WINDOWFRAME);
+ }
+ return FALSE;
+}
+
+// in client coordinates
+int GetCharRangeRect(HWND hwndEdit, LONG *cpMin, LONG cpMax, RECT *rcRange)
+{
+ LONG cpLineBreak;
+
+ LONG nLine, nLinePrev;
+ if (*cpMin>cpMax)
+ return 1;
+ nLine = SendMessage(hwndEdit, EM_EXLINEFROMCHAR, 0, *cpMin);
+ for (cpLineBreak = *cpMin + 1; cpLineBreak <= cpMax; cpLineBreak++) {
+ nLinePrev = nLine;
+ nLine = SendMessage(hwndEdit, EM_EXLINEFROMCHAR, 0, cpLineBreak);
+ if (nLine != nLinePrev)
+ break;
+ }
+ cpMax = cpLineBreak - 1;
+
+ POINTL pt;
+ if (SendMessage(hwndEdit, EM_SETTYPOGRAPHYOPTIONS, 0, 0)) { // test for richedit v3.0
+ SendMessage(hwndEdit, EM_POSFROMCHAR, (WPARAM)&pt, *cpMin);
+ rcRange->left = pt.x;
+ rcRange->top = pt.y;
+ SendMessage(hwndEdit, EM_POSFROMCHAR, (WPARAM)&pt, cpMax);
+ rcRange->right = pt.x;
+ rcRange->bottom = pt.y;
+ }
+ else {
+ DWORD pos;
+ pos = SendMessage(hwndEdit, EM_POSFROMCHAR, (WPARAM)*cpMin, 0);
+ POINTSTOPOINT(pt, MAKEPOINTS(pos));
+ rcRange->left = pt.x;
+ rcRange->top = pt.y;
+ pos = SendMessage(hwndEdit, EM_POSFROMCHAR, (WPARAM)cpMax, 0);
+ POINTSTOPOINT(pt, MAKEPOINTS(pos));
+ rcRange->right = pt.x;
+ rcRange->bottom = pt.y;
+ }
+
+ FORMATRANGE fr;
+ ZeroMemory(&fr, sizeof(fr));
+ fr.chrg.cpMin = *cpMin;
+ fr.chrg.cpMax = cpMax;
+ fr.hdc = fr.hdcTarget = GetDC(hwndEdit);
+ if (fr.hdc == NULL)
+ return 1;
+ SendMessage(hwndEdit, EM_FORMATRANGE, 0, (LPARAM)&fr);
+ PostMessage(hwndEdit, EM_FORMATRANGE, 0, 0); // clear memory
+ rcRange->bottom += MulDiv(fr.rc.bottom, GetDeviceCaps(fr.hdc, LOGPIXELSY), 1440); // twips to pixels
+ ReleaseDC(hwndEdit, fr.hdc);
+
+ *cpMin = cpLineBreak;
+ return 0;
+}
+
+static HCURSOR hHandCursor;
+static LRESULT CALLBACK HelpSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_KEYDOWN:
+ if (GetKeyState(VK_CONTROL) & 0x8000 && wParam == 'C') {
+ SendMessage(GetParent(hwnd), M_CLIPBOARDCOPY, 0, 0);
+ return 0;
+ }
+ break;
+ case WM_LBUTTONDBLCLK:
+ DestroyWindow(GetParent(hwnd));
+ return 0;
+ case WM_CONTEXTMENU:
+ return DefWindowProc(hwnd, msg, wParam, lParam); // redirect to parent
+ case WM_SETCURSOR: // not available via EN_MSGFILTER
+ if (GetDlgCtrlID((HWND)wParam) == IDC_TEXT) {
+ POINT pt;
+ DWORD pos;
+ CHARRANGE rng;
+ pos = GetMessagePos();
+ POINTSTOPOINT(pt, MAKEPOINTS(pos));
+ ScreenToClient((HWND)wParam, &pt);
+ pos = SendMessage((HWND)wParam, EM_CHARFROMPOS, 0, (WPARAM)&pt);
+ if (IsHyperlink(pos, &rng.cpMin, &rng.cpMax, NULL)) {
+ RECT rc;
+ while (!GetCharRangeRect((HWND)wParam, &rng.cpMin, rng.cpMax, &rc))
+ if (PtInRect(&rc, pt)) {
+ SetCursor(hHandCursor);
+ return TRUE;
+ }
+ }
+ }
+ break;
+ }
+ return CallWindowProc((WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA), hwnd, msg, wParam, lParam);
+}
+#endif // !defined EDITOR
+
+INT_PTR CALLBACK HelpDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HWND hwndCtl = (HWND)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ static LCID locale;
+#ifndef EDITOR
+ static HWND hwndShadowDlg;
+ static HWND hwndToolTip;
+#endif
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ hwndHelpDlg = hwndDlg;
+#ifdef EDITOR
+ TranslateDialogDefault(hwndDlg);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_DLGID), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_DLGID), GWL_STYLE) | SS_ENDELLIPSIS);
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
+ { RECT rcDlg, rcWork;
+ if (SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, FALSE) && GetWindowRect(hwndDlg, &rcDlg))
+ SetWindowPos(hwndDlg, 0, rcDlg.left, rcWork.bottom - rcDlg.bottom + rcDlg.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
+ }
+#else
+ {
+ RECT rc, rcBuf;
+ SendDlgItemMessage(hwndDlg, IDC_CTLTEXT, EM_GETRECT, 0, (LPARAM)&rcBuf);
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_GETRECT, 0, (LPARAM)&rc);
+ rc.left = rcBuf.left; // sync richedit offset with edit
+ rc.right = rcBuf.right;
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETRECTNP, 0, (LPARAM)&rc);
+ }
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_CTLTEXT), GWLP_USERDATA, SetWindowLong(GetDlgItem(hwndDlg, IDC_CTLTEXT), GWLP_WNDPROC, (LONG)HelpSubclassProc));
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_CARETSUCKER), GWLP_USERDATA, SetWindowLong(GetDlgItem(hwndDlg, IDC_CARETSUCKER), GWLP_WNDPROC, (LONG)HelpSubclassProc));
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETEVENTMASK, 0, ENM_KEYEVENTS | ENM_MOUSEEVENTS | ENM_REQUESTRESIZE);
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETBKGNDCOLOR, 0, GetSysColor(COLOR_INFOBK));
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETEDITSTYLE, SES_EXTENDBACKCOLOR, SES_EXTENDBACKCOLOR);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_TEXT), GWLP_USERDATA, SetWindowLong(GetDlgItem(hwndDlg, IDC_TEXT), GWLP_WNDPROC, (LONG)HelpSubclassProc));
+ hwndShadowDlg = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_SHADOW), hwndDlg, ShadowDlgProc, (LPARAM)&hwndShadowDlg);
+ hwndToolTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_ALWAYSTIP | TTS_NOPREFIX, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwndDlg, NULL, hInst, NULL);
+ if (hwndToolTip != NULL) {
+ SetWindowPos(hwndToolTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+ SendMessage(hwndToolTip, TTM_SETTIPBKCOLOR, GetSysColor(COLOR_WINDOW), 0); // yelleow on yellow looks silly
+ SendMessage(hwndToolTip, TTM_SETDELAYTIME, TTDT_AUTOMATIC, GetDoubleClickTime() * 3);
+ }
+ hHandCursor = (HCURSOR)LoadImage(NULL, IDC_HAND, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+ if (hHandCursor == NULL) // use fallback out of miranda32.exe
+ hHandCursor = (HCURSOR)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(214), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+#endif
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETLANGOPTIONS, 0, SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_GETLANGOPTIONS, 0, 0) | IMF_UIFONTS);
+ SetWindowPos(hwndDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+ return TRUE;
+ case WM_SIZE:
+ {
+ /* UTILRESIZEDIALOG urd;
+ ZeroMemory(&urd,sizeof(urd));
+ urd.cbSize=sizeof(urd);
+ urd.hInstance=hInst;
+ urd.hwndDlg=hwndDlg;
+ urd.lpTemplate=MAKEINTRESOURCEA(IDD_HELP);
+ urd.pfnResizer=HelpDialogResize;
+ CallService(MS_UTILS_RESIZEDIALOG,0,(LPARAM)&urd); */
+ Utils_ResizeDialog(hwndDlg, hInst, MAKEINTRESOURCEA(IDD_HELP), HelpDialogResize);
+ InvalidateRect(hwndDlg, NULL, TRUE);
+#ifdef EDITOR
+ break;
+#endif
+ }
+#ifndef EDITOR
+ case WM_MOVE:
+ if (IsWindow(hwndShadowDlg)) {
+ RECT rc;
+ HRGN hRgnShadow, hRgnDlg;
+ if (!GetWindowRect(hwndDlg, &rc))
+ break;
+ hRgnShadow = CreateRectRgnIndirect(&rc);
+ if (hRgnShadow == NULL)
+ break;
+ OffsetRgn(hRgnShadow, 5, 5);
+ hRgnDlg = CreateRectRgnIndirect(&rc);
+ if (hRgnDlg == NULL)
+ break;
+ if (CombineRgn(hRgnShadow, hRgnShadow, hRgnDlg, RGN_DIFF) == ERROR) {
+ DeleteObject(hRgnShadow);
+ DeleteObject(hRgnDlg);
+ break;
+ }
+ DeleteObject(hRgnDlg);
+ OffsetRgn(hRgnShadow, -rc.left - 5, -rc.top - 5);
+ SetWindowRgn(hwndShadowDlg, hRgnShadow, FALSE); // system gets ownership of hRgnShadow
+ SetWindowPos(hwndShadowDlg, HWND_TOPMOST, rc.left + 5, rc.top + 5, rc.right - rc.left, rc.bottom - rc.top, SWP_SHOWWINDOW);
+ SetWindowPos(hwndDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ }
+ break;
+ case WM_KEYDOWN:
+ if (GetKeyState(VK_CONTROL) & 0x8000 && wParam == 'C') {
+ SendMessage(hwndDlg, M_CLIPBOARDCOPY, 0, 0);
+ return TRUE;
+ }
+ break;
+ case WM_CONTEXTMENU:
+ {
+ HMENU hMenu;
+ POINT pt;
+ POINTSTOPOINT(pt, MAKEPOINTS(lParam));
+ hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, MF_STRING, WM_COPY, TranslateT("&Copy"));
+ if (TrackPopupMenuEx(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_HORPOSANIMATION | TPM_VERPOSANIMATION | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, pt.x, pt.y, hwndDlg, NULL))
+ SendMessage(hwndDlg, M_CLIPBOARDCOPY, 0, 0);
+ DestroyMenu(hMenu);
+ }
+ return 0;
+ case WM_LBUTTONDBLCLK:
+ DestroyWindow(hwndDlg);
+ return TRUE;
+ case M_CLIPBOARDCOPY:
+ {
+ HWND hwnd = GetFocus();
+ if (hwnd == GetDlgItem(hwndDlg, IDC_CTLTEXT) || hwnd == GetDlgItem(hwndDlg, IDC_TEXT)) {
+ CHARRANGE sel;
+ ZeroMemory(&sel, sizeof(sel));
+ SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel.cpMin, (LPARAM)&sel.cpMax);
+ if (sel.cpMin != sel.cpMax) {
+ SendMessage(hwnd, WM_COPY, 0, 0);
+ return TRUE;
+ }
+ }
+ }
+ if (OpenClipboard(hwndDlg)) {
+ HGLOBAL hglb;
+ int cch, len;
+ EmptyClipboard();
+ hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(LCID));
+ if (hglb != NULL) {
+ LCID *plocale = (LCID*)GlobalLock(hglb); // look at !!
+ if (plocale != NULL) {
+ *plocale = locale;
+ GlobalUnlock(hglb);
+ if (!SetClipboardData(CF_LOCALE, hglb))
+ GlobalFree(hglb); // shell takes ownership
+ }
+ else
+ GlobalFree(hglb);
+ }
+ cch = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_CTLTEXT)) + GetWindowTextLength(GetDlgItem(hwndDlg, IDC_TEXT)) + 3;
+ hglb = GlobalAlloc(GMEM_MOVEABLE, (cch + 1)*sizeof(TCHAR));
+ if (hglb != NULL) {
+ TCHAR *pszText = (TCHAR*)GlobalLock(hglb);
+ if (pszText != NULL) {
+ if (!GetWindowText(GetDlgItem(hwndDlg, IDC_CTLTEXT), pszText, cch - 2)) pszText[0] = _T('\0');
+ len = lstrlen(pszText);
+ if (GetWindowText(GetDlgItem(hwndDlg, IDC_TEXT), pszText + len + 2, cch - 2 - len) && len) {
+ pszText[len] = _T('\r');
+ pszText[len + 1] = _T('\n');
+ }
+ GlobalUnlock(hglb);
+ if (!SetClipboardData(CF_UNICODETEXT, hglb))
+ GlobalFree(hglb); // shell takes ownership
+ }
+ else GlobalFree(hglb);
+ }
+ CloseClipboard();
+ }
+ return TRUE;
+ case WM_CTLCOLORDLG:
+ case WM_CTLCOLORSTATIC:
+ SetTextColor((HDC)wParam, GetSysColor(COLOR_INFOTEXT));
+ SetBkColor((HDC)wParam, GetSysColor(COLOR_INFOBK));
+ return (BOOL)GetSysColorBrush(COLOR_INFOBK);
+ case WM_ACTIVATE:
+ if (LOWORD(wParam) != WA_INACTIVE)
+ break;
+ if (GetParent((HWND)lParam) == hwndDlg)
+ break;
+ // fall through
+ case WM_SYSCOLORCHANGE:
+ case WM_ACTIVATEAPP:
+ PostMessage(hwndDlg, WM_CLOSE, 0, 0); // no DestroyWindow() here! would cause recursion
+ break;
+#endif // !defined EDITOR
+ case M_CHANGEHELPCONTROL:
+ if (hwndCtl == (HWND)lParam)
+ break;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
+ hwndCtl = (HWND)lParam;
+ SetDlgItemText(hwndDlg, IDC_CTLTEXT, NULL);
+#ifdef EDITOR
+ {
+ TCHAR text[1024];
+ char *szModule, *szDlgId;
+
+ GetControlTitle(hwndCtl, text, sizeof(text));
+ SetDlgItemText(hwndDlg, IDC_CTLTEXT, text);
+ mir_sntprintf(text, sizeof(text), TranslateT("Control ID: %d"), GetControlID(hwndCtl));
+ SetDlgItemText(hwndDlg, IDC_CTLID, text);
+
+ szDlgId = CreateDialogIdString(GetControlDialog(hwndCtl));
+ mir_sntprintf(text, sizeof(text), TranslateT("Dialog ID: %hs"), (szDlgId != NULL) ? szDlgId : Translate("Unknown"));
+ mir_free(szDlgId); // does NULL check
+ SetDlgItemText(hwndDlg, IDC_DLGID, text);
+
+ mir_sntprintf(text, sizeof(text), TranslateT("Type: %s"), TranslateTS(szControlTypeNames[GetControlType(hwndCtl)]));
+ SetDlgItemText(hwndDlg, IDC_CTLTYPE, text);
+
+ szModule = GetControlModuleName(hwndCtl);
+ mir_sntprintf(text, sizeof(text), TranslateT("Module: %hs"), szModule ? szModule : Translate("Unknown"));
+ mir_free(szModule); // does NULL check
+ SetDlgItemText(hwndDlg, IDC_MODULE, text);
+ }
+#endif // defined EDITOR
+ SetDlgItemText(hwndDlg, IDC_TEXT, NULL);
+ SendMessage(hwndDlg, M_LOADHELP, 0, 0);
+#ifdef EDITOR
+ ShowWindow(hwndDlg, SW_SHOWNORMAL);
+#else
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_REQUESTRESIZE, 0, 0);
+ return FALSE;
+#endif
+ return TRUE;
+ case M_HELPLOADFAILED:
+ if (hwndCtl != (HWND)lParam)
+ break;
+#ifdef EDITOR
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXT), TRUE);
+ {
+ TCHAR text[2024];
+ GetControlTitle(hwndCtl, text, sizeof(text));
+ SetDlgItemText(hwndDlg, IDC_CTLTEXT, text);
+ }
+#else
+ SetDlgItemText(hwndDlg, IDC_CTLTEXT, TranslateT("No Help Pack installed!"));
+#endif
+ SetDlgItemText(hwndDlg, IDC_TEXT, NULL);
+ MessageBeep(MB_ICONERROR);
+ break;
+#ifdef EDITOR
+ case M_SAVECOMPLETE:
+#endif
+ case M_HELPLOADED:
+ if (hwndCtl != (HWND)lParam)
+ break;
+ case M_LOADHELP:
+ {
+ TCHAR *szTitle;
+ char *szText;
+ char *szDlgId, *szModule;
+ UINT codepage;
+ BOOL isRTL;
+ int id, loading;
+#ifdef EDITOR
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXT), TRUE);
+#endif
+ szDlgId = CreateDialogIdString(GetControlDialog(hwndCtl));
+ szModule = GetControlModuleName(hwndCtl);
+ id = GetControlID(hwndCtl);
+#ifndef EDITOR
+ // show id string instead of help text when 'ctrl' key pressed
+ if (msg == M_LOADHELP && GetAsyncKeyState(VK_CONTROL) & 0x8000) {
+ char *buf;
+ HWND hwnd;
+ buf = CreateControlIdentifier(szDlgId ? szDlgId : "unknown", szModule ? szModule : "unknown", id, hwndCtl);
+ hwnd = GetDlgItem(hwndDlg, IDC_CTLTEXT);
+ SetWindowTextA(hwnd, buf); // accepts NULL
+ SetDlgItemText(hwndDlg, IDC_TEXT, NULL);
+ mir_free(buf); // does NULL check
+ mir_free(szDlgId); // does NULL check
+ mir_free(szModule); // does NULL check
+ break;
+ }
+#endif
+ if (szDlgId == NULL || szModule == NULL) {
+ SetDlgItemText(hwndDlg, IDC_TEXT, NULL);
+#ifndef EDITOR
+ SetDlgItemText(hwndDlg, IDC_CTLTEXT, TranslateT("No help available for this item."));
+#endif
+ mir_free(szDlgId); // does NULL check
+ mir_free(szModule); // does NULL check
+ break;
+ }
+
+ loading = GetControlHelp(hwndCtl, szDlgId, szModule, id, &szTitle, &szText, NULL, &locale, &codepage, &isRTL, (msg == M_HELPLOADED) ? GCHF_DONTLOAD : 0);
+ if (!loading) {
+ if (szText)
+ StreamInHtml(GetDlgItem(hwndDlg, IDC_TEXT), szText, codepage, RGB(255, 0, 0));
+ else
+ SetDlgItemText(hwndDlg, IDC_TEXT, NULL);
+ if (szTitle) {
+ TCHAR buf[128];
+ RECT rc;
+ HFONT hFontPrev;
+ DWORD exStyle;
+ HWND hwndCtlText;
+ HDC hdc;
+ hwndCtlText = GetDlgItem(hwndDlg, IDC_CTLTEXT);
+ exStyle = GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_CTLTEXT), GWL_EXSTYLE);
+ hdc = GetDC(hwndCtlText);
+ {
+ DWORD(WINAPI *pfnGetLayout)(HDC); // obey right-to-left languages
+ *(FARPROC*)&pfnGetLayout = GetProcAddress(GetModuleHandleA("GDI32"), "GetLayout");
+ if (pfnGetLayout) isRTL = (isRTL && !pfnGetLayout(hdc));
+ if (isRTL)
+ exStyle |= WS_EX_RTLREADING | WS_EX_RIGHT;
+ else
+ exStyle &= ~(WS_EX_RTLREADING | WS_EX_RIGHT);
+ }
+ mir_sntprintf(buf, sizeof(buf) - 4, _T("%s"), szTitle);
+ if (hdc != NULL && hwndCtlText != NULL) {
+ SendMessage(hwndCtlText, EM_GETRECT, 0, (LPARAM)&rc);
+ hFontPrev = (HFONT)SelectObject(hdc, (HFONT)SendMessage(hwndCtlText, WM_GETFONT, 0, 0)); // look at !!
+ // doesn't actually draw the string due to DT_CALCRECT
+ DrawTextEx(hdc, buf, -1, &rc, DT_MODIFYSTRING | DT_CALCRECT | DT_EDITCONTROL | DT_END_ELLIPSIS | DT_INTERNAL | (isRTL ? (DT_RTLREADING | DT_RIGHT) : DT_LEFT) | DT_NOPREFIX | DT_SINGLELINE, NULL);
+ SelectObject(hdc, hFontPrev);
+ ReleaseDC(hwndCtlText, hdc);
+ }
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_CTLTEXT), GWL_EXSTYLE, exStyle);
+ SetWindowText(hwndCtlText, buf);
+ }
+ else
+ SetDlgItemText(hwndDlg, IDC_CTLTEXT, NULL);
+ }
+ else {
+ if (msg == M_HELPLOADED) {
+ SetDlgItemText(hwndDlg, IDC_TEXT, NULL);
+#ifndef EDITOR
+ SetDlgItemText(hwndDlg, IDC_CTLTEXT, TranslateT("No help available for this item."));
+#endif
+ }
+ else {
+#ifdef EDITOR
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXT), FALSE);
+ SetDlgItemText(hwndDlg, IDC_TEXT, TranslateT("Loading..."));
+#else
+ SetDlgItemText(hwndDlg, IDC_CTLTEXT, TranslateT("Loading..."));
+ SetDlgItemText(hwndDlg, IDC_TEXT, NULL);
+#endif
+ }
+ }
+ mir_free(szDlgId);
+ mir_free(szModule);
+ break;
+ }
+ case WM_NOTIFY:
+ switch (((NMHDR*)lParam)->idFrom) {
+ case IDC_TEXT:
+ switch (((NMHDR*)lParam)->code) {
+#ifdef EDITOR
+ case EN_MSGFILTER:
+ switch (((MSGFILTER*)lParam)->msg) {
+ case WM_CHAR:
+ switch (((MSGFILTER*)lParam)->wParam) {
+ case 'B' - 'A' + 1:
+ case 'I' - 'A' + 1:
+ case 'U' - 'A' + 1:
+ case 'H' - 'A' + 1:
+ case 'L' - 'A' + 1:
+ case 'S' - 'A' + 1:
+ case 'G' - 'A' + 1:
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ break;
+ case WM_KEYDOWN:
+ {
+ CHARFORMAT cf;
+ int changes = 0;
+
+ ZeroMemory(&cf, sizeof(cf));
+ if (!(GetKeyState(VK_CONTROL) & 0x8000))
+ break;
+ cf.cbSize = sizeof(cf);
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_GETCHARFORMAT, TRUE, (LPARAM)&cf);
+ switch (((MSGFILTER*)lParam)->wParam) {
+ case 'B':
+ cf.dwEffects ^= CFE_BOLD;
+ cf.dwMask = CFM_BOLD;
+ changes = 1;
+ break;
+ case 'I':
+ cf.dwEffects ^= CFE_ITALIC;
+ cf.dwMask = CFM_ITALIC;
+ changes = 1;
+ break;
+ case 'U':
+ cf.dwEffects ^= CFE_UNDERLINE;
+ cf.dwMask = CFM_UNDERLINE;
+ changes = 1;
+ break;
+ case 'L':
+ {
+ CHOOSECOLOR cc;
+ COLORREF custCol[16];
+ ZeroMemory(&custCol, sizeof(custCol));
+ ZeroMemory(&cc, sizeof(cc));
+ cc.lStructSize = sizeof(cc);
+ cc.hwndOwner = hwndDlg;
+ cc.lpCustColors = custCol;
+ cc.rgbResult = cf.crTextColor;
+ cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT;
+ if (!ChooseColor(&cc))
+ break;
+ cf.crTextColor = 0;
+ cf.dwEffects = 0;
+ if (cc.rgbResult)
+ cf.crTextColor = cc.rgbResult;
+ else cf.dwEffects = CFE_AUTOCOLOR;
+ cf.dwMask = CFM_COLOR;
+ changes = 1;
+ break;
+ }
+ case 'H':
+ cf.dwEffects ^= CFE_STRIKEOUT;
+ cf.dwMask = CFM_STRIKEOUT;
+ changes = 1;
+ break;
+ case VK_OEM_PLUS:
+ cf.yHeight = ((GetKeyState(VK_SHIFT) & 0x8000) ? TEXTSIZE_BIG : TEXTSIZE_NORMAL) * 10;
+ cf.dwMask = CFM_SIZE;
+ changes = 1;
+ break;
+ case VK_OEM_MINUS:
+ cf.yHeight = TEXTSIZE_SMALL * 10;
+ cf.dwMask = CFM_SIZE;
+ changes = 1;
+ break;
+ case 'S':
+ {
+ TCHAR szTitle[1024];
+ char *szText, *szDlgId, *szModule;
+ if (!GetDlgItemText(hwndDlg, IDC_CTLTEXT, szTitle, sizeof(szTitle)))
+ break;
+ szDlgId = CreateDialogIdString(GetControlDialog(hwndCtl));
+ if (szDlgId == NULL)
+ break;
+ szText = StreamOutHtml(GetDlgItem(hwndDlg, IDC_TEXT));
+ szModule = GetControlModuleName(hwndCtl);
+ if (szModule == NULL) {
+ mir_free(szDlgId);
+ break;
+ }
+ SetControlHelp(szDlgId, szModule, GetControlID(hwndCtl), szTitle, szText, GetControlType(hwndCtl));
+ mir_free(szText);
+ mir_free(szDlgId);
+ mir_free(szModule);
+ SaveDialogCache();
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ case 'G':
+ SendMessage(hwndDlg, M_LOADHELP, 0, 0);
+ SetWindowLong(hwndDlg, DWL_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ if (changes) {
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf);
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ break;
+ }
+ }
+ break;
+#else // defined EDITOR
+ case EN_MSGFILTER:
+ {
+ MSGFILTER *msgf = (MSGFILTER*)lParam;
+ switch (msgf->msg) {
+ case WM_LBUTTONUP:
+ {
+ POINT pt;
+ DWORD pos;
+ CHARRANGE sel;
+ char *pszLink;
+ HWND hwndEdit = msgf->nmhdr.hwndFrom;
+
+ ZeroMemory(&sel, sizeof(sel));
+ SendMessage(msgf->nmhdr.hwndFrom, EM_EXGETSEL, 0, (LPARAM)&sel);
+ if (sel.cpMin != sel.cpMax)
+ break;
+ POINTSTOPOINT(pt, MAKEPOINTS(msgf->lParam));
+ pos = SendMessage(hwndEdit, EM_CHARFROMPOS, 0, (WPARAM)&pt);
+ if (IsHyperlink(pos, NULL, NULL, &pszLink)) {
+ Utils_OpenUrl(pszLink); // pszLink is MBCS string in CP_ACP
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ }
+ break;
+ case WM_MOUSEMOVE: // register hyperlink tooltips when current
+ if (hwndToolTip != NULL) {
+ POINTL pt;
+ DWORD pos;
+ CHARRANGE rng;
+ char *pszLink;
+
+ POINTSTOPOINT(pt, MAKEPOINTS(msgf->lParam));
+ pos = SendMessage(msgf->nmhdr.hwndFrom, EM_CHARFROMPOS, 0, (WPARAM)&pt);
+ if (IsHyperlink(pos, &rng.cpMin, &rng.cpMax, &pszLink)) { // pszLink is MBCS in CP_ACP
+ TTTOOLINFOA ti = { 0 };
+ ti.cbSize = sizeof(ti);
+ ti.hwnd = msgf->nmhdr.hwndFrom;
+ ti.uId = (UINT)rng.cpMin;
+ if (!SendMessage(hwndToolTip, TTM_GETTOOLINFOA, 0, (LPARAM)&ti)) {
+ LONG cpRectMin;
+ ti.uFlags = TTF_SUBCLASS;
+ ti.lpszText = pszLink;
+ cpRectMin = rng.cpMin;
+ while (!GetCharRangeRect(ti.hwnd, &rng.cpMin, rng.cpMax, &ti.rect)) {
+ ti.uId = (UINT)cpRectMin;
+ SendMessage(hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
+ cpRectMin = rng.cpMin;
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+ case EN_REQUESTRESIZE:
+ {
+ RECT rcDlg, rcEdit, rcCtl, rcNew;
+ REQRESIZE *rr = (REQRESIZE*)lParam;
+ POINT ptScreenBottomRight;
+
+ if (!GetWindowRect(hwndDlg, &rcDlg))
+ break;
+ if (!GetWindowRect(hwndCtl, &rcCtl))
+ break;
+ if (!GetWindowRect(GetDlgItem(hwndDlg, IDC_TEXT), &rcEdit))
+ break;
+ rcNew.left = rcCtl.left + 30;
+ rcNew.top = rcCtl.bottom + 10;
+ rcNew.right = rcNew.left + (rr->rc.right - rr->rc.left) + (rcDlg.right - rcDlg.left) - (rcEdit.right - rcEdit.left) + (GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_TEXT), GWL_STYLE)&WS_VSCROLL ? GetSystemMetrics(SM_CXVSCROLL) : 0);
+ if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_TEXT)))
+ rcNew.bottom = rcNew.top + min(GetSystemMetrics(SM_CYSCREEN) / 5, (rr->rc.bottom - rr->rc.top) + (rcDlg.bottom - rcDlg.top) - (rcEdit.bottom - rcEdit.top));
+ else
+ rcNew.bottom = rcNew.top + min(GetSystemMetrics(SM_CYSCREEN) / 5, (rcDlg.bottom - rcDlg.top) - (rcEdit.bottom - rcEdit.top));
+ if (GetSystemMetrics(SM_CXVIRTUALSCREEN)) {
+ ptScreenBottomRight.x = GetSystemMetrics(SM_CXVIRTUALSCREEN) + GetSystemMetrics(SM_XVIRTUALSCREEN);
+ ptScreenBottomRight.y = GetSystemMetrics(SM_CYVIRTUALSCREEN) + GetSystemMetrics(SM_YVIRTUALSCREEN);
+ }
+ else {
+ ptScreenBottomRight.x = GetSystemMetrics(SM_CXSCREEN);
+ ptScreenBottomRight.y = GetSystemMetrics(SM_CYSCREEN);
+ }
+ if (rcNew.right >= ptScreenBottomRight.x)
+ OffsetRect(&rcNew, ptScreenBottomRight.x - rcNew.right, 0);
+ if (rcNew.bottom >= ptScreenBottomRight.y)
+ OffsetRect(&rcNew, 0, ptScreenBottomRight.y - rcNew.bottom);
+ SetWindowPos(hwndDlg, 0, rcNew.left, rcNew.top, rcNew.right - rcNew.left, rcNew.bottom - rcNew.top, SWP_NOZORDER);
+ ShowWindow(hwndDlg, SW_SHOWNORMAL);
+ break;
+ }
+#endif // defined EDITOR
+ }
+ }
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDCANCEL: // WM_CLOSE
+ DestroyWindow(hwndDlg);
+ return TRUE;
+ }
+ break;
+ case WM_DESTROY:
+#ifndef EDITOR
+ FreeHyperlinkData();
+ if (IsWindow(hwndShadowDlg))
+ DestroyWindow(hwndShadowDlg);
+ if (hwndToolTip != NULL)
+ DestroyWindow(hwndToolTip);
+#endif
+ hwndHelpDlg = NULL;
+ break;
+ }
+ return FALSE;
+}
diff --git a/plugins/ContextHelp/src/helppack.cpp b/plugins/ContextHelp/src/helppack.cpp new file mode 100644 index 0000000000..c8a21105aa --- /dev/null +++ b/plugins/ContextHelp/src/helppack.cpp @@ -0,0 +1,520 @@ +/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 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 (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "stdafx.h"
+
+
+/**************************** LOAD PACK ***************************/
+
+void TrimStringSimple(char *str)
+{
+ if (str[lstrlenA(str) - 1] == '\n')
+ str[lstrlenA(str) - 1] = '\0';
+ if (str[lstrlenA(str) - 1] == '\r')
+ str[lstrlenA(str) - 1] = '\0';
+}
+
+void TrimString(char *str)
+{
+ int len, start;
+ len = lstrlenA(str);
+ while (str[0] != '\0' && ((unsigned char)str[len - 1] <= ' '))
+ str[--len] = 0;
+ for (start = 0; str[start] && ((unsigned char)str[start] <= ' '); start++);
+ MoveMemory(str, str + start, len - start + 1);
+}
+
+BOOL IsEmpty(const char *str)
+{
+ int i;
+ for (i = 0; str[i] != '\0'; i++)
+ if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n')
+ return FALSE;
+ return TRUE;
+}
+
+static void CleanupLanguage(char *szLanguage, LCID locale)
+{
+ char *p;
+ if (PRIMARYLANGID(LANGIDFROMLCID(locale)) != LANG_ENGLISH) {
+ /* remove any appended ' (default)' */
+ p = strstr(szLanguage, " (default)");
+ if (p != NULL)
+ *p = '\0';
+ }
+}
+
+static void CleanupAuthors(char *szAuthors)
+{
+ char *p, *p2;
+ /* remove trailing dot (if any) */
+ p = &szAuthors[lstrlenA(szAuthors) - 1];
+ if (*p == '.')
+ *p = '\0';
+ /* remove any extra info in parentheses, which is ok
+ * but makes the list very long for some packs */
+ for (;;) {
+ p = strchr(szAuthors, '(');
+ p2 = strchr(szAuthors, ')');
+ if (p == NULL || p2 == NULL) {
+ p = strchr(szAuthors, '[');
+ p2 = strchr(szAuthors, ']');
+ if (p == NULL || p2 == NULL)
+ break;
+ }
+ if (*(p - 1) == ' ')
+ --p;
+ MoveMemory(p, p2 + 1, lstrlenA(p2 + 1) + 1);
+ }
+}
+
+static void CleanupEmail(char *szAuthorEmail)
+{
+ char c, *p, *pAt;
+ /* replace ' dot ' with '.' (may be removed) */
+ p = strstr(szAuthorEmail, " dot ");
+ if (p != NULL) {
+ *p = '.';
+ MoveMemory(p + 1, p + 5, lstrlenA(p + 5) + 1);
+ }
+ /* also allow ' at ' instead of '@' for obfuscation */
+ p = strstr(szAuthorEmail, " at ");
+ if (p != NULL) {
+ *p = '@';
+ MoveMemory(p + 1, p + 4, lstrlenA(p + 4) + 1);
+ }
+ /* is valid? */
+ pAt = strchr(szAuthorEmail, '@');
+ if (pAt == NULL) {
+ szAuthorEmail[0] = '\0';
+ return;
+ }
+ /* strip-off extra text except exactly one email address
+ * this is needed as a click on the email addres brings up the mail client */
+ for (c = ' ';; c = ',') {
+ p = strchr(pAt, c);
+ if (p != NULL)
+ *p = '\0';
+ p = strrchr(szAuthorEmail, c);
+ if (p != NULL)
+ MoveMemory(szAuthorEmail, p + 1, lstrlenA(p + 1) + 1);
+ if (c == ',')
+ break;
+ }
+ p = strstr(szAuthorEmail, "__");
+ if (p != NULL)
+ MoveMemory(szAuthorEmail, p + 2, lstrlenA(p + 2) + 1);
+ /* lower case */
+ CharLowerA(szAuthorEmail);
+ /* 'none' specified */
+ if (!lstrcmpiA(szAuthorEmail, "none"))
+ szAuthorEmail[0] = '\0';
+}
+
+static void CleanupLastModifiedUsing(char *szLastModifiedUsing, int nSize)
+{
+ char *p;
+ /* remove 'Unicode', as it doesn't matter */
+ p = strstr(szLastModifiedUsing, " Unicode");
+ if (p != NULL) MoveMemory(p, p + 8, lstrlenA(p + 8) + 1);
+ /* use 'Miranda IM' instead of 'Miranda' */
+ p = strstr(szLastModifiedUsing, "Miranda");
+ if (p != NULL && strncmp(p + 7, " IM", 3)) {
+ MoveMemory(p + 10, p + 7, lstrlenA(p + 7) + 1);
+ CopyMemory(p + 7, " IM", 3);
+ }
+ /* use 'Plugin' instead of 'plugin' */
+ p = strstr(szLastModifiedUsing, " plugin");
+ if (p != NULL)
+ CopyMemory(p, " Plugin", 7);
+ /* remove 'v' prefix */
+ p = strstr(szLastModifiedUsing, " v0.");
+ if (p != NULL)
+ MoveMemory(p + 1, p + 2, lstrlenA(p + 2) + 1);
+ /* default if empty */
+ if (!szLastModifiedUsing[0]) {
+ lstrcpynA(szLastModifiedUsing, MIRANDANAME" ", nSize);
+ CallService(MS_SYSTEM_GETVERSIONTEXT, nSize - lstrlenA(szLastModifiedUsing), (LPARAM)szLastModifiedUsing + lstrlenA(szLastModifiedUsing));
+ }
+}
+
+// pack struct should be initialized to zero before call
+// pack->szFileName needs to be filled in before call
+static BOOL LoadPackData(HELPPACK_INFO *pack, BOOL fEnabledPacks, const char *pszFileVersionHeader)
+{
+ FILE *fp;
+ TCHAR szFileName[MAX_PATH];
+ char line[4096], *pszColon, *buf;
+ char szLanguageA[64]; /* same size as pack->szLanguage */
+ /*
+ Miranda Help Pack Version 1
+ Language: (optional)
+ Locale: 0809
+ Authors: Miranda NG Development Team (multiple tags allowed)
+ Author-email: project-info at miranda-ng.org (" at " instead of "@" allowed)
+ Last-Modified-Using: Miranda IM 0.7
+ Plugins-included: (multiple tags allowed)
+ X-FLName: name as used on the file listing (non-standard extension)
+ X-Version: 1.2.3.4 (non-standard extension)
+ see 'Help-Translation.txt' for some header quidelines
+ */
+ if (!GetPackPath(szFileName, sizeof(szFileName), fEnabledPacks, pack->szFileName))
+ return FALSE;
+ fp = _tfopen(szFileName, _T("rt"));
+ if (fp == NULL)
+ return FALSE;
+ fgets(line, sizeof(line), fp);
+ TrimString(line);
+ if (lstrcmpA(line, pszFileVersionHeader)) {
+ fclose(fp);
+ return FALSE;
+ }
+ pack->flags = HPF_NOLOCALE;
+ pack->Locale = LOCALE_USER_DEFAULT;
+ szLanguageA[0] = '\0';
+ while (!feof(fp)) {
+ if (fgets(line, sizeof(line), fp) == NULL)
+ break;
+ TrimString(line);
+ if (IsEmpty(line) || line[0] == ';' || line[0] == '\0')
+ continue;
+ if (line[0] == '[')
+ break;
+ pszColon = strchr(line, ':');
+ if (pszColon == NULL)
+ continue;
+ *pszColon = '\0';
+ TrimString(pszColon + 1);
+ if (!lstrcmpA(line, "Language") && !pack->szLanguage[0])
+ lstrcpynA(szLanguageA, pszColon + 1, sizeof(szLanguageA)); /* buffer safe */
+ else if (!lstrcmpA(line, "Last-Modified-Using") && !pack->szLastModifiedUsing[0])
+ lstrcpynA(pack->szLastModifiedUsing, pszColon + 1, sizeof(pack->szLastModifiedUsing)); /* buffer safe */
+ else if (!lstrcmpA(line, "Authors")) {
+ buf = pack->szAuthors + lstrlenA(pack->szAuthors); /* allow multiple tags */
+ if ((sizeof(pack->szAuthors) - lstrlenA(pack->szAuthors))>0) /* buffer safe */
+ mir_snprintf(buf, sizeof(pack->szAuthors) - lstrlenA(pack->szAuthors), (pack->szAuthors[0] == '\0') ? "%s" : " %s", pszColon + 1);
+ }
+ else if (!lstrcmpA(line, "Author-email") && !pack->szAuthorEmail[0])
+ lstrcpynA(pack->szAuthorEmail, pszColon + 1, sizeof(pack->szAuthorEmail)); /* buffer safe */
+ else if (!lstrcmpA(line, "Locale") && pack->flags&HPF_NOLOCALE) {
+ pack->Locale = MAKELCID((USHORT)strtol(pszColon + 1, NULL, 16), SORT_DEFAULT);
+ if (pack->Locale)
+ pack->flags &= ~HPF_NOLOCALE;
+ }
+ else if (!lstrcmpA(line, "Plugins-included")) {
+ buf = pack->szPluginsIncluded + lstrlenA(pack->szPluginsIncluded); /* allow multiple tags */
+ if ((sizeof(pack->szPluginsIncluded) - lstrlenA(pack->szPluginsIncluded))>0) /* buffer safe */
+ mir_snprintf(buf, sizeof(pack->szPluginsIncluded) - lstrlenA(pack->szPluginsIncluded), (pack->szPluginsIncluded[0] == '\0') ? "%s" : ", %s", CharLowerA(pszColon + 1));
+ }
+ else if (!lstrcmpA(line, "X-Version") && !pack->szVersion[0])
+ lstrcpynA(pack->szVersion, pszColon + 1, sizeof(pack->szVersion)); /* buffer safe */
+ else if (!lstrcmpA(line, "X-FLName") && !pack->szFLName[0])
+ lstrcpynA(pack->szFLName, pszColon + 1, sizeof(pack->szFLName)); /* buffer safe */
+ }
+ /* default */
+ if (PRIMARYLANGID(LANGIDFROMLCID(pack->Locale)) == LANG_ENGLISH && strstr(szLanguageA, " (default)") != NULL)
+ pack->flags |= HPF_DEFAULT;
+ CleanupLanguage(szLanguageA, pack->Locale);
+ CleanupAuthors(pack->szAuthors);
+ CleanupEmail(pack->szAuthorEmail);
+ CleanupLastModifiedUsing(pack->szLastModifiedUsing, sizeof(pack->szLastModifiedUsing));
+ /* codepage */
+ if (!(pack->flags&HPF_NOLOCALE))
+ if (GetLocaleInfoA(pack->Locale, LOCALE_IDEFAULTANSICODEPAGE, line, 6))
+ pack->codepage = (WORD)atoi(line); /* CP_ACP on error */
+ /* language */
+ MultiByteToWideChar(pack->codepage, 0, szLanguageA, -1, pack->szLanguage, sizeof(pack->szLanguage));
+ /* ensure the pack always has a language name */
+ if (!pack->szLanguage[0] && !GetLocaleInfo(pack->Locale, LOCALE_SENGLANGUAGE, pack->szLanguage, sizeof(pack->szLanguage))) {
+ TCHAR *p;
+ lstrcpyn(pack->szLanguage, pack->szFileName, sizeof(pack->szLanguage)); /* buffer safe */
+ p = _tcsrchr(pack->szLanguage, _T('.'));
+ if (p != NULL)
+ *p = '\0';
+ }
+ /* ensure the pack always has a filelisting name */
+ if (!pack->szFLName[0])
+ lstrcatA(lstrcpyA(pack->szFLName, szLanguageA), " Help Pack"); /* buffer safe */
+ fclose(fp);
+
+ return TRUE;
+}
+
+/**************************** ENUM PACKS **************************/
+
+BOOL GetPackPath(TCHAR *pszPath, int nSize, BOOL fEnabledPacks, const TCHAR *pszFile)
+{
+ TCHAR *p;
+ /* main path */
+ if (!GetModuleFileName(NULL, pszPath, nSize))
+ return FALSE;
+ p = _tcsrchr(pszPath, _T('\\'));
+ if (p != NULL)
+ *(p + 1) = _T('\0');
+ /* subdirectory */
+ if (!fEnabledPacks) {
+ if (nSize<(lstrlen(pszPath) + 10))
+ return FALSE;
+ lstrcat(pszPath, _T("Languages\\"));
+ }
+ /* file name */
+ if (pszFile != NULL) {
+ if (nSize < (lstrlen(pszFile) + 11))
+ return FALSE;
+ lstrcat(pszPath, pszFile);
+ }
+
+ return TRUE;
+}
+
+// callback is allowed to be NULL
+// returns TRUE if any pack exists except default
+BOOL EnumPacks(ENUM_PACKS_CALLBACK callback, const TCHAR *pszFilePattern, const char *pszFileVersionHeader, WPARAM wParam, LPARAM lParam)
+{
+ BOOL fPackFound = FALSE;
+ BOOL res = FALSE;
+ HELPPACK_INFO pack;
+ WIN32_FIND_DATA wfd;
+ HANDLE hFind;
+
+ /* enabled packs */
+ if (GetPackPath(pack.szFileName, sizeof(pack.szFileName), TRUE, pszFilePattern)) {
+ hFind = FindFirstFile(pack.szFileName, &wfd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ if ((lstrlen(wfd.cFileName) < 4) || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.'))
+ continue;
+
+ /* get data */
+ ZeroMemory(&pack, sizeof(pack));
+ lstrcpy(pack.szFileName, CharLower(wfd.cFileName)); /* buffer safe */
+ if (LoadPackData(&pack, TRUE, pszFileVersionHeader)) {
+ pack.ftFileDate = wfd.ftLastWriteTime;
+
+ /* enabled? */
+ if (!fPackFound)
+ pack.flags |= HPF_ENABLED;
+ fPackFound = TRUE;
+
+ /* callback */
+ if (callback != NULL)
+ res = callback(&pack, wParam, lParam);
+ if (!res) {
+ FindClose(hFind);
+ return FALSE;
+ }
+ }
+ } while (FindNextFile(hFind, &wfd));
+ FindClose(hFind);
+ }
+ }
+
+ /* disabled packs */
+ if (GetPackPath(pack.szFileName, sizeof(pack.szFileName), FALSE, pszFilePattern)) {
+ hFind = FindFirstFile(pack.szFileName, &wfd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ if (lstrlen(wfd.cFileName) < 4 || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.'))
+ continue;
+
+ /* get data */
+ ZeroMemory(&pack, sizeof(pack));
+ lstrcpy(pack.szFileName, CharLower(wfd.cFileName)); /* buffer safe */
+ if (LoadPackData(&pack, FALSE, pszFileVersionHeader)) {
+ pack.ftFileDate = wfd.ftLastWriteTime;
+ fPackFound = TRUE;
+
+ /* callback */
+ if (callback != NULL)
+ res = callback(&pack, wParam, lParam);
+ if (!res) {
+ FindClose(hFind);
+ return FALSE;
+ }
+ }
+ } while (FindNextFile(hFind, &wfd));
+ FindClose(hFind);
+ }
+ }
+
+ return fPackFound;
+}
+
+BOOL IsPluginIncluded(const HELPPACK_INFO *pack, char *pszFileBaseName)
+{
+ char *p;
+ if (!lstrcmpiA(pszFileBaseName, "png2dib") || !lstrcmpiA(pszFileBaseName, "loadavatars"))
+ return TRUE; /* workaround: does not need no translation */
+ for (p = (char*)pack->szPluginsIncluded;;) {
+ p = strstr(p, CharLowerA(pszFileBaseName));
+ if (p == NULL)
+ return FALSE;
+ if (p == pack->szPluginsIncluded || *(p - 1) == ' ' || *(p - 1) == ',') {
+ p += lstrlenA(pszFileBaseName) + 1;
+ if (*p == ',' || *p == ' ' || *p == 0)
+ return TRUE;
+ }
+ else
+ p += lstrlenA(pszFileBaseName) + 1;
+ }
+
+ return FALSE;
+}
+
+/**************************** SWITCH PACKS ************************/
+
+BOOL EnablePack(const HELPPACK_INFO *pack, const TCHAR *pszFilePattern)
+{
+ TCHAR szFrom[MAX_PATH], szDest[MAX_PATH];
+
+ /* disable previous pack */
+ if (GetPackPath(szFrom, sizeof(szFrom), TRUE, pszFilePattern)) {
+ WIN32_FIND_DATA wfd;
+ HANDLE hFind = FindFirstFile(szFrom, &wfd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ if (lstrlen(wfd.cFileName) < 4 || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.'))
+ continue;
+ /* ensure dir exists */
+ if (GetPackPath(szFrom, sizeof(szFrom), FALSE, NULL))
+ CreateDirectory(szFrom, NULL);
+ /* move file */
+ if (GetPackPath(szFrom, sizeof(szFrom), TRUE, wfd.cFileName))
+ if (GetPackPath(szDest, sizeof(szDest), FALSE, wfd.cFileName))
+ if (!MoveFile(szFrom, szDest) && GetLastError() == ERROR_ALREADY_EXISTS) {
+ DeleteFile(szDest);
+ MoveFile(szFrom, szDest);
+ }
+ break;
+ } while (FindNextFile(hFind, &wfd));
+ FindClose(hFind);
+ }
+ }
+
+ /* enable current pack */
+ if (GetPackPath(szFrom, sizeof(szFrom), FALSE, pack->szFileName))
+ if (GetPackPath(szDest, sizeof(szDest), TRUE, pack->szFileName))
+ return MoveFile(szFrom, szDest);
+
+ return FALSE;
+}
+
+void CorrectPacks(const TCHAR *pszFilePattern, const TCHAR *pszDefaultFile, BOOL fDisableAll)
+{
+ TCHAR szFrom[MAX_PATH], szDest[MAX_PATH], szDir[MAX_PATH], *pszFile;
+ BOOL fDirCreated = FALSE, fOneEnabled = FALSE;
+ HANDLE hFind;
+ WIN32_FIND_DATA wfd;
+
+ /* main path */
+ if (!GetModuleFileName(NULL, szDir, sizeof(szDir)))
+ return;
+ pszFile = _tcsrchr(szDir, _T('\\'));
+ if (pszFile != NULL)
+ *pszFile = _T('\0');
+
+ /* move wrongly placed packs from 'Plugins' to 'Language' */
+ mir_sntprintf(szFrom, sizeof(szFrom), _T("%s\\Plugins\\%s"), szDir, pszFilePattern);
+ hFind = FindFirstFile(szFrom, &wfd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ if (lstrlen(wfd.cFileName) < 4 || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.'))
+ continue;
+
+ /* ensure dir exists */
+ if (!fDirCreated && GetPackPath(szFrom, sizeof(szFrom), FALSE, NULL))
+ fDirCreated = CreateDirectory(szFrom, NULL);
+
+ /* move file */
+ if (GetPackPath(szDest, sizeof(szDest), FALSE, wfd.cFileName)) {
+ mir_sntprintf(szFrom, sizeof(szFrom), _T("%s\\Plugins\\%s"), szDir, wfd.cFileName);
+ if (!MoveFile(szFrom, szDest) && GetLastError() == ERROR_ALREADY_EXISTS) {
+ DeleteFile(szDest);
+ MoveFile(szFrom, szDest);
+ }
+ }
+ } while (FindNextFile(hFind, &wfd));
+ FindClose(hFind);
+ }
+
+ /* disable all packs except one */
+ if (GetPackPath(szFrom, sizeof(szFrom), TRUE, pszFilePattern)) {
+ hFind = FindFirstFile(szFrom, &wfd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ if (lstrlen(wfd.cFileName) < 4 || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.'))
+ continue;
+ /* skip first file */
+ fOneEnabled = TRUE;
+ if (!fDisableAll) {
+ fDisableAll = TRUE;
+ continue;
+ }
+ /* ensure dir exists */
+ if (!fDirCreated && GetPackPath(szFrom, sizeof(szFrom), FALSE, NULL))
+ fDirCreated = CreateDirectory(szFrom, NULL);
+ /* move file */
+ if (GetPackPath(szFrom, sizeof(szFrom), TRUE, wfd.cFileName))
+ if (GetPackPath(szDest, sizeof(szDest), FALSE, wfd.cFileName)) {
+ if (!MoveFile(szFrom, szDest) && GetLastError() == ERROR_ALREADY_EXISTS) {
+ DeleteFile(szDest);
+ MoveFile(szFrom, szDest);
+ }
+ }
+ } while (FindNextFile(hFind, &wfd));
+ FindClose(hFind);
+ }
+ }
+
+ /* ensure one is enabled if installed */
+ if (!fOneEnabled) {
+ /* try to move english */
+ if (GetPackPath(szFrom, sizeof(szFrom), FALSE, pszDefaultFile))
+ if (GetPackPath(szDest, sizeof(szDest), TRUE, pszDefaultFile))
+ fOneEnabled = MoveFile(szFrom, szDest);
+ /* fallback on other one */
+ if (!fOneEnabled)
+ if (GetPackPath(szFrom, sizeof(szFrom), FALSE, pszFilePattern)) {
+ hFind = FindFirstFile(szFrom, &wfd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ if (lstrlen(wfd.cFileName) < 4 || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.'))
+ continue;
+ /* move first file */
+ if (GetPackPath(szFrom, sizeof(szFrom), FALSE, wfd.cFileName))
+ if (GetPackPath(szDest, sizeof(szDest), TRUE, wfd.cFileName))
+ MoveFile(szFrom, szDest);
+ break;
+ } while (FindNextFile(hFind, &wfd));
+ FindClose(hFind);
+ }
+ }
+ }
+}
diff --git a/plugins/ContextHelp/src/main.cpp b/plugins/ContextHelp/src/main.cpp new file mode 100644 index 0000000000..66cc756c67 --- /dev/null +++ b/plugins/ContextHelp/src/main.cpp @@ -0,0 +1,128 @@ +/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 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 (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+HINSTANCE hInst;
+extern HWND hwndHelpDlg;
+
+int hLangpack;
+
+PLUGININFOEX pluginInfo = {
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __AUTHOREMAIL,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {B16DF0B2-A08F-4bc6-8FA2-3D5FFFA2708D}
+ { 0xb16df0b2, 0xa08f, 0x4bc6, { 0x8f, 0xa2, 0x3d, 0x5f, 0xff, 0xa2, 0x70, 0x8d } }
+};
+
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD, LPVOID)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+static void InstallFile(const TCHAR *pszFileName, const TCHAR *pszDestSubDir)
+{
+ TCHAR szFileFrom[MAX_PATH + 1], szFileTo[MAX_PATH + 1], *p;
+ HANDLE hFile;
+
+ if (!GetModuleFileName(hInst, szFileFrom, sizeof(szFileFrom) - lstrlen(pszFileName)))
+ return;
+ p = _tcsrchr(szFileFrom, _T('\\'));
+ if (p != NULL)
+ *(++p) = 0;
+ lstrcat(szFileFrom, pszFileName); /* buffer safe */
+
+ hFile = CreateFile(szFileFrom, 0, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
+ if (hFile == INVALID_HANDLE_VALUE)
+ return;
+ CloseHandle(hFile);
+
+ if (!GetModuleFileName(NULL, szFileTo, sizeof(szFileTo) - lstrlen(pszDestSubDir) - lstrlen(pszFileName)))
+ return;
+ p = _tcsrchr(szFileTo, _T('\\'));
+ if (p != NULL)
+ *(++p) = 0;
+ lstrcat(szFileTo, pszDestSubDir); /* buffer safe */
+ CreateDirectory(szFileTo, NULL);
+ lstrcat(szFileTo, pszFileName); /* buffer safe */
+
+ if (!MoveFile(szFileFrom, szFileTo) && GetLastError() == ERROR_ALREADY_EXISTS) {
+ DeleteFile(szFileTo);
+ MoveFile(szFileFrom, szFileTo);
+ }
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD)
+{
+ return &pluginInfo;
+}
+
+
+extern "C" __declspec(dllexport) int Load(void)
+{
+ mir_getLP(&pluginInfo);
+
+ INITCOMMONCONTROLSEX icc = { 0 };
+ icc.dwSize = sizeof(icc);
+ icc.dwICC = ICC_UPDOWN_CLASS | ICC_TREEVIEW_CLASSES;
+ if (!InitCommonControlsEx(&icc))
+ return 1;
+
+ if (LoadLibrary(_T("riched20.dll")) == NULL) /* richedit v2.0 (Win98/NT4), v3.0 (WinXP/2000/Me+) */
+ if (IDYES != MessageBoxEx(NULL,
+ TranslateT("The Help Plugin can not be loaded, riched20.dll is missing. If you are using WINE please make sure you have riched20.dll installed.\n\nPress 'Yes' to continue loading Miranda NG."),
+ TranslateT("Help Plugin"), MB_YESNO | MB_ICONWARNING | MB_SETFOREGROUND | MB_TOPMOST | MB_TASKMODAL, LANGIDFROMLCID(Langpack_GetDefaultLocale())))
+ return 1;
+
+ if (InstallDialogBoxHook())
+ return 1;
+
+ InitOptions();
+ InitDialogCache();
+ // InitUpdate();
+
+ // InstallFile(_T("Help-Readme.txt"),_T("Docs\\"));
+ // InstallFile(_T("Help-License.txt"),_T("Docs\\"));
+ // InstallFile(_T("Help-SDK.zip"),_T("Docs\\"));
+
+ return 0;
+}
+
+extern "C" __declspec(dllexport) int Unload(void)
+{
+ UninitOptions();
+ // UninitUpdate();
+ RemoveDialogBoxHook();
+
+ if (hwndHelpDlg != NULL)
+ DestroyWindow(hwndHelpDlg);
+ FreeDialogCache();
+
+ return 0;
+}
diff --git a/plugins/ContextHelp/src/options.cpp b/plugins/ContextHelp/src/options.cpp new file mode 100644 index 0000000000..1078bd2e4e --- /dev/null +++ b/plugins/ContextHelp/src/options.cpp @@ -0,0 +1,797 @@ +/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 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 (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+typedef PLUGININFOEX* (__cdecl *pfnMirandaPluginInfo)(DWORD);
+
+WORD settingAutoTipDelay; /* auto show help after those milliseconds, 0 if disabled */
+extern HINSTANCE hInst;
+static HANDLE hHookOptInit;
+static HWND hwndLangOpt;
+
+/**************************** LANGUAGE OPTS ***********************/
+
+/* these are only there for a short time to
+* debug those radio buttons an Win9x */
+#define BOX(str) BOX2("%s (err:%i)",str,GetLastError())
+#define BOX2(fmt,p1,p2) { char str[256]; mir_snprintf(str,fmt,p1,p2); MessageBoxA(NULL,str,"dbg",0); }
+
+// ImageList_Destroy() the return value
+// refresh on WM_THEMECHANGED
+static HIMAGELIST CreateRadioImages(COLORREF clrBk, COLORREF clrText)
+{
+ /* draw bitmap */
+ HDC hdcScreen = GetDC(NULL);
+ if (hdcScreen == NULL)
+ return NULL;
+
+ HIMAGELIST himl = NULL;
+ HDC hdc = CreateCompatibleDC(NULL); // compatible to screen
+ if (hdc != NULL) {
+ SIZE size = { GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON) };
+ RECT rc;
+ SetRect(&rc, 0, 0, 2 * size.cx, size.cy);
+ HBITMAP hbm = CreateCompatibleBitmap(hdcScreen, rc.right, rc.bottom);
+ if (hbm != NULL) {
+ HBITMAP hbmPrev = (HBITMAP)SelectObject(hdc, hbm);
+ if (hbmPrev != NULL) { // error on select?
+ HTHEME hTheme = OpenThemeData(NULL, L"Button");
+ SetRect(&rc, 0, 0, size.cx, size.cy);
+ /* unchecked */
+ if (!DrawThemeBackground(hTheme, hdc, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL, &rc, NULL)) {
+ /* checked */
+ OffsetRect(&rc, size.cx, 0);
+ if (!DrawThemeBackground(hTheme, hdc, BP_RADIOBUTTON, RBS_CHECKEDNORMAL, &rc, NULL))
+ himl = ImageList_Create(size.cx, size.cy, ILC_COLOR32 | ILC_MASK, 3, 0);
+ }
+ CloseThemeData(hTheme);
+
+ /* the classic way */
+ if (himl == NULL) {
+ HBRUSH hbrBk = CreateSolidBrush(clrBk);
+ if (hbrBk != NULL) {
+ FillRect(hdc, &rc, hbrBk);
+ DeleteObject(hbrBk);
+ HDC hdcMono = CreateCompatibleDC(hdc);
+ if (hdcMono != NULL) {
+ HBITMAP hbmMono = CreateBitmap(rc.right, rc.bottom, 1, 1, NULL);
+ if (hbmMono != NULL) {
+ HBITMAP hbmPrevMono = (HBITMAP)SelectObject(hdcMono, hbmMono);
+ if (hbmPrevMono != NULL) { /* error on select? */
+ /* draws a black-and-white mask (see docs)
+ * we need to colorize it using BitBlt with text and background color */
+ COLORREF clrPrevText = SetTextColor(hdc, clrText);
+ COLORREF clrPrevBk = SetBkColor(hdc, clrBk);
+ /* check mark is slightly smaller than icon size */
+ RECT rcRadio;
+ SetRect(&rcRadio, 0, 0, GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK));
+ if (rcRadio.right>size.cx)
+ rcRadio.right = size.cx;
+ if (rcRadio.bottom>size.cy)
+ rcRadio.bottom = size.cy;
+ SetRect(&rc, ((size.cx - rcRadio.right) / 2) + 1, ((size.cy - rcRadio.bottom) / 2) + 1, rcRadio.right + 1, rcRadio.bottom + 1);
+ /* unchecked */
+ if (BitBlt(hdcMono, 0, 0, rcRadio.right, rcRadio.bottom, NULL, 0, 0, WHITENESS)) { /* white back */
+ if (DrawFrameControl(hdcMono, &rcRadio, DFC_BUTTON, DFCS_BUTTONRADIO | DFCS_FLAT)) {
+ if (BitBlt(hdc, rc.left, rc.top, rcRadio.right, rcRadio.bottom, hdcMono, 0, 0, SRCCOPY | NOMIRRORBITMAP)) {
+ /* checked */
+ OffsetRect(&rc, size.cx, 0);
+ if (BitBlt(hdcMono, 0, 0, rcRadio.right, rcRadio.bottom, NULL, 0, 0, WHITENESS)) {/* white back */
+ if (DrawFrameControl(hdcMono, &rcRadio, DFC_BUTTON, DFCS_BUTTONRADIO | DFCS_FLAT | DFCS_CHECKED)) {
+ if (BitBlt(hdc, rc.left, rc.top, rcRadio.right, rcRadio.bottom, hdcMono, 0, 0, SRCCOPY | NOMIRRORBITMAP)) {
+ himl = ImageList_Create(size.cx, size.cy, ILC_COLOR | ILC_MASK, 3, 0);
+ if (himl == NULL)
+ BOX("img list creation failed");
+ }
+ }
+ else
+ BOX("second DrawFrameControl() failed");
+ }
+ else
+ BOX("second BitBlt() failed");
+ }
+ else
+ BOX("intermediate BitBlt() failed");
+ }
+ else
+ BOX("DrawFrameControl() failed");
+ }
+ else
+ BOX("first BitBlt() failed");
+ /* restore */
+ SetBkColor(hdc, clrPrevBk);
+ SetTextColor(hdc, clrPrevText);
+ SelectObject(hdcMono, hbmPrevMono);
+ }
+ else
+ BOX("hbmPrevMono==NULL");
+ DeleteObject(hbmMono);
+ }
+ else
+ BOX("hbmMono==NULL");
+ DeleteDC(hdcMono);
+ }
+ else
+ BOX("hdcMono==NULL");
+ }
+ }
+ SelectObject(hdc, hbmPrev);
+ /* create imagelist */
+ if (himl != NULL) {
+ if (ImageList_AddMasked(himl, hbm, clrBk) == -1)
+ BOX("addmasked failed");
+ }
+ else
+ BOX("Win9x: drawing code not reached");
+ }
+ DeleteObject(hbm);
+ }
+ DeleteDC(hdc);
+ }
+ ReleaseDC(NULL, hdcScreen);
+
+ return himl;
+}
+
+static void CleanupPluginName(char *szShortName)
+{
+ char *p;
+ /* strip-off anything in brackets */
+ for (p = szShortName; *p != '\0'; ++p)
+ if (*p == '(' || *p == '[') {
+ *p = '\0';
+ break;
+ }
+ /* remove trailing space */
+ int len = lstrlenA(szShortName);
+ while (szShortName[0] != '\0' && szShortName[len - 1] == ' ')
+ szShortName[--len] = 0;
+}
+
+static void DisplayNotIncludedPlugins(HWND hwndListBox, const HELPPACK_INFO *pack)
+{
+ /* enum plugins */
+ TCHAR szDir[MAX_PATH];
+ if (GetModuleFileName(NULL, szDir, sizeof(szDir))) {
+ TCHAR *p = _tcsrchr(szDir, _T('\\'));
+ if (p != NULL)
+ *p = _T('\0');
+ TCHAR szSearch[MAX_PATH];
+ mir_sntprintf(szSearch, sizeof(szSearch), _T("%s\\Plugins\\*.dll"), szDir);
+ WIN32_FIND_DATA wfd;
+ HANDLE hFind = FindFirstFile(szSearch, &wfd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ DWORD mirandaVersion = CallService(MS_SYSTEM_GETVERSION, 0, 0);
+ SendMessage(hwndListBox, LB_SETLOCALE, Langpack_GetDefaultLocale(), 0); /* for sort order */
+ SendMessage(hwndListBox, LB_INITSTORAGE, 128, lstrlenA(pack->szPluginsIncluded)); /* speed up */
+ do {
+ if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ if ((lstrlen(wfd.cFileName)<4) || (wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.')))
+ continue;
+ /* file name */
+ lstrcpy(szSearch, wfd.cFileName); /* buffer safe */
+ p = _tcsrchr(szSearch, _T('.'));
+ if (p != NULL)
+ *p = _T('\0');
+ char cFileNameA[MAX_PATH];
+ cFileNameA[0] = '\0';
+ WideCharToMultiByte(CP_ACP, 0, szSearch, -1, cFileNameA, sizeof(cFileNameA), NULL, NULL);
+ if (IsPluginIncluded(pack, cFileNameA))
+ continue;
+
+ /* friendly name of the plugin */
+ mir_sntprintf(szSearch, sizeof(szSearch), _T("%s\\Plugins\\%s"), szDir, wfd.cFileName);
+ HMODULE hModule = GetModuleHandle(szSearch);
+ BOOL fNeedsFree = (hModule == NULL);
+ if (hModule == NULL) {
+ hModule = LoadLibrary(szSearch);
+ if (hModule == NULL)
+ continue;
+ }
+
+ /* plugin info */
+ pfnMirandaPluginInfo pFunc = (pfnMirandaPluginInfo)GetProcAddress(hModule, "MirandaPluginInfoEx");
+ if (pFunc != NULL) { /* both structs have the same header */
+ PLUGININFOEX *pluginInfo = pFunc(mirandaVersion);
+ if (pluginInfo != NULL && pluginInfo->cbSize >= sizeof(PLUGININFOEX) && pluginInfo->shortName != NULL) {
+ char buf[128];
+ lstrcpynA(buf, pluginInfo->shortName, sizeof(buf)); /* buffer safe */
+ CleanupPluginName(buf);
+
+ TCHAR buf2[128];
+ mir_sntprintf(buf2, sizeof(buf2), _T("%hs (%s)"), buf, CharLower(wfd.cFileName));
+ SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)buf2);
+ }
+ }
+ if (fNeedsFree)
+ FreeLibrary(hModule);
+ } while (FindNextFile(hFind, &wfd));
+ FindClose(hFind);
+ }
+ }
+ /* all are included? */
+ if (!SendMessage(hwndListBox, LB_GETCOUNT, 0, 0))
+ SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TranslateT("All installed plugins are included."));
+}
+
+// pack is allowed to be NULL
+static void DisplayPackInfo(HWND hwndDlg, const HELPPACK_INFO *pack)
+{
+ /* show/hide controls */
+ if (IsWindowVisible(GetDlgItem(hwndDlg, IDC_NOPACK)) == (pack != NULL)) {
+ const int controls[] = { IDC_LANGNOTINCLUDEDLABEL, IDC_LANGNOTINCLUDED, IDC_LANGDATELABEL, IDC_LANGDATE,
+ IDC_LANGLOCALELABEL, IDC_LANGLOCALE, IDC_LANGVERSIONLABEL, IDC_LANGVERSION,
+ IDC_LANGMODUSINGLABEL, IDC_LANGMODUSING, IDC_LANGAUTHORSLABEL, IDC_LANGAUTHORS,
+ IDC_LANGEMAILLABEL, IDC_LANGEMAIL };
+ for (int i = 0; i < sizeof(controls); i++)
+ ShowWindow(GetDlgItem(hwndDlg, controls[i]), (pack != NULL) ? SW_SHOW : SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_NOPACK), (pack != NULL) ? SW_HIDE : SW_SHOW);
+ SetDlgItemText(hwndDlg, IDC_LANGINFOFRAME, NULL);
+ }
+ if (pack == NULL)
+ return;
+ /* compute not-included from included list */
+ SendDlgItemMessage(hwndDlg, IDC_LANGNOTINCLUDED, LB_RESETCONTENT, 0, 0);
+ DisplayNotIncludedPlugins(GetDlgItem(hwndDlg, IDC_LANGNOTINCLUDED), pack);
+ /* locale string */
+ if (!(pack->flags&HPF_NOLOCALE)) {
+ TCHAR szLocaleName[128];
+ szLocaleName[0] = _T('\0');
+ /* can't use LOCALE_SNAME as it is not present on pre WinVista */
+ if (!GetLocaleInfo(pack->Locale, LOCALE_SISO639LANGNAME, szLocaleName, sizeof(szLocaleName))) { /* Win98/NT4+ */
+ if (!GetLocaleInfo(pack->Locale, LOCALE_SLANGUAGE, szLocaleName, sizeof(szLocaleName))) /* not unique! */
+ szLocaleName[0] = _T('\0');
+ }
+ else {
+ if (GetLocaleInfo(pack->Locale, LOCALE_SISO3166CTRYNAME, &szLocaleName[3], sizeof(szLocaleName) - 3)) /* Win98/NT4+ */
+ szLocaleName[2] = _T('-');
+ }
+ /* add some note if its incompatible */
+ if (szLocaleName[0]) {
+ if (!IsValidLocale(pack->Locale, LCID_INSTALLED)) {
+ TCHAR *pszIncompat;
+ pszIncompat = TranslateT("(incompatible)");
+ szLocaleName[sizeof(szLocaleName) - lstrlen(pszIncompat) - 1] = 0;
+ lstrcat(lstrcat(szLocaleName, _T(" ")), pszIncompat); /* buffer safe */
+ }
+ SetDlgItemText(hwndDlg, IDC_LANGLOCALE, szLocaleName);
+ }
+ else
+ SetDlgItemText(hwndDlg, IDC_LANGLOCALE, TranslateT("Unknown"));
+ }
+ else
+ SetDlgItemText(hwndDlg, IDC_LANGLOCALE, TranslateT("Current"));
+
+ /* file date */
+ {
+ SYSTEMTIME stFileDate;
+ TCHAR szDate[128];
+ szDate[0] = _T('\0');
+ if (FileTimeToSystemTime(&pack->ftFileDate, &stFileDate))
+ GetDateFormat(Langpack_GetDefaultLocale(), DATE_SHORTDATE, &stFileDate, NULL, szDate, sizeof(szDate));
+ SetDlgItemText(hwndDlg, IDC_LANGDATE, szDate);
+ }
+
+ /* version */
+ SetDlgItemTextA(hwndDlg, IDC_LANGVERSION, pack->szVersion);
+ if (pack->szVersion[0] && pack->szFLName[0]) {
+ if (!IsWindowVisible(GetDlgItem(hwndDlg, IDC_LANGVERSIONLABEL))) {
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LANGVERSIONLABEL), SW_SHOW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LANGVERSION), SW_SHOW);
+ }
+ }
+ else {
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LANGVERSIONLABEL), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LANGVERSION), SW_HIDE);
+ }
+
+ /* general */
+ SetDlgItemTextA(hwndDlg, IDC_LANGMODUSING, pack->szLastModifiedUsing);
+ SetDlgItemTextA(hwndDlg, IDC_LANGAUTHORS, pack->szAuthors);
+ SetDlgItemTextA(hwndDlg, IDC_LANGEMAIL, pack->szAuthorEmail);
+ SetDlgItemText(hwndDlg, IDC_LANGINFOFRAME, TranslateTS(pack->szLanguage));
+}
+
+#define M_RELOADLIST (WM_APP+1)
+#define M_SHOWFILECOL (WM_APP+2)
+
+static void DeletePackFile(HWND hwndDlg, HWND hwndList, int iItem, HELPPACK_INFO *pack)
+{
+ SHFILEOPSTRUCT sfo = { 0 };
+ sfo.hwnd = hwndDlg;
+ sfo.wFunc = FO_DELETE;
+ sfo.fFlags = FOF_SIMPLEPROGRESS | FOF_SILENT; /* silent = no progress */
+ /* double zero terminated */
+ TCHAR szFileName[MAX_PATH];
+ if (GetPackPath(szFileName, sizeof(szFileName) - 1, pack->flags&HPF_ENABLED, pack->szFileName)) {
+ szFileName[lstrlen(szFileName) + 1] = _T('\0');
+ sfo.pFrom = szFileName;
+ /* ask to delete file */
+ if (!SHFileOperation(&sfo) && !sfo.fAnyOperationsAborted) {
+ LVITEM lvi;
+ lvi.iItem = iItem;
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_STATE;
+ lvi.stateMask = LVIS_STATEIMAGEMASK | LVIS_SELECTED | LVIS_FOCUSED;
+ if (ListView_GetItem(hwndList, &lvi)) {
+ ListView_DeleteItem(hwndList, iItem);
+ /* enable/select next item at same position */
+ int nCount = ListView_GetItemCount(hwndList);
+ lvi.iItem = (iItem < nCount) ? iItem : iItem - 1;
+ if (lvi.iItem != -1) {
+ ListView_SetItemState(hwndList, lvi.iItem, lvi.state, lvi.stateMask);
+ /* enable pack file */
+ lvi.mask = LVIF_PARAM;
+ lvi.iSubItem = 0;
+ if (ListView_GetItem(hwndList, &lvi))
+ EnablePack((HELPPACK_INFO*)lvi.lParam, _T("helppack_*.txt"));
+ }
+ else
+ DisplayPackInfo(hwndDlg, NULL);
+ if (nCount == 1)
+ SendMessage(hwndDlg, M_SHOWFILECOL, 0, FALSE);
+ }
+ }
+ }
+}
+
+static INT_PTR CALLBACK InsertPackItemEnumProc(HELPPACK_INFO *pack, WPARAM wParam, LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(lParam);
+
+ HELPPACK_INFO *pack2 = (HELPPACK_INFO*)mir_alloc(sizeof(HELPPACK_INFO));
+ if (pack2 == NULL)
+ return FALSE;
+ CopyMemory(pack2, pack, sizeof(HELPPACK_INFO));
+
+ /* country flag icon */
+ LVITEM lvi;
+ lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE;
+ if ((HIMAGELIST)lParam != NULL) {
+ HICON hIcon;
+ if (pack->flags&HPF_DEFAULT) /* not really needed, but to be consistent with LangMan */
+ hIcon = (HICON)CallService(MS_FLAGS_CREATEMERGEDFLAGICON, CTRY_UNITED_STATES, CTRY_UNITED_KINGDOM);
+ else {
+ int countryId = 0xFFFF; /* Unknown */
+ TCHAR szBuf[6];
+ /* get country id from locale */
+ if (!(pack->flags&HPF_NOLOCALE))
+ if (GetLocaleInfo(pack->Locale, LOCALE_ICOUNTRY, szBuf, sizeof(szBuf)))
+ countryId = _ttoi(szBuf);
+ hIcon = (HICON)CallService(MS_FLAGS_LOADFLAGICON, countryId, 0);
+ }
+ lvi.iImage = (hIcon == NULL) ? -1 : ImageList_AddIcon((HIMAGELIST)lParam, hIcon);
+ lvi.mask |= LVIF_IMAGE;
+ }
+
+ /* insert */
+ lvi.iItem = lvi.iSubItem = 0;
+ lvi.stateMask = LVIS_STATEIMAGEMASK | LVIS_SELECTED;
+ lvi.state = INDEXTOSTATEIMAGEMASK((pack->flags&HPF_ENABLED) ? 2 : 1);
+ if (pack->flags&HPF_ENABLED)
+ lvi.state |= LVIS_SELECTED | LVIS_FOCUSED;
+ lvi.pszText = TranslateTS(pack->szLanguage);
+ lvi.lParam = (LPARAM)pack2;
+ ListView_InsertItem((HWND)wParam, &lvi);
+
+ return TRUE;
+}
+
+static int CALLBACK CompareListItem(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+{
+ int cmp = CompareString((LCID)lParamSort, 0, ((HELPPACK_INFO*)lParam1)->szLanguage, -1, ((HELPPACK_INFO*)lParam2)->szLanguage, -1);
+ if (cmp)
+ cmp -= 2;
+ if (!cmp) {
+ if (((HELPPACK_INFO*)lParam1)->flags&HPF_ENABLED)
+ cmp = -1;
+ else if (((HELPPACK_INFO*)lParam2)->flags&HPF_ENABLED)
+ cmp = 1;
+ }
+
+ return cmp;
+}
+
+static INT_PTR CALLBACK LangOptDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_LANGLIST);
+ LVITEM lvi;
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+ hwndLangOpt = hwndDlg;
+ ListView_SetExtendedListViewStyle(hwndList, LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP);
+ ListView_SetImageList(hwndList, CreateRadioImages(ListView_GetBkColor(hwndList), ListView_GetTextColor(hwndList)), LVSIL_STATE); /* auto-destroyed */
+ {
+ LVCOLUMN lvc;
+ lvc.mask = LVCF_TEXT;
+ lvc.pszText = TranslateT("Installed Languages");
+ ListView_InsertColumn(hwndList, 0, &lvc);
+ }
+ if (ServiceExists(MS_FLAGS_LOADFLAGICON))
+ ListView_SetImageList(hwndList, ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR24, 8, 8), LVSIL_SMALL);
+ CorrectPacks(_T("helppack_*.txt"), _T("helppack_english.txt"), FALSE);
+ CheckDlgButton(hwndDlg, IDC_ENABLEHELPUPDATES, db_get_b(NULL, "HelpPlugin", "EnableHelpUpdates", SETTING_ENABLEHELPUPDATES_DEFAULT) != 0);
+ SendMessage(hwndDlg, M_RELOADLIST, 0, 0);
+ SendMessage(hwndDlg, M_SHOWFILECOL, 0, 1);
+ return TRUE;
+ }
+ case M_RELOADLIST:
+ {
+ /* init list */
+ ListView_DeleteAllItems(hwndList);
+ ListView_DeleteColumn(hwndList, 1); /* if present */
+ {
+ HIMAGELIST himl = ListView_GetImageList(hwndList, LVSIL_SMALL);
+ ImageList_RemoveAll(himl);
+ /* enum all packs */
+ EnumPacks(InsertPackItemEnumProc, _T("helppack_*.txt"), "Miranda Help Pack Version 1", (WPARAM)hwndList, (LPARAM)himl);
+ /* nothing installed */
+ if (!ListView_GetItemCount(hwndList))
+ DisplayPackInfo(hwndDlg, NULL);
+ /* make it use current langpack locale for sort */
+ ListView_SortItems(hwndList, CompareListItem, Langpack_GetDefaultLocale());
+ CheckDlgButton(hwndDlg, IDC_ENABLEHELPUPDATES, db_get_b(NULL, "HelpPlugin", "EnableHelpUpdates", SETTING_ENABLEHELPUPDATES_DEFAULT) != 0);
+ /* show selection */
+ int iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
+ if (iItem != -1)
+ ListView_EnsureVisible(hwndList, iItem, FALSE);
+ }
+ return TRUE;
+ }
+ case M_SHOWFILECOL:
+ if ((BOOL)lParam && ListView_GetItemCount(hwndList)>1) {
+ /* add column */
+ LVCOLUMN lvc;
+ ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE_USEHEADER);
+ lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
+ lvc.pszText = TranslateT("File");
+ lvc.cx = 160;
+ ListView_InsertColumn(hwndList, lvc.iSubItem = 1, &lvc);
+ ListView_SetColumnWidth(hwndList, 0, ListView_GetColumnWidth(hwndList, 0) - lvc.cx);
+
+ /* add text */
+ lvi.mask = LVIF_PARAM;
+ lvi.iSubItem = 0;
+ for (lvi.iItem = 0; ListView_GetItem(hwndList, &lvi); ++lvi.iItem) {
+ HELPPACK_INFO *pack = (HELPPACK_INFO*)lvi.lParam;
+ ListView_SetItemText(hwndList, lvi.iItem, 1, pack->szFileName);
+ }
+ }
+ else {
+ ListView_DeleteColumn(hwndList, 1);
+ ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE_USEHEADER);
+ }
+ return TRUE;
+
+ case WM_THEMECHANGED:
+ case WM_SETTINGCHANGE:
+ {
+ HIMAGELIST himl = ListView_SetImageList(hwndList, CreateRadioImages(ListView_GetBkColor(hwndList), ListView_GetTextColor(hwndList)), LVSIL_STATE); /* auto-destroyed */
+ if (himl != NULL)
+ ImageList_Destroy(himl);
+ }
+ break;
+
+ case WM_CTLCOLORLISTBOX: /* mimic readonly edit */
+ return (BOOL)SendMessage(hwndDlg, WM_CTLCOLORSTATIC, wParam, lParam);
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_LANGEMAIL:
+ {
+ char buf[512];
+ lstrcpyA(buf, "mailto:");
+ if (GetWindowTextA(GetDlgItem(hwndDlg, LOWORD(wParam)), &buf[7], sizeof(buf) - 7))
+ // CallService(MS_UTILS_OPENURL,FALSE,(LPARAM)buf); // look at !!
+ return TRUE;
+ }
+
+ case IDC_MORELANG:
+ // CallService(MS_UTILS_OPENURL,TRUE,(LPARAM)"http://miranda-ng.org/"); //look at !!
+ return TRUE;
+
+ case IDC_ENABLEHELPUPDATES:
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); /* enable apply */
+ return TRUE;
+
+ case IDC_DOWNLOADLANG:
+ // ServiceShowLangDialog(0, 0);
+ return TRUE;
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ if (GetDlgCtrlID((HWND)wParam) == IDC_LANGLIST) {
+ /* get item */
+ LVHITTESTINFO hti;
+ POINTSTOPOINT(hti.pt, MAKEPOINTS(lParam));
+ if (hti.pt.x == -1 && hti.pt.y == -1) {
+ /* keyboard invoked */
+ hti.iItem = ListView_GetNextItem((HWND)wParam, -1, LVNI_SELECTED);
+ if (hti.iItem != -1)
+ break;
+
+ RECT rc;
+ if (!ListView_GetItemRect((HWND)wParam, hti.iItem, &rc, LVIR_SELECTBOUNDS))
+ break;
+
+ hti.pt.x = rc.left + (rc.right - rc.left) / 2;
+ hti.pt.y = rc.top + (rc.bottom - rc.top) / 2;
+ ClientToScreen((HWND)wParam, &hti.pt);
+ }
+ else {
+ ScreenToClient((HWND)wParam, &hti.pt);
+ if (ListView_HitTest((HWND)wParam, &hti) == -1 || !(hti.flags&LVHT_ONITEM))
+ break;
+ POINTSTOPOINT(hti.pt, MAKEPOINTS(lParam));
+ }
+ /* param */
+ lvi.iItem = hti.iItem;
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM;
+ if (!ListView_GetItem((HWND)wParam, &lvi))
+ break;
+
+ /* context menu */
+ HELPPACK_INFO *pack = (HELPPACK_INFO*)lvi.lParam;
+ HMENU hContextMenu = CreatePopupMenu();
+ if (hContextMenu != NULL) {
+ AppendMenu(hContextMenu, MF_STRING, 2, TranslateT("&Remove..."));
+ if (TrackPopupMenuEx(hContextMenu, TPM_RETURNCMD | TPM_NONOTIFY | TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_HORPOSANIMATION | TPM_VERPOSANIMATION, hti.pt.x, hti.pt.y, (HWND)wParam, NULL))
+ DeletePackFile(hwndDlg, (HWND)wParam, hti.iItem, pack);
+ DestroyMenu(hContextMenu);
+ }
+ return TRUE;
+ }
+ break;
+
+ case WM_NOTIFYFORMAT:
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, NFR_UNICODE);
+ return TRUE;
+
+ case WM_NOTIFY:
+ NMHDR *nmhdr = (NMHDR*)lParam;
+ switch (nmhdr->idFrom) {
+ case IDC_LANGLIST:
+ switch (nmhdr->code) {
+ case LVN_DELETEITEM:
+ LVITEM lvi;
+ lvi.iItem = ((NMLISTVIEW*)lParam)->iItem; /* nmlv->lParam is invalid */
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM;
+ if (ListView_GetItem(nmhdr->hwndFrom, &lvi))
+ mir_free((HELPPACK_INFO*)lvi.lParam);
+ break;
+
+ case LVN_ITEMCHANGED:
+ {
+ NMLISTVIEW *nmlv = (NMLISTVIEW*)lParam;
+ if (!(nmlv->uChanged&LVIF_STATE))
+ break;
+
+ /* display info and check radio item */
+ if (nmlv->uNewState&LVIS_SELECTED && !(nmlv->uOldState&LVIS_SELECTED)) {
+ ListView_SetItemState(nmhdr->hwndFrom, nmlv->iItem, INDEXTOSTATEIMAGEMASK(2), LVIS_STATEIMAGEMASK);
+ DisplayPackInfo(hwndDlg, (HELPPACK_INFO*)nmlv->lParam);
+ }
+ /* disable all other radio items */
+ else if (nmlv->uNewState&INDEXTOSTATEIMAGEMASK(2)) {
+ for (int iItem = ListView_GetItemCount(nmhdr->hwndFrom) - 1; iItem != -1; --iItem)
+ if (iItem != nmlv->iItem)
+ ListView_SetItemState(nmhdr->hwndFrom, iItem, INDEXTOSTATEIMAGEMASK(1), LVIS_STATEIMAGEMASK);
+
+ /* enable apply */
+ if (nmlv->uOldState)
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+ break;
+
+ case LVN_KEYDOWN:
+ {
+ int iItem = ListView_GetNextItem(nmhdr->hwndFrom, -1, LVNI_SELECTED);
+ switch (((NMLVKEYDOWN*)lParam)->wVKey) {
+ case VK_SPACE:
+ ListView_SetItemState(nmhdr->hwndFrom, iItem, INDEXTOSTATEIMAGEMASK(2), LVIS_STATEIMAGEMASK);
+ break;
+
+ case VK_DELETE:
+ lvi.iItem = iItem;
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM;
+ if (ListView_GetItem(nmhdr->hwndFrom, &lvi))
+ DeletePackFile(hwndDlg, nmhdr->hwndFrom, iItem, (HELPPACK_INFO*)lvi.lParam);
+ break;
+ }
+ }
+ break;
+
+ case NM_CLICK:
+ LVHITTESTINFO hti;
+ lParam = GetMessagePos();
+ POINTSTOPOINT(hti.pt, MAKEPOINTS(lParam));
+ ScreenToClient(nmhdr->hwndFrom, &hti.pt);
+ if (ListView_HitTest(nmhdr->hwndFrom, &hti) != -1)
+ if (hti.flags&(LVHT_ONITEMSTATEICON | LVHT_ONITEMICON)) /* one of them */
+ ListView_SetItemState(nmhdr->hwndFrom, hti.iItem, LVIS_SELECTED, LVIS_SELECTED);
+ } /* switch nmhdr->code */
+ break;
+
+ case 0:
+ switch (nmhdr->code) {
+ case PSN_APPLY:
+ lvi.mask = LVIF_STATE | LVIF_PARAM;
+ lvi.stateMask = LVIS_STATEIMAGEMASK;
+ lvi.iSubItem = 0;
+ for (lvi.iItem = 0; ListView_GetItem(hwndList, &lvi); ++lvi.iItem) {
+ HELPPACK_INFO *pack = (HELPPACK_INFO*)lvi.lParam;
+ if (lvi.state&INDEXTOSTATEIMAGEMASK(2)) {
+ EnablePack(pack, _T("helppack_*.txt"));
+ pack->flags |= HPF_ENABLED;
+ }
+ else
+ pack->flags &= ~HPF_ENABLED;
+ }
+ db_set_b(NULL, "HelpPlugin", "EnableAutoUpdates", (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_ENABLEHELPUPDATES) != 0));
+ return TRUE;
+ }
+ } /* switch nmhdr->idFrom */
+ break;
+ }
+ return FALSE;
+}
+
+void ReloadLangOptList(void)
+{
+ if (hwndLangOpt != NULL) {
+ SendMessage(hwndLangOpt, M_RELOADLIST, 0, 0);
+ SendMessage(hwndLangOpt, M_SHOWFILECOL, 0, 1);
+ }
+}
+
+/**************************** ADV OPTS ****************************/
+
+static INT_PTR CALLBACK AdvOptDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ {
+ WORD val = settingAutoTipDelay;
+ if (!val)
+ val = db_get_w(NULL, "HelpPlugin", "AutoTipDelay", SETTING_AUTOTIPDELAY_DEFAULT);
+ CheckDlgButton(hwndDlg, IDC_AUTOTIPSENABLED, settingAutoTipDelay != 0);
+ SendDlgItemMessage(hwndDlg, IDC_AUTOTIPDELAYSPIN, UDM_SETRANGE, 0, MAKELPARAM(10000, 500));
+ SendDlgItemMessage(hwndDlg, IDC_AUTOTIPDELAY, EM_SETLIMITTEXT, (WPARAM)5, 0);
+ {
+ UDACCEL uda = { 0, 500 };
+ SendDlgItemMessage(hwndDlg, IDC_AUTOTIPDELAYSPIN, UDM_SETACCEL, 1, (LPARAM)&uda);
+ }
+ SetDlgItemInt(hwndDlg, IDC_AUTOTIPDELAY, val, FALSE);
+ SendDlgItemMessage(hwndDlg, IDC_AUTOTIPDELAYSPIN, UDM_SETPOS, 0, MAKELPARAM(val, 0));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOTIPDELAYLABEL), settingAutoTipDelay != 0);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOTIPDELAY), settingAutoTipDelay != 0);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOTIPDELAYSPIN), settingAutoTipDelay != 0);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOTIPDELAYMS), settingAutoTipDelay != 0);
+ }
+ return TRUE;
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_AUTOTIPSENABLED:
+ {
+ BOOL checked = IsDlgButtonChecked(hwndDlg, IDC_AUTOTIPSENABLED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOTIPDELAYLABEL), checked);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOTIPDELAY), checked);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOTIPDELAYSPIN), checked);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOTIPDELAYMS), checked);
+ break;
+ }
+ case IDC_AUTOTIPDELAY:
+ if (HIWORD(wParam) == EN_KILLFOCUS) {
+ WORD minVal = HIWORD(SendDlgItemMessage(hwndDlg, IDC_AUTOTIPDELAYSPIN, UDM_GETRANGE, 0, 0));
+ if (GetDlgItemInt(hwndDlg, IDC_AUTOTIPDELAY, NULL, FALSE)<minVal) {
+ SetDlgItemInt(hwndDlg, IDC_AUTOTIPDELAY, minVal, FALSE);
+ SendDlgItemMessage(hwndDlg, IDC_AUTOTIPDELAYSPIN, UDM_SETPOS, 0, MAKELPARAM(minVal, 0));
+ }
+ }
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus())
+ return TRUE;
+ break;
+ }
+ PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); // enable apply
+ break;
+ case WM_NOTIFYFORMAT:
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, NFR_UNICODE);
+ return TRUE;
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ {
+ WORD minVal;
+ minVal = HIWORD(SendDlgItemMessage(hwndDlg, IDC_AUTOTIPDELAYSPIN, UDM_GETRANGE, 0, 0));
+ if (GetDlgItemInt(hwndDlg, IDC_AUTOTIPDELAY, NULL, FALSE) < minVal) {
+ SetDlgItemInt(hwndDlg, IDC_AUTOTIPDELAY, minVal, FALSE);
+ SendDlgItemMessage(hwndDlg, IDC_AUTOTIPDELAYSPIN, UDM_SETPOS, 0, MAKELPARAM(minVal, 0));
+ }
+ settingAutoTipDelay = (WORD)GetDlgItemInt(hwndDlg, IDC_AUTOTIPDELAY, NULL, FALSE);
+ db_set_w(NULL, "HelpPlugin", "AutoTipDelay", settingAutoTipDelay);
+
+ if (!IsDlgButtonChecked(hwndDlg, IDC_AUTOTIPSENABLED))
+ settingAutoTipDelay = 0;
+ db_set_b(NULL, "HelpPlugin", "AutoTipsEnabled", (BYTE)(settingAutoTipDelay != 0));
+ return TRUE;
+ }
+ }
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+/**************************** INIT ********************************/
+
+static int HelpOptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.position = 1200000080;
+ odp.hInstance = hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_LANG);
+ odp.ptszGroup = LPGENT("Customize"); // autotranslated
+ odp.ptszTitle = LPGENT("Help"); // autotranslated
+
+ odp.ptszTab = LPGENT("Language"); // autotranslated
+ odp.flags = ODPF_TCHAR | ODPF_BOLDGROUPS;
+ odp.pfnDlgProc = LangOptDlgProc;
+ Options_AddPage(wParam, &odp);
+
+#ifdef EDITOR
+ odp.ptszTab = _T("Help Editor"); /* autotranslated */
+#else
+ odp.ptszTab = _T("Advanced"); /* autotranslated */
+#endif
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_ADV);
+ odp.pfnDlgProc = AdvOptDlgProc;
+ Options_AddPage(wParam, &odp);
+
+ return 0;
+}
+
+void InitOptions(void)
+{
+ settingAutoTipDelay = 0;
+ if (db_get_b(NULL, "HelpPlugin", "AutoTipsEnabled", SETTING_AUTOTIPSENABLED_DEFAULT))
+ settingAutoTipDelay = db_get_w(NULL, "HelpPlugin", "AutoTipDelay", SETTING_AUTOTIPDELAY_DEFAULT);
+
+ hwndLangOpt = NULL;
+ hHookOptInit = HookEvent(ME_OPT_INITIALISE, HelpOptInit);
+ CorrectPacks(_T("helppack_*.txt"), _T("helppack_english.txt"), FALSE);
+}
+
+void UninitOptions(void)
+{
+ UnhookEvent(hHookOptInit);
+}
\ No newline at end of file diff --git a/plugins/ContextHelp/src/resource.h b/plugins/ContextHelp/src/resource.h new file mode 100644 index 0000000000..e9f9b0622f --- /dev/null +++ b/plugins/ContextHelp/src/resource.h @@ -0,0 +1,61 @@ +//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDD_HELP 101
+#define IDD_SHADOW 102
+#define IDD_OPT_LANG 103
+#define IDD_OPT_ADV 104
+#define IDD_UPDATENOTIFY 105
+#define IDD_DOWNLOADLANG 106
+#define IDC_CTLTEXT 1001
+#define IDC_TEXT 1002
+#define IDC_CTLTYPE 1003
+#define IDC_CTLID 1004
+#define IDC_MODULE 1005
+#define IDC_DLGID 1006
+#define IDC_CARETSUCKER 1007
+#define IDC_LANGLIST 1008
+#define IDC_LANGINFOFRAME 1009
+#define IDC_LANGAUTHORSLABEL 1010
+#define IDC_LANGAUTHORS 1011
+#define IDC_LANGEMAILLABEL 1012
+#define IDC_LANGEMAIL 1013
+#define IDC_LANGMODUSINGLABEL 1014
+#define IDC_LANGMODUSING 1015
+#define IDC_LANGDATELABEL 1016
+#define IDC_LANGDATE 1017
+#define IDC_LANGVERSIONLABEL 1018
+#define IDC_LANGVERSION 1019
+#define IDC_LANGLOCALELABEL 1020
+#define IDC_LANGLOCALE 1021
+#define IDC_LANGNOTINCLUDEDLABEL 1022
+#define IDC_LANGNOTINCLUDED 1023
+#define IDC_MORELANG 1024
+#define IDC_NOPACK 1025
+#define IDC_ENABLEHELPUPDATES 1026
+#define IDC_DOWNLOADLANG 1027
+#define IDC_LANGCOMBO 1028
+#define IDC_DOWNLOADALL 1029
+#define IDC_LOADING 1030
+#define IDC_LANGUAGELABEL 1031
+#define IDC_LANGUAGE 1032
+#define IDC_CURRENTVERSION 1033
+#define IDC_NEWVERSION 1034
+#define IDC_NEWVERSIONLABEL 1035
+#define IDC_AUTOTIPSENABLED 1036
+#define IDC_AUTOTIPDELAY 1037
+#define IDC_AUTOTIPDELAYSPIN 1038
+#define IDC_AUTOTIPDELAYLABEL 1039
+#define IDC_AUTOTIPDELAYMS 1040
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 107
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1041
+#define _APS_NEXT_SYMED_VALUE 2001
+#endif
+#endif
diff --git a/plugins/ContextHelp/src/stdafx.cxx b/plugins/ContextHelp/src/stdafx.cxx new file mode 100644 index 0000000000..0830d91bb3 --- /dev/null +++ b/plugins/ContextHelp/src/stdafx.cxx @@ -0,0 +1,18 @@ +/*
+Copyright (C) 2012-16 Miranda NG project (http://miranda-ng.org)
+
+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 version 2
+of the License.
+
+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. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
diff --git a/plugins/ContextHelp/src/stdafx.h b/plugins/ContextHelp/src/stdafx.h new file mode 100644 index 0000000000..20cac3503d --- /dev/null +++ b/plugins/ContextHelp/src/stdafx.h @@ -0,0 +1,52 @@ +/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 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 (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef CONTEXTHELP_COMMONHEADERS_H
+#define CONTEXTHELP_COMMONHEADERS_H
+
+#include <windows.h>
+#include <Uxtheme.h>
+#include <Vsstyle.h>
+
+#include <newpluginapi.h>
+//#include <win2k.h>
+#include <m_system_cpp.h>
+#include <m_options.h>
+#include <m_database.h>
+#include <m_utils.h>
+#include <m_langpack.h>
+#include <m_utils.h>
+#include <m_flags.h>
+#include <m_netlib.h>
+#include <m_skin.h>
+#include <m_icolib.h>
+#include <m_clist.h>
+
+//#define EDITOR
+#include <m_help.h>
+#include "help.h"
+#include "version.h"
+#include "resource.h"
+
+extern HINSTANCE hInstance;
+
+#define USERAGENT_VERSION "0.2.1.2"
+
+
+#endif //CONTEXTHELP_COMMONHEADERS_H
\ No newline at end of file diff --git a/plugins/ContextHelp/src/streaminout.cpp b/plugins/ContextHelp/src/streaminout.cpp new file mode 100644 index 0000000000..460bc655c2 --- /dev/null +++ b/plugins/ContextHelp/src/streaminout.cpp @@ -0,0 +1,771 @@ +/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 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 (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "stdafx.h"
+
+
+#include <richedit.h>
+
+struct EditStreamData {
+ PBYTE pbBuff;
+ int cbBuff;
+ int iCurrent;
+};
+
+#ifndef EDITOR
+struct HyperlinkData {
+ CHARRANGE range;
+ char *szLink;
+} static *hyperlink = NULL;
+static int hyperlinkCount = 0;
+#endif
+
+static DWORD CALLBACK EditStreamInRtf(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
+{
+ struct EditStreamData *esd = (struct EditStreamData*)dwCookie;
+ *pcb = min(esd->cbBuff - esd->iCurrent, cb);
+ CopyMemory(pbBuff, esd->pbBuff, *pcb);
+ esd->iCurrent += *pcb;
+
+ return 0;
+}
+
+#ifdef EDITOR
+static DWORD CALLBACK EditStreamOutRtf(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
+{
+ struct EditStreamData *esd = (struct EditStreamData*)dwCookie;
+ PBYTE buf = (PBYTE)mir_realloc(esd->pbBuff, esd->cbBuff + cb + 1);
+ if (buf == NULL)
+ return 0;
+ esd->pbBuff = buf;
+ esd->cbBuff += cb;
+ CopyMemory(esd->pbBuff + esd->iCurrent, pbBuff, cb);
+ esd->iCurrent += cb;
+ esd->pbBuff[esd->iCurrent] = '\0';
+ *pcb = cb;
+
+ return 0;
+}
+#endif
+
+struct {
+ const char *szSym;
+ char ch;
+} static const htmlSymbolChars[] = {
+ { "lt", '<' },
+ { "gt", '>' },
+ { "amp", '&' },
+ { "quot", '\"' },
+ { "nbsp", ' ' },
+};
+
+struct {
+ const char *szName;
+ const char *szClr;
+} static const htmlColourNames[] = {
+ { "black", "000000" },
+ { "maroon", "800000" },
+ { "green", "008000" },
+ { "olive", "808000" },
+ { "navy", "000080" },
+ { "purple", "800080" },
+ { "teal", "008080" },
+ { "silver", "C0C0C0" },
+ { "gray", "808080" },
+ { "red", "FF0000" },
+ { "lime", "00FF00" },
+ { "yellow", "FFFF00" },
+ { "blue", "0000FF" },
+ { "fuchsia", "FF00FF" },
+ { "aqua", "00FFFF" },
+ { "white", "FFFFFF" },
+};
+
+// a quick test to see who's read their comp.lang.c FAQ:
+#define stringize2(n) #n
+#define stringize(n) stringize2(n)
+
+struct {
+ const char *szHtml;
+ const char *szRtf;
+} static const simpleHtmlRtfConversions[] = {
+ { "i", "i" },
+ { "/i", "i0" },
+ { "b", "b" },
+ { "/b", "b0" },
+ { "u", "ul" },
+ { "/u", "ul0" },
+ { "big", "fs" stringize(TEXTSIZE_BIG) },
+ { "/big", "fs" stringize(TEXTSIZE_NORMAL) },
+ { "small", "fs" stringize(TEXTSIZE_SMALL) },
+ { "/small", "fs" stringize(TEXTSIZE_NORMAL) },
+ { "/font", "cf0" }
+};
+
+// mir_free() the return value
+char *GetHtmlTagAttribute(const char *pszTag, const char *pszAttr)
+{
+ int iAttrName, iAttrNameEnd, iAttrEquals, iAttrValue, iAttrValueEnd, iAttrEnd;
+ int attrLen = lstrlenA(pszAttr);
+
+ for (iAttrName = 0; !isspace(pszTag[iAttrName]) && pszTag[iAttrName] != '>'; iAttrName++);
+ for (;;) {
+ for (; isspace(pszTag[iAttrName]); iAttrName++);
+ if (pszTag[iAttrName] == '>' || pszTag[iAttrName] == '\0')
+ break;
+ for (iAttrNameEnd = iAttrName; isalnum(pszTag[iAttrNameEnd]); iAttrNameEnd++);
+ for (iAttrEquals = iAttrNameEnd; isspace(pszTag[iAttrEquals]); iAttrEquals++);
+ if (pszTag[iAttrEquals] != '=') {
+ iAttrName = iAttrEquals;
+ continue;
+ }
+ for (iAttrValue = iAttrEquals + 1; isspace(pszTag[iAttrValue]); iAttrValue++);
+ if (pszTag[iAttrValue] == '>' || pszTag[iAttrValue] == '\0')
+ break;
+ if (pszTag[iAttrValue] == '"' || pszTag[iAttrValue] == '\'') {
+ for (iAttrValueEnd = iAttrValue + 1; pszTag[iAttrValueEnd] && pszTag[iAttrValueEnd] != pszTag[iAttrValue]; iAttrValueEnd++);
+ iAttrValue++;
+ iAttrEnd = iAttrValueEnd + 1;
+ }
+ else {
+ for (iAttrValueEnd = iAttrValue; pszTag[iAttrValueEnd] && pszTag[iAttrValueEnd] != '>' && !isspace(pszTag[iAttrValueEnd]); iAttrValueEnd++);
+ iAttrEnd = iAttrValueEnd;
+ }
+ if (pszTag[iAttrValueEnd] == '\0')
+ break;
+ if (attrLen == iAttrNameEnd - iAttrName && !_strnicmp(pszAttr, pszTag + iAttrName, attrLen)) {
+ char *szValue;
+ szValue = (char*)mir_alloc(iAttrValueEnd - iAttrValue + 1);
+ if (szValue != NULL) {
+ CopyMemory(szValue, pszTag + iAttrValue, iAttrValueEnd - iAttrValue);
+ szValue[iAttrValueEnd - iAttrValue] = '\0';
+ }
+ return szValue;
+ }
+ iAttrName = iAttrEnd;
+ }
+ return NULL;
+}
+
+void StreamInHtml(HWND hwndEdit, const char *szHtml, UINT codepage, COLORREF clrBkgrnd)
+{
+ EDITSTREAM stream;
+ struct EditStreamData esd;
+ struct ResizableCharBuffer header, body;
+ COLORREF *colourTbl = NULL;
+ int colourTblCount = 0;
+ const char *pszHtml;
+ char *szThisTagHref = NULL;
+ int keywordAtBeginning = 1, paragraphBefore = 0, lineBreakBefore = 1;
+ int charCount = 0;
+
+ ZeroMemory(&stream, sizeof(stream));
+ ZeroMemory(&esd, sizeof(esd));
+ ZeroMemory(&header, sizeof(header));
+ ZeroMemory(&body, sizeof(body));
+
+#ifndef EDITOR
+ FreeHyperlinkData();
+#endif
+ AppendToCharBuffer(&header, "{\\rtf1\\ansi\\ansicpg%u\\deff0{\\fonttbl{\\f0 Tahoma;}}", codepage);
+ for (pszHtml = szHtml; *pszHtml != '\0';) {
+ if (*pszHtml == '<') {
+ const char *pszTagEnd;
+ int iNameEnd, i;
+ char szTagName[16];
+
+ pszTagEnd = strchr(pszHtml + 1, '>');
+ if (pszTagEnd == NULL)
+ break;
+ for (iNameEnd = 1; pszHtml[iNameEnd] != '\0' && pszHtml[iNameEnd] != '>' && !isspace(pszHtml[iNameEnd]); iNameEnd++);
+ CopyMemory(szTagName, pszHtml + 1, min(sizeof(szTagName), iNameEnd));
+ szTagName[min(sizeof(szTagName), iNameEnd) - 1] = '\0';
+
+ for (i = 0; i < sizeof(simpleHtmlRtfConversions); i++) {
+ if (!lstrcmpiA(szTagName, simpleHtmlRtfConversions[i].szHtml)) {
+ AppendToCharBuffer(&body, "\\%s ", simpleHtmlRtfConversions[i].szRtf);
+ break;
+ }
+ }
+ if (i == sizeof(simpleHtmlRtfConversions)) {
+ if (!lstrcmpiA(szTagName, "br")) {
+ AppendToCharBuffer(&body, "\\par ");
+ charCount++; // linebreaks are characters
+ lineBreakBefore = 1;
+ }
+ else if (!lstrcmpiA(szTagName, "p") || !lstrcmpiA(szTagName, "/p")) {
+ if (charCount)
+ paragraphBefore = 1;
+ }
+ else if (!lstrcmpiA(szTagName, "a")) {
+ mir_free(szThisTagHref); // does NULL check
+ szThisTagHref = GetHtmlTagAttribute(pszHtml, "href");
+#ifdef EDITOR
+ if (szThisTagHref != NULL)
+ AppendToCharBuffer(&body, "\\strike ");
+#else
+ if (szThisTagHref != NULL) {
+ struct HyperlinkData *buf = (struct HyperlinkData*)mir_realloc(hyperlink, sizeof(struct HyperlinkData)*(hyperlinkCount + 1));
+ if (buf != NULL) { // hyperlinkCount increased at </a>
+ hyperlink = buf;
+ hyperlink[hyperlinkCount].range.cpMin = paragraphBefore ? (charCount + 2) : charCount;
+ hyperlink[hyperlinkCount].range.cpMax = -1;
+ hyperlink[hyperlinkCount].szLink = NULL;
+ }
+ else {
+ mir_free(szThisTagHref);
+ szThisTagHref = NULL;
+ }
+ }
+#endif
+ }
+ else if (!lstrcmpiA(szTagName, "/a")) {
+ if (szThisTagHref) {
+#ifdef EDITOR
+ AppendToCharBuffer(&body, ":%s\\strike0 ", szThisTagHref);
+ mir_free(szThisTagHref);
+#else
+ mir_utf8decodecp(szThisTagHref, CP_ACP, NULL);
+ hyperlink[hyperlinkCount].range.cpMax = charCount;
+ hyperlink[hyperlinkCount].szLink = szThisTagHref;
+ hyperlinkCount++;
+#endif
+ szThisTagHref = NULL;
+ }
+ }
+ else if (!lstrcmpiA(szTagName, "font")) {
+ char *szColour = GetHtmlTagAttribute(pszHtml, "color");
+ if (szColour != NULL) {
+ int i, freeColour = 1;
+ if (szColour[0] != '#' || lstrlenA(szColour) != 7) {
+ for (i = 0; i < sizeof(htmlColourNames); i++) {
+ if (!lstrcmpiA(szColour, htmlColourNames[i].szName)) {
+ mir_free(szColour);
+ szColour = (char*)htmlColourNames[i].szClr;
+ freeColour = 0;
+ break;
+ }
+ }
+ }
+ else
+ szColour++;
+ if (szColour != NULL) {
+ COLORREF colour;
+ char szRed[3], szGreen[3], szBlue[3];
+ szRed[0] = szColour[0]; szRed[1] = szColour[1]; szRed[2] = '\0';
+ szGreen[0] = szColour[2]; szGreen[1] = szColour[3]; szGreen[2] = '\0';
+ szBlue[0] = szColour[4]; szBlue[1] = szColour[5]; szBlue[2] = '\0';
+ colour = RGB(strtol(szRed, NULL, 16), strtol(szGreen, NULL, 16), strtol(szBlue, NULL, 16));
+ if (freeColour)
+ mir_free(szColour);
+#ifndef EDITOR
+ if (colour != clrBkgrnd) { // ensure color is visible
+#else
+ UNREFERENCED_PARAMETER(clrBkgrnd);
+#endif // !defined EDITOR
+ for (i = 0; i < colourTblCount; i++)
+ if (colourTbl[i] == colour)
+ break;
+ if (i == colourTblCount) {
+ COLORREF *buf = (COLORREF*)mir_realloc(colourTbl, (colourTblCount + 1)*sizeof(COLORREF));
+ if (buf != NULL) {
+ colourTbl = buf;
+ colourTblCount++;
+ colourTbl[i] = colour;
+ }
+ }
+ AppendToCharBuffer(&body, "\\cf%d ", i + 2);
+#ifndef EDITOR
+ }
+#endif
+ }
+ }
+ } // endif font
+ }
+ pszHtml = pszTagEnd + 1;
+ }
+ else if (*pszHtml == '&') {
+ const char *pszTagEnd;
+ char szTag[16];
+ int i;
+
+ pszTagEnd = strchr(pszHtml + 1, ';');
+ if (pszTagEnd == NULL)
+ break;
+ CopyMemory(szTag, pszHtml + 1, min(sizeof(szTag), pszTagEnd - pszHtml));
+ szTag[min(sizeof(szTag), pszTagEnd - pszHtml) - 1] = '\0';
+ if (szTag[0] == '#') {
+ int ch;
+ if (szTag[1] == 'x' || szTag[1] == 'X')
+ ch = strtol(szTag + 2, NULL, 16);
+ else
+ ch = strtol(szTag + 1, NULL, 10);
+ if (ch >= 0x100)
+ AppendToCharBuffer(&body, "\\u%d", ch);
+ else
+ AppendToCharBuffer(&body, "\\'%02x ", ch);
+ }
+ else {
+ for (i = 0; i < sizeof(htmlSymbolChars); i++) {
+ if (!lstrcmpiA(szTag, htmlSymbolChars[i].szSym)) {
+ AppendCharToCharBuffer(&body, htmlSymbolChars[i].ch);
+ charCount++;
+ break;
+ }
+ }
+ }
+ pszHtml = pszTagEnd + 1;
+ }
+ else if (*pszHtml != ' ' || (!lineBreakBefore && !paragraphBefore)) {
+ lineBreakBefore = 0;
+ if (paragraphBefore) {
+ AppendToCharBuffer(&body, "\\par\\par ");
+ charCount += 2; // linebreaks are characters
+ paragraphBefore = 0;
+ }
+ if ((BYTE)*pszHtml >= ' ')
+ charCount++;
+ if (*pszHtml == '\\' || *pszHtml == '{' || *pszHtml == '}')
+ AppendCharToCharBuffer(&body, '\\');
+ AppendCharToCharBuffer(&body, *pszHtml++);
+ }
+ else
+ pszHtml++;
+ }
+ mir_free(szThisTagHref); // does NULL check
+
+ {
+ COLORREF clr = GetSysColorBrush(COLOR_HOTLIGHT) ? GetSysColor(COLOR_HOTLIGHT) : RGB(0, 0, 255);
+ AppendToCharBuffer(&header, "{\\colortbl ;\\red%d\\green%d\\blue%d;", GetRValue(clr), GetGValue(clr), GetBValue(clr));
+ for (int i = 0; i < colourTblCount; i++)
+ AppendToCharBuffer(&header, "\\red%d\\green%d\\blue%d;", GetRValue(colourTbl[i]), GetGValue(colourTbl[i]), GetBValue(colourTbl[i]));
+ }
+ AppendToCharBuffer(&header, "}\\pard\\fs16\\uc0");
+ if (keywordAtBeginning)
+ AppendCharToCharBuffer(&header, ' ');
+ mir_free(colourTbl); // does NULL check
+
+ if (header.sz != NULL) {
+ AppendToCharBuffer(&header, "%s}", body.sz ? body.sz : "");
+ esd.pbBuff = (PBYTE)header.sz;
+ esd.cbBuff = header.iEnd;
+ stream.dwCookie = (DWORD)&esd;
+ stream.pfnCallback = (EDITSTREAMCALLBACK)EditStreamInRtf;
+ SendMessage(hwndEdit, EM_STREAMIN, SF_RTF, (LPARAM)&stream);
+ mir_free(header.sz);
+ }
+ mir_free(body.sz); // does NULL check
+
+#ifndef EDITOR
+ CHARFORMAT cf;
+ ZeroMemory(&cf, sizeof(cf));
+ cf.cbSize = sizeof(cf);
+ cf.dwMask = CFM_UNDERLINE | CFM_COLOR; // CFE_LINK always uses RGB(0,0,255) instead of GetSysColor(COLOR_HOTLIGHT)
+ cf.dwEffects = CFE_UNDERLINE; // and ignores CFM_COLOR, so selfimplementing
+ cf.crTextColor = GetSysColorBrush(COLOR_HOTLIGHT) ? GetSysColor(COLOR_HOTLIGHT) : RGB(0, 0, 255);
+ for (int i = 0; i < hyperlinkCount; i++) {
+ SendMessage(hwndEdit, EM_EXSETSEL, 0, (LPARAM)&hyperlink[i].range);
+ SendMessage(hwndEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+ }
+ SendMessage(hwndEdit, EM_SETSEL, 0, 0);
+#endif
+}
+
+#ifndef EDITOR
+void FreeHyperlinkData(void)
+{
+ for (int i = 0; i<hyperlinkCount; i++)
+ mir_free(hyperlink[i].szLink); // does NULL check
+ mir_free(hyperlink); // does NULL check
+ hyperlink = NULL;
+ hyperlinkCount = 0;
+}
+
+int IsHyperlink(LONG cpPos, LONG *pcpMin, LONG *pcpMax, char **ppszLink)
+{
+ for (int i = 0; i<hyperlinkCount; i++)
+ if (cpPos >= hyperlink[i].range.cpMin && cpPos <= hyperlink[i].range.cpMax) {
+ if (pcpMin)
+ *pcpMin = hyperlink[i].range.cpMin;
+ if (pcpMax)
+ *pcpMax = hyperlink[i].range.cpMax;
+ if (ppszLink)
+ *ppszLink = hyperlink[i].szLink;
+
+ return 1;
+ }
+ if (pcpMin)
+ *pcpMin = -1;
+ if (pcpMax)
+ *pcpMax = -1;
+ if (ppszLink)
+ *ppszLink = NULL;
+
+ return 0;
+}
+
+#endif // !defined EDITOR
+
+#ifdef EDITOR
+struct RtfGroupStackData {
+ BYTE bold, italic, underline, strikeout;
+ BYTE isDestination, isColourTbl, isFontTbl;
+ int colour;
+ int fontSize;
+ int unicodeSkip;
+ int charset;
+};
+
+char *StreamOutHtml(HWND hwndEdit)
+{
+ EDITSTREAM stream;
+ struct EditStreamData esd;
+ struct ResizableCharBuffer htmlOut, hyperlink, *output;
+ COLORREF *colourTbl = NULL;
+ int colourTblCount = 0;
+ struct RtfGroupStackData *groupStack;
+ int groupLevel;
+ int inFontTag = 0, inAnchorTag = 0, inBigTag = 0, inSmallTag = 0, lineBreakBefore = 0;
+ char *pszRtf;
+ int *fontTblCharsets = NULL;
+ int fontTblCount = 0;
+ int normalTextSize = 0;
+ void *buf;
+
+ ZeroMemory(&stream, sizeof(stream));
+ ZeroMemory(&esd, sizeof(esd));
+ ZeroMemory(&htmlOut, sizeof(htmlOut));
+ ZeroMemory(&hyperlink, sizeof(hyperlink));
+ ZeroMemory(&output, sizeof(output));
+
+ stream.dwCookie = (DWORD)&esd;
+ stream.pfnCallback = EditStreamOutRtf;
+ SendMessage(hwndEdit, EM_STREAMOUT, (WPARAM)(CP_UTF8 << 16) | SF_USECODEPAGE | SF_RTFNOOBJS | SFF_PLAINRTF, (LPARAM)&stream);
+ if (esd.pbBuff == NULL)
+ return NULL;
+
+ output = &htmlOut;
+ groupStack = (struct RtfGroupStackData*)mir_calloc(sizeof(struct RtfGroupStackData));
+ if (groupStack != NULL) {
+ groupLevel = 0;
+ groupStack[0].unicodeSkip = 1;
+ for (pszRtf = (char*)esd.pbBuff; *pszRtf != '\0';) {
+ if (*pszRtf == '{') {
+ buf = (struct RtfGroupStackData*)mir_realloc(groupStack, sizeof(struct RtfGroupStackData)*(groupLevel + 2));
+ if (buf == NULL)
+ break;
+ groupStack = (struct RtfGroupStackData*)buf;
+ groupStack[groupLevel] = groupStack[groupLevel];
+ groupLevel++;
+ pszRtf++;
+ }
+ else if (*pszRtf == '}') {
+ groupLevel--;
+ if (groupStack[groupLevel].bold != groupStack[groupLevel + 1].bold)
+ AppendToCharBuffer(output, groupStack[groupLevel].bold ? "<b>" : "</b>");
+ if (groupStack[groupLevel].italic != groupStack[groupLevel + 1].italic)
+ AppendToCharBuffer(output, groupStack[groupLevel].bold ? "<i>" : "</i>");
+ if (groupStack[groupLevel].underline != groupStack[groupLevel + 1].underline)
+ AppendToCharBuffer(output, groupStack[groupLevel].bold ? "<u>" : "</u>");
+ if (groupStack[groupLevel].strikeout != groupStack[groupLevel + 1].strikeout && groupStack[groupLevel + 1].strikeout)
+ if (inAnchorTag) {
+ AppendToCharBuffer(output, "</a>");
+ inAnchorTag = 0;
+ }
+ if (groupStack[groupLevel].colour != groupStack[groupLevel + 1].colour)
+ if (inFontTag) {
+ AppendToCharBuffer(output, "</font>");
+ inFontTag = 0;
+ }
+ if (groupStack[groupLevel].fontSize != groupStack[groupLevel + 1].fontSize) {
+ if (inBigTag) {
+ AppendToCharBuffer(output, "</big>");
+ inBigTag = 0;
+ }
+ if (inSmallTag) {
+ AppendToCharBuffer(output, "</small>");
+ inSmallTag = 0;
+ }
+ if (groupStack[groupLevel].fontSize<normalTextSize) {
+ AppendToCharBuffer(output, "<small>");
+ inSmallTag = 1;
+ }
+ else if (groupStack[groupLevel].fontSize>normalTextSize) {
+ AppendToCharBuffer(output, "<big>"); inBigTag = 1;
+ }
+ }
+ if (groupLevel == 0)
+ break;
+ pszRtf++;
+ }
+ else if (*pszRtf == '\\' && pszRtf[1] == '*') {
+ groupStack[groupLevel].isDestination = 1;
+ pszRtf += 2;
+ }
+ else if (*pszRtf == '\\' && pszRtf[1] == '\'') {
+ char szHex[3] = "\0\0";
+ char szChar[2];
+ szHex[0] = pszRtf[2];
+ if (pszRtf[2])
+ szHex[1] = pszRtf[3];
+ else
+ pszRtf--;
+ szChar[0] = (char)strtol(szHex, NULL, 16); szChar[1] = '\0';
+ if (groupStack[groupLevel].charset) {
+ WCHAR szwChar[2];
+ CHARSETINFO csi;
+ TranslateCharsetInfo((PDWORD)groupStack[groupLevel].charset, &csi, TCI_SRCCHARSET);
+ MultiByteToWideChar(csi.ciACP, 0, szChar, 1, szwChar, 2);
+ AppendToCharBuffer(output, "&#%u;", (WORD)szwChar[0]);
+ }
+ else
+ AppendToCharBuffer(output, "&#%u;", (BYTE)szChar[0]);
+ pszRtf += 4;
+ }
+ else if (*pszRtf == '\\' && isalpha(pszRtf[1])) {
+ char szControlWord[32];
+ int iWordEnd;
+ int hasParam = 0;
+ int param = -1;
+
+ for (iWordEnd = 1; isalpha(pszRtf[iWordEnd]); iWordEnd++);
+ CopyMemory(szControlWord, pszRtf + 1, min(sizeof(szControlWord), iWordEnd));
+ szControlWord[min(sizeof(szControlWord), iWordEnd) - 1] = '\0';
+ if (isdigit(pszRtf[iWordEnd]) || pszRtf[iWordEnd] == '-') {
+ hasParam = 1;
+ param = strtol(pszRtf + iWordEnd, &pszRtf, 10);
+ }
+ else
+ pszRtf = pszRtf + iWordEnd;
+ if (*pszRtf == ' ')
+ pszRtf++;
+ if (!lstrcmpiA(szControlWord, "colortbl")) {
+ groupStack[groupLevel].isColourTbl = 1;
+ buf = (COLORREF*)mir_realloc(colourTbl, sizeof(COLORREF));
+ if (buf != NULL) {
+ colourTbl = (COLORREF*)buf;
+ colourTblCount = 1;
+ colourTbl[0] = 0;
+ }
+ groupStack[groupLevel].isDestination = 1;
+ }
+ else if (!lstrcmpiA(szControlWord, "fonttbl")) {
+ groupStack[groupLevel].isFontTbl = 1;
+ groupStack[groupLevel].isDestination = 1;
+ }
+ else if (!lstrcmpiA(szControlWord, "stylesheet")) {
+ groupStack[groupLevel].isDestination = 1;
+ }
+ else if (!lstrcmpiA(szControlWord, "red")) {
+ if (!hasParam || !colourTblCount)
+ break;
+ colourTbl[colourTblCount - 1] &= ~RGB(255, 0, 0);
+ colourTbl[colourTblCount - 1] |= RGB(param, 0, 0);
+ }
+ else if (!lstrcmpiA(szControlWord, "green")) {
+ if (!hasParam || !colourTblCount)
+ break;
+ colourTbl[colourTblCount - 1] &= ~RGB(0, 255, 0);
+ colourTbl[colourTblCount - 1] |= RGB(0, param, 0);
+ }
+ else if (!lstrcmpiA(szControlWord, "blue")) {
+ if (!hasParam || !colourTblCount)
+ break;
+ colourTbl[colourTblCount - 1] &= ~RGB(0, 0, 255);
+ colourTbl[colourTblCount - 1] |= RGB(0, 0, param);
+ }
+ else if (!lstrcmpiA(szControlWord, "f")) {
+ if (groupStack[groupLevel].isFontTbl) {
+ buf = (int*)mir_realloc(fontTblCharsets, sizeof(int)*(fontTblCount + 1));
+ if (buf != NULL) {
+ fontTblCharsets = (int*)buf;
+ fontTblCharsets[fontTblCount] = 0;
+ fontTblCount++;
+ }
+ }
+ else {
+ if (hasParam && param >= 0 && param<fontTblCount)
+ groupStack[groupLevel].charset = fontTblCharsets[param];
+ }
+ }
+ else if (!lstrcmpiA(szControlWord, "fcharset")) {
+ if (groupStack[groupLevel].isFontTbl && fontTblCount && hasParam)
+ fontTblCharsets[fontTblCount - 1] = param;
+ }
+ else if (!lstrcmpiA(szControlWord, "cf")) {
+ if (inFontTag)
+ AppendToCharBuffer(output, "</font>");
+ if (hasParam && param) {
+ int i;
+ char szColour[7];
+ wsprintfA(szColour, "%02x%02x%02x", GetRValue(colourTbl[param]), GetGValue(colourTbl[param]), GetBValue(colourTbl[param]));
+ for (i = 0; i < sizeof(htmlColourNames); i++) {
+ if (!lstrcmpiA(szColour, htmlColourNames[i].szClr)) {
+ AppendToCharBuffer(output, "<font color=\"%s\">", htmlColourNames[i].szName);
+ break;
+ }
+ }
+ if (i == sizeof(htmlColourNames))
+ AppendToCharBuffer(output, "<font color=\"#%s\">", szColour);
+ inFontTag = 1;
+ groupStack[groupLevel].colour = param;
+ }
+ else
+ groupStack[groupLevel].colour = 0;
+ }
+ else if (!lstrcmpiA(szControlWord, "fs")) {
+ if (normalTextSize == 0 && hasParam) {
+ normalTextSize = param;
+ groupStack[0].fontSize = normalTextSize;
+ }
+ if (inBigTag) {
+ AppendToCharBuffer(output, "</big>");
+ inBigTag = 0;
+ }
+ if (inSmallTag) {
+ AppendToCharBuffer(output, "</small>");
+ inSmallTag = 0;
+ }
+ if (hasParam) {
+ groupStack[groupLevel].fontSize = param;
+ if (groupStack[groupLevel].fontSize<normalTextSize) {
+ AppendToCharBuffer(output, "<small>"); inSmallTag = 1;
+ }
+ else if (groupStack[groupLevel].fontSize>normalTextSize) {
+ AppendToCharBuffer(output, "<big>"); inBigTag = 1;
+ }
+ }
+ }
+ else if (!lstrcmpiA(szControlWord, "uc")) {
+ if (hasParam)
+ groupStack[groupLevel].unicodeSkip = param;
+ }
+ else if (!lstrcmpiA(szControlWord, "u")) {
+ if (hasParam) {
+ AppendToCharBuffer(output, "&#%u;", param);
+ pszRtf += groupStack[groupLevel].unicodeSkip;
+ }
+ }
+ else if (!lstrcmpiA(szControlWord, "b")) {
+ if (!hasParam || param) {
+ groupStack[groupLevel].bold = 1;
+ AppendToCharBuffer(output, "<b>");
+ }
+ else {
+ groupStack[groupLevel].bold = 0;
+ AppendToCharBuffer(output, "</b>");
+ }
+ }
+ else if (!lstrcmpiA(szControlWord, "i")) {
+ if (!hasParam || param) {
+ groupStack[groupLevel].italic = 1;
+ AppendToCharBuffer(output, "<i>");
+ }
+ else {
+ groupStack[groupLevel].italic = 0;
+ AppendToCharBuffer(output, "</i>");
+ }
+ }
+ else if (!lstrcmpiA(szControlWord, "ul")) {
+ if (!hasParam || param) {
+ groupStack[groupLevel].underline = 1;
+ AppendToCharBuffer(output, "<u>");
+ }
+ else {
+ groupStack[groupLevel].underline = 0;
+ AppendToCharBuffer(output, "</u>");
+ }
+ }
+ else if (!lstrcmpiA(szControlWord, "ulnone")) {
+ groupStack[groupLevel].underline = 0;
+ AppendToCharBuffer(output, "</u>");
+ }
+ else if (!lstrcmpiA(szControlWord, "strike")) {
+ if (!hasParam || param) {
+ groupStack[groupLevel].strikeout = 1;
+ mir_free(hyperlink.sz); // does NULL check
+ hyperlink.iEnd = hyperlink.cbAlloced = 0;
+ hyperlink.sz = NULL;
+ output = &hyperlink;
+ }
+ else {
+ groupStack[groupLevel].strikeout = 0;
+ if (hyperlink.iEnd && hyperlink.sz != NULL) {
+ char *pszColon;
+ output = &htmlOut;
+ pszColon = strchr(hyperlink.sz, ':');
+ if (pszColon == NULL)
+ pszColon = "";
+ else
+ *pszColon++ = '\0';
+ AppendToCharBuffer(output, "<a href=\"%s\">%s</a>", pszColon, hyperlink.sz);
+ mir_free(hyperlink.sz);
+ hyperlink.iEnd = hyperlink.cbAlloced = 0;
+ hyperlink.sz = NULL;
+ }
+ }
+ }
+ else if (!lstrcmpiA(szControlWord, "par")) {
+ if (lineBreakBefore)
+ AppendToCharBuffer(output, "<br>");
+ lineBreakBefore = 1; // richedit puts a \par right at the end
+ }
+ }
+ else {
+ int i;
+ if (*pszRtf == '\\')
+ pszRtf++;
+ if (!groupStack[groupLevel].isDestination) {
+ if (lineBreakBefore && (BYTE)*pszRtf >= ' ') {
+ AppendToCharBuffer(output, "<br>");
+ lineBreakBefore = 0;
+ }
+ if (*pszRtf == ' ')
+ AppendCharToCharBuffer(output, *pszRtf);
+ else {
+ for (i = 0; i < sizeof(htmlSymbolChars); i++) {
+ if (*pszRtf == htmlSymbolChars[i].ch) {
+ AppendToCharBuffer(output, "&%s;", htmlSymbolChars[i].szSym);
+ break;
+ }
+ }
+ if (i == sizeof(htmlSymbolChars))
+ AppendCharToCharBuffer(output, *pszRtf);
+ }
+ }
+ else if (groupStack[groupLevel].isColourTbl && *pszRtf == ';') {
+ buf = (COLORREF*)mir_realloc(colourTbl, sizeof(COLORREF)*(colourTblCount + 2));
+ if (buf != NULL) {
+ colourTbl = (COLORREF*)buf;
+ colourTbl[colourTblCount] = 0;
+ colourTblCount++;
+ }
+ }
+ pszRtf++;
+ }
+ }
+ mir_free(groupStack);
+ }
+ mir_free(colourTbl); // does NULL check
+ mir_free(fontTblCharsets); // does NULL check
+ mir_free(hyperlink.sz); // does NULL check
+
+ mir_free(esd.pbBuff);
+ return htmlOut.sz;
+}
+#endif // defined EDITOR
diff --git a/plugins/ContextHelp/src/utils.cpp b/plugins/ContextHelp/src/utils.cpp new file mode 100644 index 0000000000..51d643c774 --- /dev/null +++ b/plugins/ContextHelp/src/utils.cpp @@ -0,0 +1,430 @@ +/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 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 (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "stdafx.h"
+
+#include <m_button.h>
+
+#define CHARBUFFER_ALLOCSTEP 512 // allocated buffer increase in bytes
+
+#ifdef EDITOR
+extern const TCHAR *szControlTypeNames[] = {
+ _T("Unknown"), _T("Dialog Box"), _T("Button"), _T("Check Box"), _T("Radio Button"),
+ _T("Text"), _T("Image"), _T("Edit Box"), _T("Group Box"), _T("Combo Box"),
+ _T("List Box"), _T("Spin Edit Box"), _T("Progress Bar"), _T("Slider"), _T("List View"),
+ _T("Tree View"), _T("Date/Time Picker"), _T("IP Address"), _T("Status Bar"), _T("Hyperlink"),
+ _T("Contact List"), _T("Scroll Bar"), _T("Animation"), _T("Hotkey"), _T("Tabs"),
+ _T("Colour Picker"), _T("Tool Bar"), _T("Combo Edit Box"), _T("Size Grip") };
+#endif
+
+int GetControlType(HWND hwndCtl)
+{
+ TCHAR szClassName[32];
+ DWORD style;
+
+ if (GetClassLong(hwndCtl, GCW_ATOM) == 32770)
+ return CTLTYPE_DIALOG;
+ if (!GetClassName(hwndCtl, szClassName, sizeof(szClassName)))
+ return CTLTYPE_UNKNOWN;
+ if (!lstrcmpi(szClassName, _T("MDIClient")))
+ return CTLTYPE_DIALOG;
+ else if (!lstrcmpi(szClassName, _T("Static"))) {
+ if (GetClassName(GetParent(hwndCtl), szClassName, sizeof(szClassName)) && !lstrcmpi(szClassName, _T("ComboBox")) || !lstrcmpi(szClassName, WC_COMBOBOXEX))
+ return CTLTYPE_COMBO;
+ style = GetWindowLongPtr(hwndCtl, GWL_STYLE);
+ switch (style&SS_TYPEMASK) {
+ case SS_BITMAP:
+ case SS_BLACKFRAME:
+ case SS_BLACKRECT:
+ case SS_ENHMETAFILE:
+ case SS_ETCHEDFRAME:
+ case SS_ETCHEDHORZ:
+ case SS_ETCHEDVERT:
+ case SS_WHITEFRAME:
+ case SS_WHITERECT:
+ case SS_GRAYFRAME:
+ case SS_GRAYRECT:
+ case SS_ICON:
+ case SS_OWNERDRAW:
+ return CTLTYPE_IMAGE;
+ }
+ return CTLTYPE_TEXT;
+ }
+ else if (GetClassLong(hwndCtl, GCW_ATOM) == 32772) // WinNT/2000/XP: icon titles
+ return CTLTYPE_IMAGE; // class="#32772"
+ else if (!lstrcmpi(szClassName, _T("Button"))) {
+ style = GetWindowLongPtr(hwndCtl, GWL_STYLE);
+ switch (style & 0x24) {
+ case BS_CHECKBOX:
+ case BS_AUTOCHECKBOX:
+ case BS_3STATE:
+ case BS_AUTO3STATE:
+ if (style&BS_PUSHLIKE)
+ break;
+ return CTLTYPE_CHECKBOX;
+ case BS_RADIOBUTTON:
+ case BS_AUTORADIOBUTTON:
+ if (style&BS_PUSHLIKE)
+ break;
+ return CTLTYPE_RADIO;
+ case BS_GROUPBOX:
+ return CTLTYPE_GROUP;
+ }
+ return CTLTYPE_BUTTON;
+ }
+ else if (!lstrcmpi(szClassName, MIRANDABUTTONCLASS))
+ return CTLTYPE_BUTTON;
+ else if (!lstrcmpi(szClassName, _T("Edit"))) {
+ if (GetClassName(GetParent(hwndCtl), szClassName, sizeof(szClassName)) && !lstrcmpi(szClassName, _T("ComboBox")))
+ return CTLTYPE_COMBO;
+ if (GetClassName(GetWindow(hwndCtl, GW_HWNDNEXT), szClassName, sizeof(szClassName)) && !lstrcmpi(szClassName, UPDOWN_CLASS))
+ if ((HWND)SendMessage(GetWindow(hwndCtl, GW_HWNDNEXT), UDM_GETBUDDY, 0, 0) == hwndCtl)
+ return CTLTYPE_SPINEDIT;
+ return CTLTYPE_EDIT;
+ }
+ else if (!_tcsnicmp(szClassName, _T("RichEdit"), 8))
+ return CTLTYPE_EDIT; // RICHEDIT,RichEdit20A,RichEdit20W,RichEdit50W and future versions
+ else if (!lstrcmpi(szClassName, _T("ListBox"))) {
+ style = GetWindowLongPtr(hwndCtl, GWL_STYLE);
+ if (style&LBS_COMBOBOX)
+ return CTLTYPE_COMBO;
+ return CTLTYPE_LIST;
+ }
+ else if (!lstrcmpi(szClassName, _T("ComboLBox")) || !lstrcmpi(szClassName, _T("ComboBox")) || !lstrcmpi(szClassName, WC_COMBOBOXEX))
+ return CTLTYPE_COMBO;
+ else if (!lstrcmpi(szClassName, _T("ScrollBar"))) {
+ style = GetWindowLongPtr(hwndCtl, GWL_STYLE);
+ if (style&SBS_SIZEBOX)
+ return CTLTYPE_SIZEGRIP;
+ return CTLTYPE_SCROLL;
+ }
+ else if (!lstrcmpi(szClassName, WC_PAGESCROLLER))
+ return CTLTYPE_SCROLL;
+ else if (!lstrcmpi(szClassName, UPDOWN_CLASS)) {
+ if (GetClassName((HWND)SendMessage(hwndCtl, UDM_GETBUDDY, 0, 0), szClassName, sizeof(szClassName)) && !lstrcmpi(szClassName, _T("Edit")))
+ return CTLTYPE_SPINEDIT;
+ return CTLTYPE_SCROLL;
+ }
+ else if (!lstrcmpi(szClassName, PROGRESS_CLASS))
+ return CTLTYPE_PROGRESS;
+ else if (!lstrcmpi(szClassName, TRACKBAR_CLASS))
+ return CTLTYPE_SLIDER;
+ else if (!lstrcmpi(szClassName, WC_LISTVIEW) || !lstrcmpi(szClassName, WC_HEADER))
+ return CTLTYPE_LISTVIEW;
+ else if (!lstrcmpi(szClassName, WC_TREEVIEW))
+ return CTLTYPE_TREEVIEW;
+ else if (!lstrcmpi(szClassName, DATETIMEPICK_CLASS) || !lstrcmpi(szClassName, MONTHCAL_CLASS))
+ return CTLTYPE_DATETIME;
+ else if (!lstrcmpi(szClassName, WC_IPADDRESS))
+ return CTLTYPE_IP;
+ else if (!lstrcmpi(szClassName, STATUSCLASSNAME))
+ return CTLTYPE_STATUSBAR;
+ else if (!lstrcmpi(szClassName, (LPCWSTR)CLISTCONTROL_CLASS))
+ return CTLTYPE_CLC; // look at !! casting
+ else if (!lstrcmpi(szClassName, WNDCLASS_HYPERLINK) || !lstrcmpi(szClassName, _T("SysLink")))
+ return CTLTYPE_HYPERLINK;
+ else if (!lstrcmpi(szClassName, ANIMATE_CLASS))
+ return CTLTYPE_ANIMATION;
+ else if (!lstrcmpi(szClassName, HOTKEY_CLASS))
+ return CTLTYPE_HOTKEY;
+ else if (!lstrcmpi(szClassName, WC_TABCONTROL))
+ return CTLTYPE_TABS;
+ else if (!lstrcmpi(szClassName, (LPCWSTR)WNDCLASS_COLOURPICKER))
+ return CTLTYPE_COLOUR; // look at !! casting
+ else if (!lstrcmpi(szClassName, TOOLBARCLASSNAME) || !lstrcmpi(szClassName, REBARCLASSNAME))
+ return CTLTYPE_TOOLBAR;
+ switch (SendMessage(hwndCtl, WM_GETDLGCODE, 0, (LPARAM)NULL) & 0x2070) {
+ case DLGC_RADIOBUTTON:
+ return CTLTYPE_RADIO;
+ case DLGC_DEFPUSHBUTTON:
+ case DLGC_UNDEFPUSHBUTTON:
+ case DLGC_BUTTON:
+ return CTLTYPE_BUTTON;
+ }
+ hwndCtl = GetWindow(hwndCtl, GW_CHILD); // check for owner-extended control
+ if (hwndCtl != NULL)
+ return GetControlType(hwndCtl);
+
+ return CTLTYPE_UNKNOWN;
+}
+
+HWND GetControlDialog(HWND hwndCtl)
+{
+ TCHAR szClassName[32];
+ while (hwndCtl != NULL) {
+ if (GetClassLong(hwndCtl, GCW_ATOM) == 32770)
+ return hwndCtl;
+ if (GetClassName(hwndCtl, szClassName, sizeof(szClassName)))
+ if (!lstrcmpi(szClassName, _T("MDIClient")))
+ return hwndCtl;
+ hwndCtl = GetParent(hwndCtl);
+ }
+
+ return hwndCtl;
+}
+
+// never fails
+int GetControlTitle(HWND hwndCtl, TCHAR *pszTitle, int cchTitle)
+{
+ TCHAR *p;
+ int res = 0;
+ if (cchTitle)
+ pszTitle[0] = _T('\0');
+ switch (GetControlType(hwndCtl)) {
+ case CTLTYPE_DIALOG:
+ case CTLTYPE_BUTTON:
+ case CTLTYPE_CHECKBOX:
+ case CTLTYPE_RADIO:
+ case CTLTYPE_GROUP:
+ case CTLTYPE_TEXT:
+ case CTLTYPE_HYPERLINK:
+ res = GetWindowText(hwndCtl, pszTitle, cchTitle);
+ }
+ hwndCtl = GetWindow(hwndCtl, GW_HWNDPREV);
+ if (hwndCtl)
+ switch (GetControlType(hwndCtl)) {
+ case CTLTYPE_TEXT:
+ case CTLTYPE_GROUP:
+ res = GetWindowText(hwndCtl, pszTitle, cchTitle);
+ }
+ if (res)
+ for (p = pszTitle; *p != 0; p++) {
+ // strip-off ampersand (&) prefix character
+ if (*p == _T('&') && *(p + 1) != _T('&')) {
+ MoveMemory(p, p + 1, (lstrlen(p + 1) + 1)*sizeof(TCHAR));
+ res--;
+ if (*(p + 1) == 0)
+ break;
+ }
+ // strip-off last ':'
+ if (*p == _T(':') && *(p + 1) == 0) {
+ *p = 0;
+ res--;
+ break;
+ }
+ }
+
+ return res;
+}
+
+// mir_free() the return value
+char *GetControlModuleName(HWND hwndCtl)
+{
+ char szModule[512], szMainModule[512];
+ char *pszFile, *buf;
+
+ if (!GetModuleFileNameA(NULL, szMainModule, sizeof(szMainModule)))
+ return NULL;
+ buf = strrchr(szMainModule, '\\');
+ if (buf != NULL)
+ *buf = '\0';
+ else
+ buf = szMainModule;
+
+ do {
+ if (!GetModuleFileNameA((HINSTANCE)GetWindowLongPtr(hwndCtl, GWLP_HINSTANCE), szModule, sizeof(szModule)))
+ return NULL;
+ pszFile = strrchr(szModule, '\\');
+ if (pszFile != NULL) {
+ *pszFile = '\0';
+ pszFile++;
+ }
+ else
+ pszFile = szModule;
+ if (lstrlenA(szModule)>lstrlenA(szMainModule))
+ szModule[lstrlenA(szMainModule)] = '\0';
+ if (!lstrcmpiA(szModule, szMainModule))
+ break; // found miranda module
+ hwndCtl = GetParent(hwndCtl);
+ } while (hwndCtl != NULL);
+
+ buf = strrchr(pszFile, '.');
+ if (buf != NULL)
+ *buf++ = '\0';
+
+ return mir_strdup(pszFile);
+}
+
+
+struct CreateControlIdData {
+ int id;
+ HWND hwndCtl;
+};
+
+static BOOL CALLBACK CreateCtlIdEnumProc(HWND hwnd, LPARAM lParam)
+{
+ struct CreateControlIdData* ccid = (struct CreateControlIdData*)lParam;
+ TCHAR szClassName[32];
+ if (GetClassLong(hwnd, GCW_ATOM) == 32770) // class="#32770"
+ return TRUE;
+ if (GetClassName(hwnd, szClassName, sizeof(szClassName)))
+ if (!lstrcmpi(szClassName, _T("MDIClient")))
+ return TRUE;
+ if (GetWindowLongPtr(hwnd, GWL_ID) <= 0 || GetWindowLongPtr(hwnd, GWL_ID) == 0xFFFF)
+ ccid->id--;
+ if (hwnd == ccid->hwndCtl)
+ ccid->hwndCtl = NULL;
+
+ return ccid->hwndCtl != NULL;
+}
+
+int GetControlID(HWND hwndCtl)
+{
+ struct CreateControlIdData ccid;
+ TCHAR szClassName[32];
+
+ // obey context ID when set (rarely)
+ ccid.id = GetWindowContextHelpId(hwndCtl);
+ if (ccid.id != 0)
+ return ccid.id;
+
+ if (GetClassName(hwndCtl, szClassName, sizeof(szClassName))) {
+ if (!lstrcmpi(szClassName, UPDOWN_CLASS)) { // handle spinner controls as a whole
+ DWORD style;
+ HWND hwndBuddy;
+ style = GetWindowLongPtr(hwndCtl, GWL_STYLE);
+ if (style&UDS_ALIGNRIGHT || style&UDS_ALIGNLEFT) {
+ hwndBuddy = (HWND)SendMessage(hwndCtl, UDM_GETBUDDY, 0, 0);
+ if (hwndBuddy != NULL)
+ hwndCtl = hwndBuddy;
+ }
+ }
+ else if (GetClassLong(hwndCtl, GCW_ATOM) == 32770 || !lstrcmpi(szClassName, _T("MDIClient")))
+ return 0; // ensure this is always unset
+ }
+ ccid.id = GetWindowLongPtr(hwndCtl, GWL_ID);
+ if (ccid.id <= 0 || ccid.id == 0xFFFF) {
+ ccid.id = -1;
+ ccid.hwndCtl = hwndCtl;
+ EnumChildWindows(GetParent(hwndCtl), CreateCtlIdEnumProc, (LPARAM)&ccid);
+ if (ccid.hwndCtl != NULL)
+ return -1;
+ }
+ return ccid.id;
+}
+
+// mir_free() the return value
+static char *Base64Encode(PBYTE pBuf, int cbBuf)
+{
+ /*NETLIBBASE64 nlb64;
+ nlb64.pbDecoded=pBuf;
+ nlb64.cbDecoded=cbBuf;
+ nlb64.cchEncoded=Netlib_GetBase64EncodedBufferSize(nlb64.cbDecoded);
+ nlb64.pszEncoded=(char*)mir_alloc(nlb64.cchEncoded);
+ if(nlb64.pszEncoded==NULL || !CallService(MS_NETLIB_BASE64ENCODE,0,(LPARAM)&nlb64)) {
+ mir_free(nlb64.pszEncoded); // does NULL check
+ return NULL;
+ }
+ return nlb64.pszEncoded;*/
+
+ return (char*)mir_base64_encode(pBuf, cbBuf);
+}
+
+struct CreateDialogIdBinaryData {
+ int alloced, count;
+ PBYTE buf;
+ HWND hwndParent;
+};
+
+static INT_PTR CALLBACK CreateDlgIdBinEnumProc(HWND hwnd, LPARAM lParam)
+{
+ struct CreateDialogIdBinaryData *cdib = (struct CreateDialogIdBinaryData*)lParam;
+ int type;
+
+ if (GetParent(hwnd) != cdib->hwndParent)
+ return TRUE;
+ type = GetControlType(hwnd);
+ if (type == CTLTYPE_DIALOG || type == CTLTYPE_TEXT || type == CTLTYPE_GROUP)
+ return TRUE;
+ if (cdib->count + 3>cdib->alloced) {
+ PBYTE buf2;
+ buf2 = (PBYTE)mir_realloc(cdib->buf, cdib->alloced + 32);
+ if (buf2 == NULL)
+ return FALSE;
+ cdib->alloced += 32;
+ cdib->buf = buf2;
+ }
+ cdib->buf[cdib->count] = (BYTE)type;
+ *(PWORD)(cdib->buf + cdib->count + 1) = (WORD)GetWindowLongPtr(hwnd, GWL_ID);
+ cdib->count += 3;
+
+ return TRUE;
+}
+
+// mir_free() the return value
+char *CreateDialogIdString(HWND hwndDlg)
+{
+ struct CreateDialogIdBinaryData cdib;
+ char *szRet;
+
+ ZeroMemory(&cdib, sizeof(cdib));
+ if (hwndDlg == NULL)
+ return NULL;
+ cdib.hwndParent = hwndDlg;
+ EnumChildWindows(hwndDlg, (WNDENUMPROC)CreateDlgIdBinEnumProc, (LPARAM)&cdib);
+ if (cdib.buf == NULL)
+ return NULL;
+ szRet = Base64Encode(cdib.buf, cdib.count);
+ mir_free(cdib.buf);
+
+ return szRet;
+}
+
+
+void AppendCharToCharBuffer(struct ResizableCharBuffer *rcb, char c)
+{
+ if (rcb->cbAlloced <= rcb->iEnd + 1) {
+ char* buf = (char*)mir_realloc(rcb->sz, (rcb->cbAlloced + CHARBUFFER_ALLOCSTEP));
+ if (buf == NULL)
+ return;
+ rcb->sz = buf;
+ rcb->cbAlloced += CHARBUFFER_ALLOCSTEP;
+ }
+ rcb->sz[rcb->iEnd++] = c;
+ rcb->sz[rcb->iEnd] = '\0';
+}
+
+void AppendToCharBuffer(struct ResizableCharBuffer *rcb, const char *fmt, ...)
+{
+ va_list va;
+ int charsDone;
+ char *buf;
+
+ if (rcb->cbAlloced == 0) {
+ buf = (char*)mir_alloc(CHARBUFFER_ALLOCSTEP);
+ if (buf == NULL)
+ return;
+ rcb->sz = buf;
+ rcb->cbAlloced = CHARBUFFER_ALLOCSTEP;
+ }
+ va_start(va, fmt);
+ for (;;) {
+ charsDone = mir_vsnprintf(rcb->sz + rcb->iEnd, rcb->cbAlloced - rcb->iEnd, fmt, va);
+ if (charsDone >= 0)
+ break; // returns -1 when buffer not large enough
+ buf = (char*)mir_realloc(rcb->sz, rcb->cbAlloced + CHARBUFFER_ALLOCSTEP);
+ if (buf == NULL) {
+ charsDone = 0;
+ break;
+ }
+ rcb->sz = buf;
+ rcb->cbAlloced += CHARBUFFER_ALLOCSTEP;
+ }
+ va_end(va);
+ rcb->iEnd += charsDone;
+}
diff --git a/plugins/ContextHelp/src/version.h b/plugins/ContextHelp/src/version.h new file mode 100644 index 0000000000..a1007a2174 --- /dev/null +++ b/plugins/ContextHelp/src/version.h @@ -0,0 +1,53 @@ +/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 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 (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+#define NEEDED_MIRANDA_VERSION PLUGIN_MAKE_VERSION(0,6,0,0)
+#define NEEDED_MIRANDA_VERSION_STR "0.6"
+#define PLUGIN_VERSION PLUGIN_MAKE_VERSION(0,2,1,2)
+#define FILE_VERSION 0,2,1,2
+
+#ifdef _DEBUG
+ #define FILE_VERSION_STR "0.2.1.3 alpha"
+ #define USERAGENT_VERSION "0.2.1.3 (alpha)"
+#else
+ #define FILE_VERSION_STR "0.2.1.2"
+ #define USERAGENT_VERSION "0.2.1.2"
+#endif
+
+#define PLUGIN_EMAIL "hrathh users.sourceforge.net"
+#define PLUGIN_EMAIL_ATT_POS 7 // position of the @-sign in the email adress above
+
+ #define PLUGIN_WEBSITE "http://addons.miranda-im.org/details.php?action=viewfile&id=3475"
+*/
+
+#define __MAJOR_VERSION 0
+#define __MINOR_VERSION 2
+#define __RELEASE_NUM 1
+#define __BUILD_NUM 2
+
+#include <stdver.h>
+
+#define __PLUGIN_NAME "Context help"
+#define __FILENAME "ContextHelp.dll"
+#define __DESCRIPTION "Provides context sensitive help in all of the Miranda NG dialog boxes."
+#define __AUTHOR "Richard Hughes, H. Herkenrath, Miranda NG Team"
+#define __AUTHOREMAIL ""
+#define __AUTHORWEB "http://miranda-ng.org/p/ContextHelp/"
+#define __COPYRIGHT "© 2002 Richard Hughes, 2005-2007 H. Herkenrath, 2016-16 Miranda NG Team"
|