1 /* 2 * Help Viewer 3 * 4 * Copyright 1996 Ulrich Schmid <uschmid@mail.hh.provi.de> 5 * 2002 Sylvain Petreolle <spetreolle@yahoo.fr> 6 * 2002, 2008 Eric Pouech <eric.pouech@wanadoo.fr> 7 * 2004 Ken Belleau <jamez@ivic.qc.ca> 8 * 2008 Kirill K. Smirnov <lich@math.spbu.ru> 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 23 */ 24 25 #include <assert.h> 26 #include <stdio.h> 27 #include <string.h> 28 #include <stdarg.h> 29 #include <stdlib.h> 30 31 #define NONAMELESSUNION 32 33 #include "windef.h" 34 #include "winbase.h" 35 #include "wingdi.h" 36 #include "winuser.h" 37 #include "commdlg.h" 38 #include "winhelp.h" 39 #include "winhelp_res.h" 40 #include "shellapi.h" 41 #include "richedit.h" 42 #include "commctrl.h" 43 44 #include "wine/debug.h" 45 46 WINE_DEFAULT_DEBUG_CHANNEL(winhelp); 47 48 WINHELP_GLOBALS Globals = {3, NULL, TRUE, NULL, NULL, NULL, NULL, NULL, {{{NULL,NULL}},0}, NULL}; 49 50 #define CTL_ID_BUTTON 0x700 51 #define CTL_ID_TEXT 0x701 52 53 54 /*********************************************************************** 55 * 56 * WINHELP_InitFonts 57 */ 58 static void WINHELP_InitFonts(HWND hWnd) 59 { 60 WINHELP_WINDOW *win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0); 61 LOGFONTW logfontlist[] = { 62 {-10, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, {'H','e','l','v',0}}, 63 {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, {'H','e','l','v',0}}, 64 {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, {'H','e','l','v',0}}, 65 {-12, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, {'H','e','l','v',0}}, 66 {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, {'H','e','l','v',0}}, 67 {-10, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, {'H','e','l','v',0}}, 68 { -8, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, {'H','e','l','v',0}}}; 69 70 static HFONT fonts[ARRAY_SIZE(logfontlist)]; 71 static BOOL init = FALSE; 72 73 win->fonts_len = ARRAY_SIZE(logfontlist); 74 win->fonts = fonts; 75 76 if (!init) 77 { 78 UINT i; 79 80 for (i = 0; i < ARRAY_SIZE(logfontlist); i++) 81 { 82 fonts[i] = CreateFontIndirectW(&logfontlist[i]); 83 } 84 85 init = TRUE; 86 } 87 } 88 89 static DWORD CALLBACK WINHELP_RtfStreamIn(DWORD_PTR cookie, BYTE* buff, 90 LONG cb, LONG* pcb) 91 { 92 struct RtfData* rd = (struct RtfData*)cookie; 93 94 if (rd->where >= rd->ptr) return 1; 95 if (rd->where + cb > rd->ptr) 96 cb = rd->ptr - rd->where; 97 memcpy(buff, rd->where, cb); 98 rd->where += cb; 99 *pcb = cb; 100 return 0; 101 } 102 103 static void WINHELP_SetupText(HWND hTextWnd, WINHELP_WINDOW* win, ULONG relative) 104 { 105 static const WCHAR emptyW[1]; 106 /* At first clear area - needed by EM_POSFROMCHAR/EM_SETSCROLLPOS */ 107 SendMessageW(hTextWnd, WM_SETTEXT, 0, (LPARAM)emptyW); 108 SendMessageW(hTextWnd, WM_SETREDRAW, FALSE, 0); 109 SendMessageW(hTextWnd, EM_SETBKGNDCOLOR, 0, (LPARAM)win->info->sr_color); 110 /* set word-wrap to window size (undocumented) */ 111 SendMessageW(hTextWnd, EM_SETTARGETDEVICE, 0, 0); 112 if (win->page) 113 { 114 struct RtfData rd; 115 EDITSTREAM es; 116 unsigned cp = 0; 117 POINTL ptl; 118 POINT pt; 119 120 121 if (HLPFILE_BrowsePage(win->page, &rd, win->font_scale, relative)) 122 { 123 rd.where = rd.data; 124 es.dwCookie = (DWORD_PTR)&rd; 125 es.dwError = 0; 126 es.pfnCallback = WINHELP_RtfStreamIn; 127 128 SendMessageW(hTextWnd, EM_STREAMIN, SF_RTF, (LPARAM)&es); 129 cp = rd.char_pos_rel; 130 } 131 /* FIXME: else leaking potentially the rd.first_link chain */ 132 HeapFree(GetProcessHeap(), 0, rd.data); 133 SendMessageW(hTextWnd, EM_POSFROMCHAR, (WPARAM)&ptl, cp ? cp - 1 : 0); 134 pt.x = 0; pt.y = ptl.y; 135 SendMessageW(hTextWnd, EM_SETSCROLLPOS, 0, (LPARAM)&pt); 136 } 137 SendMessageW(hTextWnd, WM_SETREDRAW, TRUE, 0); 138 RedrawWindow(hTextWnd, NULL, NULL, RDW_FRAME|RDW_INVALIDATE); 139 } 140 141 /*********************************************************************** 142 * 143 * WINHELP_GetOpenFileName 144 */ 145 BOOL WINHELP_GetOpenFileName(LPSTR lpszFile, int len) 146 { 147 OPENFILENAMEA openfilename; 148 CHAR szDir[MAX_PATH]; 149 CHAR szzFilter[2 * MAX_STRING_LEN + 100]; 150 LPSTR p = szzFilter; 151 152 WINE_TRACE("()\n"); 153 154 LoadStringA(Globals.hInstance, STID_HELP_FILES_HLP, p, MAX_STRING_LEN); 155 p += strlen(p) + 1; 156 strcpy(p, "*.hlp"); 157 p += strlen(p) + 1; 158 LoadStringA(Globals.hInstance, STID_ALL_FILES, p, MAX_STRING_LEN); 159 p += strlen(p) + 1; 160 strcpy(p, "*.*"); 161 p += strlen(p) + 1; 162 *p = '\0'; 163 164 GetCurrentDirectoryA(sizeof(szDir), szDir); 165 166 lpszFile[0]='\0'; 167 168 openfilename.lStructSize = sizeof(openfilename); 169 openfilename.hwndOwner = (Globals.active_win ? Globals.active_win->hMainWnd : 0); 170 openfilename.hInstance = Globals.hInstance; 171 openfilename.lpstrFilter = szzFilter; 172 openfilename.lpstrCustomFilter = 0; 173 openfilename.nMaxCustFilter = 0; 174 openfilename.nFilterIndex = 1; 175 openfilename.lpstrFile = lpszFile; 176 openfilename.nMaxFile = len; 177 openfilename.lpstrFileTitle = 0; 178 openfilename.nMaxFileTitle = 0; 179 openfilename.lpstrInitialDir = szDir; 180 openfilename.lpstrTitle = 0; 181 openfilename.Flags = OFN_EXPLORER | OFN_ENABLESIZING | OFN_HIDEREADONLY | OFN_READONLY; 182 openfilename.nFileOffset = 0; 183 openfilename.nFileExtension = 0; 184 openfilename.lpstrDefExt = 0; 185 openfilename.lCustData = 0; 186 openfilename.lpfnHook = 0; 187 openfilename.lpTemplateName = 0; 188 189 return GetOpenFileNameA(&openfilename); 190 } 191 192 /*********************************************************************** 193 * 194 * WINHELP_MessageBoxIDS_s 195 */ 196 static INT WINHELP_MessageBoxIDS_s(UINT ids_text, LPCSTR str, UINT ids_title, WORD type) 197 { 198 CHAR text[MAX_STRING_LEN]; 199 CHAR newtext[MAX_STRING_LEN + MAX_PATH]; 200 201 LoadStringA(Globals.hInstance, ids_text, text, sizeof(text)); 202 wsprintfA(newtext, text, str); 203 204 return MessageBoxA(0, newtext, MAKEINTRESOURCEA(ids_title), type); 205 } 206 207 /*********************************************************************** 208 * 209 * WINHELP_LookupHelpFile 210 */ 211 HLPFILE* WINHELP_LookupHelpFile(LPCSTR lpszFile) 212 { 213 HLPFILE* hlpfile; 214 char szFullName[MAX_PATH]; 215 char szAddPath[MAX_PATH]; 216 char *p; 217 218 /* 219 * NOTE: This is needed by popup windows only. 220 * In other cases it's not needed but does not hurt though. 221 */ 222 if (Globals.active_win && Globals.active_win->page && Globals.active_win->page->file) 223 { 224 strcpy(szAddPath, Globals.active_win->page->file->lpszPath); 225 p = strrchr(szAddPath, '\\'); 226 if (p) *p = 0; 227 } 228 229 /* 230 * FIXME: Should we swap conditions? 231 */ 232 if (!SearchPathA(NULL, lpszFile, ".hlp", MAX_PATH, szFullName, NULL) && 233 !SearchPathA(szAddPath, lpszFile, ".hlp", MAX_PATH, szFullName, NULL)) 234 { 235 if (WINHELP_MessageBoxIDS_s(STID_FILE_NOT_FOUND_s, lpszFile, STID_WHERROR, 236 MB_YESNO|MB_ICONQUESTION) != IDYES) 237 return NULL; 238 if (!WINHELP_GetOpenFileName(szFullName, MAX_PATH)) 239 return NULL; 240 } 241 hlpfile = HLPFILE_ReadHlpFile(szFullName); 242 if (!hlpfile) 243 WINHELP_MessageBoxIDS_s(STID_HLPFILE_ERROR_s, lpszFile, 244 STID_WHERROR, MB_OK|MB_ICONSTOP); 245 return hlpfile; 246 } 247 248 /****************************************************************** 249 * WINHELP_GetWindowInfo 250 * 251 * 252 */ 253 HLPFILE_WINDOWINFO* WINHELP_GetWindowInfo(HLPFILE* hlpfile, LPCSTR name) 254 { 255 static HLPFILE_WINDOWINFO mwi; 256 unsigned int i; 257 258 if (!name || !name[0]) 259 name = Globals.active_win->info->name; 260 261 if (hlpfile) 262 for (i = 0; i < hlpfile->numWindows; i++) 263 if (!lstrcmpiA(hlpfile->windows[i].name, name)) 264 return &hlpfile->windows[i]; 265 266 if (strcmp(name, "main") != 0) 267 { 268 WINE_FIXME("Couldn't find window info for %s\n", debugstr_a(name)); 269 assert(0); 270 return NULL; 271 } 272 if (!mwi.name[0]) 273 { 274 strcpy(mwi.type, "primary"); 275 strcpy(mwi.name, "main"); 276 if (hlpfile && hlpfile->lpszTitle[0]) 277 #ifdef __REACTOS__ 278 snprintf(mwi.caption, sizeof(mwi.caption), "%s", hlpfile->lpszTitle); 279 #else 280 { 281 char tmp[128]; 282 LoadStringA(Globals.hInstance, STID_WINE_HELP, tmp, sizeof(tmp)); 283 snprintf(mwi.caption, sizeof(mwi.caption), "%s %s - %s", 284 hlpfile->lpszTitle, tmp, hlpfile->lpszPath); 285 } 286 #endif 287 else 288 LoadStringA(Globals.hInstance, STID_WINE_HELP, mwi.caption, sizeof(mwi.caption)); 289 mwi.origin.x = mwi.origin.y = mwi.size.cx = mwi.size.cy = CW_USEDEFAULT; 290 mwi.style = SW_SHOW; 291 mwi.win_style = WS_OVERLAPPEDWINDOW; 292 mwi.sr_color = mwi.nsr_color = 0xFFFFFF; 293 } 294 return &mwi; 295 } 296 297 /****************************************************************** 298 * HLPFILE_GetPopupWindowInfo 299 * 300 * 301 */ 302 static HLPFILE_WINDOWINFO* WINHELP_GetPopupWindowInfo(HLPFILE* hlpfile, 303 WINHELP_WINDOW* parent, LPARAM mouse) 304 { 305 static HLPFILE_WINDOWINFO wi; 306 307 RECT parent_rect; 308 309 wi.type[0] = wi.name[0] = wi.caption[0] = '\0'; 310 311 /* Calculate horizontal size and position of a popup window */ 312 GetWindowRect(parent->hMainWnd, &parent_rect); 313 wi.size.cx = (parent_rect.right - parent_rect.left) / 2; 314 wi.size.cy = 10; /* need a non null value, so that borders are taken into account while computing */ 315 316 wi.origin.x = (short)LOWORD(mouse); 317 wi.origin.y = (short)HIWORD(mouse); 318 ClientToScreen(parent->hMainWnd, &wi.origin); 319 wi.origin.x -= wi.size.cx / 2; 320 wi.origin.x = min(wi.origin.x, GetSystemMetrics(SM_CXSCREEN) - wi.size.cx); 321 wi.origin.x = max(wi.origin.x, 0); 322 323 wi.style = SW_SHOW; 324 wi.win_style = WS_POPUP | WS_BORDER; 325 if (parent->page->file->has_popup_color) 326 wi.sr_color = parent->page->file->popup_color; 327 else 328 wi.sr_color = parent->info->sr_color; 329 wi.nsr_color = 0xFFFFFF; 330 331 return &wi; 332 } 333 334 typedef struct 335 { 336 WORD size; 337 WORD command; 338 LONG data; 339 LONG reserved; 340 WORD ofsFilename; 341 WORD ofsData; 342 } WINHELP,*LPWINHELP; 343 344 static BOOL WINHELP_HasWorkingWindow(void) 345 { 346 if (!Globals.active_win) return FALSE; 347 if (Globals.active_win->next || Globals.win_list != Globals.active_win) return TRUE; 348 return Globals.active_win->page != NULL && Globals.active_win->page->file != NULL; 349 } 350 351 /****************************************************************** 352 * WINHELP_HandleCommand 353 * 354 * 355 */ 356 static LRESULT WINHELP_HandleCommand(HWND hSrcWnd, LPARAM lParam) 357 { 358 COPYDATASTRUCT* cds = (COPYDATASTRUCT*)lParam; 359 WINHELP* wh; 360 361 if (cds->dwData != 0xA1DE505) 362 { 363 WINE_FIXME("Wrong magic number (%08lx)\n", cds->dwData); 364 return 0; 365 } 366 367 wh = cds->lpData; 368 369 if (wh) 370 { 371 char* ptr = (wh->ofsFilename) ? (LPSTR)wh + wh->ofsFilename : NULL; 372 373 WINE_TRACE("Got[%u]: cmd=%u data=%08x fn=%s\n", 374 wh->size, wh->command, wh->data, debugstr_a(ptr)); 375 switch (wh->command) 376 { 377 case HELP_CONTEXT: 378 if (ptr) 379 { 380 MACRO_JumpContext(ptr, "main", wh->data); 381 } 382 if (!WINHELP_HasWorkingWindow()) MACRO_Exit(); 383 break; 384 case HELP_QUIT: 385 MACRO_Exit(); 386 break; 387 case HELP_CONTENTS: 388 if (ptr) 389 { 390 MACRO_JumpContents(ptr, "main"); 391 } 392 if (!WINHELP_HasWorkingWindow()) MACRO_Exit(); 393 break; 394 case HELP_HELPONHELP: 395 MACRO_HelpOn(); 396 if (!WINHELP_HasWorkingWindow()) MACRO_Exit(); 397 break; 398 /* case HELP_SETINDEX: */ 399 case HELP_SETCONTENTS: 400 if (ptr) 401 { 402 MACRO_SetContents(ptr, wh->data); 403 } 404 break; 405 case HELP_CONTEXTPOPUP: 406 if (ptr) 407 { 408 MACRO_PopupContext(ptr, wh->data); 409 } 410 break; 411 /* case HELP_FORCEFILE:*/ 412 /* case HELP_CONTEXTMENU: */ 413 case HELP_FINDER: 414 /* in fact, should be the topic dialog box */ 415 WINE_FIXME("HELP_FINDER: stub\n"); 416 if (ptr) 417 { 418 MACRO_JumpHash(ptr, "main", 0); 419 } 420 break; 421 /* case HELP_WM_HELP: */ 422 /* case HELP_SETPOPUP_POS: */ 423 /* case HELP_KEY: */ 424 /* case HELP_COMMAND: */ 425 /* case HELP_PARTIALKEY: */ 426 /* case HELP_MULTIKEY: */ 427 /* case HELP_SETWINPOS: */ 428 default: 429 WINE_FIXME("Unhandled command (%x) for remote winhelp control\n", wh->command); 430 break; 431 } 432 } 433 /* Always return success for now */ 434 return 1; 435 } 436 437 void WINHELP_LayoutMainWindow(WINHELP_WINDOW* win) 438 { 439 RECT rect, button_box_rect; 440 INT text_top = 0; 441 HWND hButtonBoxWnd = GetDlgItem(win->hMainWnd, CTL_ID_BUTTON); 442 HWND hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT); 443 444 GetClientRect(win->hMainWnd, &rect); 445 446 /* Update button box and text Window */ 447 SetWindowPos(hButtonBoxWnd, HWND_TOP, 448 rect.left, rect.top, 449 rect.right - rect.left, 450 rect.bottom - rect.top, 0); 451 452 if (GetWindowRect(hButtonBoxWnd, &button_box_rect)) 453 text_top = rect.top + button_box_rect.bottom - button_box_rect.top; 454 455 SetWindowPos(hTextWnd, HWND_TOP, 456 rect.left, text_top, 457 rect.right - rect.left, 458 rect.bottom - text_top, 0); 459 460 } 461 462 /****************************************************************** 463 * WINHELP_DeleteButtons 464 * 465 */ 466 static void WINHELP_DeleteButtons(WINHELP_WINDOW* win) 467 { 468 WINHELP_BUTTON* b; 469 WINHELP_BUTTON* bp; 470 471 for (b = win->first_button; b; b = bp) 472 { 473 DestroyWindow(b->hWnd); 474 bp = b->next; 475 HeapFree(GetProcessHeap(), 0, b); 476 } 477 win->first_button = NULL; 478 } 479 480 /****************************************************************** 481 * WINHELP_DeleteBackSet 482 * 483 */ 484 void WINHELP_DeleteBackSet(WINHELP_WINDOW* win) 485 { 486 unsigned int i; 487 488 for (i = 0; i < win->back.index; i++) 489 { 490 HLPFILE_FreeHlpFile(win->back.set[i].page->file); 491 win->back.set[i].page = NULL; 492 } 493 win->back.index = 0; 494 } 495 496 /****************************************************************** 497 * WINHELP_DeletePageLinks 498 * 499 */ 500 static void WINHELP_DeletePageLinks(HLPFILE_PAGE* page) 501 { 502 HLPFILE_LINK* curr; 503 HLPFILE_LINK* next; 504 505 for (curr = page->first_link; curr; curr = next) 506 { 507 next = curr->next; 508 HeapFree(GetProcessHeap(), 0, curr); 509 } 510 } 511 512 /*********************************************************************** 513 * 514 * WINHELP_GrabWindow 515 */ 516 WINHELP_WINDOW* WINHELP_GrabWindow(WINHELP_WINDOW* win) 517 { 518 WINE_TRACE("Grab %p#%d++\n", win, win->ref_count); 519 win->ref_count++; 520 return win; 521 } 522 523 /*********************************************************************** 524 * 525 * WINHELP_ReleaseWindow 526 */ 527 BOOL WINHELP_ReleaseWindow(WINHELP_WINDOW* win) 528 { 529 WINE_TRACE("Release %p#%d--\n", win, win->ref_count); 530 531 if (!--win->ref_count) 532 { 533 DestroyWindow(win->hMainWnd); 534 return FALSE; 535 } 536 return TRUE; 537 } 538 539 /*********************************************************************** 540 * 541 * WINHELP_DeleteWindow 542 */ 543 static void WINHELP_DeleteWindow(WINHELP_WINDOW* win) 544 { 545 WINHELP_WINDOW** w; 546 BOOL bExit; 547 HWND hTextWnd; 548 549 for (w = &Globals.win_list; *w; w = &(*w)->next) 550 { 551 if (*w == win) 552 { 553 *w = win->next; 554 break; 555 } 556 } 557 bExit = (Globals.wVersion >= 4 && !lstrcmpiA(win->info->name, "main")); 558 559 if (Globals.active_win == win) 560 { 561 Globals.active_win = Globals.win_list; 562 if (Globals.win_list) 563 SetActiveWindow(Globals.win_list->hMainWnd); 564 } 565 566 if (win == Globals.active_popup) 567 Globals.active_popup = NULL; 568 569 hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT); 570 SetWindowLongPtrA(hTextWnd, GWLP_WNDPROC, (LONG_PTR)win->origRicheditWndProc); 571 572 WINHELP_DeleteButtons(win); 573 574 if (win->page) WINHELP_DeletePageLinks(win->page); 575 if (win->hHistoryWnd) DestroyWindow(win->hHistoryWnd); 576 577 DeleteObject(win->hBrush); 578 579 WINHELP_DeleteBackSet(win); 580 581 if (win->page) HLPFILE_FreeHlpFile(win->page->file); 582 HeapFree(GetProcessHeap(), 0, win); 583 584 if (bExit) MACRO_Exit(); 585 if (!Globals.win_list) 586 PostQuitMessage(0); 587 } 588 589 static char* WINHELP_GetCaption(WINHELP_WNDPAGE* wpage) 590 { 591 if (wpage->wininfo->caption[0]) return wpage->wininfo->caption; 592 return wpage->page->file->lpszTitle; 593 } 594 595 static void WINHELP_RememberPage(WINHELP_WINDOW* win, WINHELP_WNDPAGE* wpage) 596 { 597 unsigned num; 598 599 if (!Globals.history.index || Globals.history.set[0].page != wpage->page) 600 { 601 num = ARRAY_SIZE(Globals.history.set); 602 /* we're full, remove latest entry */ 603 if (Globals.history.index == num) 604 { 605 HLPFILE_FreeHlpFile(Globals.history.set[num - 1].page->file); 606 Globals.history.index--; 607 } 608 memmove(&Globals.history.set[1], &Globals.history.set[0], 609 Globals.history.index * sizeof(Globals.history.set[0])); 610 Globals.history.set[0] = *wpage; 611 Globals.history.index++; 612 wpage->page->file->wRefCount++; 613 } 614 if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE); 615 616 num = ARRAY_SIZE(win->back.set); 617 if (win->back.index == num) 618 { 619 /* we're full, remove latest entry */ 620 HLPFILE_FreeHlpFile(win->back.set[0].page->file); 621 memmove(&win->back.set[0], &win->back.set[1], 622 (num - 1) * sizeof(win->back.set[0])); 623 win->back.index--; 624 } 625 win->back.set[win->back.index++] = *wpage; 626 wpage->page->file->wRefCount++; 627 } 628 629 /*********************************************************************** 630 * 631 * WINHELP_FindLink 632 */ 633 static HLPFILE_LINK* WINHELP_FindLink(WINHELP_WINDOW* win, LPARAM pos) 634 { 635 HLPFILE_LINK* link; 636 POINTL mouse_ptl, char_ptl, char_next_ptl; 637 DWORD cp; 638 639 if (!win->page) return NULL; 640 641 mouse_ptl.x = (short)LOWORD(pos); 642 mouse_ptl.y = (short)HIWORD(pos); 643 cp = SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_CHARFROMPOS, 644 0, (LPARAM)&mouse_ptl); 645 646 for (link = win->page->first_link; link; link = link->next) 647 { 648 if (link->cpMin <= cp && cp <= link->cpMax) 649 { 650 /* check whether we're at end of line */ 651 SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_POSFROMCHAR, 652 (LPARAM)&char_ptl, cp); 653 SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_POSFROMCHAR, 654 (LPARAM)&char_next_ptl, cp + 1); 655 if (link->bHotSpot) 656 { 657 HLPFILE_HOTSPOTLINK* hslink = (HLPFILE_HOTSPOTLINK*)link; 658 if ((mouse_ptl.x < char_ptl.x + hslink->x) || 659 (mouse_ptl.x >= char_ptl.x + hslink->x + hslink->width) || 660 (mouse_ptl.y < char_ptl.y + hslink->y) || 661 (mouse_ptl.y >= char_ptl.y + hslink->y + hslink->height)) 662 continue; 663 break; 664 } 665 if (char_next_ptl.y != char_ptl.y || mouse_ptl.x >= char_next_ptl.x) 666 link = NULL; 667 break; 668 } 669 } 670 return link; 671 } 672 673 static LRESULT CALLBACK WINHELP_RicheditWndProc(HWND hWnd, UINT msg, 674 WPARAM wParam, LPARAM lParam) 675 { 676 WINHELP_WINDOW *win = (WINHELP_WINDOW*) GetWindowLongPtrW(GetParent(hWnd), 0); 677 DWORD messagePos; 678 POINT pt; 679 switch(msg) 680 { 681 case WM_SETCURSOR: 682 messagePos = GetMessagePos(); 683 pt.x = (short)LOWORD(messagePos); 684 pt.y = (short)HIWORD(messagePos); 685 ScreenToClient(hWnd, &pt); 686 if (win->page && WINHELP_FindLink(win, MAKELPARAM(pt.x, pt.y))) 687 { 688 SetCursor(win->hHandCur); 689 return 0; 690 } 691 /* fall through */ 692 default: 693 return CallWindowProcA(win->origRicheditWndProc, hWnd, msg, wParam, lParam); 694 } 695 } 696 697 /*********************************************************************** 698 * 699 * WINHELP_CreateHelpWindow 700 */ 701 BOOL WINHELP_CreateHelpWindow(WINHELP_WNDPAGE* wpage, int nCmdShow, BOOL remember) 702 { 703 WINHELP_WINDOW* win = NULL; 704 BOOL bPrimary, bPopup, bReUsed = FALSE; 705 HICON hIcon; 706 HWND hTextWnd = NULL; 707 708 bPrimary = !lstrcmpiA(wpage->wininfo->name, "main"); 709 bPopup = !bPrimary && (wpage->wininfo->win_style & WS_POPUP); 710 711 if (!bPopup) 712 { 713 for (win = Globals.win_list; win; win = win->next) 714 { 715 if (!lstrcmpiA(win->info->name, wpage->wininfo->name)) 716 { 717 if (win->page == wpage->page && win->info == wpage->wininfo) 718 { 719 /* see #22979, some hlp files have a macro (run at page opening), which 720 * jumps to the very same page 721 * Exit gracefully in that case 722 */ 723 return TRUE; 724 } 725 WINHELP_DeleteButtons(win); 726 bReUsed = TRUE; 727 SetWindowTextA(win->hMainWnd, WINHELP_GetCaption(wpage)); 728 if (win->info != wpage->wininfo) 729 { 730 POINT pt = {0, 0}; 731 SIZE sz = {0, 0}; 732 DWORD flags = SWP_NOSIZE | SWP_NOMOVE; 733 734 if (wpage->wininfo->origin.x != CW_USEDEFAULT && 735 wpage->wininfo->origin.y != CW_USEDEFAULT) 736 { 737 pt = wpage->wininfo->origin; 738 flags &= ~SWP_NOSIZE; 739 } 740 if (wpage->wininfo->size.cx != CW_USEDEFAULT && 741 wpage->wininfo->size.cy != CW_USEDEFAULT) 742 { 743 sz = wpage->wininfo->size; 744 flags &= ~SWP_NOMOVE; 745 } 746 SetWindowPos(win->hMainWnd, HWND_TOP, pt.x, pt.y, sz.cx, sz.cy, flags); 747 } 748 749 if (wpage->page && win->page && wpage->page->file != win->page->file) 750 WINHELP_DeleteBackSet(win); 751 WINHELP_InitFonts(win->hMainWnd); 752 753 win->page = wpage->page; 754 win->info = wpage->wininfo; 755 hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT); 756 WINHELP_SetupText(hTextWnd, win, wpage->relative); 757 758 InvalidateRect(win->hMainWnd, NULL, TRUE); 759 if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE); 760 761 break; 762 } 763 } 764 } 765 766 if (!win) 767 { 768 /* Initialize WINHELP_WINDOW struct */ 769 win = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINHELP_WINDOW)); 770 if (!win) return FALSE; 771 win->next = Globals.win_list; 772 Globals.win_list = win; 773 774 win->hHandCur = LoadCursorW(0, (LPWSTR)IDC_HAND); 775 win->back.index = 0; 776 win->font_scale = 1; 777 WINHELP_GrabWindow(win); 778 } 779 win->page = wpage->page; 780 win->info = wpage->wininfo; 781 WINHELP_GrabWindow(win); 782 783 if (!bPopup && wpage->page && remember) 784 { 785 WINHELP_RememberPage(win, wpage); 786 } 787 788 if (bPopup) 789 Globals.active_popup = win; 790 else 791 Globals.active_win = win; 792 793 /* Initialize default pushbuttons */ 794 if (bPrimary && wpage->page) 795 { 796 CHAR buffer[MAX_STRING_LEN]; 797 798 LoadStringA(Globals.hInstance, STID_CONTENTS, buffer, sizeof(buffer)); 799 MACRO_CreateButton("BTN_CONTENTS", buffer, "Contents()"); 800 LoadStringA(Globals.hInstance, STID_INDEX, buffer, sizeof(buffer)); 801 MACRO_CreateButton("BTN_INDEX", buffer, "Finder()"); 802 LoadStringA(Globals.hInstance, STID_BACK, buffer, sizeof(buffer)); 803 MACRO_CreateButton("BTN_BACK", buffer, "Back()"); 804 if (win->back.index <= 1) MACRO_DisableButton("BTN_BACK"); 805 } 806 807 if (!bReUsed) 808 { 809 win->hMainWnd = CreateWindowExA((bPopup) ? WS_EX_TOOLWINDOW : 0, MAIN_WIN_CLASS_NAME, 810 WINHELP_GetCaption(wpage), 811 bPrimary ? WS_OVERLAPPEDWINDOW : wpage->wininfo->win_style, 812 wpage->wininfo->origin.x, wpage->wininfo->origin.y, 813 wpage->wininfo->size.cx, wpage->wininfo->size.cy, 814 bPopup ? Globals.active_win->hMainWnd : NULL, 815 bPrimary ? LoadMenuW(Globals.hInstance, MAKEINTRESOURCEW(MAIN_MENU)) : 0, 816 Globals.hInstance, win); 817 if (!bPopup) 818 /* Create button box and text Window */ 819 CreateWindowA(BUTTON_BOX_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE, 820 0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_BUTTON, Globals.hInstance, NULL); 821 822 hTextWnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, 823 ES_MULTILINE | ES_READONLY | WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_VISIBLE, 824 0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_TEXT, Globals.hInstance, NULL); 825 SendMessageW(hTextWnd, EM_SETEVENTMASK, 0, 826 SendMessageW(hTextWnd, EM_GETEVENTMASK, 0, 0) | ENM_MOUSEEVENTS); 827 win->origRicheditWndProc = (WNDPROC)SetWindowLongPtrA(hTextWnd, GWLP_WNDPROC, 828 (LONG_PTR)WINHELP_RicheditWndProc); 829 } 830 831 hIcon = (wpage->page) ? wpage->page->file->hIcon : NULL; 832 if (!hIcon) hIcon = LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDI_WINHELP), IMAGE_ICON, 833 GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED); 834 SendMessageW(win->hMainWnd, WM_SETICON, ICON_SMALL, (DWORD_PTR)hIcon); 835 836 /* Initialize file specific pushbuttons */ 837 if (!(wpage->wininfo->win_style & WS_POPUP) && wpage->page) 838 { 839 HLPFILE_MACRO *macro; 840 for (macro = wpage->page->file->first_macro; macro; macro = macro->next) 841 MACRO_ExecuteMacro(win, macro->lpszMacro); 842 843 for (macro = wpage->page->first_macro; macro; macro = macro->next) 844 MACRO_ExecuteMacro(win, macro->lpszMacro); 845 } 846 /* See #17681, in some cases, the newly created window is closed by the macros it contains 847 * (braindead), so deal with this case 848 */ 849 for (win = Globals.win_list; win; win = win->next) 850 { 851 if (!lstrcmpiA(win->info->name, wpage->wininfo->name)) break; 852 } 853 if (!win || !WINHELP_ReleaseWindow(win)) return TRUE; 854 855 if (bPopup) 856 { 857 DWORD mask = SendMessageW(hTextWnd, EM_GETEVENTMASK, 0, 0); 858 859 win->font_scale = Globals.active_win->font_scale; 860 WINHELP_SetupText(hTextWnd, win, wpage->relative); 861 862 /* we need the window to be shown for richedit to compute the size */ 863 ShowWindow(win->hMainWnd, nCmdShow); 864 SendMessageW(hTextWnd, EM_SETEVENTMASK, 0, mask | ENM_REQUESTRESIZE); 865 SendMessageW(hTextWnd, EM_REQUESTRESIZE, 0, 0); 866 SendMessageW(hTextWnd, EM_SETEVENTMASK, 0, mask); 867 } 868 else 869 { 870 WINHELP_SetupText(hTextWnd, win, wpage->relative); 871 WINHELP_LayoutMainWindow(win); 872 ShowWindow(win->hMainWnd, nCmdShow); 873 } 874 875 return TRUE; 876 } 877 878 /****************************************************************** 879 * WINHELP_OpenHelpWindow 880 * Main function to search for a page and display it in a window 881 */ 882 BOOL WINHELP_OpenHelpWindow(HLPFILE_PAGE* (*lookup)(HLPFILE*, LONG, ULONG*), 883 HLPFILE* hlpfile, LONG val, HLPFILE_WINDOWINFO* wi, 884 int nCmdShow) 885 { 886 WINHELP_WNDPAGE wpage; 887 888 wpage.page = lookup(hlpfile, val, &wpage.relative); 889 if (wpage.page) wpage.page->file->wRefCount++; 890 wpage.wininfo = wi; 891 return WINHELP_CreateHelpWindow(&wpage, nCmdShow, TRUE); 892 } 893 894 /****************************************************************** 895 * WINHELP_HandleTextMouse 896 * 897 */ 898 static BOOL WINHELP_HandleTextMouse(WINHELP_WINDOW* win, UINT msg, LPARAM lParam) 899 { 900 HLPFILE* hlpfile; 901 HLPFILE_LINK* link; 902 BOOL ret = FALSE; 903 904 switch (msg) 905 { 906 case WM_LBUTTONDOWN: 907 if ((link = WINHELP_FindLink(win, lParam))) 908 { 909 HLPFILE_WINDOWINFO* wi; 910 911 switch (link->cookie) 912 { 913 case hlp_link_link: 914 if ((hlpfile = WINHELP_LookupHelpFile(link->string))) 915 { 916 if (link->window == -1) 917 { 918 wi = win->info; 919 if (wi->win_style & WS_POPUP) wi = Globals.active_win->info; 920 } 921 else if (link->window < hlpfile->numWindows) 922 wi = &hlpfile->windows[link->window]; 923 else 924 { 925 WINE_WARN("link to window %d/%d\n", link->window, hlpfile->numWindows); 926 break; 927 } 928 WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, link->hash, wi, SW_NORMAL); 929 } 930 break; 931 case hlp_link_popup: 932 if ((hlpfile = WINHELP_LookupHelpFile(link->string))) 933 WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, link->hash, 934 WINHELP_GetPopupWindowInfo(hlpfile, win, lParam), 935 SW_NORMAL); 936 break; 937 case hlp_link_macro: 938 MACRO_ExecuteMacro(win, link->string); 939 break; 940 default: 941 WINE_FIXME("Unknown link cookie %d\n", link->cookie); 942 } 943 ret = TRUE; 944 } 945 break; 946 } 947 return ret; 948 } 949 950 /*********************************************************************** 951 * 952 * WINHELP_CheckPopup 953 */ 954 static BOOL WINHELP_CheckPopup(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT* lret) 955 { 956 WINHELP_WINDOW* popup; 957 958 if (!Globals.active_popup) return FALSE; 959 960 switch (msg) 961 { 962 case WM_NOTIFY: 963 { 964 MSGFILTER* msgf = (MSGFILTER*)lParam; 965 if (msgf->nmhdr.code == EN_MSGFILTER) 966 { 967 if (!WINHELP_CheckPopup(hWnd, msgf->msg, msgf->wParam, msgf->lParam, NULL)) 968 return FALSE; 969 if (lret) *lret = 1; 970 return TRUE; 971 } 972 } 973 break; 974 case WM_ACTIVATE: 975 if (LOWORD(wParam) != WA_INACTIVE || (HWND)lParam == Globals.active_win->hMainWnd || 976 (HWND)lParam == Globals.active_popup->hMainWnd || 977 GetWindow((HWND)lParam, GW_OWNER) == Globals.active_win->hMainWnd) 978 break; 979 /* fall through */ 980 case WM_LBUTTONDOWN: 981 if (msg == WM_LBUTTONDOWN) 982 WINHELP_HandleTextMouse(Globals.active_popup, msg, lParam); 983 /* fall through */ 984 case WM_MBUTTONDOWN: 985 case WM_RBUTTONDOWN: 986 case WM_NCLBUTTONDOWN: 987 case WM_NCMBUTTONDOWN: 988 case WM_NCRBUTTONDOWN: 989 popup = Globals.active_popup; 990 Globals.active_popup = NULL; 991 WINHELP_ReleaseWindow(popup); 992 if (lret) *lret = 1; 993 return TRUE; 994 } 995 return FALSE; 996 } 997 998 /*********************************************************************** 999 * 1000 * WINHELP_ButtonWndProc 1001 */ 1002 static LRESULT CALLBACK WINHELP_ButtonWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 1003 { 1004 if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0; 1005 1006 if (msg == WM_KEYDOWN) 1007 { 1008 switch (wParam) 1009 { 1010 case VK_UP: 1011 case VK_DOWN: 1012 case VK_PRIOR: 1013 case VK_NEXT: 1014 case VK_ESCAPE: 1015 return SendMessageA(GetParent(hWnd), msg, wParam, lParam); 1016 } 1017 } 1018 1019 return CallWindowProcA(Globals.button_proc, hWnd, msg, wParam, lParam); 1020 } 1021 1022 /*********************************************************************** 1023 * 1024 * WINHELP_ButtonBoxWndProc 1025 */ 1026 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 1027 { 1028 WINDOWPOS *winpos; 1029 WINHELP_WINDOW *win; 1030 WINHELP_BUTTON *button; 1031 SIZE button_size; 1032 INT x, y; 1033 1034 if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0L; 1035 1036 switch (msg) 1037 { 1038 case WM_WINDOWPOSCHANGING: 1039 winpos = (WINDOWPOS*) lParam; 1040 win = (WINHELP_WINDOW*) GetWindowLongPtrW(GetParent(hWnd), 0); 1041 1042 /* Update buttons */ 1043 button_size.cx = 0; 1044 button_size.cy = 0; 1045 for (button = win->first_button; button; button = button->next) 1046 { 1047 HDC hDc; 1048 SIZE textsize; 1049 if (!button->hWnd) 1050 { 1051 button->hWnd = CreateWindowA(STRING_BUTTON, button->lpszName, 1052 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 1053 0, 0, 0, 0, 1054 hWnd, (HMENU) button->wParam, 1055 Globals.hInstance, 0); 1056 if (button->hWnd) 1057 { 1058 if (Globals.button_proc == NULL) 1059 { 1060 NONCLIENTMETRICSW ncm; 1061 Globals.button_proc = (WNDPROC) GetWindowLongPtrA(button->hWnd, GWLP_WNDPROC); 1062 1063 ncm.cbSize = sizeof(NONCLIENTMETRICSW); 1064 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, 1065 sizeof(NONCLIENTMETRICSW), &ncm, 0); 1066 Globals.hButtonFont = CreateFontIndirectW(&ncm.lfMenuFont); 1067 } 1068 SetWindowLongPtrA(button->hWnd, GWLP_WNDPROC, (LONG_PTR) WINHELP_ButtonWndProc); 1069 if (Globals.hButtonFont) 1070 SendMessageW(button->hWnd, WM_SETFONT, (WPARAM)Globals.hButtonFont, TRUE); 1071 } 1072 } 1073 hDc = GetDC(button->hWnd); 1074 GetTextExtentPointA(hDc, button->lpszName, strlen(button->lpszName), &textsize); 1075 ReleaseDC(button->hWnd, hDc); 1076 1077 button_size.cx = max(button_size.cx, textsize.cx + BUTTON_CX); 1078 button_size.cy = max(button_size.cy, textsize.cy + BUTTON_CY); 1079 } 1080 1081 x = 0; 1082 y = 0; 1083 for (button = win->first_button; button; button = button->next) 1084 { 1085 SetWindowPos(button->hWnd, HWND_TOP, x, y, button_size.cx, button_size.cy, 0); 1086 1087 if (x + 2 * button_size.cx <= winpos->cx) 1088 x += button_size.cx; 1089 else 1090 x = 0, y += button_size.cy; 1091 } 1092 winpos->cy = y + (x ? button_size.cy : 0); 1093 break; 1094 1095 case WM_COMMAND: 1096 SendMessageW(GetParent(hWnd), msg, wParam, lParam); 1097 break; 1098 1099 case WM_KEYDOWN: 1100 switch (wParam) 1101 { 1102 case VK_UP: 1103 case VK_DOWN: 1104 case VK_PRIOR: 1105 case VK_NEXT: 1106 case VK_ESCAPE: 1107 return SendMessageA(GetParent(hWnd), msg, wParam, lParam); 1108 } 1109 break; 1110 } 1111 1112 return DefWindowProcA(hWnd, msg, wParam, lParam); 1113 } 1114 1115 /****************************************************************** 1116 * WINHELP_HistoryWndProc 1117 * 1118 * 1119 */ 1120 static LRESULT CALLBACK WINHELP_HistoryWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 1121 { 1122 WINHELP_WINDOW* win; 1123 PAINTSTRUCT ps; 1124 HDC hDc; 1125 TEXTMETRICW tm; 1126 unsigned int i; 1127 RECT r; 1128 1129 switch (msg) 1130 { 1131 case WM_NCCREATE: 1132 win = (WINHELP_WINDOW*)((LPCREATESTRUCTA)lParam)->lpCreateParams; 1133 SetWindowLongPtrW(hWnd, 0, (ULONG_PTR)win); 1134 win->hHistoryWnd = hWnd; 1135 break; 1136 case WM_CREATE: 1137 hDc = GetDC(hWnd); 1138 GetTextMetricsW(hDc, &tm); 1139 GetWindowRect(hWnd, &r); 1140 1141 r.right = r.left + 30 * tm.tmAveCharWidth; 1142 r.bottom = r.top + ARRAY_SIZE(Globals.history.set) * tm.tmHeight; 1143 AdjustWindowRect(&r, GetWindowLongW(hWnd, GWL_STYLE), FALSE); 1144 if (r.left < 0) {r.right -= r.left; r.left = 0;} 1145 if (r.top < 0) {r.bottom -= r.top; r.top = 0;} 1146 1147 MoveWindow(hWnd, r.left, r.top, r.right, r.bottom, TRUE); 1148 ReleaseDC(hWnd, hDc); 1149 break; 1150 case WM_LBUTTONDOWN: 1151 hDc = GetDC(hWnd); 1152 GetTextMetricsW(hDc, &tm); 1153 i = HIWORD(lParam) / tm.tmHeight; 1154 if (i < Globals.history.index) 1155 WINHELP_CreateHelpWindow(&Globals.history.set[i], SW_SHOW, TRUE); 1156 ReleaseDC(hWnd, hDc); 1157 break; 1158 case WM_PAINT: 1159 hDc = BeginPaint(hWnd, &ps); 1160 GetTextMetricsW(hDc, &tm); 1161 1162 for (i = 0; i < Globals.history.index; i++) 1163 { 1164 if (Globals.history.set[i].page->file == Globals.active_win->page->file) 1165 { 1166 TextOutA(hDc, 0, i * tm.tmHeight, 1167 Globals.history.set[i].page->lpszTitle, 1168 strlen(Globals.history.set[i].page->lpszTitle)); 1169 } 1170 else 1171 { 1172 char buffer[1024]; 1173 const char* ptr1; 1174 const char* ptr2; 1175 unsigned len; 1176 1177 ptr1 = strrchr(Globals.history.set[i].page->file->lpszPath, '\\'); 1178 if (!ptr1) ptr1 = Globals.history.set[i].page->file->lpszPath; 1179 else ptr1++; 1180 ptr2 = strrchr(ptr1, '.'); 1181 len = ptr2 ? ptr2 - ptr1 : strlen(ptr1); 1182 if (len > sizeof(buffer)) len = sizeof(buffer); 1183 memcpy(buffer, ptr1, len); 1184 if (len < sizeof(buffer)) buffer[len++] = ':'; 1185 lstrcpynA(&buffer[len], Globals.history.set[i].page->lpszTitle, sizeof(buffer) - len); 1186 TextOutA(hDc, 0, i * tm.tmHeight, buffer, strlen(buffer)); 1187 } 1188 } 1189 EndPaint(hWnd, &ps); 1190 break; 1191 case WM_DESTROY: 1192 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0); 1193 if (hWnd == win->hHistoryWnd) 1194 win->hHistoryWnd = 0; 1195 break; 1196 } 1197 return DefWindowProcA(hWnd, msg, wParam, lParam); 1198 } 1199 1200 /************************************************************************** 1201 * cb_KWBTree 1202 * 1203 * HLPFILE_BPTreeCallback enumeration function for '|KWBTREE' internal file. 1204 * 1205 */ 1206 static void cb_KWBTree(void *p, void **next, void *cookie) 1207 { 1208 HWND hListWnd = cookie; 1209 int count; 1210 1211 WINE_TRACE("Adding %s to search list\n", debugstr_a((char *)p)); 1212 SendMessageA(hListWnd, LB_INSERTSTRING, -1, (LPARAM)p); 1213 count = SendMessageW(hListWnd, LB_GETCOUNT, 0, 0); 1214 SendMessageW(hListWnd, LB_SETITEMDATA, count-1, (LPARAM)p); 1215 *next = (char*)p + strlen((char*)p) + 7; 1216 } 1217 1218 struct index_data 1219 { 1220 HLPFILE* hlpfile; 1221 BOOL jump; 1222 ULONG offset; 1223 }; 1224 1225 /************************************************************************** 1226 * WINHELP_IndexDlgProc 1227 * 1228 */ 1229 static INT_PTR CALLBACK WINHELP_IndexDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 1230 { 1231 static struct index_data* id; 1232 int sel; 1233 1234 switch (msg) 1235 { 1236 case WM_INITDIALOG: 1237 id = (struct index_data*)((PROPSHEETPAGEA*)lParam)->lParam; 1238 HLPFILE_BPTreeEnum(id->hlpfile->kwbtree, cb_KWBTree, 1239 GetDlgItem(hWnd, IDC_INDEXLIST)); 1240 id->jump = FALSE; 1241 id->offset = 1; 1242 return TRUE; 1243 case WM_COMMAND: 1244 switch (HIWORD(wParam)) 1245 { 1246 case LBN_DBLCLK: 1247 if (LOWORD(wParam) == IDC_INDEXLIST) 1248 SendMessageW(GetParent(hWnd), PSM_PRESSBUTTON, PSBTN_OK, 0); 1249 break; 1250 } 1251 break; 1252 case WM_NOTIFY: 1253 switch (((NMHDR*)lParam)->code) 1254 { 1255 case PSN_APPLY: 1256 sel = SendDlgItemMessageW(hWnd, IDC_INDEXLIST, LB_GETCURSEL, 0, 0); 1257 if (sel != LB_ERR) 1258 { 1259 BYTE *p; 1260 int count; 1261 1262 p = (BYTE*)SendDlgItemMessageW(hWnd, IDC_INDEXLIST, LB_GETITEMDATA, sel, 0); 1263 count = *(short*)((char *)p + strlen((char *)p) + 1); 1264 if (count > 1) 1265 { 1266 MessageBoxA(hWnd, "count > 1 not supported yet", "Error", MB_OK | MB_ICONSTOP); 1267 SetWindowLongPtrW(hWnd, DWLP_MSGRESULT, PSNRET_INVALID); 1268 return TRUE; 1269 } 1270 id->offset = *(ULONG*)((char *)p + strlen((char *)p) + 3); 1271 id->offset = *(long*)(id->hlpfile->kwdata + id->offset + 9); 1272 if (id->offset == 0xFFFFFFFF) 1273 { 1274 MessageBoxA(hWnd, "macro keywords not supported yet", "Error", MB_OK | MB_ICONSTOP); 1275 SetWindowLongPtrW(hWnd, DWLP_MSGRESULT, PSNRET_INVALID); 1276 return TRUE; 1277 } 1278 id->jump = TRUE; 1279 SetWindowLongPtrW(hWnd, DWLP_MSGRESULT, PSNRET_NOERROR); 1280 } 1281 return TRUE; 1282 default: 1283 return FALSE; 1284 } 1285 break; 1286 default: 1287 break; 1288 } 1289 return FALSE; 1290 } 1291 1292 /************************************************************************** 1293 * WINHELP_SearchDlgProc 1294 * 1295 */ 1296 static INT_PTR CALLBACK WINHELP_SearchDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 1297 { 1298 switch (msg) 1299 { 1300 case WM_INITDIALOG: 1301 return TRUE; 1302 case WM_NOTIFY: 1303 switch (((NMHDR*)lParam)->code) 1304 { 1305 case PSN_APPLY: 1306 SetWindowLongPtrW(hWnd, DWLP_MSGRESULT, PSNRET_NOERROR); 1307 return TRUE; 1308 default: 1309 return FALSE; 1310 } 1311 break; 1312 default: 1313 break; 1314 } 1315 return FALSE; 1316 } 1317 1318 /*********************************************************************** 1319 * 1320 * WINHELP_MainWndProc 1321 */ 1322 static LRESULT CALLBACK WINHELP_MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 1323 { 1324 WINHELP_WINDOW *win; 1325 WINHELP_BUTTON *button; 1326 HWND hTextWnd; 1327 LRESULT ret; 1328 1329 if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, &ret)) return ret; 1330 1331 switch (msg) 1332 { 1333 case WM_NCCREATE: 1334 win = (WINHELP_WINDOW*) ((LPCREATESTRUCTA) lParam)->lpCreateParams; 1335 SetWindowLongPtrW(hWnd, 0, (ULONG_PTR) win); 1336 if (!win->page && Globals.isBook) 1337 PostMessageW(hWnd, WM_COMMAND, MNID_FILE_OPEN, 0); 1338 win->hMainWnd = hWnd; 1339 break; 1340 1341 case WM_WINDOWPOSCHANGED: 1342 WINHELP_LayoutMainWindow((WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0)); 1343 break; 1344 1345 case WM_COMMAND: 1346 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0); 1347 switch (LOWORD(wParam)) 1348 { 1349 /* Menu FILE */ 1350 case MNID_FILE_OPEN: MACRO_FileOpen(); break; 1351 case MNID_FILE_PRINT: MACRO_Print(); break; 1352 case MNID_FILE_SETUP: MACRO_PrinterSetup(); break; 1353 case MNID_FILE_EXIT: MACRO_Exit(); break; 1354 1355 /* Menu EDIT */ 1356 case MNID_EDIT_COPYDLG: 1357 SendDlgItemMessageW(hWnd, CTL_ID_TEXT, WM_COPY, 0, 0); 1358 break; 1359 case MNID_EDIT_ANNOTATE:MACRO_Annotate(); break; 1360 1361 /* Menu Bookmark */ 1362 case MNID_BKMK_DEFINE: MACRO_BookmarkDefine(); break; 1363 1364 /* Menu Help */ 1365 case MNID_HELP_HELPON: MACRO_HelpOn(); break; 1366 case MNID_HELP_HELPTOP: MACRO_HelpOnTop(); break; 1367 case MNID_HELP_ABOUT: MACRO_About(); break; 1368 1369 /* Context help */ 1370 case MNID_CTXT_ANNOTATE:MACRO_Annotate(); break; 1371 case MNID_CTXT_COPY: MACRO_CopyDialog(); break; 1372 case MNID_CTXT_PRINT: MACRO_Print(); break; 1373 case MNID_OPTS_HISTORY: MACRO_History(); break; 1374 case MNID_OPTS_FONTS_SMALL: 1375 case MNID_CTXT_FONTS_SMALL: 1376 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0); 1377 if (win->font_scale != 0) 1378 { 1379 win->font_scale = 0; 1380 WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */); 1381 } 1382 break; 1383 case MNID_OPTS_FONTS_NORMAL: 1384 case MNID_CTXT_FONTS_NORMAL: 1385 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0); 1386 if (win->font_scale != 1) 1387 { 1388 win->font_scale = 1; 1389 WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */); 1390 } 1391 break; 1392 case MNID_OPTS_FONTS_LARGE: 1393 case MNID_CTXT_FONTS_LARGE: 1394 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0); 1395 if (win->font_scale != 2) 1396 { 1397 win->font_scale = 2; 1398 WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */); 1399 } 1400 break; 1401 1402 default: 1403 /* Buttons */ 1404 for (button = win->first_button; button; button = button->next) 1405 if (wParam == button->wParam) break; 1406 if (button) 1407 MACRO_ExecuteMacro(win, button->lpszMacro); 1408 else if (!HIWORD(wParam)) 1409 MessageBoxW(0, MAKEINTRESOURCEW(STID_NOT_IMPLEMENTED), 1410 MAKEINTRESOURCEW(STID_WHERROR), MB_OK); 1411 break; 1412 } 1413 break; 1414 /* EPP case WM_DESTROY: */ 1415 /* EPP if (Globals.hPopupWnd) DestroyWindow(Globals.hPopupWnd); */ 1416 /* EPP break; */ 1417 case WM_COPYDATA: 1418 return WINHELP_HandleCommand((HWND)wParam, lParam); 1419 1420 case WM_CHAR: 1421 if (wParam == 3) 1422 { 1423 SendDlgItemMessageW(hWnd, CTL_ID_TEXT, WM_COPY, 0, 0); 1424 return 0; 1425 } 1426 break; 1427 1428 case WM_KEYDOWN: 1429 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0); 1430 hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT); 1431 1432 switch (wParam) 1433 { 1434 case VK_UP: 1435 SendMessageW(hTextWnd, EM_SCROLL, SB_LINEUP, 0); 1436 return 0; 1437 case VK_DOWN: 1438 SendMessageW(hTextWnd, EM_SCROLL, SB_LINEDOWN, 0); 1439 return 0; 1440 case VK_PRIOR: 1441 SendMessageW(hTextWnd, EM_SCROLL, SB_PAGEUP, 0); 1442 return 0; 1443 case VK_NEXT: 1444 SendMessageW(hTextWnd, EM_SCROLL, SB_PAGEDOWN, 0); 1445 return 0; 1446 case VK_ESCAPE: 1447 MACRO_Exit(); 1448 return 0; 1449 } 1450 break; 1451 1452 case WM_NOTIFY: 1453 if (wParam == CTL_ID_TEXT) 1454 { 1455 RECT rc; 1456 1457 switch (((NMHDR*)lParam)->code) 1458 { 1459 case EN_MSGFILTER: 1460 { 1461 const MSGFILTER* msgf = (const MSGFILTER*)lParam; 1462 switch (msgf->msg) 1463 { 1464 case WM_KEYUP: 1465 if (msgf->wParam == VK_ESCAPE) 1466 WINHELP_ReleaseWindow((WINHELP_WINDOW*)GetWindowLongPtrW(hWnd, 0)); 1467 break; 1468 case WM_RBUTTONDOWN: 1469 { 1470 HMENU hMenu; 1471 POINT pt; 1472 1473 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0); 1474 hMenu = LoadMenuW(Globals.hInstance, MAKEINTRESOURCEW(CONTEXT_MENU)); 1475 switch (win->font_scale) 1476 { 1477 case 0: 1478 CheckMenuItem(hMenu, MNID_CTXT_FONTS_SMALL, 1479 MF_BYCOMMAND|MF_CHECKED); 1480 break; 1481 default: 1482 WINE_FIXME("Unsupported %d\n", win->font_scale); 1483 /* fall through */ 1484 case 1: 1485 CheckMenuItem(hMenu, MNID_CTXT_FONTS_NORMAL, 1486 MF_BYCOMMAND|MF_CHECKED); 1487 break; 1488 case 2: 1489 CheckMenuItem(hMenu, MNID_CTXT_FONTS_LARGE, 1490 MF_BYCOMMAND|MF_CHECKED); 1491 break; 1492 } 1493 pt.x = (int)(short)LOWORD(msgf->lParam); 1494 pt.y = (int)(short)HIWORD(msgf->lParam); 1495 ClientToScreen(msgf->nmhdr.hwndFrom, &pt); 1496 TrackPopupMenu(GetSubMenu(hMenu, 0), TPM_LEFTALIGN|TPM_TOPALIGN, 1497 pt.x, pt.y, 0, hWnd, NULL); 1498 DestroyMenu(hMenu); 1499 } 1500 break; 1501 default: 1502 return WINHELP_HandleTextMouse((WINHELP_WINDOW*)GetWindowLongPtrW(hWnd, 0), 1503 msgf->msg, msgf->lParam); 1504 } 1505 } 1506 break; 1507 1508 case EN_REQUESTRESIZE: 1509 rc = ((REQRESIZE*)lParam)->rc; 1510 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0); 1511 AdjustWindowRect(&rc, GetWindowLongW(win->hMainWnd, GWL_STYLE), 1512 FALSE); 1513 SetWindowPos(win->hMainWnd, HWND_TOP, 0, 0, 1514 rc.right - rc.left, rc.bottom - rc.top, 1515 SWP_NOMOVE | SWP_NOZORDER); 1516 WINHELP_LayoutMainWindow(win); 1517 break; 1518 } 1519 } 1520 break; 1521 1522 case WM_INITMENUPOPUP: 1523 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0); 1524 CheckMenuItem((HMENU)wParam, MNID_OPTS_FONTS_SMALL, 1525 (win->font_scale == 0) ? MF_CHECKED : MF_UNCHECKED); 1526 CheckMenuItem((HMENU)wParam, MNID_OPTS_FONTS_NORMAL, 1527 (win->font_scale == 1) ? MF_CHECKED : MF_UNCHECKED); 1528 CheckMenuItem((HMENU)wParam, MNID_OPTS_FONTS_LARGE, 1529 (win->font_scale == 2) ? MF_CHECKED : MF_UNCHECKED); 1530 break; 1531 case WM_DESTROY: 1532 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0); 1533 WINHELP_DeleteWindow(win); 1534 break; 1535 } 1536 return DefWindowProcA(hWnd, msg, wParam, lParam); 1537 } 1538 1539 /************************************************************************** 1540 * WINHELP_CreateIndexWindow 1541 * 1542 * Displays a dialog with keywords of current help file. 1543 * 1544 */ 1545 BOOL WINHELP_CreateIndexWindow(BOOL is_search) 1546 { 1547 HPROPSHEETPAGE psPage[3]; 1548 PROPSHEETPAGEA psp; 1549 PROPSHEETHEADERA psHead; 1550 struct index_data id; 1551 char buf[256]; 1552 1553 if (Globals.active_win && Globals.active_win->page && Globals.active_win->page->file) 1554 id.hlpfile = Globals.active_win->page->file; 1555 else 1556 return FALSE; 1557 1558 if (id.hlpfile->kwbtree == NULL) 1559 { 1560 WINE_TRACE("No index provided\n"); 1561 return FALSE; 1562 } 1563 1564 InitCommonControls(); 1565 1566 id.jump = FALSE; 1567 memset(&psp, 0, sizeof(psp)); 1568 psp.dwSize = sizeof(psp); 1569 psp.dwFlags = 0; 1570 psp.hInstance = Globals.hInstance; 1571 1572 psp.u.pszTemplate = MAKEINTRESOURCEA(IDD_INDEX); 1573 psp.lParam = (LPARAM)&id; 1574 psp.pfnDlgProc = WINHELP_IndexDlgProc; 1575 psPage[0] = CreatePropertySheetPageA(&psp); 1576 1577 psp.u.pszTemplate = MAKEINTRESOURCEA(IDD_SEARCH); 1578 psp.lParam = (LPARAM)&id; 1579 psp.pfnDlgProc = WINHELP_SearchDlgProc; 1580 psPage[1] = CreatePropertySheetPageA(&psp); 1581 1582 memset(&psHead, 0, sizeof(psHead)); 1583 psHead.dwSize = sizeof(psHead); 1584 1585 LoadStringA(Globals.hInstance, STID_PSH_INDEX, buf, sizeof(buf)); 1586 strcat(buf, Globals.active_win->info->caption); 1587 1588 psHead.pszCaption = buf; 1589 psHead.nPages = 2; 1590 psHead.u2.nStartPage = is_search ? 1 : 0; 1591 psHead.hwndParent = Globals.active_win->hMainWnd; 1592 psHead.u3.phpage = psPage; 1593 psHead.dwFlags = PSH_NOAPPLYNOW; 1594 1595 PropertySheetA(&psHead); 1596 if (id.jump) 1597 { 1598 WINE_TRACE("got %d as an offset\n", id.offset); 1599 WINHELP_OpenHelpWindow(HLPFILE_PageByOffset, id.hlpfile, id.offset, 1600 Globals.active_win->info, SW_NORMAL); 1601 } 1602 return TRUE; 1603 } 1604 1605 /*********************************************************************** 1606 * 1607 * RegisterWinClasses 1608 */ 1609 static BOOL WINHELP_RegisterWinClasses(void) 1610 { 1611 WNDCLASSEXA class_main, class_button_box, class_history; 1612 1613 class_main.cbSize = sizeof(class_main); 1614 class_main.style = CS_HREDRAW | CS_VREDRAW; 1615 class_main.lpfnWndProc = WINHELP_MainWndProc; 1616 class_main.cbClsExtra = 0; 1617 class_main.cbWndExtra = sizeof(WINHELP_WINDOW *); 1618 class_main.hInstance = Globals.hInstance; 1619 class_main.hIcon = LoadIconW(Globals.hInstance, MAKEINTRESOURCEW(IDI_WINHELP)); 1620 class_main.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW); 1621 class_main.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); 1622 class_main.lpszMenuName = 0; 1623 class_main.lpszClassName = MAIN_WIN_CLASS_NAME; 1624 class_main.hIconSm = LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDI_WINHELP), IMAGE_ICON, 1625 GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1626 LR_SHARED); 1627 1628 class_button_box = class_main; 1629 class_button_box.lpfnWndProc = WINHELP_ButtonBoxWndProc; 1630 class_button_box.cbWndExtra = 0; 1631 class_button_box.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); 1632 class_button_box.lpszClassName = BUTTON_BOX_WIN_CLASS_NAME; 1633 1634 class_history = class_main; 1635 class_history.lpfnWndProc = WINHELP_HistoryWndProc; 1636 class_history.lpszClassName = HISTORY_WIN_CLASS_NAME; 1637 1638 return (RegisterClassExA(&class_main) && 1639 RegisterClassExA(&class_button_box) && 1640 RegisterClassExA(&class_history)); 1641 } 1642 1643 /*********************************************************************** 1644 * 1645 * WinMain 1646 */ 1647 int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show) 1648 { 1649 MSG msg; 1650 LONG lHash = 0; 1651 HLPFILE* hlpfile; 1652 static CHAR default_wndname[] = "main"; 1653 LPSTR wndname = default_wndname; 1654 WINHELP_DLL* dll; 1655 HACCEL hAccel; 1656 1657 Globals.hInstance = hInstance; 1658 1659 if (LoadLibraryA("riched20.dll") == NULL) 1660 return MessageBoxW(0, MAKEINTRESOURCEW(STID_NO_RICHEDIT), 1661 MAKEINTRESOURCEW(STID_WHERROR), MB_OK); 1662 1663 /* Get options */ 1664 while (*cmdline && (*cmdline == ' ' || *cmdline == '-')) 1665 { 1666 CHAR option; 1667 LPCSTR topic_id; 1668 if (*cmdline++ == ' ') continue; 1669 1670 option = *cmdline; 1671 if (option) cmdline++; 1672 while (*cmdline == ' ') cmdline++; 1673 switch (option) 1674 { 1675 case 'i': 1676 case 'I': 1677 topic_id = cmdline; 1678 while (*cmdline && *cmdline != ' ') cmdline++; 1679 if (*cmdline) *cmdline++ = '\0'; 1680 lHash = HLPFILE_Hash(topic_id); 1681 break; 1682 1683 case '3': 1684 case '4': 1685 Globals.wVersion = option - '0'; 1686 break; 1687 1688 case 'x': 1689 show = SW_HIDE; 1690 Globals.isBook = FALSE; 1691 break; 1692 1693 default: 1694 WINE_FIXME("Unsupported cmd line: %s\n", debugstr_a(cmdline)); 1695 break; 1696 } 1697 } 1698 1699 /* Create primary window */ 1700 if (!WINHELP_RegisterWinClasses()) 1701 { 1702 WINE_FIXME("Couldn't register classes\n"); 1703 return 0; 1704 } 1705 1706 if (*cmdline) 1707 { 1708 char* ptr; 1709 if ((*cmdline == '"') && (ptr = strchr(cmdline+1, '"'))) 1710 { 1711 cmdline++; 1712 *ptr = '\0'; 1713 } 1714 if ((ptr = strchr(cmdline, '>'))) 1715 { 1716 *ptr = '\0'; 1717 wndname = ptr + 1; 1718 } 1719 hlpfile = WINHELP_LookupHelpFile(cmdline); 1720 if (!hlpfile) return 0; 1721 } 1722 else hlpfile = NULL; 1723 WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, lHash, 1724 WINHELP_GetWindowInfo(hlpfile, wndname), show); 1725 1726 /* Message loop */ 1727 hAccel = LoadAcceleratorsW(hInstance, MAKEINTRESOURCEW(MAIN_ACCEL)); 1728 while ((Globals.win_list || Globals.active_popup) && GetMessageW(&msg, 0, 0, 0)) 1729 { 1730 HWND hWnd = Globals.active_win ? Globals.active_win->hMainWnd : NULL; 1731 if (!TranslateAcceleratorW(hWnd, hAccel, &msg)) 1732 { 1733 TranslateMessage(&msg); 1734 DispatchMessageW(&msg); 1735 } 1736 } 1737 for (dll = Globals.dlls; dll; dll = dll->next) 1738 { 1739 if (dll->class & DC_INITTERM) dll->handler(DW_TERM, 0, 0); 1740 } 1741 return 0; 1742 } 1743