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