Unicodify wineesd.
[wine] / dlls / hhctrl.ocx / help.c
1 /*
2  * Help Viewer Implementation
3  *
4  * Copyright 2005 James Hawkins
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdarg.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "wingdi.h"
26 #include "winuser.h"
27 #include "winnls.h"
28 #include "commctrl.h"
29 #include "htmlhelp.h"
30 #include "ole2.h"
31 #include "wine/unicode.h"
32
33 #include "resource.h"
34
35 /* Window type defaults */
36
37 #define WINTYPE_DEFAULT_X           280
38 #define WINTYPE_DEFAULT_Y           100
39 #define WINTYPE_DEFAULT_WIDTH       740
40 #define WINTYPE_DEFAULT_HEIGHT      640
41 #define WINTYPE_DEFAULT_NAVWIDTH    250
42
43 typedef struct tagHHInfo
44 {
45     HH_WINTYPEW *pHHWinType;
46     HINSTANCE hInstance;
47     LPWSTR szCmdLine;
48     HWND hwndTabCtrl;
49     HFONT hFont;
50 } HHInfo;
51
52 extern HINSTANCE hhctrl_hinstance;
53
54 static LPWSTR HH_ANSIToUnicode(LPCSTR ansi)
55 {
56     LPWSTR unicode;
57     int count;
58
59     count = MultiByteToWideChar(CP_ACP, 0, ansi, -1, NULL, 0);
60     unicode = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR));
61     MultiByteToWideChar(CP_ACP, 0, ansi, -1, unicode, count);
62
63     return unicode;
64 }
65
66 /* Loads a string from the resource file */
67 static LPWSTR HH_LoadString(DWORD dwID)
68 {
69     LPWSTR string = NULL;
70     int iSize;
71
72     iSize = LoadStringW(hhctrl_hinstance, dwID, NULL, 0);
73     iSize += 2; /* some strings (tab text) needs double-null termination */
74
75     string = HeapAlloc(GetProcessHeap(), 0, iSize * sizeof(WCHAR));
76     LoadStringW(hhctrl_hinstance, dwID, string, iSize);
77
78     return string;
79 }
80
81 /* Child Window */
82
83 static const WCHAR szChildClass[] = {
84     'H','H',' ','C','h','i','l','d',0
85 };
86
87 static const WCHAR szEmpty[] = {0};
88
89 LRESULT CALLBACK Child_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
90 {
91     switch (message)
92     {
93         default:
94             return DefWindowProcW(hWnd, message, wParam, lParam);
95     }
96
97     return 0;
98 }
99
100 static void HH_RegisterChildWndClass(HHInfo *pHHInfo)
101 {
102     WNDCLASSEXW wcex;
103
104     wcex.cbSize         = sizeof(WNDCLASSEXW);
105     wcex.style          = 0;
106     wcex.lpfnWndProc    = (WNDPROC)Child_WndProc;
107     wcex.cbClsExtra     = 0;
108     wcex.cbWndExtra     = 0;
109     wcex.hInstance      = pHHInfo->hInstance;
110     wcex.hIcon          = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
111     wcex.hCursor        = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
112     wcex.hbrBackground  = (HBRUSH)(COLOR_3DFACE);
113     wcex.lpszMenuName   = NULL;
114     wcex.lpszClassName  = szChildClass;
115     wcex.hIconSm        = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
116
117     RegisterClassExW(&wcex);
118 }
119
120 /* Toolbar */
121
122 #define ICON_SIZE   20
123
124 static void TB_AddButton(TBBUTTON *pButtons, DWORD dwIndex, DWORD dwID)
125 {
126     /* FIXME: Load the correct button bitmaps */
127     pButtons[dwIndex].iBitmap = STD_PRINT;
128     pButtons[dwIndex].idCommand = dwID;
129     pButtons[dwIndex].fsState = TBSTATE_ENABLED;
130     pButtons[dwIndex].fsStyle = BTNS_BUTTON;
131     pButtons[dwIndex].dwData = 0;
132     pButtons[dwIndex].iString = 0;
133 }
134
135 static void TB_AddButtonsFromFlags(TBBUTTON *pButtons, DWORD dwButtonFlags, LPDWORD pdwNumButtons)
136 {
137     *pdwNumButtons = 0;
138
139     if (dwButtonFlags & HHWIN_BUTTON_EXPAND)
140         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_EXPAND);
141
142     if (dwButtonFlags & HHWIN_BUTTON_BACK)
143         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_BACK);
144
145     if (dwButtonFlags & HHWIN_BUTTON_FORWARD)
146         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_FORWARD);
147
148     if (dwButtonFlags & HHWIN_BUTTON_STOP)
149         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_STOP);
150
151     if (dwButtonFlags & HHWIN_BUTTON_REFRESH)
152         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_REFRESH);
153
154     if (dwButtonFlags & HHWIN_BUTTON_HOME)
155         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_HOME);
156
157     if (dwButtonFlags & HHWIN_BUTTON_SYNC)
158         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_SYNC);
159
160     if (dwButtonFlags & HHWIN_BUTTON_OPTIONS)
161         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_OPTIONS);
162
163     if (dwButtonFlags & HHWIN_BUTTON_PRINT)
164         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_PRINT);
165
166     if (dwButtonFlags & HHWIN_BUTTON_JUMP1)
167         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_JUMP1);
168
169     if (dwButtonFlags & HHWIN_BUTTON_JUMP2)
170         TB_AddButton(pButtons,(*pdwNumButtons)++, IDTB_JUMP2);
171
172     if (dwButtonFlags & HHWIN_BUTTON_ZOOM)
173         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_ZOOM);
174
175     if (dwButtonFlags & HHWIN_BUTTON_TOC_NEXT)
176         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_TOC_NEXT);
177
178     if (dwButtonFlags & HHWIN_BUTTON_TOC_PREV)
179         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_TOC_PREV);
180 }
181
182 static BOOL HH_AddToolbar(HHInfo *pHHInfo)
183 {
184     HWND hToolbar;
185     HWND hwndParent = pHHInfo->pHHWinType->hwndHelp;
186     DWORD toolbarFlags = pHHInfo->pHHWinType->fsToolBarFlags;
187     TBBUTTON buttons[IDTB_TOC_PREV - IDTB_EXPAND];
188     TBADDBITMAP tbAB;
189     DWORD dwStyles, dwExStyles;
190     DWORD dwNumButtons, dwIndex;
191
192     /* FIXME: Remove the following line once we read the CHM file */
193     toolbarFlags = HHWIN_BUTTON_EXPAND | HHWIN_BUTTON_BACK | HHWIN_BUTTON_STOP |
194                    HHWIN_BUTTON_REFRESH | HHWIN_BUTTON_HOME | HHWIN_BUTTON_PRINT;
195     TB_AddButtonsFromFlags(buttons, toolbarFlags, &dwNumButtons);
196
197     dwStyles = WS_CHILDWINDOW | WS_VISIBLE | TBSTYLE_FLAT |
198                TBSTYLE_WRAPABLE | TBSTYLE_TOOLTIPS | CCS_NODIVIDER;
199     dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR;
200
201     hToolbar = CreateWindowExW(dwExStyles, TOOLBARCLASSNAMEW, NULL, dwStyles,
202                                0, 0, 0, 0, hwndParent, NULL,
203                                pHHInfo->hInstance, NULL);
204     if (!hToolbar)
205         return FALSE;
206
207     SendMessageW(hToolbar, TB_SETBITMAPSIZE, 0, MAKELONG(ICON_SIZE, ICON_SIZE));
208     SendMessageW(hToolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
209     SendMessageW(hToolbar, WM_SETFONT, (WPARAM)pHHInfo->hFont, TRUE);
210
211     /* FIXME: Load correct icons for all buttons */
212     tbAB.hInst = HINST_COMMCTRL;
213     tbAB.nID = IDB_STD_LARGE_COLOR;
214     SendMessageW(hToolbar, TB_ADDBITMAP, 0, (LPARAM)&tbAB);
215
216     for (dwIndex = 0; dwIndex < dwNumButtons; dwIndex++)
217     {
218         LPWSTR szBuf = HH_LoadString(buttons[dwIndex].idCommand);
219         DWORD dwLen = strlenW(szBuf);
220         szBuf[dwLen + 2] = 0; /* Double-null terminate */
221
222         buttons[dwIndex].iString = (DWORD)SendMessageW(hToolbar, TB_ADDSTRINGW, 0, (LPARAM)szBuf);
223         HeapFree(GetProcessHeap(), 0, szBuf);
224     }
225
226     SendMessageW(hToolbar, TB_ADDBUTTONSW, dwNumButtons, (LPARAM)&buttons);
227     SendMessageW(hToolbar, TB_AUTOSIZE, 0, 0);
228     ShowWindow(hToolbar, SW_SHOW);
229
230     pHHInfo->pHHWinType->hwndToolBar = hToolbar;
231     return TRUE;
232 }
233
234 /* Navigation Pane */
235
236 static void NP_GetNavigationRect(HHInfo *pHHInfo, RECT *rc)
237 {
238     HWND hwndParent = pHHInfo->pHHWinType->hwndHelp;
239     HWND hwndToolbar = pHHInfo->pHHWinType->hwndToolBar;
240     RECT rectWND, rectTB;
241
242     GetClientRect(hwndParent, &rectWND);
243     GetClientRect(hwndToolbar, &rectTB);
244
245     rc->left = 0;
246     rc->top = rectTB.bottom;
247     rc->bottom = rectWND.bottom - rectTB.bottom;
248
249     if (pHHInfo->pHHWinType->fsValidMembers & HHWIN_PARAM_NAV_WIDTH)
250         rc->right = pHHInfo->pHHWinType->iNavWidth;
251     else
252         rc->right = WINTYPE_DEFAULT_NAVWIDTH;
253 }
254
255 static void NP_CreateTab(HINSTANCE hInstance, HWND hwndTabCtrl, DWORD dwStrID, DWORD dwIndex)
256 {
257     TCITEMW tie;
258     LPWSTR tabText = HH_LoadString(dwStrID);
259
260     tie.mask = TCIF_TEXT;
261     tie.pszText = tabText;
262
263     TabCtrl_InsertItemW(hwndTabCtrl, dwIndex, &tie);
264     HeapFree(GetProcessHeap(), 0, tabText);
265 }
266
267 static BOOL HH_AddNavigationPane(HHInfo *pHHInfo)
268 {
269     HWND hWnd, hwndTabCtrl;
270     HWND hwndParent = pHHInfo->pHHWinType->hwndHelp;
271     DWORD dwStyles = WS_CHILDWINDOW | WS_VISIBLE;
272     DWORD dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR;
273     DWORD dwIndex = 0;
274     RECT rc;
275
276     NP_GetNavigationRect(pHHInfo, &rc);
277
278     hWnd = CreateWindowExW(dwExStyles, szChildClass, szEmpty, dwStyles,
279                            rc.left, rc.top, rc.right, rc.bottom,
280                            hwndParent, NULL, pHHInfo->hInstance, NULL);
281     if (!hWnd)
282         return FALSE;
283
284     hwndTabCtrl = CreateWindowExW(dwExStyles, WC_TABCONTROLW, szEmpty, dwStyles,
285                                   0, 0, rc.right, rc.bottom, hWnd,
286                                   NULL, pHHInfo->hInstance, NULL);
287     if (!hwndTabCtrl)
288         return FALSE;
289
290     /* FIXME: Check which tabs to include when we read the CHM file */
291     NP_CreateTab(pHHInfo->hInstance, hwndTabCtrl, IDS_CONTENTS, dwIndex++);
292     NP_CreateTab(pHHInfo->hInstance, hwndTabCtrl, IDS_INDEX, dwIndex++);
293     NP_CreateTab(pHHInfo->hInstance, hwndTabCtrl, IDS_SEARCH, dwIndex++);
294     NP_CreateTab(pHHInfo->hInstance, hwndTabCtrl, IDS_FAVORITES, dwIndex++);
295
296     SendMessageW(hwndTabCtrl, WM_SETFONT, (WPARAM)pHHInfo->hFont, TRUE);
297
298     pHHInfo->hwndTabCtrl = hwndTabCtrl;
299     pHHInfo->pHHWinType->hwndNavigation = hWnd;
300     return TRUE;
301 }
302
303 /* HTML Pane */
304
305 static void HP_GetHTMLRect(HHInfo *pHHInfo, RECT *rc)
306 {
307     HWND hwndParent = pHHInfo->pHHWinType->hwndHelp;
308     HWND hwndToolbar = pHHInfo->pHHWinType->hwndToolBar;
309     HWND hwndNavigation = pHHInfo->pHHWinType->hwndNavigation;
310     RECT rectTB, rectWND, rectNP;
311
312     GetClientRect(hwndParent, &rectWND);
313     GetClientRect(hwndToolbar, &rectTB);
314     GetClientRect(hwndNavigation, &rectNP);
315
316     rc->left = rectNP.right;
317     rc->top = rectTB.bottom;
318     rc->right = rectWND.right - rectNP.right;
319     rc->bottom = rectWND.bottom - rectTB.bottom;
320 }
321
322 static BOOL HH_AddHTMLPane(HHInfo *pHHInfo)
323 {
324     HWND hWnd;
325     HWND hwndParent = pHHInfo->pHHWinType->hwndHelp;
326     DWORD dwStyles = WS_CHILDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN;
327     DWORD dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_CLIENTEDGE;
328     RECT rc;
329
330     HP_GetHTMLRect(pHHInfo, &rc);
331
332     hWnd = CreateWindowExW(dwExStyles, szChildClass, szEmpty, dwStyles,
333                            rc.left, rc.top, rc.right, rc.bottom,
334                            hwndParent, NULL, pHHInfo->hInstance, NULL);
335     if (!hWnd)
336         return FALSE;
337
338     /* store the pointer to the HH info struct */
339     SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)pHHInfo);
340
341     ShowWindow(hWnd, SW_SHOW);
342     UpdateWindow(hWnd);
343
344     pHHInfo->pHHWinType->hwndHTML = hWnd;
345     return TRUE;
346 }
347
348 /* Viewer Window */
349
350 LRESULT CALLBACK Help_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
351 {
352     PAINTSTRUCT ps;
353     HDC hdc;
354
355     switch (message)
356     {
357
358         case WM_PAINT:
359             hdc = BeginPaint(hWnd, &ps);
360             EndPaint(hWnd, &ps);
361             break;
362         case WM_DESTROY:
363             PostQuitMessage(0);
364             break;
365
366         default:
367             return DefWindowProcW(hWnd, message, wParam, lParam);
368     }
369
370     return 0;
371 }
372
373 static BOOL HH_CreateHelpWindow(HHInfo *pHHInfo)
374 {
375     HWND hWnd;
376     HINSTANCE hInstance = pHHInfo->hInstance;
377     WNDCLASSEXW wcex;
378     DWORD dwStyles, dwExStyles;
379     DWORD x, y, width, height;
380
381     static const WCHAR windowClassW[] = {
382         'H','H',' ', 'P','a','r','e','n','t',0
383     };
384
385     static const WCHAR windowTitleW[] = {
386         'H','T','M','L',' ','H','e','l','p',0
387     };
388
389     wcex.cbSize         = sizeof(WNDCLASSEXW);
390     wcex.style          = CS_HREDRAW | CS_VREDRAW;
391     wcex.lpfnWndProc    = (WNDPROC)Help_WndProc;
392     wcex.cbClsExtra     = 0;
393     wcex.cbWndExtra     = 0;
394     wcex.hInstance      = hInstance;
395     wcex.hIcon          = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
396     wcex.hCursor        = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
397     wcex.hbrBackground  = (HBRUSH)(COLOR_BACKGROUND + 1);
398     wcex.lpszMenuName   = NULL;
399     wcex.lpszClassName  = windowClassW;
400     wcex.hIconSm        = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
401
402     RegisterClassExW(&wcex);
403
404     dwStyles = WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
405     dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR |
406                  WS_EX_WINDOWEDGE | WS_EX_APPWINDOW;
407
408     /* these will be loaded from the CHM file in the future if they're provided */
409     x = WINTYPE_DEFAULT_X;
410     y = WINTYPE_DEFAULT_Y;
411     width = WINTYPE_DEFAULT_WIDTH;
412     height = WINTYPE_DEFAULT_HEIGHT;
413
414     hWnd = CreateWindowExW(dwExStyles, windowClassW, windowTitleW, dwStyles,
415                            x, y, width, height, NULL, NULL, hInstance, NULL);
416     if (!hWnd)
417         return FALSE;
418
419     ShowWindow(hWnd, SW_SHOW);
420     UpdateWindow(hWnd);
421
422     /* store the pointer to the HH info struct */
423     SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)pHHInfo);
424
425     pHHInfo->pHHWinType->hwndHelp = hWnd;
426     return TRUE;
427 }
428
429 static void HH_CreateFont(HHInfo *pHHInfo)
430 {
431     LOGFONTW lf;
432
433     GetObjectW(GetStockObject(ANSI_VAR_FONT), sizeof(LOGFONTW), &lf);
434     lf.lfWeight = FW_NORMAL;
435     lf.lfItalic = FALSE;
436     lf.lfUnderline = FALSE;
437
438     pHHInfo->hFont = CreateFontIndirectW(&lf);
439 }
440
441 static void HH_InitRequiredControls(DWORD dwControls)
442 {
443     INITCOMMONCONTROLSEX icex;
444
445     icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
446     icex.dwICC = dwControls;
447     InitCommonControlsEx(&icex);
448 }
449
450 /* Creates the whole package */
451 static BOOL HH_CreateViewer(HHInfo *pHHInfo)
452 {
453     HH_CreateFont(pHHInfo);
454
455     if (!HH_CreateHelpWindow(pHHInfo))
456         return FALSE;
457
458     HH_InitRequiredControls(ICC_BAR_CLASSES);
459
460     if (!HH_AddToolbar(pHHInfo))
461         return FALSE;
462
463     HH_RegisterChildWndClass(pHHInfo);
464
465     if (!HH_AddNavigationPane(pHHInfo))
466         return FALSE;
467
468     if (!HH_AddHTMLPane(pHHInfo))
469         return FALSE;
470
471     return TRUE;
472 }
473
474 static HHInfo *HH_OpenHH(HINSTANCE hInstance, LPWSTR szCmdLine)
475 {
476     HHInfo *pHHInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HHInfo));
477
478     pHHInfo->pHHWinType = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HH_WINTYPEW));
479     pHHInfo->hInstance = hInstance;
480     pHHInfo->szCmdLine = szCmdLine;
481
482     return pHHInfo;
483 }
484
485 static void HH_Close(HHInfo *pHHInfo)
486 {
487     if (!pHHInfo)
488         return;
489
490     HeapFree(GetProcessHeap(), 0, pHHInfo->pHHWinType);
491     HeapFree(GetProcessHeap(), 0, pHHInfo->szCmdLine);
492 }
493
494 /* FIXME: Check szCmdLine for bad arguments */
495 int WINAPI doWinMain(HINSTANCE hInstance, LPSTR szCmdLine)
496 {
497     MSG msg;
498     HHInfo *pHHInfo;
499
500     if (OleInitialize(NULL) != S_OK)
501         return -1;
502
503     pHHInfo = HH_OpenHH(hInstance, HH_ANSIToUnicode(szCmdLine));
504     if (!pHHInfo || !HH_CreateViewer(pHHInfo))
505     {
506         OleUninitialize();
507         return -1;
508     }
509
510     while (GetMessageW(&msg, 0, 0, 0))
511     {
512         TranslateMessage(&msg);
513         DispatchMessageW(&msg);
514     }
515
516     HH_Close(pHHInfo);
517     HeapFree(GetProcessHeap(), 0, pHHInfo);
518     OleUninitialize();
519
520     return 0;
521 }