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