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