Handle generic column width changes.
[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  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include <stdio.h>
24 #include <string.h>
25 #include "winbase.h"
26 #include "wingdi.h"
27 #include "windowsx.h"
28 #include "winhelp.h"
29 #include "winhelp_res.h"
30
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(winhelp);
34
35 static BOOL    WINHELP_RegisterWinClasses(void);
36 static LRESULT CALLBACK WINHELP_MainWndProc(HWND, UINT, WPARAM, LPARAM);
37 static LRESULT CALLBACK WINHELP_TextWndProc(HWND, UINT, WPARAM, LPARAM);
38 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND, UINT, WPARAM, LPARAM);
39 static void    WINHELP_CheckPopup(UINT);
40 static BOOL    WINHELP_SplitLines(HWND hWnd, LPSIZE);
41 static void    WINHELP_InitFonts(HWND hWnd);
42 static void    WINHELP_DeleteLines(WINHELP_WINDOW*);
43 static void    WINHELP_DeleteWindow(WINHELP_WINDOW*);
44 static void    WINHELP_SetupText(HWND hWnd);
45 static WINHELP_LINE_PART* WINHELP_IsOverLink(HWND hWnd, WPARAM wParam, LPARAM lParam);
46
47 WINHELP_GLOBALS Globals = {3, 0, 0, 0, 0, 0};
48
49 static BOOL MacroTest = FALSE;
50
51 /***********************************************************************
52  *
53  *           WinMain
54  */
55 int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
56 {
57     MSG    msg;
58     LONG   lHash = 0;
59
60     Globals.hInstance = hInstance;
61
62     /* Get options */
63     while (*cmdline && (*cmdline == ' ' || *cmdline == '-'))
64     {
65         CHAR   option;
66         LPCSTR topic_id;
67         if (*cmdline++ == ' ') continue;
68
69         option = *cmdline;
70         if (option) cmdline++;
71         while (*cmdline && *cmdline == ' ') cmdline++;
72         switch (option)
73         {
74         case 'i':
75         case 'I':
76             topic_id = cmdline;
77             while (*cmdline && *cmdline != ' ') cmdline++;
78             if (*cmdline) *cmdline++ = '\0';
79             lHash = HLPFILE_Hash(topic_id);
80             break;
81
82         case '3':
83         case '4':
84             Globals.wVersion = option - '0';
85             break;
86
87         case 't':
88             MacroTest = TRUE;
89             break;
90         }
91     }
92
93     /* Create primary window */
94     WINHELP_RegisterWinClasses();
95     WINHELP_CreateHelpWindowByHash(cmdline, lHash, "main", FALSE, NULL, NULL, show);
96
97     /* Message loop */
98     while (GetMessage(&msg, 0, 0, 0))
99     {
100         TranslateMessage(&msg);
101         DispatchMessage(&msg);
102     }
103     return 0;
104 }
105
106 /***********************************************************************
107  *
108  *           RegisterWinClasses
109  */
110 static BOOL WINHELP_RegisterWinClasses(void)
111 {
112     WNDCLASS class_main, class_button_box, class_text, class_shadow;
113
114     class_main.style               = CS_HREDRAW | CS_VREDRAW;
115     class_main.lpfnWndProc         = WINHELP_MainWndProc;
116     class_main.cbClsExtra          = 0;
117     class_main.cbWndExtra          = sizeof(LONG);
118     class_main.hInstance           = Globals.hInstance;
119     class_main.hIcon               = LoadIcon(0, IDI_APPLICATION);
120     class_main.hCursor             = LoadCursor(0, IDC_ARROW);
121     class_main.hbrBackground       = GetStockObject(WHITE_BRUSH);
122     class_main.lpszMenuName        = 0;
123     class_main.lpszClassName       = MAIN_WIN_CLASS_NAME;
124
125     class_button_box               = class_main;
126     class_button_box.lpfnWndProc   = WINHELP_ButtonBoxWndProc;
127     class_button_box.hbrBackground = GetStockObject(GRAY_BRUSH);
128     class_button_box.lpszClassName = BUTTON_BOX_WIN_CLASS_NAME;
129
130     class_text = class_main;
131     class_text.lpfnWndProc         = WINHELP_TextWndProc;
132     class_text.lpszClassName       = TEXT_WIN_CLASS_NAME;
133
134     class_shadow = class_main;
135     class_shadow.lpfnWndProc       = DefWindowProc;
136     class_shadow.hbrBackground     = GetStockObject(GRAY_BRUSH);
137     class_shadow.lpszClassName     = SHADOW_WIN_CLASS_NAME;
138
139     return (RegisterClass(&class_main) &&
140             RegisterClass(&class_button_box) &&
141             RegisterClass(&class_text) &&
142             RegisterClass(&class_shadow));
143 }
144
145 /***********************************************************************
146  *
147  *           WINHELP_CreateHelpWindow
148  */
149
150 static BOOL WINHELP_CreateHelpWindow(HLPFILE_PAGE* page, LPCSTR lpszWindow,
151                                      BOOL bPopup, HWND hParentWnd, LPPOINT mouse, INT nCmdShow)
152 {
153     CHAR    szCaption[MAX_STRING_LEN];
154     SIZE    size   = {CW_USEDEFAULT, 0/*CW_USEDEFAULT*/};
155     POINT   origin = {240, 0};
156     LPSTR   ptr;
157     WINHELP_WINDOW *win, *oldwin;
158     HLPFILE_MACRO  *macro;
159     HWND hWnd;
160     BOOL bPrimary;
161
162     if (bPopup)
163         lpszWindow = NULL;
164     else if (!lpszWindow || !lpszWindow[0])
165         lpszWindow = Globals.active_win->lpszName;
166     bPrimary = lpszWindow && !lstrcmpi(lpszWindow, "main");
167
168     /* Calculate horizontal size and position of a popup window */
169     if (bPopup)
170     {
171         RECT parent_rect;
172         GetWindowRect(hParentWnd, &parent_rect);
173         size.cx = (parent_rect.right  - parent_rect.left) / 2;
174         size.cy = 10; /* need a non null value, so that border are taken into account while computing */
175
176         origin = *mouse;
177         ClientToScreen(hParentWnd, &origin);
178         origin.x -= size.cx / 2;
179         origin.x  = min(origin.x, GetSystemMetrics(SM_CXSCREEN) - size.cx);
180         origin.x  = max(origin.x, 0);
181     }
182
183     /* Initialize WINHELP_WINDOW struct */
184     win = HeapAlloc(GetProcessHeap(), 0,
185                     sizeof(WINHELP_WINDOW) + (lpszWindow ? strlen(lpszWindow) + 1 : 0));
186     if (!win) return FALSE;
187
188     win->next  = Globals.win_list;
189     Globals.win_list = win;
190     if (lpszWindow)
191     {
192         ptr = (char*)win + sizeof(WINHELP_WINDOW);
193         lstrcpy(ptr, (LPSTR) lpszWindow);
194         win->lpszName = ptr;
195     }
196     else win->lpszName = NULL;
197
198     win->page = page;
199     win->first_button = 0;
200     win->first_line = 0;
201     win->hMainWnd = 0;
202     win->hButtonBoxWnd = 0;
203     win->hTextWnd = 0;
204     win->hShadowWnd = 0;
205
206     win->hArrowCur = LoadCursorA(0, IDC_ARROWA);
207     win->hHandCur = LoadCursorA(0, IDC_HANDA);
208
209     Globals.active_win = win;
210
211     /* Initialize default pushbuttons */
212     if (MacroTest && !bPopup)
213         MACRO_CreateButton("BTN_TEST", "&Test", "MacroTest");
214     if (bPrimary && page)
215     {
216         CHAR    buffer[MAX_STRING_LEN];
217
218         LoadString(Globals.hInstance, STID_CONTENTS, buffer, sizeof(buffer));
219         MACRO_CreateButton("BTN_CONTENTS", buffer, "Contents()");
220         LoadString(Globals.hInstance, STID_SEARCH,buffer, sizeof(buffer));
221         MACRO_CreateButton("BTN_SEARCH", buffer, "Search()");
222         LoadString(Globals.hInstance, STID_BACK, buffer, sizeof(buffer));
223         MACRO_CreateButton("BTN_BACK", buffer, "Back()");
224         LoadString(Globals.hInstance, STID_HISTORY, buffer, sizeof(buffer));
225         MACRO_CreateButton("BTN_HISTORY", buffer, "History()");
226         LoadString(Globals.hInstance, STID_TOPICS, buffer, sizeof(buffer));
227         MACRO_CreateButton("BTN_TOPICS", buffer, "Finder()");
228     }
229
230     /* Initialize file specific pushbuttons */
231     if (!bPopup && page)
232         for (macro = page->file->first_macro; macro; macro = macro->next)
233             MACRO_ExecuteMacro(macro->lpszMacro);
234
235     /* Reuse existing window */
236     if (lpszWindow)
237         for (oldwin = win->next; oldwin; oldwin = oldwin->next)
238             if (oldwin->lpszName && !lstrcmpi(oldwin->lpszName, lpszWindow))
239             {
240                 WINHELP_BUTTON *button;
241
242                 win->hMainWnd      = oldwin->hMainWnd;
243                 win->hButtonBoxWnd = oldwin->hButtonBoxWnd;
244                 win->hTextWnd      = oldwin->hTextWnd;
245                 oldwin->hMainWnd = oldwin->hButtonBoxWnd = oldwin->hTextWnd = 0;
246
247                 SetWindowLong(win->hMainWnd,      0, (LONG) win);
248                 SetWindowLong(win->hButtonBoxWnd, 0, (LONG) win);
249                 SetWindowLong(win->hTextWnd,      0, (LONG) win);
250
251                 WINHELP_InitFonts(win->hMainWnd);
252
253                 if (page) {
254                     SetWindowText(win->hMainWnd, page->file->lpszTitle);
255                 }
256
257                 WINHELP_SetupText(win->hTextWnd);
258                 InvalidateRect(win->hTextWnd, NULL, TRUE);
259                 SendMessage(win->hMainWnd, WM_USER, 0, 0);
260                 UpdateWindow(win->hTextWnd);
261
262
263                 for (button = oldwin->first_button; button; button = button->next)
264                     DestroyWindow(button->hWnd);
265
266                 WINHELP_DeleteWindow(oldwin);
267                 return TRUE;
268             }
269
270     /* Create main Window */
271     if (!page) LoadString(Globals.hInstance, STID_WINE_HELP, szCaption, sizeof(szCaption));
272     hWnd = CreateWindow(bPopup ? TEXT_WIN_CLASS_NAME : MAIN_WIN_CLASS_NAME,
273                         page ? page->file->lpszTitle : szCaption,
274                         bPopup ? WS_POPUPWINDOW | WS_BORDER : WS_OVERLAPPEDWINDOW,
275                         origin.x, origin.y, size.cx, size.cy,
276                         0, bPrimary ? LoadMenu(Globals.hInstance, MAKEINTRESOURCE(MAIN_MENU)) : 0,
277                         Globals.hInstance, win);
278
279     ShowWindow(hWnd, nCmdShow);
280     UpdateWindow(hWnd);
281
282     return TRUE;
283 }
284
285 /***********************************************************************
286  *
287  *           WINHELP_CreateHelpWindowByPage
288  */
289 BOOL WINHELP_CreateHelpWindowByPage(HLPFILE_PAGE* page, LPCSTR lpszWindow,
290                                     BOOL bPopup, HWND hParentWnd, LPPOINT mouse, INT nCmdShow)
291 {
292     if (page) page->file->wRefCount++;
293     return WINHELP_CreateHelpWindow(page, lpszWindow, bPopup, hParentWnd, mouse, nCmdShow);
294 }
295
296 /***********************************************************************
297  *
298  *           WINHELP_CreateHelpWindowByHash
299  */
300 BOOL WINHELP_CreateHelpWindowByHash(LPCSTR lpszFile, LONG lHash, LPCSTR lpszWindow,
301                                     BOOL bPopup, HWND hParentWnd, LPPOINT mouse, INT nCmdShow)
302 {
303     HLPFILE_PAGE*       page;
304
305     /* Read help file */
306     if (lpszFile[0])
307     {
308         page = lHash ? HLPFILE_PageByHash(lpszFile, lHash) : HLPFILE_Contents(lpszFile);
309
310         /* Add Suffix `.hlp' */
311         if (!page && lstrcmpi(lpszFile + strlen(lpszFile) - 4, ".hlp"))
312         {
313             CHAR      szFile_hlp[MAX_PATHNAME_LEN];
314
315             lstrcpyn(szFile_hlp, lpszFile, sizeof(szFile_hlp) - 4);
316             szFile_hlp[sizeof(szFile_hlp) - 5] = '\0';
317             lstrcat(szFile_hlp, ".hlp");
318
319             page = lHash ? HLPFILE_PageByHash(szFile_hlp, lHash) : HLPFILE_Contents(szFile_hlp);
320             if (!page)
321             {
322                 WINHELP_MessageBoxIDS_s(STID_HLPFILE_ERROR_s, lpszFile, STID_WHERROR, MB_OK);
323                 if (Globals.win_list) return FALSE;
324             }
325         }
326     }
327     else page = NULL;
328     return WINHELP_CreateHelpWindowByPage(page, lpszWindow, bPopup, hParentWnd, mouse, nCmdShow);
329 }
330
331 /***********************************************************************
332  *
333  *           WINHELP_MainWndProc
334  */
335 static LRESULT CALLBACK WINHELP_MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
336 {
337     WINHELP_WINDOW *win;
338     WINHELP_BUTTON *button;
339     RECT rect, button_box_rect;
340     INT  text_top;
341
342     WINHELP_CheckPopup(msg);
343
344     switch (msg)
345     {
346     case WM_NCCREATE:
347         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
348         SetWindowLong(hWnd, 0, (LONG) win);
349         win->hMainWnd = hWnd;
350         break;
351
352     case WM_CREATE:
353         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
354
355         /* Create button box and text Window */
356         CreateWindow(BUTTON_BOX_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
357                      0, 0, 0, 0, hWnd, 0, Globals.hInstance, win);
358
359         CreateWindow(TEXT_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
360                      0, 0, 0, 0, hWnd, 0, Globals.hInstance, win);
361
362         /* Fall through */
363     case WM_USER:
364     case WM_WINDOWPOSCHANGED:
365         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
366         GetClientRect(hWnd, &rect);
367
368         /* Update button box and text Window */
369         SetWindowPos(win->hButtonBoxWnd, HWND_TOP,
370                      rect.left, rect.top,
371                      rect.right - rect.left,
372                      rect.bottom - rect.top, 0);
373
374         GetWindowRect(win->hButtonBoxWnd, &button_box_rect);
375         text_top = rect.top + button_box_rect.bottom - button_box_rect.top;
376
377         SetWindowPos(win->hTextWnd, HWND_TOP,
378                      rect.left, text_top,
379                      rect.right - rect.left,
380                      rect.bottom - text_top, 0);
381
382         break;
383
384     case WM_COMMAND:
385         Globals.active_win = win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
386         switch (wParam)
387         {
388             /* Menu FILE */
389         case MNID_FILE_OPEN:    MACRO_FileOpen();       break;
390         case MNID_FILE_PRINT:   MACRO_Print();          break;
391         case MNID_FILE_SETUP:   MACRO_PrinterSetup();   break;
392         case MNID_FILE_EXIT:    MACRO_Exit();           break;
393
394             /* Menu EDIT */
395         case MNID_EDIT_COPYDLG: MACRO_CopyDialog();     break;
396         case MNID_EDIT_ANNOTATE:MACRO_Annotate();       break;
397
398             /* Menu Bookmark */
399         case MNID_BKMK_DEFINE:  MACRO_BookmarkDefine(); break;
400
401             /* Menu Help */
402         case MNID_HELP_HELPON:  MACRO_HelpOn();         break;
403         case MNID_HELP_HELPTOP: MACRO_HelpOnTop();      break;
404         case MNID_HELP_ABOUT:   MACRO_About();          break;
405         case MNID_HELP_WINE:    ShellAbout(hWnd, "WINE", "Help", 0); break;
406
407         default:
408             /* Buttons */
409             for (button = win->first_button; button; button = button->next)
410                 if (wParam == button->wParam) break;
411             if (button)
412                 MACRO_ExecuteMacro(button->lpszMacro);
413             else
414                 WINHELP_MessageBoxIDS(STID_NOT_IMPLEMENTED, 0x121, MB_OK);
415             break;
416         }
417         break;
418     case WM_DESTROY:
419         if (Globals.hPopupWnd) DestroyWindow(Globals.hPopupWnd);
420         break;
421     }
422
423     return DefWindowProc(hWnd, msg, wParam, lParam);
424 }
425
426 /***********************************************************************
427  *
428  *           WINHELP_ButtonBoxWndProc
429  */
430 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
431 {
432     WINDOWPOS      *winpos;
433     WINHELP_WINDOW *win;
434     WINHELP_BUTTON *button;
435     SIZE button_size;
436     INT  x, y;
437
438     WINHELP_CheckPopup(msg);
439
440     switch (msg)
441     {
442     case WM_NCCREATE:
443         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
444         SetWindowLong(hWnd, 0, (LONG) win);
445         win->hButtonBoxWnd = hWnd;
446         break;
447
448     case WM_WINDOWPOSCHANGING:
449         winpos = (WINDOWPOS*) lParam;
450         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
451
452         /* Update buttons */
453         button_size.cx = 0;
454         button_size.cy = 0;
455         for (button = win->first_button; button; button = button->next)
456         {
457             HDC  hDc;
458             SIZE textsize;
459             if (!button->hWnd)
460                 button->hWnd = CreateWindow(STRING_BUTTON, (LPSTR) button->lpszName,
461                                             WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
462                                             0, 0, 0, 0,
463                                             hWnd, (HMENU) button->wParam,
464                                             Globals.hInstance, 0);
465             hDc = GetDC(button->hWnd);
466             GetTextExtentPoint(hDc, button->lpszName,
467                                lstrlen(button->lpszName), &textsize);
468             ReleaseDC(button->hWnd, hDc);
469
470             button_size.cx = max(button_size.cx, textsize.cx + BUTTON_CX);
471             button_size.cy = max(button_size.cy, textsize.cy + BUTTON_CY);
472         }
473
474         x = 0;
475         y = 0;
476         for (button = win->first_button; button; button = button->next)
477         {
478             SetWindowPos(button->hWnd, HWND_TOP, x, y, button_size.cx, button_size.cy, 0);
479
480             if (x + 2 * button_size.cx <= winpos->cx)
481                 x += button_size.cx;
482             else
483                 x = 0, y += button_size.cy;
484         }
485         winpos->cy = y + (x ? button_size.cy : 0);
486         break;
487
488     case WM_COMMAND:
489         SendMessage(GetParent(hWnd), msg, wParam, lParam);
490         break;
491     }
492
493     return DefWindowProc(hWnd, msg, wParam, lParam);
494 }
495
496 /***********************************************************************
497  *
498  *           WINHELP_TextWndProc
499  */
500 static LRESULT CALLBACK WINHELP_TextWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
501 {
502     WINHELP_WINDOW    *win;
503     WINHELP_LINE      *line;
504     WINHELP_LINE_PART *part;
505     WINDOWPOS         *winpos;
506     PAINTSTRUCT        ps;
507     HDC   hDc;
508     POINT mouse;
509     INT   scroll_pos;
510     HWND  hPopupWnd;
511     BOOL  bExit;
512
513     if (msg != WM_LBUTTONDOWN)
514         WINHELP_CheckPopup(msg);
515
516     switch (msg)
517     {
518     case WM_NCCREATE:
519         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
520         SetWindowLong(hWnd, 0, (LONG) win);
521         win->hTextWnd = hWnd;
522         if (!win->lpszName) Globals.hPopupWnd = win->hMainWnd = hWnd;
523         WINHELP_InitFonts(hWnd);
524         break;
525
526     case WM_CREATE:
527         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
528
529         /* Calculate vertical size and position of a popup window */
530         if (!win->lpszName)
531         {
532             POINT origin;
533             RECT old_window_rect;
534             RECT old_client_rect;
535             SIZE old_window_size;
536             SIZE old_client_size;
537             SIZE new_client_size;
538             SIZE new_window_size;
539
540             GetWindowRect(hWnd, &old_window_rect);
541             origin.x = old_window_rect.left;
542             origin.y = old_window_rect.top;
543             old_window_size.cx = old_window_rect.right  - old_window_rect.left;
544             old_window_size.cy = old_window_rect.bottom - old_window_rect.top;
545
546             GetClientRect(hWnd, &old_client_rect);
547             old_client_size.cx = old_client_rect.right  - old_client_rect.left;
548             old_client_size.cy = old_client_rect.bottom - old_client_rect.top;
549
550             new_client_size = old_client_size;
551             WINHELP_SplitLines(hWnd, &new_client_size);
552
553             if (origin.y + POPUP_YDISTANCE + new_client_size.cy <= GetSystemMetrics(SM_CYSCREEN))
554                 origin.y += POPUP_YDISTANCE;
555             else
556                 origin.y -= POPUP_YDISTANCE + new_client_size.cy;
557
558             new_window_size.cx = old_window_size.cx - old_client_size.cx + new_client_size.cx;
559             new_window_size.cy = old_window_size.cy - old_client_size.cy + new_client_size.cy;
560
561             win->hShadowWnd =
562                 CreateWindow(SHADOW_WIN_CLASS_NAME, "", WS_POPUP,
563                              origin.x + SHADOW_DX, origin.y + SHADOW_DY,
564                              new_window_size.cx, new_window_size.cy,
565                              0, 0, Globals.hInstance, 0);
566
567             SetWindowPos(hWnd, HWND_TOP, origin.x, origin.y,
568                          new_window_size.cx, new_window_size.cy,
569                          0);
570             SetWindowPos(win->hShadowWnd, hWnd, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
571             ShowWindow(win->hShadowWnd, SW_NORMAL);
572             SetActiveWindow(hWnd);
573         }
574         break;
575
576     case WM_WINDOWPOSCHANGED:
577         winpos = (WINDOWPOS*) lParam;
578
579         if (!(winpos->flags & SWP_NOSIZE)) WINHELP_SetupText(hWnd);
580         break;
581
582     case WM_VSCROLL:
583     {
584         BOOL  update = TRUE;
585         RECT  rect;
586         INT   Min, Max;
587         INT   CurPos = GetScrollPos(hWnd, SB_VERT);
588         GetScrollRange(hWnd, SB_VERT, &Min, &Max);
589         GetClientRect(hWnd, &rect);
590
591         switch (wParam & 0xffff)
592         {
593         case SB_THUMBTRACK:
594         case SB_THUMBPOSITION: CurPos  = wParam >> 16;                   break;
595         case SB_TOP:           CurPos  = Min;                            break;
596         case SB_BOTTOM:        CurPos  = Max;                            break;
597         case SB_PAGEUP:        CurPos -= (rect.bottom - rect.top) / 2;   break;
598         case SB_PAGEDOWN:      CurPos += (rect.bottom - rect.top) / 2;   break;
599         case SB_LINEUP:        CurPos -= GetSystemMetrics(SM_CXVSCROLL); break;
600         case SB_LINEDOWN:      CurPos += GetSystemMetrics(SM_CXVSCROLL); break;
601         default: update = FALSE;
602         }
603         if (update)
604         {
605             INT dy = GetScrollPos(hWnd, SB_VERT) - CurPos;
606             SetScrollPos(hWnd, SB_VERT, CurPos, TRUE);
607             ScrollWindow(hWnd, 0, dy, NULL, NULL);
608             UpdateWindow(hWnd);
609         }
610     }
611     break;
612
613     case WM_PAINT:
614         hDc = BeginPaint(hWnd, &ps);
615         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
616         scroll_pos = GetScrollPos(hWnd, SB_VERT);
617
618         for (line = win->first_line; line; line = line->next)
619         {
620             for (part = &line->first_part; part; part = part->next)
621             {
622                 switch (part->cookie)
623                 {
624                 case hlp_line_part_text:
625                     SelectObject(hDc, part->u.text.hFont);
626                     SetTextColor(hDc, part->u.text.color);
627                     TextOut(hDc, part->rect.left, part->rect.top - scroll_pos,
628                             part->u.text.lpsText, part->u.text.wTextLen);
629                     if (part->u.text.wUnderline)
630                     {
631                         HPEN    hPen;
632
633                         switch (part->u.text.wUnderline)
634                         {
635                         case 1: /* simple */
636                         case 2: /* double */
637                             hPen = CreatePen(PS_SOLID, 1, part->u.text.color);
638                             break;
639                         case 3: /* dotted */
640                             hPen = CreatePen(PS_DOT, 1, part->u.text.color);
641                             break;
642                         default:
643                             WINE_FIXME("Unknow underline type\n");
644                             continue;
645                         }
646
647                         SelectObject(hDc, hPen);
648                         MoveToEx(hDc, part->rect.left, part->rect.bottom - scroll_pos - 1, NULL);
649                         LineTo(hDc, part->rect.right, part->rect.bottom - scroll_pos - 1);
650                         if (part->u.text.wUnderline == 2)
651                         {
652                             MoveToEx(hDc, part->rect.left, part->rect.bottom - scroll_pos + 1, NULL);
653                             LineTo(hDc, part->rect.right, part->rect.bottom - scroll_pos + 1);
654                         }
655                         DeleteObject(hPen);
656                     }
657                     break;
658                 case hlp_line_part_image:
659                     {
660                         HDC hMemDC;
661
662                         hMemDC = CreateCompatibleDC(hDc);
663                         SelectObject(hMemDC, part->u.image.hBitmap);
664                         BitBlt(hDc, part->rect.left, part->rect.top - scroll_pos,
665                                part->rect.right - part->rect.left, part->rect.bottom - part->rect.top,
666                                hMemDC, 0, 0, SRCCOPY);
667                         DeleteDC(hMemDC);
668                     }
669                     break;
670                 }
671             }
672         }
673
674         EndPaint(hWnd, &ps);
675         break;
676
677     case WM_MOUSEMOVE:
678         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
679
680         if (WINHELP_IsOverLink(hWnd, wParam, lParam))
681             SetCursor(win->hHandCur); /* set to hand pointer cursor to indicate a link */
682         else
683             SetCursor(win->hArrowCur); /* set to hand pointer cursor to indicate a link */
684
685         break;
686
687     case WM_LBUTTONDOWN:
688         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
689
690         hPopupWnd = Globals.hPopupWnd;
691         Globals.hPopupWnd = 0;
692
693         part = WINHELP_IsOverLink(hWnd, wParam, lParam);
694         if (part)
695         {
696             mouse.x = LOWORD(lParam);
697             mouse.y = HIWORD(lParam);
698
699             switch (part->link.cookie)
700             {
701             case hlp_link_none:
702                 break;
703             case hlp_link_link:
704                 WINHELP_CreateHelpWindowByHash(part->link.lpszString, part->link.lHash, NULL,
705                                                FALSE, hWnd, &mouse, SW_NORMAL);
706                 break;
707             case hlp_link_popup:
708                 WINHELP_CreateHelpWindowByHash(part->link.lpszString, part->link.lHash, NULL,
709                                                TRUE, hWnd, &mouse, SW_NORMAL);
710                 break;
711             case hlp_link_macro:
712                 MACRO_ExecuteMacro(part->link.lpszString);
713                 break;
714             default:
715                 WINE_FIXME("Unknown link cookie %d\n", part->link.cookie);
716             }
717         }
718
719         if (hPopupWnd)
720             DestroyWindow(hPopupWnd);
721         break;
722
723     case WM_NCDESTROY:
724         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
725
726         if (hWnd == Globals.hPopupWnd) Globals.hPopupWnd = 0;
727         if (win->hShadowWnd) DestroyWindow(win->hShadowWnd);
728
729         bExit = (Globals.wVersion >= 4 && !lstrcmpi(win->lpszName, "main"));
730
731         WINHELP_DeleteWindow(win);
732
733         if (bExit) MACRO_Exit();
734
735         if (!Globals.win_list)
736             PostQuitMessage(0);
737         break;
738     }
739
740     return DefWindowProc(hWnd, msg, wParam, lParam);
741 }
742
743 /***********************************************************************
744  *
745  *           SetupText
746  */
747 static void WINHELP_SetupText(HWND hWnd)
748 {
749     HDC  hDc = GetDC(hWnd);
750     RECT rect;
751     SIZE newsize;
752
753     ShowScrollBar(hWnd, SB_VERT, FALSE);
754     if (!WINHELP_SplitLines(hWnd, NULL))
755     {
756         ShowScrollBar(hWnd, SB_VERT, TRUE);
757         GetClientRect(hWnd, &rect);
758
759         WINHELP_SplitLines(hWnd, &newsize);
760         SetScrollRange(hWnd, SB_VERT, 0, rect.top + newsize.cy - rect.bottom, TRUE);
761     }
762     else SetScrollPos(hWnd, SB_VERT, 0, FALSE);
763
764     ReleaseDC(hWnd, hDc);
765 }
766
767 /***********************************************************************
768  *
769  *           WINHELP_AppendText
770  */
771 static BOOL WINHELP_AppendText(WINHELP_LINE ***linep, WINHELP_LINE_PART ***partp,
772                                LPSIZE space, LPSIZE textsize,
773                                INT *line_ascent, INT ascent,
774                                LPCSTR text, UINT textlen,
775                                HFONT font, COLORREF color, HLPFILE_LINK *link,
776                                unsigned underline)
777 {
778     WINHELP_LINE      *line;
779     WINHELP_LINE_PART *part;
780     LPSTR ptr;
781
782     if (!*partp) /* New line */
783     {
784         *line_ascent  = ascent;
785
786         line = HeapAlloc(GetProcessHeap(), 0,
787                          sizeof(WINHELP_LINE) + textlen + (link ? lstrlen(link->lpszString) + 1 : 0));
788         if (!line) return FALSE;
789
790         line->next    = 0;
791         part          = &line->first_part;
792         ptr           = (char*)line + sizeof(WINHELP_LINE);
793
794         line->rect.top    = (**linep ? (**linep)->rect.bottom : 0) + space->cy;
795         line->rect.bottom = line->rect.top;
796         line->rect.left   = space->cx;
797         line->rect.right  = space->cx;
798
799         if (**linep) *linep = &(**linep)->next;
800         **linep = line;
801         space->cy = 0;
802     }
803     else /* Same line */
804     {
805         line = **linep;
806
807         if (*line_ascent < ascent)
808         {
809             WINHELP_LINE_PART *p;
810             for (p = &line->first_part; p; p = p->next)
811             {
812                 p->rect.top    += ascent - *line_ascent;
813                 p->rect.bottom += ascent - *line_ascent;
814             }
815             line->rect.bottom += ascent - *line_ascent;
816             *line_ascent = ascent;
817         }
818
819         part = HeapAlloc(GetProcessHeap(), 0,
820                          sizeof(WINHELP_LINE_PART) + textlen +
821                          (link ? lstrlen(link->lpszString) + 1 : 0));
822         if (!part) return FALSE;
823         **partp = part;
824         ptr     = (char*)part + sizeof(WINHELP_LINE_PART);
825     }
826
827     memcpy(ptr, text, textlen);
828     part->cookie            = hlp_line_part_text;
829     part->rect.left         = line->rect.right + (*partp ? space->cx : 0);
830     part->rect.right        = part->rect.left + textsize->cx;
831     line->rect.right        = part->rect.right;
832     part->rect.top          =
833         ((*partp) ? line->rect.top : line->rect.bottom) + *line_ascent - ascent;
834     part->rect.bottom       = part->rect.top + textsize->cy;
835     line->rect.bottom       = max(line->rect.bottom, part->rect.bottom);
836     part->u.text.lpsText    = ptr;
837     part->u.text.wTextLen   = textlen;
838     part->u.text.hFont      = font;
839     part->u.text.color      = color;
840     part->u.text.wUnderline = underline;
841
842     WINE_TRACE("Appended text '%*.*s'[%d] @ (%d,%d-%d,%d)\n",
843                part->u.text.wTextLen,
844                part->u.text.wTextLen,
845                part->u.text.lpsText,
846                part->u.text.wTextLen,
847                part->rect.left, part->rect.top, part->rect.right, part->rect.bottom);
848     if (link)
849     {
850         strcpy(ptr + textlen, link->lpszString);
851         part->link.lpszString = ptr + textlen;
852         part->link.cookie     = link->cookie;
853         part->link.lHash      = link->lHash;
854         part->link.bClrChange = link->bClrChange;
855     }
856     else part->link.cookie = hlp_link_none;
857
858     part->next          = 0;
859     *partp              = &part->next;
860
861     space->cx = 0;
862
863     return TRUE;
864 }
865
866 /***********************************************************************
867  *
868  *           WINHELP_AppendBitmap
869  */
870 static BOOL WINHELP_AppendBitmap(WINHELP_LINE ***linep, WINHELP_LINE_PART ***partp,
871                                  LPSIZE space,
872                                  HBITMAP hBmp, LPSIZE bmpSize,
873                                  HLPFILE_LINK *link, unsigned pos)
874 {
875     WINHELP_LINE      *line;
876     WINHELP_LINE_PART *part;
877     LPSTR              ptr;
878
879     if (!*partp || pos == 1) /* New line */
880     {
881         line = HeapAlloc(GetProcessHeap(), 0,
882                          sizeof(WINHELP_LINE) + (link ? lstrlen(link->lpszString) + 1 : 0));
883         if (!line) return FALSE;
884
885         line->next    = NULL;
886         part          = &line->first_part;
887
888         line->rect.top    = (**linep ? (**linep)->rect.bottom : 0) + space->cy;
889         line->rect.bottom = line->rect.top;
890         line->rect.left   = space->cx;
891         line->rect.right  = space->cx;
892
893         if (**linep) *linep = &(**linep)->next;
894         **linep = line;
895         space->cy = 0;
896         ptr = (char*)line + sizeof(WINHELP_LINE);
897     }
898     else /* Same line */
899     {
900         if (pos == 2) WINE_FIXME("Left alignment not handled\n");
901         line = **linep;
902
903         part = HeapAlloc(GetProcessHeap(), 0,
904                          sizeof(WINHELP_LINE_PART) +
905                          (link ? lstrlen(link->lpszString) + 1 : 0));
906         if (!part) return FALSE;
907         **partp = part;
908         ptr = (char*)part + sizeof(WINHELP_LINE_PART);
909     }
910
911     part->cookie          = hlp_line_part_image;
912     part->rect.left       = line->rect.right + (*partp ? space->cx : 0);
913     part->rect.right      = part->rect.left + bmpSize->cx;
914     line->rect.right      = part->rect.right;
915     part->rect.top        =
916         ((*partp) ? line->rect.top : line->rect.bottom);
917     part->rect.bottom     = part->rect.top + bmpSize->cy;
918     line->rect.bottom     = max(line->rect.bottom, part->rect.bottom);
919     part->u.image.hBitmap = hBmp;
920
921     WINE_TRACE("Appended bitmap '%d' @ (%d,%d-%d,%d)\n",
922                (unsigned)part->u.image.hBitmap,
923                part->rect.left, part->rect.top, part->rect.right, part->rect.bottom);
924
925     if (link)
926     {
927         strcpy(ptr, link->lpszString);
928         part->link.lpszString = ptr;
929         part->link.cookie     = link->cookie;
930         part->link.lHash      = link->lHash;
931         part->link.bClrChange = link->bClrChange;
932     }
933     else part->link.cookie = hlp_link_none;
934
935     part->next            = NULL;
936     *partp                = &part->next;
937
938     space->cx = 0;
939
940     return TRUE;
941 }
942
943
944 /***********************************************************************
945  *
946  *           WINHELP_SplitLines
947  */
948 static BOOL WINHELP_SplitLines(HWND hWnd, LPSIZE newsize)
949 {
950     WINHELP_WINDOW     *win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
951     HLPFILE_PARAGRAPH  *p;
952     WINHELP_LINE      **line = &win->first_line;
953     WINHELP_LINE_PART **part = 0;
954     INT                 line_ascent = 0;
955     SIZE                space;
956     RECT                rect;
957     HDC                 hDc;
958
959     if (newsize) newsize->cx = newsize->cy = 0;
960
961     if (!win->page) return TRUE;
962
963     WINHELP_DeleteLines(win);
964
965     GetClientRect(hWnd, &rect);
966
967     rect.top    += INTERNAL_BORDER_WIDTH;
968     rect.left   += INTERNAL_BORDER_WIDTH;
969     rect.right  -= INTERNAL_BORDER_WIDTH;
970     rect.bottom -= INTERNAL_BORDER_WIDTH;
971
972     space.cy = rect.top;
973     space.cx = rect.left;
974
975     hDc = GetDC(hWnd);
976
977     for (p = win->page->first_paragraph; p; p = p->next)
978     {
979         switch (p->cookie)
980         {
981         case para_normal_text:
982         case para_debug_text:
983             {
984                 TEXTMETRIC tm;
985                 SIZE textsize    = {0, 0};
986                 LPCSTR text      = p->u.text.lpszText;
987                 UINT indent      = 0;
988                 UINT len         = strlen(text);
989                 unsigned underline = 0;
990
991                 HFONT hFont = 0;
992                 COLORREF color = RGB(0, 0, 0);
993
994                 if (p->u.text.wFont < win->page->file->numFonts)
995                 {
996                     HLPFILE*    hlpfile = win->page->file;
997
998                     if (!hlpfile->fonts[p->u.text.wFont].hFont)
999                         hlpfile->fonts[p->u.text.wFont].hFont = CreateFontIndirect(&hlpfile->fonts[p->u.text.wFont].LogFont);
1000                     hFont = hlpfile->fonts[p->u.text.wFont].hFont;
1001                     color = hlpfile->fonts[p->u.text.wFont].color;
1002                 }
1003                 else
1004                 {
1005                     UINT  wFont = (p->u.text.wFont < win->fonts_len) ? p->u.text.wFont : 0;
1006
1007                     hFont = win->fonts[wFont];
1008                 }
1009
1010                 if (p->link)
1011                 {
1012                     underline = (p->link->cookie == hlp_link_popup) ? 3 : 1;
1013                     color = RGB(0, 0x80, 0);
1014                 }
1015                 if (p->cookie == para_debug_text) color = RGB(0xff, 0, 0);
1016
1017                 SelectObject(hDc, hFont);
1018
1019                 GetTextMetrics(hDc, &tm);
1020
1021                 if (p->u.text.wIndent)
1022                 {
1023                     indent = p->u.text.wIndent * 5 * tm.tmAveCharWidth;
1024                     if (!part)
1025                         space.cx = rect.left + indent - 2 * tm.tmAveCharWidth;
1026                 }
1027
1028                 if (p->u.text.wVSpace)
1029                 {
1030                     part = 0;
1031                     space.cx = rect.left + indent;
1032                     space.cy += (p->u.text.wVSpace - 1) * tm.tmHeight;
1033                 }
1034
1035                 if (p->u.text.wHSpace)
1036                 {
1037                     space.cx += p->u.text.wHSpace * 2 * tm.tmAveCharWidth;
1038                 }
1039
1040                 WINE_TRACE("splitting text %s\n", text);
1041
1042                 while (len)
1043                 {
1044                     INT free_width = rect.right - (part ? (*line)->rect.right : rect.left) - space.cx;
1045                     UINT low = 0, curr = len, high = len, textlen = 0;
1046
1047                     if (free_width > 0)
1048                     {
1049                         while (1)
1050                         {
1051                             GetTextExtentPoint(hDc, text, curr, &textsize);
1052
1053                             if (textsize.cx <= free_width) low = curr;
1054                             else high = curr;
1055
1056                             if (high <= low + 1) break;
1057
1058                             if (textsize.cx) curr = (curr * free_width) / textsize.cx;
1059                             if (curr <= low) curr = low + 1;
1060                             else if (curr >= high) curr = high - 1;
1061                         }
1062                         textlen = low;
1063                         while (textlen && text[textlen] && text[textlen] != ' ') textlen--;
1064                     }
1065                     if (!part && !textlen) textlen = max(low, 1);
1066
1067                     if (free_width <= 0 || !textlen)
1068                     {
1069                         part = 0;
1070                         space.cx = rect.left + indent;
1071                         space.cx = min(space.cx, rect.right - rect.left - 1);
1072                         continue;
1073                     }
1074
1075                     WINE_TRACE("\t => %d %*s\n", textlen, textlen, text);
1076
1077                     if (!WINHELP_AppendText(&line, &part, &space, &textsize,
1078                                             &line_ascent, tm.tmAscent,
1079                                             text, textlen, hFont, color, p->link, underline) ||
1080                         (!newsize && (*line)->rect.bottom > rect.bottom))
1081                     {
1082                         ReleaseDC(hWnd, hDc);
1083                         return FALSE;
1084                     }
1085
1086                     if (newsize)
1087                         newsize->cx = max(newsize->cx, (*line)->rect.right + INTERNAL_BORDER_WIDTH);
1088
1089                     len -= textlen;
1090                     text += textlen;
1091                     if (text[0] == ' ') text++, len--;
1092                 }
1093             }
1094             break;
1095         case para_image:
1096             {
1097                 DIBSECTION      dibs;
1098                 SIZE            bmpSize;
1099                 INT             free_width;
1100
1101                 if (p->u.image.pos & 0x8000)
1102                 {
1103                     space.cx = rect.left;
1104                     if (*line)
1105                         space.cy += (*line)->rect.bottom - (*line)->rect.top;
1106                     part = 0;
1107                 }
1108
1109                 GetObject(p->u.image.hBitmap, sizeof(dibs), &dibs);
1110                 bmpSize.cx = dibs.dsBm.bmWidth;
1111                 bmpSize.cy = dibs.dsBm.bmHeight;
1112
1113                 free_width = rect.right - ((part && *line) ? (*line)->rect.right : rect.left) - space.cx;
1114                 if (free_width <= 0)
1115                 {
1116                     part = NULL;
1117                     space.cx = rect.left;
1118                     space.cx = min(space.cx, rect.right - rect.left - 1);
1119                 }
1120                 if (!WINHELP_AppendBitmap(&line, &part, &space,
1121                                           p->u.image.hBitmap, &bmpSize,
1122                                           p->link, p->u.image.pos) ||
1123                     (!newsize && (*line)->rect.bottom > rect.bottom))
1124                 {
1125                     return FALSE;
1126                 }
1127             }
1128             break;
1129         }
1130
1131     }
1132
1133     if (newsize)
1134         newsize->cy = (*line)->rect.bottom + INTERNAL_BORDER_WIDTH;
1135
1136     ReleaseDC(hWnd, hDc);
1137     return TRUE;
1138 }
1139
1140 /***********************************************************************
1141  *
1142  *           WINHELP_CheckPopup
1143  */
1144 static void WINHELP_CheckPopup(UINT msg)
1145 {
1146     if (!Globals.hPopupWnd) return;
1147
1148     switch (msg)
1149     {
1150     case WM_COMMAND:
1151     case WM_LBUTTONDOWN:
1152     case WM_MBUTTONDOWN:
1153     case WM_RBUTTONDOWN:
1154     case WM_NCLBUTTONDOWN:
1155     case WM_NCMBUTTONDOWN:
1156     case WM_NCRBUTTONDOWN:
1157         DestroyWindow(Globals.hPopupWnd);
1158         Globals.hPopupWnd = 0;
1159     }
1160 }
1161
1162 /***********************************************************************
1163  *
1164  *           WINHELP_DeleteLines
1165  */
1166 static void WINHELP_DeleteLines(WINHELP_WINDOW *win)
1167 {
1168     WINHELP_LINE      *line, *next_line;
1169     WINHELP_LINE_PART *part, *next_part;
1170     for (line = win->first_line; line; line = next_line)
1171     {
1172         next_line = line->next;
1173         for (part = &line->first_part; part; part = next_part)
1174         {
1175             next_part = part->next;
1176             HeapFree(GetProcessHeap(), 0, part);
1177         }
1178     }
1179     win->first_line = 0;
1180 }
1181
1182 /***********************************************************************
1183  *
1184  *           WINHELP_DeleteWindow
1185  */
1186 static void WINHELP_DeleteWindow(WINHELP_WINDOW *win)
1187 {
1188     WINHELP_WINDOW **w;
1189
1190     for (w = &Globals.win_list; *w; w = &(*w)->next)
1191         if (*w == win)
1192         {
1193             *w = win->next;
1194             break;
1195         }
1196
1197     if (win->hShadowWnd) DestroyWindow(win->hShadowWnd);
1198     HLPFILE_FreeHlpFilePage(win->page);
1199     WINHELP_DeleteLines(win);
1200     HeapFree(GetProcessHeap(), 0, win);
1201 }
1202
1203 /***********************************************************************
1204  *
1205  *           WINHELP_InitFonts
1206  */
1207 static void WINHELP_InitFonts(HWND hWnd)
1208 {
1209     WINHELP_WINDOW *win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1210     LOGFONT logfontlist[] = {
1211         {-10, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1212         {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1213         {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1214         {-12, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1215         {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1216         {-10, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1217         { -8, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"}};
1218 #define FONTS_LEN (sizeof(logfontlist)/sizeof(*logfontlist))
1219
1220     static HFONT fonts[FONTS_LEN];
1221     static BOOL init = 0;
1222
1223     win->fonts_len = FONTS_LEN;
1224     win->fonts = fonts;
1225
1226     if (!init)
1227     {
1228         INT i;
1229
1230         for(i = 0; i < FONTS_LEN; i++)
1231         {
1232             fonts[i] = CreateFontIndirect(&logfontlist[i]);
1233         }
1234
1235         init = 1;
1236     }
1237 }
1238
1239 /***********************************************************************
1240  *
1241  *           WINHELP_MessageBoxIDS
1242  */
1243 INT WINHELP_MessageBoxIDS(UINT ids_text, UINT ids_title, WORD type)
1244 {
1245     CHAR text[MAX_STRING_LEN];
1246     CHAR title[MAX_STRING_LEN];
1247
1248     LoadString(Globals.hInstance, ids_text, text, sizeof(text));
1249     LoadString(Globals.hInstance, ids_title, title, sizeof(title));
1250
1251     return MessageBox(0, text, title, type);
1252 }
1253
1254 /***********************************************************************
1255  *
1256  *           MAIN_MessageBoxIDS_s
1257  */
1258 INT WINHELP_MessageBoxIDS_s(UINT ids_text, LPCSTR str, UINT ids_title, WORD type)
1259 {
1260     CHAR text[MAX_STRING_LEN];
1261     CHAR title[MAX_STRING_LEN];
1262     CHAR newtext[MAX_STRING_LEN + MAX_PATHNAME_LEN];
1263
1264     LoadString(Globals.hInstance, ids_text, text, sizeof(text));
1265     LoadString(Globals.hInstance, ids_title, title, sizeof(title));
1266     wsprintf(newtext, text, str);
1267
1268     return MessageBox(0, newtext, title, type);
1269 }
1270
1271 /******************************************************************
1272  *              WINHELP_IsOverLink
1273  *
1274  *
1275  */
1276 WINHELP_LINE_PART* WINHELP_IsOverLink(HWND hWnd, WPARAM wParam, LPARAM lParam)
1277 {
1278     WINHELP_WINDOW* win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1279     POINT mouse;
1280     WINHELP_LINE      *line;
1281     WINHELP_LINE_PART *part;
1282     int scroll_pos = GetScrollPos(hWnd, SB_VERT);
1283
1284     mouse.x = LOWORD(lParam);
1285     mouse.y = HIWORD(lParam);
1286     for (line = win->first_line; line; line = line->next)
1287     {
1288         for (part = &line->first_part; part; part = part->next)
1289         {
1290             if (part->link.cookie != hlp_link_none &&
1291                 part->link.lpszString &&
1292                 part->rect.left   <= mouse.x &&
1293                 part->rect.right  >= mouse.x &&
1294                 part->rect.top    <= mouse.y + scroll_pos &&
1295                 part->rect.bottom >= mouse.y + scroll_pos)
1296             {
1297                 return part;
1298             }
1299         }
1300     }
1301
1302     return NULL;
1303 }