Rewrote macro engine for adding dynamic macro loading capability.
[wine/wine-gecko.git] / programs / winhelp / winhelp.c
blob8c1f36900d72aaccb3a7af9d7e8ad31f83633f56
1 /*
2 * Help Viewer
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>
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.
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.
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
23 #include <stdio.h>
24 #include <string.h>
25 #include "winbase.h"
26 #include "wingdi.h"
27 #include "winuser.h"
28 #include "winhelp.h"
29 #include "winhelp_res.h"
30 #include "shellapi.h"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(winhelp);
36 static BOOL WINHELP_RegisterWinClasses(void);
37 static LRESULT CALLBACK WINHELP_MainWndProc(HWND, UINT, WPARAM, LPARAM);
38 static LRESULT CALLBACK WINHELP_TextWndProc(HWND, UINT, WPARAM, LPARAM);
39 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND, UINT, WPARAM, LPARAM);
40 static void WINHELP_CheckPopup(UINT);
41 static BOOL WINHELP_SplitLines(HWND hWnd, LPSIZE);
42 static void WINHELP_InitFonts(HWND hWnd);
43 static void WINHELP_DeleteLines(WINHELP_WINDOW*);
44 static void WINHELP_DeleteWindow(WINHELP_WINDOW*);
45 static void WINHELP_SetupText(HWND hWnd);
46 static WINHELP_LINE_PART* WINHELP_IsOverLink(HWND hWnd, WPARAM wParam, LPARAM lParam);
48 WINHELP_GLOBALS Globals = {3, 0, 0, 0, 1, 0, 0};
50 static BOOL MacroTest = FALSE;
52 /***********************************************************************
54 * WinMain
56 int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
58 MSG msg;
59 LONG lHash = 0;
61 Globals.hInstance = hInstance;
63 /* Get options */
64 while (*cmdline && (*cmdline == ' ' || *cmdline == '-'))
66 CHAR option;
67 LPCSTR topic_id;
68 if (*cmdline++ == ' ') continue;
70 option = *cmdline;
71 if (option) cmdline++;
72 while (*cmdline && *cmdline == ' ') cmdline++;
73 switch (option)
75 case 'i':
76 case 'I':
77 topic_id = cmdline;
78 while (*cmdline && *cmdline != ' ') cmdline++;
79 if (*cmdline) *cmdline++ = '\0';
80 lHash = HLPFILE_Hash(topic_id);
81 break;
83 case '3':
84 case '4':
85 Globals.wVersion = option - '0';
86 break;
88 case 't':
89 MacroTest = TRUE;
90 break;
92 case 'x':
93 show = SW_HIDE;
94 Globals.isBook = FALSE;
95 break;
97 default:
98 WINE_FIXME("Unsupported cmd line: %s\n", cmdline);
99 break;
103 /* Create primary window */
104 WINHELP_RegisterWinClasses();
105 WINHELP_CreateHelpWindowByHash(cmdline, lHash, "main", FALSE, NULL, NULL, show);
107 /* Message loop */
108 while (GetMessage(&msg, 0, 0, 0))
110 TranslateMessage(&msg);
111 DispatchMessage(&msg);
113 return 0;
116 /***********************************************************************
118 * RegisterWinClasses
120 static BOOL WINHELP_RegisterWinClasses(void)
122 WNDCLASS class_main, class_button_box, class_text, class_shadow;
124 class_main.style = CS_HREDRAW | CS_VREDRAW;
125 class_main.lpfnWndProc = WINHELP_MainWndProc;
126 class_main.cbClsExtra = 0;
127 class_main.cbWndExtra = sizeof(LONG);
128 class_main.hInstance = Globals.hInstance;
129 class_main.hIcon = LoadIcon(0, IDI_APPLICATION);
130 class_main.hCursor = LoadCursor(0, IDC_ARROW);
131 class_main.hbrBackground = GetStockObject(WHITE_BRUSH);
132 class_main.lpszMenuName = 0;
133 class_main.lpszClassName = MAIN_WIN_CLASS_NAME;
135 class_button_box = class_main;
136 class_button_box.lpfnWndProc = WINHELP_ButtonBoxWndProc;
137 class_button_box.hbrBackground = GetStockObject(GRAY_BRUSH);
138 class_button_box.lpszClassName = BUTTON_BOX_WIN_CLASS_NAME;
140 class_text = class_main;
141 class_text.lpfnWndProc = WINHELP_TextWndProc;
142 class_text.lpszClassName = TEXT_WIN_CLASS_NAME;
144 class_shadow = class_main;
145 class_shadow.lpfnWndProc = DefWindowProc;
146 class_shadow.hbrBackground = GetStockObject(GRAY_BRUSH);
147 class_shadow.lpszClassName = SHADOW_WIN_CLASS_NAME;
149 return (RegisterClass(&class_main) &&
150 RegisterClass(&class_button_box) &&
151 RegisterClass(&class_text) &&
152 RegisterClass(&class_shadow));
155 typedef struct
157 WORD size;
158 WORD command;
159 LONG data;
160 LONG reserved;
161 WORD ofsFilename;
162 WORD ofsData;
163 } WINHELP,*LPWINHELP;
165 /******************************************************************
166 * WINHELP_HandleCommand
170 static LRESULT WINHELP_HandleCommand(HWND hSrcWnd, LPARAM lParam)
172 COPYDATASTRUCT* cds = (COPYDATASTRUCT*)lParam;
173 WINHELP* wh;
175 if (cds->dwData != 0xA1DE505)
177 WINE_FIXME("Wrong magic number (%08lx)\n", cds->dwData);
178 return 0;
181 wh = (WINHELP*)cds->lpData;
183 if (wh)
185 WINE_TRACE("Got[%u]: cmd=%u data=%08lx fn=%s\n",
186 wh->size, wh->command, wh->data,
187 wh->ofsFilename ? (LPSTR)wh + wh->ofsFilename : "");
188 switch (wh->command)
190 case HELP_QUIT:
191 MACRO_Exit();
192 break;
193 case HELP_FINDER:
194 /* in fact, should be the topic dialog box */
195 if (wh->ofsFilename)
197 MACRO_JumpHash((LPSTR)wh + wh->ofsFilename, "main", 0);
199 break;
200 case HELP_CONTEXT:
201 case HELP_SETCONTENTS:
202 case HELP_CONTENTS:
203 case HELP_CONTEXTPOPUP:
204 case HELP_FORCEFILE:
205 case HELP_HELPONHELP:
206 case HELP_KEY:
207 case HELP_PARTIALKEY:
208 case HELP_COMMAND:
209 case HELP_MULTIKEY:
210 case HELP_SETWINPOS:
211 WINE_FIXME("NIY\n");
212 break;
215 return 0L;
218 /***********************************************************************
220 * WINHELP_CreateHelpWindow
223 static BOOL WINHELP_CreateHelpWindow(HLPFILE_PAGE* page, LPCSTR lpszWindow,
224 BOOL bPopup, HWND hParentWnd, LPPOINT mouse, INT nCmdShow)
226 CHAR szCaption[MAX_STRING_LEN];
227 SIZE size = {CW_USEDEFAULT, 0/*CW_USEDEFAULT*/};
228 POINT origin = {240, 0};
229 LPSTR ptr;
230 WINHELP_WINDOW *win, *oldwin;
231 HLPFILE_MACRO *macro;
232 HWND hWnd;
233 BOOL bPrimary;
235 if (bPopup)
236 lpszWindow = NULL;
237 else if (!lpszWindow || !lpszWindow[0])
238 lpszWindow = Globals.active_win->lpszName;
239 bPrimary = lpszWindow && !lstrcmpi(lpszWindow, "main");
241 /* Calculate horizontal size and position of a popup window */
242 if (bPopup)
244 RECT parent_rect;
245 GetWindowRect(hParentWnd, &parent_rect);
246 size.cx = (parent_rect.right - parent_rect.left) / 2;
247 size.cy = 10; /* need a non null value, so that border are taken into account while computing */
249 origin = *mouse;
250 ClientToScreen(hParentWnd, &origin);
251 origin.x -= size.cx / 2;
252 origin.x = min(origin.x, GetSystemMetrics(SM_CXSCREEN) - size.cx);
253 origin.x = max(origin.x, 0);
256 /* Initialize WINHELP_WINDOW struct */
257 win = HeapAlloc(GetProcessHeap(), 0,
258 sizeof(WINHELP_WINDOW) + (lpszWindow ? strlen(lpszWindow) + 1 : 0));
259 if (!win) return FALSE;
261 win->next = Globals.win_list;
262 Globals.win_list = win;
263 if (lpszWindow)
265 ptr = (char*)win + sizeof(WINHELP_WINDOW);
266 lstrcpy(ptr, (LPSTR) lpszWindow);
267 win->lpszName = ptr;
269 else win->lpszName = NULL;
271 win->page = page;
272 win->first_button = 0;
273 win->first_line = 0;
274 win->hMainWnd = 0;
275 win->hButtonBoxWnd = 0;
276 win->hTextWnd = 0;
277 win->hShadowWnd = 0;
279 win->hArrowCur = LoadCursorA(0, IDC_ARROWA);
280 win->hHandCur = LoadCursorA(0, IDC_HANDA);
282 Globals.active_win = win;
284 /* Initialize default pushbuttons */
285 if (MacroTest && !bPopup)
286 MACRO_CreateButton("BTN_TEST", "&Test", "MacroTest");
287 if (bPrimary && page)
289 CHAR buffer[MAX_STRING_LEN];
291 LoadString(Globals.hInstance, STID_CONTENTS, buffer, sizeof(buffer));
292 MACRO_CreateButton("BTN_CONTENTS", buffer, "Contents()");
293 LoadString(Globals.hInstance, STID_SEARCH,buffer, sizeof(buffer));
294 MACRO_CreateButton("BTN_SEARCH", buffer, "Search()");
295 LoadString(Globals.hInstance, STID_BACK, buffer, sizeof(buffer));
296 MACRO_CreateButton("BTN_BACK", buffer, "Back()");
297 LoadString(Globals.hInstance, STID_HISTORY, buffer, sizeof(buffer));
298 MACRO_CreateButton("BTN_HISTORY", buffer, "History()");
299 LoadString(Globals.hInstance, STID_TOPICS, buffer, sizeof(buffer));
300 MACRO_CreateButton("BTN_TOPICS", buffer, "Finder()");
303 /* Initialize file specific pushbuttons */
304 if (!bPopup && page)
305 for (macro = page->file->first_macro; macro; macro = macro->next)
306 MACRO_ExecuteMacro(macro->lpszMacro);
308 /* Reuse existing window */
309 if (lpszWindow)
310 for (oldwin = win->next; oldwin; oldwin = oldwin->next)
311 if (oldwin->lpszName && !lstrcmpi(oldwin->lpszName, lpszWindow))
313 WINHELP_BUTTON *button;
315 win->hMainWnd = oldwin->hMainWnd;
316 win->hButtonBoxWnd = oldwin->hButtonBoxWnd;
317 win->hTextWnd = oldwin->hTextWnd;
318 oldwin->hMainWnd = oldwin->hButtonBoxWnd = oldwin->hTextWnd = 0;
320 SetWindowLong(win->hMainWnd, 0, (LONG) win);
321 SetWindowLong(win->hButtonBoxWnd, 0, (LONG) win);
322 SetWindowLong(win->hTextWnd, 0, (LONG) win);
324 WINHELP_InitFonts(win->hMainWnd);
326 if (page) {
327 SetWindowText(win->hMainWnd, page->file->lpszTitle);
330 WINHELP_SetupText(win->hTextWnd);
331 InvalidateRect(win->hTextWnd, NULL, TRUE);
332 SendMessage(win->hMainWnd, WM_USER, 0, 0);
333 ShowWindow(win->hMainWnd, nCmdShow);
334 UpdateWindow(win->hTextWnd);
337 for (button = oldwin->first_button; button; button = button->next)
338 DestroyWindow(button->hWnd);
340 WINHELP_DeleteWindow(oldwin);
341 return TRUE;
344 /* Create main Window */
345 if (!page) LoadString(Globals.hInstance, STID_WINE_HELP, szCaption, sizeof(szCaption));
346 hWnd = CreateWindow(bPopup ? TEXT_WIN_CLASS_NAME : MAIN_WIN_CLASS_NAME,
347 page ? page->file->lpszTitle : szCaption,
348 bPopup ? WS_POPUPWINDOW | WS_BORDER : WS_OVERLAPPEDWINDOW,
349 origin.x, origin.y, size.cx, size.cy,
350 0, bPrimary ? LoadMenu(Globals.hInstance, MAKEINTRESOURCE(MAIN_MENU)) : 0,
351 Globals.hInstance, win);
353 ShowWindow(hWnd, nCmdShow);
354 UpdateWindow(hWnd);
356 return TRUE;
359 /***********************************************************************
361 * WINHELP_CreateHelpWindowByPage
363 BOOL WINHELP_CreateHelpWindowByPage(HLPFILE_PAGE* page, LPCSTR lpszWindow,
364 BOOL bPopup, HWND hParentWnd, LPPOINT mouse, INT nCmdShow)
366 if (page) page->file->wRefCount++;
367 return WINHELP_CreateHelpWindow(page, lpszWindow, bPopup, hParentWnd, mouse, nCmdShow);
370 /***********************************************************************
372 * WINHELP_CreateHelpWindowByHash
374 BOOL WINHELP_CreateHelpWindowByHash(LPCSTR lpszFile, LONG lHash, LPCSTR lpszWindow,
375 BOOL bPopup, HWND hParentWnd, LPPOINT mouse, INT nCmdShow)
377 HLPFILE_PAGE* page = NULL;
379 /* Read help file */
380 if (lpszFile[0])
382 HLPFILE* hlpfile;
384 hlpfile = HLPFILE_ReadHlpFile(lpszFile);
386 /* Add Suffix `.hlp' */
387 if (!hlpfile && lstrcmpi(lpszFile + strlen(lpszFile) - 4, ".hlp") != 0)
389 CHAR szFile_hlp[MAX_PATHNAME_LEN];
391 lstrcpyn(szFile_hlp, lpszFile, sizeof(szFile_hlp) - 4);
392 szFile_hlp[sizeof(szFile_hlp) - 5] = '\0';
393 lstrcat(szFile_hlp, ".hlp");
395 hlpfile = HLPFILE_ReadHlpFile(szFile_hlp);
397 if (!hlpfile)
399 WINHELP_MessageBoxIDS_s(STID_HLPFILE_ERROR_s, lpszFile, STID_WHERROR, MB_OK);
400 if (Globals.win_list) return FALSE;
402 else
403 page = lHash ? HLPFILE_PageByHash(hlpfile, lHash) :
404 HLPFILE_Contents(hlpfile);
406 else page = NULL;
407 return WINHELP_CreateHelpWindowByPage(page, lpszWindow, bPopup, hParentWnd, mouse, nCmdShow);
410 /***********************************************************************
412 * WINHELP_MainWndProc
414 static LRESULT CALLBACK WINHELP_MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
416 WINHELP_WINDOW *win;
417 WINHELP_BUTTON *button;
418 RECT rect, button_box_rect;
419 INT text_top;
421 WINHELP_CheckPopup(msg);
423 switch (msg)
425 case WM_NCCREATE:
426 win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
427 SetWindowLong(hWnd, 0, (LONG) win);
428 win->hMainWnd = hWnd;
429 break;
431 case WM_CREATE:
432 win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
434 /* Create button box and text Window */
435 CreateWindow(BUTTON_BOX_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
436 0, 0, 0, 0, hWnd, 0, Globals.hInstance, win);
438 CreateWindow(TEXT_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
439 0, 0, 0, 0, hWnd, 0, Globals.hInstance, win);
441 /* Fall through */
442 case WM_USER:
443 case WM_WINDOWPOSCHANGED:
444 win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
445 GetClientRect(hWnd, &rect);
447 /* Update button box and text Window */
448 SetWindowPos(win->hButtonBoxWnd, HWND_TOP,
449 rect.left, rect.top,
450 rect.right - rect.left,
451 rect.bottom - rect.top, 0);
453 GetWindowRect(win->hButtonBoxWnd, &button_box_rect);
454 text_top = rect.top + button_box_rect.bottom - button_box_rect.top;
456 SetWindowPos(win->hTextWnd, HWND_TOP,
457 rect.left, text_top,
458 rect.right - rect.left,
459 rect.bottom - text_top, 0);
461 break;
463 case WM_COMMAND:
464 Globals.active_win = win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
465 switch (wParam)
467 /* Menu FILE */
468 case MNID_FILE_OPEN: MACRO_FileOpen(); break;
469 case MNID_FILE_PRINT: MACRO_Print(); break;
470 case MNID_FILE_SETUP: MACRO_PrinterSetup(); break;
471 case MNID_FILE_EXIT: MACRO_Exit(); break;
473 /* Menu EDIT */
474 case MNID_EDIT_COPYDLG: MACRO_CopyDialog(); break;
475 case MNID_EDIT_ANNOTATE:MACRO_Annotate(); break;
477 /* Menu Bookmark */
478 case MNID_BKMK_DEFINE: MACRO_BookmarkDefine(); break;
480 /* Menu Help */
481 case MNID_HELP_HELPON: MACRO_HelpOn(); break;
482 case MNID_HELP_HELPTOP: MACRO_HelpOnTop(); break;
483 case MNID_HELP_ABOUT: MACRO_About(); break;
484 case MNID_HELP_WINE: ShellAbout(hWnd, "WINE", "Help", 0); break;
486 default:
487 /* Buttons */
488 for (button = win->first_button; button; button = button->next)
489 if (wParam == button->wParam) break;
490 if (button)
491 MACRO_ExecuteMacro(button->lpszMacro);
492 else
493 WINHELP_MessageBoxIDS(STID_NOT_IMPLEMENTED, 0x121, MB_OK);
494 break;
496 break;
497 case WM_DESTROY:
498 if (Globals.hPopupWnd) DestroyWindow(Globals.hPopupWnd);
499 break;
500 case WM_COPYDATA:
501 return WINHELP_HandleCommand((HWND)wParam, lParam);
503 return DefWindowProc(hWnd, msg, wParam, lParam);
506 /***********************************************************************
508 * WINHELP_ButtonBoxWndProc
510 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
512 WINDOWPOS *winpos;
513 WINHELP_WINDOW *win;
514 WINHELP_BUTTON *button;
515 SIZE button_size;
516 INT x, y;
518 WINHELP_CheckPopup(msg);
520 switch (msg)
522 case WM_NCCREATE:
523 win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
524 SetWindowLong(hWnd, 0, (LONG) win);
525 win->hButtonBoxWnd = hWnd;
526 break;
528 case WM_WINDOWPOSCHANGING:
529 winpos = (WINDOWPOS*) lParam;
530 win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
532 /* Update buttons */
533 button_size.cx = 0;
534 button_size.cy = 0;
535 for (button = win->first_button; button; button = button->next)
537 HDC hDc;
538 SIZE textsize;
539 if (!button->hWnd)
540 button->hWnd = CreateWindow(STRING_BUTTON, (LPSTR) button->lpszName,
541 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
542 0, 0, 0, 0,
543 hWnd, (HMENU) button->wParam,
544 Globals.hInstance, 0);
545 hDc = GetDC(button->hWnd);
546 GetTextExtentPoint(hDc, button->lpszName,
547 lstrlen(button->lpszName), &textsize);
548 ReleaseDC(button->hWnd, hDc);
550 button_size.cx = max(button_size.cx, textsize.cx + BUTTON_CX);
551 button_size.cy = max(button_size.cy, textsize.cy + BUTTON_CY);
554 x = 0;
555 y = 0;
556 for (button = win->first_button; button; button = button->next)
558 SetWindowPos(button->hWnd, HWND_TOP, x, y, button_size.cx, button_size.cy, 0);
560 if (x + 2 * button_size.cx <= winpos->cx)
561 x += button_size.cx;
562 else
563 x = 0, y += button_size.cy;
565 winpos->cy = y + (x ? button_size.cy : 0);
566 break;
568 case WM_COMMAND:
569 SendMessage(GetParent(hWnd), msg, wParam, lParam);
570 break;
573 return DefWindowProc(hWnd, msg, wParam, lParam);
576 /***********************************************************************
578 * WINHELP_TextWndProc
580 static LRESULT CALLBACK WINHELP_TextWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
582 WINHELP_WINDOW *win;
583 WINHELP_LINE *line;
584 WINHELP_LINE_PART *part;
585 WINDOWPOS *winpos;
586 PAINTSTRUCT ps;
587 HDC hDc;
588 POINT mouse;
589 INT scroll_pos;
590 HWND hPopupWnd;
591 BOOL bExit;
593 if (msg != WM_LBUTTONDOWN)
594 WINHELP_CheckPopup(msg);
596 switch (msg)
598 case WM_NCCREATE:
599 win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
600 SetWindowLong(hWnd, 0, (LONG) win);
601 win->hTextWnd = hWnd;
602 if (!win->lpszName) Globals.hPopupWnd = win->hMainWnd = hWnd;
603 WINHELP_InitFonts(hWnd);
604 break;
606 case WM_CREATE:
607 win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
609 /* Calculate vertical size and position of a popup window */
610 if (!win->lpszName)
612 POINT origin;
613 RECT old_window_rect;
614 RECT old_client_rect;
615 SIZE old_window_size;
616 SIZE old_client_size;
617 SIZE new_client_size;
618 SIZE new_window_size;
620 GetWindowRect(hWnd, &old_window_rect);
621 origin.x = old_window_rect.left;
622 origin.y = old_window_rect.top;
623 old_window_size.cx = old_window_rect.right - old_window_rect.left;
624 old_window_size.cy = old_window_rect.bottom - old_window_rect.top;
626 GetClientRect(hWnd, &old_client_rect);
627 old_client_size.cx = old_client_rect.right - old_client_rect.left;
628 old_client_size.cy = old_client_rect.bottom - old_client_rect.top;
630 new_client_size = old_client_size;
631 WINHELP_SplitLines(hWnd, &new_client_size);
633 if (origin.y + POPUP_YDISTANCE + new_client_size.cy <= GetSystemMetrics(SM_CYSCREEN))
634 origin.y += POPUP_YDISTANCE;
635 else
636 origin.y -= POPUP_YDISTANCE + new_client_size.cy;
638 new_window_size.cx = old_window_size.cx - old_client_size.cx + new_client_size.cx;
639 new_window_size.cy = old_window_size.cy - old_client_size.cy + new_client_size.cy;
641 win->hShadowWnd =
642 CreateWindow(SHADOW_WIN_CLASS_NAME, "", WS_POPUP,
643 origin.x + SHADOW_DX, origin.y + SHADOW_DY,
644 new_window_size.cx, new_window_size.cy,
645 0, 0, Globals.hInstance, 0);
647 SetWindowPos(hWnd, HWND_TOP, origin.x, origin.y,
648 new_window_size.cx, new_window_size.cy,
650 SetWindowPos(win->hShadowWnd, hWnd, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
651 ShowWindow(win->hShadowWnd, SW_NORMAL);
652 SetActiveWindow(hWnd);
654 break;
656 case WM_WINDOWPOSCHANGED:
657 winpos = (WINDOWPOS*) lParam;
659 if (!(winpos->flags & SWP_NOSIZE)) WINHELP_SetupText(hWnd);
660 break;
662 case WM_VSCROLL:
664 BOOL update = TRUE;
665 RECT rect;
666 INT Min, Max;
667 INT CurPos = GetScrollPos(hWnd, SB_VERT);
668 GetScrollRange(hWnd, SB_VERT, &Min, &Max);
669 GetClientRect(hWnd, &rect);
671 switch (wParam & 0xffff)
673 case SB_THUMBTRACK:
674 case SB_THUMBPOSITION: CurPos = wParam >> 16; break;
675 case SB_TOP: CurPos = Min; break;
676 case SB_BOTTOM: CurPos = Max; break;
677 case SB_PAGEUP: CurPos -= (rect.bottom - rect.top) / 2; break;
678 case SB_PAGEDOWN: CurPos += (rect.bottom - rect.top) / 2; break;
679 case SB_LINEUP: CurPos -= GetSystemMetrics(SM_CXVSCROLL); break;
680 case SB_LINEDOWN: CurPos += GetSystemMetrics(SM_CXVSCROLL); break;
681 default: update = FALSE;
683 if (update)
685 INT dy = GetScrollPos(hWnd, SB_VERT) - CurPos;
686 SetScrollPos(hWnd, SB_VERT, CurPos, TRUE);
687 ScrollWindow(hWnd, 0, dy, NULL, NULL);
688 UpdateWindow(hWnd);
691 break;
693 case WM_PAINT:
694 hDc = BeginPaint(hWnd, &ps);
695 win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
696 scroll_pos = GetScrollPos(hWnd, SB_VERT);
698 for (line = win->first_line; line; line = line->next)
700 for (part = &line->first_part; part; part = part->next)
702 switch (part->cookie)
704 case hlp_line_part_text:
705 SelectObject(hDc, part->u.text.hFont);
706 SetTextColor(hDc, part->u.text.color);
707 TextOut(hDc, part->rect.left, part->rect.top - scroll_pos,
708 part->u.text.lpsText, part->u.text.wTextLen);
709 if (part->u.text.wUnderline)
711 HPEN hPen;
713 switch (part->u.text.wUnderline)
715 case 1: /* simple */
716 case 2: /* double */
717 hPen = CreatePen(PS_SOLID, 1, part->u.text.color);
718 break;
719 case 3: /* dotted */
720 hPen = CreatePen(PS_DOT, 1, part->u.text.color);
721 break;
722 default:
723 WINE_FIXME("Unknow underline type\n");
724 continue;
727 SelectObject(hDc, hPen);
728 MoveToEx(hDc, part->rect.left, part->rect.bottom - scroll_pos - 1, NULL);
729 LineTo(hDc, part->rect.right, part->rect.bottom - scroll_pos - 1);
730 if (part->u.text.wUnderline == 2)
732 MoveToEx(hDc, part->rect.left, part->rect.bottom - scroll_pos + 1, NULL);
733 LineTo(hDc, part->rect.right, part->rect.bottom - scroll_pos + 1);
735 DeleteObject(hPen);
737 break;
738 case hlp_line_part_image:
740 HDC hMemDC;
742 hMemDC = CreateCompatibleDC(hDc);
743 SelectObject(hMemDC, part->u.image.hBitmap);
744 BitBlt(hDc, part->rect.left, part->rect.top - scroll_pos,
745 part->rect.right - part->rect.left, part->rect.bottom - part->rect.top,
746 hMemDC, 0, 0, SRCCOPY);
747 DeleteDC(hMemDC);
749 break;
754 EndPaint(hWnd, &ps);
755 break;
757 case WM_MOUSEMOVE:
758 win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
760 if (WINHELP_IsOverLink(hWnd, wParam, lParam))
761 SetCursor(win->hHandCur); /* set to hand pointer cursor to indicate a link */
762 else
763 SetCursor(win->hArrowCur); /* set to hand pointer cursor to indicate a link */
765 break;
767 case WM_LBUTTONDOWN:
768 win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
770 hPopupWnd = Globals.hPopupWnd;
771 Globals.hPopupWnd = 0;
773 part = WINHELP_IsOverLink(hWnd, wParam, lParam);
774 if (part)
776 mouse.x = LOWORD(lParam);
777 mouse.y = HIWORD(lParam);
779 switch (part->link.cookie)
781 case hlp_link_none:
782 break;
783 case hlp_link_link:
784 WINHELP_CreateHelpWindowByHash(part->link.lpszString, part->link.lHash, NULL,
785 FALSE, hWnd, &mouse, SW_NORMAL);
786 break;
787 case hlp_link_popup:
788 WINHELP_CreateHelpWindowByHash(part->link.lpszString, part->link.lHash, NULL,
789 TRUE, hWnd, &mouse, SW_NORMAL);
790 break;
791 case hlp_link_macro:
792 MACRO_ExecuteMacro(part->link.lpszString);
793 break;
794 default:
795 WINE_FIXME("Unknown link cookie %d\n", part->link.cookie);
799 if (hPopupWnd)
800 DestroyWindow(hPopupWnd);
801 break;
803 case WM_NCDESTROY:
804 win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
806 if (hWnd == Globals.hPopupWnd) Globals.hPopupWnd = 0;
807 if (win->hShadowWnd) DestroyWindow(win->hShadowWnd);
809 bExit = (Globals.wVersion >= 4 && !lstrcmpi(win->lpszName, "main"));
811 WINHELP_DeleteWindow(win);
813 if (bExit) MACRO_Exit();
815 if (!Globals.win_list)
816 PostQuitMessage(0);
817 break;
820 return DefWindowProc(hWnd, msg, wParam, lParam);
823 /***********************************************************************
825 * SetupText
827 static void WINHELP_SetupText(HWND hWnd)
829 HDC hDc = GetDC(hWnd);
830 RECT rect;
831 SIZE newsize;
833 ShowScrollBar(hWnd, SB_VERT, FALSE);
834 if (!WINHELP_SplitLines(hWnd, NULL))
836 ShowScrollBar(hWnd, SB_VERT, TRUE);
837 GetClientRect(hWnd, &rect);
839 WINHELP_SplitLines(hWnd, &newsize);
840 SetScrollRange(hWnd, SB_VERT, 0, rect.top + newsize.cy - rect.bottom, TRUE);
842 else SetScrollPos(hWnd, SB_VERT, 0, FALSE);
844 ReleaseDC(hWnd, hDc);
847 /***********************************************************************
849 * WINHELP_AppendText
851 static BOOL WINHELP_AppendText(WINHELP_LINE ***linep, WINHELP_LINE_PART ***partp,
852 LPSIZE space, LPSIZE textsize,
853 INT *line_ascent, INT ascent,
854 LPCSTR text, UINT textlen,
855 HFONT font, COLORREF color, HLPFILE_LINK *link,
856 unsigned underline)
858 WINHELP_LINE *line;
859 WINHELP_LINE_PART *part;
860 LPSTR ptr;
862 if (!*partp) /* New line */
864 *line_ascent = ascent;
866 line = HeapAlloc(GetProcessHeap(), 0,
867 sizeof(WINHELP_LINE) + textlen + (link ? lstrlen(link->lpszString) + 1 : 0));
868 if (!line) return FALSE;
870 line->next = 0;
871 part = &line->first_part;
872 ptr = (char*)line + sizeof(WINHELP_LINE);
874 line->rect.top = (**linep ? (**linep)->rect.bottom : 0) + space->cy;
875 line->rect.bottom = line->rect.top;
876 line->rect.left = space->cx;
877 line->rect.right = space->cx;
879 if (**linep) *linep = &(**linep)->next;
880 **linep = line;
881 space->cy = 0;
883 else /* Same line */
885 line = **linep;
887 if (*line_ascent < ascent)
889 WINHELP_LINE_PART *p;
890 for (p = &line->first_part; p; p = p->next)
892 p->rect.top += ascent - *line_ascent;
893 p->rect.bottom += ascent - *line_ascent;
895 line->rect.bottom += ascent - *line_ascent;
896 *line_ascent = ascent;
899 part = HeapAlloc(GetProcessHeap(), 0,
900 sizeof(WINHELP_LINE_PART) + textlen +
901 (link ? lstrlen(link->lpszString) + 1 : 0));
902 if (!part) return FALSE;
903 **partp = part;
904 ptr = (char*)part + sizeof(WINHELP_LINE_PART);
907 memcpy(ptr, text, textlen);
908 part->cookie = hlp_line_part_text;
909 part->rect.left = line->rect.right + (*partp ? space->cx : 0);
910 part->rect.right = part->rect.left + textsize->cx;
911 line->rect.right = part->rect.right;
912 part->rect.top =
913 ((*partp) ? line->rect.top : line->rect.bottom) + *line_ascent - ascent;
914 part->rect.bottom = part->rect.top + textsize->cy;
915 line->rect.bottom = max(line->rect.bottom, part->rect.bottom);
916 part->u.text.lpsText = ptr;
917 part->u.text.wTextLen = textlen;
918 part->u.text.hFont = font;
919 part->u.text.color = color;
920 part->u.text.wUnderline = underline;
922 WINE_TRACE("Appended text '%*.*s'[%d] @ (%d,%d-%d,%d)\n",
923 part->u.text.wTextLen,
924 part->u.text.wTextLen,
925 part->u.text.lpsText,
926 part->u.text.wTextLen,
927 part->rect.left, part->rect.top, part->rect.right, part->rect.bottom);
928 if (link)
930 strcpy(ptr + textlen, link->lpszString);
931 part->link.lpszString = ptr + textlen;
932 part->link.cookie = link->cookie;
933 part->link.lHash = link->lHash;
934 part->link.bClrChange = link->bClrChange;
936 else part->link.cookie = hlp_link_none;
938 part->next = 0;
939 *partp = &part->next;
941 space->cx = 0;
943 return TRUE;
946 /***********************************************************************
948 * WINHELP_AppendBitmap
950 static BOOL WINHELP_AppendBitmap(WINHELP_LINE ***linep, WINHELP_LINE_PART ***partp,
951 LPSIZE space,
952 HBITMAP hBmp, LPSIZE bmpSize,
953 HLPFILE_LINK *link, unsigned pos)
955 WINHELP_LINE *line;
956 WINHELP_LINE_PART *part;
957 LPSTR ptr;
959 if (!*partp || pos == 1) /* New line */
961 line = HeapAlloc(GetProcessHeap(), 0,
962 sizeof(WINHELP_LINE) + (link ? lstrlen(link->lpszString) + 1 : 0));
963 if (!line) return FALSE;
965 line->next = NULL;
966 part = &line->first_part;
968 line->rect.top = (**linep ? (**linep)->rect.bottom : 0) + space->cy;
969 line->rect.bottom = line->rect.top;
970 line->rect.left = space->cx;
971 line->rect.right = space->cx;
973 if (**linep) *linep = &(**linep)->next;
974 **linep = line;
975 space->cy = 0;
976 ptr = (char*)line + sizeof(WINHELP_LINE);
978 else /* Same line */
980 if (pos == 2) WINE_FIXME("Left alignment not handled\n");
981 line = **linep;
983 part = HeapAlloc(GetProcessHeap(), 0,
984 sizeof(WINHELP_LINE_PART) +
985 (link ? lstrlen(link->lpszString) + 1 : 0));
986 if (!part) return FALSE;
987 **partp = part;
988 ptr = (char*)part + sizeof(WINHELP_LINE_PART);
991 part->cookie = hlp_line_part_image;
992 part->rect.left = line->rect.right + (*partp ? space->cx : 0);
993 part->rect.right = part->rect.left + bmpSize->cx;
994 line->rect.right = part->rect.right;
995 part->rect.top =
996 ((*partp) ? line->rect.top : line->rect.bottom);
997 part->rect.bottom = part->rect.top + bmpSize->cy;
998 line->rect.bottom = max(line->rect.bottom, part->rect.bottom);
999 part->u.image.hBitmap = hBmp;
1001 WINE_TRACE("Appended bitmap '%d' @ (%d,%d-%d,%d)\n",
1002 (unsigned)part->u.image.hBitmap,
1003 part->rect.left, part->rect.top, part->rect.right, part->rect.bottom);
1005 if (link)
1007 strcpy(ptr, link->lpszString);
1008 part->link.lpszString = ptr;
1009 part->link.cookie = link->cookie;
1010 part->link.lHash = link->lHash;
1011 part->link.bClrChange = link->bClrChange;
1013 else part->link.cookie = hlp_link_none;
1015 part->next = NULL;
1016 *partp = &part->next;
1018 space->cx = 0;
1020 return TRUE;
1024 /***********************************************************************
1026 * WINHELP_SplitLines
1028 static BOOL WINHELP_SplitLines(HWND hWnd, LPSIZE newsize)
1030 WINHELP_WINDOW *win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1031 HLPFILE_PARAGRAPH *p;
1032 WINHELP_LINE **line = &win->first_line;
1033 WINHELP_LINE_PART **part = 0;
1034 INT line_ascent = 0;
1035 SIZE space;
1036 RECT rect;
1037 HDC hDc;
1039 if (newsize) newsize->cx = newsize->cy = 0;
1041 if (!win->page) return TRUE;
1043 WINHELP_DeleteLines(win);
1045 GetClientRect(hWnd, &rect);
1047 rect.top += INTERNAL_BORDER_WIDTH;
1048 rect.left += INTERNAL_BORDER_WIDTH;
1049 rect.right -= INTERNAL_BORDER_WIDTH;
1050 rect.bottom -= INTERNAL_BORDER_WIDTH;
1052 space.cy = rect.top;
1053 space.cx = rect.left;
1055 hDc = GetDC(hWnd);
1057 for (p = win->page->first_paragraph; p; p = p->next)
1059 switch (p->cookie)
1061 case para_normal_text:
1062 case para_debug_text:
1064 TEXTMETRIC tm;
1065 SIZE textsize = {0, 0};
1066 LPCSTR text = p->u.text.lpszText;
1067 UINT indent = 0;
1068 UINT len = strlen(text);
1069 unsigned underline = 0;
1071 HFONT hFont = 0;
1072 COLORREF color = RGB(0, 0, 0);
1074 if (p->u.text.wFont < win->page->file->numFonts)
1076 HLPFILE* hlpfile = win->page->file;
1078 if (!hlpfile->fonts[p->u.text.wFont].hFont)
1079 hlpfile->fonts[p->u.text.wFont].hFont = CreateFontIndirect(&hlpfile->fonts[p->u.text.wFont].LogFont);
1080 hFont = hlpfile->fonts[p->u.text.wFont].hFont;
1081 color = hlpfile->fonts[p->u.text.wFont].color;
1083 else
1085 UINT wFont = (p->u.text.wFont < win->fonts_len) ? p->u.text.wFont : 0;
1087 hFont = win->fonts[wFont];
1090 if (p->link)
1092 underline = (p->link->cookie == hlp_link_popup) ? 3 : 1;
1093 color = RGB(0, 0x80, 0);
1095 if (p->cookie == para_debug_text) color = RGB(0xff, 0, 0);
1097 SelectObject(hDc, hFont);
1099 GetTextMetrics(hDc, &tm);
1101 if (p->u.text.wIndent)
1103 indent = p->u.text.wIndent * 5 * tm.tmAveCharWidth;
1104 if (!part)
1105 space.cx = rect.left + indent - 2 * tm.tmAveCharWidth;
1108 if (p->u.text.wVSpace)
1110 part = 0;
1111 space.cx = rect.left + indent;
1112 space.cy += (p->u.text.wVSpace - 1) * tm.tmHeight;
1115 if (p->u.text.wHSpace)
1117 space.cx += p->u.text.wHSpace * 2 * tm.tmAveCharWidth;
1120 WINE_TRACE("splitting text %s\n", text);
1122 while (len)
1124 INT free_width = rect.right - (part ? (*line)->rect.right : rect.left) - space.cx;
1125 UINT low = 0, curr = len, high = len, textlen = 0;
1127 if (free_width > 0)
1129 while (1)
1131 GetTextExtentPoint(hDc, text, curr, &textsize);
1133 if (textsize.cx <= free_width) low = curr;
1134 else high = curr;
1136 if (high <= low + 1) break;
1138 if (textsize.cx) curr = (curr * free_width) / textsize.cx;
1139 if (curr <= low) curr = low + 1;
1140 else if (curr >= high) curr = high - 1;
1142 textlen = low;
1143 while (textlen && text[textlen] && text[textlen] != ' ') textlen--;
1145 if (!part && !textlen) textlen = max(low, 1);
1147 if (free_width <= 0 || !textlen)
1149 part = 0;
1150 space.cx = rect.left + indent;
1151 space.cx = min(space.cx, rect.right - rect.left - 1);
1152 continue;
1155 WINE_TRACE("\t => %d %*s\n", textlen, textlen, text);
1157 if (!WINHELP_AppendText(&line, &part, &space, &textsize,
1158 &line_ascent, tm.tmAscent,
1159 text, textlen, hFont, color, p->link, underline) ||
1160 (!newsize && (*line)->rect.bottom > rect.bottom))
1162 ReleaseDC(hWnd, hDc);
1163 return FALSE;
1166 if (newsize)
1167 newsize->cx = max(newsize->cx, (*line)->rect.right + INTERNAL_BORDER_WIDTH);
1169 len -= textlen;
1170 text += textlen;
1171 if (text[0] == ' ') text++, len--;
1174 break;
1175 case para_image:
1177 DIBSECTION dibs;
1178 SIZE bmpSize;
1179 INT free_width;
1181 if (p->u.image.pos & 0x8000)
1183 space.cx = rect.left;
1184 if (*line)
1185 space.cy += (*line)->rect.bottom - (*line)->rect.top;
1186 part = 0;
1189 GetObject(p->u.image.hBitmap, sizeof(dibs), &dibs);
1190 bmpSize.cx = dibs.dsBm.bmWidth;
1191 bmpSize.cy = dibs.dsBm.bmHeight;
1193 free_width = rect.right - ((part && *line) ? (*line)->rect.right : rect.left) - space.cx;
1194 if (free_width <= 0)
1196 part = NULL;
1197 space.cx = rect.left;
1198 space.cx = min(space.cx, rect.right - rect.left - 1);
1200 if (!WINHELP_AppendBitmap(&line, &part, &space,
1201 p->u.image.hBitmap, &bmpSize,
1202 p->link, p->u.image.pos) ||
1203 (!newsize && (*line)->rect.bottom > rect.bottom))
1205 return FALSE;
1208 break;
1213 if (newsize)
1214 newsize->cy = (*line)->rect.bottom + INTERNAL_BORDER_WIDTH;
1216 ReleaseDC(hWnd, hDc);
1217 return TRUE;
1220 /***********************************************************************
1222 * WINHELP_CheckPopup
1224 static void WINHELP_CheckPopup(UINT msg)
1226 if (!Globals.hPopupWnd) return;
1228 switch (msg)
1230 case WM_COMMAND:
1231 case WM_LBUTTONDOWN:
1232 case WM_MBUTTONDOWN:
1233 case WM_RBUTTONDOWN:
1234 case WM_NCLBUTTONDOWN:
1235 case WM_NCMBUTTONDOWN:
1236 case WM_NCRBUTTONDOWN:
1237 DestroyWindow(Globals.hPopupWnd);
1238 Globals.hPopupWnd = 0;
1242 /***********************************************************************
1244 * WINHELP_DeleteLines
1246 static void WINHELP_DeleteLines(WINHELP_WINDOW *win)
1248 WINHELP_LINE *line, *next_line;
1249 WINHELP_LINE_PART *part, *next_part;
1250 for (line = win->first_line; line; line = next_line)
1252 next_line = line->next;
1253 for (part = &line->first_part; part; part = next_part)
1255 next_part = part->next;
1256 HeapFree(GetProcessHeap(), 0, part);
1259 win->first_line = 0;
1262 /***********************************************************************
1264 * WINHELP_DeleteWindow
1266 static void WINHELP_DeleteWindow(WINHELP_WINDOW *win)
1268 WINHELP_WINDOW **w;
1270 for (w = &Globals.win_list; *w; w = &(*w)->next)
1271 if (*w == win)
1273 *w = win->next;
1274 break;
1277 if (win->hShadowWnd) DestroyWindow(win->hShadowWnd);
1278 HLPFILE_FreeHlpFilePage(win->page);
1279 WINHELP_DeleteLines(win);
1280 HeapFree(GetProcessHeap(), 0, win);
1283 /***********************************************************************
1285 * WINHELP_InitFonts
1287 static void WINHELP_InitFonts(HWND hWnd)
1289 WINHELP_WINDOW *win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1290 LOGFONT logfontlist[] = {
1291 {-10, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1292 {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1293 {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1294 {-12, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1295 {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1296 {-10, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1297 { -8, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"}};
1298 #define FONTS_LEN (sizeof(logfontlist)/sizeof(*logfontlist))
1300 static HFONT fonts[FONTS_LEN];
1301 static BOOL init = 0;
1303 win->fonts_len = FONTS_LEN;
1304 win->fonts = fonts;
1306 if (!init)
1308 INT i;
1310 for(i = 0; i < FONTS_LEN; i++)
1312 fonts[i] = CreateFontIndirect(&logfontlist[i]);
1315 init = 1;
1319 /***********************************************************************
1321 * WINHELP_MessageBoxIDS
1323 INT WINHELP_MessageBoxIDS(UINT ids_text, UINT ids_title, WORD type)
1325 CHAR text[MAX_STRING_LEN];
1326 CHAR title[MAX_STRING_LEN];
1328 LoadString(Globals.hInstance, ids_text, text, sizeof(text));
1329 LoadString(Globals.hInstance, ids_title, title, sizeof(title));
1331 return MessageBox(0, text, title, type);
1334 /***********************************************************************
1336 * MAIN_MessageBoxIDS_s
1338 INT WINHELP_MessageBoxIDS_s(UINT ids_text, LPCSTR str, UINT ids_title, WORD type)
1340 CHAR text[MAX_STRING_LEN];
1341 CHAR title[MAX_STRING_LEN];
1342 CHAR newtext[MAX_STRING_LEN + MAX_PATHNAME_LEN];
1344 LoadString(Globals.hInstance, ids_text, text, sizeof(text));
1345 LoadString(Globals.hInstance, ids_title, title, sizeof(title));
1346 wsprintf(newtext, text, str);
1348 return MessageBox(0, newtext, title, type);
1351 /******************************************************************
1352 * WINHELP_IsOverLink
1356 WINHELP_LINE_PART* WINHELP_IsOverLink(HWND hWnd, WPARAM wParam, LPARAM lParam)
1358 WINHELP_WINDOW* win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1359 POINT mouse;
1360 WINHELP_LINE *line;
1361 WINHELP_LINE_PART *part;
1362 int scroll_pos = GetScrollPos(hWnd, SB_VERT);
1364 mouse.x = LOWORD(lParam);
1365 mouse.y = HIWORD(lParam);
1366 for (line = win->first_line; line; line = line->next)
1368 for (part = &line->first_part; part; part = part->next)
1370 if (part->link.cookie != hlp_link_none &&
1371 part->link.lpszString &&
1372 part->rect.left <= mouse.x &&
1373 part->rect.right >= mouse.x &&
1374 part->rect.top <= mouse.y + scroll_pos &&
1375 part->rect.bottom >= mouse.y + scroll_pos)
1377 return part;
1382 return NULL;