winedbg: Fix "floating pointer" typo.
[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 static BOOL WINHELP_HasWorkingWindow(void)
380 {
381     if (!Globals.active_win) return FALSE;
382     if (Globals.active_win->next || Globals.win_list != Globals.active_win) return TRUE;
383     return Globals.active_win->page != NULL && Globals.active_win->page->file != NULL;
384 }
385
386 /******************************************************************
387  *              WINHELP_HandleCommand
388  *
389  *
390  */
391 static LRESULT  WINHELP_HandleCommand(HWND hSrcWnd, LPARAM lParam)
392 {
393     COPYDATASTRUCT*     cds = (COPYDATASTRUCT*)lParam;
394     WINHELP*            wh;
395
396     if (cds->dwData != 0xA1DE505)
397     {
398         WINE_FIXME("Wrong magic number (%08lx)\n", cds->dwData);
399         return 0;
400     }
401
402     wh = (WINHELP*)cds->lpData;
403
404     if (wh)
405     {
406         char*   ptr = (wh->ofsFilename) ? (LPSTR)wh + wh->ofsFilename : NULL;
407
408         WINE_TRACE("Got[%u]: cmd=%u data=%08x fn=%s\n",
409                    wh->size, wh->command, wh->data, ptr);
410         switch (wh->command)
411         {
412         case HELP_CONTEXT:
413             if (ptr)
414             {
415                 MACRO_JumpContext(ptr, "main", wh->data);
416             }
417             if (!WINHELP_HasWorkingWindow()) MACRO_Exit();
418             break;
419         case HELP_QUIT:
420             MACRO_Exit();
421             break;
422         case HELP_CONTENTS:
423             if (ptr)
424             {
425                 MACRO_JumpContents(ptr, "main");
426             }
427             if (!WINHELP_HasWorkingWindow()) MACRO_Exit();
428             break;
429         case HELP_HELPONHELP:
430             MACRO_HelpOn();
431             if (!WINHELP_HasWorkingWindow()) MACRO_Exit();
432             break;
433         /* case HELP_SETINDEX: */
434         case HELP_SETCONTENTS:
435             if (ptr)
436             {
437                 MACRO_SetContents(ptr, wh->data);
438             }
439             break;
440         case HELP_CONTEXTPOPUP:
441             if (ptr)
442             {
443                 MACRO_PopupContext(ptr, wh->data);
444             }
445             break;
446         /* case HELP_FORCEFILE:*/
447         /* case HELP_CONTEXTMENU: */
448         case HELP_FINDER:
449             /* in fact, should be the topic dialog box */
450             WINE_FIXME("HELP_FINDER: stub\n");
451             if (ptr)
452             {
453                 MACRO_JumpHash(ptr, "main", 0);
454             }
455             break;
456         /* case HELP_WM_HELP: */
457         /* case HELP_SETPOPUP_POS: */
458         /* case HELP_KEY: */
459         /* case HELP_COMMAND: */
460         /* case HELP_PARTIALKEY: */
461         /* case HELP_MULTIKEY: */
462         /* case HELP_SETWINPOS: */
463         default:
464             WINE_FIXME("Unhandled command (%x) for remote winhelp control\n", wh->command);
465             break;
466         }
467     }
468     /* Always return success for now */
469     return 1;
470 }
471
472 void            WINHELP_LayoutMainWindow(WINHELP_WINDOW* win)
473 {
474     RECT        rect, button_box_rect;
475     INT         text_top = 0;
476     HWND        hButtonBoxWnd = GetDlgItem(win->hMainWnd, CTL_ID_BUTTON);
477     HWND        hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
478
479     GetClientRect(win->hMainWnd, &rect);
480
481     /* Update button box and text Window */
482     SetWindowPos(hButtonBoxWnd, HWND_TOP,
483                  rect.left, rect.top,
484                  rect.right - rect.left,
485                  rect.bottom - rect.top, 0);
486
487     if (GetWindowRect(hButtonBoxWnd, &button_box_rect))
488         text_top = rect.top + button_box_rect.bottom - button_box_rect.top;
489
490     SetWindowPos(hTextWnd, HWND_TOP,
491                  rect.left, text_top,
492                  rect.right - rect.left,
493                  rect.bottom - text_top, 0);
494
495 }
496
497 static void     WINHELP_RememberPage(WINHELP_WINDOW* win, WINHELP_WNDPAGE* wpage)
498 {
499     unsigned        num;
500
501     if (!Globals.history.index || Globals.history.set[0].page != wpage->page)
502     {
503         num = sizeof(Globals.history.set) / sizeof(Globals.history.set[0]);
504         /* we're full, remove latest entry */
505         if (Globals.history.index == num)
506         {
507             HLPFILE_FreeHlpFile(Globals.history.set[num - 1].page->file);
508             Globals.history.index--;
509         }
510         memmove(&Globals.history.set[1], &Globals.history.set[0],
511                 Globals.history.index * sizeof(Globals.history.set[0]));
512         Globals.history.set[0] = *wpage;
513         Globals.history.index++;
514         wpage->page->file->wRefCount++;
515     }
516     if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE);
517
518     num = sizeof(win->back.set) / sizeof(win->back.set[0]);
519     if (win->back.index == num)
520     {
521         /* we're full, remove latest entry */
522         HLPFILE_FreeHlpFile(win->back.set[0].page->file);
523         memmove(&win->back.set[0], &win->back.set[1],
524                 (num - 1) * sizeof(win->back.set[0]));
525         win->back.index--;
526     }
527     win->back.set[win->back.index++] = *wpage;
528     wpage->page->file->wRefCount++;
529 }
530
531 /***********************************************************************
532  *
533  *           WINHELP_CreateHelpWindow
534  */
535 BOOL WINHELP_CreateHelpWindow(WINHELP_WNDPAGE* wpage, int nCmdShow, BOOL remember)
536 {
537     WINHELP_WINDOW*     win = NULL;
538     BOOL                bPrimary, bPopup, bReUsed = FALSE;
539     LPSTR               name;
540     HICON               hIcon;
541     HWND                hTextWnd = NULL;
542
543     bPrimary = !lstrcmpi(wpage->wininfo->name, "main");
544     bPopup = !bPrimary && (wpage->wininfo->win_style & WS_POPUP);
545
546     if (!bPopup)
547     {
548         for (win = Globals.win_list; win; win = win->next)
549         {
550             if (!lstrcmpi(win->lpszName, wpage->wininfo->name))
551             {
552                 POINT   pt = {0, 0};
553                 SIZE    sz = {0, 0};
554                 DWORD   flags = SWP_NOSIZE | SWP_NOMOVE;
555
556                 WINHELP_DeleteButtons(win);
557                 bReUsed = TRUE;
558                 SetWindowText(win->hMainWnd, WINHELP_GetCaption(wpage));
559                 if (wpage->wininfo->origin.x != CW_USEDEFAULT &&
560                     wpage->wininfo->origin.y != CW_USEDEFAULT)
561                 {
562                     pt = wpage->wininfo->origin;
563                     flags &= ~SWP_NOSIZE;
564                 }
565                 if (wpage->wininfo->size.cx != CW_USEDEFAULT &&
566                     wpage->wininfo->size.cy != CW_USEDEFAULT)
567                 {
568                     sz = wpage->wininfo->size;
569                     flags &= ~SWP_NOMOVE;
570                 }
571                 SetWindowPos(win->hMainWnd, HWND_TOP, pt.x, pt.y, sz.cx, sz.cy, flags);
572
573                 if (wpage->page && win->page && wpage->page->file != win->page->file)
574                     WINHELP_DeleteBackSet(win);
575                 WINHELP_InitFonts(win->hMainWnd);
576
577                 win->page = wpage->page;
578                 win->info = wpage->wininfo;
579                 hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
580                 WINHELP_SetupText(hTextWnd, win, wpage->relative);
581
582                 InvalidateRect(win->hMainWnd, NULL, TRUE);
583                 if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE);
584
585                 break;
586             }
587         }
588     }
589
590     if (!win)
591     {
592         /* Initialize WINHELP_WINDOW struct */
593         win = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
594                         sizeof(WINHELP_WINDOW) + strlen(wpage->wininfo->name) + 1);
595         if (!win) return FALSE;
596         win->next = Globals.win_list;
597         Globals.win_list = win;
598
599         name = (char*)win + sizeof(WINHELP_WINDOW);
600         lstrcpy(name, wpage->wininfo->name);
601         win->lpszName = name;
602         win->hHandCur = LoadCursorW(0, (LPWSTR)IDC_HAND);
603         win->back.index = 0;
604         win->font_scale = 1;
605     }
606     win->page = wpage->page;
607     win->info = wpage->wininfo;
608
609     if (!bPopup && wpage->page && remember)
610     {
611         WINHELP_RememberPage(win, wpage);
612     }
613
614     if (bPopup)
615         Globals.active_popup = win;
616     else
617         Globals.active_win = win;
618
619     /* Initialize default pushbuttons */
620     if (bPrimary && wpage->page)
621     {
622         CHAR    buffer[MAX_STRING_LEN];
623
624         LoadString(Globals.hInstance, STID_CONTENTS, buffer, sizeof(buffer));
625         MACRO_CreateButton("BTN_CONTENTS", buffer, "Contents()");
626         LoadString(Globals.hInstance, STID_SEARCH,buffer, sizeof(buffer));
627         MACRO_CreateButton("BTN_SEARCH", buffer, "Search()");
628         LoadString(Globals.hInstance, STID_BACK, buffer, sizeof(buffer));
629         MACRO_CreateButton("BTN_BACK", buffer, "Back()");
630         if (win->back.index <= 1) MACRO_DisableButton("BTN_BACK");
631         LoadString(Globals.hInstance, STID_TOPICS, buffer, sizeof(buffer));
632         MACRO_CreateButton("BTN_TOPICS", buffer, "Finder()");
633     }
634
635     if (!bReUsed)
636     {
637         win->hMainWnd = CreateWindowEx((bPopup) ? WS_EX_TOOLWINDOW : 0, MAIN_WIN_CLASS_NAME,
638                                        WINHELP_GetCaption(wpage),
639                                        bPrimary ? WS_OVERLAPPEDWINDOW : wpage->wininfo->win_style,
640                                        wpage->wininfo->origin.x, wpage->wininfo->origin.y,
641                                        wpage->wininfo->size.cx, wpage->wininfo->size.cy,
642                                        bPopup ? Globals.active_win->hMainWnd : NULL,
643                                        bPrimary ? LoadMenu(Globals.hInstance, MAKEINTRESOURCE(MAIN_MENU)) : 0,
644                                        Globals.hInstance, win);
645         if (!bPopup)
646             /* Create button box and text Window */
647             CreateWindow(BUTTON_BOX_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
648                          0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_BUTTON, Globals.hInstance, NULL);
649
650         hTextWnd = CreateWindow(RICHEDIT_CLASS, NULL,
651                                 ES_MULTILINE | ES_READONLY | WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_VISIBLE,
652                                 0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_TEXT, Globals.hInstance, NULL);
653         SendMessage(hTextWnd, EM_SETEVENTMASK, 0,
654                     SendMessage(hTextWnd, EM_GETEVENTMASK, 0, 0) | ENM_MOUSEEVENTS);
655     }
656
657     hIcon = (wpage->page) ? wpage->page->file->hIcon : NULL;
658     if (!hIcon) hIcon = LoadIcon(Globals.hInstance, MAKEINTRESOURCE(IDI_WINHELP));
659     SendMessage(win->hMainWnd, WM_SETICON, ICON_SMALL, (DWORD_PTR)hIcon);
660
661     /* Initialize file specific pushbuttons */
662     if (!(wpage->wininfo->win_style & WS_POPUP) && wpage->page)
663     {
664         HLPFILE_MACRO  *macro;
665         for (macro = wpage->page->file->first_macro; macro; macro = macro->next)
666             MACRO_ExecuteMacro(macro->lpszMacro);
667
668         for (macro = wpage->page->first_macro; macro; macro = macro->next)
669             MACRO_ExecuteMacro(macro->lpszMacro);
670     }
671
672     if (bPopup)
673     {
674         DWORD   mask = SendMessage(hTextWnd, EM_GETEVENTMASK, 0, 0);
675         RECT    rect;
676
677         win->font_scale = Globals.active_win->font_scale;
678         WINHELP_SetupText(hTextWnd, win, wpage->relative);
679
680         /* we need the window to be shown for richedit to compute the size */
681         ShowWindow(win->hMainWnd, nCmdShow);
682         SendMessage(hTextWnd, EM_SETEVENTMASK, 0, mask | ENM_REQUESTRESIZE);
683         SendMessage(hTextWnd, EM_REQUESTRESIZE, 0, 0);
684         SendMessage(hTextWnd, EM_SETEVENTMASK, 0, mask);
685
686         GetWindowRect(win->hMainWnd, &rect);
687         win->hShadowWnd = CreateWindowEx(WS_EX_TOOLWINDOW, SHADOW_WIN_CLASS_NAME,
688                                          "", WS_POPUP | WS_VISIBLE,
689                                          rect.left + SHADOW_DX, rect.top + SHADOW_DY,
690                                          rect.right - rect.left,
691                                          rect.bottom - rect.top,
692                                          Globals.active_win->hMainWnd, 0,
693                                          Globals.hInstance, NULL);
694         SetWindowPos(win->hMainWnd, win->hShadowWnd, 0, 0, 0, 0,
695                      SWP_NOSIZE | SWP_NOMOVE);
696     }
697     else
698     {
699         WINHELP_SetupText(hTextWnd, win, wpage->relative);
700         WINHELP_LayoutMainWindow(win);
701         ShowWindow(win->hMainWnd, nCmdShow);
702     }
703
704     return TRUE;
705 }
706
707 /******************************************************************
708  *             WINHELP_OpenHelpWindow
709  * Main function to search for a page and display it in a window
710  */
711 BOOL WINHELP_OpenHelpWindow(HLPFILE_PAGE* (*lookup)(HLPFILE*, LONG, ULONG*),
712                             HLPFILE* hlpfile, LONG val, HLPFILE_WINDOWINFO* wi,
713                             int nCmdShow)
714 {
715     WINHELP_WNDPAGE     wpage;
716
717     wpage.page = lookup(hlpfile, val, &wpage.relative);
718     if (wpage.page) wpage.page->file->wRefCount++;
719     wpage.wininfo = wi;
720     return WINHELP_CreateHelpWindow(&wpage, nCmdShow, TRUE);
721 }
722
723 /***********************************************************************
724  *
725  *           WINHELP_FindLink
726  */
727 static HLPFILE_LINK* WINHELP_FindLink(WINHELP_WINDOW* win, LPARAM pos)
728 {
729     HLPFILE_LINK*           link;
730     POINTL                  mouse_ptl, char_ptl, char_next_ptl;
731     DWORD                   cp;
732
733     if (!win->page) return NULL;
734
735     mouse_ptl.x = (short)LOWORD(pos);
736     mouse_ptl.y = (short)HIWORD(pos);
737     cp = SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_CHARFROMPOS,
738                       0, (LPARAM)&mouse_ptl);
739
740     for (link = win->page->first_link; link; link = link->next)
741     {
742         if (link->cpMin <= cp && cp <= link->cpMax)
743         {
744             /* check whether we're at end of line */
745             SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_POSFROMCHAR,
746                          (LPARAM)&char_ptl, cp);
747             SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_POSFROMCHAR,
748                          (LPARAM)&char_next_ptl, cp + 1);
749             if (char_next_ptl.y != char_ptl.y || mouse_ptl.x >= char_next_ptl.x)
750                 link = NULL;
751             break;
752         }
753     }
754     return link;
755 }
756
757 /******************************************************************
758  *             WINHELP_HandleTextMouse
759  *
760  */
761 static BOOL WINHELP_HandleTextMouse(WINHELP_WINDOW* win, UINT msg, LPARAM lParam)
762 {
763     HLPFILE*                hlpfile;
764     HLPFILE_LINK*           link;
765     BOOL                    ret = FALSE;
766
767     switch (msg)
768     {
769     case WM_MOUSEMOVE:
770         if (WINHELP_FindLink(win, lParam))
771             SetCursor(win->hHandCur);
772         else
773             SetCursor(LoadCursor(0, IDC_ARROW));
774         break;
775
776      case WM_LBUTTONDOWN:
777          if ((win->current_link = WINHELP_FindLink(win, lParam)))
778              ret = TRUE;
779          break;
780
781     case WM_LBUTTONUP:
782         if ((link = WINHELP_FindLink(win, lParam)) && link == win->current_link)
783         {
784             HLPFILE_WINDOWINFO*     wi;
785
786             switch (link->cookie)
787             {
788             case hlp_link_link:
789                 if ((hlpfile = WINHELP_LookupHelpFile(link->string)))
790                 {
791                     if (link->window == -1)
792                         wi = win->info;
793                     else if (link->window < hlpfile->numWindows)
794                         wi = &hlpfile->windows[link->window];
795                     else
796                     {
797                         WINE_WARN("link to window %d/%d\n", link->window, hlpfile->numWindows);
798                         break;
799                     }
800                     WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, link->hash, wi, SW_NORMAL);
801                 }
802                 break;
803             case hlp_link_popup:
804                 if ((hlpfile = WINHELP_LookupHelpFile(link->string)))
805                     WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, link->hash,
806                                            WINHELP_GetPopupWindowInfo(hlpfile, win, lParam),
807                                            SW_NORMAL);
808                 break;
809             case hlp_link_macro:
810                 MACRO_ExecuteMacro(link->string);
811                 break;
812             default:
813                 WINE_FIXME("Unknown link cookie %d\n", link->cookie);
814             }
815             ret = TRUE;
816         }
817         win->current_link = NULL;
818         break;
819     }
820     return ret;
821 }
822
823 /***********************************************************************
824  *
825  *           WINHELP_MainWndProc
826  */
827 static LRESULT CALLBACK WINHELP_MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
828 {
829     WINHELP_WINDOW *win;
830     WINHELP_BUTTON *button;
831     RECT rect;
832     INT  curPos, min, max, dy, keyDelta;
833     HWND hTextWnd;
834     LRESULT ret;
835
836     if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, &ret)) return ret;
837
838     switch (msg)
839     {
840     case WM_NCCREATE:
841         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
842         SetWindowLongPtr(hWnd, 0, (ULONG_PTR) win);
843         if (!win->page && Globals.isBook)
844             PostMessage(hWnd, WM_COMMAND, MNID_FILE_OPEN, 0);
845         win->hMainWnd = hWnd;
846         break;
847
848     case WM_WINDOWPOSCHANGED:
849         WINHELP_LayoutMainWindow((WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0));
850         break;
851
852     case WM_COMMAND:
853         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
854         switch (wParam)
855         {
856             /* Menu FILE */
857         case MNID_FILE_OPEN:    MACRO_FileOpen();       break;
858         case MNID_FILE_PRINT:   MACRO_Print();          break;
859         case MNID_FILE_SETUP:   MACRO_PrinterSetup();   break;
860         case MNID_FILE_EXIT:    MACRO_Exit();           break;
861
862             /* Menu EDIT */
863         case MNID_EDIT_COPYDLG: MACRO_CopyDialog();     break;
864         case MNID_EDIT_ANNOTATE:MACRO_Annotate();       break;
865
866             /* Menu Bookmark */
867         case MNID_BKMK_DEFINE:  MACRO_BookmarkDefine(); break;
868
869             /* Menu Help */
870         case MNID_HELP_HELPON:  MACRO_HelpOn();         break;
871         case MNID_HELP_HELPTOP: MACRO_HelpOnTop();      break;
872         case MNID_HELP_ABOUT:   MACRO_About();          break;
873         case MNID_HELP_WINE:    ShellAbout(hWnd, "WINE", "Help", 0); break;
874
875             /* Context help */
876         case MNID_CTXT_ANNOTATE:MACRO_Annotate();       break;
877         case MNID_CTXT_COPY:    MACRO_CopyDialog();     break;
878         case MNID_CTXT_PRINT:   MACRO_Print();          break;
879         case MNID_OPTS_HISTORY: MACRO_History();        break;
880         case MNID_OPTS_FONTS_SMALL:
881         case MNID_CTXT_FONTS_SMALL:
882             win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
883             if (win->font_scale != 0)
884             {
885                 win->font_scale = 0;
886                 WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */);
887             }
888             break;
889         case MNID_OPTS_FONTS_NORMAL:
890         case MNID_CTXT_FONTS_NORMAL:
891             win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
892             if (win->font_scale != 1)
893             {
894                 win->font_scale = 1;
895                 WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */);
896             }
897             break;
898         case MNID_OPTS_FONTS_LARGE:
899         case MNID_CTXT_FONTS_LARGE:
900             win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
901             if (win->font_scale != 2)
902             {
903                 win->font_scale = 2;
904                 WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */);
905             }
906             break;
907         case MNID_OPTS_HELP_DEFAULT:
908         case MNID_OPTS_HELP_VISIBLE:
909         case MNID_OPTS_HELP_NONVISIBLE:
910         case MNID_OPTS_SYSTEM_COLORS:
911         case MNID_CTXT_HELP_DEFAULT:
912         case MNID_CTXT_HELP_VISIBLE:
913         case MNID_CTXT_HELP_NONVISIBLE:
914         case MNID_CTXT_SYSTEM_COLORS:
915             /* FIXME: NIY */
916
917         default:
918             /* Buttons */
919             for (button = win->first_button; button; button = button->next)
920                 if (wParam == button->wParam) break;
921             if (button)
922                 MACRO_ExecuteMacro(button->lpszMacro);
923             else if (!HIWORD(wParam))
924                 MessageBox(0, MAKEINTRESOURCE(STID_NOT_IMPLEMENTED),
925                            MAKEINTRESOURCE(STID_WHERROR), MB_OK);
926             break;
927         }
928         break;
929 /* EPP     case WM_DESTROY: */
930 /* EPP         if (Globals.hPopupWnd) DestroyWindow(Globals.hPopupWnd); */
931 /* EPP         break; */
932     case WM_COPYDATA:
933         return WINHELP_HandleCommand((HWND)wParam, lParam);
934
935     case WM_KEYDOWN:
936         keyDelta = 0;
937
938         switch (wParam)
939         {
940         case VK_UP:
941         case VK_DOWN:
942             keyDelta = GetSystemMetrics(SM_CXVSCROLL);
943             if (wParam == VK_UP)
944                 keyDelta = -keyDelta;
945
946         case VK_PRIOR:
947         case VK_NEXT:
948             win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
949             hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
950             curPos = GetScrollPos(hTextWnd, SB_VERT);
951             GetScrollRange(hTextWnd, SB_VERT, &min, &max);
952
953             if (keyDelta == 0)
954             {            
955                 GetClientRect(hTextWnd, &rect);
956                 keyDelta = (rect.bottom - rect.top) / 2;
957                 if (wParam == VK_PRIOR)
958                     keyDelta = -keyDelta;
959             }
960
961             curPos += keyDelta;
962             if (curPos > max)
963                  curPos = max;
964             else if (curPos < min)
965                  curPos = min;
966
967             dy = GetScrollPos(hTextWnd, SB_VERT) - curPos;
968             SetScrollPos(hTextWnd, SB_VERT, curPos, TRUE);
969             ScrollWindow(hTextWnd, 0, dy, NULL, NULL);
970             UpdateWindow(hTextWnd);
971             return 0;
972
973         case VK_ESCAPE:
974             MACRO_Exit();
975             return 0;
976         }
977         break;
978
979     case WM_NOTIFY:
980         if (wParam == CTL_ID_TEXT)
981         {
982             RECT        rc;
983
984             switch (((NMHDR*)lParam)->code)
985             {
986             case EN_MSGFILTER:
987                 {
988                     const MSGFILTER*    msgf = (const MSGFILTER*)lParam;
989                     switch (msgf->msg)
990                     {
991                     case WM_KEYUP:
992                         if (msgf->wParam == VK_ESCAPE) DestroyWindow(hWnd);
993                         break;
994                     case WM_RBUTTONDOWN:
995                     {
996                         HMENU       hMenu;
997                         POINT       pt;
998
999                         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1000                         hMenu = LoadMenu(Globals.hInstance, (LPSTR)CONTEXT_MENU);
1001                         switch (win->font_scale)
1002                         {
1003                         case 0:
1004                             CheckMenuItem(hMenu, MNID_CTXT_FONTS_SMALL,
1005                                           MF_BYCOMMAND|MF_CHECKED);
1006                             break;
1007                         default:
1008                             WINE_FIXME("Unsupported %d\n", win->font_scale);
1009                         case 1:
1010                             CheckMenuItem(hMenu, MNID_CTXT_FONTS_NORMAL,
1011                                           MF_BYCOMMAND|MF_CHECKED);
1012                             break;
1013                         case 2:
1014                             CheckMenuItem(hMenu, MNID_CTXT_FONTS_LARGE,
1015                                           MF_BYCOMMAND|MF_CHECKED);
1016                             break;
1017                         }
1018                         pt.x = (int)(short)LOWORD(msgf->lParam);
1019                         pt.y = (int)(short)HIWORD(msgf->lParam);
1020                         ClientToScreen(msgf->nmhdr.hwndFrom, &pt);
1021                         TrackPopupMenu(GetSubMenu(hMenu, 0), TPM_LEFTALIGN|TPM_TOPALIGN,
1022                                        pt.x, pt.y, 0, hWnd, NULL);
1023                         DestroyMenu(hMenu);
1024                     }
1025                     break;
1026                     default:
1027                         return WINHELP_HandleTextMouse((WINHELP_WINDOW*)GetWindowLongPtr(hWnd, 0),
1028                                                        msgf->msg, msgf->lParam);
1029                     }
1030                 }
1031                 break;
1032
1033             case EN_REQUESTRESIZE:
1034                 rc = ((REQRESIZE*)lParam)->rc;
1035                 win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1036                 AdjustWindowRect(&rc, GetWindowLong(win->hMainWnd, GWL_STYLE),
1037                                  FALSE);
1038                 SetWindowPos(win->hMainWnd, HWND_TOP, 0, 0,
1039                              rc.right - rc.left, rc.bottom - rc.top,
1040                              SWP_NOMOVE | SWP_NOZORDER);
1041                 WINHELP_LayoutMainWindow(win);
1042                 break;
1043             }
1044         }
1045         break;
1046
1047     case WM_INITMENUPOPUP:
1048         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1049         CheckMenuItem((HMENU)wParam, MNID_OPTS_FONTS_SMALL,
1050                       MF_BYCOMMAND | (win->font_scale == 0) ? MF_CHECKED : 0);
1051         CheckMenuItem((HMENU)wParam, MNID_OPTS_FONTS_NORMAL,
1052                       MF_BYCOMMAND | (win->font_scale == 1) ? MF_CHECKED : 0);
1053         CheckMenuItem((HMENU)wParam, MNID_OPTS_FONTS_LARGE,
1054                       MF_BYCOMMAND | (win->font_scale == 2) ? MF_CHECKED : 0);
1055         break;
1056
1057     case WM_NCDESTROY:
1058         {
1059             BOOL bExit;
1060             win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1061             bExit = (Globals.wVersion >= 4 && !lstrcmpi(win->lpszName, "main"));
1062             WINHELP_DeleteWindow(win);
1063
1064             if (bExit) MACRO_Exit();
1065             if (!Globals.win_list)
1066                 PostQuitMessage(0);
1067         }
1068         break;
1069     }
1070     return DefWindowProc(hWnd, msg, wParam, lParam);
1071 }
1072
1073 static DWORD CALLBACK WINHELP_RtfStreamIn(DWORD_PTR cookie, BYTE* buff,
1074                                           LONG cb, LONG* pcb)
1075 {
1076     struct RtfData*     rd = (struct RtfData*)cookie;
1077
1078     if (rd->where >= rd->ptr) return 1;
1079     if (rd->where + cb > rd->ptr)
1080         cb = rd->ptr - rd->where;
1081     memcpy(buff, rd->where, cb);
1082     rd->where += cb;
1083     *pcb = cb;
1084     return 0;
1085 }
1086
1087 static void WINHELP_SetupText(HWND hTextWnd, WINHELP_WINDOW* win, ULONG relative)
1088 {
1089     SendMessage(hTextWnd, WM_SETREDRAW, FALSE, 0);
1090     SendMessage(hTextWnd, EM_SETBKGNDCOLOR, 0, (LPARAM)win->info->sr_color);
1091     /* set word-wrap to window size (undocumented) */
1092     SendMessage(hTextWnd, EM_SETTARGETDEVICE, 0, 0);
1093     if (win->page)
1094     {
1095         struct RtfData  rd;
1096         EDITSTREAM      es;
1097         unsigned        cp = 0;
1098         POINTL          ptl;
1099         POINT           pt;
1100
1101
1102         if (HLPFILE_BrowsePage(win->page, &rd, win->font_scale, relative))
1103         {
1104             rd.where = rd.data;
1105             es.dwCookie = (DWORD_PTR)&rd;
1106             es.dwError = 0;
1107             es.pfnCallback = WINHELP_RtfStreamIn;
1108
1109             SendMessageW(hTextWnd, EM_STREAMIN, SF_RTF, (LPARAM)&es);
1110             cp = rd.char_pos_rel;
1111         }
1112         /* FIXME: else leaking potentially the rd.first_link chain */
1113         HeapFree(GetProcessHeap(), 0, rd.data);
1114         SendMessage(hTextWnd, EM_POSFROMCHAR, (WPARAM)&ptl, cp ? cp - 1 : 0);
1115         pt.x = 0; pt.y = ptl.y;
1116         SendMessage(hTextWnd, EM_SETSCROLLPOS, 0, (LPARAM)&pt);
1117     }
1118     else
1119     {
1120         SendMessage(hTextWnd, WM_SETTEXT, 0, (LPARAM)"");
1121     }
1122     SendMessage(hTextWnd, WM_SETREDRAW, TRUE, 0);
1123     InvalidateRect(hTextWnd, NULL, TRUE);
1124 }
1125
1126 /***********************************************************************
1127  *
1128  *           WINHELP_ButtonBoxWndProc
1129  */
1130 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1131 {
1132     WINDOWPOS      *winpos;
1133     WINHELP_WINDOW *win;
1134     WINHELP_BUTTON *button;
1135     SIZE button_size;
1136     INT  x, y;
1137
1138     if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0L;
1139
1140     switch (msg)
1141     {
1142     case WM_WINDOWPOSCHANGING:
1143         winpos = (WINDOWPOS*) lParam;
1144         win = (WINHELP_WINDOW*) GetWindowLongPtr(GetParent(hWnd), 0);
1145
1146         /* Update buttons */
1147         button_size.cx = 0;
1148         button_size.cy = 0;
1149         for (button = win->first_button; button; button = button->next)
1150         {
1151             HDC  hDc;
1152             SIZE textsize;
1153             if (!button->hWnd)
1154             {
1155                 button->hWnd = CreateWindow(STRING_BUTTON, button->lpszName,
1156                                             WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
1157                                             0, 0, 0, 0,
1158                                             hWnd, (HMENU) button->wParam,
1159                                             Globals.hInstance, 0);
1160                 if (button->hWnd) {
1161                     if (Globals.button_proc == NULL)
1162                         Globals.button_proc = (WNDPROC) GetWindowLongPtr(button->hWnd, GWLP_WNDPROC);
1163                     SetWindowLongPtr(button->hWnd, GWLP_WNDPROC, (LONG_PTR) WINHELP_ButtonWndProc);
1164                 }
1165             }
1166             hDc = GetDC(button->hWnd);
1167             GetTextExtentPoint(hDc, button->lpszName,
1168                                lstrlen(button->lpszName), &textsize);
1169             ReleaseDC(button->hWnd, hDc);
1170
1171             button_size.cx = max(button_size.cx, textsize.cx + BUTTON_CX);
1172             button_size.cy = max(button_size.cy, textsize.cy + BUTTON_CY);
1173         }
1174
1175         x = 0;
1176         y = 0;
1177         for (button = win->first_button; button; button = button->next)
1178         {
1179             SetWindowPos(button->hWnd, HWND_TOP, x, y, button_size.cx, button_size.cy, 0);
1180
1181             if (x + 2 * button_size.cx <= winpos->cx)
1182                 x += button_size.cx;
1183             else
1184                 x = 0, y += button_size.cy;
1185         }
1186         winpos->cy = y + (x ? button_size.cy : 0);
1187         break;
1188
1189     case WM_COMMAND:
1190         SendMessage(GetParent(hWnd), msg, wParam, lParam);
1191         break;
1192
1193     case WM_KEYDOWN:
1194         switch (wParam)
1195         {
1196         case VK_UP:
1197         case VK_DOWN:
1198         case VK_PRIOR:
1199         case VK_NEXT:
1200         case VK_ESCAPE:
1201             return SendMessage(GetParent(hWnd), msg, wParam, lParam);
1202         }
1203         break;
1204     }
1205
1206     return DefWindowProc(hWnd, msg, wParam, lParam);
1207 }
1208
1209 /***********************************************************************
1210  *
1211  *           WINHELP_ButtonWndProc
1212  */
1213 static LRESULT CALLBACK WINHELP_ButtonWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1214 {
1215     if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0;
1216
1217     if (msg == WM_KEYDOWN)
1218     {
1219         switch (wParam)
1220         {
1221         case VK_UP:
1222         case VK_DOWN:
1223         case VK_PRIOR:
1224         case VK_NEXT:
1225         case VK_ESCAPE:
1226             return SendMessage(GetParent(hWnd), msg, wParam, lParam);
1227         }
1228     }
1229
1230     return CallWindowProc(Globals.button_proc, hWnd, msg, wParam, lParam);
1231 }
1232
1233 /******************************************************************
1234  *              WINHELP_HistoryWndProc
1235  *
1236  *
1237  */
1238 static LRESULT CALLBACK WINHELP_HistoryWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1239 {
1240     WINHELP_WINDOW*     win;
1241     PAINTSTRUCT         ps;
1242     HDC                 hDc;
1243     TEXTMETRIC          tm;
1244     unsigned int        i;
1245     RECT                r;
1246
1247     switch (msg)
1248     {
1249     case WM_NCCREATE:
1250         win = (WINHELP_WINDOW*)((LPCREATESTRUCT)lParam)->lpCreateParams;
1251         SetWindowLongPtr(hWnd, 0, (ULONG_PTR)win);
1252         win->hHistoryWnd = hWnd;
1253         break;
1254     case WM_CREATE:
1255         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1256         hDc = GetDC(hWnd);
1257         GetTextMetrics(hDc, &tm);
1258         GetWindowRect(hWnd, &r);
1259
1260         r.right = r.left + 30 * tm.tmAveCharWidth;
1261         r.bottom = r.top + (sizeof(Globals.history.set) / sizeof(Globals.history.set[0])) * tm.tmHeight;
1262         AdjustWindowRect(&r, GetWindowLong(hWnd, GWL_STYLE), FALSE);
1263         if (r.left < 0) {r.right -= r.left; r.left = 0;}
1264         if (r.top < 0) {r.bottom -= r.top; r.top = 0;}
1265
1266         MoveWindow(hWnd, r.left, r.top, r.right, r.bottom, TRUE);
1267         ReleaseDC(hWnd, hDc);
1268         break;
1269     case WM_LBUTTONDOWN:
1270         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1271         hDc = GetDC(hWnd);
1272         GetTextMetrics(hDc, &tm);
1273         i = HIWORD(lParam) / tm.tmHeight;
1274         if (i < Globals.history.index)
1275             WINHELP_CreateHelpWindow(&Globals.history.set[i], SW_SHOW, TRUE);
1276         ReleaseDC(hWnd, hDc);
1277         break;
1278     case WM_PAINT:
1279         hDc = BeginPaint(hWnd, &ps);
1280         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1281         GetTextMetrics(hDc, &tm);
1282
1283         for (i = 0; i < Globals.history.index; i++)
1284         {
1285             if (Globals.history.set[i].page->file == Globals.active_win->page->file)
1286             {
1287                 TextOut(hDc, 0, i * tm.tmHeight,
1288                         Globals.history.set[i].page->lpszTitle,
1289                         strlen(Globals.history.set[i].page->lpszTitle));
1290             }
1291             else
1292             {
1293                 char        buffer[1024];
1294                 const char* ptr1;
1295                 const char* ptr2;
1296                 unsigned    len;
1297
1298                 ptr1 = strrchr(Globals.history.set[i].page->file->lpszPath, '\\');
1299                 if (!ptr1) ptr1 = Globals.history.set[i].page->file->lpszPath;
1300                 else ptr1++;
1301                 ptr2 = strrchr(ptr1, '.');
1302                 len = ptr2 ? ptr2 - ptr1 : strlen(ptr1);
1303                 if (len > sizeof(buffer)) len = sizeof(buffer);
1304                 memcpy(buffer, ptr1, len);
1305                 if (len < sizeof(buffer)) buffer[len++] = ':';
1306                 strncpy(&buffer[len], Globals.history.set[i].page->lpszTitle, sizeof(buffer) - len);
1307                 buffer[sizeof(buffer) - 1] = '\0';
1308                 TextOut(hDc, 0, i * tm.tmHeight, buffer, strlen(buffer));
1309             }
1310         }
1311         EndPaint(hWnd, &ps);
1312         break;
1313     case WM_DESTROY:
1314         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1315         if (hWnd == win->hHistoryWnd)
1316             win->hHistoryWnd = 0;
1317         break;
1318     }
1319     return DefWindowProc(hWnd, msg, wParam, lParam);
1320 }
1321
1322 /***********************************************************************
1323  *
1324  *           WINHELP_ShadowWndProc
1325  */
1326 static LRESULT CALLBACK WINHELP_ShadowWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1327 {
1328     if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0;
1329     return WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL) ? 0L : DefWindowProc(hWnd, msg, wParam, lParam);
1330 }
1331
1332 /***********************************************************************
1333  *
1334  *           WINHELP_CheckPopup
1335  */
1336 static BOOL WINHELP_CheckPopup(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT* lret)
1337 {
1338     HWND        hPopup;
1339
1340     if (!Globals.active_popup) return FALSE;
1341
1342     switch (msg)
1343     {
1344     case WM_NOTIFY:
1345         {
1346             MSGFILTER*  msgf = (MSGFILTER*)lParam;
1347             if (msgf->nmhdr.code == EN_MSGFILTER)
1348             {
1349                 if (!WINHELP_CheckPopup(hWnd, msgf->msg, msgf->wParam, msgf->lParam, NULL))
1350                     return FALSE;
1351                 if (lret) *lret = 1;
1352                 return TRUE;
1353             }
1354         }
1355         break;
1356     case WM_ACTIVATE:
1357         if (wParam != WA_INACTIVE || (HWND)lParam == Globals.active_win->hMainWnd ||
1358             (HWND)lParam == Globals.active_popup->hMainWnd ||
1359             GetWindow((HWND)lParam, GW_OWNER) == Globals.active_win->hMainWnd)
1360             break;
1361     case WM_LBUTTONUP:
1362     case WM_LBUTTONDOWN:
1363         if (WINHELP_HandleTextMouse(Globals.active_popup, msg, lParam) && msg == WM_LBUTTONDOWN)
1364             return FALSE;
1365         /* fall through */
1366     case WM_MBUTTONDOWN:
1367     case WM_RBUTTONDOWN:
1368     case WM_NCLBUTTONDOWN:
1369     case WM_NCMBUTTONDOWN:
1370     case WM_NCRBUTTONDOWN:
1371         hPopup = Globals.active_popup->hMainWnd;
1372         Globals.active_popup = NULL;
1373         DestroyWindow(hPopup);
1374         return TRUE;
1375     }
1376     return FALSE;
1377 }
1378
1379 /******************************************************************
1380  *              WINHELP_DeleteButtons
1381  *
1382  */
1383 static void WINHELP_DeleteButtons(WINHELP_WINDOW* win)
1384 {
1385     WINHELP_BUTTON*     b;
1386     WINHELP_BUTTON*     bp;
1387
1388     for (b = win->first_button; b; b = bp)
1389     {
1390         DestroyWindow(b->hWnd);
1391         bp = b->next;
1392         HeapFree(GetProcessHeap(), 0, b);
1393     }
1394     win->first_button = NULL;
1395 }
1396
1397 /******************************************************************
1398  *              WINHELP_DeleteBackSet
1399  *
1400  */
1401 void WINHELP_DeleteBackSet(WINHELP_WINDOW* win)
1402 {
1403     unsigned int i;
1404
1405     for (i = 0; i < win->back.index; i++)
1406     {
1407         HLPFILE_FreeHlpFile(win->back.set[i].page->file);
1408         win->back.set[i].page = NULL;
1409     }
1410     win->back.index = 0;
1411 }
1412
1413 /******************************************************************
1414  *             WINHELP_DeletePageLinks
1415  *
1416  */
1417 static void WINHELP_DeletePageLinks(HLPFILE_PAGE* page)
1418 {
1419     HLPFILE_LINK*       curr;
1420     HLPFILE_LINK*       next;
1421
1422     for (curr = page->first_link; curr; curr = next)
1423     {
1424         next = curr->next;
1425         HeapFree(GetProcessHeap(), 0, curr);
1426     }
1427 }
1428
1429 /***********************************************************************
1430  *
1431  *           WINHELP_DeleteWindow
1432  */
1433 static void WINHELP_DeleteWindow(WINHELP_WINDOW* win)
1434 {
1435     WINHELP_WINDOW**    w;
1436
1437     for (w = &Globals.win_list; *w; w = &(*w)->next)
1438     {
1439         if (*w == win)
1440         {
1441             *w = win->next;
1442             break;
1443         }
1444     }
1445
1446     if (Globals.active_win == win)
1447     {
1448         Globals.active_win = Globals.win_list;
1449         if (Globals.win_list)
1450             SetActiveWindow(Globals.win_list->hMainWnd);
1451     }
1452
1453     if (win == Globals.active_popup)
1454         Globals.active_popup = NULL;
1455
1456     WINHELP_DeleteButtons(win);
1457
1458     if (win->page) WINHELP_DeletePageLinks(win->page);
1459     if (win->hShadowWnd) DestroyWindow(win->hShadowWnd);
1460     if (win->hHistoryWnd) DestroyWindow(win->hHistoryWnd);
1461
1462     DeleteObject(win->hBrush);
1463
1464     WINHELP_DeleteBackSet(win);
1465
1466     if (win->page) HLPFILE_FreeHlpFile(win->page->file);
1467     HeapFree(GetProcessHeap(), 0, win);
1468 }
1469
1470 /***********************************************************************
1471  *
1472  *           WINHELP_InitFonts
1473  */
1474 static void WINHELP_InitFonts(HWND hWnd)
1475 {
1476     WINHELP_WINDOW *win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1477     LOGFONT logfontlist[] = {
1478         {-10, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1479         {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1480         {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1481         {-12, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1482         {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1483         {-10, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1484         { -8, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"}};
1485 #define FONTS_LEN (sizeof(logfontlist)/sizeof(*logfontlist))
1486
1487     static HFONT fonts[FONTS_LEN];
1488     static BOOL init = 0;
1489
1490     win->fonts_len = FONTS_LEN;
1491     win->fonts = fonts;
1492
1493     if (!init)
1494     {
1495         UINT i;
1496
1497         for (i = 0; i < FONTS_LEN; i++)
1498         {
1499             fonts[i] = CreateFontIndirect(&logfontlist[i]);
1500         }
1501
1502         init = 1;
1503     }
1504 }
1505
1506 /***********************************************************************
1507  *
1508  *           WINHELP_MessageBoxIDS_s
1509  */
1510 INT WINHELP_MessageBoxIDS_s(UINT ids_text, LPCSTR str, UINT ids_title, WORD type)
1511 {
1512     CHAR text[MAX_STRING_LEN];
1513     CHAR newtext[MAX_STRING_LEN + MAX_PATH];
1514
1515     LoadString(Globals.hInstance, ids_text, text, sizeof(text));
1516     wsprintf(newtext, text, str);
1517
1518     return MessageBox(0, newtext, MAKEINTRESOURCE(ids_title), type);
1519 }
1520
1521 /**************************************************************************
1522  * cb_KWBTree
1523  *
1524  * HLPFILE_BPTreeCallback enumeration function for '|KWBTREE' internal file.
1525  *
1526  */
1527 static void cb_KWBTree(void *p, void **next, void *cookie)
1528 {
1529     HWND hListWnd = (HWND)cookie;
1530     int count;
1531
1532     WINE_TRACE("Adding '%s' to search list\n", (char *)p);
1533     SendMessage(hListWnd, LB_INSERTSTRING, -1, (LPARAM)p);
1534     count = SendMessage(hListWnd, LB_GETCOUNT, 0, 0);
1535     SendMessage(hListWnd, LB_SETITEMDATA, count-1, (LPARAM)p);
1536     *next = (char*)p + strlen((char*)p) + 7;
1537 }
1538
1539 /**************************************************************************
1540  * WINHELP_IndexDlgProc
1541  *
1542  * Index dialog callback function.
1543  *
1544  * nResult passed to EndDialog:
1545  *   1: CANCEL button
1546  *  >1: valid offset value +2.
1547  *  EndDialog itself can return 0 (error).
1548  */
1549 INT_PTR CALLBACK WINHELP_SearchDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1550 {
1551     static HLPFILE *file;
1552     int sel;
1553     ULONG offset = 1;
1554
1555     switch (msg)
1556     {
1557     case WM_INITDIALOG:
1558         file = (HLPFILE *)lParam;
1559         HLPFILE_BPTreeEnum(file->kwbtree, cb_KWBTree,
1560                            GetDlgItem(hWnd, IDC_INDEXLIST));
1561         return TRUE;
1562     case WM_COMMAND:
1563         switch (LOWORD(wParam))
1564         {
1565         case IDOK:
1566             sel = SendDlgItemMessage(hWnd, IDC_INDEXLIST, LB_GETCURSEL, 0, 0);
1567             if (sel != LB_ERR)
1568             {
1569                 BYTE *p;
1570                 int count;
1571
1572                 p = (BYTE*)SendDlgItemMessage(hWnd, IDC_INDEXLIST,
1573                                               LB_GETITEMDATA, sel, 0);
1574                 count = *(short*)((char *)p + strlen((char *)p) + 1);
1575                 if (count > 1)
1576                 {
1577                     MessageBox(hWnd, "count > 1 not supported yet", "Error", MB_OK | MB_ICONSTOP);
1578                     return TRUE;
1579                 }
1580                 offset = *(ULONG*)((char *)p + strlen((char *)p) + 3);
1581                 offset = *(long*)(file->kwdata + offset + 9);
1582                 if (offset == 0xFFFFFFFF)
1583                 {
1584                     MessageBox(hWnd, "macro keywords not supported yet", "Error", MB_OK | MB_ICONSTOP);
1585                     return TRUE;
1586                 }
1587                 offset += 2;
1588             }
1589             /* Fall through */
1590         case IDCANCEL:
1591             EndDialog(hWnd, offset);
1592             return TRUE;
1593         default:
1594             break;
1595         }
1596     default:
1597         break;
1598     }
1599     return FALSE;
1600 }
1601
1602 /**************************************************************************
1603  * WINHELP_CreateIndexWindow
1604  *
1605  * Displays a dialog with keywords of current help file.
1606  *
1607  */
1608 BOOL WINHELP_CreateIndexWindow(void)
1609 {
1610     int ret;
1611     HLPFILE *hlpfile;
1612
1613     if (Globals.active_win && Globals.active_win->page && Globals.active_win->page->file)
1614         hlpfile = Globals.active_win->page->file;
1615     else
1616         return FALSE;
1617
1618     if (hlpfile->kwbtree == NULL)
1619     {
1620         WINE_TRACE("No index provided\n");
1621         return FALSE;
1622     }
1623
1624     ret = DialogBoxParam(Globals.hInstance, MAKEINTRESOURCE(IDD_INDEX),
1625                          Globals.active_win->hMainWnd, WINHELP_SearchDlgProc,
1626                          (LPARAM)hlpfile);
1627     if (ret > 1)
1628     {
1629         ret -= 2;
1630         WINE_TRACE("got %d as an offset\n", ret);
1631         WINHELP_OpenHelpWindow(HLPFILE_PageByOffset, hlpfile, ret,
1632                                Globals.active_win->info, SW_NORMAL);
1633     }
1634     return TRUE;
1635 }