1 /* 2 * Help Viewer Implementation 3 * 4 * Copyright 2005 James Hawkins 5 * Copyright 2007 Jacek Caban for CodeWeavers 6 * Copyright 2011 Owen Rudge for CodeWeavers 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public 10 * License as published by the Free Software Foundation; either 11 * version 2.1 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with this library; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 21 */ 22 23 #include "hhctrl.h" 24 25 #include "wingdi.h" 26 #include "commctrl.h" 27 #include "wininet.h" 28 29 #include "wine/debug.h" 30 31 #include "resource.h" 32 33 WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp); 34 35 static LRESULT Help_OnSize(HWND hWnd); 36 static void ExpandContract(HHInfo *pHHInfo); 37 38 /* Window type defaults */ 39 40 #define WINTYPE_DEFAULT_X 280 41 #define WINTYPE_DEFAULT_Y 100 42 #define WINTYPE_DEFAULT_WIDTH 740 43 #define WINTYPE_DEFAULT_HEIGHT 640 44 #define WINTYPE_DEFAULT_NAVWIDTH 250 45 46 #define TAB_TOP_PADDING 8 47 #define TAB_RIGHT_PADDING 4 48 #define TAB_MARGIN 8 49 #define EDIT_HEIGHT 20 50 51 struct list window_list = LIST_INIT(window_list); 52 53 static const WCHAR szEmpty[] = {0}; 54 55 struct html_encoded_symbol { 56 const char *html_code; 57 char ansi_symbol; 58 }; 59 60 /* 61 * Table mapping the conversion between HTML encoded symbols and their ANSI code page equivalent. 62 * Note: Add additional entries in proper alphabetical order (a binary search is used on this table). 63 */ 64 static struct html_encoded_symbol html_encoded_symbols[] = 65 { 66 {"AElig", 0xC6}, 67 {"Aacute", 0xC1}, 68 {"Acirc", 0xC2}, 69 {"Agrave", 0xC0}, 70 {"Aring", 0xC5}, 71 {"Atilde", 0xC3}, 72 {"Auml", 0xC4}, 73 {"Ccedil", 0xC7}, 74 {"ETH", 0xD0}, 75 {"Eacute", 0xC9}, 76 {"Ecirc", 0xCA}, 77 {"Egrave", 0xC8}, 78 {"Euml", 0xCB}, 79 {"Iacute", 0xCD}, 80 {"Icirc", 0xCE}, 81 {"Igrave", 0xCC}, 82 {"Iuml", 0xCF}, 83 {"Ntilde", 0xD1}, 84 {"Oacute", 0xD3}, 85 {"Ocirc", 0xD4}, 86 {"Ograve", 0xD2}, 87 {"Oslash", 0xD8}, 88 {"Otilde", 0xD5}, 89 {"Ouml", 0xD6}, 90 {"THORN", 0xDE}, 91 {"Uacute", 0xDA}, 92 {"Ucirc", 0xDB}, 93 {"Ugrave", 0xD9}, 94 {"Uuml", 0xDC}, 95 {"Yacute", 0xDD}, 96 {"aacute", 0xE1}, 97 {"acirc", 0xE2}, 98 {"acute", 0xB4}, 99 {"aelig", 0xE6}, 100 {"agrave", 0xE0}, 101 {"amp", '&'}, 102 {"aring", 0xE5}, 103 {"atilde", 0xE3}, 104 {"auml", 0xE4}, 105 {"brvbar", 0xA6}, 106 {"ccedil", 0xE7}, 107 {"cedil", 0xB8}, 108 {"cent", 0xA2}, 109 {"copy", 0xA9}, 110 {"curren", 0xA4}, 111 {"deg", 0xB0}, 112 {"divide", 0xF7}, 113 {"eacute", 0xE9}, 114 {"ecirc", 0xEA}, 115 {"egrave", 0xE8}, 116 {"eth", 0xF0}, 117 {"euml", 0xEB}, 118 {"frac12", 0xBD}, 119 {"frac14", 0xBC}, 120 {"frac34", 0xBE}, 121 {"gt", '>'}, 122 {"iacute", 0xED}, 123 {"icirc", 0xEE}, 124 {"iexcl", 0xA1}, 125 {"igrave", 0xEC}, 126 {"iquest", 0xBF}, 127 {"iuml", 0xEF}, 128 {"laquo", 0xAB}, 129 {"lt", '<'}, 130 {"macr", 0xAF}, 131 {"micro", 0xB5}, 132 {"middot", 0xB7}, 133 {"nbsp", ' '}, 134 {"not", 0xAC}, 135 {"ntilde", 0xF1}, 136 {"oacute", 0xF3}, 137 {"ocirc", 0xF4}, 138 {"ograve", 0xF2}, 139 {"ordf", 0xAA}, 140 {"ordm", 0xBA}, 141 {"oslash", 0xF8}, 142 {"otilde", 0xF5}, 143 {"ouml", 0xF6}, 144 {"para", 0xB6}, 145 {"plusmn", 0xB1}, 146 {"pound", 0xA3}, 147 {"quot", '"'}, 148 {"raquo", 0xBB}, 149 {"reg", 0xAE}, 150 {"sect", 0xA7}, 151 {"shy", 0xAD}, 152 {"sup1", 0xB9}, 153 {"sup2", 0xB2}, 154 {"sup3", 0xB3}, 155 {"szlig", 0xDF}, 156 {"thorn", 0xFE}, 157 {"times", 0xD7}, 158 {"uacute", 0xFA}, 159 {"ucirc", 0xFB}, 160 {"ugrave", 0xF9}, 161 {"uml", 0xA8}, 162 {"uuml", 0xFC}, 163 {"yacute", 0xFD}, 164 {"yen", 0xA5}, 165 {"yuml", 0xFF} 166 }; 167 168 static inline BOOL navigation_visible(HHInfo *info) 169 { 170 return ((info->WinType.fsWinProperties & HHWIN_PROP_TRI_PANE) && !info->WinType.fNotExpanded); 171 } 172 173 /* Loads a string from the resource file */ 174 #ifdef __REACTOS__ 175 LPWSTR HH_LoadString(DWORD dwID) 176 { 177 LPWSTR string = NULL; 178 LPCWSTR stringresource; 179 int iSize; 180 181 iSize = LoadStringW(hhctrl_hinstance, dwID, (LPWSTR)&stringresource, 0); 182 183 string = heap_alloc((iSize + 2) * sizeof(WCHAR)); /* some strings (tab text) needs double-null termination */ 184 memcpy(string, stringresource, iSize * sizeof(WCHAR)); 185 string[iSize] = UNICODE_NULL; 186 187 return string; 188 } 189 #else 190 static LPWSTR HH_LoadString(DWORD dwID) 191 { 192 LPWSTR string = NULL; 193 LPCWSTR stringresource; 194 int iSize; 195 196 iSize = LoadStringW(hhctrl_hinstance, dwID, (LPWSTR)&stringresource, 0); 197 198 string = heap_alloc((iSize + 2) * sizeof(WCHAR)); /* some strings (tab text) needs double-null termination */ 199 memcpy(string, stringresource, iSize*sizeof(WCHAR)); 200 string[iSize] = 0; 201 202 return string; 203 } 204 #endif 205 206 static HRESULT navigate_url(HHInfo *info, LPCWSTR surl) 207 { 208 VARIANT url; 209 HRESULT hres; 210 211 TRACE("%s\n", debugstr_w(surl)); 212 213 V_VT(&url) = VT_BSTR; 214 V_BSTR(&url) = SysAllocString(surl); 215 216 hres = IWebBrowser2_Navigate2(info->web_browser->web_browser, &url, 0, 0, 0, 0); 217 218 VariantClear(&url); 219 220 if(FAILED(hres)) 221 TRACE("Navigation failed: %08x\n", hres); 222 223 return hres; 224 } 225 226 BOOL NavigateToUrl(HHInfo *info, LPCWSTR surl) 227 { 228 ChmPath chm_path; 229 BOOL ret; 230 HRESULT hres; 231 232 static const WCHAR url_indicator[] = {':', '/', '/', 0}; 233 234 TRACE("%s\n", debugstr_w(surl)); 235 236 if (wcsstr(surl, url_indicator)) { 237 hres = navigate_url(info, surl); 238 if(SUCCEEDED(hres)) 239 return TRUE; 240 } /* look up in chm if it doesn't look like a full url */ 241 242 SetChmPath(&chm_path, info->pCHMInfo->szFile, surl); 243 ret = NavigateToChm(info, chm_path.chm_file, chm_path.chm_index); 244 245 heap_free(chm_path.chm_file); 246 heap_free(chm_path.chm_index); 247 248 return ret; 249 } 250 251 static BOOL AppendFullPathURL(LPCWSTR file, LPWSTR buf, LPCWSTR index) 252 { 253 static const WCHAR url_format[] = 254 {'m','k',':','@','M','S','I','T','S','t','o','r','e',':','%','s',':',':','%','s','%','s',0}; 255 static const WCHAR slash[] = {'/',0}; 256 static const WCHAR empty[] = {0}; 257 WCHAR full_path[MAX_PATH]; 258 259 TRACE("%s %p %s\n", debugstr_w(file), buf, debugstr_w(index)); 260 261 if (!GetFullPathNameW(file, ARRAY_SIZE(full_path), full_path, NULL)) { 262 WARN("GetFullPathName failed: %u\n", GetLastError()); 263 return FALSE; 264 } 265 266 wsprintfW(buf, url_format, full_path, (!index || index[0] == '/') ? empty : slash, index); 267 return TRUE; 268 } 269 270 BOOL NavigateToChm(HHInfo *info, LPCWSTR file, LPCWSTR index) 271 { 272 WCHAR buf[INTERNET_MAX_URL_LENGTH]; 273 274 TRACE("%p %s %s\n", info, debugstr_w(file), debugstr_w(index)); 275 276 if ((!info->web_browser) || !AppendFullPathURL(file, buf, index)) 277 return FALSE; 278 279 return SUCCEEDED(navigate_url(info, buf)); 280 } 281 282 static void DoSync(HHInfo *info) 283 { 284 WCHAR buf[INTERNET_MAX_URL_LENGTH]; 285 HRESULT hres; 286 BSTR url; 287 288 hres = IWebBrowser2_get_LocationURL(info->web_browser->web_browser, &url); 289 290 if (FAILED(hres)) 291 { 292 WARN("get_LocationURL failed: %08x\n", hres); 293 return; 294 } 295 296 /* If we're not currently viewing a page in the active .chm file, abort */ 297 if ((!AppendFullPathURL(info->WinType.pszFile, buf, NULL)) || (lstrlenW(buf) > lstrlenW(url))) 298 { 299 SysFreeString(url); 300 return; 301 } 302 303 if (lstrcmpiW(buf, url) > 0) 304 { 305 static const WCHAR delimW[] = {':',':','/',0}; 306 const WCHAR *index; 307 308 index = wcsstr(url, delimW); 309 310 if (index) 311 ActivateContentTopic(info->tabs[TAB_CONTENTS].hwnd, index + 3, info->content); /* skip over ::/ */ 312 } 313 314 SysFreeString(url); 315 } 316 317 /* Size Bar */ 318 319 #define SIZEBAR_WIDTH 4 320 321 static const WCHAR szSizeBarClass[] = { 322 'H','H',' ','S','i','z','e','B','a','r',0 323 }; 324 325 /* Draw the SizeBar */ 326 static void SB_OnPaint(HWND hWnd) 327 { 328 PAINTSTRUCT ps; 329 HDC hdc; 330 RECT rc; 331 332 hdc = BeginPaint(hWnd, &ps); 333 334 GetClientRect(hWnd, &rc); 335 336 /* dark frame */ 337 rc.right += 1; 338 rc.bottom -= 1; 339 FrameRect(hdc, &rc, GetStockObject(GRAY_BRUSH)); 340 341 /* white highlight */ 342 SelectObject(hdc, GetStockObject(WHITE_PEN)); 343 MoveToEx(hdc, rc.right, 1, NULL); 344 LineTo(hdc, 1, 1); 345 LineTo(hdc, 1, rc.bottom - 1); 346 347 348 MoveToEx(hdc, 0, rc.bottom, NULL); 349 LineTo(hdc, rc.right, rc.bottom); 350 351 EndPaint(hWnd, &ps); 352 } 353 354 static void SB_OnLButtonDown(HWND hWnd, WPARAM wParam, LPARAM lParam) 355 { 356 SetCapture(hWnd); 357 } 358 359 static void SB_OnLButtonUp(HWND hWnd, WPARAM wParam, LPARAM lParam) 360 { 361 HHInfo *pHHInfo = (HHInfo *)GetWindowLongPtrW(hWnd, 0); 362 POINT pt; 363 364 pt.x = (short)LOWORD(lParam); 365 pt.y = (short)HIWORD(lParam); 366 367 /* update the window sizes */ 368 pHHInfo->WinType.iNavWidth += pt.x; 369 Help_OnSize(hWnd); 370 371 ReleaseCapture(); 372 } 373 374 static void SB_OnMouseMove(HWND hWnd, WPARAM wParam, LPARAM lParam) 375 { 376 /* ignore WM_MOUSEMOVE if not dragging the SizeBar */ 377 if (!(wParam & MK_LBUTTON)) 378 return; 379 } 380 381 static LRESULT CALLBACK SizeBar_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 382 { 383 switch (message) 384 { 385 case WM_LBUTTONDOWN: 386 SB_OnLButtonDown(hWnd, wParam, lParam); 387 break; 388 case WM_LBUTTONUP: 389 SB_OnLButtonUp(hWnd, wParam, lParam); 390 break; 391 case WM_MOUSEMOVE: 392 SB_OnMouseMove(hWnd, wParam, lParam); 393 break; 394 case WM_PAINT: 395 SB_OnPaint(hWnd); 396 break; 397 default: 398 return DefWindowProcW(hWnd, message, wParam, lParam); 399 } 400 401 return 0; 402 } 403 404 static void HH_RegisterSizeBarClass(HHInfo *pHHInfo) 405 { 406 WNDCLASSEXW wcex; 407 408 wcex.cbSize = sizeof(WNDCLASSEXW); 409 wcex.style = 0; 410 wcex.lpfnWndProc = SizeBar_WndProc; 411 wcex.cbClsExtra = 0; 412 wcex.cbWndExtra = sizeof(LONG_PTR); 413 wcex.hInstance = hhctrl_hinstance; 414 wcex.hIcon = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); 415 wcex.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_SIZEWE); 416 wcex.hbrBackground = (HBRUSH)(COLOR_MENU + 1); 417 wcex.lpszMenuName = NULL; 418 wcex.lpszClassName = szSizeBarClass; 419 wcex.hIconSm = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); 420 421 RegisterClassExW(&wcex); 422 } 423 424 static void SB_GetSizeBarRect(HHInfo *info, RECT *rc) 425 { 426 RECT rectWND, rectTB, rectNP; 427 428 GetClientRect(info->WinType.hwndHelp, &rectWND); 429 GetClientRect(info->WinType.hwndToolBar, &rectTB); 430 GetClientRect(info->WinType.hwndNavigation, &rectNP); 431 432 SetRect(rc, rectNP.right, rectTB.bottom, SIZEBAR_WIDTH, rectWND.bottom - rectTB.bottom); 433 } 434 435 static BOOL HH_AddSizeBar(HHInfo *pHHInfo) 436 { 437 HWND hWnd; 438 HWND hwndParent = pHHInfo->WinType.hwndHelp; 439 DWORD dwStyles = WS_CHILDWINDOW | WS_OVERLAPPED; 440 DWORD dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR; 441 RECT rc; 442 443 if (navigation_visible(pHHInfo)) 444 dwStyles |= WS_VISIBLE; 445 446 SB_GetSizeBarRect(pHHInfo, &rc); 447 448 hWnd = CreateWindowExW(dwExStyles, szSizeBarClass, szEmpty, dwStyles, 449 rc.left, rc.top, rc.right, rc.bottom, 450 hwndParent, NULL, hhctrl_hinstance, NULL); 451 if (!hWnd) 452 return FALSE; 453 454 /* store the pointer to the HH info struct */ 455 SetWindowLongPtrW(hWnd, 0, (LONG_PTR)pHHInfo); 456 457 pHHInfo->hwndSizeBar = hWnd; 458 return TRUE; 459 } 460 461 /* Child Window */ 462 463 static const WCHAR szChildClass[] = { 464 'H','H',' ','C','h','i','l','d',0 465 }; 466 467 static LRESULT Child_OnPaint(HWND hWnd) 468 { 469 PAINTSTRUCT ps; 470 HDC hdc; 471 RECT rc; 472 473 hdc = BeginPaint(hWnd, &ps); 474 475 /* Only paint the Navigation pane, identified by the fact 476 * that it has a child window 477 */ 478 if (GetWindow(hWnd, GW_CHILD)) 479 { 480 GetClientRect(hWnd, &rc); 481 482 /* set the border color */ 483 SelectObject(hdc, GetStockObject(DC_PEN)); 484 SetDCPenColor(hdc, GetSysColor(COLOR_BTNSHADOW)); 485 486 /* Draw the top border */ 487 LineTo(hdc, rc.right, 0); 488 489 SelectObject(hdc, GetStockObject(WHITE_PEN)); 490 MoveToEx(hdc, 0, 1, NULL); 491 LineTo(hdc, rc.right, 1); 492 } 493 494 EndPaint(hWnd, &ps); 495 496 return 0; 497 } 498 499 static void ResizeTabChild(HHInfo *info, int tab) 500 { 501 HWND hwnd = info->tabs[tab].hwnd; 502 INT width, height; 503 RECT rect, tabrc; 504 DWORD cnt; 505 506 GetClientRect(info->WinType.hwndNavigation, &rect); 507 SendMessageW(info->hwndTabCtrl, TCM_GETITEMRECT, 0, (LPARAM)&tabrc); 508 cnt = SendMessageW(info->hwndTabCtrl, TCM_GETROWCOUNT, 0, 0); 509 510 rect.left = TAB_MARGIN; 511 rect.top = TAB_TOP_PADDING + cnt*(tabrc.bottom-tabrc.top) + TAB_MARGIN; 512 rect.right -= TAB_RIGHT_PADDING + TAB_MARGIN; 513 rect.bottom -= TAB_MARGIN; 514 width = rect.right-rect.left; 515 height = rect.bottom-rect.top; 516 517 SetWindowPos(hwnd, NULL, rect.left, rect.top, width, height, 518 SWP_NOZORDER | SWP_NOACTIVATE); 519 520 switch (tab) 521 { 522 case TAB_INDEX: { 523 int scroll_width = GetSystemMetrics(SM_CXVSCROLL); 524 int border_width = GetSystemMetrics(SM_CXBORDER); 525 int edge_width = GetSystemMetrics(SM_CXEDGE); 526 527 /* Resize the tab widget column to perfectly fit the tab window and 528 * leave sufficient space for the scroll widget. 529 */ 530 SendMessageW(info->tabs[TAB_INDEX].hwnd, LVM_SETCOLUMNWIDTH, 0, 531 width-scroll_width-2*border_width-2*edge_width); 532 533 break; 534 } 535 case TAB_SEARCH: { 536 int scroll_width = GetSystemMetrics(SM_CXVSCROLL); 537 int border_width = GetSystemMetrics(SM_CXBORDER); 538 int edge_width = GetSystemMetrics(SM_CXEDGE); 539 int top_pos = 0; 540 541 SetWindowPos(info->search.hwndEdit, NULL, 0, top_pos, width, 542 EDIT_HEIGHT, SWP_NOZORDER | SWP_NOACTIVATE); 543 top_pos += EDIT_HEIGHT + TAB_MARGIN; 544 SetWindowPos(info->search.hwndList, NULL, 0, top_pos, width, 545 height-top_pos, SWP_NOZORDER | SWP_NOACTIVATE); 546 /* Resize the tab widget column to perfectly fit the tab window and 547 * leave sufficient space for the scroll widget. 548 */ 549 SendMessageW(info->search.hwndList, LVM_SETCOLUMNWIDTH, 0, 550 width-scroll_width-2*border_width-2*edge_width); 551 552 break; 553 } 554 } 555 } 556 557 static LRESULT Child_OnSize(HWND hwnd) 558 { 559 HHInfo *info = (HHInfo*)GetWindowLongPtrW(hwnd, 0); 560 RECT rect; 561 562 if(!info || hwnd != info->WinType.hwndNavigation) 563 return 0; 564 565 GetClientRect(hwnd, &rect); 566 SetWindowPos(info->hwndTabCtrl, HWND_TOP, 0, 0, 567 rect.right - TAB_RIGHT_PADDING, 568 rect.bottom - TAB_TOP_PADDING, SWP_NOMOVE); 569 570 ResizeTabChild(info, TAB_CONTENTS); 571 ResizeTabChild(info, TAB_INDEX); 572 ResizeTabChild(info, TAB_SEARCH); 573 return 0; 574 } 575 576 static LRESULT OnTabChange(HWND hwnd) 577 { 578 HHInfo *info = (HHInfo*)GetWindowLongPtrW(hwnd, 0); 579 int tab_id, tab_index, i; 580 581 TRACE("%p\n", hwnd); 582 583 if (!info) 584 return 0; 585 586 if(info->tabs[info->current_tab].hwnd) 587 ShowWindow(info->tabs[info->current_tab].hwnd, SW_HIDE); 588 589 tab_id = (int) SendMessageW(info->hwndTabCtrl, TCM_GETCURSEL, 0, 0); 590 /* convert the ID of the tab to an index in our tab list */ 591 tab_index = -1; 592 for (i=0; i<TAB_NUMTABS; i++) 593 { 594 if (info->tabs[i].id == tab_id) 595 { 596 tab_index = i; 597 break; 598 } 599 } 600 if (tab_index == -1) 601 { 602 FIXME("Tab ID %d does not correspond to a valid index in the tab list.\n", tab_id); 603 return 0; 604 } 605 info->current_tab = tab_index; 606 607 if(info->tabs[info->current_tab].hwnd) 608 ShowWindow(info->tabs[info->current_tab].hwnd, SW_SHOW); 609 610 return 0; 611 } 612 613 static LRESULT OnTopicChange(HHInfo *info, void *user_data) 614 { 615 LPCWSTR chmfile = NULL, name = NULL, local = NULL; 616 ContentItem *citer; 617 SearchItem *siter; 618 IndexItem *iiter; 619 620 if(!user_data || !info) 621 return 0; 622 623 switch (info->current_tab) 624 { 625 case TAB_CONTENTS: 626 citer = (ContentItem *) user_data; 627 name = citer->name; 628 local = citer->local; 629 while(citer) { 630 if(citer->merge.chm_file) { 631 chmfile = citer->merge.chm_file; 632 break; 633 } 634 citer = citer->parent; 635 } 636 break; 637 case TAB_INDEX: 638 iiter = (IndexItem *) user_data; 639 if(iiter->nItems == 0) { 640 FIXME("No entries for this item!\n"); 641 return 0; 642 } 643 if(iiter->nItems > 1) { 644 int i = 0; 645 LVITEMW lvi; 646 647 SendMessageW(info->popup.hwndList, LVM_DELETEALLITEMS, 0, 0); 648 for(i=0;i<iiter->nItems;i++) { 649 IndexSubItem *item = &iiter->items[i]; 650 WCHAR *name = iiter->keyword; 651 652 if(!item->name) 653 item->name = GetDocumentTitle(info->pCHMInfo, item->local); 654 if(item->name) 655 name = item->name; 656 memset(&lvi, 0, sizeof(lvi)); 657 lvi.iItem = i; 658 lvi.mask = LVIF_TEXT|LVIF_PARAM; 659 lvi.cchTextMax = lstrlenW(name)+1; 660 lvi.pszText = name; 661 lvi.lParam = (LPARAM) item; 662 SendMessageW(info->popup.hwndList, LVM_INSERTITEMW, 0, (LPARAM)&lvi); 663 } 664 ShowWindow(info->popup.hwndPopup, SW_SHOW); 665 return 0; 666 } 667 name = iiter->items[0].name; 668 local = iiter->items[0].local; 669 chmfile = iiter->merge.chm_file; 670 break; 671 case TAB_SEARCH: 672 siter = (SearchItem *) user_data; 673 name = siter->filename; 674 local = siter->filename; 675 chmfile = info->pCHMInfo->szFile; 676 break; 677 default: 678 FIXME("Unhandled operation for this tab!\n"); 679 return 0; 680 } 681 682 if(!chmfile) 683 { 684 FIXME("No help file found for this item!\n"); 685 return 0; 686 } 687 688 TRACE("name %s loal %s\n", debugstr_w(name), debugstr_w(local)); 689 690 NavigateToChm(info, chmfile, local); 691 return 0; 692 } 693 694 /* Capture the Enter/Return key and send it up to Child_WndProc as an NM_RETURN message */ 695 static LRESULT CALLBACK EditChild_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 696 { 697 WNDPROC editWndProc = (WNDPROC)GetWindowLongPtrW(hWnd, GWLP_USERDATA); 698 699 if(message == WM_KEYUP && wParam == VK_RETURN) 700 { 701 NMHDR nmhdr; 702 703 nmhdr.hwndFrom = hWnd; 704 nmhdr.code = NM_RETURN; 705 SendMessageW(GetParent(GetParent(hWnd)), WM_NOTIFY, wParam, (LPARAM)&nmhdr); 706 } 707 return editWndProc(hWnd, message, wParam, lParam); 708 } 709 710 static LRESULT CALLBACK Child_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 711 { 712 switch (message) 713 { 714 case WM_PAINT: 715 return Child_OnPaint(hWnd); 716 case WM_SIZE: 717 return Child_OnSize(hWnd); 718 case WM_NOTIFY: { 719 HHInfo *info = (HHInfo*)GetWindowLongPtrW(hWnd, 0); 720 NMHDR *nmhdr = (NMHDR*)lParam; 721 722 switch(nmhdr->code) { 723 case TCN_SELCHANGE: 724 return OnTabChange(hWnd); 725 case TVN_SELCHANGEDW: 726 return OnTopicChange(info, (void*)((NMTREEVIEWW *)lParam)->itemNew.lParam); 727 case TVN_ITEMEXPANDINGW: { 728 TVITEMW *item = &((NMTREEVIEWW *)lParam)->itemNew; 729 HWND hwndTreeView = info->tabs[TAB_CONTENTS].hwnd; 730 731 item->mask = TVIF_IMAGE|TVIF_SELECTEDIMAGE; 732 if (item->state & TVIS_EXPANDED) 733 { 734 item->iImage = HHTV_FOLDER; 735 item->iSelectedImage = HHTV_FOLDER; 736 } 737 else 738 { 739 item->iImage = HHTV_OPENFOLDER; 740 item->iSelectedImage = HHTV_OPENFOLDER; 741 } 742 SendMessageW(hwndTreeView, TVM_SETITEMW, 0, (LPARAM)item); 743 return 0; 744 } 745 case NM_DBLCLK: 746 if(!info) 747 return 0; 748 switch(info->current_tab) 749 { 750 case TAB_INDEX: 751 return OnTopicChange(info, (void*)((NMITEMACTIVATE *)lParam)->lParam); 752 case TAB_SEARCH: 753 return OnTopicChange(info, (void*)((NMITEMACTIVATE *)lParam)->lParam); 754 } 755 break; 756 case NM_RETURN: 757 if(!info) 758 return 0; 759 switch(info->current_tab) { 760 case TAB_INDEX: { 761 HWND hwndList = info->tabs[TAB_INDEX].hwnd; 762 LVITEMW lvItem; 763 764 lvItem.iItem = (int) SendMessageW(hwndList, LVM_GETSELECTIONMARK, 0, 0); 765 lvItem.mask = TVIF_PARAM; 766 SendMessageW(hwndList, LVM_GETITEMW, 0, (LPARAM)&lvItem); 767 OnTopicChange(info, (void*) lvItem.lParam); 768 return 0; 769 } 770 case TAB_SEARCH: { 771 if(nmhdr->hwndFrom == info->search.hwndEdit) { 772 char needle[100]; 773 DWORD i, len; 774 775 len = GetWindowTextA(info->search.hwndEdit, needle, sizeof(needle)); 776 if(!len) 777 { 778 FIXME("Unable to get search text.\n"); 779 return 0; 780 } 781 /* Convert the requested text for comparison later against the 782 * lower case version of HTML file contents. 783 */ 784 for(i=0;i<len;i++) 785 needle[i] = tolower(needle[i]); 786 InitSearch(info, needle); 787 return 0; 788 }else if(nmhdr->hwndFrom == info->search.hwndList) { 789 HWND hwndList = info->search.hwndList; 790 LVITEMW lvItem; 791 792 lvItem.iItem = (int) SendMessageW(hwndList, LVM_GETSELECTIONMARK, 0, 0); 793 lvItem.mask = TVIF_PARAM; 794 SendMessageW(hwndList, LVM_GETITEMW, 0, (LPARAM)&lvItem); 795 OnTopicChange(info, (void*) lvItem.lParam); 796 return 0; 797 } 798 break; 799 } 800 } 801 break; 802 } 803 break; 804 } 805 default: 806 return DefWindowProcW(hWnd, message, wParam, lParam); 807 } 808 809 return 0; 810 } 811 812 static void HH_RegisterChildWndClass(HHInfo *pHHInfo) 813 { 814 WNDCLASSEXW wcex; 815 816 wcex.cbSize = sizeof(WNDCLASSEXW); 817 wcex.style = 0; 818 wcex.lpfnWndProc = Child_WndProc; 819 wcex.cbClsExtra = 0; 820 wcex.cbWndExtra = sizeof(LONG_PTR); 821 wcex.hInstance = hhctrl_hinstance; 822 wcex.hIcon = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); 823 wcex.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); 824 wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); 825 wcex.lpszMenuName = NULL; 826 wcex.lpszClassName = szChildClass; 827 wcex.hIconSm = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); 828 829 RegisterClassExW(&wcex); 830 } 831 832 /* Toolbar */ 833 834 #define ICON_SIZE 20 835 836 static void DisplayPopupMenu(HHInfo *info) 837 { 838 HMENU menu, submenu; 839 TBBUTTONINFOW button; 840 MENUITEMINFOW item; 841 POINT coords; 842 RECT rect; 843 DWORD index; 844 845 menu = LoadMenuW(hhctrl_hinstance, MAKEINTRESOURCEW(MENU_POPUP)); 846 847 if (!menu) 848 return; 849 850 submenu = GetSubMenu(menu, 0); 851 852 /* Update the Show/Hide menu item */ 853 item.cbSize = sizeof(MENUITEMINFOW); 854 item.fMask = MIIM_FTYPE | MIIM_STATE | MIIM_STRING; 855 item.fType = MFT_STRING; 856 item.fState = MF_ENABLED; 857 858 if (info->WinType.fNotExpanded) 859 item.dwTypeData = HH_LoadString(IDS_SHOWTABS); 860 else 861 item.dwTypeData = HH_LoadString(IDS_HIDETABS); 862 863 SetMenuItemInfoW(submenu, IDTB_EXPAND, FALSE, &item); 864 heap_free(item.dwTypeData); 865 866 /* Find the index toolbar button */ 867 button.cbSize = sizeof(TBBUTTONINFOW); 868 button.dwMask = TBIF_COMMAND; 869 index = SendMessageW(info->WinType.hwndToolBar, TB_GETBUTTONINFOW, IDTB_OPTIONS, (LPARAM) &button); 870 871 if (index == -1) 872 return; 873 874 /* Get position */ 875 SendMessageW(info->WinType.hwndToolBar, TB_GETITEMRECT, index, (LPARAM) &rect); 876 877 coords.x = rect.left; 878 coords.y = rect.bottom; 879 880 ClientToScreen(info->WinType.hwndToolBar, &coords); 881 TrackPopupMenu(submenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON | TPM_NOANIMATION, coords.x, coords.y, 0, info->WinType.hwndHelp, NULL); 882 } 883 884 static void TB_OnClick(HWND hWnd, DWORD dwID) 885 { 886 HHInfo *info = (HHInfo *)GetWindowLongPtrW(hWnd, 0); 887 888 switch (dwID) 889 { 890 case IDTB_STOP: 891 DoPageAction(info->web_browser, WB_STOP); 892 break; 893 case IDTB_REFRESH: 894 DoPageAction(info->web_browser, WB_REFRESH); 895 break; 896 case IDTB_BACK: 897 DoPageAction(info->web_browser, WB_GOBACK); 898 break; 899 case IDTB_HOME: 900 NavigateToChm(info, info->pCHMInfo->szFile, info->WinType.pszHome); 901 break; 902 case IDTB_FORWARD: 903 DoPageAction(info->web_browser, WB_GOFORWARD); 904 break; 905 case IDTB_PRINT: 906 DoPageAction(info->web_browser, WB_PRINT); 907 break; 908 case IDTB_EXPAND: 909 case IDTB_CONTRACT: 910 ExpandContract(info); 911 break; 912 case IDTB_SYNC: 913 DoSync(info); 914 break; 915 case IDTB_OPTIONS: 916 DisplayPopupMenu(info); 917 break; 918 case IDTB_NOTES: 919 case IDTB_CONTENTS: 920 case IDTB_INDEX: 921 case IDTB_SEARCH: 922 case IDTB_HISTORY: 923 case IDTB_FAVORITES: 924 /* These are officially unimplemented as of the Windows 7 SDK */ 925 break; 926 case IDTB_BROWSE_FWD: 927 case IDTB_BROWSE_BACK: 928 case IDTB_JUMP1: 929 case IDTB_JUMP2: 930 case IDTB_CUSTOMIZE: 931 case IDTB_ZOOM: 932 case IDTB_TOC_NEXT: 933 case IDTB_TOC_PREV: 934 break; 935 } 936 } 937 938 static void TB_AddButton(TBBUTTON *pButtons, DWORD dwIndex, DWORD dwID, DWORD dwBitmap) 939 { 940 pButtons[dwIndex].iBitmap = dwBitmap; 941 pButtons[dwIndex].idCommand = dwID; 942 pButtons[dwIndex].fsState = TBSTATE_ENABLED; 943 pButtons[dwIndex].fsStyle = BTNS_BUTTON; 944 pButtons[dwIndex].dwData = 0; 945 pButtons[dwIndex].iString = 0; 946 } 947 948 static void TB_AddButtonsFromFlags(HHInfo *pHHInfo, TBBUTTON *pButtons, DWORD dwButtonFlags, LPDWORD pdwNumButtons) 949 { 950 int nHistBitmaps = 0, nStdBitmaps = 0, nHHBitmaps = 0; 951 HWND hToolbar = pHHInfo->WinType.hwndToolBar; 952 TBADDBITMAP tbAB; 953 DWORD unsupported; 954 955 /* Common bitmaps */ 956 tbAB.hInst = HINST_COMMCTRL; 957 tbAB.nID = IDB_HIST_LARGE_COLOR; 958 nHistBitmaps = SendMessageW(hToolbar, TB_ADDBITMAP, 0, (LPARAM)&tbAB); 959 tbAB.nID = IDB_STD_LARGE_COLOR; 960 nStdBitmaps = SendMessageW(hToolbar, TB_ADDBITMAP, 0, (LPARAM)&tbAB); 961 /* hhctrl.ocx bitmaps */ 962 tbAB.hInst = hhctrl_hinstance; 963 tbAB.nID = IDB_HHTOOLBAR; 964 nHHBitmaps = SendMessageW(hToolbar, TB_ADDBITMAP, HHTB_NUMBITMAPS, (LPARAM)&tbAB); 965 966 *pdwNumButtons = 0; 967 968 unsupported = dwButtonFlags & (HHWIN_BUTTON_BROWSE_FWD | 969 HHWIN_BUTTON_BROWSE_BCK | HHWIN_BUTTON_NOTES | HHWIN_BUTTON_CONTENTS | 970 HHWIN_BUTTON_INDEX | HHWIN_BUTTON_SEARCH | HHWIN_BUTTON_HISTORY | 971 HHWIN_BUTTON_FAVORITES | HHWIN_BUTTON_JUMP1 | HHWIN_BUTTON_JUMP2 | 972 HHWIN_BUTTON_ZOOM | HHWIN_BUTTON_TOC_NEXT | HHWIN_BUTTON_TOC_PREV); 973 if (unsupported) 974 FIXME("got asked for unsupported buttons: %06x\n", unsupported); 975 976 if (dwButtonFlags & HHWIN_BUTTON_EXPAND) 977 { 978 TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_EXPAND, nHHBitmaps + HHTB_EXPAND); 979 TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_CONTRACT, nHHBitmaps + HHTB_CONTRACT); 980 981 if (pHHInfo->WinType.fNotExpanded) 982 pButtons[1].fsState |= TBSTATE_HIDDEN; 983 else 984 pButtons[0].fsState |= TBSTATE_HIDDEN; 985 } 986 987 if (dwButtonFlags & HHWIN_BUTTON_BACK) 988 TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_BACK, nHistBitmaps + HIST_BACK); 989 990 if (dwButtonFlags & HHWIN_BUTTON_FORWARD) 991 TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_FORWARD, nHistBitmaps + HIST_FORWARD); 992 993 if (dwButtonFlags & HHWIN_BUTTON_STOP) 994 TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_STOP, nHHBitmaps + HHTB_STOP); 995 996 if (dwButtonFlags & HHWIN_BUTTON_REFRESH) 997 TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_REFRESH, nHHBitmaps + HHTB_REFRESH); 998 999 if (dwButtonFlags & HHWIN_BUTTON_HOME) 1000 TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_HOME, nHHBitmaps + HHTB_HOME); 1001 1002 if (dwButtonFlags & HHWIN_BUTTON_SYNC) 1003 TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_SYNC, nHHBitmaps + HHTB_SYNC); 1004 1005 if (dwButtonFlags & HHWIN_BUTTON_OPTIONS) 1006 TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_OPTIONS, nStdBitmaps + STD_PROPERTIES); 1007 1008 if (dwButtonFlags & HHWIN_BUTTON_PRINT) 1009 TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_PRINT, nStdBitmaps + STD_PRINT); 1010 } 1011 1012 static BOOL HH_AddToolbar(HHInfo *pHHInfo) 1013 { 1014 HWND hToolbar; 1015 HWND hwndParent = pHHInfo->WinType.hwndHelp; 1016 DWORD toolbarFlags; 1017 TBBUTTON buttons[IDTB_TOC_PREV - IDTB_EXPAND]; 1018 DWORD dwStyles, dwExStyles; 1019 DWORD dwNumButtons, dwIndex; 1020 1021 if (pHHInfo->WinType.fsWinProperties & HHWIN_PARAM_TB_FLAGS) 1022 toolbarFlags = pHHInfo->WinType.fsToolBarFlags; 1023 else 1024 toolbarFlags = HHWIN_DEF_BUTTONS; 1025 1026 dwStyles = WS_CHILDWINDOW | TBSTYLE_FLAT | TBSTYLE_WRAPABLE | TBSTYLE_TOOLTIPS | CCS_NODIVIDER; 1027 dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR; 1028 1029 hToolbar = CreateWindowExW(dwExStyles, TOOLBARCLASSNAMEW, NULL, dwStyles, 1030 0, 0, 0, 0, hwndParent, NULL, 1031 hhctrl_hinstance, NULL); 1032 if (!hToolbar) 1033 return FALSE; 1034 pHHInfo->WinType.hwndToolBar = hToolbar; 1035 1036 SendMessageW(hToolbar, TB_SETBITMAPSIZE, 0, MAKELONG(ICON_SIZE, ICON_SIZE)); 1037 SendMessageW(hToolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0); 1038 SendMessageW(hToolbar, WM_SETFONT, (WPARAM)pHHInfo->hFont, TRUE); 1039 1040 TB_AddButtonsFromFlags(pHHInfo, buttons, toolbarFlags, &dwNumButtons); 1041 1042 for (dwIndex = 0; dwIndex < dwNumButtons; dwIndex++) 1043 { 1044 LPWSTR szBuf = HH_LoadString(buttons[dwIndex].idCommand); 1045 DWORD dwLen = lstrlenW(szBuf); 1046 szBuf[dwLen + 1] = 0; /* Double-null terminate */ 1047 1048 buttons[dwIndex].iString = (DWORD)SendMessageW(hToolbar, TB_ADDSTRINGW, 0, (LPARAM)szBuf); 1049 heap_free(szBuf); 1050 } 1051 1052 SendMessageW(hToolbar, TB_ADDBUTTONSW, dwNumButtons, (LPARAM)buttons); 1053 SendMessageW(hToolbar, TB_AUTOSIZE, 0, 0); 1054 if (pHHInfo->WinType.fsWinProperties & HHWIN_PROP_TRI_PANE) 1055 ShowWindow(hToolbar, SW_SHOW); 1056 1057 return TRUE; 1058 } 1059 1060 /* Navigation Pane */ 1061 1062 static void NP_GetNavigationRect(HHInfo *pHHInfo, RECT *rc) 1063 { 1064 HWND hwndParent = pHHInfo->WinType.hwndHelp; 1065 HWND hwndToolbar = pHHInfo->WinType.hwndToolBar; 1066 RECT rectWND, rectTB; 1067 1068 GetClientRect(hwndParent, &rectWND); 1069 GetClientRect(hwndToolbar, &rectTB); 1070 1071 rc->left = 0; 1072 rc->top = rectTB.bottom; 1073 rc->bottom = rectWND.bottom - rectTB.bottom; 1074 1075 if (!(pHHInfo->WinType.fsValidMembers & HHWIN_PARAM_NAV_WIDTH) && 1076 pHHInfo->WinType.iNavWidth == 0) 1077 { 1078 pHHInfo->WinType.iNavWidth = WINTYPE_DEFAULT_NAVWIDTH; 1079 } 1080 1081 rc->right = pHHInfo->WinType.iNavWidth; 1082 } 1083 1084 static DWORD NP_CreateTab(HINSTANCE hInstance, HWND hwndTabCtrl, DWORD index) 1085 { 1086 TCITEMW tie; 1087 LPWSTR tabText = HH_LoadString(index); 1088 DWORD ret; 1089 1090 tie.mask = TCIF_TEXT; 1091 tie.pszText = tabText; 1092 1093 ret = SendMessageW( hwndTabCtrl, TCM_INSERTITEMW, index, (LPARAM)&tie ); 1094 1095 heap_free(tabText); 1096 return ret; 1097 } 1098 1099 static BOOL HH_AddNavigationPane(HHInfo *info) 1100 { 1101 HWND hWnd, hwndTabCtrl; 1102 HWND hwndParent = info->WinType.hwndHelp; 1103 DWORD dwStyles = WS_CHILDWINDOW; 1104 DWORD dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR; 1105 RECT rc; 1106 1107 if (navigation_visible(info)) 1108 dwStyles |= WS_VISIBLE; 1109 1110 NP_GetNavigationRect(info, &rc); 1111 1112 hWnd = CreateWindowExW(dwExStyles, szChildClass, szEmpty, dwStyles, 1113 rc.left, rc.top, rc.right, rc.bottom, 1114 hwndParent, NULL, hhctrl_hinstance, NULL); 1115 if (!hWnd) 1116 return FALSE; 1117 1118 SetWindowLongPtrW(hWnd, 0, (LONG_PTR)info); 1119 1120 hwndTabCtrl = CreateWindowExW(dwExStyles, WC_TABCONTROLW, szEmpty, dwStyles | WS_VISIBLE, 1121 0, TAB_TOP_PADDING, 1122 rc.right - TAB_RIGHT_PADDING, 1123 rc.bottom - TAB_TOP_PADDING, 1124 hWnd, NULL, hhctrl_hinstance, NULL); 1125 if (!hwndTabCtrl) 1126 return FALSE; 1127 1128 if (*info->WinType.pszToc) 1129 info->tabs[TAB_CONTENTS].id = NP_CreateTab(hhctrl_hinstance, hwndTabCtrl, IDS_CONTENTS); 1130 1131 if (*info->WinType.pszIndex) 1132 info->tabs[TAB_INDEX].id = NP_CreateTab(hhctrl_hinstance, hwndTabCtrl, IDS_INDEX); 1133 1134 if (info->WinType.fsWinProperties & HHWIN_PROP_TAB_SEARCH) 1135 info->tabs[TAB_SEARCH].id = NP_CreateTab(hhctrl_hinstance, hwndTabCtrl, IDS_SEARCH); 1136 1137 if (info->WinType.fsWinProperties & HHWIN_PROP_TAB_FAVORITES) 1138 info->tabs[TAB_FAVORITES].id = NP_CreateTab(hhctrl_hinstance, hwndTabCtrl, IDS_FAVORITES); 1139 1140 SendMessageW(hwndTabCtrl, WM_SETFONT, (WPARAM)info->hFont, TRUE); 1141 1142 info->hwndTabCtrl = hwndTabCtrl; 1143 info->WinType.hwndNavigation = hWnd; 1144 return TRUE; 1145 } 1146 1147 /* HTML Pane */ 1148 1149 static void HP_GetHTMLRect(HHInfo *info, RECT *rc) 1150 { 1151 RECT rectTB, rectWND, rectNP, rectSB; 1152 1153 GetClientRect(info->WinType.hwndHelp, &rectWND); 1154 GetClientRect(info->hwndSizeBar, &rectSB); 1155 1156 rc->left = 0; 1157 rc->top = 0; 1158 if (navigation_visible(info)) 1159 { 1160 GetClientRect(info->WinType.hwndNavigation, &rectNP); 1161 rc->left += rectNP.right + rectSB.right; 1162 } 1163 if (info->WinType.fsWinProperties & HHWIN_PROP_TRI_PANE) 1164 { 1165 GetClientRect(info->WinType.hwndToolBar, &rectTB); 1166 rc->top += rectTB.bottom; 1167 } 1168 rc->right = rectWND.right - rc->left; 1169 rc->bottom = rectWND.bottom - rc->top; 1170 } 1171 1172 static BOOL HH_AddHTMLPane(HHInfo *pHHInfo) 1173 { 1174 HWND hWnd; 1175 HWND hwndParent = pHHInfo->WinType.hwndHelp; 1176 DWORD dwStyles = WS_CHILDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN; 1177 DWORD dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_CLIENTEDGE; 1178 RECT rc; 1179 1180 HP_GetHTMLRect(pHHInfo, &rc); 1181 1182 hWnd = CreateWindowExW(dwExStyles, szChildClass, szEmpty, dwStyles, 1183 rc.left, rc.top, rc.right, rc.bottom, 1184 hwndParent, NULL, hhctrl_hinstance, NULL); 1185 if (!hWnd) 1186 return FALSE; 1187 1188 if (!InitWebBrowser(pHHInfo, hWnd)) 1189 return FALSE; 1190 1191 /* store the pointer to the HH info struct */ 1192 SetWindowLongPtrW(hWnd, 0, (LONG_PTR)pHHInfo); 1193 1194 ShowWindow(hWnd, SW_SHOW); 1195 UpdateWindow(hWnd); 1196 1197 pHHInfo->WinType.hwndHTML = hWnd; 1198 return TRUE; 1199 } 1200 1201 static BOOL AddContentTab(HHInfo *info) 1202 { 1203 HIMAGELIST hImageList; 1204 HBITMAP hBitmap; 1205 HWND hWnd; 1206 1207 if(info->tabs[TAB_CONTENTS].id == -1) 1208 return TRUE; /* No "Contents" tab */ 1209 hWnd = CreateWindowExW(WS_EX_CLIENTEDGE, WC_TREEVIEWW, szEmpty, WS_CHILD | WS_BORDER | TVS_LINESATROOT 1210 | TVS_SHOWSELALWAYS | TVS_HASBUTTONS, 50, 50, 100, 100, 1211 info->WinType.hwndNavigation, NULL, hhctrl_hinstance, NULL); 1212 if(!hWnd) { 1213 ERR("Could not create treeview control\n"); 1214 return FALSE; 1215 } 1216 1217 hImageList = ImageList_Create(16, 16, ILC_COLOR32, 0, HHTV_NUMBITMAPS); 1218 hBitmap = LoadBitmapW(hhctrl_hinstance, MAKEINTRESOURCEW(IDB_HHTREEVIEW)); 1219 ImageList_Add(hImageList, hBitmap, NULL); 1220 SendMessageW(hWnd, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)hImageList); 1221 1222 info->contents.hImageList = hImageList; 1223 info->tabs[TAB_CONTENTS].hwnd = hWnd; 1224 ResizeTabChild(info, TAB_CONTENTS); 1225 ShowWindow(hWnd, SW_SHOW); 1226 1227 return TRUE; 1228 } 1229 1230 static BOOL AddIndexTab(HHInfo *info) 1231 { 1232 char hidden_column[] = "Column"; 1233 LVCOLUMNA lvc; 1234 1235 if(info->tabs[TAB_INDEX].id == -1) 1236 return TRUE; /* No "Index" tab */ 1237 info->tabs[TAB_INDEX].hwnd = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEWW, 1238 szEmpty, WS_CHILD | WS_BORDER | LVS_SINGLESEL | LVS_REPORT | LVS_NOCOLUMNHEADER, 50, 50, 100, 100, 1239 info->WinType.hwndNavigation, NULL, hhctrl_hinstance, NULL); 1240 if(!info->tabs[TAB_INDEX].hwnd) { 1241 ERR("Could not create ListView control\n"); 1242 return FALSE; 1243 } 1244 memset(&lvc, 0, sizeof(lvc)); 1245 lvc.mask = LVCF_TEXT; 1246 lvc.pszText = hidden_column; 1247 if(SendMessageW(info->tabs[TAB_INDEX].hwnd, LVM_INSERTCOLUMNA, 0, (LPARAM) &lvc) == -1) 1248 { 1249 ERR("Could not create ListView column\n"); 1250 return FALSE; 1251 } 1252 1253 ResizeTabChild(info, TAB_INDEX); 1254 ShowWindow(info->tabs[TAB_INDEX].hwnd, SW_HIDE); 1255 1256 return TRUE; 1257 } 1258 1259 static BOOL AddSearchTab(HHInfo *info) 1260 { 1261 HWND hwndList, hwndEdit, hwndContainer; 1262 char hidden_column[] = "Column"; 1263 WNDPROC editWndProc; 1264 LVCOLUMNA lvc; 1265 1266 if(info->tabs[TAB_SEARCH].id == -1) 1267 return TRUE; /* No "Search" tab */ 1268 hwndContainer = CreateWindowExW(WS_EX_CONTROLPARENT, szChildClass, szEmpty, 1269 WS_CHILD, 0, 0, 0, 0, info->WinType.hwndNavigation, 1270 NULL, hhctrl_hinstance, NULL); 1271 if(!hwndContainer) { 1272 ERR("Could not create search window container control.\n"); 1273 return FALSE; 1274 } 1275 hwndEdit = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDITW, szEmpty, WS_CHILD 1276 | WS_VISIBLE | ES_LEFT | SS_NOTIFY, 0, 0, 0, 0, 1277 hwndContainer, NULL, hhctrl_hinstance, NULL); 1278 if(!hwndEdit) { 1279 ERR("Could not create search ListView control.\n"); 1280 return FALSE; 1281 } 1282 if(SendMessageW(hwndEdit, WM_SETFONT, (WPARAM) info->hFont, (LPARAM) FALSE) == -1) 1283 { 1284 ERR("Could not set font for edit control.\n"); 1285 return FALSE; 1286 } 1287 editWndProc = (WNDPROC) SetWindowLongPtrW(hwndEdit, GWLP_WNDPROC, (LONG_PTR)EditChild_WndProc); 1288 if(!editWndProc) { 1289 ERR("Could not redirect messages for edit control.\n"); 1290 return FALSE; 1291 } 1292 SetWindowLongPtrW(hwndEdit, GWLP_USERDATA, (LONG_PTR)editWndProc); 1293 hwndList = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEWW, szEmpty, 1294 WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_SINGLESEL 1295 | LVS_REPORT | LVS_NOCOLUMNHEADER, 0, 0, 0, 0, 1296 hwndContainer, NULL, hhctrl_hinstance, NULL); 1297 if(!hwndList) { 1298 ERR("Could not create search ListView control.\n"); 1299 return FALSE; 1300 } 1301 memset(&lvc, 0, sizeof(lvc)); 1302 lvc.mask = LVCF_TEXT; 1303 lvc.pszText = hidden_column; 1304 if(SendMessageW(hwndList, LVM_INSERTCOLUMNA, 0, (LPARAM) &lvc) == -1) 1305 { 1306 ERR("Could not create ListView column\n"); 1307 return FALSE; 1308 } 1309 1310 info->search.hwndEdit = hwndEdit; 1311 info->search.hwndList = hwndList; 1312 info->search.hwndContainer = hwndContainer; 1313 info->tabs[TAB_SEARCH].hwnd = hwndContainer; 1314 1315 SetWindowLongPtrW(hwndContainer, 0, (LONG_PTR)info); 1316 1317 ResizeTabChild(info, TAB_SEARCH); 1318 1319 return TRUE; 1320 } 1321 1322 /* The Index tab's sub-topic popup */ 1323 1324 static void ResizePopupChild(HHInfo *info) 1325 { 1326 int scroll_width = GetSystemMetrics(SM_CXVSCROLL); 1327 int border_width = GetSystemMetrics(SM_CXBORDER); 1328 int edge_width = GetSystemMetrics(SM_CXEDGE); 1329 INT width, height; 1330 RECT rect; 1331 1332 if(!info) 1333 return; 1334 1335 GetClientRect(info->popup.hwndPopup, &rect); 1336 SetWindowPos(info->popup.hwndCallback, HWND_TOP, 0, 0, 1337 rect.right, rect.bottom, SWP_NOMOVE); 1338 1339 rect.left = TAB_MARGIN; 1340 rect.top = TAB_TOP_PADDING + TAB_MARGIN; 1341 rect.right -= TAB_RIGHT_PADDING + TAB_MARGIN; 1342 rect.bottom -= TAB_MARGIN; 1343 width = rect.right-rect.left; 1344 height = rect.bottom-rect.top; 1345 1346 SetWindowPos(info->popup.hwndList, NULL, rect.left, rect.top, width, height, 1347 SWP_NOZORDER | SWP_NOACTIVATE); 1348 1349 SendMessageW(info->popup.hwndList, LVM_SETCOLUMNWIDTH, 0, 1350 width-scroll_width-2*border_width-2*edge_width); 1351 } 1352 1353 static LRESULT CALLBACK HelpPopup_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 1354 { 1355 HHInfo *info = (HHInfo *)GetWindowLongPtrW(hWnd, 0); 1356 1357 switch (message) 1358 { 1359 case WM_SIZE: 1360 ResizePopupChild(info); 1361 return 0; 1362 case WM_DESTROY: 1363 DestroyWindow(hWnd); 1364 return 0; 1365 case WM_CLOSE: 1366 ShowWindow(hWnd, SW_HIDE); 1367 return 0; 1368 1369 default: 1370 return DefWindowProcW(hWnd, message, wParam, lParam); 1371 } 1372 1373 return 0; 1374 } 1375 1376 static LRESULT CALLBACK PopupChild_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 1377 { 1378 switch (message) 1379 { 1380 case WM_NOTIFY: { 1381 NMHDR *nmhdr = (NMHDR*)lParam; 1382 switch(nmhdr->code) 1383 { 1384 case NM_DBLCLK: { 1385 HHInfo *info = (HHInfo*)GetWindowLongPtrW(hWnd, 0); 1386 IndexSubItem *iter; 1387 1388 if(info == 0 || lParam == 0) 1389 return 0; 1390 iter = (IndexSubItem*) ((NMITEMACTIVATE *)lParam)->lParam; 1391 if(iter == 0) 1392 return 0; 1393 NavigateToChm(info, info->index->merge.chm_file, iter->local); 1394 ShowWindow(info->popup.hwndPopup, SW_HIDE); 1395 return 0; 1396 } 1397 case NM_RETURN: { 1398 HHInfo *info = (HHInfo*)GetWindowLongPtrW(hWnd, 0); 1399 IndexSubItem *iter; 1400 LVITEMW lvItem; 1401 1402 if(info == 0) 1403 return 0; 1404 1405 lvItem.iItem = (int) SendMessageW(info->popup.hwndList, LVM_GETSELECTIONMARK, 0, 0); 1406 lvItem.mask = TVIF_PARAM; 1407 SendMessageW(info->popup.hwndList, LVM_GETITEMW, 0, (LPARAM)&lvItem); 1408 iter = (IndexSubItem*) lvItem.lParam; 1409 NavigateToChm(info, info->index->merge.chm_file, iter->local); 1410 ShowWindow(info->popup.hwndPopup, SW_HIDE); 1411 return 0; 1412 } 1413 } 1414 break; 1415 } 1416 default: 1417 return DefWindowProcW(hWnd, message, wParam, lParam); 1418 } 1419 1420 return 0; 1421 } 1422 1423 static BOOL AddIndexPopup(HHInfo *info) 1424 { 1425 static const WCHAR szPopupChildClass[] = {'H','H',' ','P','o','p','u','p',' ','C','h','i','l','d',0}; 1426 static const WCHAR windowCaptionW[] = {'S','e','l','e','c','t',' ','T','o','p','i','c',':',0}; 1427 static const WCHAR windowClassW[] = {'H','H',' ','P','o','p','u','p',0}; 1428 HWND hwndList, hwndPopup, hwndCallback; 1429 char hidden_column[] = "Column"; 1430 WNDCLASSEXW wcex; 1431 LVCOLUMNA lvc; 1432 1433 if(info->tabs[TAB_INDEX].id == -1) 1434 return TRUE; /* No "Index" tab */ 1435 1436 wcex.cbSize = sizeof(WNDCLASSEXW); 1437 wcex.style = CS_HREDRAW | CS_VREDRAW; 1438 wcex.lpfnWndProc = HelpPopup_WndProc; 1439 wcex.cbClsExtra = 0; 1440 wcex.cbWndExtra = sizeof(LONG_PTR); 1441 wcex.hInstance = hhctrl_hinstance; 1442 wcex.hIcon = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); 1443 wcex.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); 1444 wcex.hbrBackground = (HBRUSH)(COLOR_MENU + 1); 1445 wcex.lpszMenuName = NULL; 1446 wcex.lpszClassName = windowClassW; 1447 wcex.hIconSm = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); 1448 RegisterClassExW(&wcex); 1449 1450 wcex.cbSize = sizeof(WNDCLASSEXW); 1451 wcex.style = 0; 1452 wcex.lpfnWndProc = PopupChild_WndProc; 1453 wcex.cbClsExtra = 0; 1454 wcex.cbWndExtra = sizeof(LONG_PTR); 1455 wcex.hInstance = hhctrl_hinstance; 1456 wcex.hIcon = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); 1457 wcex.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); 1458 wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); 1459 wcex.lpszMenuName = NULL; 1460 wcex.lpszClassName = szPopupChildClass; 1461 wcex.hIconSm = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); 1462 RegisterClassExW(&wcex); 1463 1464 hwndPopup = CreateWindowExW(WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_APPWINDOW 1465 | WS_EX_WINDOWEDGE | WS_EX_RIGHTSCROLLBAR, 1466 windowClassW, windowCaptionW, WS_POPUPWINDOW 1467 | WS_OVERLAPPEDWINDOW | WS_VISIBLE 1468 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, CW_USEDEFAULT, 1469 CW_USEDEFAULT, 300, 200, info->WinType.hwndHelp, 1470 NULL, hhctrl_hinstance, NULL); 1471 if (!hwndPopup) 1472 return FALSE; 1473 1474 hwndCallback = CreateWindowExW(WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR, 1475 szPopupChildClass, szEmpty, WS_CHILDWINDOW | WS_VISIBLE, 1476 0, 0, 0, 0, 1477 hwndPopup, NULL, hhctrl_hinstance, NULL); 1478 if (!hwndCallback) 1479 return FALSE; 1480 1481 ShowWindow(hwndPopup, SW_HIDE); 1482 hwndList = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEWW, szEmpty, 1483 WS_CHILD | WS_BORDER | LVS_SINGLESEL | LVS_REPORT 1484 | LVS_NOCOLUMNHEADER, 50, 50, 100, 100, 1485 hwndCallback, NULL, hhctrl_hinstance, NULL); 1486 if(!hwndList) { 1487 ERR("Could not create popup ListView control\n"); 1488 return FALSE; 1489 } 1490 memset(&lvc, 0, sizeof(lvc)); 1491 lvc.mask = LVCF_TEXT; 1492 lvc.pszText = hidden_column; 1493 if(SendMessageW(hwndList, LVM_INSERTCOLUMNA, 0, (LPARAM) &lvc) == -1) 1494 { 1495 ERR("Could not create popup ListView column\n"); 1496 return FALSE; 1497 } 1498 1499 info->popup.hwndCallback = hwndCallback; 1500 info->popup.hwndPopup = hwndPopup; 1501 info->popup.hwndList = hwndList; 1502 SetWindowLongPtrW(hwndPopup, 0, (LONG_PTR)info); 1503 SetWindowLongPtrW(hwndCallback, 0, (LONG_PTR)info); 1504 1505 ResizePopupChild(info); 1506 ShowWindow(hwndList, SW_SHOW); 1507 1508 return TRUE; 1509 } 1510 1511 /* Viewer Window */ 1512 1513 static void ExpandContract(HHInfo *pHHInfo) 1514 { 1515 RECT r, nav; 1516 1517 pHHInfo->WinType.fNotExpanded = !pHHInfo->WinType.fNotExpanded; 1518 GetWindowRect(pHHInfo->WinType.hwndHelp, &r); 1519 NP_GetNavigationRect(pHHInfo, &nav); 1520 1521 /* hide/show both the nav bar and the size bar */ 1522 if (pHHInfo->WinType.fNotExpanded) 1523 { 1524 ShowWindow(pHHInfo->WinType.hwndNavigation, SW_HIDE); 1525 ShowWindow(pHHInfo->hwndSizeBar, SW_HIDE); 1526 r.left = r.left + nav.right; 1527 1528 SendMessageW(pHHInfo->WinType.hwndToolBar, TB_HIDEBUTTON, IDTB_EXPAND, MAKELPARAM(FALSE, 0)); 1529 SendMessageW(pHHInfo->WinType.hwndToolBar, TB_HIDEBUTTON, IDTB_CONTRACT, MAKELPARAM(TRUE, 0)); 1530 } 1531 else 1532 { 1533 ShowWindow(pHHInfo->WinType.hwndNavigation, SW_SHOW); 1534 ShowWindow(pHHInfo->hwndSizeBar, SW_SHOW); 1535 r.left = r.left - nav.right; 1536 1537 SendMessageW(pHHInfo->WinType.hwndToolBar, TB_HIDEBUTTON, IDTB_EXPAND, MAKELPARAM(TRUE, 0)); 1538 SendMessageW(pHHInfo->WinType.hwndToolBar, TB_HIDEBUTTON, IDTB_CONTRACT, MAKELPARAM(FALSE, 0)); 1539 } 1540 1541 MoveWindow(pHHInfo->WinType.hwndHelp, r.left, r.top, r.right-r.left, r.bottom-r.top, TRUE); 1542 } 1543 1544 static LRESULT Help_OnSize(HWND hWnd) 1545 { 1546 HHInfo *pHHInfo = (HHInfo *)GetWindowLongPtrW(hWnd, 0); 1547 DWORD dwSize; 1548 RECT rc; 1549 1550 if (!pHHInfo) 1551 return 0; 1552 1553 if (navigation_visible(pHHInfo)) 1554 { 1555 NP_GetNavigationRect(pHHInfo, &rc); 1556 SetWindowPos(pHHInfo->WinType.hwndNavigation, HWND_TOP, 0, 0, 1557 rc.right, rc.bottom, SWP_NOMOVE); 1558 1559 SB_GetSizeBarRect(pHHInfo, &rc); 1560 SetWindowPos(pHHInfo->hwndSizeBar, HWND_TOP, rc.left, rc.top, 1561 rc.right, rc.bottom, SWP_SHOWWINDOW); 1562 1563 } 1564 1565 HP_GetHTMLRect(pHHInfo, &rc); 1566 SetWindowPos(pHHInfo->WinType.hwndHTML, HWND_TOP, rc.left, rc.top, 1567 rc.right, rc.bottom, SWP_SHOWWINDOW); 1568 1569 /* Resize browser window taking the frame size into account */ 1570 dwSize = GetSystemMetrics(SM_CXFRAME); 1571 ResizeWebBrowser(pHHInfo, rc.right - dwSize, rc.bottom - dwSize); 1572 1573 return 0; 1574 } 1575 1576 void UpdateHelpWindow(HHInfo *info) 1577 { 1578 if (!info->WinType.hwndHelp) 1579 return; 1580 1581 WARN("Only the size of the window is currently updated.\n"); 1582 if (info->WinType.fsValidMembers & HHWIN_PARAM_RECT) 1583 { 1584 RECT *rect = &info->WinType.rcWindowPos; 1585 INT x, y, width, height; 1586 1587 x = rect->left; 1588 y = rect->top; 1589 width = rect->right - x; 1590 height = rect->bottom - y; 1591 SetWindowPos(info->WinType.hwndHelp, NULL, rect->left, rect->top, width, height, 1592 SWP_NOZORDER | SWP_NOACTIVATE); 1593 } 1594 } 1595 1596 static LRESULT CALLBACK Help_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 1597 { 1598 switch (message) 1599 { 1600 case WM_COMMAND: 1601 if (HIWORD(wParam) == BN_CLICKED) 1602 TB_OnClick(hWnd, LOWORD(wParam)); 1603 break; 1604 case WM_SIZE: 1605 return Help_OnSize(hWnd); 1606 case WM_CLOSE: 1607 ReleaseHelpViewer((HHInfo *)GetWindowLongPtrW(hWnd, 0)); 1608 return 0; 1609 case WM_DESTROY: 1610 if(hh_process) 1611 PostQuitMessage(0); 1612 break; 1613 1614 default: 1615 return DefWindowProcW(hWnd, message, wParam, lParam); 1616 } 1617 1618 return 0; 1619 } 1620 1621 static BOOL HH_CreateHelpWindow(HHInfo *info) 1622 { 1623 HWND hWnd; 1624 RECT winPos = info->WinType.rcWindowPos; 1625 WNDCLASSEXW wcex; 1626 DWORD dwStyles, dwExStyles; 1627 DWORD x, y, width = 0, height = 0; 1628 LPCWSTR caption; 1629 1630 static const WCHAR windowClassW[] = { 1631 'H','H',' ', 'P','a','r','e','n','t',0 1632 }; 1633 1634 wcex.cbSize = sizeof(WNDCLASSEXW); 1635 wcex.style = CS_HREDRAW | CS_VREDRAW; 1636 wcex.lpfnWndProc = Help_WndProc; 1637 wcex.cbClsExtra = 0; 1638 wcex.cbWndExtra = sizeof(LONG_PTR); 1639 wcex.hInstance = hhctrl_hinstance; 1640 #ifdef __REACTOS__ 1641 wcex.hIcon = LoadIconW(hhctrl_hinstance, MAKEINTRESOURCEW(IDI_HHICON)); 1642 #else 1643 wcex.hIcon = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); 1644 #endif 1645 wcex.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); 1646 wcex.hbrBackground = (HBRUSH)(COLOR_MENU + 1); 1647 wcex.lpszMenuName = NULL; 1648 wcex.lpszClassName = windowClassW; 1649 #ifdef __REACTOS__ 1650 wcex.hIconSm = NULL; 1651 #else 1652 wcex.hIconSm = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); 1653 #endif 1654 1655 RegisterClassExW(&wcex); 1656 1657 /* Read in window parameters if available */ 1658 if (info->WinType.fsValidMembers & HHWIN_PARAM_STYLES) 1659 { 1660 dwStyles = info->WinType.dwStyles; 1661 if (!(info->WinType.dwStyles & WS_CHILD)) 1662 dwStyles |= WS_OVERLAPPEDWINDOW; 1663 } 1664 else 1665 dwStyles = WS_OVERLAPPEDWINDOW | WS_VISIBLE | 1666 WS_CLIPSIBLINGS | WS_CLIPCHILDREN; 1667 1668 if (info->WinType.fsValidMembers & HHWIN_PARAM_EXSTYLES) 1669 dwExStyles = info->WinType.dwExStyles; 1670 else 1671 dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_APPWINDOW | 1672 WS_EX_WINDOWEDGE | WS_EX_RIGHTSCROLLBAR; 1673 1674 if (info->WinType.fsValidMembers & HHWIN_PARAM_RECT) 1675 { 1676 x = winPos.left; 1677 y = winPos.top; 1678 width = winPos.right - x; 1679 height = winPos.bottom - y; 1680 } 1681 if (!width || !height) 1682 { 1683 x = WINTYPE_DEFAULT_X; 1684 y = WINTYPE_DEFAULT_Y; 1685 width = WINTYPE_DEFAULT_WIDTH; 1686 height = WINTYPE_DEFAULT_HEIGHT; 1687 } 1688 1689 if (!(info->WinType.fsWinProperties & HHWIN_PROP_TRI_PANE) && info->WinType.fNotExpanded) 1690 { 1691 if (!(info->WinType.fsValidMembers & HHWIN_PARAM_NAV_WIDTH) && 1692 info->WinType.iNavWidth == 0) 1693 { 1694 info->WinType.iNavWidth = WINTYPE_DEFAULT_NAVWIDTH; 1695 } 1696 1697 x += info->WinType.iNavWidth; 1698 width -= info->WinType.iNavWidth; 1699 } 1700 1701 1702 caption = info->WinType.pszCaption; 1703 if (!*caption) caption = info->pCHMInfo->defTitle; 1704 1705 hWnd = CreateWindowExW(dwExStyles, windowClassW, caption, dwStyles, x, y, width, height, 1706 info->WinType.hwndCaller, NULL, hhctrl_hinstance, NULL); 1707 if (!hWnd) 1708 return FALSE; 1709 1710 ShowWindow(hWnd, SW_SHOW); 1711 UpdateWindow(hWnd); 1712 1713 /* store the pointer to the HH info struct */ 1714 SetWindowLongPtrW(hWnd, 0, (LONG_PTR)info); 1715 1716 info->WinType.hwndHelp = hWnd; 1717 return TRUE; 1718 } 1719 1720 static void HH_CreateFont(HHInfo *pHHInfo) 1721 { 1722 LOGFONTW lf; 1723 1724 GetObjectW(GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONTW), &lf); 1725 lf.lfWeight = FW_NORMAL; 1726 lf.lfItalic = FALSE; 1727 lf.lfUnderline = FALSE; 1728 1729 pHHInfo->hFont = CreateFontIndirectW(&lf); 1730 } 1731 1732 static void HH_InitRequiredControls(DWORD dwControls) 1733 { 1734 INITCOMMONCONTROLSEX icex; 1735 1736 icex.dwSize = sizeof(INITCOMMONCONTROLSEX); 1737 icex.dwICC = dwControls; 1738 InitCommonControlsEx(&icex); 1739 } 1740 1741 /* Creates the whole package */ 1742 static BOOL CreateViewer(HHInfo *pHHInfo) 1743 { 1744 HH_CreateFont(pHHInfo); 1745 1746 if (!HH_CreateHelpWindow(pHHInfo)) 1747 return FALSE; 1748 1749 HH_InitRequiredControls(ICC_BAR_CLASSES); 1750 1751 if (!HH_AddToolbar(pHHInfo)) 1752 return FALSE; 1753 1754 HH_RegisterChildWndClass(pHHInfo); 1755 1756 if (!HH_AddNavigationPane(pHHInfo)) 1757 return FALSE; 1758 1759 HH_RegisterSizeBarClass(pHHInfo); 1760 1761 if (!HH_AddSizeBar(pHHInfo)) 1762 return FALSE; 1763 1764 if (!HH_AddHTMLPane(pHHInfo)) 1765 return FALSE; 1766 1767 if (!AddContentTab(pHHInfo)) 1768 return FALSE; 1769 1770 if (!AddIndexTab(pHHInfo)) 1771 return FALSE; 1772 1773 if (!AddIndexPopup(pHHInfo)) 1774 return FALSE; 1775 1776 if (!AddSearchTab(pHHInfo)) 1777 return FALSE; 1778 1779 InitContent(pHHInfo); 1780 InitIndex(pHHInfo); 1781 1782 pHHInfo->viewer_initialized = TRUE; 1783 return TRUE; 1784 } 1785 1786 void wintype_stringsW_free(struct wintype_stringsW *stringsW) 1787 { 1788 heap_free(stringsW->pszType); 1789 heap_free(stringsW->pszCaption); 1790 heap_free(stringsW->pszToc); 1791 heap_free(stringsW->pszIndex); 1792 heap_free(stringsW->pszFile); 1793 heap_free(stringsW->pszHome); 1794 heap_free(stringsW->pszJump1); 1795 heap_free(stringsW->pszJump2); 1796 heap_free(stringsW->pszUrlJump1); 1797 heap_free(stringsW->pszUrlJump2); 1798 } 1799 1800 void wintype_stringsA_free(struct wintype_stringsA *stringsA) 1801 { 1802 heap_free(stringsA->pszType); 1803 heap_free(stringsA->pszCaption); 1804 heap_free(stringsA->pszToc); 1805 heap_free(stringsA->pszIndex); 1806 heap_free(stringsA->pszFile); 1807 heap_free(stringsA->pszHome); 1808 heap_free(stringsA->pszJump1); 1809 heap_free(stringsA->pszJump2); 1810 heap_free(stringsA->pszUrlJump1); 1811 heap_free(stringsA->pszUrlJump2); 1812 heap_free(stringsA->pszCustomTabs); 1813 } 1814 1815 void ReleaseHelpViewer(HHInfo *info) 1816 { 1817 TRACE("(%p)\n", info); 1818 1819 if (!info) 1820 return; 1821 1822 list_remove(&info->entry); 1823 1824 wintype_stringsA_free(&info->stringsA); 1825 wintype_stringsW_free(&info->stringsW); 1826 1827 if (info->pCHMInfo) 1828 CloseCHM(info->pCHMInfo); 1829 1830 ReleaseWebBrowser(info); 1831 ReleaseContent(info); 1832 ReleaseIndex(info); 1833 ReleaseSearch(info); 1834 1835 if(info->contents.hImageList) 1836 ImageList_Destroy(info->contents.hImageList); 1837 if(info->WinType.hwndHelp) 1838 DestroyWindow(info->WinType.hwndHelp); 1839 1840 heap_free(info); 1841 OleUninitialize(); 1842 } 1843 1844 HHInfo *CreateHelpViewer(HHInfo *info, LPCWSTR filename, HWND caller) 1845 { 1846 HHInfo *tmp_info; 1847 unsigned int i; 1848 1849 if(!info) 1850 { 1851 info = heap_alloc_zero(sizeof(HHInfo)); 1852 list_add_tail(&window_list, &info->entry); 1853 } 1854 1855 /* Set the invalid tab ID (-1) as the default value for all 1856 * of the tabs, this matches a failed TCM_INSERTITEM call. 1857 */ 1858 for (i = 0; i < ARRAY_SIZE(info->tabs); i++) 1859 info->tabs[i].id = -1; 1860 1861 OleInitialize(NULL); 1862 1863 info->pCHMInfo = OpenCHM(filename); 1864 if(!info->pCHMInfo) { 1865 ReleaseHelpViewer(info); 1866 return NULL; 1867 } 1868 1869 if (!LoadWinTypeFromCHM(info)) { 1870 ReleaseHelpViewer(info); 1871 return NULL; 1872 } 1873 info->WinType.hwndCaller = caller; 1874 1875 /* If the window is already open then load the file in that existing window */ 1876 if ((tmp_info = find_window(info->WinType.pszType)) && tmp_info != info) 1877 { 1878 ReleaseHelpViewer(info); 1879 return CreateHelpViewer(tmp_info, filename, caller); 1880 } 1881 1882 if(!info->viewer_initialized && !CreateViewer(info)) { 1883 ReleaseHelpViewer(info); 1884 return NULL; 1885 } 1886 1887 return info; 1888 } 1889 1890 /* 1891 * Search the table of HTML entities and return the corresponding ANSI symbol. 1892 */ 1893 static char find_html_symbol(const char *entity, int entity_len) 1894 { 1895 int max = ARRAY_SIZE(html_encoded_symbols)-1; 1896 int min = 0, dir; 1897 1898 while(min <= max) 1899 { 1900 int pos = (min+max)/2; 1901 const char *encoded_symbol = html_encoded_symbols[pos].html_code; 1902 dir = strncmp(encoded_symbol, entity, entity_len); 1903 if(dir == 0 && !encoded_symbol[entity_len]) return html_encoded_symbols[pos].ansi_symbol; 1904 if(dir < 0) 1905 min = pos+1; 1906 else 1907 max = pos-1; 1908 } 1909 return 0; 1910 } 1911 1912 /* 1913 * Decode a string containing HTML encoded characters into a unicode string. 1914 */ 1915 WCHAR *decode_html(const char *html_fragment, int html_fragment_len, UINT code_page) 1916 { 1917 const char *h = html_fragment, *amp, *sem; 1918 char symbol, *tmp; 1919 int len, tmp_len = 0; 1920 WCHAR *unicode_text; 1921 1922 tmp = heap_alloc(html_fragment_len+1); 1923 while(1) 1924 { 1925 symbol = 0; 1926 amp = strchr(h, '&'); 1927 if(!amp) break; 1928 len = amp-h; 1929 /* Copy the characters prior to the HTML encoded character */ 1930 memcpy(&tmp[tmp_len], h, len); 1931 tmp_len += len; 1932 amp++; /* skip ampersand */ 1933 sem = strchr(amp, ';'); 1934 /* Require a semicolon after the ampersand */ 1935 if(!sem) 1936 { 1937 h = amp; 1938 tmp[tmp_len++] = '&'; 1939 continue; 1940 } 1941 /* Find the symbol either by using the ANSI character number (prefixed by the pound symbol) 1942 * or by searching the HTML entity table */ 1943 len = sem-amp; 1944 if(amp[0] == '#') 1945 { 1946 char *endnum = NULL; 1947 int tmp; 1948 1949 tmp = (char) strtol(amp, &endnum, 10); 1950 if(endnum == sem) 1951 symbol = tmp; 1952 } 1953 else 1954 symbol = find_html_symbol(amp, len); 1955 if(!symbol) 1956 { 1957 FIXME("Failed to translate HTML encoded character '&%.*s;'.\n", len, amp); 1958 h = amp; 1959 tmp[tmp_len++] = '&'; 1960 continue; 1961 } 1962 /* Insert the new symbol */ 1963 h = sem+1; 1964 tmp[tmp_len++] = symbol; 1965 } 1966 /* Convert any remaining characters */ 1967 len = html_fragment_len-(h-html_fragment); 1968 memcpy(&tmp[tmp_len], h, len); 1969 tmp_len += len; 1970 tmp[tmp_len++] = 0; /* NULL-terminate the string */ 1971 1972 len = MultiByteToWideChar(code_page, 0, tmp, tmp_len, NULL, 0); 1973 unicode_text = heap_alloc(len*sizeof(WCHAR)); 1974 MultiByteToWideChar(code_page, 0, tmp, tmp_len, unicode_text, len); 1975 heap_free(tmp); 1976 return unicode_text; 1977 } 1978 1979 /* Find the HTMLHelp structure for an existing window title */ 1980 HHInfo *find_window(const WCHAR *window) 1981 { 1982 HHInfo *info; 1983 1984 LIST_FOR_EACH_ENTRY(info, &window_list, HHInfo, entry) 1985 { 1986 if (lstrcmpW(info->WinType.pszType, window) == 0) 1987 return info; 1988 } 1989 return NULL; 1990 } 1991