1 /* 2 * hhctrl implementation 3 * 4 * Copyright 2004 Krzysztof Foltman 5 * Copyright 2007 Jacek Caban for CodeWeavers 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22 #include "hhctrl.h" 23 24 #include <rpcproxy.h> 25 26 HINSTANCE hhctrl_hinstance; 27 BOOL hh_process = FALSE; 28 29 30 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, LPVOID lpvReserved) 31 { 32 TRACE("(%p,%d,%p)\n", hInstance, fdwReason, lpvReserved); 33 34 switch (fdwReason) 35 { 36 case DLL_PROCESS_ATTACH: 37 hhctrl_hinstance = hInstance; 38 DisableThreadLibraryCalls(hInstance); 39 break; 40 } 41 return TRUE; 42 } 43 44 static const char *command_to_string(UINT command) 45 { 46 #define X(x) case x: return #x 47 switch (command) 48 { 49 X( HH_DISPLAY_TOPIC ); 50 X( HH_DISPLAY_TOC ); 51 X( HH_DISPLAY_INDEX ); 52 X( HH_DISPLAY_SEARCH ); 53 X( HH_SET_WIN_TYPE ); 54 X( HH_GET_WIN_TYPE ); 55 X( HH_GET_WIN_HANDLE ); 56 X( HH_ENUM_INFO_TYPE ); 57 X( HH_SET_INFO_TYPE ); 58 X( HH_SYNC ); 59 X( HH_RESERVED1 ); 60 X( HH_RESERVED2 ); 61 X( HH_RESERVED3 ); 62 X( HH_KEYWORD_LOOKUP ); 63 X( HH_DISPLAY_TEXT_POPUP ); 64 X( HH_HELP_CONTEXT ); 65 X( HH_TP_HELP_CONTEXTMENU ); 66 X( HH_TP_HELP_WM_HELP ); 67 X( HH_CLOSE_ALL ); 68 X( HH_ALINK_LOOKUP ); 69 X( HH_GET_LAST_ERROR ); 70 X( HH_ENUM_CATEGORY ); 71 X( HH_ENUM_CATEGORY_IT ); 72 X( HH_RESET_IT_FILTER ); 73 X( HH_SET_INCLUSIVE_FILTER ); 74 X( HH_SET_EXCLUSIVE_FILTER ); 75 X( HH_INITIALIZE ); 76 X( HH_UNINITIALIZE ); 77 X( HH_SAFE_DISPLAY_TOPIC ); 78 X( HH_PRETRANSLATEMESSAGE ); 79 X( HH_SET_GLOBAL_PROPERTY ); 80 default: return "???"; 81 } 82 #undef X 83 } 84 85 static BOOL resolve_filename(const WCHAR *filename, WCHAR *fullname, DWORD buflen, WCHAR **index, WCHAR **window) 86 { 87 const WCHAR *extra; 88 WCHAR chm_file[MAX_PATH]; 89 90 static const WCHAR helpW[] = {'\\','h','e','l','p','\\',0}; 91 static const WCHAR delimW[] = {':',':',0}; 92 static const WCHAR delim2W[] = {'>',0}; 93 94 filename = skip_schema(filename); 95 96 /* the format is "helpFile[::/index][>window]" */ 97 if (index) *index = NULL; 98 if (window) *window = NULL; 99 100 extra = strstrW(filename, delim2W); 101 if (extra) 102 { 103 memcpy(chm_file, filename, (extra-filename)*sizeof(WCHAR)); 104 chm_file[extra-filename] = 0; 105 filename = chm_file; 106 if (window) 107 *window = strdupW(extra+1); 108 } 109 110 extra = strstrW(filename, delimW); 111 if (extra) 112 { 113 if (filename != chm_file) 114 memcpy(chm_file, filename, (extra-filename)*sizeof(WCHAR)); 115 chm_file[extra-filename] = 0; 116 filename = chm_file; 117 if (index) 118 *index = strdupW(extra+2); 119 } 120 121 GetFullPathNameW(filename, buflen, fullname, NULL); 122 if (GetFileAttributesW(fullname) == INVALID_FILE_ATTRIBUTES) 123 { 124 GetWindowsDirectoryW(fullname, buflen); 125 strcatW(fullname, helpW); 126 strcatW(fullname, filename); 127 } 128 return (GetFileAttributesW(fullname) != INVALID_FILE_ATTRIBUTES); 129 } 130 131 /****************************************************************** 132 * HtmlHelpW (HHCTRL.OCX.15) 133 */ 134 HWND WINAPI HtmlHelpW(HWND caller, LPCWSTR filename, UINT command, DWORD_PTR data) 135 { 136 WCHAR fullname[MAX_PATH]; 137 138 TRACE("(%p, %s, command=%s, data=%lx)\n", 139 caller, debugstr_w( filename ), 140 command_to_string( command ), data); 141 142 switch (command) 143 { 144 case HH_DISPLAY_TOPIC: 145 case HH_DISPLAY_TOC: 146 case HH_DISPLAY_INDEX: 147 case HH_DISPLAY_SEARCH:{ 148 BOOL res; 149 NMHDR nmhdr; 150 HHInfo *info = NULL; 151 WCHAR *window = NULL; 152 const WCHAR *index = NULL; 153 WCHAR *default_index = NULL; 154 int tab_index = TAB_CONTENTS; 155 156 if (!filename) 157 return NULL; 158 159 if (!resolve_filename(filename, fullname, MAX_PATH, &default_index, &window)) 160 { 161 WARN("can't find %s\n", debugstr_w(filename)); 162 return 0; 163 } 164 index = default_index; 165 166 if (window) 167 info = find_window(window); 168 169 info = CreateHelpViewer(info, fullname, caller); 170 if(!info) 171 { 172 heap_free(default_index); 173 heap_free(window); 174 return NULL; 175 } 176 177 if(!index) 178 index = info->WinType.pszFile; 179 if(!info->WinType.pszType) 180 info->WinType.pszType = info->stringsW.pszType = window; 181 else 182 heap_free(window); 183 184 /* called to load a specified topic */ 185 switch(command) 186 { 187 case HH_DISPLAY_TOPIC: 188 case HH_DISPLAY_TOC: 189 if (data) 190 { 191 static const WCHAR delimW[] = {':',':',0}; 192 const WCHAR *i = (const WCHAR *)data; 193 194 index = strstrW(i, delimW); 195 if(index) 196 { 197 if(memcmp(info->pCHMInfo->szFile, i, index-i)) 198 FIXME("Opening a CHM file in the context of another is not supported.\n"); 199 index += strlenW(delimW); 200 } 201 else 202 index = i; 203 } 204 break; 205 } 206 207 res = NavigateToChm(info, info->pCHMInfo->szFile, index); 208 heap_free(default_index); 209 210 if(!res) 211 { 212 ReleaseHelpViewer(info); 213 return NULL; 214 } 215 216 switch(command) 217 { 218 case HH_DISPLAY_TOPIC: 219 case HH_DISPLAY_TOC: 220 tab_index = TAB_CONTENTS; 221 break; 222 case HH_DISPLAY_INDEX: 223 tab_index = TAB_INDEX; 224 if (data) 225 FIXME("Should select keyword '%s'.\n", debugstr_w((WCHAR *)data)); 226 break; 227 case HH_DISPLAY_SEARCH: 228 tab_index = TAB_SEARCH; 229 if (data) 230 FIXME("Should display search specified by HH_FTS_QUERY structure.\n"); 231 break; 232 } 233 /* open the requested tab */ 234 memset(&nmhdr, 0, sizeof(nmhdr)); 235 nmhdr.code = TCN_SELCHANGE; 236 SendMessageW(info->hwndTabCtrl, TCM_SETCURSEL, (WPARAM)info->tabs[tab_index].id, 0); 237 SendMessageW(info->WinType.hwndNavigation, WM_NOTIFY, 0, (LPARAM)&nmhdr); 238 239 return info->WinType.hwndHelp; 240 } 241 case HH_HELP_CONTEXT: { 242 WCHAR *window = NULL; 243 HHInfo *info = NULL; 244 LPWSTR url; 245 246 if (!filename) 247 return NULL; 248 249 if (!resolve_filename(filename, fullname, MAX_PATH, NULL, &window)) 250 { 251 WARN("can't find %s\n", debugstr_w(filename)); 252 return 0; 253 } 254 255 if (window) 256 info = find_window(window); 257 258 info = CreateHelpViewer(info, fullname, caller); 259 if(!info) 260 { 261 heap_free(window); 262 return NULL; 263 } 264 265 if(!info->WinType.pszType) 266 info->WinType.pszType = info->stringsW.pszType = window; 267 else 268 heap_free(window); 269 270 url = FindContextAlias(info->pCHMInfo, data); 271 if(!url) 272 { 273 if(!data) /* there may legitimately be no context alias for id 0 */ 274 return info->WinType.hwndHelp; 275 ReleaseHelpViewer(info); 276 return NULL; 277 } 278 279 NavigateToUrl(info, url); 280 heap_free(url); 281 return info->WinType.hwndHelp; 282 } 283 case HH_PRETRANSLATEMESSAGE: { 284 static BOOL warned = FALSE; 285 286 if (!warned) 287 { 288 FIXME("HH_PRETRANSLATEMESSAGE unimplemented\n"); 289 warned = TRUE; 290 } 291 return 0; 292 } 293 case HH_CLOSE_ALL: { 294 HHInfo *info, *next; 295 296 LIST_FOR_EACH_ENTRY_SAFE(info, next, &window_list, HHInfo, entry) 297 { 298 TRACE("Destroying window %s.\n", debugstr_w(info->WinType.pszType)); 299 ReleaseHelpViewer(info); 300 } 301 return 0; 302 } 303 case HH_SET_WIN_TYPE: { 304 HH_WINTYPEW *wintype = (HH_WINTYPEW *)data; 305 WCHAR *window = NULL; 306 HHInfo *info = NULL; 307 308 if (!filename && wintype->pszType) 309 window = strdupW(wintype->pszType); 310 else if (!filename || !resolve_filename(filename, fullname, MAX_PATH, NULL, &window) || !window) 311 { 312 WARN("can't find window name: %s\n", debugstr_w(filename)); 313 return 0; 314 } 315 info = find_window(window); 316 if (!info) 317 { 318 info = heap_alloc_zero(sizeof(HHInfo)); 319 info->WinType.pszType = info->stringsW.pszType = window; 320 list_add_tail(&window_list, &info->entry); 321 } 322 else 323 heap_free(window); 324 325 TRACE("Changing WINTYPE, fsValidMembers=0x%x\n", wintype->fsValidMembers); 326 327 MergeChmProperties(wintype, info, TRUE); 328 UpdateHelpWindow(info); 329 return 0; 330 } 331 case HH_GET_WIN_TYPE: { 332 HH_WINTYPEW *wintype = (HH_WINTYPEW *)data; 333 WCHAR *window = NULL; 334 HHInfo *info = NULL; 335 336 if (!filename || !resolve_filename(filename, fullname, MAX_PATH, NULL, &window) || !window) 337 { 338 WARN("can't find window name: %s\n", debugstr_w(filename)); 339 return 0; 340 } 341 info = find_window(window); 342 if (!info) 343 { 344 WARN("Could not find window named %s.\n", debugstr_w(window)); 345 heap_free(window); 346 return (HWND)~0; 347 } 348 349 TRACE("Retrieving WINTYPE for %s.\n", debugstr_w(window)); 350 *wintype = info->WinType; 351 heap_free(window); 352 return 0; 353 } 354 default: 355 FIXME("HH case %s not handled.\n", command_to_string( command )); 356 } 357 358 return 0; 359 } 360 361 static void wintypeAtoW(const HH_WINTYPEA *data, HH_WINTYPEW *wdata, struct wintype_stringsW *stringsW) 362 { 363 memcpy(wdata, data, sizeof(*data)); 364 /* convert all of the ANSI strings to Unicode */ 365 wdata->pszType = stringsW->pszType = strdupAtoW(data->pszType); 366 wdata->pszCaption = stringsW->pszCaption = strdupAtoW(data->pszCaption); 367 wdata->pszToc = stringsW->pszToc = strdupAtoW(data->pszToc); 368 wdata->pszIndex = stringsW->pszIndex = strdupAtoW(data->pszIndex); 369 wdata->pszFile = stringsW->pszFile = strdupAtoW(data->pszFile); 370 wdata->pszHome = stringsW->pszHome = strdupAtoW(data->pszHome); 371 wdata->pszJump1 = stringsW->pszJump1 = strdupAtoW(data->pszJump1); 372 wdata->pszJump2 = stringsW->pszJump2 = strdupAtoW(data->pszJump2); 373 wdata->pszUrlJump1 = stringsW->pszUrlJump1 = strdupAtoW(data->pszUrlJump1); 374 wdata->pszUrlJump2 = stringsW->pszUrlJump2 = strdupAtoW(data->pszUrlJump2); 375 wdata->pszCustomTabs = stringsW->pszCustomTabs = strdupAtoW(data->pszCustomTabs); 376 } 377 378 static void wintypeWtoA(const HH_WINTYPEW *wdata, HH_WINTYPEA *data, struct wintype_stringsA *stringsA) 379 { 380 memcpy(data, wdata, sizeof(*wdata)); 381 /* convert all of the Unicode strings to ANSI */ 382 data->pszType = stringsA->pszType = strdupWtoA(wdata->pszType); 383 data->pszCaption = stringsA->pszCaption = strdupWtoA(wdata->pszCaption); 384 data->pszToc = stringsA->pszToc = strdupWtoA(wdata->pszToc); 385 data->pszIndex = stringsA->pszFile = strdupWtoA(wdata->pszIndex); 386 data->pszFile = stringsA->pszFile = strdupWtoA(wdata->pszFile); 387 data->pszHome = stringsA->pszHome = strdupWtoA(wdata->pszHome); 388 data->pszJump1 = stringsA->pszJump1 = strdupWtoA(wdata->pszJump1); 389 data->pszJump2 = stringsA->pszJump2 = strdupWtoA(wdata->pszJump2); 390 data->pszUrlJump1 = stringsA->pszUrlJump1 = strdupWtoA(wdata->pszUrlJump1); 391 data->pszUrlJump2 = stringsA->pszUrlJump2 = strdupWtoA(wdata->pszUrlJump2); 392 data->pszCustomTabs = stringsA->pszCustomTabs = strdupWtoA(wdata->pszCustomTabs); 393 } 394 395 /****************************************************************** 396 * HtmlHelpA (HHCTRL.OCX.14) 397 */ 398 HWND WINAPI HtmlHelpA(HWND caller, LPCSTR filename, UINT command, DWORD_PTR data) 399 { 400 WCHAR *wfile = strdupAtoW( filename ); 401 HWND result = 0; 402 403 if (data) 404 { 405 switch(command) 406 { 407 case HH_ALINK_LOOKUP: 408 case HH_DISPLAY_SEARCH: 409 case HH_DISPLAY_TEXT_POPUP: 410 case HH_GET_LAST_ERROR: 411 case HH_KEYWORD_LOOKUP: 412 case HH_SYNC: 413 FIXME("structures not handled yet\n"); 414 break; 415 416 case HH_SET_WIN_TYPE: 417 { 418 struct wintype_stringsW stringsW; 419 HH_WINTYPEW wdata; 420 421 wintypeAtoW((HH_WINTYPEA *)data, &wdata, &stringsW); 422 result = HtmlHelpW( caller, wfile, command, (DWORD_PTR)&wdata ); 423 wintype_stringsW_free(&stringsW); 424 goto done; 425 } 426 case HH_GET_WIN_TYPE: 427 { 428 HH_WINTYPEW wdata; 429 HHInfo *info; 430 431 result = HtmlHelpW( caller, wfile, command, (DWORD_PTR)&wdata ); 432 if (!wdata.pszType) break; 433 info = find_window(wdata.pszType); 434 if (!info) break; 435 wintype_stringsA_free(&info->stringsA); 436 wintypeWtoA(&wdata, (HH_WINTYPEA *)data, &info->stringsA); 437 goto done; 438 } 439 440 case HH_DISPLAY_INDEX: 441 case HH_DISPLAY_TOPIC: 442 case HH_DISPLAY_TOC: 443 case HH_GET_WIN_HANDLE: 444 case HH_SAFE_DISPLAY_TOPIC: 445 { 446 WCHAR *wdata = strdupAtoW( (const char *)data ); 447 result = HtmlHelpW( caller, wfile, command, (DWORD_PTR)wdata ); 448 heap_free(wdata); 449 goto done; 450 } 451 452 case HH_CLOSE_ALL: 453 case HH_HELP_CONTEXT: 454 case HH_INITIALIZE: 455 case HH_PRETRANSLATEMESSAGE: 456 case HH_TP_HELP_CONTEXTMENU: 457 case HH_TP_HELP_WM_HELP: 458 case HH_UNINITIALIZE: 459 /* either scalar or pointer to scalar - do nothing */ 460 break; 461 462 default: 463 FIXME("Unknown command: %s (%d)\n", command_to_string(command), command); 464 break; 465 } 466 } 467 468 result = HtmlHelpW( caller, wfile, command, data ); 469 done: 470 heap_free(wfile); 471 return result; 472 } 473 474 /****************************************************************** 475 * doWinMain (HHCTRL.OCX.13) 476 */ 477 int WINAPI doWinMain(HINSTANCE hInstance, LPSTR szCmdLine) 478 { 479 MSG msg; 480 int len, buflen, mapid = -1; 481 WCHAR *filename; 482 char *endq = NULL; 483 HWND hwnd; 484 485 hh_process = TRUE; 486 487 /* Parse command line option of the HTML Help command. 488 * 489 * Note: The only currently handled action is "mapid", 490 * which corresponds to opening a specific page. 491 */ 492 while(*szCmdLine == '-') 493 { 494 LPSTR space, ptr; 495 496 ptr = szCmdLine + 1; 497 space = strchr(ptr, ' '); 498 if(!strncmp(ptr, "mapid", space-ptr)) 499 { 500 char idtxt[10]; 501 502 ptr += strlen("mapid")+1; 503 space = strchr(ptr, ' '); 504 /* command line ends without number */ 505 if (!space) 506 return 0; 507 memcpy(idtxt, ptr, space-ptr); 508 idtxt[space-ptr] = '\0'; 509 mapid = atoi(idtxt); 510 szCmdLine = space+1; 511 } 512 else 513 { 514 FIXME("Unhandled HTML Help command line parameter! (%.*s)\n", (int)(space-szCmdLine), szCmdLine); 515 return 0; 516 } 517 } 518 519 /* FIXME: Check szCmdLine for bad arguments */ 520 if (*szCmdLine == '\"') 521 endq = strchr(++szCmdLine, '\"'); 522 523 if (endq) 524 len = endq - szCmdLine; 525 else 526 len = strlen(szCmdLine); 527 528 /* no filename given */ 529 if (!len) 530 return 0; 531 532 buflen = MultiByteToWideChar(CP_ACP, 0, szCmdLine, len, NULL, 0) + 1; 533 filename = heap_alloc(buflen * sizeof(WCHAR)); 534 MultiByteToWideChar(CP_ACP, 0, szCmdLine, len, filename, buflen); 535 filename[buflen-1] = 0; 536 537 /* Open a specific help topic */ 538 if(mapid != -1) 539 hwnd = HtmlHelpW(GetDesktopWindow(), filename, HH_HELP_CONTEXT, mapid); 540 else 541 hwnd = HtmlHelpW(GetDesktopWindow(), filename, HH_DISPLAY_TOPIC, 0); 542 543 heap_free(filename); 544 545 if (!hwnd) 546 { 547 ERR("Failed to open HTML Help file '%s'.\n", szCmdLine); 548 return 0; 549 } 550 551 while (GetMessageW(&msg, 0, 0, 0)) 552 { 553 TranslateMessage(&msg); 554 DispatchMessageW(&msg); 555 } 556 557 return 0; 558 } 559 560 /****************************************************************** 561 * DllGetClassObject (HHCTRL.OCX.@) 562 */ 563 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) 564 { 565 FIXME("(%s %s %p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv); 566 return CLASS_E_CLASSNOTAVAILABLE; 567 } 568 569 /*********************************************************************** 570 * DllRegisterServer (HHCTRL.OCX.@) 571 */ 572 HRESULT WINAPI DllRegisterServer(void) 573 { 574 return __wine_register_resources( hhctrl_hinstance ); 575 } 576 577 /*********************************************************************** 578 * DllUnregisterServer (HHCTRL.OCX.@) 579 */ 580 HRESULT WINAPI DllUnregisterServer(void) 581 { 582 return __wine_unregister_resources( hhctrl_hinstance ); 583 } 584