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