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