hhctrl.ocx: Added import library.
[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         BOOL res;
167         NMHDR nmhdr;
168         HHInfo *info = NULL;
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         if (window)
185             info = find_window(window);
186
187         info = CreateHelpViewer(info, fullname, caller);
188         if(!info)
189         {
190             heap_free(default_index);
191             heap_free(window);
192             return NULL;
193         }
194
195         if(!index)
196             index = info->WinType.pszFile;
197         if(!info->WinType.pszType)
198             info->WinType.pszType = info->stringsW.pszType = window;
199         else
200             heap_free(window);
201
202         /* called to load a specified topic */
203         switch(command)
204         {
205         case HH_DISPLAY_TOPIC:
206         case HH_DISPLAY_TOC:
207             if (data)
208                 index = (const WCHAR *)data;
209             break;
210         }
211
212         res = NavigateToChm(info, info->pCHMInfo->szFile, index);
213         heap_free(default_index);
214
215         if(!res)
216         {
217             ReleaseHelpViewer(info);
218             return NULL;
219         }
220
221         switch(command)
222         {
223         case HH_DISPLAY_TOPIC:
224         case HH_DISPLAY_TOC:
225             tab_index = TAB_CONTENTS;
226             break;
227         case HH_DISPLAY_INDEX:
228             tab_index = TAB_INDEX;
229             if (data)
230                 FIXME("Should select keyword '%s'.\n", debugstr_w((WCHAR *)data));
231             break;
232         case HH_DISPLAY_SEARCH:
233             tab_index = TAB_SEARCH;
234             if (data)
235                 FIXME("Should display search specified by HH_FTS_QUERY structure.\n");
236             break;
237         }
238         /* open the requested tab */
239         memset(&nmhdr, 0, sizeof(nmhdr));
240         nmhdr.code = TCN_SELCHANGE;
241         SendMessageW(info->hwndTabCtrl, TCM_SETCURSEL, (WPARAM)info->tabs[tab_index].id, 0);
242         SendMessageW(info->WinType.hwndNavigation, WM_NOTIFY, 0, (LPARAM)&nmhdr);
243
244         return info->WinType.hwndHelp;
245     }
246     case HH_HELP_CONTEXT: {
247         WCHAR *window = NULL;
248         HHInfo *info = NULL;
249         LPWSTR url;
250
251         if (!filename)
252             return NULL;
253
254         if (!resolve_filename(filename, fullname, MAX_PATH, NULL, &window))
255         {
256             WARN("can't find %s\n", debugstr_w(filename));
257             return 0;
258         }
259
260         if (window)
261             info = find_window(window);
262
263         info = CreateHelpViewer(info, fullname, caller);
264         if(!info)
265         {
266             heap_free(window);
267             return NULL;
268         }
269
270         if(!info->WinType.pszType)
271             info->WinType.pszType = info->stringsW.pszType = window;
272         else
273             heap_free(window);
274
275         url = FindContextAlias(info->pCHMInfo, data);
276         if(!url)
277         {
278             ReleaseHelpViewer(info);
279             return NULL;
280         }
281
282         NavigateToUrl(info, url);
283         heap_free(url);
284         return info->WinType.hwndHelp;
285     }
286     case HH_PRETRANSLATEMESSAGE: {
287         static BOOL warned = FALSE;
288
289         if (!warned)
290         {
291             FIXME("HH_PRETRANSLATEMESSAGE unimplemented\n");
292             warned = TRUE;
293         }
294         return 0;
295     }
296     case HH_CLOSE_ALL: {
297         HHInfo *info, *next;
298
299         LIST_FOR_EACH_ENTRY_SAFE(info, next, &window_list, HHInfo, entry)
300         {
301             TRACE("Destroying window %s.\n", debugstr_w(info->WinType.pszType));
302             ReleaseHelpViewer(info);
303         }
304         return 0;
305     }
306     case HH_SET_WIN_TYPE: {
307         HH_WINTYPEW *wintype = (HH_WINTYPEW *)data;
308         WCHAR *window = NULL;
309         HHInfo *info = NULL;
310
311         if (!filename && wintype->pszType)
312             window = strdupW(wintype->pszType);
313         else if (!filename || !resolve_filename(filename, fullname, MAX_PATH, NULL, &window) || !window)
314         {
315             WARN("can't find window name: %s\n", debugstr_w(filename));
316             return 0;
317         }
318         info = find_window(window);
319         if (!info)
320         {
321             info = heap_alloc_zero(sizeof(HHInfo));
322             info->WinType.pszType = info->stringsW.pszType = window;
323             list_add_tail(&window_list, &info->entry);
324         }
325         else
326             heap_free(window);
327
328         TRACE("Changing WINTYPE, fsValidMembers=0x%x\n", wintype->fsValidMembers);
329
330         MergeChmProperties(wintype, info, TRUE);
331         UpdateHelpWindow(info);
332         return 0;
333     }
334     case HH_GET_WIN_TYPE: {
335         HH_WINTYPEW *wintype = (HH_WINTYPEW *)data;
336         WCHAR *window = NULL;
337         HHInfo *info = NULL;
338
339         if (!filename || !resolve_filename(filename, fullname, MAX_PATH, NULL, &window) || !window)
340         {
341             WARN("can't find window name: %s\n", debugstr_w(filename));
342             return 0;
343         }
344         info = find_window(window);
345         if (!info)
346         {
347             WARN("Could not find window named %s.\n", debugstr_w(window));
348             heap_free(window);
349             return (HWND)~0;
350         }
351
352         TRACE("Retrieving WINTYPE for %s.\n", debugstr_w(window));
353         *wintype = info->WinType;
354         heap_free(window);
355         return 0;
356     }
357     default:
358         FIXME("HH case %s not handled.\n", command_to_string( command ));
359     }
360
361     return 0;
362 }
363
364 static void wintypeAtoW(const HH_WINTYPEA *data, HH_WINTYPEW *wdata, struct wintype_stringsW *stringsW)
365 {
366     memcpy(wdata, data, sizeof(*data));
367     /* convert all of the ANSI strings to Unicode */
368     wdata->pszType       = stringsW->pszType       = strdupAtoW(data->pszType);
369     wdata->pszCaption    = stringsW->pszCaption    = strdupAtoW(data->pszCaption);
370     wdata->pszToc        = stringsW->pszToc        = strdupAtoW(data->pszToc);
371     wdata->pszIndex      = stringsW->pszIndex      = strdupAtoW(data->pszIndex);
372     wdata->pszFile       = stringsW->pszFile       = strdupAtoW(data->pszFile);
373     wdata->pszHome       = stringsW->pszHome       = strdupAtoW(data->pszHome);
374     wdata->pszJump1      = stringsW->pszJump1      = strdupAtoW(data->pszJump1);
375     wdata->pszJump2      = stringsW->pszJump2      = strdupAtoW(data->pszJump2);
376     wdata->pszUrlJump1   = stringsW->pszUrlJump1   = strdupAtoW(data->pszUrlJump1);
377     wdata->pszUrlJump2   = stringsW->pszUrlJump2   = strdupAtoW(data->pszUrlJump2);
378     wdata->pszCustomTabs = stringsW->pszCustomTabs = strdupAtoW(data->pszCustomTabs);
379 }
380
381 static void wintypeWtoA(const HH_WINTYPEW *wdata, HH_WINTYPEA *data, struct wintype_stringsA *stringsA)
382 {
383     memcpy(data, wdata, sizeof(*wdata));
384     /* convert all of the Unicode strings to ANSI */
385     data->pszType       = stringsA->pszType       = strdupWtoA(wdata->pszType);
386     data->pszCaption    = stringsA->pszCaption    = strdupWtoA(wdata->pszCaption);
387     data->pszToc        = stringsA->pszToc        = strdupWtoA(wdata->pszToc);
388     data->pszIndex      = stringsA->pszFile       = strdupWtoA(wdata->pszIndex);
389     data->pszFile       = stringsA->pszFile       = strdupWtoA(wdata->pszFile);
390     data->pszHome       = stringsA->pszHome       = strdupWtoA(wdata->pszHome);
391     data->pszJump1      = stringsA->pszJump1      = strdupWtoA(wdata->pszJump1);
392     data->pszJump2      = stringsA->pszJump2      = strdupWtoA(wdata->pszJump2);
393     data->pszUrlJump1   = stringsA->pszUrlJump1   = strdupWtoA(wdata->pszUrlJump1);
394     data->pszUrlJump2   = stringsA->pszUrlJump2   = strdupWtoA(wdata->pszUrlJump2);
395     data->pszCustomTabs = stringsA->pszCustomTabs = strdupWtoA(wdata->pszCustomTabs);
396 }
397
398 /******************************************************************
399  *              HtmlHelpA (HHCTRL.OCX.14)
400  */
401 HWND WINAPI HtmlHelpA(HWND caller, LPCSTR filename, UINT command, DWORD_PTR data)
402 {
403     WCHAR *wfile = strdupAtoW( filename );
404     HWND result = 0;
405
406     if (data)
407     {
408         switch(command)
409         {
410         case HH_ALINK_LOOKUP:
411         case HH_DISPLAY_SEARCH:
412         case HH_DISPLAY_TEXT_POPUP:
413         case HH_GET_LAST_ERROR:
414         case HH_KEYWORD_LOOKUP:
415         case HH_SYNC:
416             FIXME("structures not handled yet\n");
417             break;
418
419         case HH_SET_WIN_TYPE:
420         {
421             struct wintype_stringsW stringsW;
422             HH_WINTYPEW wdata;
423
424             wintypeAtoW((HH_WINTYPEA *)data, &wdata, &stringsW);
425             result = HtmlHelpW( caller, wfile, command, (DWORD_PTR)&wdata );
426             wintype_stringsW_free(&stringsW);
427             goto done;
428         }
429         case HH_GET_WIN_TYPE:
430         {
431             HH_WINTYPEW wdata;
432             HHInfo *info;
433
434             result = HtmlHelpW( caller, wfile, command, (DWORD_PTR)&wdata );
435             if (!wdata.pszType) break;
436             info = find_window(wdata.pszType);
437             if (!info) break;
438             wintype_stringsA_free(&info->stringsA);
439             wintypeWtoA(&wdata, (HH_WINTYPEA *)data, &info->stringsA);
440             goto done;
441         }
442
443         case HH_DISPLAY_INDEX:
444         case HH_DISPLAY_TOPIC:
445         case HH_DISPLAY_TOC:
446         case HH_GET_WIN_HANDLE:
447         case HH_SAFE_DISPLAY_TOPIC:
448         {
449             WCHAR *wdata = strdupAtoW( (const char *)data );
450             result = HtmlHelpW( caller, wfile, command, (DWORD_PTR)wdata );
451             heap_free(wdata);
452             goto done;
453         }
454
455         case HH_CLOSE_ALL:
456         case HH_HELP_CONTEXT:
457         case HH_INITIALIZE:
458         case HH_PRETRANSLATEMESSAGE:
459         case HH_TP_HELP_CONTEXTMENU:
460         case HH_TP_HELP_WM_HELP:
461         case HH_UNINITIALIZE:
462             /* either scalar or pointer to scalar - do nothing */
463             break;
464
465         default:
466             FIXME("Unknown command: %s (%d)\n", command_to_string(command), command);
467             break;
468         }
469     }
470
471     result = HtmlHelpW( caller, wfile, command, data );
472 done:
473     heap_free(wfile);
474     return result;
475 }
476
477 /******************************************************************
478  *              doWinMain (HHCTRL.OCX.13)
479  */
480 int WINAPI doWinMain(HINSTANCE hInstance, LPSTR szCmdLine)
481 {
482     MSG msg;
483     int len, buflen, mapid = -1;
484     WCHAR *filename;
485     char *endq = NULL;
486     HWND hwnd;
487
488     hh_process = TRUE;
489
490     /* Parse command line option of the HTML Help command.
491      *
492      * Note: The only currently handled action is "mapid",
493      *  which corresponds to opening a specific page.
494      */
495     while(*szCmdLine == '-')
496     {
497         LPSTR space, ptr;
498
499         ptr = szCmdLine + 1;
500         space = strchr(ptr, ' ');
501         if(!strncmp(ptr, "mapid", space-ptr))
502         {
503             char idtxt[10];
504
505             ptr += strlen("mapid")+1;
506             space = strchr(ptr, ' ');
507             /* command line ends without number */
508             if (!space)
509                 return 0;
510             memcpy(idtxt, ptr, space-ptr);
511             idtxt[space-ptr] = '\0';
512             mapid = atoi(idtxt);
513             szCmdLine = space+1;
514         }
515         else
516         {
517             FIXME("Unhandled HTML Help command line parameter! (%.*s)\n", (int)(space-szCmdLine), szCmdLine);
518             return 0;
519         }
520     }
521
522     /* FIXME: Check szCmdLine for bad arguments */
523     if (*szCmdLine == '\"')
524         endq = strchr(++szCmdLine, '\"');
525
526     if (endq)
527         len = endq - szCmdLine;
528     else
529         len = strlen(szCmdLine);
530
531     /* no filename given */
532     if (!len)
533         return 0;
534
535     buflen = MultiByteToWideChar(CP_ACP, 0, szCmdLine, len, NULL, 0) + 1;
536     filename = heap_alloc(buflen * sizeof(WCHAR));
537     MultiByteToWideChar(CP_ACP, 0, szCmdLine, len, filename, buflen);
538     filename[buflen-1] = 0;
539
540     /* Open a specific help topic */
541     if(mapid != -1)
542         hwnd = HtmlHelpW(GetDesktopWindow(), filename, HH_HELP_CONTEXT, mapid);
543     else
544         hwnd = HtmlHelpW(GetDesktopWindow(), filename, HH_DISPLAY_TOPIC, 0);
545
546     heap_free(filename);
547
548     if (!hwnd)
549     {
550         ERR("Failed to open HTML Help file '%s'.\n", szCmdLine);
551         return 0;
552     }
553
554     while (GetMessageW(&msg, 0, 0, 0))
555     {
556         TranslateMessage(&msg);
557         DispatchMessageW(&msg);
558     }
559
560     return 0;
561 }
562
563 /******************************************************************
564  *              DllGetClassObject (HHCTRL.OCX.@)
565  */
566 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
567 {
568     FIXME("(%s %s %p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
569     return CLASS_E_CLASSNOTAVAILABLE;
570 }
571
572 /***********************************************************************
573  *              DllRegisterServer (HHCTRL.OCX.@)
574  */
575 HRESULT WINAPI DllRegisterServer(void)
576 {
577     return __wine_register_resources( hhctrl_hinstance );
578 }
579
580 /***********************************************************************
581  *              DllUnregisterServer (HHCTRL.OCX.@)
582  */
583 HRESULT WINAPI DllUnregisterServer(void)
584 {
585     return __wine_unregister_resources( hhctrl_hinstance );
586 }