urlmon: Don't create stgmed_obj for binding to object.
[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 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, 0, TRUE, NULL, NULL, NULL, NULL};
59
60
61 /***********************************************************************
62  *
63  *           WINHELP_GetOpenFileName
64  */
65 BOOL WINHELP_GetOpenFileName(LPSTR lpszFile, int len)
66 {
67     OPENFILENAME openfilename;
68     CHAR szDir[MAX_PATH];
69     CHAR szzFilter[2 * MAX_STRING_LEN + 100];
70     LPSTR p = szzFilter;
71
72     WINE_TRACE("()\n");
73
74     LoadString(Globals.hInstance, STID_HELP_FILES_HLP, p, MAX_STRING_LEN);
75     p += strlen(p) + 1;
76     lstrcpy(p, "*.hlp");
77     p += strlen(p) + 1;
78     LoadString(Globals.hInstance, STID_ALL_FILES, p, MAX_STRING_LEN);
79     p += strlen(p) + 1;
80     lstrcpy(p, "*.*");
81     p += strlen(p) + 1;
82     *p = '\0';
83
84     GetCurrentDirectory(sizeof(szDir), szDir);
85
86     lpszFile[0]='\0';
87
88     openfilename.lStructSize       = sizeof(OPENFILENAME);
89     openfilename.hwndOwner         = NULL;
90     openfilename.hInstance         = Globals.hInstance;
91     openfilename.lpstrFilter       = szzFilter;
92     openfilename.lpstrCustomFilter = 0;
93     openfilename.nMaxCustFilter    = 0;
94     openfilename.nFilterIndex      = 1;
95     openfilename.lpstrFile         = lpszFile;
96     openfilename.nMaxFile          = len;
97     openfilename.lpstrFileTitle    = 0;
98     openfilename.nMaxFileTitle     = 0;
99     openfilename.lpstrInitialDir   = szDir;
100     openfilename.lpstrTitle        = 0;
101     openfilename.Flags             = 0;
102     openfilename.nFileOffset       = 0;
103     openfilename.nFileExtension    = 0;
104     openfilename.lpstrDefExt       = 0;
105     openfilename.lCustData         = 0;
106     openfilename.lpfnHook          = 0;
107     openfilename.lpTemplateName    = 0;
108
109     return GetOpenFileName(&openfilename);
110 }
111
112 /***********************************************************************
113  *
114  *           WINHELP_LookupHelpFile
115  */
116 HLPFILE* WINHELP_LookupHelpFile(LPCSTR lpszFile)
117 {
118     HLPFILE*        hlpfile;
119     char szFullName[MAX_PATH];
120     char szAddPath[MAX_PATH];
121     char *p;
122
123     /*
124      * NOTE: This is needed by popup windows only.
125      * In other cases it's not needed but does not hurt though.
126      */
127     if (Globals.active_win && Globals.active_win->page && Globals.active_win->page->file)
128     {
129         strcpy(szAddPath, Globals.active_win->page->file->lpszPath);
130         p = strrchr(szAddPath, '\\');
131         if (p) *p = 0;
132     }
133
134     /*
135      * FIXME: Should we swap conditions?
136      */
137     if (!SearchPath(NULL, lpszFile, ".hlp", MAX_PATH, szFullName, NULL) &&
138         !SearchPath(szAddPath, lpszFile, ".hlp", MAX_PATH, szFullName, NULL))
139     {
140         if (WINHELP_MessageBoxIDS_s(STID_FILE_NOT_FOUND_s, lpszFile, STID_WHERROR,
141                                     MB_YESNO|MB_ICONQUESTION) != IDYES)
142             return NULL;
143         if (!WINHELP_GetOpenFileName(szFullName, MAX_PATH))
144             return NULL;
145     }
146     hlpfile = HLPFILE_ReadHlpFile(szFullName);
147     if (!hlpfile)
148         WINHELP_MessageBoxIDS_s(STID_HLPFILE_ERROR_s, lpszFile,
149                                 STID_WHERROR, MB_OK|MB_ICONSTOP);
150     return hlpfile;
151 }
152
153 /******************************************************************
154  *              WINHELP_GetWindowInfo
155  *
156  *
157  */
158 HLPFILE_WINDOWINFO*     WINHELP_GetWindowInfo(HLPFILE* hlpfile, LPCSTR name)
159 {
160     static      HLPFILE_WINDOWINFO      mwi;
161     unsigned int     i;
162
163     if (!name || !name[0])
164         name = Globals.active_win->lpszName;
165
166     if (hlpfile)
167         for (i = 0; i < hlpfile->numWindows; i++)
168             if (!strcmp(hlpfile->windows[i].name, name))
169                 return &hlpfile->windows[i];
170
171     if (strcmp(name, "main") != 0)
172     {
173         WINE_FIXME("Couldn't find window info for %s\n", name);
174         assert(0);
175         return NULL;
176     }
177     if (!mwi.name[0])
178     {
179         strcpy(mwi.type, "primary");
180         strcpy(mwi.name, "main");
181         if (!LoadString(Globals.hInstance, STID_WINE_HELP, 
182                         mwi.caption, sizeof(mwi.caption)))
183             strcpy(mwi.caption, hlpfile->lpszTitle);
184         mwi.origin.x = mwi.origin.y = mwi.size.cx = mwi.size.cy = CW_USEDEFAULT;
185         mwi.style = SW_SHOW;
186         mwi.win_style = WS_OVERLAPPEDWINDOW;
187         mwi.sr_color = mwi.sr_color = 0xFFFFFF;
188     }
189     return &mwi;
190 }
191
192 /******************************************************************
193  *              HLPFILE_GetPopupWindowInfo
194  *
195  *
196  */
197 static HLPFILE_WINDOWINFO*     WINHELP_GetPopupWindowInfo(HLPFILE* hlpfile, HWND hParentWnd, POINT* mouse)
198 {
199     static      HLPFILE_WINDOWINFO      wi;
200
201     RECT parent_rect;
202     
203     wi.type[0] = wi.name[0] = wi.caption[0] = '\0';
204
205     /* Calculate horizontal size and position of a popup window */
206     GetWindowRect(hParentWnd, &parent_rect);
207     wi.size.cx = (parent_rect.right  - parent_rect.left) / 2;
208     wi.size.cy = 10; /* need a non null value, so that border are taken into account while computing */
209
210     wi.origin = *mouse;
211     ClientToScreen(hParentWnd, &wi.origin);
212     wi.origin.x -= wi.size.cx / 2;
213     wi.origin.x  = min(wi.origin.x, GetSystemMetrics(SM_CXSCREEN) - wi.size.cx);
214     wi.origin.x  = max(wi.origin.x, 0);
215
216     wi.style = SW_SHOW;
217     wi.win_style = WS_POPUP | WS_BORDER;
218     wi.sr_color = wi.sr_color = 0xFFFFFF;
219
220     return &wi;
221 }
222
223 /***********************************************************************
224  *
225  *           WinMain
226  */
227 int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
228 {
229     MSG                 msg;
230     LONG                lHash = 0;
231     HLPFILE*            hlpfile;
232     static CHAR         default_wndname[] = "main";
233     LPSTR               wndname = default_wndname;
234     WINHELP_DLL*        dll;
235
236     Globals.hInstance = hInstance;
237
238     /* Get options */
239     while (*cmdline && (*cmdline == ' ' || *cmdline == '-'))
240     {
241         CHAR   option;
242         LPCSTR topic_id;
243         if (*cmdline++ == ' ') continue;
244
245         option = *cmdline;
246         if (option) cmdline++;
247         while (*cmdline && *cmdline == ' ') cmdline++;
248         switch (option)
249         {
250         case 'i':
251         case 'I':
252             topic_id = cmdline;
253             while (*cmdline && *cmdline != ' ') cmdline++;
254             if (*cmdline) *cmdline++ = '\0';
255             lHash = HLPFILE_Hash(topic_id);
256             break;
257
258         case '3':
259         case '4':
260             Globals.wVersion = option - '0';
261             break;
262
263         case 'x':
264             show = SW_HIDE; 
265             Globals.isBook = FALSE;
266             break;
267
268         default:
269             WINE_FIXME("Unsupported cmd line: %s\n", cmdline);
270             break;
271         }
272     }
273
274     /* Create primary window */
275     if (!WINHELP_RegisterWinClasses())
276     {
277         WINE_FIXME("Couldn't register classes\n");
278         return 0;
279     }
280
281     if (*cmdline)
282     {
283         char*   ptr;
284         if ((*cmdline == '"') && (ptr = strchr(cmdline+1, '"')))
285         {
286             cmdline++;
287             *ptr = '\0';
288         }
289         if ((ptr = strchr(cmdline, '>')))
290         {
291             *ptr = '\0';
292             wndname = ptr + 1;
293         }
294         hlpfile = WINHELP_LookupHelpFile(cmdline);
295         if (!hlpfile) return 0;
296     }
297     else hlpfile = NULL;
298     WINHELP_CreateHelpWindowByHash(hlpfile, lHash, 
299                                    WINHELP_GetWindowInfo(hlpfile, wndname), show);
300
301     /* Message loop */
302     while (GetMessage(&msg, 0, 0, 0))
303     {
304         TranslateMessage(&msg);
305         DispatchMessage(&msg);
306     }
307     for (dll = Globals.dlls; dll; dll = dll->next)
308     {
309         if (dll->class & DC_INITTERM) dll->handler(DW_TERM, 0, 0);
310     }
311     return 0;
312 }
313
314 /***********************************************************************
315  *
316  *           RegisterWinClasses
317  */
318 static BOOL WINHELP_RegisterWinClasses(void)
319 {
320     WNDCLASS class_main, class_button_box, class_text, class_shadow, class_history;
321
322     class_main.style               = CS_HREDRAW | CS_VREDRAW;
323     class_main.lpfnWndProc         = WINHELP_MainWndProc;
324     class_main.cbClsExtra          = 0;
325     class_main.cbWndExtra          = sizeof(LONG);
326     class_main.hInstance           = Globals.hInstance;
327     class_main.hIcon               = LoadIcon(0, IDI_APPLICATION);
328     class_main.hCursor             = LoadCursor(0, IDC_ARROW);
329     class_main.hbrBackground       = (HBRUSH)(COLOR_WINDOW+1);
330     class_main.lpszMenuName        = 0;
331     class_main.lpszClassName       = MAIN_WIN_CLASS_NAME;
332
333     class_button_box               = class_main;
334     class_button_box.lpfnWndProc   = WINHELP_ButtonBoxWndProc;
335     class_button_box.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
336     class_button_box.lpszClassName = BUTTON_BOX_WIN_CLASS_NAME;
337
338     class_text = class_main;
339     class_text.lpfnWndProc         = WINHELP_TextWndProc;
340     class_text.hbrBackground       = 0;
341     class_text.lpszClassName       = TEXT_WIN_CLASS_NAME;
342
343     class_shadow = class_main;
344     class_shadow.lpfnWndProc       = WINHELP_ShadowWndProc;
345     class_shadow.hbrBackground     = (HBRUSH)(COLOR_3DDKSHADOW+1);
346     class_shadow.lpszClassName     = SHADOW_WIN_CLASS_NAME;
347
348     class_history = class_main;
349     class_history.lpfnWndProc      = WINHELP_HistoryWndProc;
350     class_history.lpszClassName    = HISTORY_WIN_CLASS_NAME;
351
352     return (RegisterClass(&class_main) &&
353             RegisterClass(&class_button_box) &&
354             RegisterClass(&class_text) &&
355             RegisterClass(&class_shadow) &&
356             RegisterClass(&class_history));
357 }
358
359 typedef struct
360 {
361     WORD size;
362     WORD command;
363     LONG data;
364     LONG reserved;
365     WORD ofsFilename;
366     WORD ofsData;
367 } WINHELP,*LPWINHELP;
368
369 /******************************************************************
370  *              WINHELP_HandleCommand
371  *
372  *
373  */
374 static LRESULT  WINHELP_HandleCommand(HWND hSrcWnd, LPARAM lParam)
375 {
376     COPYDATASTRUCT*     cds = (COPYDATASTRUCT*)lParam;
377     WINHELP*            wh;
378
379     if (cds->dwData != 0xA1DE505)
380     {
381         WINE_FIXME("Wrong magic number (%08lx)\n", cds->dwData);
382         return 0;
383     }
384
385     wh = (WINHELP*)cds->lpData;
386
387     if (wh)
388     {
389         char*   ptr = (wh->ofsFilename) ? (LPSTR)wh + wh->ofsFilename : NULL;
390
391         WINE_TRACE("Got[%u]: cmd=%u data=%08x fn=%s\n",
392                    wh->size, wh->command, wh->data, ptr);
393         switch (wh->command)
394         {
395         case HELP_CONTEXT:
396             if (ptr)
397             {
398                 MACRO_JumpContext(ptr, "main", wh->data);
399             }
400             break;
401         case HELP_QUIT:
402             MACRO_Exit();
403             break;
404         case HELP_CONTENTS:
405             if (ptr)
406             {
407                 MACRO_JumpContents(ptr, "main");
408             }
409             break;
410         case HELP_HELPONHELP:
411             MACRO_HelpOn();
412             break;
413         /* case HELP_SETINDEX: */
414         case HELP_SETCONTENTS:
415             if (ptr)
416             {
417                 MACRO_SetContents(ptr, wh->data);
418             }
419             break;
420         case HELP_CONTEXTPOPUP:
421             if (ptr)
422             {
423                 MACRO_PopupContext(ptr, wh->data);
424             }
425             break;
426         /* case HELP_FORCEFILE:*/
427         /* case HELP_CONTEXTMENU: */
428         case HELP_FINDER:
429             /* in fact, should be the topic dialog box */
430             WINE_FIXME("HELP_FINDER: stub\n");
431             if (ptr)
432             {
433                 MACRO_JumpHash(ptr, "main", 0);
434             }
435             break;
436         /* case HELP_WM_HELP: */
437         /* case HELP_SETPOPUP_POS: */
438         /* case HELP_KEY: */
439         /* case HELP_COMMAND: */
440         /* case HELP_PARTIALKEY: */
441         /* case HELP_MULTIKEY: */
442         /* case HELP_SETWINPOS: */
443         default:
444             WINE_FIXME("Unhandled command (%x) for remote winhelp control\n", wh->command);
445             break;
446         }
447     }
448     /* Always return success for now */
449     return 1;
450 }
451
452 /******************************************************************
453  *              WINHELP_ReuseWindow
454  *
455  *
456  */
457 static BOOL     WINHELP_ReuseWindow(WINHELP_WINDOW* win, WINHELP_WINDOW* oldwin, 
458                                     HLPFILE_PAGE* page, int nCmdShow)
459 {
460     unsigned int i;
461
462     win->hMainWnd      = oldwin->hMainWnd;
463     win->hButtonBoxWnd = oldwin->hButtonBoxWnd;
464     win->hTextWnd      = oldwin->hTextWnd;
465     win->hHistoryWnd   = oldwin->hHistoryWnd;
466     oldwin->hMainWnd   = oldwin->hButtonBoxWnd = oldwin->hTextWnd = oldwin->hHistoryWnd = 0;
467     win->hBrush        = CreateSolidBrush(win->info->sr_color);
468
469     SetWindowLong(win->hMainWnd,      0, (LONG)win);
470     SetWindowLong(win->hButtonBoxWnd, 0, (LONG)win);
471     SetWindowLong(win->hTextWnd,      0, (LONG)win);
472     SetWindowLong(win->hHistoryWnd,   0, (LONG)win);
473
474     WINHELP_InitFonts(win->hMainWnd);
475
476     if (page)
477         SetWindowText(win->hMainWnd, page->file->lpszTitle);
478
479     WINHELP_SetupText(win->hTextWnd);
480     InvalidateRect(win->hTextWnd, NULL, TRUE);
481     SendMessage(win->hMainWnd, WM_USER, 0, 0);
482     ShowWindow(win->hMainWnd, nCmdShow);
483     UpdateWindow(win->hTextWnd);
484
485     if (!(win->info->win_style & WS_POPUP))
486     {
487         unsigned        num;
488
489         memcpy(win->history, oldwin->history, sizeof(win->history));
490         win->histIndex = oldwin->histIndex;
491
492         /* FIXME: when using back, we shouldn't update the history... */
493
494         if (page)
495         {
496             for (i = 0; i < win->histIndex; i++)
497                 if (win->history[i] == page) break;
498
499             /* if the new page is already in the history, do nothing */
500             if (i == win->histIndex)
501             {
502                 num = sizeof(win->history) / sizeof(win->history[0]);
503                 if (win->histIndex == num)
504                 {
505                     /* we're full, remove latest entry */
506                     HLPFILE_FreeHlpFile(win->history[0]->file);
507                     memmove(&win->history[0], &win->history[1], 
508                             (num - 1) * sizeof(win->history[0]));
509                     win->histIndex--;
510                 }
511                 win->history[win->histIndex++] = page;
512                 page->file->wRefCount++;
513                 if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE);
514             }
515         }
516
517         memcpy(win->back, oldwin->back, sizeof(win->back));
518         win->backIndex = oldwin->backIndex;
519
520         if (page)
521         {
522             num = sizeof(win->back) / sizeof(win->back[0]);
523             if (win->backIndex == num)
524             {
525                 /* we're full, remove latest entry */
526                 HLPFILE_FreeHlpFile(win->back[0]->file);
527                 memmove(&win->back[0], &win->back[1], 
528                         (num - 1) * sizeof(win->back[0]));
529                 win->backIndex--;
530             }
531             win->back[win->backIndex++] = page;
532             page->file->wRefCount++;
533         }
534     }
535     else
536         win->backIndex = win->histIndex = 0;
537
538     oldwin->histIndex = oldwin->backIndex = 0;
539     WINHELP_DeleteWindow(oldwin);
540     return TRUE;
541 }
542
543 /***********************************************************************
544  *
545  *           WINHELP_CreateHelpWindow
546  */
547 BOOL WINHELP_CreateHelpWindow(HLPFILE_PAGE* page, HLPFILE_WINDOWINFO* wi,
548                               int nCmdShow)
549 {
550     WINHELP_WINDOW *win, *oldwin;
551     HWND hWnd;
552     BOOL bPrimary;
553     BOOL bPopup;
554     LPSTR name;
555     DWORD ex_style;
556
557     bPrimary = !lstrcmpi(wi->name, "main");
558     bPopup = wi->win_style & WS_POPUP;
559
560     /* Initialize WINHELP_WINDOW struct */
561     win = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
562                     sizeof(WINHELP_WINDOW) + strlen(wi->name) + 1);
563     if (!win) return FALSE;
564
565     win->next = Globals.win_list;
566     Globals.win_list = win;
567
568     name = (char*)win + sizeof(WINHELP_WINDOW);
569     lstrcpy(name, wi->name);
570     win->lpszName = name;
571
572     win->page = page;
573
574     win->hArrowCur = LoadCursorA(0, (LPSTR)IDC_ARROW);
575     win->hHandCur = LoadCursorA(0, (LPSTR)IDC_HAND);
576
577     win->info = wi;
578
579     Globals.active_win = win;
580
581     /* Initialize default pushbuttons */
582     if (bPrimary && page)
583     {
584         CHAR    buffer[MAX_STRING_LEN];
585
586         LoadString(Globals.hInstance, STID_CONTENTS, buffer, sizeof(buffer));
587         MACRO_CreateButton("BTN_CONTENTS", buffer, "Contents()");
588         LoadString(Globals.hInstance, STID_SEARCH,buffer, sizeof(buffer));
589         MACRO_CreateButton("BTN_SEARCH", buffer, "Search()");
590         LoadString(Globals.hInstance, STID_BACK, buffer, sizeof(buffer));
591         MACRO_CreateButton("BTN_BACK", buffer, "Back()");
592         LoadString(Globals.hInstance, STID_HISTORY, buffer, sizeof(buffer));
593         MACRO_CreateButton("BTN_HISTORY", buffer, "History()");
594         LoadString(Globals.hInstance, STID_TOPICS, buffer, sizeof(buffer));
595         MACRO_CreateButton("BTN_TOPICS", buffer, "Finder()");
596     }
597
598     /* Initialize file specific pushbuttons */
599     if (!(wi->win_style & WS_POPUP) && page)
600     {
601         HLPFILE_MACRO  *macro;
602         for (macro = page->file->first_macro; macro; macro = macro->next)
603             MACRO_ExecuteMacro(macro->lpszMacro);
604
605         for (macro = page->first_macro; macro; macro = macro->next)
606             MACRO_ExecuteMacro(macro->lpszMacro);
607     }
608
609     /* Reuse existing window */
610     if (!bPopup)
611     {
612         for (oldwin = win->next; oldwin; oldwin = oldwin->next)
613         {
614             if (!lstrcmpi(oldwin->lpszName, wi->name))
615             {
616                 return WINHELP_ReuseWindow(win, oldwin, page, nCmdShow);
617             }
618         }
619         if (page)
620         {
621             win->histIndex = win->backIndex = 1;
622             win->history[0] = win->back[0] = page;
623             page->file->wRefCount += 2;
624             strcpy(wi->caption, page->file->lpszTitle);
625         }
626     }
627
628     ex_style = 0;
629     if (bPopup) ex_style = WS_EX_TOOLWINDOW;
630     hWnd = CreateWindowEx(ex_style, bPopup ? TEXT_WIN_CLASS_NAME : MAIN_WIN_CLASS_NAME,
631                         wi->caption, 
632                         bPrimary ? WS_OVERLAPPEDWINDOW : wi->win_style,
633                         wi->origin.x, wi->origin.y, wi->size.cx, wi->size.cy,
634                         NULL, bPrimary ? LoadMenu(Globals.hInstance, MAKEINTRESOURCE(MAIN_MENU)) : 0,
635                         Globals.hInstance, win);
636
637     ShowWindow(hWnd, nCmdShow);
638     UpdateWindow(hWnd);
639
640     return TRUE;
641 }
642
643 /***********************************************************************
644  *
645  *           WINHELP_CreateHelpWindowByHash
646  */
647 BOOL WINHELP_CreateHelpWindowByHash(HLPFILE* hlpfile, LONG lHash, 
648                                     HLPFILE_WINDOWINFO* wi, int nCmdShow)
649 {
650     HLPFILE_PAGE*       page = NULL;
651
652     if (hlpfile)
653         page = lHash ? HLPFILE_PageByHash(hlpfile, lHash) : 
654             HLPFILE_Contents(hlpfile);
655     if (page) page->file->wRefCount++;
656     return WINHELP_CreateHelpWindow(page, wi, nCmdShow);
657 }
658
659 /***********************************************************************
660  *
661  *           WINHELP_CreateHelpWindowByMap
662  */
663 BOOL WINHELP_CreateHelpWindowByMap(HLPFILE* hlpfile, LONG lMap,
664                                    HLPFILE_WINDOWINFO* wi, int nCmdShow)
665 {
666     HLPFILE_PAGE*       page = NULL;
667
668     page = HLPFILE_PageByMap(hlpfile, lMap);
669     if (page) page->file->wRefCount++;
670     return WINHELP_CreateHelpWindow(page, wi, nCmdShow);
671 }
672
673 /***********************************************************************
674  *
675  *           WINHELP_CreateHelpWindowByOffset
676  */
677 BOOL WINHELP_CreateHelpWindowByOffset(HLPFILE* hlpfile, LONG lOffset,
678                                       HLPFILE_WINDOWINFO* wi, int nCmdShow)
679 {
680     HLPFILE_PAGE*       page = NULL;
681
682     page = HLPFILE_PageByOffset(hlpfile, lOffset);
683     if (page) page->file->wRefCount++;
684     return WINHELP_CreateHelpWindow(page, wi, nCmdShow);
685 }
686
687 /***********************************************************************
688  *
689  *           WINHELP_MainWndProc
690  */
691 static LRESULT CALLBACK WINHELP_MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
692 {
693     WINHELP_WINDOW *win;
694     WINHELP_BUTTON *button;
695     RECT rect, button_box_rect;
696     INT  text_top, curPos, min, max, dy, keyDelta;
697
698     WINHELP_CheckPopup(msg);
699
700     switch (msg)
701     {
702     case WM_NCCREATE:
703         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
704         SetWindowLong(hWnd, 0, (LONG) win);
705         win->hMainWnd = hWnd;
706         break;
707
708     case WM_CREATE:
709         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
710
711         /* Create button box and text Window */
712         CreateWindow(BUTTON_BOX_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
713                      0, 0, 0, 0, hWnd, 0, Globals.hInstance, win);
714
715         CreateWindow(TEXT_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
716                      0, 0, 0, 0, hWnd, 0, Globals.hInstance, win);
717
718         /* Fall through */
719     case WM_USER:
720     case WM_WINDOWPOSCHANGED:
721         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
722         GetClientRect(hWnd, &rect);
723
724         /* Update button box and text Window */
725         SetWindowPos(win->hButtonBoxWnd, HWND_TOP,
726                      rect.left, rect.top,
727                      rect.right - rect.left,
728                      rect.bottom - rect.top, 0);
729
730         GetWindowRect(win->hButtonBoxWnd, &button_box_rect);
731         text_top = rect.top + button_box_rect.bottom - button_box_rect.top;
732
733         SetWindowPos(win->hTextWnd, HWND_TOP,
734                      rect.left, text_top,
735                      rect.right - rect.left,
736                      rect.bottom - text_top, 0);
737
738         break;
739
740     case WM_COMMAND:
741         Globals.active_win = win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
742         switch (wParam)
743         {
744             /* Menu FILE */
745         case MNID_FILE_OPEN:    MACRO_FileOpen();       break;
746         case MNID_FILE_PRINT:   MACRO_Print();          break;
747         case MNID_FILE_SETUP:   MACRO_PrinterSetup();   break;
748         case MNID_FILE_EXIT:    MACRO_Exit();           break;
749
750             /* Menu EDIT */
751         case MNID_EDIT_COPYDLG: MACRO_CopyDialog();     break;
752         case MNID_EDIT_ANNOTATE:MACRO_Annotate();       break;
753
754             /* Menu Bookmark */
755         case MNID_BKMK_DEFINE:  MACRO_BookmarkDefine(); break;
756
757             /* Menu Help */
758         case MNID_HELP_HELPON:  MACRO_HelpOn();         break;
759         case MNID_HELP_HELPTOP: MACRO_HelpOnTop();      break;
760         case MNID_HELP_ABOUT:   MACRO_About();          break;
761         case MNID_HELP_WINE:    ShellAbout(hWnd, "WINE", "Help", 0); break;
762
763         default:
764             /* Buttons */
765             for (button = win->first_button; button; button = button->next)
766                 if (wParam == button->wParam) break;
767             if (button)
768                 MACRO_ExecuteMacro(button->lpszMacro);
769             else
770                 WINHELP_MessageBoxIDS(STID_NOT_IMPLEMENTED, 0x121, MB_OK);
771             break;
772         }
773         break;
774     case WM_DESTROY:
775         if (Globals.hPopupWnd) DestroyWindow(Globals.hPopupWnd);
776         break;
777     case WM_COPYDATA:
778         return WINHELP_HandleCommand((HWND)wParam, lParam);
779
780     case WM_KEYDOWN:
781         keyDelta = 0;
782
783         switch (wParam)
784         {
785         case VK_UP:
786         case VK_DOWN:
787             keyDelta = GetSystemMetrics(SM_CXVSCROLL);
788             if (wParam == VK_UP)
789                 keyDelta = -keyDelta;
790
791         case VK_PRIOR:
792         case VK_NEXT:
793             win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
794             curPos = GetScrollPos(win->hTextWnd, SB_VERT);
795             GetScrollRange(win->hTextWnd, SB_VERT, &min, &max);
796
797             if (keyDelta == 0)
798             {            
799                 GetClientRect(win->hTextWnd, &rect);
800                 keyDelta = (rect.bottom - rect.top) / 2;
801                 if (wParam == VK_PRIOR)
802                     keyDelta = -keyDelta;
803             }
804
805             curPos += keyDelta;
806             if (curPos > max)
807                  curPos = max;
808             else if (curPos < min)
809                  curPos = min;
810
811             dy = GetScrollPos(win->hTextWnd, SB_VERT) - curPos;
812             SetScrollPos(win->hTextWnd, SB_VERT, curPos, TRUE);
813             ScrollWindow(win->hTextWnd, 0, dy, NULL, NULL);
814             UpdateWindow(win->hTextWnd);
815             return 0;
816
817         case VK_ESCAPE:
818             MACRO_Exit();
819             return 0;
820         }
821         break;
822     }
823     return DefWindowProc(hWnd, msg, wParam, lParam);
824 }
825
826 /***********************************************************************
827  *
828  *           WINHELP_ButtonBoxWndProc
829  */
830 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
831 {
832     WINDOWPOS      *winpos;
833     WINHELP_WINDOW *win;
834     WINHELP_BUTTON *button;
835     SIZE button_size;
836     INT  x, y;
837
838     WINHELP_CheckPopup(msg);
839
840     switch (msg)
841     {
842     case WM_NCCREATE:
843         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
844         SetWindowLong(hWnd, 0, (LONG) win);
845         win->hButtonBoxWnd = hWnd;
846         break;
847
848     case WM_WINDOWPOSCHANGING:
849         winpos = (WINDOWPOS*) lParam;
850         win = (WINHELP_WINDOW*) GetWindowLong(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     BOOL  bExit;
953
954     if (msg != WM_LBUTTONDOWN)
955         WINHELP_CheckPopup(msg);
956
957     switch (msg)
958     {
959     case WM_NCCREATE:
960         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
961         SetWindowLong(hWnd, 0, (LONG) win);
962         win->hTextWnd = hWnd;
963         win->hBrush = CreateSolidBrush(win->info->sr_color);
964         if (win->info->win_style & WS_POPUP) Globals.hPopupWnd = win->hMainWnd = hWnd;
965         WINHELP_InitFonts(hWnd);
966         break;
967
968     case WM_CREATE:
969         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
970
971         /* Calculate vertical size and position of a popup window */
972         if (win->info->win_style & WS_POPUP)
973         {
974             POINT origin;
975             RECT old_window_rect;
976             RECT old_client_rect;
977             SIZE old_window_size;
978             SIZE old_client_size;
979             SIZE new_client_size;
980             SIZE new_window_size;
981
982             GetWindowRect(hWnd, &old_window_rect);
983             origin.x = old_window_rect.left;
984             origin.y = old_window_rect.top;
985             old_window_size.cx = old_window_rect.right  - old_window_rect.left;
986             old_window_size.cy = old_window_rect.bottom - old_window_rect.top;
987
988             GetClientRect(hWnd, &old_client_rect);
989             old_client_size.cx = old_client_rect.right  - old_client_rect.left;
990             old_client_size.cy = old_client_rect.bottom - old_client_rect.top;
991
992             new_client_size = old_client_size;
993             WINHELP_SplitLines(hWnd, &new_client_size);
994
995             if (origin.y + POPUP_YDISTANCE + new_client_size.cy <= GetSystemMetrics(SM_CYSCREEN))
996                 origin.y += POPUP_YDISTANCE;
997             else
998                 origin.y -= POPUP_YDISTANCE + new_client_size.cy;
999
1000             new_window_size.cx = old_window_size.cx - old_client_size.cx + new_client_size.cx;
1001             new_window_size.cy = old_window_size.cy - old_client_size.cy + new_client_size.cy;
1002
1003             win->hShadowWnd =
1004                 CreateWindowEx(WS_EX_TOOLWINDOW, SHADOW_WIN_CLASS_NAME, "", WS_POPUP,
1005                              origin.x + SHADOW_DX, origin.y + SHADOW_DY,
1006                              new_window_size.cx, new_window_size.cy,
1007                              0, 0, Globals.hInstance, 0);
1008
1009             SetWindowPos(hWnd, HWND_TOP, origin.x, origin.y,
1010                          new_window_size.cx, new_window_size.cy,
1011                          0);
1012             SetWindowPos(win->hShadowWnd, hWnd, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
1013             ShowWindow(win->hShadowWnd, SW_NORMAL);
1014             SetActiveWindow(hWnd);
1015         }
1016         break;
1017
1018     case WM_WINDOWPOSCHANGED:
1019         winpos = (WINDOWPOS*) lParam;
1020
1021         if (!(winpos->flags & SWP_NOSIZE)) WINHELP_SetupText(hWnd);
1022         break;
1023
1024     case WM_MOUSEWHEEL:
1025     {
1026        int wheelDelta = 0;
1027        UINT scrollLines = 3;
1028        int curPos = GetScrollPos(hWnd, SB_VERT);
1029        int min, max;
1030
1031        GetScrollRange(hWnd, SB_VERT, &min, &max);
1032
1033        SystemParametersInfo(SPI_GETWHEELSCROLLLINES,0, &scrollLines, 0);
1034        if (wParam & (MK_SHIFT | MK_CONTROL))
1035            return DefWindowProc(hWnd, msg, wParam, lParam);
1036        wheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
1037        if (abs(wheelDelta) >= WHEEL_DELTA && scrollLines) {
1038            int dy;
1039
1040            curPos += wheelDelta;
1041            if (curPos > max)
1042                 curPos = max;
1043            else if (curPos < min)
1044                 curPos = min;
1045
1046            dy = GetScrollPos(hWnd, SB_VERT) - curPos;
1047            SetScrollPos(hWnd, SB_VERT, curPos, TRUE);
1048            ScrollWindow(hWnd, 0, dy, NULL, NULL);
1049            UpdateWindow(hWnd);
1050        }
1051     }
1052     break;
1053
1054     case WM_VSCROLL:
1055     {
1056         BOOL  update = TRUE;
1057         RECT  rect;
1058         INT   Min, Max;
1059         INT   CurPos = GetScrollPos(hWnd, SB_VERT);
1060         INT   dy;
1061         
1062         GetScrollRange(hWnd, SB_VERT, &Min, &Max);
1063         GetClientRect(hWnd, &rect);
1064
1065         switch (wParam & 0xffff)
1066         {
1067         case SB_THUMBTRACK:
1068         case SB_THUMBPOSITION: CurPos  = wParam >> 16;                   break;
1069         case SB_TOP:           CurPos  = Min;                            break;
1070         case SB_BOTTOM:        CurPos  = Max;                            break;
1071         case SB_PAGEUP:        CurPos -= (rect.bottom - rect.top) / 2;   break;
1072         case SB_PAGEDOWN:      CurPos += (rect.bottom - rect.top) / 2;   break;
1073         case SB_LINEUP:        CurPos -= GetSystemMetrics(SM_CXVSCROLL); break;
1074         case SB_LINEDOWN:      CurPos += GetSystemMetrics(SM_CXVSCROLL); break;
1075         default: update = FALSE;
1076         }
1077         if (update)
1078         {
1079             if (CurPos > Max)
1080                 CurPos = Max;
1081             else if (CurPos < Min)
1082                 CurPos = Min;
1083             dy = GetScrollPos(hWnd, SB_VERT) - CurPos;
1084             SetScrollPos(hWnd, SB_VERT, CurPos, TRUE);
1085             ScrollWindow(hWnd, 0, dy, NULL, NULL);
1086             UpdateWindow(hWnd);
1087         }
1088     }
1089     break;
1090
1091     case WM_PAINT:
1092         hDc = BeginPaint(hWnd, &ps);
1093         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1094         scroll_pos = GetScrollPos(hWnd, SB_VERT);
1095
1096         /* No DPtoLP needed - MM_TEXT map mode */
1097         if (ps.fErase) FillRect(hDc, &ps.rcPaint, win->hBrush);
1098         for (line = win->first_line; line; line = line->next)
1099         {
1100             for (part = &line->first_part; part; part = part->next)
1101             {
1102                 switch (part->cookie)
1103                 {
1104                 case hlp_line_part_text:
1105                     SelectObject(hDc, part->u.text.hFont);
1106                     SetTextColor(hDc, part->u.text.color);
1107                     SetBkColor(hDc, win->info->sr_color);
1108                     TextOut(hDc, part->rect.left, part->rect.top - scroll_pos,
1109                             part->u.text.lpsText, part->u.text.wTextLen);
1110                     if (part->u.text.wUnderline)
1111                     {
1112                         HPEN    hPen;
1113
1114                         switch (part->u.text.wUnderline)
1115                         {
1116                         case 1: /* simple */
1117                         case 2: /* double */
1118                             hPen = CreatePen(PS_SOLID, 1, part->u.text.color);
1119                             break;
1120                         case 3: /* dotted */
1121                             hPen = CreatePen(PS_DOT, 1, part->u.text.color);
1122                             break;
1123                         default:
1124                             WINE_FIXME("Unknow underline type\n");
1125                             continue;
1126                         }
1127
1128                         SelectObject(hDc, hPen);
1129                         MoveToEx(hDc, part->rect.left, part->rect.bottom - scroll_pos - 1, NULL);
1130                         LineTo(hDc, part->rect.right, part->rect.bottom - scroll_pos - 1);
1131                         if (part->u.text.wUnderline == 2)
1132                         {
1133                             MoveToEx(hDc, part->rect.left, part->rect.bottom - scroll_pos + 1, NULL);
1134                             LineTo(hDc, part->rect.right, part->rect.bottom - scroll_pos + 1);
1135                         }
1136                         DeleteObject(hPen);
1137                     }
1138                     break;
1139                 case hlp_line_part_bitmap:
1140                     {
1141                         HDC hMemDC;
1142
1143                         hMemDC = CreateCompatibleDC(hDc);
1144                         SelectObject(hMemDC, part->u.bitmap.hBitmap);
1145                         BitBlt(hDc, part->rect.left, part->rect.top - scroll_pos,
1146                                part->rect.right - part->rect.left, part->rect.bottom - part->rect.top,
1147                                hMemDC, 0, 0, SRCCOPY);
1148                         DeleteDC(hMemDC);
1149                     }
1150                     break;
1151                 case hlp_line_part_metafile:
1152                     {
1153                         HDC hMemDC;
1154                         HBITMAP hBitmap;
1155                         SIZE sz;
1156                         RECT rc;
1157
1158                         sz.cx = part->rect.right - part->rect.left;
1159                         sz.cy = part->rect.bottom - part->rect.top;
1160                         hMemDC = CreateCompatibleDC(hDc);
1161                         hBitmap = CreateCompatibleBitmap(hDc, sz.cx, sz.cy);
1162                         SelectObject(hMemDC, hBitmap);
1163                         SelectObject(hMemDC, win->hBrush);
1164                         rc.left = 0;
1165                         rc.top = 0;
1166                         rc.right = sz.cx;
1167                         rc.bottom = sz.cy;
1168                         FillRect(hMemDC, &rc, win->hBrush);
1169                         SetMapMode(hMemDC, part->u.metafile.mm);
1170                         SetWindowExtEx(hMemDC, sz.cx, sz.cy, 0);
1171                         SetViewportExtEx(hMemDC, sz.cx, sz.cy, 0);
1172                         SetWindowOrgEx(hMemDC, 0, 0, 0);
1173                         SetViewportOrgEx(hMemDC, 0, 0, 0);
1174                         PlayMetaFile(hMemDC, part->u.metafile.hMetaFile);
1175                         SetMapMode(hMemDC, MM_TEXT);
1176                         SetWindowOrgEx(hMemDC, 0, 0, 0);
1177                         SetViewportOrgEx(hMemDC, 0, 0, 0);
1178                         BitBlt(hDc, part->rect.left, part->rect.top - scroll_pos,
1179                                sz.cx, sz.cy, hMemDC, 0, 0, SRCCOPY);
1180                         DeleteDC(hMemDC);
1181                         DeleteObject(hBitmap);
1182                     }
1183                     break;
1184                 }
1185             }
1186         }
1187
1188         EndPaint(hWnd, &ps);
1189         break;
1190
1191     case WM_MOUSEMOVE:
1192         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1193
1194         if (WINHELP_IsOverLink(win, wParam, lParam))
1195             SetCursor(win->hHandCur); /* set to hand pointer cursor to indicate a link */
1196         else
1197             SetCursor(win->hArrowCur); /* set to hand pointer cursor to indicate a link */
1198
1199         break;
1200
1201     case WM_LBUTTONDOWN:
1202         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1203
1204         hPopupWnd = Globals.hPopupWnd;
1205         Globals.hPopupWnd = 0;
1206
1207         part = WINHELP_IsOverLink(win, wParam, lParam);
1208         if (part)
1209         {
1210             HLPFILE*            hlpfile;
1211             HLPFILE_WINDOWINFO* wi;
1212
1213             mouse.x = (short)LOWORD(lParam);
1214             mouse.y = (short)HIWORD(lParam);
1215
1216             if (part->link) switch (part->link->cookie)
1217             {
1218             case hlp_link_link:
1219                 hlpfile = WINHELP_LookupHelpFile(part->link->lpszString);
1220                 if (part->link->window == -1)
1221                     wi = win->info;
1222                 else if (part->link->window < hlpfile->numWindows)
1223                     wi = &hlpfile->windows[part->link->window];
1224                 else
1225                 {
1226                     WINE_WARN("link to window %d/%d\n", part->link->window, hlpfile->numWindows);
1227                     break;
1228                 }
1229                 WINHELP_CreateHelpWindowByHash(hlpfile, part->link->lHash, wi,
1230                                                SW_NORMAL);
1231                 break;
1232             case hlp_link_popup:
1233                 hlpfile = WINHELP_LookupHelpFile(part->link->lpszString);
1234                 if (hlpfile) WINHELP_CreateHelpWindowByHash(hlpfile, part->link->lHash, 
1235                                                WINHELP_GetPopupWindowInfo(hlpfile, hWnd, &mouse),
1236                                                SW_NORMAL);
1237                 break;
1238             case hlp_link_macro:
1239                 MACRO_ExecuteMacro(part->link->lpszString);
1240                 break;
1241             default:
1242                 WINE_FIXME("Unknown link cookie %d\n", part->link->cookie);
1243             }
1244         }
1245
1246         if (hPopupWnd)
1247             DestroyWindow(hPopupWnd);
1248         break;
1249
1250     case WM_NCDESTROY:
1251         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1252
1253         if (hWnd == Globals.hPopupWnd) Globals.hPopupWnd = 0;
1254
1255         bExit = (Globals.wVersion >= 4 && !lstrcmpi(win->lpszName, "main"));
1256
1257         WINHELP_DeleteWindow(win);
1258
1259         if (bExit) MACRO_Exit();
1260
1261         if (!Globals.win_list)
1262             PostQuitMessage(0);
1263         break;
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         SetWindowLong(hWnd, 0, (LONG)win);
1288         win->hHistoryWnd = hWnd;
1289         break;
1290     case WM_CREATE:
1291         win = (WINHELP_WINDOW*) GetWindowLong(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*) GetWindowLong(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*) GetWindowLong(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*) GetWindowLong(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*) GetWindowLong(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*) GetWindowLong(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 }