Fixed some issues found by winapi_check.
[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     CHAR    szContents[MAX_STRING_LEN];
155     CHAR    szSearch[MAX_STRING_LEN];
156     CHAR    szBack[MAX_STRING_LEN];
157     CHAR    szHistory[MAX_STRING_LEN];
158     SIZE    size   = {CW_USEDEFAULT, 0/*CW_USEDEFAULT*/};
159     POINT   origin = {240, 0};
160     LPSTR   ptr;
161     WINHELP_WINDOW *win, *oldwin;
162     HLPFILE_MACRO  *macro;
163     HWND hWnd;
164     BOOL bPrimary;
165
166     if (bPopup)
167         lpszWindow = NULL;
168     else if (!lpszWindow || !lpszWindow[0])
169         lpszWindow = Globals.active_win->lpszName;
170     bPrimary = lpszWindow && !lstrcmpi(lpszWindow, "main");
171
172     /* Calculate horizontal size and position of a popup window */
173     if (bPopup)
174     {
175         RECT parent_rect;
176         GetWindowRect(hParentWnd, &parent_rect);
177         size.cx = (parent_rect.right  - parent_rect.left) / 2;
178         size.cy = 10; /* need a non null value, so that border are taken into account while computing */
179
180         origin = *mouse;
181         ClientToScreen(hParentWnd, &origin);
182         origin.x -= size.cx / 2;
183         origin.x  = min(origin.x, GetSystemMetrics(SM_CXSCREEN) - size.cx);
184         origin.x  = max(origin.x, 0);
185     }
186
187     /* Initialize WINHELP_WINDOW struct */
188     win = HeapAlloc(GetProcessHeap(), 0,
189                     sizeof(WINHELP_WINDOW) + (lpszWindow ? strlen(lpszWindow) + 1 : 0));
190     if (!win) return FALSE;
191
192     win->next  = Globals.win_list;
193     Globals.win_list = win;
194     if (lpszWindow)
195     {
196         ptr = (char*)win + sizeof(WINHELP_WINDOW);
197         lstrcpy(ptr, (LPSTR) lpszWindow);
198         win->lpszName = ptr;
199     }
200     else win->lpszName = NULL;
201
202     win->page = page;
203     win->first_button = 0;
204     win->first_line = 0;
205     win->hMainWnd = 0;
206     win->hButtonBoxWnd = 0;
207     win->hTextWnd = 0;
208     win->hShadowWnd = 0;
209
210     win->hArrowCur = LoadCursorA(0, IDC_ARROWA);
211     win->hHandCur = LoadCursorA(0, IDC_HANDA);
212
213     Globals.active_win = win;
214
215     /* Initialize default pushbuttons */
216     if (MacroTest && !bPopup)
217         MACRO_CreateButton("BTN_TEST", "&Test", "MacroTest");
218     if (bPrimary && page)
219     {
220         LoadString(Globals.hInstance, 0x126, szContents, sizeof(szContents));
221         LoadString(Globals.hInstance, 0x127,   szSearch,   sizeof(szSearch));
222         LoadString(Globals.hInstance, 0x128,     szBack,     sizeof(szBack));
223         LoadString(Globals.hInstance, 0x129,  szHistory,  sizeof(szHistory));
224         MACRO_CreateButton("BTN_CONTENTS", szContents, "Contents()");
225         MACRO_CreateButton("BTN_SEARCH",   szSearch,   "Search()");
226         MACRO_CreateButton("BTN_BACK",     szBack,     "Back()");
227         MACRO_CreateButton("BTN_HISTORY",  szHistory,  "History()");
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, 0x120, 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(HLPFILE_ERROR_s, lpszFile, 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 0x101:                     MACRO_FileOpen();       break;
390         case 0x104:                     MACRO_Print();          break;
391         case 0x106:                     MACRO_PrinterSetup();   break;
392         case 0x108:                     MACRO_Exit();           break;
393
394             /* Menu EDIT */
395         case 0x10A:                     MACRO_CopyDialog();     break;
396         case 0x10C:                     MACRO_Annotate();       break;
397
398             /* Menu Bookmark */
399         case 0x10E:                     MACRO_BookmarkDefine(); break;
400
401             /* Menu Help */
402         case 0x110:                     MACRO_HelpOn();         break;
403         case 0x111:                     MACRO_HelpOnTop();      break;
404
405             /* Menu Info */
406         case 0x113:                     MACRO_About();          break;
407
408         case 0x114:
409             ShellAbout(hWnd, "WINE", "Help", 0);
410             break;
411
412         default:
413             /* Buttons */
414             for (button = win->first_button; button; button = button->next)
415                 if (wParam == button->wParam) break;
416             if (button)
417                 MACRO_ExecuteMacro(button->lpszMacro);
418             else
419                 WINHELP_MessageBoxIDS(0x124, 0x121, MB_OK);
420             break;
421         }
422         break;
423     case WM_DESTROY:
424         if (Globals.hPopupWnd) DestroyWindow(Globals.hPopupWnd);
425         break;
426     }
427
428     return DefWindowProc(hWnd, msg, wParam, lParam);
429 }
430
431 /***********************************************************************
432  *
433  *           WINHELP_ButtonBoxWndProc
434  */
435 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
436 {
437     WINDOWPOS      *winpos;
438     WINHELP_WINDOW *win;
439     WINHELP_BUTTON *button;
440     SIZE button_size;
441     INT  x, y;
442
443     WINHELP_CheckPopup(msg);
444
445     switch (msg)
446     {
447     case WM_NCCREATE:
448         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
449         SetWindowLong(hWnd, 0, (LONG) win);
450         win->hButtonBoxWnd = hWnd;
451         break;
452
453     case WM_WINDOWPOSCHANGING:
454         winpos = (WINDOWPOS*) lParam;
455         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
456
457         /* Update buttons */
458         button_size.cx = 0;
459         button_size.cy = 0;
460         for (button = win->first_button; button; button = button->next)
461         {
462             HDC  hDc;
463             SIZE textsize;
464             if (!button->hWnd)
465                 button->hWnd = CreateWindow(STRING_BUTTON, (LPSTR) button->lpszName,
466                                             WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
467                                             0, 0, 0, 0,
468                                             hWnd, (HMENU) button->wParam,
469                                             Globals.hInstance, 0);
470             hDc = GetDC(button->hWnd);
471             GetTextExtentPoint(hDc, button->lpszName,
472                                lstrlen(button->lpszName), &textsize);
473             ReleaseDC(button->hWnd, hDc);
474
475             button_size.cx = max(button_size.cx, textsize.cx + BUTTON_CX);
476             button_size.cy = max(button_size.cy, textsize.cy + BUTTON_CY);
477         }
478
479         x = 0;
480         y = 0;
481         for (button = win->first_button; button; button = button->next)
482         {
483             SetWindowPos(button->hWnd, HWND_TOP, x, y, button_size.cx, button_size.cy, 0);
484
485             if (x + 2 * button_size.cx <= winpos->cx)
486                 x += button_size.cx;
487             else
488                 x = 0, y += button_size.cy;
489         }
490         winpos->cy = y + (x ? button_size.cy : 0);
491         break;
492
493     case WM_COMMAND:
494         SendMessage(GetParent(hWnd), msg, wParam, lParam);
495         break;
496     }
497
498     return DefWindowProc(hWnd, msg, wParam, lParam);
499 }
500
501 /***********************************************************************
502  *
503  *           WINHELP_TextWndProc
504  */
505 static LRESULT CALLBACK WINHELP_TextWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
506 {
507     WINHELP_WINDOW    *win;
508     WINHELP_LINE      *line;
509     WINHELP_LINE_PART *part;
510     WINDOWPOS         *winpos;
511     PAINTSTRUCT        ps;
512     HDC   hDc;
513     POINT mouse;
514     INT   scroll_pos;
515     HWND  hPopupWnd;
516     BOOL  bExit;
517
518     if (msg != WM_LBUTTONDOWN)
519         WINHELP_CheckPopup(msg);
520
521     switch (msg)
522     {
523     case WM_NCCREATE:
524         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
525         SetWindowLong(hWnd, 0, (LONG) win);
526         win->hTextWnd = hWnd;
527         if (!win->lpszName) Globals.hPopupWnd = win->hMainWnd = hWnd;
528         WINHELP_InitFonts(hWnd);
529         break;
530
531     case WM_CREATE:
532         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
533
534         /* Calculate vertical size and position of a popup window */
535         if (!win->lpszName)
536         {
537             POINT origin;
538             RECT old_window_rect;
539             RECT old_client_rect;
540             SIZE old_window_size;
541             SIZE old_client_size;
542             SIZE new_client_size;
543             SIZE new_window_size;
544
545             GetWindowRect(hWnd, &old_window_rect);
546             origin.x = old_window_rect.left;
547             origin.y = old_window_rect.top;
548             old_window_size.cx = old_window_rect.right  - old_window_rect.left;
549             old_window_size.cy = old_window_rect.bottom - old_window_rect.top;
550
551             GetClientRect(hWnd, &old_client_rect);
552             old_client_size.cx = old_client_rect.right  - old_client_rect.left;
553             old_client_size.cy = old_client_rect.bottom - old_client_rect.top;
554
555             new_client_size = old_client_size;
556             WINHELP_SplitLines(hWnd, &new_client_size);
557
558             if (origin.y + POPUP_YDISTANCE + new_client_size.cy <= GetSystemMetrics(SM_CYSCREEN))
559                 origin.y += POPUP_YDISTANCE;
560             else
561                 origin.y -= POPUP_YDISTANCE + new_client_size.cy;
562
563             new_window_size.cx = old_window_size.cx - old_client_size.cx + new_client_size.cx;
564             new_window_size.cy = old_window_size.cy - old_client_size.cy + new_client_size.cy;
565
566             win->hShadowWnd =
567                 CreateWindow(SHADOW_WIN_CLASS_NAME, "", WS_POPUP,
568                              origin.x + SHADOW_DX, origin.y + SHADOW_DY,
569                              new_window_size.cx, new_window_size.cy,
570                              0, 0, Globals.hInstance, 0);
571
572             SetWindowPos(hWnd, HWND_TOP, origin.x, origin.y,
573                          new_window_size.cx, new_window_size.cy,
574                          0);
575             SetWindowPos(win->hShadowWnd, hWnd, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
576             ShowWindow(win->hShadowWnd, SW_NORMAL);
577             SetActiveWindow(hWnd);
578         }
579         break;
580
581     case WM_WINDOWPOSCHANGED:
582         winpos = (WINDOWPOS*) lParam;
583
584         if (!(winpos->flags & SWP_NOSIZE)) WINHELP_SetupText(hWnd);
585         break;
586
587     case WM_VSCROLL:
588     {
589         BOOL  update = TRUE;
590         RECT  rect;
591         INT   Min, Max;
592         INT   CurPos = GetScrollPos(hWnd, SB_VERT);
593         GetScrollRange(hWnd, SB_VERT, &Min, &Max);
594         GetClientRect(hWnd, &rect);
595
596         switch (wParam & 0xffff)
597         {
598         case SB_THUMBTRACK:
599         case SB_THUMBPOSITION: CurPos  = wParam >> 16;                   break;
600         case SB_TOP:           CurPos  = Min;                            break;
601         case SB_BOTTOM:        CurPos  = Max;                            break;
602         case SB_PAGEUP:        CurPos -= (rect.bottom - rect.top) / 2;   break;
603         case SB_PAGEDOWN:      CurPos += (rect.bottom - rect.top) / 2;   break;
604         case SB_LINEUP:        CurPos -= GetSystemMetrics(SM_CXVSCROLL); break;
605         case SB_LINEDOWN:      CurPos += GetSystemMetrics(SM_CXVSCROLL); break;
606         default: update = FALSE;
607         }
608         if (update)
609         {
610             INT dy = GetScrollPos(hWnd, SB_VERT) - CurPos;
611             SetScrollPos(hWnd, SB_VERT, CurPos, TRUE);
612             ScrollWindow(hWnd, 0, dy, NULL, NULL);
613             UpdateWindow(hWnd);
614         }
615     }
616     break;
617
618     case WM_PAINT:
619         hDc = BeginPaint(hWnd, &ps);
620         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
621         scroll_pos = GetScrollPos(hWnd, SB_VERT);
622
623         for (line = win->first_line; line; line = line->next)
624         {
625             for (part = &line->first_part; part; part = part->next)
626             {
627                 switch (part->cookie)
628                 {
629                 case hlp_line_part_text:
630                     SelectObject(hDc, part->u.text.hFont);
631                     SetTextColor(hDc, part->u.text.color);
632                     TextOut(hDc, part->rect.left, part->rect.top - scroll_pos,
633                             part->u.text.lpsText, part->u.text.wTextLen);
634                     if (part->u.text.wUnderline)
635                     {
636                         HPEN    hPen;
637
638                         switch (part->u.text.wUnderline)
639                         {
640                         case 1: /* simple */
641                         case 2: /* double */
642                             hPen = CreatePen(PS_SOLID, 1, part->u.text.color);
643                             break;
644                         case 3: /* dotted */
645                             hPen = CreatePen(PS_DOT, 1, part->u.text.color);
646                             break;
647                         default:
648                             WINE_FIXME("Unknow underline type\n");
649                             continue;
650                         }
651
652                         SelectObject(hDc, hPen);
653                         MoveToEx(hDc, part->rect.left, part->rect.bottom - scroll_pos - 1, NULL);
654                         LineTo(hDc, part->rect.right, part->rect.bottom - scroll_pos - 1);
655                         if (part->u.text.wUnderline == 2)
656                         {
657                             MoveToEx(hDc, part->rect.left, part->rect.bottom - scroll_pos + 1, NULL);
658                             LineTo(hDc, part->rect.right, part->rect.bottom - scroll_pos + 1);
659                         }
660                         DeleteObject(hPen);
661                     }
662                     break;
663                 case hlp_line_part_image:
664                     {
665                         HDC hMemDC;
666
667                         hMemDC = CreateCompatibleDC(hDc);
668                         SelectObject(hMemDC, part->u.image.hBitmap);
669                         BitBlt(hDc, part->rect.left, part->rect.top - scroll_pos,
670                                part->rect.right - part->rect.left, part->rect.bottom - part->rect.top,
671                                hMemDC, 0, 0, SRCCOPY);
672                         DeleteDC(hMemDC);
673                     }
674                     break;
675                 }
676             }
677         }
678
679         EndPaint(hWnd, &ps);
680         break;
681
682     case WM_MOUSEMOVE:
683         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
684
685         if (WINHELP_IsOverLink(hWnd, wParam, lParam))
686             SetCursor(win->hHandCur); /* set to hand pointer cursor to indicate a link */
687         else
688             SetCursor(win->hArrowCur); /* set to hand pointer cursor to indicate a link */
689
690         break;
691
692     case WM_LBUTTONDOWN:
693         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
694
695         hPopupWnd = Globals.hPopupWnd;
696         Globals.hPopupWnd = 0;
697
698         part = WINHELP_IsOverLink(hWnd, wParam, lParam);
699         if (part)
700         {
701             mouse.x = LOWORD(lParam);
702             mouse.y = HIWORD(lParam);
703
704             WINHELP_CreateHelpWindowByHash(part->link.lpszPath, part->link.lHash, NULL,
705                                            part->link.bPopup, hWnd, &mouse, SW_NORMAL);
706         }
707
708         if (hPopupWnd)
709             DestroyWindow(hPopupWnd);
710         break;
711
712     case WM_NCDESTROY:
713         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
714
715         if (hWnd == Globals.hPopupWnd) Globals.hPopupWnd = 0;
716         if (win->hShadowWnd) DestroyWindow(win->hShadowWnd);
717
718         bExit = (Globals.wVersion >= 4 && !lstrcmpi(win->lpszName, "main"));
719
720         WINHELP_DeleteWindow(win);
721
722         if (bExit) MACRO_Exit();
723
724         if (!Globals.win_list)
725             PostQuitMessage(0);
726         break;
727     }
728
729     return DefWindowProc(hWnd, msg, wParam, lParam);
730 }
731
732 /***********************************************************************
733  *
734  *           SetupText
735  */
736 static void WINHELP_SetupText(HWND hWnd)
737 {
738     HDC  hDc = GetDC(hWnd);
739     RECT rect;
740     SIZE newsize;
741
742     ShowScrollBar(hWnd, SB_VERT, FALSE);
743     if (!WINHELP_SplitLines(hWnd, NULL))
744     {
745         ShowScrollBar(hWnd, SB_VERT, TRUE);
746         GetClientRect(hWnd, &rect);
747
748         WINHELP_SplitLines(hWnd, &newsize);
749         SetScrollRange(hWnd, SB_VERT, 0, rect.top + newsize.cy - rect.bottom, TRUE);
750     }
751     else SetScrollPos(hWnd, SB_VERT, 0, FALSE);
752
753     ReleaseDC(hWnd, hDc);
754 }
755
756 /***********************************************************************
757  *
758  *           WINHELP_AppendText
759  */
760 static BOOL WINHELP_AppendText(WINHELP_LINE ***linep, WINHELP_LINE_PART ***partp,
761                                LPSIZE space, LPSIZE textsize,
762                                INT *line_ascent, INT ascent,
763                                LPCSTR text, UINT textlen,
764                                HFONT font, COLORREF color, HLPFILE_LINK *link,
765                                unsigned underline)
766 {
767     WINHELP_LINE      *line;
768     WINHELP_LINE_PART *part;
769     LPSTR ptr;
770
771     if (!*partp) /* New line */
772     {
773         *line_ascent  = ascent;
774
775         line = HeapAlloc(GetProcessHeap(), 0,
776                          sizeof(WINHELP_LINE) + textlen + (link ? lstrlen(link->lpszPath) + 1 : 0));
777         if (!line) return FALSE;
778
779         line->next    = 0;
780         part          = &line->first_part;
781         ptr           = (char*)line + sizeof(WINHELP_LINE);
782
783         line->rect.top    = (**linep ? (**linep)->rect.bottom : 0) + space->cy;
784         line->rect.bottom = line->rect.top;
785         line->rect.left   = space->cx;
786         line->rect.right  = space->cx;
787
788         if (**linep) *linep = &(**linep)->next;
789         **linep = line;
790         space->cy = 0;
791     }
792     else /* Same line */
793     {
794         line = **linep;
795
796         if (*line_ascent < ascent)
797         {
798             WINHELP_LINE_PART *p;
799             for (p = &line->first_part; p; p = p->next)
800             {
801                 p->rect.top    += ascent - *line_ascent;
802                 p->rect.bottom += ascent - *line_ascent;
803             }
804             line->rect.bottom += ascent - *line_ascent;
805             *line_ascent = ascent;
806         }
807
808         part = HeapAlloc(GetProcessHeap(), 0,
809                          sizeof(WINHELP_LINE_PART) + textlen +
810                          (link ? lstrlen(link->lpszPath) + 1 : 0));
811         if (!part) return FALSE;
812         **partp = part;
813         ptr     = (char*)part + sizeof(WINHELP_LINE_PART);
814     }
815
816     memcpy(ptr, text, textlen);
817     part->cookie            = hlp_line_part_text;
818     part->rect.left         = line->rect.right + (*partp ? space->cx : 0);
819     part->rect.right        = part->rect.left + textsize->cx;
820     line->rect.right        = part->rect.right;
821     part->rect.top          =
822         ((*partp) ? line->rect.top : line->rect.bottom) + *line_ascent - ascent;
823     part->rect.bottom       = part->rect.top + textsize->cy;
824     line->rect.bottom       = max(line->rect.bottom, part->rect.bottom);
825     part->u.text.lpsText    = ptr;
826     part->u.text.wTextLen   = textlen;
827     part->u.text.hFont      = font;
828     part->u.text.color      = color;
829     part->u.text.wUnderline = underline;
830
831     WINE_TRACE("Appended text '%*.*s'[%d] @ (%d,%d-%d,%d)\n",
832                part->u.text.wTextLen,
833                part->u.text.wTextLen,
834                part->u.text.lpsText,
835                part->u.text.wTextLen,
836                part->rect.left, part->rect.top, part->rect.right, part->rect.bottom);
837     if (link)
838     {
839         strcpy(ptr + textlen, link->lpszPath);
840         part->link.lpszPath = ptr + textlen;
841         part->link.lHash    = link->lHash;
842         part->link.bPopup   = link->bPopup;
843     }
844     else part->link.lpszPath = 0;
845
846     part->next          = 0;
847     *partp              = &part->next;
848
849     space->cx = 0;
850
851     return TRUE;
852 }
853
854 /***********************************************************************
855  *
856  *           WINHELP_AppendBitmap
857  */
858 static BOOL WINHELP_AppendBitmap(WINHELP_LINE ***linep, WINHELP_LINE_PART ***partp,
859                                  LPSIZE space,
860                                  HBITMAP hBmp, LPSIZE bmpSize,
861                                  HLPFILE_LINK *link, unsigned pos)
862 {
863     WINHELP_LINE      *line;
864     WINHELP_LINE_PART *part;
865     LPSTR              ptr;
866
867     if (!*partp || pos == 1) /* New line */
868     {
869         line = HeapAlloc(GetProcessHeap(), 0,
870                          sizeof(WINHELP_LINE) + (link ? lstrlen(link->lpszPath) + 1 : 0));
871         if (!line) return FALSE;
872
873         line->next    = NULL;
874         part          = &line->first_part;
875
876         line->rect.top    = (**linep ? (**linep)->rect.bottom : 0) + space->cy;
877         line->rect.bottom = line->rect.top;
878         line->rect.left   = space->cx;
879         line->rect.right  = space->cx;
880
881         if (**linep) *linep = &(**linep)->next;
882         **linep = line;
883         space->cy = 0;
884         ptr = (char*)line + sizeof(WINHELP_LINE);
885     }
886     else /* Same line */
887     {
888         if (pos == 2) WINE_FIXME("Left alignment not handled\n");
889         line = **linep;
890
891         part = HeapAlloc(GetProcessHeap(), 0,
892                          sizeof(WINHELP_LINE_PART) +
893                          (link ? lstrlen(link->lpszPath) + 1 : 0));
894         if (!part) return FALSE;
895         **partp = part;
896         ptr = (char*)part + sizeof(WINHELP_LINE_PART);
897     }
898
899     part->cookie          = hlp_line_part_image;
900     part->rect.left       = line->rect.right + (*partp ? space->cx : 0);
901     part->rect.right      = part->rect.left + bmpSize->cx;
902     line->rect.right      = part->rect.right;
903     part->rect.top        =
904         ((*partp) ? line->rect.top : line->rect.bottom);
905     part->rect.bottom     = part->rect.top + bmpSize->cy;
906     line->rect.bottom     = max(line->rect.bottom, part->rect.bottom);
907     part->u.image.hBitmap = hBmp;
908
909     WINE_TRACE("Appended bitmap '%d' @ (%d,%d-%d,%d)\n",
910                (unsigned)part->u.image.hBitmap,
911                part->rect.left, part->rect.top, part->rect.right, part->rect.bottom);
912
913     if (link)
914     {
915         strcpy(ptr, link->lpszPath);
916         part->link.lpszPath = ptr;
917         part->link.lHash    = link->lHash;
918         part->link.bPopup   = link->bPopup;
919     }
920     else part->link.lpszPath = 0;
921
922     part->next            = NULL;
923     *partp                = &part->next;
924
925     space->cx = 0;
926
927     return TRUE;
928 }
929
930
931 /***********************************************************************
932  *
933  *           WINHELP_SplitLines
934  */
935 static BOOL WINHELP_SplitLines(HWND hWnd, LPSIZE newsize)
936 {
937     WINHELP_WINDOW     *win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
938     HLPFILE_PARAGRAPH  *p;
939     WINHELP_LINE      **line = &win->first_line;
940     WINHELP_LINE_PART **part = 0;
941     INT                 line_ascent = 0;
942     SIZE                space;
943     RECT                rect;
944     HDC                 hDc;
945
946     if (newsize) newsize->cx = newsize->cy = 0;
947
948     if (!win->page) return TRUE;
949
950     WINHELP_DeleteLines(win);
951
952     GetClientRect(hWnd, &rect);
953
954     rect.top    += INTERNAL_BORDER_WIDTH;
955     rect.left   += INTERNAL_BORDER_WIDTH;
956     rect.right  -= INTERNAL_BORDER_WIDTH;
957     rect.bottom -= INTERNAL_BORDER_WIDTH;
958
959     space.cy = rect.top;
960     space.cx = rect.left;
961
962     hDc = GetDC(hWnd);
963
964     for (p = win->page->first_paragraph; p; p = p->next)
965     {
966         switch (p->cookie)
967         {
968         case para_normal_text:
969         case para_debug_text:
970             {
971                 TEXTMETRIC tm;
972                 SIZE textsize    = {0, 0};
973                 LPCSTR text      = p->u.text.lpszText;
974                 UINT indent      = 0;
975                 UINT len         = strlen(text);
976                 unsigned underline = 0;
977
978                 HFONT hFont = 0;
979                 COLORREF color = RGB(0, 0, 0);
980
981                 if (p->u.text.wFont < win->page->file->numFonts)
982                 {
983                     HLPFILE*    hlpfile = win->page->file;
984
985                     if (!hlpfile->fonts[p->u.text.wFont].hFont)
986                         hlpfile->fonts[p->u.text.wFont].hFont = CreateFontIndirect(&hlpfile->fonts[p->u.text.wFont].LogFont);
987                     hFont = hlpfile->fonts[p->u.text.wFont].hFont;
988                     color = hlpfile->fonts[p->u.text.wFont].color;
989                 }
990                 else
991                 {
992                     UINT  wFont = (p->u.text.wFont < win->fonts_len) ? p->u.text.wFont : 0;
993
994                     hFont = win->fonts[wFont];
995                 }
996
997                 if (p->link)
998                 {
999                     underline = (p->link->bPopup) ? 3 : 1;
1000                     color = RGB(0, 0x80, 0);
1001                 }
1002                 if (p->cookie == para_debug_text) color = RGB(0xff, 0, 0);
1003
1004                 SelectObject(hDc, hFont);
1005
1006                 GetTextMetrics(hDc, &tm);
1007
1008                 if (p->u.text.wIndent)
1009                 {
1010                     indent = p->u.text.wIndent * 5 * tm.tmAveCharWidth;
1011                     if (!part)
1012                         space.cx = rect.left + indent - 2 * tm.tmAveCharWidth;
1013                 }
1014
1015                 if (p->u.text.wVSpace)
1016                 {
1017                     part = 0;
1018                     space.cx = rect.left + indent;
1019                     space.cy += (p->u.text.wVSpace - 1) * tm.tmHeight;
1020                 }
1021
1022                 if (p->u.text.wHSpace)
1023                 {
1024                     space.cx += p->u.text.wHSpace * 2 * tm.tmAveCharWidth;
1025                 }
1026
1027                 WINE_TRACE("splitting text %s\n", text);
1028
1029                 while (len)
1030                 {
1031                     INT free_width = rect.right - (part ? (*line)->rect.right : rect.left) - space.cx;
1032                     UINT low = 0, curr = len, high = len, textlen = 0;
1033
1034                     if (free_width > 0)
1035                     {
1036                         while (1)
1037                         {
1038                             GetTextExtentPoint(hDc, text, curr, &textsize);
1039
1040                             if (textsize.cx <= free_width) low = curr;
1041                             else high = curr;
1042
1043                             if (high <= low + 1) break;
1044
1045                             if (textsize.cx) curr = (curr * free_width) / textsize.cx;
1046                             if (curr <= low) curr = low + 1;
1047                             else if (curr >= high) curr = high - 1;
1048                         }
1049                         textlen = low;
1050                         while (textlen && text[textlen] && text[textlen] != ' ') textlen--;
1051                     }
1052                     if (!part && !textlen) textlen = max(low, 1);
1053
1054                     if (free_width <= 0 || !textlen)
1055                     {
1056                         part = 0;
1057                         space.cx = rect.left + indent;
1058                         space.cx = min(space.cx, rect.right - rect.left - 1);
1059                         continue;
1060                     }
1061
1062                     WINE_TRACE("\t => %d %*s\n", textlen, textlen, text);
1063
1064                     if (!WINHELP_AppendText(&line, &part, &space, &textsize,
1065                                             &line_ascent, tm.tmAscent,
1066                                             text, textlen, hFont, color, p->link, underline) ||
1067                         (!newsize && (*line)->rect.bottom > rect.bottom))
1068                     {
1069                         ReleaseDC(hWnd, hDc);
1070                         return FALSE;
1071                     }
1072
1073                     if (newsize)
1074                         newsize->cx = max(newsize->cx, (*line)->rect.right + INTERNAL_BORDER_WIDTH);
1075
1076                     len -= textlen;
1077                     text += textlen;
1078                     if (text[0] == ' ') text++, len--;
1079                 }
1080             }
1081             break;
1082         case para_image:
1083             {
1084                 DIBSECTION      dibs;
1085                 SIZE            bmpSize;
1086                 INT             free_width;
1087
1088                 if (p->u.image.pos & 0x8000)
1089                 {
1090                     space.cx = rect.left;
1091                     space.cy += (*line)->rect.bottom - (*line)->rect.top;
1092                     part = 0;
1093                 }
1094
1095                 GetObject(p->u.image.hBitmap, sizeof(dibs), &dibs);
1096                 bmpSize.cx = dibs.dsBm.bmWidth;
1097                 bmpSize.cy = dibs.dsBm.bmHeight;
1098
1099                 free_width = rect.right - (part ? (*line)->rect.right : rect.left) - space.cx;
1100                 if (free_width <= 0)
1101                 {
1102                     part = NULL;
1103                     space.cx = rect.left;
1104                     space.cx = min(space.cx, rect.right - rect.left - 1);
1105                 }
1106                 if (!WINHELP_AppendBitmap(&line, &part, &space,
1107                                           p->u.image.hBitmap, &bmpSize,
1108                                           p->link, p->u.image.pos) ||
1109                     (!newsize && (*line)->rect.bottom > rect.bottom))
1110                 {
1111                     return FALSE;
1112                 }
1113             }
1114             break;
1115         }
1116
1117     }
1118
1119     if (newsize)
1120         newsize->cy = (*line)->rect.bottom + INTERNAL_BORDER_WIDTH;
1121
1122     ReleaseDC(hWnd, hDc);
1123     return TRUE;
1124 }
1125
1126 /***********************************************************************
1127  *
1128  *           WINHELP_CheckPopup
1129  */
1130 static void WINHELP_CheckPopup(UINT msg)
1131 {
1132     if (!Globals.hPopupWnd) return;
1133
1134     switch (msg)
1135     {
1136     case WM_COMMAND:
1137     case WM_LBUTTONDOWN:
1138     case WM_MBUTTONDOWN:
1139     case WM_RBUTTONDOWN:
1140     case WM_NCLBUTTONDOWN:
1141     case WM_NCMBUTTONDOWN:
1142     case WM_NCRBUTTONDOWN:
1143         DestroyWindow(Globals.hPopupWnd);
1144         Globals.hPopupWnd = 0;
1145     }
1146 }
1147
1148 /***********************************************************************
1149  *
1150  *           WINHELP_DeleteLines
1151  */
1152 static void WINHELP_DeleteLines(WINHELP_WINDOW *win)
1153 {
1154     WINHELP_LINE      *line, *next_line;
1155     WINHELP_LINE_PART *part, *next_part;
1156     for(line = win->first_line; line; line = next_line)
1157     {
1158         next_line = line->next;
1159         for (part = &line->first_part; part; part = next_part)
1160         {
1161             next_part = part->next;
1162             HeapFree(GetProcessHeap(), 0, part);
1163         }
1164     }
1165     win->first_line = 0;
1166 }
1167
1168 /***********************************************************************
1169  *
1170  *           WINHELP_DeleteWindow
1171  */
1172 static void WINHELP_DeleteWindow(WINHELP_WINDOW *win)
1173 {
1174     WINHELP_WINDOW **w;
1175
1176     for (w = &Globals.win_list; *w; w = &(*w)->next)
1177         if (*w == win)
1178         {
1179             *w = win->next;
1180             break;
1181         }
1182
1183     if (win->hShadowWnd) DestroyWindow(win->hShadowWnd);
1184     HLPFILE_FreeHlpFilePage(win->page);
1185     WINHELP_DeleteLines(win);
1186     HeapFree(GetProcessHeap(), 0, win);
1187 }
1188
1189 /***********************************************************************
1190  *
1191  *           WINHELP_InitFonts
1192  */
1193 static void WINHELP_InitFonts(HWND hWnd)
1194 {
1195     WINHELP_WINDOW *win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1196     LOGFONT logfontlist[] = {
1197         {-10, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1198         {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1199         {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1200         {-12, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1201         {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1202         {-10, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1203         { -8, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"}};
1204 #define FONTS_LEN (sizeof(logfontlist)/sizeof(*logfontlist))
1205
1206     static HFONT fonts[FONTS_LEN];
1207     static BOOL init = 0;
1208
1209     win->fonts_len = FONTS_LEN;
1210     win->fonts = fonts;
1211
1212     if (!init)
1213     {
1214         INT i;
1215
1216         for(i = 0; i < FONTS_LEN; i++)
1217         {
1218             fonts[i] = CreateFontIndirect(&logfontlist[i]);
1219         }
1220
1221         init = 1;
1222     }
1223 }
1224
1225 /***********************************************************************
1226  *
1227  *           WINHELP_MessageBoxIDS
1228  */
1229 INT WINHELP_MessageBoxIDS(UINT ids_text, UINT ids_title, WORD type)
1230 {
1231     CHAR text[MAX_STRING_LEN];
1232     CHAR title[MAX_STRING_LEN];
1233
1234     LoadString(Globals.hInstance, ids_text, text, sizeof(text));
1235     LoadString(Globals.hInstance, ids_title, title, sizeof(title));
1236
1237     return MessageBox(0, text, title, type);
1238 }
1239
1240 /***********************************************************************
1241  *
1242  *           MAIN_MessageBoxIDS_s
1243  */
1244 INT WINHELP_MessageBoxIDS_s(UINT ids_text, LPCSTR str, UINT ids_title, WORD type)
1245 {
1246     CHAR text[MAX_STRING_LEN];
1247     CHAR title[MAX_STRING_LEN];
1248     CHAR newtext[MAX_STRING_LEN + MAX_PATHNAME_LEN];
1249
1250     LoadString(Globals.hInstance, ids_text, text, sizeof(text));
1251     LoadString(Globals.hInstance, ids_title, title, sizeof(title));
1252     wsprintf(newtext, text, str);
1253
1254     return MessageBox(0, newtext, title, type);
1255 }
1256
1257 /******************************************************************
1258  *              WINHELP_IsOverLink
1259  *
1260  *
1261  */
1262 WINHELP_LINE_PART* WINHELP_IsOverLink(HWND hWnd, WPARAM wParam, LPARAM lParam)
1263 {
1264     WINHELP_WINDOW* win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1265     POINT mouse;
1266     WINHELP_LINE      *line;
1267     WINHELP_LINE_PART *part;
1268     int scroll_pos = GetScrollPos(hWnd, SB_VERT);
1269
1270     mouse.x = LOWORD(lParam);
1271     mouse.y = HIWORD(lParam);
1272     for (line = win->first_line; line; line = line->next)
1273     {
1274         for (part = &line->first_part; part; part = part->next)
1275         {
1276             if (part->link.lpszPath &&
1277                 part->rect.left   <= mouse.x &&
1278                 part->rect.right  >= mouse.x &&
1279                 part->rect.top    <= mouse.y + scroll_pos &&
1280                 part->rect.bottom >= mouse.y + scroll_pos)
1281             {
1282                 return part;
1283             }
1284         }
1285     }
1286
1287     return NULL;
1288 }