xref: /reactos/dll/win32/hhctrl.ocx/hhctrl.c (revision c2c66aff)
1 /*
2  * hhctrl implementation
3  *
4  * Copyright 2004 Krzysztof Foltman
5  * Copyright 2007 Jacek Caban for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include "hhctrl.h"
23 
24 #include <rpcproxy.h>
25 
26 HINSTANCE hhctrl_hinstance;
27 BOOL hh_process = FALSE;
28 
29 
30 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, LPVOID lpvReserved)
31 {
32     TRACE("(%p,%d,%p)\n", hInstance, fdwReason, lpvReserved);
33 
34     switch (fdwReason)
35     {
36     case DLL_PROCESS_ATTACH:
37         hhctrl_hinstance = hInstance;
38         DisableThreadLibraryCalls(hInstance);
39         break;
40     }
41     return TRUE;
42 }
43 
44 static const char *command_to_string(UINT command)
45 {
46 #define X(x) case x: return #x
47     switch (command)
48     {
49         X( HH_DISPLAY_TOPIC );
50         X( HH_DISPLAY_TOC );
51         X( HH_DISPLAY_INDEX );
52         X( HH_DISPLAY_SEARCH );
53         X( HH_SET_WIN_TYPE );
54         X( HH_GET_WIN_TYPE );
55         X( HH_GET_WIN_HANDLE );
56         X( HH_ENUM_INFO_TYPE );
57         X( HH_SET_INFO_TYPE );
58         X( HH_SYNC );
59         X( HH_RESERVED1 );
60         X( HH_RESERVED2 );
61         X( HH_RESERVED3 );
62         X( HH_KEYWORD_LOOKUP );
63         X( HH_DISPLAY_TEXT_POPUP );
64         X( HH_HELP_CONTEXT );
65         X( HH_TP_HELP_CONTEXTMENU );
66         X( HH_TP_HELP_WM_HELP );
67         X( HH_CLOSE_ALL );
68         X( HH_ALINK_LOOKUP );
69         X( HH_GET_LAST_ERROR );
70         X( HH_ENUM_CATEGORY );
71         X( HH_ENUM_CATEGORY_IT );
72         X( HH_RESET_IT_FILTER );
73         X( HH_SET_INCLUSIVE_FILTER );
74         X( HH_SET_EXCLUSIVE_FILTER );
75         X( HH_INITIALIZE );
76         X( HH_UNINITIALIZE );
77         X( HH_SAFE_DISPLAY_TOPIC );
78         X( HH_PRETRANSLATEMESSAGE );
79         X( HH_SET_GLOBAL_PROPERTY );
80     default: return "???";
81     }
82 #undef X
83 }
84 
85 static BOOL resolve_filename(const WCHAR *filename, WCHAR *fullname, DWORD buflen, WCHAR **index, WCHAR **window)
86 {
87     const WCHAR *extra;
88     WCHAR chm_file[MAX_PATH];
89 
90     static const WCHAR helpW[] = {'\\','h','e','l','p','\\',0};
91     static const WCHAR delimW[] = {':',':',0};
92     static const WCHAR delim2W[] = {'>',0};
93 
94     filename = skip_schema(filename);
95 
96     /* the format is "helpFile[::/index][>window]" */
97     if (index) *index = NULL;
98     if (window) *window = NULL;
99 
100     extra = strstrW(filename, delim2W);
101     if (extra)
102     {
103         memcpy(chm_file, filename, (extra-filename)*sizeof(WCHAR));
104         chm_file[extra-filename] = 0;
105         filename = chm_file;
106         if (window)
107             *window = strdupW(extra+1);
108     }
109 
110     extra = strstrW(filename, delimW);
111     if (extra)
112     {
113         if (filename != chm_file)
114             memcpy(chm_file, filename, (extra-filename)*sizeof(WCHAR));
115         chm_file[extra-filename] = 0;
116         filename = chm_file;
117         if (index)
118             *index = strdupW(extra+2);
119     }
120 
121     GetFullPathNameW(filename, buflen, fullname, NULL);
122     if (GetFileAttributesW(fullname) == INVALID_FILE_ATTRIBUTES)
123     {
124         GetWindowsDirectoryW(fullname, buflen);
125         strcatW(fullname, helpW);
126         strcatW(fullname, filename);
127     }
128     return (GetFileAttributesW(fullname) != INVALID_FILE_ATTRIBUTES);
129 }
130 
131 /******************************************************************
132  *		HtmlHelpW (HHCTRL.OCX.15)
133  */
134 HWND WINAPI HtmlHelpW(HWND caller, LPCWSTR filename, UINT command, DWORD_PTR data)
135 {
136     WCHAR fullname[MAX_PATH];
137 
138     TRACE("(%p, %s, command=%s, data=%lx)\n",
139           caller, debugstr_w( filename ),
140           command_to_string( command ), data);
141 
142     switch (command)
143     {
144     case HH_DISPLAY_TOPIC:
145     case HH_DISPLAY_TOC:
146     case HH_DISPLAY_INDEX:
147     case HH_DISPLAY_SEARCH:{
148         BOOL res;
149         NMHDR nmhdr;
150         HHInfo *info = NULL;
151         WCHAR *window = NULL;
152         const WCHAR *index = NULL;
153         WCHAR *default_index = NULL;
154         int tab_index = TAB_CONTENTS;
155 
156         if (!filename)
157             return NULL;
158 
159         if (!resolve_filename(filename, fullname, MAX_PATH, &default_index, &window))
160         {
161             WARN("can't find %s\n", debugstr_w(filename));
162             return 0;
163         }
164         index = default_index;
165 
166         if (window)
167             info = find_window(window);
168 
169         info = CreateHelpViewer(info, fullname, caller);
170         if(!info)
171         {
172             heap_free(default_index);
173             heap_free(window);
174             return NULL;
175         }
176 
177         if(!index)
178             index = info->WinType.pszFile;
179         if(!info->WinType.pszType)
180             info->WinType.pszType = info->stringsW.pszType = window;
181         else
182             heap_free(window);
183 
184         /* called to load a specified topic */
185         switch(command)
186         {
187         case HH_DISPLAY_TOPIC:
188         case HH_DISPLAY_TOC:
189             if (data)
190             {
191                 static const WCHAR delimW[] = {':',':',0};
192                 const WCHAR *i = (const WCHAR *)data;
193 
194                 index = strstrW(i, delimW);
195                 if(index)
196                 {
197                     if(memcmp(info->pCHMInfo->szFile, i, index-i))
198                         FIXME("Opening a CHM file in the context of another is not supported.\n");
199                     index += strlenW(delimW);
200                 }
201                 else
202                     index = i;
203             }
204             break;
205         }
206 
207         res = NavigateToChm(info, info->pCHMInfo->szFile, index);
208         heap_free(default_index);
209 
210         if(!res)
211         {
212             ReleaseHelpViewer(info);
213             return NULL;
214         }
215 
216         switch(command)
217         {
218         case HH_DISPLAY_TOPIC:
219         case HH_DISPLAY_TOC:
220             tab_index = TAB_CONTENTS;
221             break;
222         case HH_DISPLAY_INDEX:
223             tab_index = TAB_INDEX;
224             if (data)
225                 FIXME("Should select keyword '%s'.\n", debugstr_w((WCHAR *)data));
226             break;
227         case HH_DISPLAY_SEARCH:
228             tab_index = TAB_SEARCH;
229             if (data)
230                 FIXME("Should display search specified by HH_FTS_QUERY structure.\n");
231             break;
232         }
233         /* open the requested tab */
234         memset(&nmhdr, 0, sizeof(nmhdr));
235         nmhdr.code = TCN_SELCHANGE;
236         SendMessageW(info->hwndTabCtrl, TCM_SETCURSEL, (WPARAM)info->tabs[tab_index].id, 0);
237         SendMessageW(info->WinType.hwndNavigation, WM_NOTIFY, 0, (LPARAM)&nmhdr);
238 
239         return info->WinType.hwndHelp;
240     }
241     case HH_HELP_CONTEXT: {
242         WCHAR *window = NULL;
243         HHInfo *info = NULL;
244         LPWSTR url;
245 
246         if (!filename)
247             return NULL;
248 
249         if (!resolve_filename(filename, fullname, MAX_PATH, NULL, &window))
250         {
251             WARN("can't find %s\n", debugstr_w(filename));
252             return 0;
253         }
254 
255         if (window)
256             info = find_window(window);
257 
258         info = CreateHelpViewer(info, fullname, caller);
259         if(!info)
260         {
261             heap_free(window);
262             return NULL;
263         }
264 
265         if(!info->WinType.pszType)
266             info->WinType.pszType = info->stringsW.pszType = window;
267         else
268             heap_free(window);
269 
270         url = FindContextAlias(info->pCHMInfo, data);
271         if(!url)
272         {
273             if(!data) /* there may legitimately be no context alias for id 0 */
274                 return info->WinType.hwndHelp;
275             ReleaseHelpViewer(info);
276             return NULL;
277         }
278 
279         NavigateToUrl(info, url);
280         heap_free(url);
281         return info->WinType.hwndHelp;
282     }
283     case HH_PRETRANSLATEMESSAGE: {
284         static BOOL warned = FALSE;
285 
286         if (!warned)
287         {
288             FIXME("HH_PRETRANSLATEMESSAGE unimplemented\n");
289             warned = TRUE;
290         }
291         return 0;
292     }
293     case HH_CLOSE_ALL: {
294         HHInfo *info, *next;
295 
296         LIST_FOR_EACH_ENTRY_SAFE(info, next, &window_list, HHInfo, entry)
297         {
298             TRACE("Destroying window %s.\n", debugstr_w(info->WinType.pszType));
299             ReleaseHelpViewer(info);
300         }
301         return 0;
302     }
303     case HH_SET_WIN_TYPE: {
304         HH_WINTYPEW *wintype = (HH_WINTYPEW *)data;
305         WCHAR *window = NULL;
306         HHInfo *info = NULL;
307 
308         if (!filename && wintype->pszType)
309             window = strdupW(wintype->pszType);
310         else if (!filename || !resolve_filename(filename, fullname, MAX_PATH, NULL, &window) || !window)
311         {
312             WARN("can't find window name: %s\n", debugstr_w(filename));
313             return 0;
314         }
315         info = find_window(window);
316         if (!info)
317         {
318             info = heap_alloc_zero(sizeof(HHInfo));
319             info->WinType.pszType = info->stringsW.pszType = window;
320             list_add_tail(&window_list, &info->entry);
321         }
322         else
323             heap_free(window);
324 
325         TRACE("Changing WINTYPE, fsValidMembers=0x%x\n", wintype->fsValidMembers);
326 
327         MergeChmProperties(wintype, info, TRUE);
328         UpdateHelpWindow(info);
329         return 0;
330     }
331     case HH_GET_WIN_TYPE: {
332         HH_WINTYPEW *wintype = (HH_WINTYPEW *)data;
333         WCHAR *window = NULL;
334         HHInfo *info = NULL;
335 
336         if (!filename || !resolve_filename(filename, fullname, MAX_PATH, NULL, &window) || !window)
337         {
338             WARN("can't find window name: %s\n", debugstr_w(filename));
339             return 0;
340         }
341         info = find_window(window);
342         if (!info)
343         {
344             WARN("Could not find window named %s.\n", debugstr_w(window));
345             heap_free(window);
346             return (HWND)~0;
347         }
348 
349         TRACE("Retrieving WINTYPE for %s.\n", debugstr_w(window));
350         *wintype = info->WinType;
351         heap_free(window);
352         return 0;
353     }
354     default:
355         FIXME("HH case %s not handled.\n", command_to_string( command ));
356     }
357 
358     return 0;
359 }
360 
361 static void wintypeAtoW(const HH_WINTYPEA *data, HH_WINTYPEW *wdata, struct wintype_stringsW *stringsW)
362 {
363     memcpy(wdata, data, sizeof(*data));
364     /* convert all of the ANSI strings to Unicode */
365     wdata->pszType       = stringsW->pszType       = strdupAtoW(data->pszType);
366     wdata->pszCaption    = stringsW->pszCaption    = strdupAtoW(data->pszCaption);
367     wdata->pszToc        = stringsW->pszToc        = strdupAtoW(data->pszToc);
368     wdata->pszIndex      = stringsW->pszIndex      = strdupAtoW(data->pszIndex);
369     wdata->pszFile       = stringsW->pszFile       = strdupAtoW(data->pszFile);
370     wdata->pszHome       = stringsW->pszHome       = strdupAtoW(data->pszHome);
371     wdata->pszJump1      = stringsW->pszJump1      = strdupAtoW(data->pszJump1);
372     wdata->pszJump2      = stringsW->pszJump2      = strdupAtoW(data->pszJump2);
373     wdata->pszUrlJump1   = stringsW->pszUrlJump1   = strdupAtoW(data->pszUrlJump1);
374     wdata->pszUrlJump2   = stringsW->pszUrlJump2   = strdupAtoW(data->pszUrlJump2);
375     wdata->pszCustomTabs = stringsW->pszCustomTabs = strdupAtoW(data->pszCustomTabs);
376 }
377 
378 static void wintypeWtoA(const HH_WINTYPEW *wdata, HH_WINTYPEA *data, struct wintype_stringsA *stringsA)
379 {
380     memcpy(data, wdata, sizeof(*wdata));
381     /* convert all of the Unicode strings to ANSI */
382     data->pszType       = stringsA->pszType       = strdupWtoA(wdata->pszType);
383     data->pszCaption    = stringsA->pszCaption    = strdupWtoA(wdata->pszCaption);
384     data->pszToc        = stringsA->pszToc        = strdupWtoA(wdata->pszToc);
385     data->pszIndex      = stringsA->pszFile       = strdupWtoA(wdata->pszIndex);
386     data->pszFile       = stringsA->pszFile       = strdupWtoA(wdata->pszFile);
387     data->pszHome       = stringsA->pszHome       = strdupWtoA(wdata->pszHome);
388     data->pszJump1      = stringsA->pszJump1      = strdupWtoA(wdata->pszJump1);
389     data->pszJump2      = stringsA->pszJump2      = strdupWtoA(wdata->pszJump2);
390     data->pszUrlJump1   = stringsA->pszUrlJump1   = strdupWtoA(wdata->pszUrlJump1);
391     data->pszUrlJump2   = stringsA->pszUrlJump2   = strdupWtoA(wdata->pszUrlJump2);
392     data->pszCustomTabs = stringsA->pszCustomTabs = strdupWtoA(wdata->pszCustomTabs);
393 }
394 
395 /******************************************************************
396  *		HtmlHelpA (HHCTRL.OCX.14)
397  */
398 HWND WINAPI HtmlHelpA(HWND caller, LPCSTR filename, UINT command, DWORD_PTR data)
399 {
400     WCHAR *wfile = strdupAtoW( filename );
401     HWND result = 0;
402 
403     if (data)
404     {
405         switch(command)
406         {
407         case HH_ALINK_LOOKUP:
408         case HH_DISPLAY_SEARCH:
409         case HH_DISPLAY_TEXT_POPUP:
410         case HH_GET_LAST_ERROR:
411         case HH_KEYWORD_LOOKUP:
412         case HH_SYNC:
413             FIXME("structures not handled yet\n");
414             break;
415 
416         case HH_SET_WIN_TYPE:
417         {
418             struct wintype_stringsW stringsW;
419             HH_WINTYPEW wdata;
420 
421             wintypeAtoW((HH_WINTYPEA *)data, &wdata, &stringsW);
422             result = HtmlHelpW( caller, wfile, command, (DWORD_PTR)&wdata );
423             wintype_stringsW_free(&stringsW);
424             goto done;
425         }
426         case HH_GET_WIN_TYPE:
427         {
428             HH_WINTYPEW wdata;
429             HHInfo *info;
430 
431             result = HtmlHelpW( caller, wfile, command, (DWORD_PTR)&wdata );
432             if (!wdata.pszType) break;
433             info = find_window(wdata.pszType);
434             if (!info) break;
435             wintype_stringsA_free(&info->stringsA);
436             wintypeWtoA(&wdata, (HH_WINTYPEA *)data, &info->stringsA);
437             goto done;
438         }
439 
440         case HH_DISPLAY_INDEX:
441         case HH_DISPLAY_TOPIC:
442         case HH_DISPLAY_TOC:
443         case HH_GET_WIN_HANDLE:
444         case HH_SAFE_DISPLAY_TOPIC:
445         {
446             WCHAR *wdata = strdupAtoW( (const char *)data );
447             result = HtmlHelpW( caller, wfile, command, (DWORD_PTR)wdata );
448             heap_free(wdata);
449             goto done;
450         }
451 
452         case HH_CLOSE_ALL:
453         case HH_HELP_CONTEXT:
454         case HH_INITIALIZE:
455         case HH_PRETRANSLATEMESSAGE:
456         case HH_TP_HELP_CONTEXTMENU:
457         case HH_TP_HELP_WM_HELP:
458         case HH_UNINITIALIZE:
459             /* either scalar or pointer to scalar - do nothing */
460             break;
461 
462         default:
463             FIXME("Unknown command: %s (%d)\n", command_to_string(command), command);
464             break;
465         }
466     }
467 
468     result = HtmlHelpW( caller, wfile, command, data );
469 done:
470     heap_free(wfile);
471     return result;
472 }
473 
474 /******************************************************************
475  *		doWinMain (HHCTRL.OCX.13)
476  */
477 int WINAPI doWinMain(HINSTANCE hInstance, LPSTR szCmdLine)
478 {
479     MSG msg;
480     int len, buflen, mapid = -1;
481     WCHAR *filename;
482     char *endq = NULL;
483     HWND hwnd;
484 
485     hh_process = TRUE;
486 
487     /* Parse command line option of the HTML Help command.
488      *
489      * Note: The only currently handled action is "mapid",
490      *  which corresponds to opening a specific page.
491      */
492     while(*szCmdLine == '-')
493     {
494         LPSTR space, ptr;
495 
496         ptr = szCmdLine + 1;
497         space = strchr(ptr, ' ');
498         if(!strncmp(ptr, "mapid", space-ptr))
499         {
500             char idtxt[10];
501 
502             ptr += strlen("mapid")+1;
503             space = strchr(ptr, ' ');
504             /* command line ends without number */
505             if (!space)
506                 return 0;
507             memcpy(idtxt, ptr, space-ptr);
508             idtxt[space-ptr] = '\0';
509             mapid = atoi(idtxt);
510             szCmdLine = space+1;
511         }
512         else
513         {
514             FIXME("Unhandled HTML Help command line parameter! (%.*s)\n", (int)(space-szCmdLine), szCmdLine);
515             return 0;
516         }
517     }
518 
519     /* FIXME: Check szCmdLine for bad arguments */
520     if (*szCmdLine == '\"')
521         endq = strchr(++szCmdLine, '\"');
522 
523     if (endq)
524         len = endq - szCmdLine;
525     else
526         len = strlen(szCmdLine);
527 
528     /* no filename given */
529     if (!len)
530         return 0;
531 
532     buflen = MultiByteToWideChar(CP_ACP, 0, szCmdLine, len, NULL, 0) + 1;
533     filename = heap_alloc(buflen * sizeof(WCHAR));
534     MultiByteToWideChar(CP_ACP, 0, szCmdLine, len, filename, buflen);
535     filename[buflen-1] = 0;
536 
537     /* Open a specific help topic */
538     if(mapid != -1)
539         hwnd = HtmlHelpW(GetDesktopWindow(), filename, HH_HELP_CONTEXT, mapid);
540     else
541         hwnd = HtmlHelpW(GetDesktopWindow(), filename, HH_DISPLAY_TOPIC, 0);
542 
543     heap_free(filename);
544 
545     if (!hwnd)
546     {
547         ERR("Failed to open HTML Help file '%s'.\n", szCmdLine);
548         return 0;
549     }
550 
551     while (GetMessageW(&msg, 0, 0, 0))
552     {
553         TranslateMessage(&msg);
554         DispatchMessageW(&msg);
555     }
556 
557     return 0;
558 }
559 
560 /******************************************************************
561  *		DllGetClassObject (HHCTRL.OCX.@)
562  */
563 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
564 {
565     FIXME("(%s %s %p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
566     return CLASS_E_CLASSNOTAVAILABLE;
567 }
568 
569 /***********************************************************************
570  *		DllRegisterServer (HHCTRL.OCX.@)
571  */
572 HRESULT WINAPI DllRegisterServer(void)
573 {
574     return __wine_register_resources( hhctrl_hinstance );
575 }
576 
577 /***********************************************************************
578  *		DllUnregisterServer (HHCTRL.OCX.@)
579  */
580 HRESULT WINAPI DllUnregisterServer(void)
581 {
582     return __wine_unregister_resources( hhctrl_hinstance );
583 }
584