winhelp: Don't add a page to the history when using the back button.
[wine] / programs / winhelp / 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
39 #include "wine/debug.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(winhelp);
42
43 static BOOL    WINHELP_RegisterWinClasses(void);
44 static LRESULT CALLBACK WINHELP_MainWndProc(HWND, UINT, WPARAM, LPARAM);
45 static LRESULT CALLBACK WINHELP_TextWndProc(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 void    WINHELP_CheckPopup(UINT);
51 static BOOL    WINHELP_SplitLines(HWND hWnd, LPSIZE);
52 static void    WINHELP_InitFonts(HWND hWnd);
53 static void    WINHELP_DeleteLines(WINHELP_WINDOW*);
54 static void    WINHELP_DeleteWindow(WINHELP_WINDOW*);
55 static void    WINHELP_DeleteButtons(WINHELP_WINDOW*);
56 static void    WINHELP_SetupText(HWND hWnd);
57 static WINHELP_LINE_PART* WINHELP_IsOverLink(WINHELP_WINDOW*, WPARAM, LPARAM);
58
59 WINHELP_GLOBALS Globals = {3, NULL, NULL, TRUE, NULL, NULL, NULL, NULL, {{{NULL,NULL}},0}};
60
61 #define CTL_ID_BUTTON   0x700
62 #define CTL_ID_TEXT     0x701
63
64 /***********************************************************************
65  *
66  *           WINHELP_GetOpenFileName
67  */
68 BOOL WINHELP_GetOpenFileName(LPSTR lpszFile, int len)
69 {
70     OPENFILENAME openfilename;
71     CHAR szDir[MAX_PATH];
72     CHAR szzFilter[2 * MAX_STRING_LEN + 100];
73     LPSTR p = szzFilter;
74
75     WINE_TRACE("()\n");
76
77     LoadString(Globals.hInstance, STID_HELP_FILES_HLP, p, MAX_STRING_LEN);
78     p += strlen(p) + 1;
79     lstrcpy(p, "*.hlp");
80     p += strlen(p) + 1;
81     LoadString(Globals.hInstance, STID_ALL_FILES, p, MAX_STRING_LEN);
82     p += strlen(p) + 1;
83     lstrcpy(p, "*.*");
84     p += strlen(p) + 1;
85     *p = '\0';
86
87     GetCurrentDirectory(sizeof(szDir), szDir);
88
89     lpszFile[0]='\0';
90
91     openfilename.lStructSize       = sizeof(OPENFILENAME);
92     openfilename.hwndOwner         = NULL;
93     openfilename.hInstance         = Globals.hInstance;
94     openfilename.lpstrFilter       = szzFilter;
95     openfilename.lpstrCustomFilter = 0;
96     openfilename.nMaxCustFilter    = 0;
97     openfilename.nFilterIndex      = 1;
98     openfilename.lpstrFile         = lpszFile;
99     openfilename.nMaxFile          = len;
100     openfilename.lpstrFileTitle    = 0;
101     openfilename.nMaxFileTitle     = 0;
102     openfilename.lpstrInitialDir   = szDir;
103     openfilename.lpstrTitle        = 0;
104     openfilename.Flags             = 0;
105     openfilename.nFileOffset       = 0;
106     openfilename.nFileExtension    = 0;
107     openfilename.lpstrDefExt       = 0;
108     openfilename.lCustData         = 0;
109     openfilename.lpfnHook          = 0;
110     openfilename.lpTemplateName    = 0;
111
112     return GetOpenFileName(&openfilename);
113 }
114
115 static char* WINHELP_GetCaption(WINHELP_WNDPAGE* wpage)
116 {
117     if (wpage->wininfo->caption[0]) return wpage->wininfo->caption;
118     return wpage->page->file->lpszTitle;
119 }
120
121 /***********************************************************************
122  *
123  *           WINHELP_LookupHelpFile
124  */
125 HLPFILE* WINHELP_LookupHelpFile(LPCSTR lpszFile)
126 {
127     HLPFILE*        hlpfile;
128     char szFullName[MAX_PATH];
129     char szAddPath[MAX_PATH];
130     char *p;
131
132     /*
133      * NOTE: This is needed by popup windows only.
134      * In other cases it's not needed but does not hurt though.
135      */
136     if (Globals.active_win && Globals.active_win->page && Globals.active_win->page->file)
137     {
138         strcpy(szAddPath, Globals.active_win->page->file->lpszPath);
139         p = strrchr(szAddPath, '\\');
140         if (p) *p = 0;
141     }
142
143     /*
144      * FIXME: Should we swap conditions?
145      */
146     if (!SearchPath(NULL, lpszFile, ".hlp", MAX_PATH, szFullName, NULL) &&
147         !SearchPath(szAddPath, lpszFile, ".hlp", MAX_PATH, szFullName, NULL))
148     {
149         if (WINHELP_MessageBoxIDS_s(STID_FILE_NOT_FOUND_s, lpszFile, STID_WHERROR,
150                                     MB_YESNO|MB_ICONQUESTION) != IDYES)
151             return NULL;
152         if (!WINHELP_GetOpenFileName(szFullName, MAX_PATH))
153             return NULL;
154     }
155     hlpfile = HLPFILE_ReadHlpFile(szFullName);
156     if (!hlpfile)
157         WINHELP_MessageBoxIDS_s(STID_HLPFILE_ERROR_s, lpszFile,
158                                 STID_WHERROR, MB_OK|MB_ICONSTOP);
159     return hlpfile;
160 }
161
162 /******************************************************************
163  *              WINHELP_GetWindowInfo
164  *
165  *
166  */
167 HLPFILE_WINDOWINFO*     WINHELP_GetWindowInfo(HLPFILE* hlpfile, LPCSTR name)
168 {
169     static      HLPFILE_WINDOWINFO      mwi;
170     unsigned int     i;
171
172     if (!name || !name[0])
173         name = Globals.active_win->lpszName;
174
175     if (hlpfile)
176         for (i = 0; i < hlpfile->numWindows; i++)
177             if (!strcmp(hlpfile->windows[i].name, name))
178                 return &hlpfile->windows[i];
179
180     if (strcmp(name, "main") != 0)
181     {
182         WINE_FIXME("Couldn't find window info for %s\n", name);
183         assert(0);
184         return NULL;
185     }
186     if (!mwi.name[0])
187     {
188         strcpy(mwi.type, "primary");
189         strcpy(mwi.name, "main");
190         if (!LoadString(Globals.hInstance, STID_WINE_HELP, 
191                         mwi.caption, sizeof(mwi.caption)))
192             strcpy(mwi.caption, hlpfile->lpszTitle);
193         mwi.origin.x = mwi.origin.y = mwi.size.cx = mwi.size.cy = CW_USEDEFAULT;
194         mwi.style = SW_SHOW;
195         mwi.win_style = WS_OVERLAPPEDWINDOW;
196         mwi.sr_color = mwi.sr_color = 0xFFFFFF;
197     }
198     return &mwi;
199 }
200
201 /******************************************************************
202  *              HLPFILE_GetPopupWindowInfo
203  *
204  *
205  */
206 static HLPFILE_WINDOWINFO*     WINHELP_GetPopupWindowInfo(HLPFILE* hlpfile, HWND hParentWnd, POINT* mouse)
207 {
208     static      HLPFILE_WINDOWINFO      wi;
209
210     RECT parent_rect;
211     
212     wi.type[0] = wi.name[0] = wi.caption[0] = '\0';
213
214     /* Calculate horizontal size and position of a popup window */
215     GetWindowRect(hParentWnd, &parent_rect);
216     wi.size.cx = (parent_rect.right  - parent_rect.left) / 2;
217     wi.size.cy = 10; /* need a non null value, so that border are taken into account while computing */
218
219     wi.origin = *mouse;
220     ClientToScreen(hParentWnd, &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 = wi.sr_color = 0xFFFFFF;
228
229     return &wi;
230 }
231
232 /***********************************************************************
233  *
234  *           WinMain
235  */
236 int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
237 {
238     MSG                 msg;
239     LONG                lHash = 0;
240     HLPFILE*            hlpfile;
241     static CHAR         default_wndname[] = "main";
242     LPSTR               wndname = default_wndname;
243     WINHELP_DLL*        dll;
244
245     Globals.hInstance = hInstance;
246
247     /* Get options */
248     while (*cmdline && (*cmdline == ' ' || *cmdline == '-'))
249     {
250         CHAR   option;
251         LPCSTR topic_id;
252         if (*cmdline++ == ' ') continue;
253
254         option = *cmdline;
255         if (option) cmdline++;
256         while (*cmdline && *cmdline == ' ') cmdline++;
257         switch (option)
258         {
259         case 'i':
260         case 'I':
261             topic_id = cmdline;
262             while (*cmdline && *cmdline != ' ') cmdline++;
263             if (*cmdline) *cmdline++ = '\0';
264             lHash = HLPFILE_Hash(topic_id);
265             break;
266
267         case '3':
268         case '4':
269             Globals.wVersion = option - '0';
270             break;
271
272         case 'x':
273             show = SW_HIDE; 
274             Globals.isBook = FALSE;
275             break;
276
277         default:
278             WINE_FIXME("Unsupported cmd line: %s\n", cmdline);
279             break;
280         }
281     }
282
283     /* Create primary window */
284     if (!WINHELP_RegisterWinClasses())
285     {
286         WINE_FIXME("Couldn't register classes\n");
287         return 0;
288     }
289
290     if (*cmdline)
291     {
292         char*   ptr;
293         if ((*cmdline == '"') && (ptr = strchr(cmdline+1, '"')))
294         {
295             cmdline++;
296             *ptr = '\0';
297         }
298         if ((ptr = strchr(cmdline, '>')))
299         {
300             *ptr = '\0';
301             wndname = ptr + 1;
302         }
303         hlpfile = WINHELP_LookupHelpFile(cmdline);
304         if (!hlpfile) return 0;
305     }
306     else hlpfile = NULL;
307     WINHELP_CreateHelpWindowByHash(hlpfile, lHash, 
308                                    WINHELP_GetWindowInfo(hlpfile, wndname), show);
309
310     /* Message loop */
311     while (GetMessage(&msg, 0, 0, 0))
312     {
313         TranslateMessage(&msg);
314         DispatchMessage(&msg);
315     }
316     for (dll = Globals.dlls; dll; dll = dll->next)
317     {
318         if (dll->class & DC_INITTERM) dll->handler(DW_TERM, 0, 0);
319     }
320     return 0;
321 }
322
323 /***********************************************************************
324  *
325  *           RegisterWinClasses
326  */
327 static BOOL WINHELP_RegisterWinClasses(void)
328 {
329     WNDCLASS class_main, class_button_box, class_text, class_shadow, class_history;
330
331     class_main.style               = CS_HREDRAW | CS_VREDRAW;
332     class_main.lpfnWndProc         = WINHELP_MainWndProc;
333     class_main.cbClsExtra          = 0;
334     class_main.cbWndExtra          = sizeof(LONG);
335     class_main.hInstance           = Globals.hInstance;
336     class_main.hIcon               = LoadIcon(Globals.hInstance, MAKEINTRESOURCE(IDI_WINHELP));
337     class_main.hCursor             = LoadCursor(0, IDC_ARROW);
338     class_main.hbrBackground       = (HBRUSH)(COLOR_WINDOW+1);
339     class_main.lpszMenuName        = 0;
340     class_main.lpszClassName       = MAIN_WIN_CLASS_NAME;
341
342     class_button_box               = class_main;
343     class_button_box.lpfnWndProc   = WINHELP_ButtonBoxWndProc;
344     class_button_box.cbWndExtra    = 0;
345     class_button_box.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
346     class_button_box.lpszClassName = BUTTON_BOX_WIN_CLASS_NAME;
347
348     class_text                     = class_main;
349     class_text.lpfnWndProc         = WINHELP_TextWndProc;
350     class_text.hbrBackground       = 0;
351     class_text.lpszClassName       = TEXT_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_text) &&
366             RegisterClass(&class_shadow) &&
367             RegisterClass(&class_history));
368 }
369
370 typedef struct
371 {
372     WORD size;
373     WORD command;
374     LONG data;
375     LONG reserved;
376     WORD ofsFilename;
377     WORD ofsData;
378 } WINHELP,*LPWINHELP;
379
380 /******************************************************************
381  *              WINHELP_HandleCommand
382  *
383  *
384  */
385 static LRESULT  WINHELP_HandleCommand(HWND hSrcWnd, LPARAM lParam)
386 {
387     COPYDATASTRUCT*     cds = (COPYDATASTRUCT*)lParam;
388     WINHELP*            wh;
389
390     if (cds->dwData != 0xA1DE505)
391     {
392         WINE_FIXME("Wrong magic number (%08lx)\n", cds->dwData);
393         return 0;
394     }
395
396     wh = (WINHELP*)cds->lpData;
397
398     if (wh)
399     {
400         char*   ptr = (wh->ofsFilename) ? (LPSTR)wh + wh->ofsFilename : NULL;
401
402         WINE_TRACE("Got[%u]: cmd=%u data=%08x fn=%s\n",
403                    wh->size, wh->command, wh->data, ptr);
404         switch (wh->command)
405         {
406         case HELP_CONTEXT:
407             if (ptr)
408             {
409                 MACRO_JumpContext(ptr, "main", wh->data);
410             }
411             break;
412         case HELP_QUIT:
413             MACRO_Exit();
414             break;
415         case HELP_CONTENTS:
416             if (ptr)
417             {
418                 MACRO_JumpContents(ptr, "main");
419             }
420             break;
421         case HELP_HELPONHELP:
422             MACRO_HelpOn();
423             break;
424         /* case HELP_SETINDEX: */
425         case HELP_SETCONTENTS:
426             if (ptr)
427             {
428                 MACRO_SetContents(ptr, wh->data);
429             }
430             break;
431         case HELP_CONTEXTPOPUP:
432             if (ptr)
433             {
434                 MACRO_PopupContext(ptr, wh->data);
435             }
436             break;
437         /* case HELP_FORCEFILE:*/
438         /* case HELP_CONTEXTMENU: */
439         case HELP_FINDER:
440             /* in fact, should be the topic dialog box */
441             WINE_FIXME("HELP_FINDER: stub\n");
442             if (ptr)
443             {
444                 MACRO_JumpHash(ptr, "main", 0);
445             }
446             break;
447         /* case HELP_WM_HELP: */
448         /* case HELP_SETPOPUP_POS: */
449         /* case HELP_KEY: */
450         /* case HELP_COMMAND: */
451         /* case HELP_PARTIALKEY: */
452         /* case HELP_MULTIKEY: */
453         /* case HELP_SETWINPOS: */
454         default:
455             WINE_FIXME("Unhandled command (%x) for remote winhelp control\n", wh->command);
456             break;
457         }
458     }
459     /* Always return success for now */
460     return 1;
461 }
462
463 void            WINHELP_LayoutMainWindow(WINHELP_WINDOW* win)
464 {
465     RECT        rect, button_box_rect;
466     INT         text_top = 0;
467     HWND        hButtonBoxWnd = GetDlgItem(win->hMainWnd, CTL_ID_BUTTON);
468     HWND        hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
469
470     GetClientRect(win->hMainWnd, &rect);
471
472     /* Update button box and text Window */
473     SetWindowPos(hButtonBoxWnd, HWND_TOP,
474                  rect.left, rect.top,
475                  rect.right - rect.left,
476                  rect.bottom - rect.top, 0);
477
478     if (GetWindowRect(hButtonBoxWnd, &button_box_rect))
479         text_top = rect.top + button_box_rect.bottom - button_box_rect.top;
480
481     SetWindowPos(hTextWnd, HWND_TOP,
482                  rect.left, text_top,
483                  rect.right - rect.left,
484                  rect.bottom - text_top, 0);
485
486 }
487
488 static void     WINHELP_RememberPage(WINHELP_WINDOW* win, HLPFILE_PAGE* page)
489 {
490     unsigned        num;
491
492     if (!Globals.history.index || Globals.history.set[0].page != page)
493     {
494         num = sizeof(Globals.history.set) / sizeof(Globals.history.set[0]);
495         /* we're full, remove latest entry */
496         if (Globals.history.index == num)
497         {
498             HLPFILE_FreeHlpFile(Globals.history.set[num - 1].page->file);
499             Globals.history.index--;
500         }
501         memmove(&Globals.history.set[1], &Globals.history.set[0],
502                 Globals.history.index * sizeof(Globals.history.set[0]));
503         Globals.history.set[0].page = page;
504         Globals.history.set[0].wininfo = win->info;
505         Globals.history.index++;
506         page->file->wRefCount++;
507     }
508     if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE);
509
510     num = sizeof(win->back.set) / sizeof(win->back.set[0]);
511     if (win->back.index == num)
512     {
513         /* we're full, remove latest entry */
514         HLPFILE_FreeHlpFile(win->back.set[0].page->file);
515         memmove(&win->back.set[0], &win->back.set[1],
516                 (num - 1) * sizeof(win->back.set[0]));
517         win->back.index--;
518     }
519     win->back.set[win->back.index].page = page;
520     win->back.set[win->back.index++].wininfo = win->info;
521     page->file->wRefCount++;
522 }
523
524 /***********************************************************************
525  *
526  *           WINHELP_CreateHelpWindow
527  */
528 BOOL WINHELP_CreateHelpWindow(WINHELP_WNDPAGE* wpage, int nCmdShow, BOOL remember)
529 {
530     WINHELP_WINDOW*     win = NULL;
531     BOOL                bPrimary, bPopup, bReUsed = FALSE;
532     LPSTR               name;
533
534     bPrimary = !lstrcmpi(wpage->wininfo->name, "main");
535     bPopup = !bPrimary && (wpage->wininfo->win_style & WS_POPUP);
536
537     if (wpage->page && !wpage->page->first_paragraph) HLPFILE_BrowsePage(wpage->page);
538
539     if (!bPopup)
540     {
541         for (win = Globals.win_list; win; win = win->next)
542         {
543             if (!lstrcmpi(win->lpszName, wpage->wininfo->name))
544             {
545                 WINHELP_DeleteButtons(win);
546                 bReUsed = TRUE;
547                 SetWindowText(win->hMainWnd, WINHELP_GetCaption(wpage));
548                 if (wpage->wininfo->origin.x != CW_USEDEFAULT &&
549                     wpage->wininfo->origin.y != CW_USEDEFAULT)
550                     SetWindowPos(win->hMainWnd, HWND_TOP,
551                                  wpage->wininfo->origin.x, wpage->wininfo->origin.y, 0, 0, SWP_NOSIZE);
552                 if (wpage->wininfo->size.cx != CW_USEDEFAULT &&
553                     wpage->wininfo->size.cy != CW_USEDEFAULT)
554                     SetWindowPos(win->hMainWnd, HWND_TOP,
555                                  0, 0, wpage->wininfo->size.cx, wpage->wininfo->size.cy, SWP_NOMOVE);
556                 if (wpage->page && wpage->page->file != win->page->file)
557                     WINHELP_DeleteBackSet(win);
558                 WINHELP_InitFonts(win->hMainWnd);
559
560                 win->page = wpage->page;
561                 win->info = wpage->wininfo;
562                 WINHELP_SetupText(GetDlgItem(win->hMainWnd, CTL_ID_TEXT));
563                 InvalidateRect(win->hMainWnd, NULL, TRUE);
564                 if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE);
565                 break;
566             }
567         }
568     }
569
570     if (!win)
571     {
572         /* Initialize WINHELP_WINDOW struct */
573         win = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
574                         sizeof(WINHELP_WINDOW) + strlen(wpage->wininfo->name) + 1);
575         if (!win) return FALSE;
576         win->next = Globals.win_list;
577         Globals.win_list = win;
578
579         name = (char*)win + sizeof(WINHELP_WINDOW);
580         lstrcpy(name, wpage->wininfo->name);
581         win->lpszName = name;
582         win->hHandCur = LoadCursorW(0, (LPWSTR)IDC_HAND);
583         win->back.index = 0;
584     }
585     win->page = wpage->page;
586     win->info = wpage->wininfo;
587
588     win->hArrowCur = LoadCursorA(0, (LPSTR)IDC_ARROW);
589     win->hHandCur = LoadCursorA(0, (LPSTR)IDC_HAND);
590
591     if (!bPopup && wpage->page && remember)
592     {
593         WINHELP_RememberPage(win, wpage->page);
594     }
595
596     if (!bPopup)
597         Globals.active_win = win;
598
599     /* Initialize default pushbuttons */
600     if (bPrimary && wpage->page)
601     {
602         CHAR    buffer[MAX_STRING_LEN];
603
604         LoadString(Globals.hInstance, STID_CONTENTS, buffer, sizeof(buffer));
605         MACRO_CreateButton("BTN_CONTENTS", buffer, "Contents()");
606         LoadString(Globals.hInstance, STID_SEARCH,buffer, sizeof(buffer));
607         MACRO_CreateButton("BTN_SEARCH", buffer, "Search()");
608         LoadString(Globals.hInstance, STID_BACK, buffer, sizeof(buffer));
609         MACRO_CreateButton("BTN_BACK", buffer, "Back()");
610         if (win->back.index <= 1) MACRO_DisableButton("BTN_BACK");
611         LoadString(Globals.hInstance, STID_HISTORY, buffer, sizeof(buffer));
612         MACRO_CreateButton("BTN_HISTORY", buffer, "History()");
613         LoadString(Globals.hInstance, STID_TOPICS, buffer, sizeof(buffer));
614         MACRO_CreateButton("BTN_TOPICS", buffer, "Finder()");
615     }
616
617     /* Initialize file specific pushbuttons */
618     if (!(wpage->wininfo->win_style & WS_POPUP) && wpage->page)
619     {
620         HLPFILE_MACRO  *macro;
621         for (macro = wpage->page->file->first_macro; macro; macro = macro->next)
622             MACRO_ExecuteMacro(macro->lpszMacro);
623
624         for (macro = wpage->page->first_macro; macro; macro = macro->next)
625             MACRO_ExecuteMacro(macro->lpszMacro);
626     }
627
628     if (!bReUsed)
629     {
630         win->hMainWnd = CreateWindowEx((bPopup) ? WS_EX_TOOLWINDOW : 0, MAIN_WIN_CLASS_NAME,
631                                        WINHELP_GetCaption(wpage),
632                                        bPrimary ? WS_OVERLAPPEDWINDOW : wpage->wininfo->win_style,
633                                        wpage->wininfo->origin.x, wpage->wininfo->origin.y,
634                                        wpage->wininfo->size.cx, wpage->wininfo->size.cy,
635                                        bPopup ? Globals.active_win->hMainWnd : NULL,
636                                        bPrimary ? LoadMenu(Globals.hInstance, MAKEINTRESOURCE(MAIN_MENU)) : 0,
637                                        Globals.hInstance, win);
638         if (!bPopup)
639             /* Create button box and text Window */
640             CreateWindow(BUTTON_BOX_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
641                          0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_BUTTON, Globals.hInstance, NULL);
642
643         CreateWindow(TEXT_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
644                      0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_TEXT, Globals.hInstance, win);
645     }
646
647     WINHELP_LayoutMainWindow(win);
648     if (bPopup) Globals.hPopupWnd = win->hMainWnd;
649
650     ShowWindow(win->hMainWnd, nCmdShow);
651     UpdateWindow(win->hMainWnd);
652
653     return TRUE;
654 }
655
656 /***********************************************************************
657  *
658  *           WINHELP_CreateHelpWindowByHash
659  */
660 BOOL WINHELP_CreateHelpWindowByHash(HLPFILE* hlpfile, LONG lHash, 
661                                     HLPFILE_WINDOWINFO* wi, int nCmdShow)
662 {
663     WINHELP_WNDPAGE     wpage;
664
665     wpage.page = NULL;
666     if (hlpfile)
667         wpage.page = lHash ? HLPFILE_PageByHash(hlpfile, lHash) :
668             HLPFILE_Contents(hlpfile);
669     if (wpage.page) wpage.page->file->wRefCount++;
670     wpage.wininfo = wi;
671     return WINHELP_CreateHelpWindow(&wpage, nCmdShow, TRUE);
672 }
673
674 /***********************************************************************
675  *
676  *           WINHELP_CreateHelpWindowByMap
677  */
678 BOOL WINHELP_CreateHelpWindowByMap(HLPFILE* hlpfile, LONG lMap,
679                                    HLPFILE_WINDOWINFO* wi, int nCmdShow)
680 {
681     WINHELP_WNDPAGE     wpage;
682
683     wpage.page = HLPFILE_PageByMap(hlpfile, lMap);
684     if (wpage.page) wpage.page->file->wRefCount++;
685     wpage.wininfo = wi;
686     return WINHELP_CreateHelpWindow(&wpage, nCmdShow, TRUE);
687 }
688
689 /***********************************************************************
690  *
691  *           WINHELP_CreateHelpWindowByOffset
692  */
693 BOOL WINHELP_CreateHelpWindowByOffset(HLPFILE* hlpfile, LONG lOffset,
694                                       HLPFILE_WINDOWINFO* wi, int nCmdShow)
695 {
696     WINHELP_WNDPAGE     wpage;
697
698     wpage.page = HLPFILE_PageByOffset(hlpfile, lOffset);
699     if (wpage.page) wpage.page->file->wRefCount++;
700     wpage.wininfo = wi;
701     return WINHELP_CreateHelpWindow(&wpage, nCmdShow, TRUE);
702 }
703
704 /***********************************************************************
705  *
706  *           WINHELP_MainWndProc
707  */
708 static LRESULT CALLBACK WINHELP_MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
709 {
710     WINHELP_WINDOW *win;
711     WINHELP_BUTTON *button;
712     RECT rect;
713     INT  curPos, min, max, dy, keyDelta;
714     HWND hTextWnd;
715
716     WINHELP_CheckPopup(msg);
717
718     switch (msg)
719     {
720     case WM_NCCREATE:
721         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
722         SetWindowLongPtr(hWnd, 0, (ULONG_PTR) win);
723         if (!win->page && Globals.isBook)
724             PostMessage(hWnd, WM_COMMAND, MNID_FILE_OPEN, 0);
725         win->hMainWnd = hWnd;
726         break;
727
728     case WM_WINDOWPOSCHANGED:
729         WINHELP_LayoutMainWindow((WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0));
730         break;
731
732     case WM_COMMAND:
733         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
734         switch (wParam)
735         {
736             /* Menu FILE */
737         case MNID_FILE_OPEN:    MACRO_FileOpen();       break;
738         case MNID_FILE_PRINT:   MACRO_Print();          break;
739         case MNID_FILE_SETUP:   MACRO_PrinterSetup();   break;
740         case MNID_FILE_EXIT:    MACRO_Exit();           break;
741
742             /* Menu EDIT */
743         case MNID_EDIT_COPYDLG: MACRO_CopyDialog();     break;
744         case MNID_EDIT_ANNOTATE:MACRO_Annotate();       break;
745
746             /* Menu Bookmark */
747         case MNID_BKMK_DEFINE:  MACRO_BookmarkDefine(); break;
748
749             /* Menu Help */
750         case MNID_HELP_HELPON:  MACRO_HelpOn();         break;
751         case MNID_HELP_HELPTOP: MACRO_HelpOnTop();      break;
752         case MNID_HELP_ABOUT:   MACRO_About();          break;
753         case MNID_HELP_WINE:    ShellAbout(hWnd, "WINE", "Help", 0); break;
754
755         default:
756             /* Buttons */
757             for (button = win->first_button; button; button = button->next)
758                 if (wParam == button->wParam) break;
759             if (button)
760                 MACRO_ExecuteMacro(button->lpszMacro);
761             else if (!HIWORD(wParam))
762                 WINHELP_MessageBoxIDS(STID_NOT_IMPLEMENTED, 0x121, MB_OK);
763             break;
764         }
765         break;
766     case WM_DESTROY:
767         if (Globals.hPopupWnd) DestroyWindow(Globals.hPopupWnd);
768         break;
769     case WM_COPYDATA:
770         return WINHELP_HandleCommand((HWND)wParam, lParam);
771
772     case WM_KEYDOWN:
773         keyDelta = 0;
774
775         switch (wParam)
776         {
777         case VK_UP:
778         case VK_DOWN:
779             keyDelta = GetSystemMetrics(SM_CXVSCROLL);
780             if (wParam == VK_UP)
781                 keyDelta = -keyDelta;
782
783         case VK_PRIOR:
784         case VK_NEXT:
785             win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
786             hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
787             curPos = GetScrollPos(hTextWnd, SB_VERT);
788             GetScrollRange(hTextWnd, SB_VERT, &min, &max);
789
790             if (keyDelta == 0)
791             {            
792                 GetClientRect(hTextWnd, &rect);
793                 keyDelta = (rect.bottom - rect.top) / 2;
794                 if (wParam == VK_PRIOR)
795                     keyDelta = -keyDelta;
796             }
797
798             curPos += keyDelta;
799             if (curPos > max)
800                  curPos = max;
801             else if (curPos < min)
802                  curPos = min;
803
804             dy = GetScrollPos(hTextWnd, SB_VERT) - curPos;
805             SetScrollPos(hTextWnd, SB_VERT, curPos, TRUE);
806             ScrollWindow(hTextWnd, 0, dy, NULL, NULL);
807             UpdateWindow(hTextWnd);
808             return 0;
809
810         case VK_ESCAPE:
811             MACRO_Exit();
812             return 0;
813         }
814         break;
815     case WM_NCDESTROY:
816         {
817             BOOL bExit;
818             win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
819             if (hWnd == Globals.hPopupWnd) Globals.hPopupWnd = 0;
820             bExit = (Globals.wVersion >= 4 && !lstrcmpi(win->lpszName, "main"));
821             WINHELP_DeleteWindow(win);
822
823             if (bExit) MACRO_Exit();
824             if (!Globals.win_list)
825                 PostQuitMessage(0);
826         }
827         break;
828     }
829     return DefWindowProc(hWnd, msg, wParam, lParam);
830 }
831
832 /***********************************************************************
833  *
834  *           WINHELP_ButtonBoxWndProc
835  */
836 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
837 {
838     WINDOWPOS      *winpos;
839     WINHELP_WINDOW *win;
840     WINHELP_BUTTON *button;
841     SIZE button_size;
842     INT  x, y;
843
844     WINHELP_CheckPopup(msg);
845
846     switch (msg)
847     {
848     case WM_WINDOWPOSCHANGING:
849         winpos = (WINDOWPOS*) lParam;
850         win = (WINHELP_WINDOW*) GetWindowLongPtr(GetParent(hWnd), 0);
851
852         /* Update buttons */
853         button_size.cx = 0;
854         button_size.cy = 0;
855         for (button = win->first_button; button; button = button->next)
856         {
857             HDC  hDc;
858             SIZE textsize;
859             if (!button->hWnd)
860             {
861                 button->hWnd = CreateWindow(STRING_BUTTON, button->lpszName,
862                                             WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
863                                             0, 0, 0, 0,
864                                             hWnd, (HMENU) button->wParam,
865                                             Globals.hInstance, 0);
866                 if (button->hWnd) {
867                     if (Globals.button_proc == NULL)
868                         Globals.button_proc = (WNDPROC) GetWindowLongPtr(button->hWnd, GWLP_WNDPROC);
869                     SetWindowLongPtr(button->hWnd, GWLP_WNDPROC, (LONG_PTR) WINHELP_ButtonWndProc);
870                 }
871             }
872             hDc = GetDC(button->hWnd);
873             GetTextExtentPoint(hDc, button->lpszName,
874                                lstrlen(button->lpszName), &textsize);
875             ReleaseDC(button->hWnd, hDc);
876
877             button_size.cx = max(button_size.cx, textsize.cx + BUTTON_CX);
878             button_size.cy = max(button_size.cy, textsize.cy + BUTTON_CY);
879         }
880
881         x = 0;
882         y = 0;
883         for (button = win->first_button; button; button = button->next)
884         {
885             SetWindowPos(button->hWnd, HWND_TOP, x, y, button_size.cx, button_size.cy, 0);
886
887             if (x + 2 * button_size.cx <= winpos->cx)
888                 x += button_size.cx;
889             else
890                 x = 0, y += button_size.cy;
891         }
892         winpos->cy = y + (x ? button_size.cy : 0);
893         break;
894
895     case WM_COMMAND:
896         SendMessage(GetParent(hWnd), msg, wParam, lParam);
897         break;
898
899     case WM_KEYDOWN:
900         switch (wParam)
901         {
902         case VK_UP:
903         case VK_DOWN:
904         case VK_PRIOR:
905         case VK_NEXT:
906         case VK_ESCAPE:
907             return SendMessage(GetParent(hWnd), msg, wParam, lParam);
908         }
909         break;
910     }
911
912     return DefWindowProc(hWnd, msg, wParam, lParam);
913 }
914
915 /***********************************************************************
916  *
917  *           WINHELP_ButtonWndProc
918  */
919 static LRESULT CALLBACK WINHELP_ButtonWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
920 {
921     if (msg == WM_KEYDOWN)
922     {
923         switch (wParam)
924         {
925         case VK_UP:
926         case VK_DOWN:
927         case VK_PRIOR:
928         case VK_NEXT:
929         case VK_ESCAPE:
930             return SendMessage(GetParent(hWnd), msg, wParam, lParam);
931         }
932     }
933
934     return CallWindowProc(Globals.button_proc, hWnd, msg, wParam, lParam);
935 }
936
937 /***********************************************************************
938  *
939  *           WINHELP_TextWndProc
940  */
941 static LRESULT CALLBACK WINHELP_TextWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
942 {
943     WINHELP_WINDOW    *win;
944     WINHELP_LINE      *line;
945     WINHELP_LINE_PART *part;
946     WINDOWPOS         *winpos;
947     PAINTSTRUCT        ps;
948     HDC   hDc;
949     POINT mouse;
950     INT   scroll_pos;
951     HWND  hPopupWnd;
952
953     if (msg != WM_LBUTTONDOWN)
954         WINHELP_CheckPopup(msg);
955
956     switch (msg)
957     {
958     case WM_NCCREATE:
959         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
960         SetWindowLongPtr(hWnd, 0, (ULONG_PTR) win);
961         win->hBrush = CreateSolidBrush(win->info->sr_color);
962         WINHELP_InitFonts(hWnd);
963         break;
964
965     case WM_CREATE:
966         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
967
968         /* Calculate vertical size and position of a popup window */
969         if (win->info->win_style & WS_POPUP)
970         {
971             POINT origin;
972             RECT old_window_rect;
973             RECT old_client_rect;
974             SIZE old_window_size;
975             SIZE old_client_size;
976             SIZE new_client_size;
977             SIZE new_window_size;
978
979             GetWindowRect(GetParent(hWnd), &old_window_rect);
980             origin.x = old_window_rect.left;
981             origin.y = old_window_rect.top;
982             old_window_size.cx = old_window_rect.right  - old_window_rect.left;
983             old_window_size.cy = old_window_rect.bottom - old_window_rect.top;
984
985             WINHELP_LayoutMainWindow(win);
986             GetClientRect(hWnd, &old_client_rect);
987             old_client_size.cx = old_client_rect.right  - old_client_rect.left;
988             old_client_size.cy = old_client_rect.bottom - old_client_rect.top;
989
990             new_client_size = old_client_size;
991             WINHELP_SplitLines(hWnd, &new_client_size);
992
993             if (origin.y + POPUP_YDISTANCE + new_client_size.cy <= GetSystemMetrics(SM_CYSCREEN))
994                 origin.y += POPUP_YDISTANCE;
995             else
996                 origin.y -= POPUP_YDISTANCE + new_client_size.cy;
997
998             new_window_size.cx = old_window_size.cx - old_client_size.cx + new_client_size.cx;
999             new_window_size.cy = old_window_size.cy - old_client_size.cy + new_client_size.cy;
1000
1001             win->hShadowWnd =
1002                 CreateWindowEx(WS_EX_TOOLWINDOW, SHADOW_WIN_CLASS_NAME, "", WS_POPUP,
1003                              origin.x + SHADOW_DX, origin.y + SHADOW_DY,
1004                              new_window_size.cx, new_window_size.cy,
1005                              0, 0, Globals.hInstance, 0);
1006
1007             SetWindowPos(GetParent(hWnd), HWND_TOP, origin.x, origin.y,
1008                          new_window_size.cx, new_window_size.cy,
1009                          0);
1010             SetWindowPos(win->hShadowWnd, hWnd, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
1011             ShowWindow(win->hShadowWnd, SW_NORMAL);
1012             SetActiveWindow(hWnd);
1013         }
1014         break;
1015
1016     case WM_WINDOWPOSCHANGED:
1017         winpos = (WINDOWPOS*) lParam;
1018
1019         if (!(winpos->flags & SWP_NOSIZE)) WINHELP_SetupText(hWnd);
1020         break;
1021
1022     case WM_MOUSEWHEEL:
1023     {
1024        int wheelDelta = 0;
1025        UINT scrollLines = 3;
1026        int curPos = GetScrollPos(hWnd, SB_VERT);
1027        int min, max;
1028
1029        GetScrollRange(hWnd, SB_VERT, &min, &max);
1030
1031        SystemParametersInfo(SPI_GETWHEELSCROLLLINES,0, &scrollLines, 0);
1032        if (wParam & (MK_SHIFT | MK_CONTROL))
1033            return DefWindowProc(hWnd, msg, wParam, lParam);
1034        wheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
1035        if (abs(wheelDelta) >= WHEEL_DELTA && scrollLines) {
1036            int dy;
1037
1038            curPos += wheelDelta;
1039            if (curPos > max)
1040                 curPos = max;
1041            else if (curPos < min)
1042                 curPos = min;
1043
1044            dy = GetScrollPos(hWnd, SB_VERT) - curPos;
1045            SetScrollPos(hWnd, SB_VERT, curPos, TRUE);
1046            ScrollWindow(hWnd, 0, dy, NULL, NULL);
1047            UpdateWindow(hWnd);
1048        }
1049     }
1050     break;
1051
1052     case WM_VSCROLL:
1053     {
1054         BOOL  update = TRUE;
1055         RECT  rect;
1056         INT   Min, Max;
1057         INT   CurPos = GetScrollPos(hWnd, SB_VERT);
1058         INT   dy;
1059         
1060         GetScrollRange(hWnd, SB_VERT, &Min, &Max);
1061         GetClientRect(hWnd, &rect);
1062
1063         switch (wParam & 0xffff)
1064         {
1065         case SB_THUMBTRACK:
1066         case SB_THUMBPOSITION: CurPos  = wParam >> 16;                   break;
1067         case SB_TOP:           CurPos  = Min;                            break;
1068         case SB_BOTTOM:        CurPos  = Max;                            break;
1069         case SB_PAGEUP:        CurPos -= (rect.bottom - rect.top) / 2;   break;
1070         case SB_PAGEDOWN:      CurPos += (rect.bottom - rect.top) / 2;   break;
1071         case SB_LINEUP:        CurPos -= GetSystemMetrics(SM_CXVSCROLL); break;
1072         case SB_LINEDOWN:      CurPos += GetSystemMetrics(SM_CXVSCROLL); break;
1073         default: update = FALSE;
1074         }
1075         if (update)
1076         {
1077             if (CurPos > Max)
1078                 CurPos = Max;
1079             else if (CurPos < Min)
1080                 CurPos = Min;
1081             dy = GetScrollPos(hWnd, SB_VERT) - CurPos;
1082             SetScrollPos(hWnd, SB_VERT, CurPos, TRUE);
1083             ScrollWindow(hWnd, 0, dy, NULL, NULL);
1084             UpdateWindow(hWnd);
1085         }
1086     }
1087     break;
1088
1089     case WM_PAINT:
1090         hDc = BeginPaint(hWnd, &ps);
1091         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1092         scroll_pos = GetScrollPos(hWnd, SB_VERT);
1093
1094         /* No DPtoLP needed - MM_TEXT map mode */
1095         if (ps.fErase) FillRect(hDc, &ps.rcPaint, win->hBrush);
1096         for (line = win->first_line; line; line = line->next)
1097         {
1098             for (part = &line->first_part; part; part = part->next)
1099             {
1100                 switch (part->cookie)
1101                 {
1102                 case hlp_line_part_text:
1103                     SelectObject(hDc, part->u.text.hFont);
1104                     SetTextColor(hDc, part->u.text.color);
1105                     SetBkColor(hDc, win->info->sr_color);
1106                     TextOut(hDc, part->rect.left, part->rect.top - scroll_pos,
1107                             part->u.text.lpsText, part->u.text.wTextLen);
1108                     if (part->u.text.wUnderline)
1109                     {
1110                         HPEN    hPen;
1111
1112                         switch (part->u.text.wUnderline)
1113                         {
1114                         case 1: /* simple */
1115                         case 2: /* double */
1116                             hPen = CreatePen(PS_SOLID, 1, part->u.text.color);
1117                             break;
1118                         case 3: /* dotted */
1119                             hPen = CreatePen(PS_DOT, 1, part->u.text.color);
1120                             break;
1121                         default:
1122                             WINE_FIXME("Unknow underline type\n");
1123                             continue;
1124                         }
1125
1126                         SelectObject(hDc, hPen);
1127                         MoveToEx(hDc, part->rect.left, part->rect.bottom - scroll_pos - 1, NULL);
1128                         LineTo(hDc, part->rect.right, part->rect.bottom - scroll_pos - 1);
1129                         if (part->u.text.wUnderline == 2)
1130                         {
1131                             MoveToEx(hDc, part->rect.left, part->rect.bottom - scroll_pos + 1, NULL);
1132                             LineTo(hDc, part->rect.right, part->rect.bottom - scroll_pos + 1);
1133                         }
1134                         DeleteObject(hPen);
1135                     }
1136                     break;
1137                 case hlp_line_part_bitmap:
1138                     {
1139                         HDC hMemDC;
1140
1141                         hMemDC = CreateCompatibleDC(hDc);
1142                         SelectObject(hMemDC, part->u.bitmap.hBitmap);
1143                         BitBlt(hDc, part->rect.left, part->rect.top - scroll_pos,
1144                                part->rect.right - part->rect.left, part->rect.bottom - part->rect.top,
1145                                hMemDC, 0, 0, SRCCOPY);
1146                         DeleteDC(hMemDC);
1147                     }
1148                     break;
1149                 case hlp_line_part_metafile:
1150                     {
1151                         HDC hMemDC;
1152                         HBITMAP hBitmap;
1153                         SIZE sz;
1154                         RECT rc;
1155
1156                         sz.cx = part->rect.right - part->rect.left;
1157                         sz.cy = part->rect.bottom - part->rect.top;
1158                         hMemDC = CreateCompatibleDC(hDc);
1159                         hBitmap = CreateCompatibleBitmap(hDc, sz.cx, sz.cy);
1160                         SelectObject(hMemDC, hBitmap);
1161                         SelectObject(hMemDC, win->hBrush);
1162                         rc.left = 0;
1163                         rc.top = 0;
1164                         rc.right = sz.cx;
1165                         rc.bottom = sz.cy;
1166                         FillRect(hMemDC, &rc, win->hBrush);
1167                         SetMapMode(hMemDC, part->u.metafile.mm);
1168                         SetWindowExtEx(hMemDC, sz.cx, sz.cy, 0);
1169                         SetViewportExtEx(hMemDC, sz.cx, sz.cy, 0);
1170                         SetWindowOrgEx(hMemDC, 0, 0, 0);
1171                         SetViewportOrgEx(hMemDC, 0, 0, 0);
1172                         PlayMetaFile(hMemDC, part->u.metafile.hMetaFile);
1173                         SetMapMode(hMemDC, MM_TEXT);
1174                         SetWindowOrgEx(hMemDC, 0, 0, 0);
1175                         SetViewportOrgEx(hMemDC, 0, 0, 0);
1176                         BitBlt(hDc, part->rect.left, part->rect.top - scroll_pos,
1177                                sz.cx, sz.cy, hMemDC, 0, 0, SRCCOPY);
1178                         DeleteDC(hMemDC);
1179                         DeleteObject(hBitmap);
1180                     }
1181                     break;
1182                 }
1183             }
1184         }
1185
1186         EndPaint(hWnd, &ps);
1187         break;
1188
1189     case WM_MOUSEMOVE:
1190         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1191
1192         if (WINHELP_IsOverLink(win, wParam, lParam))
1193             SetCursor(win->hHandCur); /* set to hand pointer cursor to indicate a link */
1194         else
1195             SetCursor(win->hArrowCur); /* set to hand pointer cursor to indicate a link */
1196
1197         break;
1198
1199     case WM_LBUTTONDOWN:
1200         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1201
1202         hPopupWnd = Globals.hPopupWnd;
1203         Globals.hPopupWnd = 0;
1204
1205         part = WINHELP_IsOverLink(win, wParam, lParam);
1206         if (part)
1207         {
1208             HLPFILE*            hlpfile;
1209             HLPFILE_WINDOWINFO* wi;
1210
1211             mouse.x = (short)LOWORD(lParam);
1212             mouse.y = (short)HIWORD(lParam);
1213
1214             if (part->link) switch (part->link->cookie)
1215             {
1216             case hlp_link_link:
1217                 hlpfile = WINHELP_LookupHelpFile(part->link->lpszString);
1218                 if (part->link->window == -1)
1219                     wi = win->info;
1220                 else if (part->link->window < hlpfile->numWindows)
1221                     wi = &hlpfile->windows[part->link->window];
1222                 else
1223                 {
1224                     WINE_WARN("link to window %d/%d\n", part->link->window, hlpfile->numWindows);
1225                     break;
1226                 }
1227                 WINHELP_CreateHelpWindowByHash(hlpfile, part->link->lHash, wi,
1228                                                SW_NORMAL);
1229                 break;
1230             case hlp_link_popup:
1231                 hlpfile = WINHELP_LookupHelpFile(part->link->lpszString);
1232                 if (hlpfile) WINHELP_CreateHelpWindowByHash(hlpfile, part->link->lHash, 
1233                                                WINHELP_GetPopupWindowInfo(hlpfile, hWnd, &mouse),
1234                                                SW_NORMAL);
1235                 break;
1236             case hlp_link_macro:
1237                 MACRO_ExecuteMacro(part->link->lpszString);
1238                 break;
1239             default:
1240                 WINE_FIXME("Unknown link cookie %d\n", part->link->cookie);
1241             }
1242         }
1243
1244         if (hPopupWnd)
1245             DestroyWindow(hPopupWnd);
1246         break;
1247
1248     }
1249
1250     return DefWindowProc(hWnd, msg, wParam, lParam);
1251 }
1252
1253 /******************************************************************
1254  *              WINHELP_HistoryWndProc
1255  *
1256  *
1257  */
1258 static LRESULT CALLBACK WINHELP_HistoryWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1259 {
1260     WINHELP_WINDOW*     win;
1261     PAINTSTRUCT         ps;
1262     HDC                 hDc;
1263     TEXTMETRIC          tm;
1264     unsigned int        i;
1265     RECT                r;
1266
1267     switch (msg)
1268     {
1269     case WM_NCCREATE:
1270         win = (WINHELP_WINDOW*)((LPCREATESTRUCT)lParam)->lpCreateParams;
1271         SetWindowLongPtr(hWnd, 0, (ULONG_PTR)win);
1272         win->hHistoryWnd = hWnd;
1273         break;
1274     case WM_CREATE:
1275         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1276         hDc = GetDC(hWnd);
1277         GetTextMetrics(hDc, &tm);
1278         GetWindowRect(hWnd, &r);
1279
1280         r.right = r.left + 30 * tm.tmAveCharWidth;
1281         r.bottom = r.top + (sizeof(Globals.history.set) / sizeof(Globals.history.set[0])) * tm.tmHeight;
1282         AdjustWindowRect(&r, GetWindowLong(hWnd, GWL_STYLE), FALSE);
1283         if (r.left < 0) {r.right -= r.left; r.left = 0;}
1284         if (r.top < 0) {r.bottom -= r.top; r.top = 0;}
1285
1286         MoveWindow(hWnd, r.left, r.top, r.right, r.bottom, TRUE);
1287         ReleaseDC(hWnd, hDc);
1288         break;
1289     case WM_LBUTTONDOWN:
1290         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1291         hDc = GetDC(hWnd);
1292         GetTextMetrics(hDc, &tm);
1293         i = HIWORD(lParam) / tm.tmHeight;
1294         if (i < Globals.history.index)
1295             WINHELP_CreateHelpWindow(&Globals.history.set[i], SW_SHOW, TRUE);
1296         ReleaseDC(hWnd, hDc);
1297         break;
1298     case WM_PAINT:
1299         hDc = BeginPaint(hWnd, &ps);
1300         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1301         GetTextMetrics(hDc, &tm);
1302
1303         for (i = 0; i < Globals.history.index; i++)
1304         {
1305             if (Globals.history.set[i].page->file == Globals.active_win->page->file)
1306             {
1307                 TextOut(hDc, 0, i * tm.tmHeight,
1308                         Globals.history.set[i].page->lpszTitle,
1309                         strlen(Globals.history.set[i].page->lpszTitle));
1310             }
1311             else
1312             {
1313                 char        buffer[1024];
1314                 const char* ptr1;
1315                 const char* ptr2;
1316                 unsigned    len;
1317
1318                 ptr1 = strrchr(Globals.history.set[i].page->file->lpszPath, '\\');
1319                 if (!ptr1) ptr1 = Globals.history.set[i].page->file->lpszPath;
1320                 else ptr1++;
1321                 ptr2 = strrchr(ptr1, '.');
1322                 len = ptr2 ? ptr2 - ptr1 : strlen(ptr1);
1323                 if (len > sizeof(buffer)) len = sizeof(buffer);
1324                 memcpy(buffer, ptr1, len);
1325                 if (len < sizeof(buffer)) buffer[len++] = ':';
1326                 strncpy(&buffer[len], Globals.history.set[i].page->lpszTitle, sizeof(buffer) - len);
1327                 buffer[sizeof(buffer) - 1] = '\0';
1328                 TextOut(hDc, 0, i * tm.tmHeight, buffer, strlen(buffer));
1329             }
1330         }
1331         EndPaint(hWnd, &ps);
1332         break;
1333     case WM_DESTROY:
1334         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1335         if (hWnd == win->hHistoryWnd)
1336             win->hHistoryWnd = 0;
1337         break;
1338     }
1339     return DefWindowProc(hWnd, msg, wParam, lParam);
1340 }
1341
1342 /***********************************************************************
1343  *
1344  *           WINHELP_ShadowWndProc
1345  */
1346 static LRESULT CALLBACK WINHELP_ShadowWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1347 {
1348     WINHELP_CheckPopup(msg);
1349     return DefWindowProc(hWnd, msg, wParam, lParam);
1350 }
1351
1352 /***********************************************************************
1353  *
1354  *           SetupText
1355  */
1356 static void WINHELP_SetupText(HWND hWnd)
1357 {
1358     HDC  hDc = GetDC(hWnd);
1359     RECT rect;
1360     SIZE newsize;
1361
1362     ShowScrollBar(hWnd, SB_VERT, FALSE);
1363     if (!WINHELP_SplitLines(hWnd, NULL))
1364     {
1365         ShowScrollBar(hWnd, SB_VERT, TRUE);
1366         GetClientRect(hWnd, &rect);
1367
1368         WINHELP_SplitLines(hWnd, &newsize);
1369         SetScrollRange(hWnd, SB_VERT, 0, rect.top + newsize.cy - rect.bottom, TRUE);
1370     }
1371     else
1372     {
1373         SetScrollPos(hWnd, SB_VERT, 0, FALSE);
1374         SetScrollRange(hWnd, SB_VERT, 0, 0, FALSE);
1375     }
1376
1377     ReleaseDC(hWnd, hDc);
1378 }
1379
1380 /***********************************************************************
1381  *
1382  *           WINHELP_AppendText
1383  */
1384 static BOOL WINHELP_AppendText(WINHELP_LINE ***linep, WINHELP_LINE_PART ***partp,
1385                                LPSIZE space, LPSIZE textsize,
1386                                INT *line_ascent, INT ascent,
1387                                LPCSTR text, UINT textlen,
1388                                HFONT font, COLORREF color, HLPFILE_LINK *link,
1389                                unsigned underline)
1390 {
1391     WINHELP_LINE      *line;
1392     WINHELP_LINE_PART *part;
1393     LPSTR ptr;
1394
1395     if (!*partp) /* New line */
1396     {
1397         *line_ascent  = ascent;
1398
1399         line = HeapAlloc(GetProcessHeap(), 0,
1400                          sizeof(WINHELP_LINE) + textlen);
1401         if (!line) return FALSE;
1402
1403         line->next    = 0;
1404         part          = &line->first_part;
1405         ptr           = (char*)line + sizeof(WINHELP_LINE);
1406
1407         line->rect.top    = (**linep ? (**linep)->rect.bottom : 0) + space->cy;
1408         line->rect.bottom = line->rect.top;
1409         line->rect.left   = space->cx;
1410         line->rect.right  = space->cx;
1411
1412         if (**linep) *linep = &(**linep)->next;
1413         **linep = line;
1414         space->cy = 0;
1415     }
1416     else /* Same line */
1417     {
1418         line = **linep;
1419
1420         if (*line_ascent < ascent)
1421         {
1422             WINHELP_LINE_PART *p;
1423             for (p = &line->first_part; p; p = p->next)
1424             {
1425                 p->rect.top    += ascent - *line_ascent;
1426                 p->rect.bottom += ascent - *line_ascent;
1427             }
1428             line->rect.bottom += ascent - *line_ascent;
1429             *line_ascent = ascent;
1430         }
1431
1432         part = HeapAlloc(GetProcessHeap(), 0,
1433                          sizeof(WINHELP_LINE_PART) + textlen);
1434         if (!part) return FALSE;
1435         **partp = part;
1436         ptr     = (char*)part + sizeof(WINHELP_LINE_PART);
1437     }
1438
1439     memcpy(ptr, text, textlen);
1440     part->cookie            = hlp_line_part_text;
1441     part->rect.left         = line->rect.right + (*partp ? space->cx : 0);
1442     part->rect.right        = part->rect.left + textsize->cx;
1443     line->rect.right        = part->rect.right;
1444     part->rect.top          =
1445         ((*partp) ? line->rect.top : line->rect.bottom) + *line_ascent - ascent;
1446     part->rect.bottom       = part->rect.top + textsize->cy;
1447     line->rect.bottom       = max(line->rect.bottom, part->rect.bottom);
1448     part->u.text.lpsText    = ptr;
1449     part->u.text.wTextLen   = textlen;
1450     part->u.text.hFont      = font;
1451     part->u.text.color      = color;
1452     part->u.text.wUnderline = underline;
1453
1454     WINE_TRACE("Appended text '%*.*s'[%d] @ (%d,%d-%d,%d)\n",
1455                part->u.text.wTextLen,
1456                part->u.text.wTextLen,
1457                part->u.text.lpsText,
1458                part->u.text.wTextLen,
1459                part->rect.left, part->rect.top, part->rect.right, part->rect.bottom);
1460
1461     part->link = link;
1462     if (link) link->wRefCount++;
1463
1464     part->next          = 0;
1465     *partp              = &part->next;
1466
1467     space->cx = 0;
1468
1469     return TRUE;
1470 }
1471
1472 /***********************************************************************
1473  *
1474  *           WINHELP_AppendGfxObject
1475  */
1476 static WINHELP_LINE_PART* WINHELP_AppendGfxObject(WINHELP_LINE ***linep, WINHELP_LINE_PART ***partp,
1477                                                   LPSIZE space, LPSIZE gfxSize,
1478                                                   HLPFILE_LINK *link, unsigned pos)
1479 {
1480     WINHELP_LINE      *line;
1481     WINHELP_LINE_PART *part;
1482     LPSTR              ptr;
1483
1484     if (!*partp || pos == 1) /* New line */
1485     {
1486         line = HeapAlloc(GetProcessHeap(), 0, sizeof(WINHELP_LINE));
1487         if (!line) return NULL;
1488
1489         line->next    = NULL;
1490         part          = &line->first_part;
1491
1492         line->rect.top    = (**linep ? (**linep)->rect.bottom : 0) + space->cy;
1493         line->rect.bottom = line->rect.top;
1494         line->rect.left   = space->cx;
1495         line->rect.right  = space->cx;
1496
1497         if (**linep) *linep = &(**linep)->next;
1498         **linep = line;
1499         space->cy = 0;
1500         ptr = (char*)line + sizeof(WINHELP_LINE);
1501     }
1502     else /* Same line */
1503     {
1504         if (pos == 2) WINE_FIXME("Left alignment not handled\n");
1505         line = **linep;
1506
1507         part = HeapAlloc(GetProcessHeap(), 0, sizeof(WINHELP_LINE_PART));
1508         if (!part) return NULL;
1509         **partp = part;
1510         ptr = (char*)part + sizeof(WINHELP_LINE_PART);
1511     }
1512
1513     /* part->cookie should be set by caller (image or metafile) */
1514     part->rect.left       = line->rect.right + (*partp ? space->cx : 0);
1515     part->rect.right      = part->rect.left + gfxSize->cx;
1516     line->rect.right      = part->rect.right;
1517     part->rect.top        = (*partp) ? line->rect.top : line->rect.bottom;
1518     part->rect.bottom     = part->rect.top + gfxSize->cy;
1519     line->rect.bottom     = max(line->rect.bottom, part->rect.bottom);
1520
1521     WINE_TRACE("Appended gfx @ (%d,%d-%d,%d)\n",
1522                part->rect.left, part->rect.top, part->rect.right, part->rect.bottom);
1523
1524     part->link = link;
1525     if (link) link->wRefCount++;
1526
1527     part->next            = NULL;
1528     *partp                = &part->next;
1529
1530     space->cx = 0;
1531
1532     return part;
1533 }
1534
1535
1536 /***********************************************************************
1537  *
1538  *           WINHELP_SplitLines
1539  */
1540 static BOOL WINHELP_SplitLines(HWND hWnd, LPSIZE newsize)
1541 {
1542     WINHELP_WINDOW     *win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1543     HLPFILE_PARAGRAPH  *p;
1544     WINHELP_LINE      **line = &win->first_line;
1545     WINHELP_LINE_PART **part = 0;
1546     INT                 line_ascent = 0;
1547     SIZE                space;
1548     RECT                rect;
1549     HDC                 hDc;
1550
1551     if (newsize) newsize->cx = newsize->cy = 0;
1552
1553     if (!win->page) return TRUE;
1554
1555     WINHELP_DeleteLines(win);
1556
1557     GetClientRect(hWnd, &rect);
1558
1559     rect.top    += INTERNAL_BORDER_WIDTH;
1560     rect.left   += INTERNAL_BORDER_WIDTH;
1561     rect.right  -= INTERNAL_BORDER_WIDTH;
1562     rect.bottom -= INTERNAL_BORDER_WIDTH;
1563
1564     space.cy = rect.top;
1565     space.cx = rect.left;
1566
1567     hDc = GetDC(hWnd);
1568
1569     for (p = win->page->first_paragraph; p; p = p->next)
1570     {
1571         switch (p->cookie)
1572         {
1573         case para_normal_text:
1574         case para_debug_text:
1575             {
1576                 TEXTMETRIC tm;
1577                 SIZE textsize    = {0, 0};
1578                 LPCSTR text      = p->u.text.lpszText;
1579                 UINT indent      = 0;
1580                 UINT len         = strlen(text);
1581                 unsigned underline = 0;
1582
1583                 HFONT hFont = 0;
1584                 COLORREF color = RGB(0, 0, 0);
1585
1586                 if (p->u.text.wFont < win->page->file->numFonts)
1587                 {
1588                     HLPFILE*    hlpfile = win->page->file;
1589
1590                     if (!hlpfile->fonts[p->u.text.wFont].hFont)
1591                         hlpfile->fonts[p->u.text.wFont].hFont = CreateFontIndirect(&hlpfile->fonts[p->u.text.wFont].LogFont);
1592                     hFont = hlpfile->fonts[p->u.text.wFont].hFont;
1593                     color = hlpfile->fonts[p->u.text.wFont].color;
1594                 }
1595                 else
1596                 {
1597                     UINT  wFont = (p->u.text.wFont < win->fonts_len) ? p->u.text.wFont : 0;
1598
1599                     hFont = win->fonts[wFont];
1600                 }
1601
1602                 if (p->link && p->link->bClrChange)
1603                 {
1604                     underline = (p->link->cookie == hlp_link_popup) ? 3 : 1;
1605                     color = RGB(0, 0x80, 0);
1606                 }
1607                 if (p->cookie == para_debug_text) color = RGB(0xff, 0, 0);
1608
1609                 SelectObject(hDc, hFont);
1610
1611                 GetTextMetrics(hDc, &tm);
1612
1613                 if (p->u.text.wIndent)
1614                 {
1615                     indent = p->u.text.wIndent * 5 * tm.tmAveCharWidth;
1616                     if (!part)
1617                         space.cx = rect.left + indent - 2 * tm.tmAveCharWidth;
1618                 }
1619
1620                 if (p->u.text.wVSpace)
1621                 {
1622                     part = 0;
1623                     space.cx = rect.left + indent;
1624                     space.cy += (p->u.text.wVSpace - 1) * tm.tmHeight;
1625                 }
1626
1627                 if (p->u.text.wHSpace)
1628                 {
1629                     space.cx += p->u.text.wHSpace * 2 * tm.tmAveCharWidth;
1630                 }
1631
1632                 WINE_TRACE("splitting text %s\n", text);
1633
1634                 while (len)
1635                 {
1636                     INT free_width = rect.right - (part ? (*line)->rect.right : rect.left) - space.cx;
1637                     UINT low = 0, curr = len, high = len, textlen = 0;
1638
1639                     if (free_width > 0)
1640                     {
1641                         while (1)
1642                         {
1643                             GetTextExtentPoint(hDc, text, curr, &textsize);
1644
1645                             if (textsize.cx <= free_width) low = curr;
1646                             else high = curr;
1647
1648                             if (high <= low + 1) break;
1649
1650                             if (textsize.cx) curr = (curr * free_width) / textsize.cx;
1651                             if (curr <= low) curr = low + 1;
1652                             else if (curr >= high) curr = high - 1;
1653                         }
1654                         textlen = low;
1655                         while (textlen && text[textlen] && text[textlen] != ' ') textlen--;
1656                     }
1657                     if (!part && !textlen) textlen = max(low, 1);
1658
1659                     if (free_width <= 0 || !textlen)
1660                     {
1661                         part = 0;
1662                         space.cx = rect.left + indent;
1663                         space.cx = min(space.cx, rect.right - rect.left - 1);
1664                         continue;
1665                     }
1666
1667                     WINE_TRACE("\t => %d %*s\n", textlen, textlen, text);
1668
1669                     if (!WINHELP_AppendText(&line, &part, &space, &textsize,
1670                                             &line_ascent, tm.tmAscent,
1671                                             text, textlen, hFont, color, p->link, underline) ||
1672                         (!newsize && (*line)->rect.bottom > rect.bottom))
1673                     {
1674                         ReleaseDC(hWnd, hDc);
1675                         return FALSE;
1676                     }
1677
1678                     if (newsize)
1679                         newsize->cx = max(newsize->cx, (*line)->rect.right + INTERNAL_BORDER_WIDTH);
1680
1681                     len -= textlen;
1682                     text += textlen;
1683                     if (text[0] == ' ') text++, len--;
1684                 }
1685             }
1686             break;
1687         case para_bitmap:
1688         case para_metafile:
1689             {
1690                 SIZE                    gfxSize;
1691                 INT                     free_width;
1692                 WINHELP_LINE_PART*      ref_part;
1693
1694                 if (p->u.gfx.pos & 0x8000)
1695                 {
1696                     space.cx = rect.left;
1697                     if (*line)
1698                         space.cy += (*line)->rect.bottom - (*line)->rect.top;
1699                     part = 0;
1700                 }
1701
1702                 if (p->cookie == para_bitmap)
1703                 {
1704                     DIBSECTION              dibs;
1705                     
1706                     GetObject(p->u.gfx.u.bmp.hBitmap, sizeof(dibs), &dibs);
1707                     gfxSize.cx = dibs.dsBm.bmWidth;
1708                     gfxSize.cy = dibs.dsBm.bmHeight;
1709                 }
1710                 else
1711                 {
1712                     LPMETAFILEPICT lpmfp = &p->u.gfx.u.mfp;
1713                     if (lpmfp->mm == MM_ANISOTROPIC || lpmfp->mm == MM_ISOTROPIC)
1714                     {
1715                         gfxSize.cx = MulDiv(lpmfp->xExt, GetDeviceCaps(hDc, HORZRES),
1716                                             100*GetDeviceCaps(hDc, HORZSIZE));
1717                         gfxSize.cy = MulDiv(lpmfp->yExt, GetDeviceCaps(hDc, VERTRES),
1718                                             100*GetDeviceCaps(hDc, VERTSIZE));
1719                     }
1720                     else
1721                     {
1722                         gfxSize.cx = lpmfp->xExt;
1723                         gfxSize.cy = lpmfp->yExt;
1724                     }
1725                 }
1726
1727                 free_width = rect.right - ((part && *line) ? (*line)->rect.right : rect.left) - space.cx;
1728                 if (free_width <= 0)
1729                 {
1730                     part = NULL;
1731                     space.cx = rect.left;
1732                     space.cx = min(space.cx, rect.right - rect.left - 1);
1733                 }
1734                 ref_part = WINHELP_AppendGfxObject(&line, &part, &space, &gfxSize,
1735                                                    p->link, p->u.gfx.pos);
1736                 if (!ref_part || (!newsize && (*line)->rect.bottom > rect.bottom))
1737                 {
1738                     return FALSE;
1739                 }
1740                 if (p->cookie == para_bitmap)
1741                 {
1742                     ref_part->cookie = hlp_line_part_bitmap;
1743                     ref_part->u.bitmap.hBitmap = p->u.gfx.u.bmp.hBitmap;
1744                 }
1745                 else
1746                 {
1747                     ref_part->cookie = hlp_line_part_metafile;
1748                     ref_part->u.metafile.hMetaFile = p->u.gfx.u.mfp.hMF;
1749                     ref_part->u.metafile.mm = p->u.gfx.u.mfp.mm;
1750                 }
1751             }
1752             break;
1753         }
1754     }
1755
1756     if (newsize)
1757         newsize->cy = (*line)->rect.bottom + INTERNAL_BORDER_WIDTH;
1758
1759     ReleaseDC(hWnd, hDc);
1760     return TRUE;
1761 }
1762
1763 /***********************************************************************
1764  *
1765  *           WINHELP_CheckPopup
1766  */
1767 static void WINHELP_CheckPopup(UINT msg)
1768 {
1769     if (!Globals.hPopupWnd) return;
1770
1771     switch (msg)
1772     {
1773     case WM_COMMAND:
1774     case WM_LBUTTONDOWN:
1775     case WM_MBUTTONDOWN:
1776     case WM_RBUTTONDOWN:
1777     case WM_NCLBUTTONDOWN:
1778     case WM_NCMBUTTONDOWN:
1779     case WM_NCRBUTTONDOWN:
1780         DestroyWindow(Globals.hPopupWnd);
1781         Globals.hPopupWnd = 0;
1782     }
1783 }
1784
1785 /***********************************************************************
1786  *
1787  *           WINHELP_DeleteLines
1788  */
1789 static void WINHELP_DeleteLines(WINHELP_WINDOW *win)
1790 {
1791     WINHELP_LINE      *line, *next_line;
1792     WINHELP_LINE_PART *part, *next_part;
1793     for (line = win->first_line; line; line = next_line)
1794     {
1795         next_line = line->next;
1796         for (part = &line->first_part; part; part = next_part)
1797         {
1798             next_part = part->next;
1799             HLPFILE_FreeLink(part->link);
1800             HeapFree(GetProcessHeap(), 0, part);
1801         }
1802     }
1803     win->first_line = 0;
1804 }
1805
1806 /******************************************************************
1807  *              WINHELP_DeleteButtons
1808  *
1809  */
1810 static void WINHELP_DeleteButtons(WINHELP_WINDOW* win)
1811 {
1812     WINHELP_BUTTON*     b;
1813     WINHELP_BUTTON*     bp;
1814
1815     for (b = win->first_button; b; b = bp)
1816     {
1817         DestroyWindow(b->hWnd);
1818         bp = b->next;
1819         HeapFree(GetProcessHeap(), 0, b);
1820     }
1821     win->first_button = NULL;
1822 }
1823
1824 /******************************************************************
1825  *              WINHELP_DeleteBackSet
1826  *
1827  */
1828 void WINHELP_DeleteBackSet(WINHELP_WINDOW* win)
1829 {
1830     unsigned int i;
1831
1832     for (i = 0; i < win->back.index; i++)
1833     {
1834         HLPFILE_FreeHlpFile(win->back.set[i].page->file);
1835         win->back.set[i].page = NULL;
1836     }
1837     win->back.index = 0;
1838 }
1839
1840 /***********************************************************************
1841  *
1842  *           WINHELP_DeleteWindow
1843  */
1844 static void WINHELP_DeleteWindow(WINHELP_WINDOW* win)
1845 {
1846     WINHELP_WINDOW**    w;
1847
1848     for (w = &Globals.win_list; *w; w = &(*w)->next)
1849     {
1850         if (*w == win)
1851         {
1852             *w = win->next;
1853             break;
1854         }
1855     }
1856
1857     if (Globals.active_win == win)
1858     {
1859         Globals.active_win = Globals.win_list;
1860         if (Globals.win_list)
1861             SetActiveWindow(Globals.win_list->hMainWnd);
1862     }
1863
1864     WINHELP_DeleteButtons(win);
1865
1866     if (win->hShadowWnd) DestroyWindow(win->hShadowWnd);
1867     if (win->hHistoryWnd) DestroyWindow(win->hHistoryWnd);
1868
1869     DeleteObject(win->hBrush);
1870
1871     WINHELP_DeleteBackSet(win);
1872
1873     if (win->page) HLPFILE_FreeHlpFile(win->page->file);
1874     WINHELP_DeleteLines(win);
1875     HeapFree(GetProcessHeap(), 0, win);
1876 }
1877
1878 /***********************************************************************
1879  *
1880  *           WINHELP_InitFonts
1881  */
1882 static void WINHELP_InitFonts(HWND hWnd)
1883 {
1884     WINHELP_WINDOW *win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1885     LOGFONT logfontlist[] = {
1886         {-10, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1887         {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1888         {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1889         {-12, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1890         {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1891         {-10, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1892         { -8, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"}};
1893 #define FONTS_LEN (sizeof(logfontlist)/sizeof(*logfontlist))
1894
1895     static HFONT fonts[FONTS_LEN];
1896     static BOOL init = 0;
1897
1898     win->fonts_len = FONTS_LEN;
1899     win->fonts = fonts;
1900
1901     if (!init)
1902     {
1903         UINT i;
1904
1905         for (i = 0; i < FONTS_LEN; i++)
1906         {
1907             fonts[i] = CreateFontIndirect(&logfontlist[i]);
1908         }
1909
1910         init = 1;
1911     }
1912 }
1913
1914 /***********************************************************************
1915  *
1916  *           WINHELP_MessageBoxIDS
1917  */
1918 INT WINHELP_MessageBoxIDS(UINT ids_text, UINT ids_title, WORD type)
1919 {
1920     CHAR text[MAX_STRING_LEN];
1921     CHAR title[MAX_STRING_LEN];
1922
1923     LoadString(Globals.hInstance, ids_text, text, sizeof(text));
1924     LoadString(Globals.hInstance, ids_title, title, sizeof(title));
1925
1926     return MessageBox(0, text, title, type);
1927 }
1928
1929 /***********************************************************************
1930  *
1931  *           MAIN_MessageBoxIDS_s
1932  */
1933 INT WINHELP_MessageBoxIDS_s(UINT ids_text, LPCSTR str, UINT ids_title, WORD type)
1934 {
1935     CHAR text[MAX_STRING_LEN];
1936     CHAR title[MAX_STRING_LEN];
1937     CHAR newtext[MAX_STRING_LEN + MAX_PATH];
1938
1939     LoadString(Globals.hInstance, ids_text, text, sizeof(text));
1940     LoadString(Globals.hInstance, ids_title, title, sizeof(title));
1941     wsprintf(newtext, text, str);
1942
1943     return MessageBox(0, newtext, title, type);
1944 }
1945
1946 /******************************************************************
1947  *              WINHELP_IsOverLink
1948  *
1949  *
1950  */
1951 WINHELP_LINE_PART* WINHELP_IsOverLink(WINHELP_WINDOW* win, WPARAM wParam, LPARAM lParam)
1952 {
1953     POINT mouse;
1954     WINHELP_LINE      *line;
1955     WINHELP_LINE_PART *part;
1956     int scroll_pos = GetScrollPos(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), SB_VERT);
1957
1958     mouse.x = LOWORD(lParam);
1959     mouse.y = HIWORD(lParam);
1960     for (line = win->first_line; line; line = line->next)
1961     {
1962         for (part = &line->first_part; part; part = part->next)
1963         {
1964             if (part->link && 
1965                 part->link->lpszString &&
1966                 part->rect.left   <= mouse.x &&
1967                 part->rect.right  >= mouse.x &&
1968                 part->rect.top    <= mouse.y + scroll_pos &&
1969                 part->rect.bottom >= mouse.y + scroll_pos)
1970             {
1971                 return part;
1972             }
1973         }
1974     }
1975
1976     return NULL;
1977 }
1978
1979 /**************************************************************************
1980  * cb_KWBTree
1981  *
1982  * HLPFILE_BPTreeCallback enumeration function for '|KWBTREE' internal file.
1983  *
1984  */
1985 static void cb_KWBTree(void *p, void **next, void *cookie)
1986 {
1987     HWND hListWnd = (HWND)cookie;
1988     int count;
1989
1990     WINE_TRACE("Adding '%s' to search list\n", (char *)p);
1991     SendMessage(hListWnd, LB_INSERTSTRING, -1, (LPARAM)p);
1992     count = SendMessage(hListWnd, LB_GETCOUNT, 0, 0);
1993     SendMessage(hListWnd, LB_SETITEMDATA, count-1, (LPARAM)p);
1994     *next = (char*)p + strlen((char*)p) + 7;
1995 }
1996
1997 /**************************************************************************
1998  * WINHELP_IndexDlgProc
1999  *
2000  * Index dialog callback function.
2001  *
2002  * nResult passed to EndDialog:
2003  *   1: CANCEL button
2004  *  >1: valid offset value +2.
2005  *  EndDialog itself can return 0 (error).
2006  */
2007 INT_PTR CALLBACK WINHELP_SearchDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
2008 {
2009     static HLPFILE *file;
2010     int sel;
2011     ULONG offset = 1;
2012
2013     switch (msg)
2014     {
2015     case WM_INITDIALOG:
2016         file = (HLPFILE *)lParam;
2017         HLPFILE_BPTreeEnum(file->kwbtree, cb_KWBTree,
2018                            GetDlgItem(hWnd, IDC_INDEXLIST));
2019         return TRUE;
2020     case WM_COMMAND:
2021         switch (LOWORD(wParam))
2022         {
2023         case IDOK:
2024             sel = SendDlgItemMessage(hWnd, IDC_INDEXLIST, LB_GETCURSEL, 0, 0);
2025             if (sel != LB_ERR)
2026             {
2027                 BYTE *p;
2028                 int count;
2029
2030                 p = (BYTE*)SendDlgItemMessage(hWnd, IDC_INDEXLIST,
2031                                               LB_GETITEMDATA, sel, 0);
2032                 count = *(short*)((char *)p + strlen((char *)p) + 1);
2033                 if (count > 1)
2034                 {
2035                     MessageBox(hWnd, "count > 1 not supported yet", "Error", MB_OK | MB_ICONSTOP);
2036                     return TRUE;
2037                 }
2038                 offset = *(ULONG*)((char *)p + strlen((char *)p) + 3);
2039                 offset = *(long*)(file->kwdata + offset + 9);
2040                 if (offset == 0xFFFFFFFF)
2041                 {
2042                     MessageBox(hWnd, "macro keywords not supported yet", "Error", MB_OK | MB_ICONSTOP);
2043                     return TRUE;
2044                 }
2045                 offset += 2;
2046             }
2047             /* Fall through */
2048         case IDCANCEL:
2049             EndDialog(hWnd, offset);
2050             return TRUE;
2051         default:
2052             break;
2053         }
2054     default:
2055         break;
2056     }
2057     return FALSE;
2058 }
2059
2060 /**************************************************************************
2061  * WINHELP_CreateIndexWindow
2062  *
2063  * Displays a dialog with keywords of current help file.
2064  *
2065  */
2066 BOOL WINHELP_CreateIndexWindow(void)
2067 {
2068     int ret;
2069     HLPFILE *hlpfile;
2070
2071     if (Globals.active_win && Globals.active_win->page && Globals.active_win->page->file)
2072         hlpfile = Globals.active_win->page->file;
2073     else
2074         return FALSE;
2075
2076     if (hlpfile->kwbtree == NULL)
2077     {
2078         WINE_TRACE("No index provided\n");
2079         return FALSE;
2080     }
2081
2082     ret = DialogBoxParam(Globals.hInstance, MAKEINTRESOURCE(IDD_INDEX),
2083                          Globals.active_win->hMainWnd, WINHELP_SearchDlgProc,
2084                          (LPARAM)hlpfile);
2085     if (ret > 1)
2086     {
2087         ret -= 2;
2088         WINE_TRACE("got %d as an offset\n", ret);
2089         WINHELP_CreateHelpWindowByOffset(hlpfile, ret, Globals.active_win->info, SW_NORMAL);
2090     }
2091     return TRUE;
2092 }