3f3d2ffe51874c217776a717395f0284d38234c2
[reactos.git] / dll / win32 / hhctrl.ocx / help.c
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, parent = 0;
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 if (info->WinType.dwStyles & WS_CHILD)
1674 parent = info->WinType.hwndCaller;
1675
1676 hWnd = CreateWindowExW(dwExStyles, windowClassW, caption,
1677 dwStyles, x, y, width, height, parent, NULL, hhctrl_hinstance, NULL);
1678 if (!hWnd)
1679 return FALSE;
1680
1681 ShowWindow(hWnd, SW_SHOW);
1682 UpdateWindow(hWnd);
1683
1684 /* store the pointer to the HH info struct */
1685 SetWindowLongPtrW(hWnd, 0, (LONG_PTR)info);
1686
1687 info->WinType.hwndHelp = hWnd;
1688 return TRUE;
1689 }
1690
1691 static void HH_CreateFont(HHInfo *pHHInfo)
1692 {
1693 LOGFONTW lf;
1694
1695 GetObjectW(GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONTW), &lf);
1696 lf.lfWeight = FW_NORMAL;
1697 lf.lfItalic = FALSE;
1698 lf.lfUnderline = FALSE;
1699
1700 pHHInfo->hFont = CreateFontIndirectW(&lf);
1701 }
1702
1703 static void HH_InitRequiredControls(DWORD dwControls)
1704 {
1705 INITCOMMONCONTROLSEX icex;
1706
1707 icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
1708 icex.dwICC = dwControls;
1709 InitCommonControlsEx(&icex);
1710 }
1711
1712 /* Creates the whole package */
1713 static BOOL CreateViewer(HHInfo *pHHInfo)
1714 {
1715 HH_CreateFont(pHHInfo);
1716
1717 if (!HH_CreateHelpWindow(pHHInfo))
1718 return FALSE;
1719
1720 HH_InitRequiredControls(ICC_BAR_CLASSES);
1721
1722 if (!HH_AddToolbar(pHHInfo))
1723 return FALSE;
1724
1725 HH_RegisterChildWndClass(pHHInfo);
1726
1727 if (!HH_AddNavigationPane(pHHInfo))
1728 return FALSE;
1729
1730 HH_RegisterSizeBarClass(pHHInfo);
1731
1732 if (!HH_AddSizeBar(pHHInfo))
1733 return FALSE;
1734
1735 if (!HH_AddHTMLPane(pHHInfo))
1736 return FALSE;
1737
1738 if (!AddContentTab(pHHInfo))
1739 return FALSE;
1740
1741 if (!AddIndexTab(pHHInfo))
1742 return FALSE;
1743
1744 if (!AddIndexPopup(pHHInfo))
1745 return FALSE;
1746
1747 if (!AddSearchTab(pHHInfo))
1748 return FALSE;
1749
1750 InitContent(pHHInfo);
1751 InitIndex(pHHInfo);
1752
1753 pHHInfo->viewer_initialized = TRUE;
1754 return TRUE;
1755 }
1756
1757 void wintype_stringsW_free(struct wintype_stringsW *stringsW)
1758 {
1759 heap_free(stringsW->pszType);
1760 heap_free(stringsW->pszCaption);
1761 heap_free(stringsW->pszToc);
1762 heap_free(stringsW->pszIndex);
1763 heap_free(stringsW->pszFile);
1764 heap_free(stringsW->pszHome);
1765 heap_free(stringsW->pszJump1);
1766 heap_free(stringsW->pszJump2);
1767 heap_free(stringsW->pszUrlJump1);
1768 heap_free(stringsW->pszUrlJump2);
1769 }
1770
1771 void wintype_stringsA_free(struct wintype_stringsA *stringsA)
1772 {
1773 heap_free(stringsA->pszType);
1774 heap_free(stringsA->pszCaption);
1775 heap_free(stringsA->pszToc);
1776 heap_free(stringsA->pszIndex);
1777 heap_free(stringsA->pszFile);
1778 heap_free(stringsA->pszHome);
1779 heap_free(stringsA->pszJump1);
1780 heap_free(stringsA->pszJump2);
1781 heap_free(stringsA->pszUrlJump1);
1782 heap_free(stringsA->pszUrlJump2);
1783 heap_free(stringsA->pszCustomTabs);
1784 }
1785
1786 void ReleaseHelpViewer(HHInfo *info)
1787 {
1788 TRACE("(%p)\n", info);
1789
1790 if (!info)
1791 return;
1792
1793 list_remove(&info->entry);
1794
1795 wintype_stringsA_free(&info->stringsA);
1796 wintype_stringsW_free(&info->stringsW);
1797
1798 if (info->pCHMInfo)
1799 CloseCHM(info->pCHMInfo);
1800
1801 ReleaseWebBrowser(info);
1802 ReleaseContent(info);
1803 ReleaseIndex(info);
1804 ReleaseSearch(info);
1805
1806 if(info->contents.hImageList)
1807 ImageList_Destroy(info->contents.hImageList);
1808 if(info->WinType.hwndHelp)
1809 DestroyWindow(info->WinType.hwndHelp);
1810
1811 heap_free(info);
1812 OleUninitialize();
1813 }
1814
1815 HHInfo *CreateHelpViewer(HHInfo *info, LPCWSTR filename, HWND caller)
1816 {
1817 HHInfo *tmp_info;
1818 unsigned int i;
1819
1820 if(!info)
1821 {
1822 info = heap_alloc_zero(sizeof(HHInfo));
1823 list_add_tail(&window_list, &info->entry);
1824 }
1825
1826 /* Set the invalid tab ID (-1) as the default value for all
1827 * of the tabs, this matches a failed TCM_INSERTITEM call.
1828 */
1829 for(i=0;i<sizeof(info->tabs)/sizeof(HHTab);i++)
1830 info->tabs[i].id = -1;
1831
1832 OleInitialize(NULL);
1833
1834 info->pCHMInfo = OpenCHM(filename);
1835 if(!info->pCHMInfo) {
1836 ReleaseHelpViewer(info);
1837 return NULL;
1838 }
1839
1840 if (!LoadWinTypeFromCHM(info)) {
1841 ReleaseHelpViewer(info);
1842 return NULL;
1843 }
1844 info->WinType.hwndCaller = caller;
1845
1846 /* If the window is already open then load the file in that existing window */
1847 if ((tmp_info = find_window(info->WinType.pszType)) && tmp_info != info)
1848 {
1849 ReleaseHelpViewer(info);
1850 return CreateHelpViewer(tmp_info, filename, caller);
1851 }
1852
1853 if(!info->viewer_initialized && !CreateViewer(info)) {
1854 ReleaseHelpViewer(info);
1855 return NULL;
1856 }
1857
1858 return info;
1859 }
1860
1861 /*
1862 * Search the table of HTML entities and return the corresponding ANSI symbol.
1863 */
1864 static char find_html_symbol(const char *entity, int entity_len)
1865 {
1866 int max = sizeof(html_encoded_symbols)/sizeof(html_encoded_symbols[0])-1;
1867 int min = 0, dir;
1868
1869 while(min <= max)
1870 {
1871 int pos = (min+max)/2;
1872 const char *encoded_symbol = html_encoded_symbols[pos].html_code;
1873 dir = strncmp(encoded_symbol, entity, entity_len);
1874 if(dir == 0 && !encoded_symbol[entity_len]) return html_encoded_symbols[pos].ansi_symbol;
1875 if(dir < 0)
1876 min = pos+1;
1877 else
1878 max = pos-1;
1879 }
1880 return 0;
1881 }
1882
1883 /*
1884 * Decode a string containing HTML encoded characters into a unicode string.
1885 */
1886 WCHAR *decode_html(const char *html_fragment, int html_fragment_len, UINT code_page)
1887 {
1888 const char *h = html_fragment, *amp, *sem;
1889 char symbol, *tmp;
1890 int len, tmp_len = 0;
1891 WCHAR *unicode_text;
1892
1893 tmp = heap_alloc(html_fragment_len+1);
1894 while(1)
1895 {
1896 symbol = 0;
1897 amp = strchr(h, '&');
1898 if(!amp) break;
1899 len = amp-h;
1900 /* Copy the characters prior to the HTML encoded character */
1901 memcpy(&tmp[tmp_len], h, len);
1902 tmp_len += len;
1903 amp++; /* skip ampersand */
1904 sem = strchr(amp, ';');
1905 /* Require a semicolon after the ampersand */
1906 if(!sem)
1907 {
1908 h = amp;
1909 tmp[tmp_len++] = '&';
1910 continue;
1911 }
1912 /* Find the symbol either by using the ANSI character number (prefixed by the pound symbol)
1913 * or by searching the HTML entity table */
1914 len = sem-amp;
1915 if(amp[0] == '#')
1916 {
1917 char *endnum = NULL;
1918 int tmp;
1919
1920 tmp = (char) strtol(amp, &endnum, 10);
1921 if(endnum == sem)
1922 symbol = tmp;
1923 }
1924 else
1925 symbol = find_html_symbol(amp, len);
1926 if(!symbol)
1927 {
1928 FIXME("Failed to translate HTML encoded character '&%.*s;'.\n", len, amp);
1929 h = amp;
1930 tmp[tmp_len++] = '&';
1931 continue;
1932 }
1933 /* Insert the new symbol */
1934 h = sem+1;
1935 tmp[tmp_len++] = symbol;
1936 }
1937 /* Convert any remaining characters */
1938 len = html_fragment_len-(h-html_fragment);
1939 memcpy(&tmp[tmp_len], h, len);
1940 tmp_len += len;
1941 tmp[tmp_len++] = 0; /* NULL-terminate the string */
1942
1943 len = MultiByteToWideChar(code_page, 0, tmp, tmp_len, NULL, 0);
1944 unicode_text = heap_alloc(len*sizeof(WCHAR));
1945 MultiByteToWideChar(code_page, 0, tmp, tmp_len, unicode_text, len);
1946 heap_free(tmp);
1947 return unicode_text;
1948 }
1949
1950 /* Find the HTMLHelp structure for an existing window title */
1951 HHInfo *find_window(const WCHAR *window)
1952 {
1953 HHInfo *info;
1954
1955 LIST_FOR_EACH_ENTRY(info, &window_list, HHInfo, entry)
1956 {
1957 if (strcmpW(info->WinType.pszType, window) == 0)
1958 return info;
1959 }
1960 return NULL;
1961 }