- Add the toolbar to the viewer window.
[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 /* Window type defaults */
34
35 #define WINTYPE_DEFAULT_X       280
36 #define WINTYPE_DEFAULT_Y       100
37 #define WINTYPE_DEFAULT_WIDTH   740
38 #define WINTYPE_DEFAULT_HEIGHT  640
39
40 typedef struct tagHHInfo
41 {
42     HH_WINTYPEW *pHHWinType;
43     HINSTANCE hInstance;
44     LPCWSTR szCmdLine;
45     DWORD dwNumTBButtons;
46     HFONT hFont;
47 } HHInfo;
48
49 extern HINSTANCE hhctrl_hinstance;
50
51 static LPWSTR HH_ANSIToUnicode(LPCSTR ansi)
52 {
53     LPWSTR unicode;
54     int count;
55
56     count = MultiByteToWideChar(CP_ACP, 0, ansi, -1, NULL, 0);
57     unicode = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR));
58     MultiByteToWideChar(CP_ACP, 0, ansi, -1, unicode, count);
59
60     return unicode;
61 }
62
63 /* Loads a string from the resource file */
64 static LPWSTR HH_LoadString(DWORD dwID)
65 {
66     LPWSTR string = NULL;
67     int iSize;
68
69     iSize = LoadStringW(hhctrl_hinstance, dwID, NULL, 0);
70     iSize += 2; /* some strings (tab text) needs double-null termination */
71
72     string = HeapAlloc(GetProcessHeap(), 0, iSize * sizeof(WCHAR));
73     LoadStringW(hhctrl_hinstance, dwID, string, iSize);
74
75     return string;
76 }
77
78 /* Toolbar */
79
80 #define ICON_SIZE   20
81
82 static void TB_AddButton(TBBUTTON *pButtons, DWORD dwIndex, DWORD dwID)
83 {
84     /* FIXME: Load the correct button bitmaps */
85     pButtons[dwIndex].iBitmap = STD_PRINT;
86     pButtons[dwIndex].idCommand = dwID;
87     pButtons[dwIndex].fsState = TBSTATE_ENABLED;
88     pButtons[dwIndex].fsStyle = BTNS_BUTTON;
89     pButtons[dwIndex].dwData = 0;
90     pButtons[dwIndex].iString = 0;
91 }
92
93 static void TB_AddButtonsFromFlags(TBBUTTON *pButtons, DWORD dwButtonFlags, LPDWORD pdwNumButtons)
94 {
95     *pdwNumButtons = 0;
96
97     if (dwButtonFlags & HHWIN_BUTTON_EXPAND)
98         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_EXPAND);
99
100     if (dwButtonFlags & HHWIN_BUTTON_BACK)
101         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_BACK);
102
103     if (dwButtonFlags & HHWIN_BUTTON_FORWARD)
104         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_FORWARD);
105
106     if (dwButtonFlags & HHWIN_BUTTON_STOP)
107         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_STOP);
108
109     if (dwButtonFlags & HHWIN_BUTTON_REFRESH)
110         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_REFRESH);
111
112     if (dwButtonFlags & HHWIN_BUTTON_HOME)
113         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_HOME);
114
115     if (dwButtonFlags & HHWIN_BUTTON_SYNC)
116         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_SYNC);
117
118     if (dwButtonFlags & HHWIN_BUTTON_OPTIONS)
119         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_OPTIONS);
120
121     if (dwButtonFlags & HHWIN_BUTTON_PRINT)
122         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_PRINT);
123
124     if (dwButtonFlags & HHWIN_BUTTON_JUMP1)
125         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_JUMP1);
126
127     if (dwButtonFlags & HHWIN_BUTTON_JUMP2)
128         TB_AddButton(pButtons,(*pdwNumButtons)++, IDTB_JUMP2);
129
130     if (dwButtonFlags & HHWIN_BUTTON_ZOOM)
131         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_ZOOM);
132
133     if (dwButtonFlags & HHWIN_BUTTON_TOC_NEXT)
134         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_TOC_NEXT);
135
136     if (dwButtonFlags & HHWIN_BUTTON_TOC_PREV)
137         TB_AddButton(pButtons, (*pdwNumButtons)++, IDTB_TOC_PREV);
138 }
139
140 static BOOL HH_AddToolbar(HHInfo *pHHInfo)
141 {
142     HWND hToolbar;
143     HWND hwndParent = pHHInfo->pHHWinType->hwndHelp;
144     DWORD toolbarFlags = pHHInfo->pHHWinType->fsToolBarFlags;
145     TBBUTTON buttons[IDTB_TOC_PREV - IDTB_EXPAND];
146     TBADDBITMAP tbAB;
147     DWORD dwStyles, dwExStyles;
148     DWORD dwNumButtons, dwIndex;
149
150     /* FIXME: Remove the following line once we read the CHM file */
151     toolbarFlags = HHWIN_BUTTON_EXPAND | HHWIN_BUTTON_BACK | HHWIN_BUTTON_STOP |
152                    HHWIN_BUTTON_REFRESH | HHWIN_BUTTON_HOME | HHWIN_BUTTON_PRINT;
153     TB_AddButtonsFromFlags(buttons, toolbarFlags, &dwNumButtons);
154     pHHInfo->dwNumTBButtons = dwNumButtons;
155
156     dwStyles = WS_CHILDWINDOW | WS_VISIBLE | TBSTYLE_FLAT |
157                TBSTYLE_WRAPABLE | TBSTYLE_TOOLTIPS | CCS_NODIVIDER;
158     dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR;
159
160     hToolbar = CreateWindowExW(dwExStyles, TOOLBARCLASSNAMEW, NULL, dwStyles,
161                                0, 0, 0, 0, hwndParent, NULL,
162                                pHHInfo->hInstance, NULL);
163     if (!hToolbar)
164         return FALSE;
165
166     SendMessageW(hToolbar, TB_SETBITMAPSIZE, 0, MAKELONG(ICON_SIZE, ICON_SIZE));
167     SendMessageW(hToolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
168     SendMessageW(hToolbar, WM_SETFONT, (WPARAM)pHHInfo->hFont, TRUE);
169
170     /* FIXME: Load correct icons for all buttons */
171     tbAB.hInst = HINST_COMMCTRL;
172     tbAB.nID = IDB_STD_LARGE_COLOR;
173     SendMessageW(hToolbar, TB_ADDBITMAP, 0, (LPARAM)&tbAB);
174
175     for (dwIndex = 0; dwIndex < dwNumButtons; dwIndex++)
176     {
177         LPWSTR szBuf = HH_LoadString(buttons[dwIndex].idCommand);
178         DWORD dwLen = strlenW(szBuf);
179         szBuf[dwLen + 2] = 0; /* Double-null terminate */
180
181         buttons[dwIndex].iString = (DWORD)SendMessageW(hToolbar, TB_ADDSTRINGW, 0, (LPARAM)szBuf);
182         HeapFree(GetProcessHeap(), 0, szBuf);
183     }
184
185     SendMessageW(hToolbar, TB_ADDBUTTONSW, dwNumButtons, (LPARAM)&buttons);
186     SendMessageW(hToolbar, TB_AUTOSIZE, 0, 0);
187     ShowWindow(hToolbar, SW_SHOW);
188
189     pHHInfo->pHHWinType->hwndToolBar = hToolbar;
190     return TRUE;
191 }
192
193 /* Navigation Pane */
194
195 static BOOL HH_AddNavigationPane(HHInfo *pHHInfo)
196 {
197     return TRUE;
198 }
199
200 /* HTML Pane */
201
202 static BOOL HH_AddHTMLPane(HHInfo *pHHInfo)
203 {
204     return TRUE;
205 }
206
207 /* Viewer Window */
208
209 LRESULT CALLBACK Help_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
210 {
211     PAINTSTRUCT ps;
212     HDC hdc;
213
214     switch (message)
215     {
216
217         case WM_PAINT:
218             hdc = BeginPaint(hWnd, &ps);
219             EndPaint(hWnd, &ps);
220             break;
221         case WM_DESTROY:
222             PostQuitMessage(0);
223             break;
224
225         default:
226             return DefWindowProcW(hWnd, message, wParam, lParam);
227     }
228
229     return 0;
230 }
231
232 static BOOL HH_CreateHelpWindow(HHInfo *pHHInfo)
233 {
234     HWND hWnd;
235     HINSTANCE hInstance = pHHInfo->hInstance;
236     WNDCLASSEXW wcex;
237     DWORD dwStyles, dwExStyles;
238     DWORD x, y, width, height;
239
240     static const WCHAR windowClassW[] = {
241         'H','H',' ', 'P','a','r','e','n','t',0
242     };
243
244     static const WCHAR windowTitleW[] = {
245         'H','T','M','L',' ','H','e','l','p',0
246     };
247
248     wcex.cbSize         = sizeof(WNDCLASSEXW);
249     wcex.style          = CS_HREDRAW | CS_VREDRAW;
250     wcex.lpfnWndProc    = (WNDPROC)Help_WndProc;
251     wcex.cbClsExtra     = 0;
252     wcex.cbWndExtra     = 0;
253     wcex.hInstance      = hInstance;
254     wcex.hIcon          = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
255     wcex.hCursor        = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
256     wcex.hbrBackground  = (HBRUSH)(COLOR_BACKGROUND + 1);
257     wcex.lpszMenuName   = NULL;
258     wcex.lpszClassName  = windowClassW;
259     wcex.hIconSm        = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
260
261     RegisterClassExW(&wcex);
262
263     dwStyles = WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
264     dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR |
265                  WS_EX_WINDOWEDGE | WS_EX_APPWINDOW;
266
267     /* these will be loaded from the CHM file in the future if they're provided */
268     x = WINTYPE_DEFAULT_X;
269     y = WINTYPE_DEFAULT_Y;
270     width = WINTYPE_DEFAULT_WIDTH;
271     height = WINTYPE_DEFAULT_HEIGHT;
272
273     hWnd = CreateWindowExW(dwExStyles, windowClassW, windowTitleW, dwStyles,
274                            x, y, width, height, NULL, NULL, hInstance, NULL);
275     if (!hWnd)
276         return FALSE;
277
278     ShowWindow(hWnd, SW_SHOW);
279     UpdateWindow(hWnd);
280
281     /* store the pointer to the HH info struct */
282     SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)pHHInfo);
283
284     pHHInfo->pHHWinType->hwndHelp = hWnd;
285     return TRUE;
286 }
287
288 static void HH_CreateFont(HHInfo *pHHInfo)
289 {
290     LOGFONTW lf;
291
292     GetObjectW(GetStockObject(ANSI_VAR_FONT), sizeof(LOGFONTW), &lf);
293     lf.lfWeight = FW_NORMAL;
294     lf.lfItalic = FALSE;
295     lf.lfUnderline = FALSE;
296
297     pHHInfo->hFont = CreateFontIndirectW(&lf);
298 }
299
300 static void HH_InitRequiredControls(DWORD dwControls)
301 {
302     INITCOMMONCONTROLSEX icex;
303
304     icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
305     icex.dwICC = dwControls;
306     InitCommonControlsEx(&icex);
307 }
308
309 /* Creates the whole package */
310 static BOOL HH_CreateViewer(HHInfo *pHHInfo)
311 {
312     HH_CreateFont(pHHInfo);
313
314     if (!HH_CreateHelpWindow(pHHInfo))
315         return FALSE;
316
317     HH_InitRequiredControls(ICC_BAR_CLASSES);
318
319     if (!HH_AddToolbar(pHHInfo))
320         return FALSE;
321
322     if (!HH_AddNavigationPane(pHHInfo))
323         return FALSE;
324
325     if (!HH_AddHTMLPane(pHHInfo))
326         return FALSE;
327
328     return TRUE;
329 }
330
331 static HHInfo *HH_OpenHH(HINSTANCE hInstance, LPCWSTR szCmdLine)
332 {
333     HHInfo *pHHInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HHInfo));
334
335     pHHInfo->pHHWinType = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HH_WINTYPEW));
336     pHHInfo->hInstance = hInstance;
337     pHHInfo->szCmdLine = szCmdLine;
338
339     return pHHInfo;
340 }
341
342 static void HH_Close(HHInfo *pHHInfo)
343 {
344     if (!pHHInfo)
345         return;
346
347     HeapFree(GetProcessHeap(), 0, pHHInfo->pHHWinType);
348 }
349
350 /* FIXME: Check szCmdLine for bad arguments */
351 int WINAPI doWinMain(HINSTANCE hInstance, LPSTR szCmdLine)
352 {
353     MSG msg;
354     HHInfo *pHHInfo;
355
356     if (OleInitialize(NULL) != S_OK)
357         return -1;
358
359     pHHInfo = HH_OpenHH(hInstance, HH_ANSIToUnicode(szCmdLine));
360     if (!pHHInfo || !HH_CreateViewer(pHHInfo))
361     {
362         OleUninitialize();
363         return -1;
364     }
365
366     while (GetMessageW(&msg, 0, 0, 0))
367     {
368         TranslateMessage(&msg);
369         DispatchMessageW(&msg);
370     }
371
372     HH_Close(pHHInfo);
373     HeapFree(GetProcessHeap(), 0, pHHInfo);
374     OleUninitialize();
375
376     return 0;
377 }