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