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