winhelp: Fix popup window style.
[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       = GetStockObject(WHITE_BRUSH);
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 = GetStockObject(GRAY_BRUSH);
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     = GetStockObject(GRAY_BRUSH);
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             WINE_FIXME("Unknown command (%x) for remote winhelp control\n", wh->command);
444             break;
445         }
446     }
447     return 0L;
448 }
449
450 /******************************************************************
451  *              WINHELP_ReuseWindow
452  *
453  *
454  */
455 static BOOL     WINHELP_ReuseWindow(WINHELP_WINDOW* win, WINHELP_WINDOW* oldwin, 
456                                     HLPFILE_PAGE* page, int nCmdShow)
457 {
458     unsigned int i;
459
460     win->hMainWnd      = oldwin->hMainWnd;
461     win->hButtonBoxWnd = oldwin->hButtonBoxWnd;
462     win->hTextWnd      = oldwin->hTextWnd;
463     win->hHistoryWnd   = oldwin->hHistoryWnd;
464     oldwin->hMainWnd = oldwin->hButtonBoxWnd = oldwin->hTextWnd = oldwin->hHistoryWnd = 0;
465     win->hBrush = oldwin->hBrush;
466
467     SetWindowLong(win->hMainWnd,      0, (LONG)win);
468     SetWindowLong(win->hButtonBoxWnd, 0, (LONG)win);
469     SetWindowLong(win->hTextWnd,      0, (LONG)win);
470     SetWindowLong(win->hHistoryWnd,   0, (LONG)win);
471
472     WINHELP_InitFonts(win->hMainWnd);
473
474     if (page)
475         SetWindowText(win->hMainWnd, page->file->lpszTitle);
476
477     WINHELP_SetupText(win->hTextWnd);
478     InvalidateRect(win->hTextWnd, NULL, TRUE);
479     SendMessage(win->hMainWnd, WM_USER, 0, 0);
480     ShowWindow(win->hMainWnd, nCmdShow);
481     UpdateWindow(win->hTextWnd);
482
483     if (!(win->info->win_style & WS_POPUP))
484     {
485         unsigned        num;
486
487         memcpy(win->history, oldwin->history, sizeof(win->history));
488         win->histIndex = oldwin->histIndex;
489
490         /* FIXME: when using back, we shouldn't update the history... */
491
492         if (page)
493         {
494             for (i = 0; i < win->histIndex; i++)
495                 if (win->history[i] == page) break;
496
497             /* if the new page is already in the history, do nothing */
498             if (i == win->histIndex)
499             {
500                 num = sizeof(win->history) / sizeof(win->history[0]);
501                 if (win->histIndex == num)
502                 {
503                     /* we're full, remove latest entry */
504                     HLPFILE_FreeHlpFile(win->history[0]->file);
505                     memmove(&win->history[0], &win->history[1], 
506                             (num - 1) * sizeof(win->history[0]));
507                     win->histIndex--;
508                 }
509                 win->history[win->histIndex++] = page;
510                 page->file->wRefCount++;
511                 if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE);
512             }
513         }
514
515         memcpy(win->back, oldwin->back, sizeof(win->back));
516         win->backIndex = oldwin->backIndex;
517
518         if (page)
519         {
520             num = sizeof(win->back) / sizeof(win->back[0]);
521             if (win->backIndex == num)
522             {
523                 /* we're full, remove latest entry */
524                 HLPFILE_FreeHlpFile(win->back[0]->file);
525                 memmove(&win->back[0], &win->back[1], 
526                         (num - 1) * sizeof(win->back[0]));
527                 win->backIndex--;
528             }
529             win->back[win->backIndex++] = page;
530             page->file->wRefCount++;
531         }
532     }
533     else
534         win->backIndex = win->histIndex = 0;
535
536     oldwin->histIndex = oldwin->backIndex = 0;
537     WINHELP_DeleteWindow(oldwin);
538     return TRUE;
539 }
540
541 /***********************************************************************
542  *
543  *           WINHELP_CreateHelpWindow
544  */
545 BOOL WINHELP_CreateHelpWindow(HLPFILE_PAGE* page, HLPFILE_WINDOWINFO* wi,
546                               int nCmdShow)
547 {
548     WINHELP_WINDOW *win, *oldwin;
549     HWND hWnd;
550     BOOL bPrimary;
551     BOOL bPopup;
552     LPSTR name;
553
554     bPrimary = !lstrcmpi(wi->name, "main");
555     bPopup = wi->win_style & WS_POPUP;
556
557     /* Initialize WINHELP_WINDOW struct */
558     win = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
559                     sizeof(WINHELP_WINDOW) + strlen(wi->name) + 1);
560     if (!win) return FALSE;
561
562     win->next = Globals.win_list;
563     Globals.win_list = win;
564
565     name = (char*)win + sizeof(WINHELP_WINDOW);
566     lstrcpy(name, wi->name);
567     win->lpszName = name;
568
569     win->page = page;
570
571     win->hArrowCur = LoadCursorA(0, (LPSTR)IDC_ARROW);
572     win->hHandCur = LoadCursorA(0, (LPSTR)IDC_HAND);
573
574     win->info = wi;
575
576     Globals.active_win = win;
577
578     /* Initialize default pushbuttons */
579     if (bPrimary && page)
580     {
581         CHAR    buffer[MAX_STRING_LEN];
582
583         LoadString(Globals.hInstance, STID_CONTENTS, buffer, sizeof(buffer));
584         MACRO_CreateButton("BTN_CONTENTS", buffer, "Contents()");
585         LoadString(Globals.hInstance, STID_SEARCH,buffer, sizeof(buffer));
586         MACRO_CreateButton("BTN_SEARCH", buffer, "Search()");
587         LoadString(Globals.hInstance, STID_BACK, buffer, sizeof(buffer));
588         MACRO_CreateButton("BTN_BACK", buffer, "Back()");
589         LoadString(Globals.hInstance, STID_HISTORY, buffer, sizeof(buffer));
590         MACRO_CreateButton("BTN_HISTORY", buffer, "History()");
591         LoadString(Globals.hInstance, STID_TOPICS, buffer, sizeof(buffer));
592         MACRO_CreateButton("BTN_TOPICS", buffer, "Finder()");
593     }
594
595     /* Initialize file specific pushbuttons */
596     if (!(wi->win_style & WS_POPUP) && page)
597     {
598         HLPFILE_MACRO  *macro;
599         for (macro = page->file->first_macro; macro; macro = macro->next)
600             MACRO_ExecuteMacro(macro->lpszMacro);
601
602         for (macro = page->first_macro; macro; macro = macro->next)
603             MACRO_ExecuteMacro(macro->lpszMacro);
604     }
605
606     /* Reuse existing window */
607     if (!bPopup)
608     {
609         for (oldwin = win->next; oldwin; oldwin = oldwin->next)
610         {
611             if (!lstrcmpi(oldwin->lpszName, wi->name))
612             {
613                 return WINHELP_ReuseWindow(win, oldwin, page, nCmdShow);
614             }
615         }
616         if (page)
617         {
618             win->histIndex = win->backIndex = 1;
619             win->history[0] = win->back[0] = page;
620             page->file->wRefCount += 2;
621             strcpy(wi->caption, page->file->lpszTitle);
622         }
623     }
624
625     hWnd = CreateWindow(bPopup ? TEXT_WIN_CLASS_NAME : MAIN_WIN_CLASS_NAME,
626                         wi->caption, 
627                         bPrimary ? WS_OVERLAPPEDWINDOW : wi->win_style,
628                         wi->origin.x, wi->origin.y, wi->size.cx, wi->size.cy,
629                         NULL, bPrimary ? LoadMenu(Globals.hInstance, MAKEINTRESOURCE(MAIN_MENU)) : 0,
630                         Globals.hInstance, win);
631
632     ShowWindow(hWnd, nCmdShow);
633     UpdateWindow(hWnd);
634
635     return TRUE;
636 }
637
638 /***********************************************************************
639  *
640  *           WINHELP_CreateHelpWindowByHash
641  */
642 BOOL WINHELP_CreateHelpWindowByHash(HLPFILE* hlpfile, LONG lHash, 
643                                     HLPFILE_WINDOWINFO* wi, int nCmdShow)
644 {
645     HLPFILE_PAGE*       page = NULL;
646
647     if (hlpfile)
648         page = lHash ? HLPFILE_PageByHash(hlpfile, lHash) : 
649             HLPFILE_Contents(hlpfile);
650     if (page) page->file->wRefCount++;
651     return WINHELP_CreateHelpWindow(page, wi, nCmdShow);
652 }
653
654 /***********************************************************************
655  *
656  *           WINHELP_CreateHelpWindowByMap
657  */
658 BOOL WINHELP_CreateHelpWindowByMap(HLPFILE* hlpfile, LONG lMap,
659                                    HLPFILE_WINDOWINFO* wi, int nCmdShow)
660 {
661     HLPFILE_PAGE*       page = NULL;
662
663     page = HLPFILE_PageByMap(hlpfile, lMap);
664     if (page) page->file->wRefCount++;
665     return WINHELP_CreateHelpWindow(page, wi, nCmdShow);
666 }
667
668 /***********************************************************************
669  *
670  *           WINHELP_MainWndProc
671  */
672 static LRESULT CALLBACK WINHELP_MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
673 {
674     WINHELP_WINDOW *win;
675     WINHELP_BUTTON *button;
676     RECT rect, button_box_rect;
677     INT  text_top, curPos, min, max, dy, keyDelta;
678
679     WINHELP_CheckPopup(msg);
680
681     switch (msg)
682     {
683     case WM_NCCREATE:
684         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
685         SetWindowLong(hWnd, 0, (LONG) win);
686         win->hMainWnd = hWnd;
687         break;
688
689     case WM_CREATE:
690         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
691
692         /* Create button box and text Window */
693         CreateWindow(BUTTON_BOX_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
694                      0, 0, 0, 0, hWnd, 0, Globals.hInstance, win);
695
696         CreateWindow(TEXT_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
697                      0, 0, 0, 0, hWnd, 0, Globals.hInstance, win);
698
699         /* Fall through */
700     case WM_USER:
701     case WM_WINDOWPOSCHANGED:
702         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
703         GetClientRect(hWnd, &rect);
704
705         /* Update button box and text Window */
706         SetWindowPos(win->hButtonBoxWnd, HWND_TOP,
707                      rect.left, rect.top,
708                      rect.right - rect.left,
709                      rect.bottom - rect.top, 0);
710
711         GetWindowRect(win->hButtonBoxWnd, &button_box_rect);
712         text_top = rect.top + button_box_rect.bottom - button_box_rect.top;
713
714         SetWindowPos(win->hTextWnd, HWND_TOP,
715                      rect.left, text_top,
716                      rect.right - rect.left,
717                      rect.bottom - text_top, 0);
718
719         break;
720
721     case WM_COMMAND:
722         Globals.active_win = win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
723         switch (wParam)
724         {
725             /* Menu FILE */
726         case MNID_FILE_OPEN:    MACRO_FileOpen();       break;
727         case MNID_FILE_PRINT:   MACRO_Print();          break;
728         case MNID_FILE_SETUP:   MACRO_PrinterSetup();   break;
729         case MNID_FILE_EXIT:    MACRO_Exit();           break;
730
731             /* Menu EDIT */
732         case MNID_EDIT_COPYDLG: MACRO_CopyDialog();     break;
733         case MNID_EDIT_ANNOTATE:MACRO_Annotate();       break;
734
735             /* Menu Bookmark */
736         case MNID_BKMK_DEFINE:  MACRO_BookmarkDefine(); break;
737
738             /* Menu Help */
739         case MNID_HELP_HELPON:  MACRO_HelpOn();         break;
740         case MNID_HELP_HELPTOP: MACRO_HelpOnTop();      break;
741         case MNID_HELP_ABOUT:   MACRO_About();          break;
742         case MNID_HELP_WINE:    ShellAbout(hWnd, "WINE", "Help", 0); break;
743
744         default:
745             /* Buttons */
746             for (button = win->first_button; button; button = button->next)
747                 if (wParam == button->wParam) break;
748             if (button)
749                 MACRO_ExecuteMacro(button->lpszMacro);
750             else
751                 WINHELP_MessageBoxIDS(STID_NOT_IMPLEMENTED, 0x121, MB_OK);
752             break;
753         }
754         break;
755     case WM_DESTROY:
756         if (Globals.hPopupWnd) DestroyWindow(Globals.hPopupWnd);
757         break;
758     case WM_COPYDATA:
759         return WINHELP_HandleCommand((HWND)wParam, lParam);
760
761     case WM_KEYDOWN:
762         keyDelta = 0;
763
764         switch (wParam)
765         {
766         case VK_UP:
767         case VK_DOWN:
768             keyDelta = GetSystemMetrics(SM_CXVSCROLL);
769             if (wParam == VK_UP)
770                 keyDelta = -keyDelta;
771
772         case VK_PRIOR:
773         case VK_NEXT:
774             win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
775             curPos = GetScrollPos(win->hTextWnd, SB_VERT);
776             GetScrollRange(win->hTextWnd, SB_VERT, &min, &max);
777
778             if (keyDelta == 0)
779             {            
780                 GetClientRect(win->hTextWnd, &rect);
781                 keyDelta = (rect.bottom - rect.top) / 2;
782                 if (wParam == VK_PRIOR)
783                     keyDelta = -keyDelta;
784             }
785
786             curPos += keyDelta;
787             if (curPos > max)
788                  curPos = max;
789             else if (curPos < min)
790                  curPos = min;
791
792             dy = GetScrollPos(win->hTextWnd, SB_VERT) - curPos;
793             SetScrollPos(win->hTextWnd, SB_VERT, curPos, TRUE);
794             ScrollWindow(win->hTextWnd, 0, dy, NULL, NULL);
795             UpdateWindow(win->hTextWnd);
796             return 0;
797
798         case VK_ESCAPE:
799             MACRO_Exit();
800             return 0;
801         }
802         break;
803     }
804     return DefWindowProc(hWnd, msg, wParam, lParam);
805 }
806
807 /***********************************************************************
808  *
809  *           WINHELP_ButtonBoxWndProc
810  */
811 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
812 {
813     WINDOWPOS      *winpos;
814     WINHELP_WINDOW *win;
815     WINHELP_BUTTON *button;
816     SIZE button_size;
817     INT  x, y;
818
819     WINHELP_CheckPopup(msg);
820
821     switch (msg)
822     {
823     case WM_NCCREATE:
824         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
825         SetWindowLong(hWnd, 0, (LONG) win);
826         win->hButtonBoxWnd = hWnd;
827         break;
828
829     case WM_WINDOWPOSCHANGING:
830         winpos = (WINDOWPOS*) lParam;
831         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
832
833         /* Update buttons */
834         button_size.cx = 0;
835         button_size.cy = 0;
836         for (button = win->first_button; button; button = button->next)
837         {
838             HDC  hDc;
839             SIZE textsize;
840             if (!button->hWnd)
841             {
842                 button->hWnd = CreateWindow(STRING_BUTTON, button->lpszName,
843                                             WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
844                                             0, 0, 0, 0,
845                                             hWnd, (HMENU) button->wParam,
846                                             Globals.hInstance, 0);
847                 if (button->hWnd) {
848                     if (Globals.button_proc == NULL)
849                         Globals.button_proc = (WNDPROC) GetWindowLongPtr(button->hWnd, GWLP_WNDPROC);
850                     SetWindowLongPtr(button->hWnd, GWLP_WNDPROC, (LONG_PTR) WINHELP_ButtonWndProc);
851                 }
852             }
853             hDc = GetDC(button->hWnd);
854             GetTextExtentPoint(hDc, button->lpszName,
855                                lstrlen(button->lpszName), &textsize);
856             ReleaseDC(button->hWnd, hDc);
857
858             button_size.cx = max(button_size.cx, textsize.cx + BUTTON_CX);
859             button_size.cy = max(button_size.cy, textsize.cy + BUTTON_CY);
860         }
861
862         x = 0;
863         y = 0;
864         for (button = win->first_button; button; button = button->next)
865         {
866             SetWindowPos(button->hWnd, HWND_TOP, x, y, button_size.cx, button_size.cy, 0);
867
868             if (x + 2 * button_size.cx <= winpos->cx)
869                 x += button_size.cx;
870             else
871                 x = 0, y += button_size.cy;
872         }
873         winpos->cy = y + (x ? button_size.cy : 0);
874         break;
875
876     case WM_COMMAND:
877         SendMessage(GetParent(hWnd), msg, wParam, lParam);
878         break;
879
880     case WM_KEYDOWN:
881         switch (wParam)
882         {
883         case VK_UP:
884         case VK_DOWN:
885         case VK_PRIOR:
886         case VK_NEXT:
887         case VK_ESCAPE:
888             return SendMessage(GetParent(hWnd), msg, wParam, lParam);
889         }
890         break;
891     }
892
893     return DefWindowProc(hWnd, msg, wParam, lParam);
894 }
895
896 /***********************************************************************
897  *
898  *           WINHELP_ButtonWndProc
899  */
900 static LRESULT CALLBACK WINHELP_ButtonWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
901 {
902     if (msg == WM_KEYDOWN)
903     {
904         switch (wParam)
905         {
906         case VK_UP:
907         case VK_DOWN:
908         case VK_PRIOR:
909         case VK_NEXT:
910         case VK_ESCAPE:
911             return SendMessage(GetParent(hWnd), msg, wParam, lParam);
912         }
913     }
914
915     return CallWindowProc(Globals.button_proc, hWnd, msg, wParam, lParam);
916 }
917
918 /***********************************************************************
919  *
920  *           WINHELP_TextWndProc
921  */
922 static LRESULT CALLBACK WINHELP_TextWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
923 {
924     WINHELP_WINDOW    *win;
925     WINHELP_LINE      *line;
926     WINHELP_LINE_PART *part;
927     WINDOWPOS         *winpos;
928     PAINTSTRUCT        ps;
929     HDC   hDc;
930     POINT mouse;
931     INT   scroll_pos;
932     HWND  hPopupWnd;
933     BOOL  bExit;
934
935     if (msg != WM_LBUTTONDOWN)
936         WINHELP_CheckPopup(msg);
937
938     switch (msg)
939     {
940     case WM_NCCREATE:
941         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
942         SetWindowLong(hWnd, 0, (LONG) win);
943         win->hTextWnd = hWnd;
944         win->hBrush = CreateSolidBrush(win->info->sr_color);
945         if (win->info->win_style & WS_POPUP) Globals.hPopupWnd = win->hMainWnd = hWnd;
946         WINHELP_InitFonts(hWnd);
947         break;
948
949     case WM_CREATE:
950         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
951
952         /* Calculate vertical size and position of a popup window */
953         if (win->info->win_style & WS_POPUP)
954         {
955             POINT origin;
956             RECT old_window_rect;
957             RECT old_client_rect;
958             SIZE old_window_size;
959             SIZE old_client_size;
960             SIZE new_client_size;
961             SIZE new_window_size;
962
963             GetWindowRect(hWnd, &old_window_rect);
964             origin.x = old_window_rect.left;
965             origin.y = old_window_rect.top;
966             old_window_size.cx = old_window_rect.right  - old_window_rect.left;
967             old_window_size.cy = old_window_rect.bottom - old_window_rect.top;
968
969             GetClientRect(hWnd, &old_client_rect);
970             old_client_size.cx = old_client_rect.right  - old_client_rect.left;
971             old_client_size.cy = old_client_rect.bottom - old_client_rect.top;
972
973             new_client_size = old_client_size;
974             WINHELP_SplitLines(hWnd, &new_client_size);
975
976             if (origin.y + POPUP_YDISTANCE + new_client_size.cy <= GetSystemMetrics(SM_CYSCREEN))
977                 origin.y += POPUP_YDISTANCE;
978             else
979                 origin.y -= POPUP_YDISTANCE + new_client_size.cy;
980
981             new_window_size.cx = old_window_size.cx - old_client_size.cx + new_client_size.cx;
982             new_window_size.cy = old_window_size.cy - old_client_size.cy + new_client_size.cy;
983
984             win->hShadowWnd =
985                 CreateWindow(SHADOW_WIN_CLASS_NAME, "", WS_POPUP,
986                              origin.x + SHADOW_DX, origin.y + SHADOW_DY,
987                              new_window_size.cx, new_window_size.cy,
988                              0, 0, Globals.hInstance, 0);
989
990             SetWindowPos(hWnd, HWND_TOP, origin.x, origin.y,
991                          new_window_size.cx, new_window_size.cy,
992                          0);
993             SetWindowPos(win->hShadowWnd, hWnd, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
994             ShowWindow(win->hShadowWnd, SW_NORMAL);
995             SetActiveWindow(hWnd);
996         }
997         break;
998
999     case WM_WINDOWPOSCHANGED:
1000         winpos = (WINDOWPOS*) lParam;
1001
1002         if (!(winpos->flags & SWP_NOSIZE)) WINHELP_SetupText(hWnd);
1003         break;
1004
1005     case WM_MOUSEWHEEL:
1006     {
1007        int wheelDelta = 0;
1008        UINT scrollLines = 3;
1009        int curPos = GetScrollPos(hWnd, SB_VERT);
1010        int min, max;
1011
1012        GetScrollRange(hWnd, SB_VERT, &min, &max);
1013
1014        SystemParametersInfo(SPI_GETWHEELSCROLLLINES,0, &scrollLines, 0);
1015        if (wParam & (MK_SHIFT | MK_CONTROL))
1016            return DefWindowProc(hWnd, msg, wParam, lParam);
1017        wheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
1018        if (abs(wheelDelta) >= WHEEL_DELTA && scrollLines) {
1019            int dy;
1020
1021            curPos += wheelDelta;
1022            if (curPos > max)
1023                 curPos = max;
1024            else if (curPos < min)
1025                 curPos = min;
1026
1027            dy = GetScrollPos(hWnd, SB_VERT) - curPos;
1028            SetScrollPos(hWnd, SB_VERT, curPos, TRUE);
1029            ScrollWindow(hWnd, 0, dy, NULL, NULL);
1030            UpdateWindow(hWnd);
1031        }
1032     }
1033     break;
1034
1035     case WM_VSCROLL:
1036     {
1037         BOOL  update = TRUE;
1038         RECT  rect;
1039         INT   Min, Max;
1040         INT   CurPos = GetScrollPos(hWnd, SB_VERT);
1041         INT   dy;
1042         
1043         GetScrollRange(hWnd, SB_VERT, &Min, &Max);
1044         GetClientRect(hWnd, &rect);
1045
1046         switch (wParam & 0xffff)
1047         {
1048         case SB_THUMBTRACK:
1049         case SB_THUMBPOSITION: CurPos  = wParam >> 16;                   break;
1050         case SB_TOP:           CurPos  = Min;                            break;
1051         case SB_BOTTOM:        CurPos  = Max;                            break;
1052         case SB_PAGEUP:        CurPos -= (rect.bottom - rect.top) / 2;   break;
1053         case SB_PAGEDOWN:      CurPos += (rect.bottom - rect.top) / 2;   break;
1054         case SB_LINEUP:        CurPos -= GetSystemMetrics(SM_CXVSCROLL); break;
1055         case SB_LINEDOWN:      CurPos += GetSystemMetrics(SM_CXVSCROLL); break;
1056         default: update = FALSE;
1057         }
1058         if (update)
1059         {
1060             if (CurPos > Max)
1061                 CurPos = Max;
1062             else if (CurPos < Min)
1063                 CurPos = Min;
1064             dy = GetScrollPos(hWnd, SB_VERT) - CurPos;
1065             SetScrollPos(hWnd, SB_VERT, CurPos, TRUE);
1066             ScrollWindow(hWnd, 0, dy, NULL, NULL);
1067             UpdateWindow(hWnd);
1068         }
1069     }
1070     break;
1071
1072     case WM_PAINT:
1073         hDc = BeginPaint(hWnd, &ps);
1074         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1075         scroll_pos = GetScrollPos(hWnd, SB_VERT);
1076
1077         /* No DPtoLP needed - MM_TEXT map mode */
1078         if (ps.fErase) FillRect(hDc, &ps.rcPaint, win->hBrush);
1079         for (line = win->first_line; line; line = line->next)
1080         {
1081             for (part = &line->first_part; part; part = part->next)
1082             {
1083                 switch (part->cookie)
1084                 {
1085                 case hlp_line_part_text:
1086                     SelectObject(hDc, part->u.text.hFont);
1087                     SetTextColor(hDc, part->u.text.color);
1088                     SetBkColor(hDc, win->info->sr_color);
1089                     TextOut(hDc, part->rect.left, part->rect.top - scroll_pos,
1090                             part->u.text.lpsText, part->u.text.wTextLen);
1091                     if (part->u.text.wUnderline)
1092                     {
1093                         HPEN    hPen;
1094
1095                         switch (part->u.text.wUnderline)
1096                         {
1097                         case 1: /* simple */
1098                         case 2: /* double */
1099                             hPen = CreatePen(PS_SOLID, 1, part->u.text.color);
1100                             break;
1101                         case 3: /* dotted */
1102                             hPen = CreatePen(PS_DOT, 1, part->u.text.color);
1103                             break;
1104                         default:
1105                             WINE_FIXME("Unknow underline type\n");
1106                             continue;
1107                         }
1108
1109                         SelectObject(hDc, hPen);
1110                         MoveToEx(hDc, part->rect.left, part->rect.bottom - scroll_pos - 1, NULL);
1111                         LineTo(hDc, part->rect.right, part->rect.bottom - scroll_pos - 1);
1112                         if (part->u.text.wUnderline == 2)
1113                         {
1114                             MoveToEx(hDc, part->rect.left, part->rect.bottom - scroll_pos + 1, NULL);
1115                             LineTo(hDc, part->rect.right, part->rect.bottom - scroll_pos + 1);
1116                         }
1117                         DeleteObject(hPen);
1118                     }
1119                     break;
1120                 case hlp_line_part_bitmap:
1121                     {
1122                         HDC hMemDC;
1123
1124                         hMemDC = CreateCompatibleDC(hDc);
1125                         SelectObject(hMemDC, part->u.bitmap.hBitmap);
1126                         BitBlt(hDc, part->rect.left, part->rect.top - scroll_pos,
1127                                part->rect.right - part->rect.left, part->rect.bottom - part->rect.top,
1128                                hMemDC, 0, 0, SRCCOPY);
1129                         DeleteDC(hMemDC);
1130                     }
1131                     break;
1132                 case hlp_line_part_metafile:
1133                     {
1134                         HDC hMemDC;
1135                         HBITMAP hBitmap;
1136                         SIZE sz;
1137                         RECT rc;
1138
1139                         sz.cx = part->rect.right - part->rect.left;
1140                         sz.cy = part->rect.bottom - part->rect.top;
1141                         hMemDC = CreateCompatibleDC(hDc);
1142                         hBitmap = CreateCompatibleBitmap(hDc, sz.cx, sz.cy);
1143                         SelectObject(hMemDC, hBitmap);
1144                         SelectObject(hMemDC, win->hBrush);
1145                         rc.left = 0;
1146                         rc.top = 0;
1147                         rc.right = sz.cx;
1148                         rc.bottom = sz.cy;
1149                         FillRect(hMemDC, &rc, win->hBrush);
1150                         SetMapMode(hMemDC, part->u.metafile.mm);
1151                         SetWindowExtEx(hMemDC, sz.cx, sz.cy, 0);
1152                         SetViewportExtEx(hMemDC, sz.cx, sz.cy, 0);
1153                         SetWindowOrgEx(hMemDC, 0, 0, 0);
1154                         SetViewportOrgEx(hMemDC, 0, 0, 0);
1155                         PlayMetaFile(hMemDC, part->u.metafile.hMetaFile);
1156                         SetMapMode(hMemDC, MM_TEXT);
1157                         SetWindowOrgEx(hMemDC, 0, 0, 0);
1158                         SetViewportOrgEx(hMemDC, 0, 0, 0);
1159                         BitBlt(hDc, part->rect.left, part->rect.top - scroll_pos,
1160                                sz.cx, sz.cy, hMemDC, 0, 0, SRCCOPY);
1161                         DeleteDC(hMemDC);
1162                         DeleteObject(hBitmap);
1163                     }
1164                     break;
1165                 }
1166             }
1167         }
1168
1169         EndPaint(hWnd, &ps);
1170         break;
1171
1172     case WM_MOUSEMOVE:
1173         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1174
1175         if (WINHELP_IsOverLink(win, wParam, lParam))
1176             SetCursor(win->hHandCur); /* set to hand pointer cursor to indicate a link */
1177         else
1178             SetCursor(win->hArrowCur); /* set to hand pointer cursor to indicate a link */
1179
1180         break;
1181
1182     case WM_LBUTTONDOWN:
1183         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1184
1185         hPopupWnd = Globals.hPopupWnd;
1186         Globals.hPopupWnd = 0;
1187
1188         part = WINHELP_IsOverLink(win, wParam, lParam);
1189         if (part)
1190         {
1191             HLPFILE*            hlpfile;
1192             HLPFILE_WINDOWINFO* wi;
1193
1194             mouse.x = (short)LOWORD(lParam);
1195             mouse.y = (short)HIWORD(lParam);
1196
1197             if (part->link) switch (part->link->cookie)
1198             {
1199             case hlp_link_link:
1200                 hlpfile = WINHELP_LookupHelpFile(part->link->lpszString);
1201                 if (part->link->window == -1)
1202                     wi = win->info;
1203                 else if ((part->link->window >= 0) && (part->link->window < hlpfile->numWindows))
1204                     wi = &hlpfile->windows[part->link->window];
1205                 else
1206                 {
1207                     WINE_WARN("link to window %d/%d\n", part->link->window, hlpfile->numWindows);
1208                     break;
1209                 }
1210                 WINHELP_CreateHelpWindowByHash(hlpfile, part->link->lHash, wi,
1211                                                SW_NORMAL);
1212                 break;
1213             case hlp_link_popup:
1214                 hlpfile = WINHELP_LookupHelpFile(part->link->lpszString);
1215                 if (hlpfile) WINHELP_CreateHelpWindowByHash(hlpfile, part->link->lHash, 
1216                                                WINHELP_GetPopupWindowInfo(hlpfile, hWnd, &mouse),
1217                                                SW_NORMAL);
1218                 break;
1219             case hlp_link_macro:
1220                 MACRO_ExecuteMacro(part->link->lpszString);
1221                 break;
1222             default:
1223                 WINE_FIXME("Unknown link cookie %d\n", part->link->cookie);
1224             }
1225         }
1226
1227         if (hPopupWnd)
1228             DestroyWindow(hPopupWnd);
1229         break;
1230
1231     case WM_NCDESTROY:
1232         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1233
1234         if (hWnd == Globals.hPopupWnd) Globals.hPopupWnd = 0;
1235
1236         bExit = (Globals.wVersion >= 4 && !lstrcmpi(win->lpszName, "main"));
1237         DeleteObject(win->hBrush);
1238
1239         WINHELP_DeleteWindow(win);
1240
1241         if (bExit) MACRO_Exit();
1242
1243         if (!Globals.win_list)
1244             PostQuitMessage(0);
1245         break;
1246     }
1247
1248     return DefWindowProc(hWnd, msg, wParam, lParam);
1249 }
1250
1251 /******************************************************************
1252  *              WINHELP_HistoryWndProc
1253  *
1254  *
1255  */
1256 static LRESULT CALLBACK WINHELP_HistoryWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1257 {
1258     WINHELP_WINDOW*     win;
1259     PAINTSTRUCT         ps;
1260     HDC                 hDc;
1261     TEXTMETRIC          tm;
1262     unsigned int        i;
1263     RECT                r;
1264
1265     switch (msg)
1266     {
1267     case WM_NCCREATE:
1268         win = (WINHELP_WINDOW*)((LPCREATESTRUCT)lParam)->lpCreateParams;
1269         SetWindowLong(hWnd, 0, (LONG)win);
1270         win->hHistoryWnd = hWnd;
1271         break;
1272     case WM_CREATE:
1273         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1274         hDc = GetDC(hWnd);
1275         GetTextMetrics(hDc, &tm);
1276         GetWindowRect(hWnd, &r);
1277
1278         r.right = r.left + 30 * tm.tmAveCharWidth;
1279         r.bottom = r.top + (sizeof(win->history) / sizeof(win->history[0])) * tm.tmHeight;
1280         AdjustWindowRect(&r, GetWindowLong(hWnd, GWL_STYLE), FALSE);
1281         if (r.left < 0) {r.right -= r.left; r.left = 0;}
1282         if (r.top < 0) {r.bottom -= r.top; r.top = 0;}
1283
1284         MoveWindow(hWnd, r.left, r.top, r.right, r.bottom, TRUE);
1285         ReleaseDC(hWnd, hDc);
1286         break;
1287     case WM_LBUTTONDOWN:
1288         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1289         hDc = GetDC(hWnd);
1290         GetTextMetrics(hDc, &tm);
1291         i = HIWORD(lParam) / tm.tmHeight;
1292         if (i < win->histIndex)
1293             WINHELP_CreateHelpWindow(win->history[i], win->info, SW_SHOW);
1294         ReleaseDC(hWnd, hDc);
1295         break;
1296     case WM_PAINT:
1297         hDc = BeginPaint(hWnd, &ps);
1298         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1299         GetTextMetrics(hDc, &tm);
1300
1301         for (i = 0; i < win->histIndex; i++)
1302         {
1303             TextOut(hDc, 0, i * tm.tmHeight, win->history[i]->lpszTitle, 
1304                     strlen(win->history[i]->lpszTitle));
1305         }
1306         EndPaint(hWnd, &ps);
1307         break;
1308     case WM_DESTROY:
1309         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1310         if (hWnd == win->hHistoryWnd)
1311             win->hHistoryWnd = 0;
1312         break;
1313     }
1314     return DefWindowProc(hWnd, msg, wParam, lParam);
1315 }
1316
1317 /***********************************************************************
1318  *
1319  *           WINHELP_ShadowWndProc
1320  */
1321 static LRESULT CALLBACK WINHELP_ShadowWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1322 {
1323     WINHELP_CheckPopup(msg);
1324     return DefWindowProc(hWnd, msg, wParam, lParam);
1325 }
1326
1327 /***********************************************************************
1328  *
1329  *           SetupText
1330  */
1331 static void WINHELP_SetupText(HWND hWnd)
1332 {
1333     HDC  hDc = GetDC(hWnd);
1334     RECT rect;
1335     SIZE newsize;
1336
1337     ShowScrollBar(hWnd, SB_VERT, FALSE);
1338     if (!WINHELP_SplitLines(hWnd, NULL))
1339     {
1340         ShowScrollBar(hWnd, SB_VERT, TRUE);
1341         GetClientRect(hWnd, &rect);
1342
1343         WINHELP_SplitLines(hWnd, &newsize);
1344         SetScrollRange(hWnd, SB_VERT, 0, rect.top + newsize.cy - rect.bottom, TRUE);
1345     }
1346     else
1347     {
1348         SetScrollPos(hWnd, SB_VERT, 0, FALSE);
1349         SetScrollRange(hWnd, SB_VERT, 0, 0, FALSE);
1350     }
1351
1352     ReleaseDC(hWnd, hDc);
1353 }
1354
1355 /***********************************************************************
1356  *
1357  *           WINHELP_AppendText
1358  */
1359 static BOOL WINHELP_AppendText(WINHELP_LINE ***linep, WINHELP_LINE_PART ***partp,
1360                                LPSIZE space, LPSIZE textsize,
1361                                INT *line_ascent, INT ascent,
1362                                LPCSTR text, UINT textlen,
1363                                HFONT font, COLORREF color, HLPFILE_LINK *link,
1364                                unsigned underline)
1365 {
1366     WINHELP_LINE      *line;
1367     WINHELP_LINE_PART *part;
1368     LPSTR ptr;
1369
1370     if (!*partp) /* New line */
1371     {
1372         *line_ascent  = ascent;
1373
1374         line = HeapAlloc(GetProcessHeap(), 0,
1375                          sizeof(WINHELP_LINE) + textlen);
1376         if (!line) return FALSE;
1377
1378         line->next    = 0;
1379         part          = &line->first_part;
1380         ptr           = (char*)line + sizeof(WINHELP_LINE);
1381
1382         line->rect.top    = (**linep ? (**linep)->rect.bottom : 0) + space->cy;
1383         line->rect.bottom = line->rect.top;
1384         line->rect.left   = space->cx;
1385         line->rect.right  = space->cx;
1386
1387         if (**linep) *linep = &(**linep)->next;
1388         **linep = line;
1389         space->cy = 0;
1390     }
1391     else /* Same line */
1392     {
1393         line = **linep;
1394
1395         if (*line_ascent < ascent)
1396         {
1397             WINHELP_LINE_PART *p;
1398             for (p = &line->first_part; p; p = p->next)
1399             {
1400                 p->rect.top    += ascent - *line_ascent;
1401                 p->rect.bottom += ascent - *line_ascent;
1402             }
1403             line->rect.bottom += ascent - *line_ascent;
1404             *line_ascent = ascent;
1405         }
1406
1407         part = HeapAlloc(GetProcessHeap(), 0,
1408                          sizeof(WINHELP_LINE_PART) + textlen);
1409         if (!part) return FALSE;
1410         **partp = part;
1411         ptr     = (char*)part + sizeof(WINHELP_LINE_PART);
1412     }
1413
1414     memcpy(ptr, text, textlen);
1415     part->cookie            = hlp_line_part_text;
1416     part->rect.left         = line->rect.right + (*partp ? space->cx : 0);
1417     part->rect.right        = part->rect.left + textsize->cx;
1418     line->rect.right        = part->rect.right;
1419     part->rect.top          =
1420         ((*partp) ? line->rect.top : line->rect.bottom) + *line_ascent - ascent;
1421     part->rect.bottom       = part->rect.top + textsize->cy;
1422     line->rect.bottom       = max(line->rect.bottom, part->rect.bottom);
1423     part->u.text.lpsText    = ptr;
1424     part->u.text.wTextLen   = textlen;
1425     part->u.text.hFont      = font;
1426     part->u.text.color      = color;
1427     part->u.text.wUnderline = underline;
1428
1429     WINE_TRACE("Appended text '%*.*s'[%d] @ (%d,%d-%d,%d)\n",
1430                part->u.text.wTextLen,
1431                part->u.text.wTextLen,
1432                part->u.text.lpsText,
1433                part->u.text.wTextLen,
1434                part->rect.left, part->rect.top, part->rect.right, part->rect.bottom);
1435
1436     part->link = link;
1437     if (link) link->wRefCount++;
1438
1439     part->next          = 0;
1440     *partp              = &part->next;
1441
1442     space->cx = 0;
1443
1444     return TRUE;
1445 }
1446
1447 /***********************************************************************
1448  *
1449  *           WINHELP_AppendGfxObject
1450  */
1451 static WINHELP_LINE_PART* WINHELP_AppendGfxObject(WINHELP_LINE ***linep, WINHELP_LINE_PART ***partp,
1452                                                   LPSIZE space, LPSIZE gfxSize,
1453                                                   HLPFILE_LINK *link, unsigned pos)
1454 {
1455     WINHELP_LINE      *line;
1456     WINHELP_LINE_PART *part;
1457     LPSTR              ptr;
1458
1459     if (!*partp || pos == 1) /* New line */
1460     {
1461         line = HeapAlloc(GetProcessHeap(), 0, sizeof(WINHELP_LINE));
1462         if (!line) return NULL;
1463
1464         line->next    = NULL;
1465         part          = &line->first_part;
1466
1467         line->rect.top    = (**linep ? (**linep)->rect.bottom : 0) + space->cy;
1468         line->rect.bottom = line->rect.top;
1469         line->rect.left   = space->cx;
1470         line->rect.right  = space->cx;
1471
1472         if (**linep) *linep = &(**linep)->next;
1473         **linep = line;
1474         space->cy = 0;
1475         ptr = (char*)line + sizeof(WINHELP_LINE);
1476     }
1477     else /* Same line */
1478     {
1479         if (pos == 2) WINE_FIXME("Left alignment not handled\n");
1480         line = **linep;
1481
1482         part = HeapAlloc(GetProcessHeap(), 0, sizeof(WINHELP_LINE_PART));
1483         if (!part) return NULL;
1484         **partp = part;
1485         ptr = (char*)part + sizeof(WINHELP_LINE_PART);
1486     }
1487
1488     /* part->cookie should be set by caller (image or metafile) */
1489     part->rect.left       = line->rect.right + (*partp ? space->cx : 0);
1490     part->rect.right      = part->rect.left + gfxSize->cx;
1491     line->rect.right      = part->rect.right;
1492     part->rect.top        = (*partp) ? line->rect.top : line->rect.bottom;
1493     part->rect.bottom     = part->rect.top + gfxSize->cy;
1494     line->rect.bottom     = max(line->rect.bottom, part->rect.bottom);
1495
1496     WINE_TRACE("Appended gfx @ (%d,%d-%d,%d)\n",
1497                part->rect.left, part->rect.top, part->rect.right, part->rect.bottom);
1498
1499     part->link = link;
1500     if (link) link->wRefCount++;
1501
1502     part->next            = NULL;
1503     *partp                = &part->next;
1504
1505     space->cx = 0;
1506
1507     return part;
1508 }
1509
1510
1511 /***********************************************************************
1512  *
1513  *           WINHELP_SplitLines
1514  */
1515 static BOOL WINHELP_SplitLines(HWND hWnd, LPSIZE newsize)
1516 {
1517     WINHELP_WINDOW     *win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1518     HLPFILE_PARAGRAPH  *p;
1519     WINHELP_LINE      **line = &win->first_line;
1520     WINHELP_LINE_PART **part = 0;
1521     INT                 line_ascent = 0;
1522     SIZE                space;
1523     RECT                rect;
1524     HDC                 hDc;
1525
1526     if (newsize) newsize->cx = newsize->cy = 0;
1527
1528     if (!win->page) return TRUE;
1529
1530     WINHELP_DeleteLines(win);
1531
1532     GetClientRect(hWnd, &rect);
1533
1534     rect.top    += INTERNAL_BORDER_WIDTH;
1535     rect.left   += INTERNAL_BORDER_WIDTH;
1536     rect.right  -= INTERNAL_BORDER_WIDTH;
1537     rect.bottom -= INTERNAL_BORDER_WIDTH;
1538
1539     space.cy = rect.top;
1540     space.cx = rect.left;
1541
1542     hDc = GetDC(hWnd);
1543
1544     for (p = win->page->first_paragraph; p; p = p->next)
1545     {
1546         switch (p->cookie)
1547         {
1548         case para_normal_text:
1549         case para_debug_text:
1550             {
1551                 TEXTMETRIC tm;
1552                 SIZE textsize    = {0, 0};
1553                 LPCSTR text      = p->u.text.lpszText;
1554                 UINT indent      = 0;
1555                 UINT len         = strlen(text);
1556                 unsigned underline = 0;
1557
1558                 HFONT hFont = 0;
1559                 COLORREF color = RGB(0, 0, 0);
1560
1561                 if (p->u.text.wFont < win->page->file->numFonts)
1562                 {
1563                     HLPFILE*    hlpfile = win->page->file;
1564
1565                     if (!hlpfile->fonts[p->u.text.wFont].hFont)
1566                         hlpfile->fonts[p->u.text.wFont].hFont = CreateFontIndirect(&hlpfile->fonts[p->u.text.wFont].LogFont);
1567                     hFont = hlpfile->fonts[p->u.text.wFont].hFont;
1568                     color = hlpfile->fonts[p->u.text.wFont].color;
1569                 }
1570                 else
1571                 {
1572                     UINT  wFont = (p->u.text.wFont < win->fonts_len) ? p->u.text.wFont : 0;
1573
1574                     hFont = win->fonts[wFont];
1575                 }
1576
1577                 if (p->link && p->link->bClrChange)
1578                 {
1579                     underline = (p->link->cookie == hlp_link_popup) ? 3 : 1;
1580                     color = RGB(0, 0x80, 0);
1581                 }
1582                 if (p->cookie == para_debug_text) color = RGB(0xff, 0, 0);
1583
1584                 SelectObject(hDc, hFont);
1585
1586                 GetTextMetrics(hDc, &tm);
1587
1588                 if (p->u.text.wIndent)
1589                 {
1590                     indent = p->u.text.wIndent * 5 * tm.tmAveCharWidth;
1591                     if (!part)
1592                         space.cx = rect.left + indent - 2 * tm.tmAveCharWidth;
1593                 }
1594
1595                 if (p->u.text.wVSpace)
1596                 {
1597                     part = 0;
1598                     space.cx = rect.left + indent;
1599                     space.cy += (p->u.text.wVSpace - 1) * tm.tmHeight;
1600                 }
1601
1602                 if (p->u.text.wHSpace)
1603                 {
1604                     space.cx += p->u.text.wHSpace * 2 * tm.tmAveCharWidth;
1605                 }
1606
1607                 WINE_TRACE("splitting text %s\n", text);
1608
1609                 while (len)
1610                 {
1611                     INT free_width = rect.right - (part ? (*line)->rect.right : rect.left) - space.cx;
1612                     UINT low = 0, curr = len, high = len, textlen = 0;
1613
1614                     if (free_width > 0)
1615                     {
1616                         while (1)
1617                         {
1618                             GetTextExtentPoint(hDc, text, curr, &textsize);
1619
1620                             if (textsize.cx <= free_width) low = curr;
1621                             else high = curr;
1622
1623                             if (high <= low + 1) break;
1624
1625                             if (textsize.cx) curr = (curr * free_width) / textsize.cx;
1626                             if (curr <= low) curr = low + 1;
1627                             else if (curr >= high) curr = high - 1;
1628                         }
1629                         textlen = low;
1630                         while (textlen && text[textlen] && text[textlen] != ' ') textlen--;
1631                     }
1632                     if (!part && !textlen) textlen = max(low, 1);
1633
1634                     if (free_width <= 0 || !textlen)
1635                     {
1636                         part = 0;
1637                         space.cx = rect.left + indent;
1638                         space.cx = min(space.cx, rect.right - rect.left - 1);
1639                         continue;
1640                     }
1641
1642                     WINE_TRACE("\t => %d %*s\n", textlen, textlen, text);
1643
1644                     if (!WINHELP_AppendText(&line, &part, &space, &textsize,
1645                                             &line_ascent, tm.tmAscent,
1646                                             text, textlen, hFont, color, p->link, underline) ||
1647                         (!newsize && (*line)->rect.bottom > rect.bottom))
1648                     {
1649                         ReleaseDC(hWnd, hDc);
1650                         return FALSE;
1651                     }
1652
1653                     if (newsize)
1654                         newsize->cx = max(newsize->cx, (*line)->rect.right + INTERNAL_BORDER_WIDTH);
1655
1656                     len -= textlen;
1657                     text += textlen;
1658                     if (text[0] == ' ') text++, len--;
1659                 }
1660             }
1661             break;
1662         case para_bitmap:
1663         case para_metafile:
1664             {
1665                 SIZE                    gfxSize;
1666                 INT                     free_width;
1667                 WINHELP_LINE_PART*      ref_part;
1668
1669                 if (p->u.gfx.pos & 0x8000)
1670                 {
1671                     space.cx = rect.left;
1672                     if (*line)
1673                         space.cy += (*line)->rect.bottom - (*line)->rect.top;
1674                     part = 0;
1675                 }
1676
1677                 if (p->cookie == para_bitmap)
1678                 {
1679                     DIBSECTION              dibs;
1680                     
1681                     GetObject(p->u.gfx.u.bmp.hBitmap, sizeof(dibs), &dibs);
1682                     gfxSize.cx = dibs.dsBm.bmWidth;
1683                     gfxSize.cy = dibs.dsBm.bmHeight;
1684                 }
1685                 else
1686                 {
1687                     LPMETAFILEPICT lpmfp = &p->u.gfx.u.mfp;
1688                     if (lpmfp->mm == MM_ANISOTROPIC || lpmfp->mm == MM_ISOTROPIC)
1689                     {
1690                         gfxSize.cx = MulDiv(lpmfp->xExt, GetDeviceCaps(hDc, HORZRES),
1691                                             100*GetDeviceCaps(hDc, HORZSIZE));
1692                         gfxSize.cy = MulDiv(lpmfp->yExt, GetDeviceCaps(hDc, VERTRES),
1693                                             100*GetDeviceCaps(hDc, VERTSIZE));
1694                     }
1695                     else
1696                     {
1697                         gfxSize.cx = lpmfp->xExt;
1698                         gfxSize.cy = lpmfp->yExt;
1699                     }
1700                 }
1701
1702                 free_width = rect.right - ((part && *line) ? (*line)->rect.right : rect.left) - space.cx;
1703                 if (free_width <= 0)
1704                 {
1705                     part = NULL;
1706                     space.cx = rect.left;
1707                     space.cx = min(space.cx, rect.right - rect.left - 1);
1708                 }
1709                 ref_part = WINHELP_AppendGfxObject(&line, &part, &space, &gfxSize,
1710                                                    p->link, p->u.gfx.pos);
1711                 if (!ref_part || (!newsize && (*line)->rect.bottom > rect.bottom))
1712                 {
1713                     return FALSE;
1714                 }
1715                 if (p->cookie == para_bitmap)
1716                 {
1717                     ref_part->cookie = hlp_line_part_bitmap;
1718                     ref_part->u.bitmap.hBitmap = p->u.gfx.u.bmp.hBitmap;
1719                 }
1720                 else
1721                 {
1722                     ref_part->cookie = hlp_line_part_metafile;
1723                     ref_part->u.metafile.hMetaFile = p->u.gfx.u.mfp.hMF;
1724                     ref_part->u.metafile.mm = p->u.gfx.u.mfp.mm;
1725                 }
1726             }
1727             break;
1728         }
1729     }
1730
1731     if (newsize)
1732         newsize->cy = (*line)->rect.bottom + INTERNAL_BORDER_WIDTH;
1733
1734     ReleaseDC(hWnd, hDc);
1735     return TRUE;
1736 }
1737
1738 /***********************************************************************
1739  *
1740  *           WINHELP_CheckPopup
1741  */
1742 static void WINHELP_CheckPopup(UINT msg)
1743 {
1744     if (!Globals.hPopupWnd) return;
1745
1746     switch (msg)
1747     {
1748     case WM_COMMAND:
1749     case WM_LBUTTONDOWN:
1750     case WM_MBUTTONDOWN:
1751     case WM_RBUTTONDOWN:
1752     case WM_NCLBUTTONDOWN:
1753     case WM_NCMBUTTONDOWN:
1754     case WM_NCRBUTTONDOWN:
1755         DestroyWindow(Globals.hPopupWnd);
1756         Globals.hPopupWnd = 0;
1757     }
1758 }
1759
1760 /***********************************************************************
1761  *
1762  *           WINHELP_DeleteLines
1763  */
1764 static void WINHELP_DeleteLines(WINHELP_WINDOW *win)
1765 {
1766     WINHELP_LINE      *line, *next_line;
1767     WINHELP_LINE_PART *part, *next_part;
1768     for (line = win->first_line; line; line = next_line)
1769     {
1770         next_line = line->next;
1771         for (part = &line->first_part; part; part = next_part)
1772         {
1773             next_part = part->next;
1774             HLPFILE_FreeLink(part->link);
1775             HeapFree(GetProcessHeap(), 0, part);
1776         }
1777     }
1778     win->first_line = 0;
1779 }
1780
1781 /***********************************************************************
1782  *
1783  *           WINHELP_DeleteWindow
1784  */
1785 static void WINHELP_DeleteWindow(WINHELP_WINDOW* win)
1786 {
1787     WINHELP_WINDOW**    w;
1788     unsigned int        i;
1789     WINHELP_BUTTON*     b;
1790     WINHELP_BUTTON*     bp;
1791
1792     for (w = &Globals.win_list; *w; w = &(*w)->next)
1793     {
1794         if (*w == win)
1795         {
1796             *w = win->next;
1797             break;
1798         }
1799     }
1800
1801     if (Globals.active_win == win)
1802     {
1803         Globals.active_win = Globals.win_list;
1804         if (Globals.win_list)
1805             SetActiveWindow(Globals.win_list->hMainWnd);
1806     }
1807
1808     for (b = win->first_button; b; b = bp)
1809     {
1810         DestroyWindow(b->hWnd);
1811         bp = b->next;
1812         HeapFree(GetProcessHeap(), 0, b);
1813     }
1814
1815     if (win->hShadowWnd) DestroyWindow(win->hShadowWnd);
1816     if (win->hHistoryWnd) DestroyWindow(win->hHistoryWnd);
1817
1818     for (i = 0; i < win->histIndex; i++)
1819     {
1820         HLPFILE_FreeHlpFile(win->history[i]->file);
1821     }
1822
1823     for (i = 0; i < win->backIndex; i++)
1824         HLPFILE_FreeHlpFile(win->back[i]->file);
1825
1826     if (win->page) HLPFILE_FreeHlpFile(win->page->file);
1827     WINHELP_DeleteLines(win);
1828     HeapFree(GetProcessHeap(), 0, win);
1829 }
1830
1831 /***********************************************************************
1832  *
1833  *           WINHELP_InitFonts
1834  */
1835 static void WINHELP_InitFonts(HWND hWnd)
1836 {
1837     WINHELP_WINDOW *win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1838     LOGFONT logfontlist[] = {
1839         {-10, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1840         {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1841         {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1842         {-12, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1843         {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1844         {-10, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1845         { -8, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"}};
1846 #define FONTS_LEN (sizeof(logfontlist)/sizeof(*logfontlist))
1847
1848     static HFONT fonts[FONTS_LEN];
1849     static BOOL init = 0;
1850
1851     win->fonts_len = FONTS_LEN;
1852     win->fonts = fonts;
1853
1854     if (!init)
1855     {
1856         UINT i;
1857
1858         for (i = 0; i < FONTS_LEN; i++)
1859         {
1860             fonts[i] = CreateFontIndirect(&logfontlist[i]);
1861         }
1862
1863         init = 1;
1864     }
1865 }
1866
1867 /***********************************************************************
1868  *
1869  *           WINHELP_MessageBoxIDS
1870  */
1871 INT WINHELP_MessageBoxIDS(UINT ids_text, UINT ids_title, WORD type)
1872 {
1873     CHAR text[MAX_STRING_LEN];
1874     CHAR title[MAX_STRING_LEN];
1875
1876     LoadString(Globals.hInstance, ids_text, text, sizeof(text));
1877     LoadString(Globals.hInstance, ids_title, title, sizeof(title));
1878
1879     return MessageBox(0, text, title, type);
1880 }
1881
1882 /***********************************************************************
1883  *
1884  *           MAIN_MessageBoxIDS_s
1885  */
1886 INT WINHELP_MessageBoxIDS_s(UINT ids_text, LPCSTR str, UINT ids_title, WORD type)
1887 {
1888     CHAR text[MAX_STRING_LEN];
1889     CHAR title[MAX_STRING_LEN];
1890     CHAR newtext[MAX_STRING_LEN + MAX_PATH];
1891
1892     LoadString(Globals.hInstance, ids_text, text, sizeof(text));
1893     LoadString(Globals.hInstance, ids_title, title, sizeof(title));
1894     wsprintf(newtext, text, str);
1895
1896     return MessageBox(0, newtext, title, type);
1897 }
1898
1899 /******************************************************************
1900  *              WINHELP_IsOverLink
1901  *
1902  *
1903  */
1904 WINHELP_LINE_PART* WINHELP_IsOverLink(WINHELP_WINDOW* win, WPARAM wParam, LPARAM lParam)
1905 {
1906     POINT mouse;
1907     WINHELP_LINE      *line;
1908     WINHELP_LINE_PART *part;
1909     int scroll_pos = GetScrollPos(win->hTextWnd, SB_VERT);
1910
1911     mouse.x = LOWORD(lParam);
1912     mouse.y = HIWORD(lParam);
1913     for (line = win->first_line; line; line = line->next)
1914     {
1915         for (part = &line->first_part; part; part = part->next)
1916         {
1917             if (part->link && 
1918                 part->link->lpszString &&
1919                 part->rect.left   <= mouse.x &&
1920                 part->rect.right  >= mouse.x &&
1921                 part->rect.top    <= mouse.y + scroll_pos &&
1922                 part->rect.bottom >= mouse.y + scroll_pos)
1923             {
1924                 return part;
1925             }
1926         }
1927     }
1928
1929     return NULL;
1930 }