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