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