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