xref: /reactos/dll/win32/hhctrl.ocx/help.c (revision 595b846d)
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