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