winhelp.exe: Renamed to winhlp32.exe.
[wine] / programs / winhlp32 / winhelp.c
1 /*
2  * Help Viewer
3  *
4  * Copyright    1996 Ulrich Schmid <uschmid@mail.hh.provi.de>
5  *              2002 Sylvain Petreolle <spetreolle@yahoo.fr>
6  *              2002, 2008 Eric Pouech <eric.pouech@wanadoo.fr>
7  *              2004 Ken Belleau <jamez@ivic.qc.ca>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include <assert.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <stdlib.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "wingdi.h"
33 #include "winuser.h"
34 #include "commdlg.h"
35 #include "winhelp.h"
36 #include "winhelp_res.h"
37 #include "shellapi.h"
38 #include "richedit.h"
39
40 #include "wine/debug.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(winhelp);
43
44 static BOOL    WINHELP_RegisterWinClasses(void);
45 static LRESULT CALLBACK WINHELP_MainWndProc(HWND, UINT, WPARAM, LPARAM);
46 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND, UINT, WPARAM, LPARAM);
47 static LRESULT CALLBACK WINHELP_ButtonWndProc(HWND, UINT, WPARAM, LPARAM);
48 static LRESULT CALLBACK WINHELP_HistoryWndProc(HWND, UINT, WPARAM, LPARAM);
49 static LRESULT CALLBACK WINHELP_ShadowWndProc(HWND, UINT, WPARAM, LPARAM);
50 static BOOL    WINHELP_CheckPopup(HWND, UINT, WPARAM, LPARAM, LRESULT*);
51 static void    WINHELP_InitFonts(HWND hWnd);
52 static void    WINHELP_DeleteWindow(WINHELP_WINDOW*);
53 static void    WINHELP_DeleteButtons(WINHELP_WINDOW*);
54 static void    WINHELP_SetupText(HWND hWnd, WINHELP_WINDOW *win, ULONG relative);
55 static void    WINHELP_DeletePageLinks(HLPFILE_PAGE* page);
56
57 WINHELP_GLOBALS Globals = {3, NULL, TRUE, NULL, NULL, NULL, NULL, NULL, {{{NULL,NULL}},0}};
58
59 #define CTL_ID_BUTTON   0x700
60 #define CTL_ID_TEXT     0x701
61
62 /***********************************************************************
63  *
64  *           WINHELP_GetOpenFileName
65  */
66 BOOL WINHELP_GetOpenFileName(LPSTR lpszFile, int len)
67 {
68     OPENFILENAME openfilename;
69     CHAR szDir[MAX_PATH];
70     CHAR szzFilter[2 * MAX_STRING_LEN + 100];
71     LPSTR p = szzFilter;
72
73     WINE_TRACE("()\n");
74
75     LoadString(Globals.hInstance, STID_HELP_FILES_HLP, p, MAX_STRING_LEN);
76     p += strlen(p) + 1;
77     lstrcpy(p, "*.hlp");
78     p += strlen(p) + 1;
79     LoadString(Globals.hInstance, STID_ALL_FILES, p, MAX_STRING_LEN);
80     p += strlen(p) + 1;
81     lstrcpy(p, "*.*");
82     p += strlen(p) + 1;
83     *p = '\0';
84
85     GetCurrentDirectory(sizeof(szDir), szDir);
86
87     lpszFile[0]='\0';
88
89     openfilename.lStructSize       = sizeof(OPENFILENAME);
90     openfilename.hwndOwner         = NULL;
91     openfilename.hInstance         = Globals.hInstance;
92     openfilename.lpstrFilter       = szzFilter;
93     openfilename.lpstrCustomFilter = 0;
94     openfilename.nMaxCustFilter    = 0;
95     openfilename.nFilterIndex      = 1;
96     openfilename.lpstrFile         = lpszFile;
97     openfilename.nMaxFile          = len;
98     openfilename.lpstrFileTitle    = 0;
99     openfilename.nMaxFileTitle     = 0;
100     openfilename.lpstrInitialDir   = szDir;
101     openfilename.lpstrTitle        = 0;
102     openfilename.Flags             = 0;
103     openfilename.nFileOffset       = 0;
104     openfilename.nFileExtension    = 0;
105     openfilename.lpstrDefExt       = 0;
106     openfilename.lCustData         = 0;
107     openfilename.lpfnHook          = 0;
108     openfilename.lpTemplateName    = 0;
109
110     return GetOpenFileName(&openfilename);
111 }
112
113 static char* WINHELP_GetCaption(WINHELP_WNDPAGE* wpage)
114 {
115     if (wpage->wininfo->caption[0]) return wpage->wininfo->caption;
116     return wpage->page->file->lpszTitle;
117 }
118
119 /***********************************************************************
120  *
121  *           WINHELP_LookupHelpFile
122  */
123 HLPFILE* WINHELP_LookupHelpFile(LPCSTR lpszFile)
124 {
125     HLPFILE*        hlpfile;
126     char szFullName[MAX_PATH];
127     char szAddPath[MAX_PATH];
128     char *p;
129
130     /*
131      * NOTE: This is needed by popup windows only.
132      * In other cases it's not needed but does not hurt though.
133      */
134     if (Globals.active_win && Globals.active_win->page && Globals.active_win->page->file)
135     {
136         strcpy(szAddPath, Globals.active_win->page->file->lpszPath);
137         p = strrchr(szAddPath, '\\');
138         if (p) *p = 0;
139     }
140
141     /*
142      * FIXME: Should we swap conditions?
143      */
144     if (!SearchPath(NULL, lpszFile, ".hlp", MAX_PATH, szFullName, NULL) &&
145         !SearchPath(szAddPath, lpszFile, ".hlp", MAX_PATH, szFullName, NULL))
146     {
147         if (WINHELP_MessageBoxIDS_s(STID_FILE_NOT_FOUND_s, lpszFile, STID_WHERROR,
148                                     MB_YESNO|MB_ICONQUESTION) != IDYES)
149             return NULL;
150         if (!WINHELP_GetOpenFileName(szFullName, MAX_PATH))
151             return NULL;
152     }
153     hlpfile = HLPFILE_ReadHlpFile(szFullName);
154     if (!hlpfile)
155         WINHELP_MessageBoxIDS_s(STID_HLPFILE_ERROR_s, lpszFile,
156                                 STID_WHERROR, MB_OK|MB_ICONSTOP);
157     return hlpfile;
158 }
159
160 /******************************************************************
161  *              WINHELP_GetWindowInfo
162  *
163  *
164  */
165 HLPFILE_WINDOWINFO*     WINHELP_GetWindowInfo(HLPFILE* hlpfile, LPCSTR name)
166 {
167     static      HLPFILE_WINDOWINFO      mwi;
168     unsigned int     i;
169
170     if (!name || !name[0])
171         name = Globals.active_win->lpszName;
172
173     if (hlpfile)
174         for (i = 0; i < hlpfile->numWindows; i++)
175             if (!strcmp(hlpfile->windows[i].name, name))
176                 return &hlpfile->windows[i];
177
178     if (strcmp(name, "main") != 0)
179     {
180         WINE_FIXME("Couldn't find window info for %s\n", name);
181         assert(0);
182         return NULL;
183     }
184     if (!mwi.name[0])
185     {
186         strcpy(mwi.type, "primary");
187         strcpy(mwi.name, "main");
188         if (!LoadString(Globals.hInstance, STID_WINE_HELP, 
189                         mwi.caption, sizeof(mwi.caption)))
190             strcpy(mwi.caption, hlpfile->lpszTitle);
191         mwi.origin.x = mwi.origin.y = mwi.size.cx = mwi.size.cy = CW_USEDEFAULT;
192         mwi.style = SW_SHOW;
193         mwi.win_style = WS_OVERLAPPEDWINDOW;
194         mwi.sr_color = mwi.sr_color = 0xFFFFFF;
195     }
196     return &mwi;
197 }
198
199 /******************************************************************
200  *              HLPFILE_GetPopupWindowInfo
201  *
202  *
203  */
204 static HLPFILE_WINDOWINFO*     WINHELP_GetPopupWindowInfo(HLPFILE* hlpfile,
205                                                           WINHELP_WINDOW* parent, LPARAM mouse)
206 {
207     static      HLPFILE_WINDOWINFO      wi;
208
209     RECT parent_rect;
210     
211     wi.type[0] = wi.name[0] = wi.caption[0] = '\0';
212
213     /* Calculate horizontal size and position of a popup window */
214     GetWindowRect(parent->hMainWnd, &parent_rect);
215     wi.size.cx = (parent_rect.right  - parent_rect.left) / 2;
216     wi.size.cy = 10; /* need a non null value, so that border are taken into account while computing */
217
218     wi.origin.x = (short)LOWORD(mouse);
219     wi.origin.y = (short)HIWORD(mouse);
220     ClientToScreen(parent->hMainWnd, &wi.origin);
221     wi.origin.x -= wi.size.cx / 2;
222     wi.origin.x  = min(wi.origin.x, GetSystemMetrics(SM_CXSCREEN) - wi.size.cx);
223     wi.origin.x  = max(wi.origin.x, 0);
224
225     wi.style = SW_SHOW;
226     wi.win_style = WS_POPUP | WS_BORDER;
227     wi.sr_color = parent->info->sr_color;
228     wi.nsr_color = 0xFFFFFF;
229
230     return &wi;
231 }
232
233 /***********************************************************************
234  *
235  *           WinMain
236  */
237 int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
238 {
239     MSG                 msg;
240     LONG                lHash = 0;
241     HLPFILE*            hlpfile;
242     static CHAR         default_wndname[] = "main";
243     LPSTR               wndname = default_wndname;
244     WINHELP_DLL*        dll;
245
246     Globals.hInstance = hInstance;
247
248     if (LoadLibrary("riched20.dll") == NULL)
249         return MessageBox(0, MAKEINTRESOURCE(STID_NO_RICHEDIT),
250                           MAKEINTRESOURCE(STID_WHERROR), MB_OK);
251
252     /* Get options */
253     while (*cmdline && (*cmdline == ' ' || *cmdline == '-'))
254     {
255         CHAR   option;
256         LPCSTR topic_id;
257         if (*cmdline++ == ' ') continue;
258
259         option = *cmdline;
260         if (option) cmdline++;
261         while (*cmdline && *cmdline == ' ') cmdline++;
262         switch (option)
263         {
264         case 'i':
265         case 'I':
266             topic_id = cmdline;
267             while (*cmdline && *cmdline != ' ') cmdline++;
268             if (*cmdline) *cmdline++ = '\0';
269             lHash = HLPFILE_Hash(topic_id);
270             break;
271
272         case '3':
273         case '4':
274             Globals.wVersion = option - '0';
275             break;
276
277         case 'x':
278             show = SW_HIDE; 
279             Globals.isBook = FALSE;
280             break;
281
282         default:
283             WINE_FIXME("Unsupported cmd line: %s\n", cmdline);
284             break;
285         }
286     }
287
288     /* Create primary window */
289     if (!WINHELP_RegisterWinClasses())
290     {
291         WINE_FIXME("Couldn't register classes\n");
292         return 0;
293     }
294
295     if (*cmdline)
296     {
297         char*   ptr;
298         if ((*cmdline == '"') && (ptr = strchr(cmdline+1, '"')))
299         {
300             cmdline++;
301             *ptr = '\0';
302         }
303         if ((ptr = strchr(cmdline, '>')))
304         {
305             *ptr = '\0';
306             wndname = ptr + 1;
307         }
308         hlpfile = WINHELP_LookupHelpFile(cmdline);
309         if (!hlpfile) return 0;
310     }
311     else hlpfile = NULL;
312     WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, lHash,
313                            WINHELP_GetWindowInfo(hlpfile, wndname), show);
314
315     /* Message loop */
316     while (GetMessage(&msg, 0, 0, 0))
317     {
318         TranslateMessage(&msg);
319         DispatchMessage(&msg);
320     }
321     for (dll = Globals.dlls; dll; dll = dll->next)
322     {
323         if (dll->class & DC_INITTERM) dll->handler(DW_TERM, 0, 0);
324     }
325     return 0;
326 }
327
328 /***********************************************************************
329  *
330  *           RegisterWinClasses
331  */
332 static BOOL WINHELP_RegisterWinClasses(void)
333 {
334     WNDCLASS class_main, class_button_box, class_shadow, class_history;
335
336     class_main.style               = CS_HREDRAW | CS_VREDRAW;
337     class_main.lpfnWndProc         = WINHELP_MainWndProc;
338     class_main.cbClsExtra          = 0;
339     class_main.cbWndExtra          = sizeof(LONG);
340     class_main.hInstance           = Globals.hInstance;
341     class_main.hIcon               = LoadIcon(Globals.hInstance, MAKEINTRESOURCE(IDI_WINHELP));
342     class_main.hCursor             = LoadCursor(0, IDC_ARROW);
343     class_main.hbrBackground       = (HBRUSH)(COLOR_WINDOW+1);
344     class_main.lpszMenuName        = 0;
345     class_main.lpszClassName       = MAIN_WIN_CLASS_NAME;
346
347     class_button_box               = class_main;
348     class_button_box.lpfnWndProc   = WINHELP_ButtonBoxWndProc;
349     class_button_box.cbWndExtra    = 0;
350     class_button_box.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
351     class_button_box.lpszClassName = BUTTON_BOX_WIN_CLASS_NAME;
352
353     class_shadow                   = class_main;
354     class_shadow.lpfnWndProc       = WINHELP_ShadowWndProc;
355     class_shadow.cbWndExtra        = 0;
356     class_shadow.hbrBackground     = (HBRUSH)(COLOR_3DDKSHADOW+1);
357     class_shadow.lpszClassName     = SHADOW_WIN_CLASS_NAME;
358
359     class_history                  = class_main;
360     class_history.lpfnWndProc      = WINHELP_HistoryWndProc;
361     class_history.lpszClassName    = HISTORY_WIN_CLASS_NAME;
362
363     return (RegisterClass(&class_main) &&
364             RegisterClass(&class_button_box) &&
365             RegisterClass(&class_shadow) &&
366             RegisterClass(&class_history));
367 }
368
369 typedef struct
370 {
371     WORD size;
372     WORD command;
373     LONG data;
374     LONG reserved;
375     WORD ofsFilename;
376     WORD ofsData;
377 } WINHELP,*LPWINHELP;
378
379 /******************************************************************
380  *              WINHELP_HandleCommand
381  *
382  *
383  */
384 static LRESULT  WINHELP_HandleCommand(HWND hSrcWnd, LPARAM lParam)
385 {
386     COPYDATASTRUCT*     cds = (COPYDATASTRUCT*)lParam;
387     WINHELP*            wh;
388
389     if (cds->dwData != 0xA1DE505)
390     {
391         WINE_FIXME("Wrong magic number (%08lx)\n", cds->dwData);
392         return 0;
393     }
394
395     wh = (WINHELP*)cds->lpData;
396
397     if (wh)
398     {
399         char*   ptr = (wh->ofsFilename) ? (LPSTR)wh + wh->ofsFilename : NULL;
400
401         WINE_TRACE("Got[%u]: cmd=%u data=%08x fn=%s\n",
402                    wh->size, wh->command, wh->data, ptr);
403         switch (wh->command)
404         {
405         case HELP_CONTEXT:
406             if (ptr)
407             {
408                 MACRO_JumpContext(ptr, "main", wh->data);
409             }
410             break;
411         case HELP_QUIT:
412             MACRO_Exit();
413             break;
414         case HELP_CONTENTS:
415             if (ptr)
416             {
417                 MACRO_JumpContents(ptr, "main");
418             }
419             break;
420         case HELP_HELPONHELP:
421             MACRO_HelpOn();
422             break;
423         /* case HELP_SETINDEX: */
424         case HELP_SETCONTENTS:
425             if (ptr)
426             {
427                 MACRO_SetContents(ptr, wh->data);
428             }
429             break;
430         case HELP_CONTEXTPOPUP:
431             if (ptr)
432             {
433                 MACRO_PopupContext(ptr, wh->data);
434             }
435             break;
436         /* case HELP_FORCEFILE:*/
437         /* case HELP_CONTEXTMENU: */
438         case HELP_FINDER:
439             /* in fact, should be the topic dialog box */
440             WINE_FIXME("HELP_FINDER: stub\n");
441             if (ptr)
442             {
443                 MACRO_JumpHash(ptr, "main", 0);
444             }
445             break;
446         /* case HELP_WM_HELP: */
447         /* case HELP_SETPOPUP_POS: */
448         /* case HELP_KEY: */
449         /* case HELP_COMMAND: */
450         /* case HELP_PARTIALKEY: */
451         /* case HELP_MULTIKEY: */
452         /* case HELP_SETWINPOS: */
453         default:
454             WINE_FIXME("Unhandled command (%x) for remote winhelp control\n", wh->command);
455             break;
456         }
457     }
458     /* Always return success for now */
459     return 1;
460 }
461
462 void            WINHELP_LayoutMainWindow(WINHELP_WINDOW* win)
463 {
464     RECT        rect, button_box_rect;
465     INT         text_top = 0;
466     HWND        hButtonBoxWnd = GetDlgItem(win->hMainWnd, CTL_ID_BUTTON);
467     HWND        hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
468
469     GetClientRect(win->hMainWnd, &rect);
470
471     /* Update button box and text Window */
472     SetWindowPos(hButtonBoxWnd, HWND_TOP,
473                  rect.left, rect.top,
474                  rect.right - rect.left,
475                  rect.bottom - rect.top, 0);
476
477     if (GetWindowRect(hButtonBoxWnd, &button_box_rect))
478         text_top = rect.top + button_box_rect.bottom - button_box_rect.top;
479
480     SetWindowPos(hTextWnd, HWND_TOP,
481                  rect.left, text_top,
482                  rect.right - rect.left,
483                  rect.bottom - text_top, 0);
484
485 }
486
487 static void     WINHELP_RememberPage(WINHELP_WINDOW* win, WINHELP_WNDPAGE* wpage)
488 {
489     unsigned        num;
490
491     if (!Globals.history.index || Globals.history.set[0].page != wpage->page)
492     {
493         num = sizeof(Globals.history.set) / sizeof(Globals.history.set[0]);
494         /* we're full, remove latest entry */
495         if (Globals.history.index == num)
496         {
497             HLPFILE_FreeHlpFile(Globals.history.set[num - 1].page->file);
498             Globals.history.index--;
499         }
500         memmove(&Globals.history.set[1], &Globals.history.set[0],
501                 Globals.history.index * sizeof(Globals.history.set[0]));
502         Globals.history.set[0] = *wpage;
503         Globals.history.index++;
504         wpage->page->file->wRefCount++;
505     }
506     if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE);
507
508     num = sizeof(win->back.set) / sizeof(win->back.set[0]);
509     if (win->back.index == num)
510     {
511         /* we're full, remove latest entry */
512         HLPFILE_FreeHlpFile(win->back.set[0].page->file);
513         memmove(&win->back.set[0], &win->back.set[1],
514                 (num - 1) * sizeof(win->back.set[0]));
515         win->back.index--;
516     }
517     win->back.set[win->back.index++] = *wpage;
518     wpage->page->file->wRefCount++;
519 }
520
521 /***********************************************************************
522  *
523  *           WINHELP_CreateHelpWindow
524  */
525 BOOL WINHELP_CreateHelpWindow(WINHELP_WNDPAGE* wpage, int nCmdShow, BOOL remember)
526 {
527     WINHELP_WINDOW*     win = NULL;
528     BOOL                bPrimary, bPopup, bReUsed = FALSE;
529     LPSTR               name;
530     HICON               hIcon;
531     HWND                hTextWnd = NULL;
532
533     bPrimary = !lstrcmpi(wpage->wininfo->name, "main");
534     bPopup = !bPrimary && (wpage->wininfo->win_style & WS_POPUP);
535
536     if (!bPopup)
537     {
538         for (win = Globals.win_list; win; win = win->next)
539         {
540             if (!lstrcmpi(win->lpszName, wpage->wininfo->name))
541             {
542                 POINT   pt = {0, 0};
543                 SIZE    sz = {0, 0};
544                 DWORD   flags = SWP_NOSIZE | SWP_NOMOVE;
545
546                 WINHELP_DeleteButtons(win);
547                 bReUsed = TRUE;
548                 SetWindowText(win->hMainWnd, WINHELP_GetCaption(wpage));
549                 if (wpage->wininfo->origin.x != CW_USEDEFAULT &&
550                     wpage->wininfo->origin.y != CW_USEDEFAULT)
551                 {
552                     pt = wpage->wininfo->origin;
553                     flags &= ~SWP_NOSIZE;
554                 }
555                 if (wpage->wininfo->size.cx != CW_USEDEFAULT &&
556                     wpage->wininfo->size.cy != CW_USEDEFAULT)
557                 {
558                     sz = wpage->wininfo->size;
559                     flags &= ~SWP_NOMOVE;
560                 }
561                 SetWindowPos(win->hMainWnd, HWND_TOP, pt.x, pt.y, sz.cx, sz.cy, flags);
562
563                 if (wpage->page && win->page && wpage->page->file != win->page->file)
564                     WINHELP_DeleteBackSet(win);
565                 WINHELP_InitFonts(win->hMainWnd);
566
567                 win->page = wpage->page;
568                 win->info = wpage->wininfo;
569                 hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
570                 WINHELP_SetupText(hTextWnd, win, wpage->relative);
571
572                 InvalidateRect(win->hMainWnd, NULL, TRUE);
573                 if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE);
574
575                 break;
576             }
577         }
578     }
579
580     if (!win)
581     {
582         /* Initialize WINHELP_WINDOW struct */
583         win = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
584                         sizeof(WINHELP_WINDOW) + strlen(wpage->wininfo->name) + 1);
585         if (!win) return FALSE;
586         win->next = Globals.win_list;
587         Globals.win_list = win;
588
589         name = (char*)win + sizeof(WINHELP_WINDOW);
590         lstrcpy(name, wpage->wininfo->name);
591         win->lpszName = name;
592         win->hHandCur = LoadCursorW(0, (LPWSTR)IDC_HAND);
593         win->back.index = 0;
594         win->font_scale = 1;
595     }
596     win->page = wpage->page;
597     win->info = wpage->wininfo;
598
599     if (!bPopup && wpage->page && remember)
600     {
601         WINHELP_RememberPage(win, wpage);
602     }
603
604     if (bPopup)
605         Globals.active_popup = win;
606     else
607         Globals.active_win = win;
608
609     /* Initialize default pushbuttons */
610     if (bPrimary && wpage->page)
611     {
612         CHAR    buffer[MAX_STRING_LEN];
613
614         LoadString(Globals.hInstance, STID_CONTENTS, buffer, sizeof(buffer));
615         MACRO_CreateButton("BTN_CONTENTS", buffer, "Contents()");
616         LoadString(Globals.hInstance, STID_SEARCH,buffer, sizeof(buffer));
617         MACRO_CreateButton("BTN_SEARCH", buffer, "Search()");
618         LoadString(Globals.hInstance, STID_BACK, buffer, sizeof(buffer));
619         MACRO_CreateButton("BTN_BACK", buffer, "Back()");
620         if (win->back.index <= 1) MACRO_DisableButton("BTN_BACK");
621         LoadString(Globals.hInstance, STID_HISTORY, buffer, sizeof(buffer));
622         MACRO_CreateButton("BTN_HISTORY", buffer, "History()");
623         LoadString(Globals.hInstance, STID_TOPICS, buffer, sizeof(buffer));
624         MACRO_CreateButton("BTN_TOPICS", buffer, "Finder()");
625     }
626
627     if (!bReUsed)
628     {
629         win->hMainWnd = CreateWindowEx((bPopup) ? WS_EX_TOOLWINDOW : 0, MAIN_WIN_CLASS_NAME,
630                                        WINHELP_GetCaption(wpage),
631                                        bPrimary ? WS_OVERLAPPEDWINDOW : wpage->wininfo->win_style,
632                                        wpage->wininfo->origin.x, wpage->wininfo->origin.y,
633                                        wpage->wininfo->size.cx, wpage->wininfo->size.cy,
634                                        bPopup ? Globals.active_win->hMainWnd : NULL,
635                                        bPrimary ? LoadMenu(Globals.hInstance, MAKEINTRESOURCE(MAIN_MENU)) : 0,
636                                        Globals.hInstance, win);
637         if (!bPopup)
638             /* Create button box and text Window */
639             CreateWindow(BUTTON_BOX_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
640                          0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_BUTTON, Globals.hInstance, NULL);
641
642         hTextWnd = CreateWindow(RICHEDIT_CLASS, NULL,
643                                 ES_MULTILINE | ES_READONLY | WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_VISIBLE,
644                                 0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_TEXT, Globals.hInstance, NULL);
645         SendMessage(hTextWnd, EM_SETEVENTMASK, 0,
646                     SendMessage(hTextWnd, EM_GETEVENTMASK, 0, 0) | ENM_MOUSEEVENTS);
647     }
648
649     hIcon = (wpage->page) ? wpage->page->file->hIcon : NULL;
650     if (!hIcon) hIcon = LoadIcon(Globals.hInstance, MAKEINTRESOURCE(IDI_WINHELP));
651     SendMessage(win->hMainWnd, WM_SETICON, ICON_SMALL, (DWORD_PTR)hIcon);
652
653     /* Initialize file specific pushbuttons */
654     if (!(wpage->wininfo->win_style & WS_POPUP) && wpage->page)
655     {
656         HLPFILE_MACRO  *macro;
657         for (macro = wpage->page->file->first_macro; macro; macro = macro->next)
658             MACRO_ExecuteMacro(macro->lpszMacro);
659
660         for (macro = wpage->page->first_macro; macro; macro = macro->next)
661             MACRO_ExecuteMacro(macro->lpszMacro);
662     }
663
664     if (bPopup)
665     {
666         DWORD   mask = SendMessage(hTextWnd, EM_GETEVENTMASK, 0, 0);
667         RECT    rect;
668
669         win->font_scale = Globals.active_win->font_scale;
670         WINHELP_SetupText(hTextWnd, win, wpage->relative);
671
672         /* we need the window to be shown for richedit to compute the size */
673         ShowWindow(win->hMainWnd, nCmdShow);
674         SendMessage(hTextWnd, EM_SETEVENTMASK, 0, mask | ENM_REQUESTRESIZE);
675         SendMessage(hTextWnd, EM_REQUESTRESIZE, 0, 0);
676         SendMessage(hTextWnd, EM_SETEVENTMASK, 0, mask);
677
678         GetWindowRect(win->hMainWnd, &rect);
679         win->hShadowWnd = CreateWindowEx(WS_EX_TOOLWINDOW, SHADOW_WIN_CLASS_NAME,
680                                          "", WS_POPUP | WS_VISIBLE,
681                                          rect.left + SHADOW_DX, rect.top + SHADOW_DY,
682                                          rect.right - rect.left,
683                                          rect.bottom - rect.top,
684                                          Globals.active_win->hMainWnd, 0,
685                                          Globals.hInstance, NULL);
686         SetWindowPos(win->hMainWnd, win->hShadowWnd, 0, 0, 0, 0,
687                      SWP_NOSIZE | SWP_NOMOVE);
688     }
689     else
690     {
691         WINHELP_SetupText(hTextWnd, win, wpage->relative);
692         WINHELP_LayoutMainWindow(win);
693         ShowWindow(win->hMainWnd, nCmdShow);
694     }
695
696     return TRUE;
697 }
698
699 /******************************************************************
700  *             WINHELP_OpenHelpWindow
701  * Main function to search for a page and display it in a window
702  */
703 BOOL WINHELP_OpenHelpWindow(HLPFILE_PAGE* (*lookup)(HLPFILE*, LONG, ULONG*),
704                             HLPFILE* hlpfile, LONG val, HLPFILE_WINDOWINFO* wi,
705                             int nCmdShow)
706 {
707     WINHELP_WNDPAGE     wpage;
708
709     wpage.page = lookup(hlpfile, val, &wpage.relative);
710     if (wpage.page) wpage.page->file->wRefCount++;
711     wpage.wininfo = wi;
712     return WINHELP_CreateHelpWindow(&wpage, nCmdShow, TRUE);
713 }
714
715 /***********************************************************************
716  *
717  *           WINHELP_FindLink
718  */
719 static HLPFILE_LINK* WINHELP_FindLink(WINHELP_WINDOW* win, LPARAM pos)
720 {
721     HLPFILE_LINK*           link;
722     POINTL                  mouse_ptl, char_ptl, char_next_ptl;
723     DWORD                   cp;
724
725     if (!win->page) return NULL;
726
727     mouse_ptl.x = (short)LOWORD(pos);
728     mouse_ptl.y = (short)HIWORD(pos);
729     cp = SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_CHARFROMPOS,
730                       0, (LPARAM)&mouse_ptl);
731
732     for (link = win->page->first_link; link; link = link->next)
733     {
734         if (link->cpMin <= cp && cp <= link->cpMax)
735         {
736             /* check whether we're at end of line */
737             SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_POSFROMCHAR,
738                          (LPARAM)&char_ptl, cp);
739             SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_POSFROMCHAR,
740                          (LPARAM)&char_next_ptl, cp + 1);
741             if (char_next_ptl.y != char_ptl.y || mouse_ptl.x >= char_next_ptl.x)
742                 link = NULL;
743             break;
744         }
745     }
746     return link;
747 }
748
749 /******************************************************************
750  *             WINHELP_HandleTextMouse
751  *
752  */
753 static BOOL WINHELP_HandleTextMouse(WINHELP_WINDOW* win, UINT msg, LPARAM lParam)
754 {
755     HLPFILE*                hlpfile;
756     HLPFILE_LINK*           link;
757     BOOL                    ret = FALSE;
758
759     switch (msg)
760     {
761     case WM_MOUSEMOVE:
762         if (WINHELP_FindLink(win, lParam))
763             SetCursor(win->hHandCur);
764         else
765             SetCursor(LoadCursor(0, IDC_ARROW));
766         break;
767
768      case WM_LBUTTONDOWN:
769          if ((win->current_link = WINHELP_FindLink(win, lParam)))
770              ret = TRUE;
771          break;
772
773     case WM_LBUTTONUP:
774         if ((link = WINHELP_FindLink(win, lParam)) && link == win->current_link)
775         {
776             HLPFILE_WINDOWINFO*     wi;
777
778             switch (link->cookie)
779             {
780             case hlp_link_link:
781                 if ((hlpfile = WINHELP_LookupHelpFile(link->string)))
782                 {
783                     if (link->window == -1)
784                         wi = win->info;
785                     else if (link->window < hlpfile->numWindows)
786                         wi = &hlpfile->windows[link->window];
787                     else
788                     {
789                         WINE_WARN("link to window %d/%d\n", link->window, hlpfile->numWindows);
790                         break;
791                     }
792                     WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, link->hash, wi, SW_NORMAL);
793                 }
794                 break;
795             case hlp_link_popup:
796                 if ((hlpfile = WINHELP_LookupHelpFile(link->string)))
797                     WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, link->hash,
798                                            WINHELP_GetPopupWindowInfo(hlpfile, win, lParam),
799                                            SW_NORMAL);
800                 break;
801             case hlp_link_macro:
802                 MACRO_ExecuteMacro(link->string);
803                 break;
804             default:
805                 WINE_FIXME("Unknown link cookie %d\n", link->cookie);
806             }
807             ret = TRUE;
808         }
809         win->current_link = NULL;
810         break;
811     }
812     return ret;
813 }
814
815 /***********************************************************************
816  *
817  *           WINHELP_MainWndProc
818  */
819 static LRESULT CALLBACK WINHELP_MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
820 {
821     WINHELP_WINDOW *win;
822     WINHELP_BUTTON *button;
823     RECT rect;
824     INT  curPos, min, max, dy, keyDelta;
825     HWND hTextWnd;
826     LRESULT ret;
827
828     if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, &ret)) return ret;
829
830     switch (msg)
831     {
832     case WM_NCCREATE:
833         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
834         SetWindowLongPtr(hWnd, 0, (ULONG_PTR) win);
835         if (!win->page && Globals.isBook)
836             PostMessage(hWnd, WM_COMMAND, MNID_FILE_OPEN, 0);
837         win->hMainWnd = hWnd;
838         break;
839
840     case WM_WINDOWPOSCHANGED:
841         WINHELP_LayoutMainWindow((WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0));
842         break;
843
844     case WM_COMMAND:
845         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
846         switch (wParam)
847         {
848             /* Menu FILE */
849         case MNID_FILE_OPEN:    MACRO_FileOpen();       break;
850         case MNID_FILE_PRINT:   MACRO_Print();          break;
851         case MNID_FILE_SETUP:   MACRO_PrinterSetup();   break;
852         case MNID_FILE_EXIT:    MACRO_Exit();           break;
853
854             /* Menu EDIT */
855         case MNID_EDIT_COPYDLG: MACRO_CopyDialog();     break;
856         case MNID_EDIT_ANNOTATE:MACRO_Annotate();       break;
857
858             /* Menu Bookmark */
859         case MNID_BKMK_DEFINE:  MACRO_BookmarkDefine(); break;
860
861             /* Menu Help */
862         case MNID_HELP_HELPON:  MACRO_HelpOn();         break;
863         case MNID_HELP_HELPTOP: MACRO_HelpOnTop();      break;
864         case MNID_HELP_ABOUT:   MACRO_About();          break;
865         case MNID_HELP_WINE:    ShellAbout(hWnd, "WINE", "Help", 0); break;
866
867             /* Context help */
868         case MNID_CTXT_ANNOTATE:MACRO_Annotate();       break;
869         case MNID_CTXT_COPY:    MACRO_CopyDialog();     break;
870         case MNID_CTXT_PRINT:   MACRO_Print();          break;
871         case MNID_CTXT_FONTS_SMALL:
872             win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
873             if (win->font_scale != 0)
874             {
875                 win->font_scale = 0;
876                 WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */);
877             }
878             break;
879         case MNID_CTXT_FONTS_NORMAL:
880             win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
881             if (win->font_scale != 1)
882             {
883                 win->font_scale = 1;
884                 WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */);
885             }
886             break;
887         case MNID_CTXT_FONTS_LARGE:
888             win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
889             if (win->font_scale != 2)
890             {
891                 win->font_scale = 2;
892                 WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */);
893             }
894             break;
895         case MNID_CTXT_HELP_DEFAULT:
896         case MNID_CTXT_HELP_VISIBLE:
897         case MNID_CTXT_HELP_NONVISIBLE:
898         case MNID_CTXT_SYSTEM_COLORS:
899             /* FIXME: NIY */
900
901         default:
902             /* Buttons */
903             for (button = win->first_button; button; button = button->next)
904                 if (wParam == button->wParam) break;
905             if (button)
906                 MACRO_ExecuteMacro(button->lpszMacro);
907             else if (!HIWORD(wParam))
908                 MessageBox(0, MAKEINTRESOURCE(STID_NOT_IMPLEMENTED),
909                            MAKEINTRESOURCE(STID_WHERROR), MB_OK);
910             break;
911         }
912         break;
913 /* EPP     case WM_DESTROY: */
914 /* EPP         if (Globals.hPopupWnd) DestroyWindow(Globals.hPopupWnd); */
915 /* EPP         break; */
916     case WM_COPYDATA:
917         return WINHELP_HandleCommand((HWND)wParam, lParam);
918
919     case WM_KEYDOWN:
920         keyDelta = 0;
921
922         switch (wParam)
923         {
924         case VK_UP:
925         case VK_DOWN:
926             keyDelta = GetSystemMetrics(SM_CXVSCROLL);
927             if (wParam == VK_UP)
928                 keyDelta = -keyDelta;
929
930         case VK_PRIOR:
931         case VK_NEXT:
932             win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
933             hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
934             curPos = GetScrollPos(hTextWnd, SB_VERT);
935             GetScrollRange(hTextWnd, SB_VERT, &min, &max);
936
937             if (keyDelta == 0)
938             {            
939                 GetClientRect(hTextWnd, &rect);
940                 keyDelta = (rect.bottom - rect.top) / 2;
941                 if (wParam == VK_PRIOR)
942                     keyDelta = -keyDelta;
943             }
944
945             curPos += keyDelta;
946             if (curPos > max)
947                  curPos = max;
948             else if (curPos < min)
949                  curPos = min;
950
951             dy = GetScrollPos(hTextWnd, SB_VERT) - curPos;
952             SetScrollPos(hTextWnd, SB_VERT, curPos, TRUE);
953             ScrollWindow(hTextWnd, 0, dy, NULL, NULL);
954             UpdateWindow(hTextWnd);
955             return 0;
956
957         case VK_ESCAPE:
958             MACRO_Exit();
959             return 0;
960         }
961         break;
962
963     case WM_NOTIFY:
964         if (wParam == CTL_ID_TEXT)
965         {
966             RECT        rc;
967
968             switch (((NMHDR*)lParam)->code)
969             {
970             case EN_MSGFILTER:
971                 {
972                     const MSGFILTER*    msgf = (const MSGFILTER*)lParam;
973                     switch (msgf->msg)
974                     {
975                     case WM_KEYUP:
976                         if (msgf->wParam == VK_ESCAPE) DestroyWindow(hWnd);
977                         break;
978                     case WM_RBUTTONDOWN:
979                     {
980                         HMENU       hMenu;
981                         POINT       pt;
982
983                         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
984                         hMenu = LoadMenu(Globals.hInstance, (LPSTR)CONTEXT_MENU);
985                         switch (win->font_scale)
986                         {
987                         case 0:
988                             CheckMenuItem(hMenu, MNID_CTXT_FONTS_SMALL,
989                                           MF_BYCOMMAND|MF_CHECKED);
990                             break;
991                         default:
992                             WINE_FIXME("Unsupported %d\n", win->font_scale);
993                         case 1:
994                             CheckMenuItem(hMenu, MNID_CTXT_FONTS_NORMAL,
995                                           MF_BYCOMMAND|MF_CHECKED);
996                             break;
997                         case 2:
998                             CheckMenuItem(hMenu, MNID_CTXT_FONTS_LARGE,
999                                           MF_BYCOMMAND|MF_CHECKED);
1000                             break;
1001                         }
1002                         pt.x = (int)(short)LOWORD(msgf->lParam);
1003                         pt.y = (int)(short)HIWORD(msgf->lParam);
1004                         ClientToScreen(msgf->nmhdr.hwndFrom, &pt);
1005                         TrackPopupMenu(GetSubMenu(hMenu, 0), TPM_LEFTALIGN|TPM_TOPALIGN,
1006                                        pt.x, pt.y, 0, hWnd, NULL);
1007                         DestroyMenu(hMenu);
1008                     }
1009                     break;
1010                     default:
1011                         return WINHELP_HandleTextMouse((WINHELP_WINDOW*)GetWindowLongPtr(hWnd, 0),
1012                                                        msgf->msg, msgf->lParam);
1013                     }
1014                 }
1015                 break;
1016
1017             case EN_REQUESTRESIZE:
1018                 rc = ((REQRESIZE*)lParam)->rc;
1019                 win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1020                 AdjustWindowRect(&rc, GetWindowLong(win->hMainWnd, GWL_STYLE),
1021                                  FALSE);
1022                 SetWindowPos(win->hMainWnd, HWND_TOP, 0, 0,
1023                              rc.right - rc.left, rc.bottom - rc.top,
1024                              SWP_NOMOVE | SWP_NOZORDER);
1025                 WINHELP_LayoutMainWindow(win);
1026                 break;
1027             }
1028         }
1029         break;
1030
1031     case WM_NCDESTROY:
1032         {
1033             BOOL bExit;
1034             win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1035             bExit = (Globals.wVersion >= 4 && !lstrcmpi(win->lpszName, "main"));
1036             WINHELP_DeleteWindow(win);
1037
1038             if (bExit) MACRO_Exit();
1039             if (!Globals.win_list)
1040                 PostQuitMessage(0);
1041         }
1042         break;
1043     }
1044     return DefWindowProc(hWnd, msg, wParam, lParam);
1045 }
1046
1047 static DWORD CALLBACK WINHELP_RtfStreamIn(DWORD_PTR cookie, BYTE* buff,
1048                                           LONG cb, LONG* pcb)
1049 {
1050     struct RtfData*     rd = (struct RtfData*)cookie;
1051
1052     if (rd->where >= rd->ptr) return 1;
1053     if (rd->where + cb > rd->ptr)
1054         cb = rd->ptr - rd->where;
1055     memcpy(buff, rd->where, cb);
1056     rd->where += cb;
1057     *pcb = cb;
1058     return 0;
1059 }
1060
1061 static void WINHELP_SetupText(HWND hTextWnd, WINHELP_WINDOW* win, ULONG relative)
1062 {
1063     SendMessage(hTextWnd, WM_SETREDRAW, FALSE, 0);
1064     SendMessage(hTextWnd, EM_SETBKGNDCOLOR, 0, (LPARAM)win->info->sr_color);
1065     /* set word-wrap to window size (undocumented) */
1066     SendMessage(hTextWnd, EM_SETTARGETDEVICE, 0, 0);
1067     if (win->page)
1068     {
1069         struct RtfData  rd;
1070         EDITSTREAM      es;
1071         unsigned        cp = 0;
1072         POINTL          ptl;
1073         POINT           pt;
1074
1075
1076         if (HLPFILE_BrowsePage(win->page, &rd, win->font_scale, relative))
1077         {
1078             rd.where = rd.data;
1079             es.dwCookie = (DWORD_PTR)&rd;
1080             es.dwError = 0;
1081             es.pfnCallback = WINHELP_RtfStreamIn;
1082
1083             SendMessageW(hTextWnd, EM_STREAMIN, SF_RTF, (LPARAM)&es);
1084             cp = rd.char_pos_rel;
1085         }
1086         /* FIXME: else leaking potentially the rd.first_link chain */
1087         HeapFree(GetProcessHeap(), 0, rd.data);
1088         SendMessage(hTextWnd, EM_POSFROMCHAR, (WPARAM)&ptl, cp ? cp - 1 : 0);
1089         pt.x = 0; pt.y = ptl.y;
1090         SendMessage(hTextWnd, EM_SETSCROLLPOS, 0, (LPARAM)&pt);
1091     }
1092     else
1093     {
1094         SendMessage(hTextWnd, WM_SETTEXT, 0, (LPARAM)"");
1095     }
1096     SendMessage(hTextWnd, WM_SETREDRAW, TRUE, 0);
1097     InvalidateRect(hTextWnd, NULL, TRUE);
1098 }
1099
1100 /***********************************************************************
1101  *
1102  *           WINHELP_ButtonBoxWndProc
1103  */
1104 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1105 {
1106     WINDOWPOS      *winpos;
1107     WINHELP_WINDOW *win;
1108     WINHELP_BUTTON *button;
1109     SIZE button_size;
1110     INT  x, y;
1111
1112     if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0L;
1113
1114     switch (msg)
1115     {
1116     case WM_WINDOWPOSCHANGING:
1117         winpos = (WINDOWPOS*) lParam;
1118         win = (WINHELP_WINDOW*) GetWindowLongPtr(GetParent(hWnd), 0);
1119
1120         /* Update buttons */
1121         button_size.cx = 0;
1122         button_size.cy = 0;
1123         for (button = win->first_button; button; button = button->next)
1124         {
1125             HDC  hDc;
1126             SIZE textsize;
1127             if (!button->hWnd)
1128             {
1129                 button->hWnd = CreateWindow(STRING_BUTTON, button->lpszName,
1130                                             WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
1131                                             0, 0, 0, 0,
1132                                             hWnd, (HMENU) button->wParam,
1133                                             Globals.hInstance, 0);
1134                 if (button->hWnd) {
1135                     if (Globals.button_proc == NULL)
1136                         Globals.button_proc = (WNDPROC) GetWindowLongPtr(button->hWnd, GWLP_WNDPROC);
1137                     SetWindowLongPtr(button->hWnd, GWLP_WNDPROC, (LONG_PTR) WINHELP_ButtonWndProc);
1138                 }
1139             }
1140             hDc = GetDC(button->hWnd);
1141             GetTextExtentPoint(hDc, button->lpszName,
1142                                lstrlen(button->lpszName), &textsize);
1143             ReleaseDC(button->hWnd, hDc);
1144
1145             button_size.cx = max(button_size.cx, textsize.cx + BUTTON_CX);
1146             button_size.cy = max(button_size.cy, textsize.cy + BUTTON_CY);
1147         }
1148
1149         x = 0;
1150         y = 0;
1151         for (button = win->first_button; button; button = button->next)
1152         {
1153             SetWindowPos(button->hWnd, HWND_TOP, x, y, button_size.cx, button_size.cy, 0);
1154
1155             if (x + 2 * button_size.cx <= winpos->cx)
1156                 x += button_size.cx;
1157             else
1158                 x = 0, y += button_size.cy;
1159         }
1160         winpos->cy = y + (x ? button_size.cy : 0);
1161         break;
1162
1163     case WM_COMMAND:
1164         SendMessage(GetParent(hWnd), msg, wParam, lParam);
1165         break;
1166
1167     case WM_KEYDOWN:
1168         switch (wParam)
1169         {
1170         case VK_UP:
1171         case VK_DOWN:
1172         case VK_PRIOR:
1173         case VK_NEXT:
1174         case VK_ESCAPE:
1175             return SendMessage(GetParent(hWnd), msg, wParam, lParam);
1176         }
1177         break;
1178     }
1179
1180     return DefWindowProc(hWnd, msg, wParam, lParam);
1181 }
1182
1183 /***********************************************************************
1184  *
1185  *           WINHELP_ButtonWndProc
1186  */
1187 static LRESULT CALLBACK WINHELP_ButtonWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1188 {
1189     if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0;
1190
1191     if (msg == WM_KEYDOWN)
1192     {
1193         switch (wParam)
1194         {
1195         case VK_UP:
1196         case VK_DOWN:
1197         case VK_PRIOR:
1198         case VK_NEXT:
1199         case VK_ESCAPE:
1200             return SendMessage(GetParent(hWnd), msg, wParam, lParam);
1201         }
1202     }
1203
1204     return CallWindowProc(Globals.button_proc, hWnd, msg, wParam, lParam);
1205 }
1206
1207 /******************************************************************
1208  *              WINHELP_HistoryWndProc
1209  *
1210  *
1211  */
1212 static LRESULT CALLBACK WINHELP_HistoryWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1213 {
1214     WINHELP_WINDOW*     win;
1215     PAINTSTRUCT         ps;
1216     HDC                 hDc;
1217     TEXTMETRIC          tm;
1218     unsigned int        i;
1219     RECT                r;
1220
1221     switch (msg)
1222     {
1223     case WM_NCCREATE:
1224         win = (WINHELP_WINDOW*)((LPCREATESTRUCT)lParam)->lpCreateParams;
1225         SetWindowLongPtr(hWnd, 0, (ULONG_PTR)win);
1226         win->hHistoryWnd = hWnd;
1227         break;
1228     case WM_CREATE:
1229         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1230         hDc = GetDC(hWnd);
1231         GetTextMetrics(hDc, &tm);
1232         GetWindowRect(hWnd, &r);
1233
1234         r.right = r.left + 30 * tm.tmAveCharWidth;
1235         r.bottom = r.top + (sizeof(Globals.history.set) / sizeof(Globals.history.set[0])) * tm.tmHeight;
1236         AdjustWindowRect(&r, GetWindowLong(hWnd, GWL_STYLE), FALSE);
1237         if (r.left < 0) {r.right -= r.left; r.left = 0;}
1238         if (r.top < 0) {r.bottom -= r.top; r.top = 0;}
1239
1240         MoveWindow(hWnd, r.left, r.top, r.right, r.bottom, TRUE);
1241         ReleaseDC(hWnd, hDc);
1242         break;
1243     case WM_LBUTTONDOWN:
1244         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1245         hDc = GetDC(hWnd);
1246         GetTextMetrics(hDc, &tm);
1247         i = HIWORD(lParam) / tm.tmHeight;
1248         if (i < Globals.history.index)
1249             WINHELP_CreateHelpWindow(&Globals.history.set[i], SW_SHOW, TRUE);
1250         ReleaseDC(hWnd, hDc);
1251         break;
1252     case WM_PAINT:
1253         hDc = BeginPaint(hWnd, &ps);
1254         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1255         GetTextMetrics(hDc, &tm);
1256
1257         for (i = 0; i < Globals.history.index; i++)
1258         {
1259             if (Globals.history.set[i].page->file == Globals.active_win->page->file)
1260             {
1261                 TextOut(hDc, 0, i * tm.tmHeight,
1262                         Globals.history.set[i].page->lpszTitle,
1263                         strlen(Globals.history.set[i].page->lpszTitle));
1264             }
1265             else
1266             {
1267                 char        buffer[1024];
1268                 const char* ptr1;
1269                 const char* ptr2;
1270                 unsigned    len;
1271
1272                 ptr1 = strrchr(Globals.history.set[i].page->file->lpszPath, '\\');
1273                 if (!ptr1) ptr1 = Globals.history.set[i].page->file->lpszPath;
1274                 else ptr1++;
1275                 ptr2 = strrchr(ptr1, '.');
1276                 len = ptr2 ? ptr2 - ptr1 : strlen(ptr1);
1277                 if (len > sizeof(buffer)) len = sizeof(buffer);
1278                 memcpy(buffer, ptr1, len);
1279                 if (len < sizeof(buffer)) buffer[len++] = ':';
1280                 strncpy(&buffer[len], Globals.history.set[i].page->lpszTitle, sizeof(buffer) - len);
1281                 buffer[sizeof(buffer) - 1] = '\0';
1282                 TextOut(hDc, 0, i * tm.tmHeight, buffer, strlen(buffer));
1283             }
1284         }
1285         EndPaint(hWnd, &ps);
1286         break;
1287     case WM_DESTROY:
1288         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1289         if (hWnd == win->hHistoryWnd)
1290             win->hHistoryWnd = 0;
1291         break;
1292     }
1293     return DefWindowProc(hWnd, msg, wParam, lParam);
1294 }
1295
1296 /***********************************************************************
1297  *
1298  *           WINHELP_ShadowWndProc
1299  */
1300 static LRESULT CALLBACK WINHELP_ShadowWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1301 {
1302     if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0;
1303     return WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL) ? 0L : DefWindowProc(hWnd, msg, wParam, lParam);
1304 }
1305
1306 /***********************************************************************
1307  *
1308  *           WINHELP_CheckPopup
1309  */
1310 static BOOL WINHELP_CheckPopup(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT* lret)
1311 {
1312     HWND        hPopup;
1313
1314     if (!Globals.active_popup) return FALSE;
1315
1316     switch (msg)
1317     {
1318     case WM_NOTIFY:
1319         {
1320             MSGFILTER*  msgf = (MSGFILTER*)lParam;
1321             if (msgf->nmhdr.code == EN_MSGFILTER)
1322             {
1323                 if (!WINHELP_CheckPopup(hWnd, msgf->msg, msgf->wParam, msgf->lParam, NULL))
1324                     return FALSE;
1325                 if (lret) *lret = 1;
1326                 return TRUE;
1327             }
1328         }
1329         break;
1330     case WM_ACTIVATE:
1331         if (wParam != WA_INACTIVE || (HWND)lParam == Globals.active_win->hMainWnd ||
1332             (HWND)lParam == Globals.active_popup->hMainWnd ||
1333             GetWindow((HWND)lParam, GW_OWNER) == Globals.active_win->hMainWnd)
1334             break;
1335     case WM_LBUTTONUP:
1336     case WM_LBUTTONDOWN:
1337         if (WINHELP_HandleTextMouse(Globals.active_popup, msg, lParam) && msg == WM_LBUTTONDOWN)
1338             return FALSE;
1339         /* fall through */
1340     case WM_MBUTTONDOWN:
1341     case WM_RBUTTONDOWN:
1342     case WM_NCLBUTTONDOWN:
1343     case WM_NCMBUTTONDOWN:
1344     case WM_NCRBUTTONDOWN:
1345         hPopup = Globals.active_popup->hMainWnd;
1346         Globals.active_popup = NULL;
1347         DestroyWindow(hPopup);
1348         return TRUE;
1349     }
1350     return FALSE;
1351 }
1352
1353 /******************************************************************
1354  *              WINHELP_DeleteButtons
1355  *
1356  */
1357 static void WINHELP_DeleteButtons(WINHELP_WINDOW* win)
1358 {
1359     WINHELP_BUTTON*     b;
1360     WINHELP_BUTTON*     bp;
1361
1362     for (b = win->first_button; b; b = bp)
1363     {
1364         DestroyWindow(b->hWnd);
1365         bp = b->next;
1366         HeapFree(GetProcessHeap(), 0, b);
1367     }
1368     win->first_button = NULL;
1369 }
1370
1371 /******************************************************************
1372  *              WINHELP_DeleteBackSet
1373  *
1374  */
1375 void WINHELP_DeleteBackSet(WINHELP_WINDOW* win)
1376 {
1377     unsigned int i;
1378
1379     for (i = 0; i < win->back.index; i++)
1380     {
1381         HLPFILE_FreeHlpFile(win->back.set[i].page->file);
1382         win->back.set[i].page = NULL;
1383     }
1384     win->back.index = 0;
1385 }
1386
1387 /******************************************************************
1388  *             WINHELP_DeletePageLinks
1389  *
1390  */
1391 static void WINHELP_DeletePageLinks(HLPFILE_PAGE* page)
1392 {
1393     HLPFILE_LINK*       curr;
1394     HLPFILE_LINK*       next;
1395
1396     for (curr = page->first_link; curr; curr = next)
1397     {
1398         next = curr->next;
1399         HeapFree(GetProcessHeap(), 0, curr);
1400     }
1401 }
1402
1403 /***********************************************************************
1404  *
1405  *           WINHELP_DeleteWindow
1406  */
1407 static void WINHELP_DeleteWindow(WINHELP_WINDOW* win)
1408 {
1409     WINHELP_WINDOW**    w;
1410
1411     for (w = &Globals.win_list; *w; w = &(*w)->next)
1412     {
1413         if (*w == win)
1414         {
1415             *w = win->next;
1416             break;
1417         }
1418     }
1419
1420     if (Globals.active_win == win)
1421     {
1422         Globals.active_win = Globals.win_list;
1423         if (Globals.win_list)
1424             SetActiveWindow(Globals.win_list->hMainWnd);
1425     }
1426
1427     if (win == Globals.active_popup)
1428         Globals.active_popup = NULL;
1429
1430     WINHELP_DeleteButtons(win);
1431
1432     if (win->page) WINHELP_DeletePageLinks(win->page);
1433     if (win->hShadowWnd) DestroyWindow(win->hShadowWnd);
1434     if (win->hHistoryWnd) DestroyWindow(win->hHistoryWnd);
1435
1436     DeleteObject(win->hBrush);
1437
1438     WINHELP_DeleteBackSet(win);
1439
1440     if (win->page) HLPFILE_FreeHlpFile(win->page->file);
1441     HeapFree(GetProcessHeap(), 0, win);
1442 }
1443
1444 /***********************************************************************
1445  *
1446  *           WINHELP_InitFonts
1447  */
1448 static void WINHELP_InitFonts(HWND hWnd)
1449 {
1450     WINHELP_WINDOW *win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1451     LOGFONT logfontlist[] = {
1452         {-10, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1453         {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1454         {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1455         {-12, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1456         {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1457         {-10, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1458         { -8, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"}};
1459 #define FONTS_LEN (sizeof(logfontlist)/sizeof(*logfontlist))
1460
1461     static HFONT fonts[FONTS_LEN];
1462     static BOOL init = 0;
1463
1464     win->fonts_len = FONTS_LEN;
1465     win->fonts = fonts;
1466
1467     if (!init)
1468     {
1469         UINT i;
1470
1471         for (i = 0; i < FONTS_LEN; i++)
1472         {
1473             fonts[i] = CreateFontIndirect(&logfontlist[i]);
1474         }
1475
1476         init = 1;
1477     }
1478 }
1479
1480 /***********************************************************************
1481  *
1482  *           WINHELP_MessageBoxIDS_s
1483  */
1484 INT WINHELP_MessageBoxIDS_s(UINT ids_text, LPCSTR str, UINT ids_title, WORD type)
1485 {
1486     CHAR text[MAX_STRING_LEN];
1487     CHAR newtext[MAX_STRING_LEN + MAX_PATH];
1488
1489     LoadString(Globals.hInstance, ids_text, text, sizeof(text));
1490     wsprintf(newtext, text, str);
1491
1492     return MessageBox(0, newtext, MAKEINTRESOURCE(ids_title), type);
1493 }
1494
1495 /**************************************************************************
1496  * cb_KWBTree
1497  *
1498  * HLPFILE_BPTreeCallback enumeration function for '|KWBTREE' internal file.
1499  *
1500  */
1501 static void cb_KWBTree(void *p, void **next, void *cookie)
1502 {
1503     HWND hListWnd = (HWND)cookie;
1504     int count;
1505
1506     WINE_TRACE("Adding '%s' to search list\n", (char *)p);
1507     SendMessage(hListWnd, LB_INSERTSTRING, -1, (LPARAM)p);
1508     count = SendMessage(hListWnd, LB_GETCOUNT, 0, 0);
1509     SendMessage(hListWnd, LB_SETITEMDATA, count-1, (LPARAM)p);
1510     *next = (char*)p + strlen((char*)p) + 7;
1511 }
1512
1513 /**************************************************************************
1514  * WINHELP_IndexDlgProc
1515  *
1516  * Index dialog callback function.
1517  *
1518  * nResult passed to EndDialog:
1519  *   1: CANCEL button
1520  *  >1: valid offset value +2.
1521  *  EndDialog itself can return 0 (error).
1522  */
1523 INT_PTR CALLBACK WINHELP_SearchDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1524 {
1525     static HLPFILE *file;
1526     int sel;
1527     ULONG offset = 1;
1528
1529     switch (msg)
1530     {
1531     case WM_INITDIALOG:
1532         file = (HLPFILE *)lParam;
1533         HLPFILE_BPTreeEnum(file->kwbtree, cb_KWBTree,
1534                            GetDlgItem(hWnd, IDC_INDEXLIST));
1535         return TRUE;
1536     case WM_COMMAND:
1537         switch (LOWORD(wParam))
1538         {
1539         case IDOK:
1540             sel = SendDlgItemMessage(hWnd, IDC_INDEXLIST, LB_GETCURSEL, 0, 0);
1541             if (sel != LB_ERR)
1542             {
1543                 BYTE *p;
1544                 int count;
1545
1546                 p = (BYTE*)SendDlgItemMessage(hWnd, IDC_INDEXLIST,
1547                                               LB_GETITEMDATA, sel, 0);
1548                 count = *(short*)((char *)p + strlen((char *)p) + 1);
1549                 if (count > 1)
1550                 {
1551                     MessageBox(hWnd, "count > 1 not supported yet", "Error", MB_OK | MB_ICONSTOP);
1552                     return TRUE;
1553                 }
1554                 offset = *(ULONG*)((char *)p + strlen((char *)p) + 3);
1555                 offset = *(long*)(file->kwdata + offset + 9);
1556                 if (offset == 0xFFFFFFFF)
1557                 {
1558                     MessageBox(hWnd, "macro keywords not supported yet", "Error", MB_OK | MB_ICONSTOP);
1559                     return TRUE;
1560                 }
1561                 offset += 2;
1562             }
1563             /* Fall through */
1564         case IDCANCEL:
1565             EndDialog(hWnd, offset);
1566             return TRUE;
1567         default:
1568             break;
1569         }
1570     default:
1571         break;
1572     }
1573     return FALSE;
1574 }
1575
1576 /**************************************************************************
1577  * WINHELP_CreateIndexWindow
1578  *
1579  * Displays a dialog with keywords of current help file.
1580  *
1581  */
1582 BOOL WINHELP_CreateIndexWindow(void)
1583 {
1584     int ret;
1585     HLPFILE *hlpfile;
1586
1587     if (Globals.active_win && Globals.active_win->page && Globals.active_win->page->file)
1588         hlpfile = Globals.active_win->page->file;
1589     else
1590         return FALSE;
1591
1592     if (hlpfile->kwbtree == NULL)
1593     {
1594         WINE_TRACE("No index provided\n");
1595         return FALSE;
1596     }
1597
1598     ret = DialogBoxParam(Globals.hInstance, MAKEINTRESOURCE(IDD_INDEX),
1599                          Globals.active_win->hMainWnd, WINHELP_SearchDlgProc,
1600                          (LPARAM)hlpfile);
1601     if (ret > 1)
1602     {
1603         ret -= 2;
1604         WINE_TRACE("got %d as an offset\n", ret);
1605         WINHELP_OpenHelpWindow(HLPFILE_PageByOffset, hlpfile, ret,
1606                                Globals.active_win->info, SW_NORMAL);
1607     }
1608     return TRUE;
1609 }