hhctrl.ocx: Permit WS_CHILD help windows.
[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     case DLL_PROCESS_DETACH:
57         break;
58     }
59     return TRUE;
60 }
61
62 static const char *command_to_string(UINT command)
63 {
64 #define X(x) case x: return #x
65     switch (command)
66     {
67         X( HH_DISPLAY_TOPIC );
68         X( HH_DISPLAY_TOC );
69         X( HH_DISPLAY_INDEX );
70         X( HH_DISPLAY_SEARCH );
71         X( HH_SET_WIN_TYPE );
72         X( HH_GET_WIN_TYPE );
73         X( HH_GET_WIN_HANDLE );
74         X( HH_ENUM_INFO_TYPE );
75         X( HH_SET_INFO_TYPE );
76         X( HH_SYNC );
77         X( HH_RESERVED1 );
78         X( HH_RESERVED2 );
79         X( HH_RESERVED3 );
80         X( HH_KEYWORD_LOOKUP );
81         X( HH_DISPLAY_TEXT_POPUP );
82         X( HH_HELP_CONTEXT );
83         X( HH_TP_HELP_CONTEXTMENU );
84         X( HH_TP_HELP_WM_HELP );
85         X( HH_CLOSE_ALL );
86         X( HH_ALINK_LOOKUP );
87         X( HH_GET_LAST_ERROR );
88         X( HH_ENUM_CATEGORY );
89         X( HH_ENUM_CATEGORY_IT );
90         X( HH_RESET_IT_FILTER );
91         X( HH_SET_INCLUSIVE_FILTER );
92         X( HH_SET_EXCLUSIVE_FILTER );
93         X( HH_INITIALIZE );
94         X( HH_UNINITIALIZE );
95         X( HH_SAFE_DISPLAY_TOPIC );
96         X( HH_PRETRANSLATEMESSAGE );
97         X( HH_SET_GLOBAL_PROPERTY );
98     default: return "???";
99     }
100 #undef X
101 }
102
103 static BOOL resolve_filename(const WCHAR *filename, WCHAR *fullname, DWORD buflen, WCHAR **index, WCHAR **window)
104 {
105     const WCHAR *extra;
106     WCHAR chm_file[MAX_PATH];
107
108     static const WCHAR helpW[] = {'\\','h','e','l','p','\\',0};
109     static const WCHAR delimW[] = {':',':',0};
110     static const WCHAR delim2W[] = {'>',0};
111
112     filename = skip_schema(filename);
113
114     /* the format is "helpFile[::/index][>window]" */
115     if (index) *index = NULL;
116     if (window) *window = NULL;
117
118     extra = strstrW(filename, delim2W);
119     if (extra)
120     {
121         memcpy(chm_file, filename, (extra-filename)*sizeof(WCHAR));
122         chm_file[extra-filename] = 0;
123         filename = chm_file;
124         if (window)
125             *window = strdupW(extra+1);
126     }
127
128     extra = strstrW(filename, delimW);
129     if (extra)
130     {
131         if (filename != chm_file)
132             memcpy(chm_file, filename, (extra-filename)*sizeof(WCHAR));
133         chm_file[extra-filename] = 0;
134         filename = chm_file;
135         if (index)
136             *index = strdupW(extra+2);
137     }
138
139     GetFullPathNameW(filename, buflen, fullname, NULL);
140     if (GetFileAttributesW(fullname) == INVALID_FILE_ATTRIBUTES)
141     {
142         GetWindowsDirectoryW(fullname, buflen);
143         strcatW(fullname, helpW);
144         strcatW(fullname, filename);
145     }
146     return (GetFileAttributesW(fullname) != INVALID_FILE_ATTRIBUTES);
147 }
148
149 /******************************************************************
150  *              HtmlHelpW (HHCTRL.OCX.15)
151  */
152 HWND WINAPI HtmlHelpW(HWND caller, LPCWSTR filename, UINT command, DWORD_PTR data)
153 {
154     WCHAR fullname[MAX_PATH];
155
156     TRACE("(%p, %s, command=%s, data=%lx)\n",
157           caller, debugstr_w( filename ),
158           command_to_string( command ), data);
159
160     switch (command)
161     {
162     case HH_DISPLAY_TOPIC:
163     case HH_DISPLAY_TOC:
164     case HH_DISPLAY_INDEX:
165     case HH_DISPLAY_SEARCH:{
166         HHInfo *info;
167         BOOL res;
168         NMHDR nmhdr;
169         WCHAR *window = NULL;
170         const WCHAR *index = NULL;
171         WCHAR *default_index = NULL;
172         int tab_index = TAB_CONTENTS;
173
174         if (!filename)
175             return NULL;
176
177         if (!resolve_filename(filename, fullname, MAX_PATH, &default_index, &window))
178         {
179             WARN("can't find %s\n", debugstr_w(filename));
180             return 0;
181         }
182         index = default_index;
183
184         info = CreateHelpViewer(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->pszType)
195             info->WinType.pszType = info->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                 index = (const WCHAR *)data;
206             break;
207         }
208
209         res = NavigateToChm(info, info->pCHMInfo->szFile, index);
210         heap_free(default_index);
211
212         if(!res)
213         {
214             ReleaseHelpViewer(info);
215             return NULL;
216         }
217
218         switch(command)
219         {
220         case HH_DISPLAY_TOPIC:
221         case HH_DISPLAY_TOC:
222             tab_index = TAB_CONTENTS;
223             break;
224         case HH_DISPLAY_INDEX:
225             tab_index = TAB_INDEX;
226             if (data)
227                 FIXME("Should select keyword '%s'.\n", debugstr_w((WCHAR *)data));
228             break;
229         case HH_DISPLAY_SEARCH:
230             tab_index = TAB_SEARCH;
231             if (data)
232                 FIXME("Should display search specified by HH_FTS_QUERY structure.\n");
233             break;
234         }
235         /* open the requested tab */
236         memset(&nmhdr, 0, sizeof(nmhdr));
237         nmhdr.code = TCN_SELCHANGE;
238         SendMessageW(info->hwndTabCtrl, TCM_SETCURSEL, (WPARAM)info->tabs[tab_index].id, 0);
239         SendMessageW(info->WinType.hwndNavigation, WM_NOTIFY, 0, (LPARAM)&nmhdr);
240
241         return info->WinType.hwndHelp;
242     }
243     case HH_HELP_CONTEXT: {
244         HHInfo *info;
245         LPWSTR url;
246
247         if (!filename)
248             return NULL;
249
250         if (!resolve_filename(filename, fullname, MAX_PATH, NULL, NULL))
251         {
252             WARN("can't find %s\n", debugstr_w(filename));
253             return 0;
254         }
255
256         info = CreateHelpViewer(fullname, caller);
257         if(!info)
258             return NULL;
259
260         url = FindContextAlias(info->pCHMInfo, data);
261         if(!url)
262         {
263             ReleaseHelpViewer(info);
264             return NULL;
265         }
266
267         NavigateToUrl(info, url);
268         heap_free(url);
269         return info->WinType.hwndHelp;
270     }
271     case HH_PRETRANSLATEMESSAGE: {
272         static BOOL warned = FALSE;
273
274         if (!warned)
275         {
276             FIXME("HH_PRETRANSLATEMESSAGE unimplemented\n");
277             warned = TRUE;
278         }
279         return 0;
280     }
281     case HH_CLOSE_ALL: {
282         HHInfo *info, *next;
283
284         LIST_FOR_EACH_ENTRY_SAFE(info, next, &window_list, HHInfo, entry)
285         {
286             TRACE("Destroying window %s.\n", debugstr_w(info->WinType.pszType));
287             ReleaseHelpViewer(info);
288         }
289         return 0;
290     }
291     default:
292         FIXME("HH case %s not handled.\n", command_to_string( command ));
293     }
294
295     return 0;
296 }
297
298 /******************************************************************
299  *              HtmlHelpA (HHCTRL.OCX.14)
300  */
301 HWND WINAPI HtmlHelpA(HWND caller, LPCSTR filename, UINT command, DWORD_PTR data)
302 {
303     WCHAR *wfile = NULL, *wdata = NULL;
304     DWORD len;
305     HWND result;
306
307     if (filename)
308     {
309         len = MultiByteToWideChar( CP_ACP, 0, filename, -1, NULL, 0 );
310         wfile = heap_alloc(len*sizeof(WCHAR));
311         MultiByteToWideChar( CP_ACP, 0, filename, -1, wfile, len );
312     }
313
314     if (data)
315     {
316         switch(command)
317         {
318         case HH_ALINK_LOOKUP:
319         case HH_DISPLAY_SEARCH:
320         case HH_DISPLAY_TEXT_POPUP:
321         case HH_GET_LAST_ERROR:
322         case HH_GET_WIN_TYPE:
323         case HH_KEYWORD_LOOKUP:
324         case HH_SET_WIN_TYPE:
325         case HH_SYNC:
326             FIXME("structures not handled yet\n");
327             break;
328
329         case HH_DISPLAY_INDEX:
330         case HH_DISPLAY_TOPIC:
331         case HH_DISPLAY_TOC:
332         case HH_GET_WIN_HANDLE:
333         case HH_SAFE_DISPLAY_TOPIC:
334             len = MultiByteToWideChar( CP_ACP, 0, (const char*)data, -1, NULL, 0 );
335             wdata = heap_alloc(len*sizeof(WCHAR));
336             MultiByteToWideChar( CP_ACP, 0, (const char*)data, -1, wdata, len );
337             break;
338
339         case HH_CLOSE_ALL:
340         case HH_HELP_CONTEXT:
341         case HH_INITIALIZE:
342         case HH_PRETRANSLATEMESSAGE:
343         case HH_TP_HELP_CONTEXTMENU:
344         case HH_TP_HELP_WM_HELP:
345         case HH_UNINITIALIZE:
346             /* either scalar or pointer to scalar - do nothing */
347             break;
348
349         default:
350             FIXME("Unknown command: %s (%d)\n", command_to_string(command), command);
351             break;
352         }
353     }
354
355     result = HtmlHelpW( caller, wfile, command, wdata ? (DWORD_PTR)wdata : data );
356
357     heap_free(wfile);
358     heap_free(wdata);
359     return result;
360 }
361
362 /******************************************************************
363  *              doWinMain (HHCTRL.OCX.13)
364  */
365 int WINAPI doWinMain(HINSTANCE hInstance, LPSTR szCmdLine)
366 {
367     MSG msg;
368     int len, buflen, mapid = -1;
369     WCHAR *filename;
370     char *endq = NULL;
371     HWND hwnd;
372
373     hh_process = TRUE;
374
375     /* Parse command line option of the HTML Help command.
376      *
377      * Note: The only currently handled action is "mapid",
378      *  which corresponds to opening a specific page.
379      */
380     while(*szCmdLine == '-')
381     {
382         LPSTR space, ptr;
383
384         ptr = szCmdLine + 1;
385         space = strchr(ptr, ' ');
386         if(!strncmp(ptr, "mapid", space-ptr))
387         {
388             char idtxt[10];
389
390             ptr += strlen("mapid")+1;
391             space = strchr(ptr, ' ');
392             /* command line ends without number */
393             if (!space)
394                 return 0;
395             memcpy(idtxt, ptr, space-ptr);
396             idtxt[space-ptr] = '\0';
397             mapid = atoi(idtxt);
398             szCmdLine = space+1;
399         }
400         else
401         {
402             FIXME("Unhandled HTML Help command line parameter! (%.*s)\n", (int)(space-szCmdLine), szCmdLine);
403             return 0;
404         }
405     }
406
407     /* FIXME: Check szCmdLine for bad arguments */
408     if (*szCmdLine == '\"')
409         endq = strchr(++szCmdLine, '\"');
410
411     if (endq)
412         len = endq - szCmdLine;
413     else
414         len = strlen(szCmdLine);
415
416     /* no filename given */
417     if (!len)
418         return 0;
419
420     buflen = MultiByteToWideChar(CP_ACP, 0, szCmdLine, len, NULL, 0) + 1;
421     filename = heap_alloc(buflen * sizeof(WCHAR));
422     MultiByteToWideChar(CP_ACP, 0, szCmdLine, len, filename, buflen);
423     filename[buflen-1] = 0;
424
425     /* Open a specific help topic */
426     if(mapid != -1)
427         hwnd = HtmlHelpW(GetDesktopWindow(), filename, HH_HELP_CONTEXT, mapid);
428     else
429         hwnd = HtmlHelpW(GetDesktopWindow(), filename, HH_DISPLAY_TOPIC, 0);
430
431     heap_free(filename);
432
433     if (!hwnd)
434     {
435         ERR("Failed to open HTML Help file '%s'.\n", szCmdLine);
436         return 0;
437     }
438
439     while (GetMessageW(&msg, 0, 0, 0))
440     {
441         TranslateMessage(&msg);
442         DispatchMessageW(&msg);
443     }
444
445     return 0;
446 }
447
448 /******************************************************************
449  *              DllGetClassObject (HHCTRL.OCX.@)
450  */
451 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
452 {
453     FIXME("(%s %s %p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
454     return CLASS_E_CLASSNOTAVAILABLE;
455 }
456
457 /***********************************************************************
458  *              DllRegisterServer (HHCTRL.OCX.@)
459  */
460 HRESULT WINAPI DllRegisterServer(void)
461 {
462     return __wine_register_resources( hhctrl_hinstance );
463 }
464
465 /***********************************************************************
466  *              DllUnregisterServer (HHCTRL.OCX.@)
467  */
468 HRESULT WINAPI DllUnregisterServer(void)
469 {
470     return __wine_unregister_resources( hhctrl_hinstance );
471 }