Create a branch for network fixes.
[reactos.git] / base / applications / winhlp32 / winhelp.c
1 /*
2 * Help Viewer
3 *
4 * Copyright 1996 Ulrich Schmid <uschmid@mail.hh.provi.de>
5 * 2002 Sylvain Petreolle <spetreolle@yahoo.fr>
6 * 2002, 2008 Eric Pouech <eric.pouech@wanadoo.fr>
7 * 2004 Ken Belleau <jamez@ivic.qc.ca>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24 #include <assert.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <stdlib.h>
29
30 #define NONAMELESSUNION
31 #define NONAMELESSSTRUCT
32
33 #include "windef.h"
34 #include "winbase.h"
35 #include "wingdi.h"
36 #include "winuser.h"
37 #include "commdlg.h"
38 #include "winhelp.h"
39 #include "winhelp_res.h"
40 #include "shellapi.h"
41 #include "richedit.h"
42 #include "commctrl.h"
43
44 #include "wine/debug.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL(winhelp);
47
48 static BOOL WINHELP_RegisterWinClasses(void);
49 static LRESULT CALLBACK WINHELP_MainWndProc(HWND, UINT, WPARAM, LPARAM);
50 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND, UINT, WPARAM, LPARAM);
51 static LRESULT CALLBACK WINHELP_ButtonWndProc(HWND, UINT, WPARAM, LPARAM);
52 static LRESULT CALLBACK WINHELP_HistoryWndProc(HWND, UINT, WPARAM, LPARAM);
53 static LRESULT CALLBACK WINHELP_ShadowWndProc(HWND, UINT, WPARAM, LPARAM);
54 static BOOL WINHELP_CheckPopup(HWND, UINT, WPARAM, LPARAM, LRESULT*);
55 static void WINHELP_InitFonts(HWND hWnd);
56 static void WINHELP_DeleteWindow(WINHELP_WINDOW*);
57 static void WINHELP_DeleteButtons(WINHELP_WINDOW*);
58 static void WINHELP_SetupText(HWND hWnd, WINHELP_WINDOW *win, ULONG relative);
59 static void WINHELP_DeletePageLinks(HLPFILE_PAGE* page);
60
61 WINHELP_GLOBALS Globals = {3, NULL, TRUE, NULL, NULL, NULL, NULL, NULL, {{{NULL,NULL}},0}};
62
63 #define CTL_ID_BUTTON 0x700
64 #define CTL_ID_TEXT 0x701
65
66 /***********************************************************************
67 *
68 * WINHELP_GetOpenFileName
69 */
70 BOOL WINHELP_GetOpenFileName(LPSTR lpszFile, int len)
71 {
72 OPENFILENAME openfilename;
73 CHAR szDir[MAX_PATH];
74 CHAR szzFilter[2 * MAX_STRING_LEN + 100];
75 LPSTR p = szzFilter;
76
77 WINE_TRACE("()\n");
78
79 LoadString(Globals.hInstance, STID_HELP_FILES_HLP, p, MAX_STRING_LEN);
80 p += strlen(p) + 1;
81 lstrcpy(p, "*.hlp");
82 p += strlen(p) + 1;
83 LoadString(Globals.hInstance, STID_ALL_FILES, p, MAX_STRING_LEN);
84 p += strlen(p) + 1;
85 lstrcpy(p, "*.*");
86 p += strlen(p) + 1;
87 *p = '\0';
88
89 GetCurrentDirectory(sizeof(szDir), szDir);
90
91 lpszFile[0]='\0';
92
93 openfilename.lStructSize = sizeof(OPENFILENAME);
94 openfilename.hwndOwner = NULL;
95 openfilename.hInstance = Globals.hInstance;
96 openfilename.lpstrFilter = szzFilter;
97 openfilename.lpstrCustomFilter = 0;
98 openfilename.nMaxCustFilter = 0;
99 openfilename.nFilterIndex = 1;
100 openfilename.lpstrFile = lpszFile;
101 openfilename.nMaxFile = len;
102 openfilename.lpstrFileTitle = 0;
103 openfilename.nMaxFileTitle = 0;
104 openfilename.lpstrInitialDir = szDir;
105 openfilename.lpstrTitle = 0;
106 openfilename.Flags = 0;
107 openfilename.nFileOffset = 0;
108 openfilename.nFileExtension = 0;
109 openfilename.lpstrDefExt = 0;
110 openfilename.lCustData = 0;
111 openfilename.lpfnHook = 0;
112 openfilename.lpTemplateName = 0;
113
114 return GetOpenFileName(&openfilename);
115 }
116
117 static char* WINHELP_GetCaption(WINHELP_WNDPAGE* wpage)
118 {
119 if (wpage->wininfo->caption[0]) return wpage->wininfo->caption;
120 return wpage->page->file->lpszTitle;
121 }
122
123 /***********************************************************************
124 *
125 * WINHELP_LookupHelpFile
126 */
127 HLPFILE* WINHELP_LookupHelpFile(LPCSTR lpszFile)
128 {
129 HLPFILE* hlpfile;
130 char szFullName[MAX_PATH];
131 char szAddPath[MAX_PATH];
132 char *p;
133
134 /*
135 * NOTE: This is needed by popup windows only.
136 * In other cases it's not needed but does not hurt though.
137 */
138 if (Globals.active_win && Globals.active_win->page && Globals.active_win->page->file)
139 {
140 strcpy(szAddPath, Globals.active_win->page->file->lpszPath);
141 p = strrchr(szAddPath, '\\');
142 if (p) *p = 0;
143 }
144
145 /*
146 * FIXME: Should we swap conditions?
147 */
148 if (!SearchPath(NULL, lpszFile, ".hlp", MAX_PATH, szFullName, NULL) &&
149 !SearchPath(szAddPath, lpszFile, ".hlp", MAX_PATH, szFullName, NULL))
150 {
151 if (WINHELP_MessageBoxIDS_s(STID_FILE_NOT_FOUND_s, lpszFile, STID_WHERROR,
152 MB_YESNO|MB_ICONQUESTION) != IDYES)
153 return NULL;
154 if (!WINHELP_GetOpenFileName(szFullName, MAX_PATH))
155 return NULL;
156 }
157 hlpfile = HLPFILE_ReadHlpFile(szFullName);
158 if (!hlpfile)
159 WINHELP_MessageBoxIDS_s(STID_HLPFILE_ERROR_s, lpszFile,
160 STID_WHERROR, MB_OK|MB_ICONSTOP);
161 return hlpfile;
162 }
163
164 /******************************************************************
165 * WINHELP_GetWindowInfo
166 *
167 *
168 */
169 HLPFILE_WINDOWINFO* WINHELP_GetWindowInfo(HLPFILE* hlpfile, LPCSTR name)
170 {
171 static HLPFILE_WINDOWINFO mwi;
172 unsigned int i;
173
174 if (!name || !name[0])
175 name = Globals.active_win->lpszName;
176
177 if (hlpfile)
178 for (i = 0; i < hlpfile->numWindows; i++)
179 if (!strcmp(hlpfile->windows[i].name, name))
180 return &hlpfile->windows[i];
181
182 if (strcmp(name, "main") != 0)
183 {
184 WINE_FIXME("Couldn't find window info for %s\n", name);
185 assert(0);
186 return NULL;
187 }
188 if (!mwi.name[0])
189 {
190 strcpy(mwi.type, "primary");
191 strcpy(mwi.name, "main");
192 if (!LoadString(Globals.hInstance, STID_WINE_HELP,
193 mwi.caption, sizeof(mwi.caption)))
194 strcpy(mwi.caption, hlpfile->lpszTitle);
195 mwi.origin.x = mwi.origin.y = mwi.size.cx = mwi.size.cy = CW_USEDEFAULT;
196 mwi.style = SW_SHOW;
197 mwi.win_style = WS_OVERLAPPEDWINDOW;
198 mwi.sr_color = mwi.sr_color = 0xFFFFFF;
199 }
200 return &mwi;
201 }
202
203 /******************************************************************
204 * HLPFILE_GetPopupWindowInfo
205 *
206 *
207 */
208 static HLPFILE_WINDOWINFO* WINHELP_GetPopupWindowInfo(HLPFILE* hlpfile,
209 WINHELP_WINDOW* parent, LPARAM mouse)
210 {
211 static HLPFILE_WINDOWINFO wi;
212
213 RECT parent_rect;
214
215 wi.type[0] = wi.name[0] = wi.caption[0] = '\0';
216
217 /* Calculate horizontal size and position of a popup window */
218 GetWindowRect(parent->hMainWnd, &parent_rect);
219 wi.size.cx = (parent_rect.right - parent_rect.left) / 2;
220 wi.size.cy = 10; /* need a non null value, so that border are taken into account while computing */
221
222 wi.origin.x = (short)LOWORD(mouse);
223 wi.origin.y = (short)HIWORD(mouse);
224 ClientToScreen(parent->hMainWnd, &wi.origin);
225 wi.origin.x -= wi.size.cx / 2;
226 wi.origin.x = min(wi.origin.x, GetSystemMetrics(SM_CXSCREEN) - wi.size.cx);
227 wi.origin.x = max(wi.origin.x, 0);
228
229 wi.style = SW_SHOW;
230 wi.win_style = WS_POPUP | WS_BORDER;
231 wi.sr_color = parent->info->sr_color;
232 wi.nsr_color = 0xFFFFFF;
233
234 return &wi;
235 }
236
237 /***********************************************************************
238 *
239 * WinMain
240 */
241 int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
242 {
243 MSG msg;
244 LONG lHash = 0;
245 HLPFILE* hlpfile;
246 static CHAR default_wndname[] = "main";
247 LPSTR wndname = default_wndname;
248 WINHELP_DLL* dll;
249
250 Globals.hInstance = hInstance;
251
252 if (LoadLibrary("riched20.dll") == NULL)
253 return MessageBox(0, MAKEINTRESOURCE(STID_NO_RICHEDIT),
254 MAKEINTRESOURCE(STID_WHERROR), MB_OK);
255
256 /* Get options */
257 while (*cmdline && (*cmdline == ' ' || *cmdline == '-'))
258 {
259 CHAR option;
260 LPCSTR topic_id;
261 if (*cmdline++ == ' ') continue;
262
263 option = *cmdline;
264 if (option) cmdline++;
265 while (*cmdline && *cmdline == ' ') cmdline++;
266 switch (option)
267 {
268 case 'i':
269 case 'I':
270 topic_id = cmdline;
271 while (*cmdline && *cmdline != ' ') cmdline++;
272 if (*cmdline) *cmdline++ = '\0';
273 lHash = HLPFILE_Hash(topic_id);
274 break;
275
276 case '3':
277 case '4':
278 Globals.wVersion = option - '0';
279 break;
280
281 case 'x':
282 show = SW_HIDE;
283 Globals.isBook = FALSE;
284 break;
285
286 default:
287 WINE_FIXME("Unsupported cmd line: %s\n", cmdline);
288 break;
289 }
290 }
291
292 /* Create primary window */
293 if (!WINHELP_RegisterWinClasses())
294 {
295 WINE_FIXME("Couldn't register classes\n");
296 return 0;
297 }
298
299 if (*cmdline)
300 {
301 char* ptr;
302 if ((*cmdline == '"') && (ptr = strchr(cmdline+1, '"')))
303 {
304 cmdline++;
305 *ptr = '\0';
306 }
307 if ((ptr = strchr(cmdline, '>')))
308 {
309 *ptr = '\0';
310 wndname = ptr + 1;
311 }
312 hlpfile = WINHELP_LookupHelpFile(cmdline);
313 if (!hlpfile) return 0;
314 }
315 else hlpfile = NULL;
316 WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, lHash,
317 WINHELP_GetWindowInfo(hlpfile, wndname), show);
318
319 /* Message loop */
320 while (GetMessage(&msg, 0, 0, 0))
321 {
322 TranslateMessage(&msg);
323 DispatchMessage(&msg);
324 }
325 for (dll = Globals.dlls; dll; dll = dll->next)
326 {
327 if (dll->class & DC_INITTERM) dll->handler(DW_TERM, 0, 0);
328 }
329 return 0;
330 }
331
332 /***********************************************************************
333 *
334 * RegisterWinClasses
335 */
336 static BOOL WINHELP_RegisterWinClasses(void)
337 {
338 WNDCLASS class_main, class_button_box, class_shadow, class_history;
339
340 class_main.style = CS_HREDRAW | CS_VREDRAW;
341 class_main.lpfnWndProc = WINHELP_MainWndProc;
342 class_main.cbClsExtra = 0;
343 class_main.cbWndExtra = sizeof(LONG);
344 class_main.hInstance = Globals.hInstance;
345 class_main.hIcon = LoadIcon(Globals.hInstance, MAKEINTRESOURCE(IDI_WINHELP));
346 class_main.hCursor = LoadCursor(0, IDC_ARROW);
347 class_main.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
348 class_main.lpszMenuName = 0;
349 class_main.lpszClassName = MAIN_WIN_CLASS_NAME;
350
351 class_button_box = class_main;
352 class_button_box.lpfnWndProc = WINHELP_ButtonBoxWndProc;
353 class_button_box.cbWndExtra = 0;
354 class_button_box.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
355 class_button_box.lpszClassName = BUTTON_BOX_WIN_CLASS_NAME;
356
357 class_shadow = class_main;
358 class_shadow.lpfnWndProc = WINHELP_ShadowWndProc;
359 class_shadow.cbWndExtra = 0;
360 class_shadow.hbrBackground = (HBRUSH)(COLOR_3DDKSHADOW+1);
361 class_shadow.lpszClassName = SHADOW_WIN_CLASS_NAME;
362
363 class_history = class_main;
364 class_history.lpfnWndProc = WINHELP_HistoryWndProc;
365 class_history.lpszClassName = HISTORY_WIN_CLASS_NAME;
366
367 return (RegisterClass(&class_main) &&
368 RegisterClass(&class_button_box) &&
369 RegisterClass(&class_shadow) &&
370 RegisterClass(&class_history));
371 }
372
373 typedef struct
374 {
375 WORD size;
376 WORD command;
377 LONG data;
378 LONG reserved;
379 WORD ofsFilename;
380 WORD ofsData;
381 } WINHELP,*LPWINHELP;
382
383 static BOOL WINHELP_HasWorkingWindow(void)
384 {
385 if (!Globals.active_win) return FALSE;
386 if (Globals.active_win->next || Globals.win_list != Globals.active_win) return TRUE;
387 return Globals.active_win->page != NULL && Globals.active_win->page->file != NULL;
388 }
389
390 /******************************************************************
391 * WINHELP_HandleCommand
392 *
393 *
394 */
395 static LRESULT WINHELP_HandleCommand(HWND hSrcWnd, LPARAM lParam)
396 {
397 COPYDATASTRUCT* cds = (COPYDATASTRUCT*)lParam;
398 WINHELP* wh;
399
400 if (cds->dwData != 0xA1DE505)
401 {
402 WINE_FIXME("Wrong magic number (%08lx)\n", cds->dwData);
403 return 0;
404 }
405
406 wh = (WINHELP*)cds->lpData;
407
408 if (wh)
409 {
410 char* ptr = (wh->ofsFilename) ? (LPSTR)wh + wh->ofsFilename : NULL;
411
412 WINE_TRACE("Got[%u]: cmd=%u data=%08x fn=%s\n",
413 wh->size, wh->command, wh->data, ptr);
414 switch (wh->command)
415 {
416 case HELP_CONTEXT:
417 if (ptr)
418 {
419 MACRO_JumpContext(ptr, "main", wh->data);
420 }
421 if (!WINHELP_HasWorkingWindow()) MACRO_Exit();
422 break;
423 case HELP_QUIT:
424 MACRO_Exit();
425 break;
426 case HELP_CONTENTS:
427 if (ptr)
428 {
429 MACRO_JumpContents(ptr, "main");
430 }
431 if (!WINHELP_HasWorkingWindow()) MACRO_Exit();
432 break;
433 case HELP_HELPONHELP:
434 MACRO_HelpOn();
435 if (!WINHELP_HasWorkingWindow()) MACRO_Exit();
436 break;
437 /* case HELP_SETINDEX: */
438 case HELP_SETCONTENTS:
439 if (ptr)
440 {
441 MACRO_SetContents(ptr, wh->data);
442 }
443 break;
444 case HELP_CONTEXTPOPUP:
445 if (ptr)
446 {
447 MACRO_PopupContext(ptr, wh->data);
448 }
449 break;
450 /* case HELP_FORCEFILE:*/
451 /* case HELP_CONTEXTMENU: */
452 case HELP_FINDER:
453 /* in fact, should be the topic dialog box */
454 WINE_FIXME("HELP_FINDER: stub\n");
455 if (ptr)
456 {
457 MACRO_JumpHash(ptr, "main", 0);
458 }
459 break;
460 /* case HELP_WM_HELP: */
461 /* case HELP_SETPOPUP_POS: */
462 /* case HELP_KEY: */
463 /* case HELP_COMMAND: */
464 /* case HELP_PARTIALKEY: */
465 /* case HELP_MULTIKEY: */
466 /* case HELP_SETWINPOS: */
467 default:
468 WINE_FIXME("Unhandled command (%x) for remote winhelp control\n", wh->command);
469 break;
470 }
471 }
472 /* Always return success for now */
473 return 1;
474 }
475
476 void WINHELP_LayoutMainWindow(WINHELP_WINDOW* win)
477 {
478 RECT rect, button_box_rect;
479 INT text_top = 0;
480 HWND hButtonBoxWnd = GetDlgItem(win->hMainWnd, CTL_ID_BUTTON);
481 HWND hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
482
483 GetClientRect(win->hMainWnd, &rect);
484
485 /* Update button box and text Window */
486 SetWindowPos(hButtonBoxWnd, HWND_TOP,
487 rect.left, rect.top,
488 rect.right - rect.left,
489 rect.bottom - rect.top, 0);
490
491 if (GetWindowRect(hButtonBoxWnd, &button_box_rect))
492 text_top = rect.top + button_box_rect.bottom - button_box_rect.top;
493
494 SetWindowPos(hTextWnd, HWND_TOP,
495 rect.left, text_top,
496 rect.right - rect.left,
497 rect.bottom - text_top, 0);
498
499 }
500
501 static void WINHELP_RememberPage(WINHELP_WINDOW* win, WINHELP_WNDPAGE* wpage)
502 {
503 unsigned num;
504
505 if (!Globals.history.index || Globals.history.set[0].page != wpage->page)
506 {
507 num = sizeof(Globals.history.set) / sizeof(Globals.history.set[0]);
508 /* we're full, remove latest entry */
509 if (Globals.history.index == num)
510 {
511 HLPFILE_FreeHlpFile(Globals.history.set[num - 1].page->file);
512 Globals.history.index--;
513 }
514 memmove(&Globals.history.set[1], &Globals.history.set[0],
515 Globals.history.index * sizeof(Globals.history.set[0]));
516 Globals.history.set[0] = *wpage;
517 Globals.history.index++;
518 wpage->page->file->wRefCount++;
519 }
520 if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE);
521
522 num = sizeof(win->back.set) / sizeof(win->back.set[0]);
523 if (win->back.index == num)
524 {
525 /* we're full, remove latest entry */
526 HLPFILE_FreeHlpFile(win->back.set[0].page->file);
527 memmove(&win->back.set[0], &win->back.set[1],
528 (num - 1) * sizeof(win->back.set[0]));
529 win->back.index--;
530 }
531 win->back.set[win->back.index++] = *wpage;
532 wpage->page->file->wRefCount++;
533 }
534
535 /***********************************************************************
536 *
537 * WINHELP_CreateHelpWindow
538 */
539 BOOL WINHELP_CreateHelpWindow(WINHELP_WNDPAGE* wpage, int nCmdShow, BOOL remember)
540 {
541 WINHELP_WINDOW* win = NULL;
542 BOOL bPrimary, bPopup, bReUsed = FALSE;
543 LPSTR name;
544 HICON hIcon;
545 HWND hTextWnd = NULL;
546
547 bPrimary = !lstrcmpi(wpage->wininfo->name, "main");
548 bPopup = !bPrimary && (wpage->wininfo->win_style & WS_POPUP);
549
550 if (!bPopup)
551 {
552 for (win = Globals.win_list; win; win = win->next)
553 {
554 if (!lstrcmpi(win->lpszName, wpage->wininfo->name))
555 {
556 POINT pt = {0, 0};
557 SIZE sz = {0, 0};
558 DWORD flags = SWP_NOSIZE | SWP_NOMOVE;
559
560 WINHELP_DeleteButtons(win);
561 bReUsed = TRUE;
562 SetWindowText(win->hMainWnd, WINHELP_GetCaption(wpage));
563 if (wpage->wininfo->origin.x != CW_USEDEFAULT &&
564 wpage->wininfo->origin.y != CW_USEDEFAULT)
565 {
566 pt = wpage->wininfo->origin;
567 flags &= ~SWP_NOSIZE;
568 }
569 if (wpage->wininfo->size.cx != CW_USEDEFAULT &&
570 wpage->wininfo->size.cy != CW_USEDEFAULT)
571 {
572 sz = wpage->wininfo->size;
573 flags &= ~SWP_NOMOVE;
574 }
575 SetWindowPos(win->hMainWnd, HWND_TOP, pt.x, pt.y, sz.cx, sz.cy, flags);
576
577 if (wpage->page && win->page && wpage->page->file != win->page->file)
578 WINHELP_DeleteBackSet(win);
579 WINHELP_InitFonts(win->hMainWnd);
580
581 win->page = wpage->page;
582 win->info = wpage->wininfo;
583 hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
584 WINHELP_SetupText(hTextWnd, win, wpage->relative);
585
586 InvalidateRect(win->hMainWnd, NULL, TRUE);
587 if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE);
588
589 break;
590 }
591 }
592 }
593
594 if (!win)
595 {
596 /* Initialize WINHELP_WINDOW struct */
597 win = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
598 sizeof(WINHELP_WINDOW) + strlen(wpage->wininfo->name) + 1);
599 if (!win) return FALSE;
600 win->next = Globals.win_list;
601 Globals.win_list = win;
602
603 name = (char*)win + sizeof(WINHELP_WINDOW);
604 lstrcpy(name, wpage->wininfo->name);
605 win->lpszName = name;
606 win->hHandCur = LoadCursorW(0, (LPWSTR)IDC_HAND);
607 win->back.index = 0;
608 win->font_scale = 1;
609 }
610 win->page = wpage->page;
611 win->info = wpage->wininfo;
612
613 if (!bPopup && wpage->page && remember)
614 {
615 WINHELP_RememberPage(win, wpage);
616 }
617
618 if (bPopup)
619 Globals.active_popup = win;
620 else
621 Globals.active_win = win;
622
623 /* Initialize default pushbuttons */
624 if (bPrimary && wpage->page)
625 {
626 CHAR buffer[MAX_STRING_LEN];
627
628 LoadString(Globals.hInstance, STID_CONTENTS, buffer, sizeof(buffer));
629 MACRO_CreateButton("BTN_CONTENTS", buffer, "Contents()");
630 LoadString(Globals.hInstance, STID_INDEX, buffer, sizeof(buffer));
631 MACRO_CreateButton("BTN_INDEX", buffer, "Finder()");
632 LoadString(Globals.hInstance, STID_BACK, buffer, sizeof(buffer));
633 MACRO_CreateButton("BTN_BACK", buffer, "Back()");
634 if (win->back.index <= 1) MACRO_DisableButton("BTN_BACK");
635 }
636
637 if (!bReUsed)
638 {
639 win->hMainWnd = CreateWindowEx((bPopup) ? WS_EX_TOOLWINDOW : 0, MAIN_WIN_CLASS_NAME,
640 WINHELP_GetCaption(wpage),
641 bPrimary ? WS_OVERLAPPEDWINDOW : wpage->wininfo->win_style,
642 wpage->wininfo->origin.x, wpage->wininfo->origin.y,
643 wpage->wininfo->size.cx, wpage->wininfo->size.cy,
644 bPopup ? Globals.active_win->hMainWnd : NULL,
645 bPrimary ? LoadMenu(Globals.hInstance, MAKEINTRESOURCE(MAIN_MENU)) : 0,
646 Globals.hInstance, win);
647 if (!bPopup)
648 /* Create button box and text Window */
649 CreateWindow(BUTTON_BOX_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
650 0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_BUTTON, Globals.hInstance, NULL);
651
652 hTextWnd = CreateWindow(RICHEDIT_CLASS, NULL,
653 ES_MULTILINE | ES_READONLY | WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_VISIBLE,
654 0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_TEXT, Globals.hInstance, NULL);
655 SendMessage(hTextWnd, EM_SETEVENTMASK, 0,
656 SendMessage(hTextWnd, EM_GETEVENTMASK, 0, 0) | ENM_MOUSEEVENTS);
657 }
658
659 hIcon = (wpage->page) ? wpage->page->file->hIcon : NULL;
660 if (!hIcon) hIcon = LoadIcon(Globals.hInstance, MAKEINTRESOURCE(IDI_WINHELP));
661 SendMessage(win->hMainWnd, WM_SETICON, ICON_SMALL, (DWORD_PTR)hIcon);
662
663 /* Initialize file specific pushbuttons */
664 if (!(wpage->wininfo->win_style & WS_POPUP) && wpage->page)
665 {
666 HLPFILE_MACRO *macro;
667 for (macro = wpage->page->file->first_macro; macro; macro = macro->next)
668 MACRO_ExecuteMacro(macro->lpszMacro);
669
670 for (macro = wpage->page->first_macro; macro; macro = macro->next)
671 MACRO_ExecuteMacro(macro->lpszMacro);
672 }
673
674 if (bPopup)
675 {
676 DWORD mask = SendMessage(hTextWnd, EM_GETEVENTMASK, 0, 0);
677 RECT rect;
678
679 win->font_scale = Globals.active_win->font_scale;
680 WINHELP_SetupText(hTextWnd, win, wpage->relative);
681
682 /* we need the window to be shown for richedit to compute the size */
683 ShowWindow(win->hMainWnd, nCmdShow);
684 SendMessage(hTextWnd, EM_SETEVENTMASK, 0, mask | ENM_REQUESTRESIZE);
685 SendMessage(hTextWnd, EM_REQUESTRESIZE, 0, 0);
686 SendMessage(hTextWnd, EM_SETEVENTMASK, 0, mask);
687
688 GetWindowRect(win->hMainWnd, &rect);
689 win->hShadowWnd = CreateWindowEx(WS_EX_TOOLWINDOW, SHADOW_WIN_CLASS_NAME,
690 "", WS_POPUP | WS_VISIBLE,
691 rect.left + SHADOW_DX, rect.top + SHADOW_DY,
692 rect.right - rect.left,
693 rect.bottom - rect.top,
694 Globals.active_win->hMainWnd, 0,
695 Globals.hInstance, NULL);
696 SetWindowPos(win->hMainWnd, win->hShadowWnd, 0, 0, 0, 0,
697 SWP_NOSIZE | SWP_NOMOVE);
698 }
699 else
700 {
701 WINHELP_SetupText(hTextWnd, win, wpage->relative);
702 WINHELP_LayoutMainWindow(win);
703 ShowWindow(win->hMainWnd, nCmdShow);
704 }
705
706 return TRUE;
707 }
708
709 /******************************************************************
710 * WINHELP_OpenHelpWindow
711 * Main function to search for a page and display it in a window
712 */
713 BOOL WINHELP_OpenHelpWindow(HLPFILE_PAGE* (*lookup)(HLPFILE*, LONG, ULONG*),
714 HLPFILE* hlpfile, LONG val, HLPFILE_WINDOWINFO* wi,
715 int nCmdShow)
716 {
717 WINHELP_WNDPAGE wpage;
718
719 wpage.page = lookup(hlpfile, val, &wpage.relative);
720 if (wpage.page) wpage.page->file->wRefCount++;
721 wpage.wininfo = wi;
722 return WINHELP_CreateHelpWindow(&wpage, nCmdShow, TRUE);
723 }
724
725 /***********************************************************************
726 *
727 * WINHELP_FindLink
728 */
729 static HLPFILE_LINK* WINHELP_FindLink(WINHELP_WINDOW* win, LPARAM pos)
730 {
731 HLPFILE_LINK* link;
732 POINTL mouse_ptl, char_ptl, char_next_ptl;
733 DWORD cp;
734
735 if (!win->page) return NULL;
736
737 mouse_ptl.x = (short)LOWORD(pos);
738 mouse_ptl.y = (short)HIWORD(pos);
739 cp = SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_CHARFROMPOS,
740 0, (LPARAM)&mouse_ptl);
741
742 for (link = win->page->first_link; link; link = link->next)
743 {
744 if (link->cpMin <= cp && cp <= link->cpMax)
745 {
746 /* check whether we're at end of line */
747 SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_POSFROMCHAR,
748 (LPARAM)&char_ptl, cp);
749 SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_POSFROMCHAR,
750 (LPARAM)&char_next_ptl, cp + 1);
751 if (char_next_ptl.y != char_ptl.y || mouse_ptl.x >= char_next_ptl.x)
752 link = NULL;
753 break;
754 }
755 }
756 return link;
757 }
758
759 /******************************************************************
760 * WINHELP_HandleTextMouse
761 *
762 */
763 static BOOL WINHELP_HandleTextMouse(WINHELP_WINDOW* win, UINT msg, LPARAM lParam)
764 {
765 HLPFILE* hlpfile;
766 HLPFILE_LINK* link;
767 BOOL ret = FALSE;
768
769 switch (msg)
770 {
771 case WM_MOUSEMOVE:
772 if (WINHELP_FindLink(win, lParam))
773 SetCursor(win->hHandCur);
774 else
775 SetCursor(LoadCursor(0, IDC_ARROW));
776 break;
777
778 case WM_LBUTTONDOWN:
779 if ((win->current_link = WINHELP_FindLink(win, lParam)))
780 ret = TRUE;
781 break;
782
783 case WM_LBUTTONUP:
784 if ((link = WINHELP_FindLink(win, lParam)) && link == win->current_link)
785 {
786 HLPFILE_WINDOWINFO* wi;
787
788 switch (link->cookie)
789 {
790 case hlp_link_link:
791 if ((hlpfile = WINHELP_LookupHelpFile(link->string)))
792 {
793 if (link->window == -1)
794 wi = win->info;
795 else if (link->window < hlpfile->numWindows)
796 wi = &hlpfile->windows[link->window];
797 else
798 {
799 WINE_WARN("link to window %d/%d\n", link->window, hlpfile->numWindows);
800 break;
801 }
802 WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, link->hash, wi, SW_NORMAL);
803 }
804 break;
805 case hlp_link_popup:
806 if ((hlpfile = WINHELP_LookupHelpFile(link->string)))
807 WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, link->hash,
808 WINHELP_GetPopupWindowInfo(hlpfile, win, lParam),
809 SW_NORMAL);
810 break;
811 case hlp_link_macro:
812 MACRO_ExecuteMacro(link->string);
813 break;
814 default:
815 WINE_FIXME("Unknown link cookie %d\n", link->cookie);
816 }
817 ret = TRUE;
818 }
819 win->current_link = NULL;
820 break;
821 }
822 return ret;
823 }
824
825 /***********************************************************************
826 *
827 * WINHELP_MainWndProc
828 */
829 static LRESULT CALLBACK WINHELP_MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
830 {
831 WINHELP_WINDOW *win;
832 WINHELP_BUTTON *button;
833 RECT rect;
834 INT curPos, min, max, dy, keyDelta;
835 HWND hTextWnd;
836 LRESULT ret;
837
838 if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, &ret)) return ret;
839
840 switch (msg)
841 {
842 case WM_NCCREATE:
843 win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
844 SetWindowLongPtr(hWnd, 0, (ULONG_PTR) win);
845 if (!win->page && Globals.isBook)
846 PostMessage(hWnd, WM_COMMAND, MNID_FILE_OPEN, 0);
847 win->hMainWnd = hWnd;
848 break;
849
850 case WM_WINDOWPOSCHANGED:
851 WINHELP_LayoutMainWindow((WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0));
852 break;
853
854 case WM_COMMAND:
855 win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
856 switch (wParam)
857 {
858 /* Menu FILE */
859 case MNID_FILE_OPEN: MACRO_FileOpen(); break;
860 case MNID_FILE_PRINT: MACRO_Print(); break;
861 case MNID_FILE_SETUP: MACRO_PrinterSetup(); break;
862 case MNID_FILE_EXIT: MACRO_Exit(); break;
863
864 /* Menu EDIT */
865 case MNID_EDIT_COPYDLG:
866 SendMessage(GetDlgItem(hWnd, CTL_ID_TEXT), WM_COPY, 0, 0);
867 break;
868 case MNID_EDIT_ANNOTATE:MACRO_Annotate(); break;
869
870 /* Menu Bookmark */
871 case MNID_BKMK_DEFINE: MACRO_BookmarkDefine(); break;
872
873 /* Menu Help */
874 case MNID_HELP_HELPON: MACRO_HelpOn(); break;
875 case MNID_HELP_HELPTOP: MACRO_HelpOnTop(); break;
876 case MNID_HELP_ABOUT: MACRO_About(); break;
877 case MNID_HELP_WINE: ShellAbout(hWnd, "WINE", "Help", 0); break;
878
879 /* Context help */
880 case MNID_CTXT_ANNOTATE:MACRO_Annotate(); break;
881 case MNID_CTXT_COPY: MACRO_CopyDialog(); break;
882 case MNID_CTXT_PRINT: MACRO_Print(); break;
883 case MNID_OPTS_HISTORY: MACRO_History(); break;
884 case MNID_OPTS_FONTS_SMALL:
885 case MNID_CTXT_FONTS_SMALL:
886 win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
887 if (win->font_scale != 0)
888 {
889 win->font_scale = 0;
890 WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */);
891 }
892 break;
893 case MNID_OPTS_FONTS_NORMAL:
894 case MNID_CTXT_FONTS_NORMAL:
895 win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
896 if (win->font_scale != 1)
897 {
898 win->font_scale = 1;
899 WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */);
900 }
901 break;
902 case MNID_OPTS_FONTS_LARGE:
903 case MNID_CTXT_FONTS_LARGE:
904 win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
905 if (win->font_scale != 2)
906 {
907 win->font_scale = 2;
908 WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */);
909 }
910 break;
911 case MNID_OPTS_HELP_DEFAULT:
912 case MNID_OPTS_HELP_VISIBLE:
913 case MNID_OPTS_HELP_NONVISIBLE:
914 case MNID_OPTS_SYSTEM_COLORS:
915 case MNID_CTXT_HELP_DEFAULT:
916 case MNID_CTXT_HELP_VISIBLE:
917 case MNID_CTXT_HELP_NONVISIBLE:
918 case MNID_CTXT_SYSTEM_COLORS:
919 /* FIXME: NIY */
920
921 default:
922 /* Buttons */
923 for (button = win->first_button; button; button = button->next)
924 if (wParam == button->wParam) break;
925 if (button)
926 MACRO_ExecuteMacro(button->lpszMacro);
927 else if (!HIWORD(wParam))
928 MessageBox(0, MAKEINTRESOURCE(STID_NOT_IMPLEMENTED),
929 MAKEINTRESOURCE(STID_WHERROR), MB_OK);
930 break;
931 }
932 break;
933 /* EPP case WM_DESTROY: */
934 /* EPP if (Globals.hPopupWnd) DestroyWindow(Globals.hPopupWnd); */
935 /* EPP break; */
936 case WM_COPYDATA:
937 return WINHELP_HandleCommand((HWND)wParam, lParam);
938
939 case WM_CHAR:
940 if (wParam == 3)
941 {
942 SendMessage(GetDlgItem(hWnd, CTL_ID_TEXT), WM_COPY, 0, 0);
943 return 0;
944 }
945 break;
946
947 case WM_KEYDOWN:
948 keyDelta = 0;
949
950 switch (wParam)
951 {
952 case VK_UP:
953 case VK_DOWN:
954 keyDelta = GetSystemMetrics(SM_CXVSCROLL);
955 if (wParam == VK_UP)
956 keyDelta = -keyDelta;
957
958 case VK_PRIOR:
959 case VK_NEXT:
960 win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
961 hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
962 curPos = GetScrollPos(hTextWnd, SB_VERT);
963 GetScrollRange(hTextWnd, SB_VERT, &min, &max);
964
965 if (keyDelta == 0)
966 {
967 GetClientRect(hTextWnd, &rect);
968 keyDelta = (rect.bottom - rect.top) / 2;
969 if (wParam == VK_PRIOR)
970 keyDelta = -keyDelta;
971 }
972
973 curPos += keyDelta;
974 if (curPos > max)
975 curPos = max;
976 else if (curPos < min)
977 curPos = min;
978
979 dy = GetScrollPos(hTextWnd, SB_VERT) - curPos;
980 SetScrollPos(hTextWnd, SB_VERT, curPos, TRUE);
981 ScrollWindow(hTextWnd, 0, dy, NULL, NULL);
982 UpdateWindow(hTextWnd);
983 return 0;
984
985 case VK_ESCAPE:
986 MACRO_Exit();
987 return 0;
988 }
989 break;
990
991 case WM_NOTIFY:
992 if (wParam == CTL_ID_TEXT)
993 {
994 RECT rc;
995
996 switch (((NMHDR*)lParam)->code)
997 {
998 case EN_MSGFILTER:
999 {
1000 const MSGFILTER* msgf = (const MSGFILTER*)lParam;
1001 switch (msgf->msg)
1002 {
1003 case WM_KEYUP:
1004 if (msgf->wParam == VK_ESCAPE) DestroyWindow(hWnd);
1005 break;
1006 case WM_RBUTTONDOWN:
1007 {
1008 HMENU hMenu;
1009 POINT pt;
1010
1011 win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1012 hMenu = LoadMenu(Globals.hInstance, (LPSTR)CONTEXT_MENU);
1013 switch (win->font_scale)
1014 {
1015 case 0:
1016 CheckMenuItem(hMenu, MNID_CTXT_FONTS_SMALL,
1017 MF_BYCOMMAND|MF_CHECKED);
1018 break;
1019 default:
1020 WINE_FIXME("Unsupported %d\n", win->font_scale);
1021 case 1:
1022 CheckMenuItem(hMenu, MNID_CTXT_FONTS_NORMAL,
1023 MF_BYCOMMAND|MF_CHECKED);
1024 break;
1025 case 2:
1026 CheckMenuItem(hMenu, MNID_CTXT_FONTS_LARGE,
1027 MF_BYCOMMAND|MF_CHECKED);
1028 break;
1029 }
1030 pt.x = (int)(short)LOWORD(msgf->lParam);
1031 pt.y = (int)(short)HIWORD(msgf->lParam);
1032 ClientToScreen(msgf->nmhdr.hwndFrom, &pt);
1033 TrackPopupMenu(GetSubMenu(hMenu, 0), TPM_LEFTALIGN|TPM_TOPALIGN,
1034 pt.x, pt.y, 0, hWnd, NULL);
1035 DestroyMenu(hMenu);
1036 }
1037 break;
1038 default:
1039 return WINHELP_HandleTextMouse((WINHELP_WINDOW*)GetWindowLongPtr(hWnd, 0),
1040 msgf->msg, msgf->lParam);
1041 }
1042 }
1043 break;
1044
1045 case EN_REQUESTRESIZE:
1046 rc = ((REQRESIZE*)lParam)->rc;
1047 win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1048 AdjustWindowRect(&rc, GetWindowLong(win->hMainWnd, GWL_STYLE),
1049 FALSE);
1050 SetWindowPos(win->hMainWnd, HWND_TOP, 0, 0,
1051 rc.right - rc.left, rc.bottom - rc.top,
1052 SWP_NOMOVE | SWP_NOZORDER);
1053 WINHELP_LayoutMainWindow(win);
1054 break;
1055 }
1056 }
1057 break;
1058
1059 case WM_INITMENUPOPUP:
1060 win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1061 CheckMenuItem((HMENU)wParam, MNID_OPTS_FONTS_SMALL,
1062 MF_BYCOMMAND | (win->font_scale == 0) ? MF_CHECKED : 0);
1063 CheckMenuItem((HMENU)wParam, MNID_OPTS_FONTS_NORMAL,
1064 MF_BYCOMMAND | (win->font_scale == 1) ? MF_CHECKED : 0);
1065 CheckMenuItem((HMENU)wParam, MNID_OPTS_FONTS_LARGE,
1066 MF_BYCOMMAND | (win->font_scale == 2) ? MF_CHECKED : 0);
1067 break;
1068
1069 case WM_NCDESTROY:
1070 {
1071 BOOL bExit;
1072 win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1073 bExit = (Globals.wVersion >= 4 && !lstrcmpi(win->lpszName, "main"));
1074 WINHELP_DeleteWindow(win);
1075
1076 if (bExit) MACRO_Exit();
1077 if (!Globals.win_list)
1078 PostQuitMessage(0);
1079 }
1080 break;
1081 }
1082 return DefWindowProc(hWnd, msg, wParam, lParam);
1083 }
1084
1085 static DWORD CALLBACK WINHELP_RtfStreamIn(DWORD_PTR cookie, BYTE* buff,
1086 LONG cb, LONG* pcb)
1087 {
1088 struct RtfData* rd = (struct RtfData*)cookie;
1089
1090 if (rd->where >= rd->ptr) return 1;
1091 if (rd->where + cb > rd->ptr)
1092 cb = rd->ptr - rd->where;
1093 memcpy(buff, rd->where, cb);
1094 rd->where += cb;
1095 *pcb = cb;
1096 return 0;
1097 }
1098
1099 static void WINHELP_SetupText(HWND hTextWnd, WINHELP_WINDOW* win, ULONG relative)
1100 {
1101 SendMessage(hTextWnd, WM_SETREDRAW, FALSE, 0);
1102 SendMessage(hTextWnd, EM_SETBKGNDCOLOR, 0, (LPARAM)win->info->sr_color);
1103 /* set word-wrap to window size (undocumented) */
1104 SendMessage(hTextWnd, EM_SETTARGETDEVICE, 0, 0);
1105 if (win->page)
1106 {
1107 struct RtfData rd;
1108 EDITSTREAM es;
1109 unsigned cp = 0;
1110 POINTL ptl;
1111 POINT pt;
1112
1113
1114 if (HLPFILE_BrowsePage(win->page, &rd, win->font_scale, relative))
1115 {
1116 rd.where = rd.data;
1117 es.dwCookie = (DWORD_PTR)&rd;
1118 es.dwError = 0;
1119 es.pfnCallback = WINHELP_RtfStreamIn;
1120
1121 SendMessageW(hTextWnd, EM_STREAMIN, SF_RTF, (LPARAM)&es);
1122 cp = rd.char_pos_rel;
1123 }
1124 /* FIXME: else leaking potentially the rd.first_link chain */
1125 HeapFree(GetProcessHeap(), 0, rd.data);
1126 SendMessage(hTextWnd, EM_POSFROMCHAR, (WPARAM)&ptl, cp ? cp - 1 : 0);
1127 pt.x = 0; pt.y = ptl.y;
1128 SendMessage(hTextWnd, EM_SETSCROLLPOS, 0, (LPARAM)&pt);
1129 }
1130 else
1131 {
1132 SendMessage(hTextWnd, WM_SETTEXT, 0, (LPARAM)"");
1133 }
1134 SendMessage(hTextWnd, WM_SETREDRAW, TRUE, 0);
1135 InvalidateRect(hTextWnd, NULL, TRUE);
1136 }
1137
1138 /***********************************************************************
1139 *
1140 * WINHELP_ButtonBoxWndProc
1141 */
1142 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1143 {
1144 WINDOWPOS *winpos;
1145 WINHELP_WINDOW *win;
1146 WINHELP_BUTTON *button;
1147 SIZE button_size;
1148 INT x, y;
1149
1150 if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0L;
1151
1152 switch (msg)
1153 {
1154 case WM_WINDOWPOSCHANGING:
1155 winpos = (WINDOWPOS*) lParam;
1156 win = (WINHELP_WINDOW*) GetWindowLongPtr(GetParent(hWnd), 0);
1157
1158 /* Update buttons */
1159 button_size.cx = 0;
1160 button_size.cy = 0;
1161 for (button = win->first_button; button; button = button->next)
1162 {
1163 HDC hDc;
1164 SIZE textsize;
1165 if (!button->hWnd)
1166 {
1167 button->hWnd = CreateWindow(STRING_BUTTON, button->lpszName,
1168 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
1169 0, 0, 0, 0,
1170 hWnd, (HMENU) button->wParam,
1171 Globals.hInstance, 0);
1172 if (button->hWnd) {
1173 if (Globals.button_proc == NULL)
1174 Globals.button_proc = (WNDPROC) GetWindowLongPtr(button->hWnd, GWLP_WNDPROC);
1175 SetWindowLongPtr(button->hWnd, GWLP_WNDPROC, (LONG_PTR) WINHELP_ButtonWndProc);
1176 }
1177 }
1178 hDc = GetDC(button->hWnd);
1179 GetTextExtentPoint(hDc, button->lpszName,
1180 lstrlen(button->lpszName), &textsize);
1181 ReleaseDC(button->hWnd, hDc);
1182
1183 button_size.cx = max(button_size.cx, textsize.cx + BUTTON_CX);
1184 button_size.cy = max(button_size.cy, textsize.cy + BUTTON_CY);
1185 }
1186
1187 x = 0;
1188 y = 0;
1189 for (button = win->first_button; button; button = button->next)
1190 {
1191 SetWindowPos(button->hWnd, HWND_TOP, x, y, button_size.cx, button_size.cy, 0);
1192
1193 if (x + 2 * button_size.cx <= winpos->cx)
1194 x += button_size.cx;
1195 else
1196 x = 0, y += button_size.cy;
1197 }
1198 winpos->cy = y + (x ? button_size.cy : 0);
1199 break;
1200
1201 case WM_COMMAND:
1202 SendMessage(GetParent(hWnd), msg, wParam, lParam);
1203 break;
1204
1205 case WM_KEYDOWN:
1206 switch (wParam)
1207 {
1208 case VK_UP:
1209 case VK_DOWN:
1210 case VK_PRIOR:
1211 case VK_NEXT:
1212 case VK_ESCAPE:
1213 return SendMessage(GetParent(hWnd), msg, wParam, lParam);
1214 }
1215 break;
1216 }
1217
1218 return DefWindowProc(hWnd, msg, wParam, lParam);
1219 }
1220
1221 /***********************************************************************
1222 *
1223 * WINHELP_ButtonWndProc
1224 */
1225 static LRESULT CALLBACK WINHELP_ButtonWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1226 {
1227 if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0;
1228
1229 if (msg == WM_KEYDOWN)
1230 {
1231 switch (wParam)
1232 {
1233 case VK_UP:
1234 case VK_DOWN:
1235 case VK_PRIOR:
1236 case VK_NEXT:
1237 case VK_ESCAPE:
1238 return SendMessage(GetParent(hWnd), msg, wParam, lParam);
1239 }
1240 }
1241
1242 return CallWindowProc(Globals.button_proc, hWnd, msg, wParam, lParam);
1243 }
1244
1245 /******************************************************************
1246 * WINHELP_HistoryWndProc
1247 *
1248 *
1249 */
1250 static LRESULT CALLBACK WINHELP_HistoryWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1251 {
1252 WINHELP_WINDOW* win;
1253 PAINTSTRUCT ps;
1254 HDC hDc;
1255 TEXTMETRIC tm;
1256 unsigned int i;
1257 RECT r;
1258
1259 switch (msg)
1260 {
1261 case WM_NCCREATE:
1262 win = (WINHELP_WINDOW*)((LPCREATESTRUCT)lParam)->lpCreateParams;
1263 SetWindowLongPtr(hWnd, 0, (ULONG_PTR)win);
1264 win->hHistoryWnd = hWnd;
1265 break;
1266 case WM_CREATE:
1267 win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1268 hDc = GetDC(hWnd);
1269 GetTextMetrics(hDc, &tm);
1270 GetWindowRect(hWnd, &r);
1271
1272 r.right = r.left + 30 * tm.tmAveCharWidth;
1273 r.bottom = r.top + (sizeof(Globals.history.set) / sizeof(Globals.history.set[0])) * tm.tmHeight;
1274 AdjustWindowRect(&r, GetWindowLong(hWnd, GWL_STYLE), FALSE);
1275 if (r.left < 0) {r.right -= r.left; r.left = 0;}
1276 if (r.top < 0) {r.bottom -= r.top; r.top = 0;}
1277
1278 MoveWindow(hWnd, r.left, r.top, r.right, r.bottom, TRUE);
1279 ReleaseDC(hWnd, hDc);
1280 break;
1281 case WM_LBUTTONDOWN:
1282 win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1283 hDc = GetDC(hWnd);
1284 GetTextMetrics(hDc, &tm);
1285 i = HIWORD(lParam) / tm.tmHeight;
1286 if (i < Globals.history.index)
1287 WINHELP_CreateHelpWindow(&Globals.history.set[i], SW_SHOW, TRUE);
1288 ReleaseDC(hWnd, hDc);
1289 break;
1290 case WM_PAINT:
1291 hDc = BeginPaint(hWnd, &ps);
1292 win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1293 GetTextMetrics(hDc, &tm);
1294
1295 for (i = 0; i < Globals.history.index; i++)
1296 {
1297 if (Globals.history.set[i].page->file == Globals.active_win->page->file)
1298 {
1299 TextOut(hDc, 0, i * tm.tmHeight,
1300 Globals.history.set[i].page->lpszTitle,
1301 strlen(Globals.history.set[i].page->lpszTitle));
1302 }
1303 else
1304 {
1305 char buffer[1024];
1306 const char* ptr1;
1307 const char* ptr2;
1308 unsigned len;
1309
1310 ptr1 = strrchr(Globals.history.set[i].page->file->lpszPath, '\\');
1311 if (!ptr1) ptr1 = Globals.history.set[i].page->file->lpszPath;
1312 else ptr1++;
1313 ptr2 = strrchr(ptr1, '.');
1314 len = ptr2 ? ptr2 - ptr1 : strlen(ptr1);
1315 if (len > sizeof(buffer)) len = sizeof(buffer);
1316 memcpy(buffer, ptr1, len);
1317 if (len < sizeof(buffer)) buffer[len++] = ':';
1318 strncpy(&buffer[len], Globals.history.set[i].page->lpszTitle, sizeof(buffer) - len);
1319 buffer[sizeof(buffer) - 1] = '\0';
1320 TextOut(hDc, 0, i * tm.tmHeight, buffer, strlen(buffer));
1321 }
1322 }
1323 EndPaint(hWnd, &ps);
1324 break;
1325 case WM_DESTROY:
1326 win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1327 if (hWnd == win->hHistoryWnd)
1328 win->hHistoryWnd = 0;
1329 break;
1330 }
1331 return DefWindowProc(hWnd, msg, wParam, lParam);
1332 }
1333
1334 /***********************************************************************
1335 *
1336 * WINHELP_ShadowWndProc
1337 */
1338 static LRESULT CALLBACK WINHELP_ShadowWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1339 {
1340 if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0;
1341 return WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL) ? 0L : DefWindowProc(hWnd, msg, wParam, lParam);
1342 }
1343
1344 /***********************************************************************
1345 *
1346 * WINHELP_CheckPopup
1347 */
1348 static BOOL WINHELP_CheckPopup(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT* lret)
1349 {
1350 HWND hPopup;
1351
1352 if (!Globals.active_popup) return FALSE;
1353
1354 switch (msg)
1355 {
1356 case WM_NOTIFY:
1357 {
1358 MSGFILTER* msgf = (MSGFILTER*)lParam;
1359 if (msgf->nmhdr.code == EN_MSGFILTER)
1360 {
1361 if (!WINHELP_CheckPopup(hWnd, msgf->msg, msgf->wParam, msgf->lParam, NULL))
1362 return FALSE;
1363 if (lret) *lret = 1;
1364 return TRUE;
1365 }
1366 }
1367 break;
1368 case WM_ACTIVATE:
1369 if (wParam != WA_INACTIVE || (HWND)lParam == Globals.active_win->hMainWnd ||
1370 (HWND)lParam == Globals.active_popup->hMainWnd ||
1371 GetWindow((HWND)lParam, GW_OWNER) == Globals.active_win->hMainWnd)
1372 break;
1373 case WM_LBUTTONUP:
1374 case WM_LBUTTONDOWN:
1375 if (WINHELP_HandleTextMouse(Globals.active_popup, msg, lParam) && msg == WM_LBUTTONDOWN)
1376 return FALSE;
1377 /* fall through */
1378 case WM_MBUTTONDOWN:
1379 case WM_RBUTTONDOWN:
1380 case WM_NCLBUTTONDOWN:
1381 case WM_NCMBUTTONDOWN:
1382 case WM_NCRBUTTONDOWN:
1383 hPopup = Globals.active_popup->hMainWnd;
1384 Globals.active_popup = NULL;
1385 DestroyWindow(hPopup);
1386 return TRUE;
1387 }
1388 return FALSE;
1389 }
1390
1391 /******************************************************************
1392 * WINHELP_DeleteButtons
1393 *
1394 */
1395 static void WINHELP_DeleteButtons(WINHELP_WINDOW* win)
1396 {
1397 WINHELP_BUTTON* b;
1398 WINHELP_BUTTON* bp;
1399
1400 for (b = win->first_button; b; b = bp)
1401 {
1402 DestroyWindow(b->hWnd);
1403 bp = b->next;
1404 HeapFree(GetProcessHeap(), 0, b);
1405 }
1406 win->first_button = NULL;
1407 }
1408
1409 /******************************************************************
1410 * WINHELP_DeleteBackSet
1411 *
1412 */
1413 void WINHELP_DeleteBackSet(WINHELP_WINDOW* win)
1414 {
1415 unsigned int i;
1416
1417 for (i = 0; i < win->back.index; i++)
1418 {
1419 HLPFILE_FreeHlpFile(win->back.set[i].page->file);
1420 win->back.set[i].page = NULL;
1421 }
1422 win->back.index = 0;
1423 }
1424
1425 /******************************************************************
1426 * WINHELP_DeletePageLinks
1427 *
1428 */
1429 static void WINHELP_DeletePageLinks(HLPFILE_PAGE* page)
1430 {
1431 HLPFILE_LINK* curr;
1432 HLPFILE_LINK* next;
1433
1434 for (curr = page->first_link; curr; curr = next)
1435 {
1436 next = curr->next;
1437 HeapFree(GetProcessHeap(), 0, curr);
1438 }
1439 }
1440
1441 /***********************************************************************
1442 *
1443 * WINHELP_DeleteWindow
1444 */
1445 static void WINHELP_DeleteWindow(WINHELP_WINDOW* win)
1446 {
1447 WINHELP_WINDOW** w;
1448
1449 for (w = &Globals.win_list; *w; w = &(*w)->next)
1450 {
1451 if (*w == win)
1452 {
1453 *w = win->next;
1454 break;
1455 }
1456 }
1457
1458 if (Globals.active_win == win)
1459 {
1460 Globals.active_win = Globals.win_list;
1461 if (Globals.win_list)
1462 SetActiveWindow(Globals.win_list->hMainWnd);
1463 }
1464
1465 if (win == Globals.active_popup)
1466 Globals.active_popup = NULL;
1467
1468 WINHELP_DeleteButtons(win);
1469
1470 if (win->page) WINHELP_DeletePageLinks(win->page);
1471 if (win->hShadowWnd) DestroyWindow(win->hShadowWnd);
1472 if (win->hHistoryWnd) DestroyWindow(win->hHistoryWnd);
1473
1474 DeleteObject(win->hBrush);
1475
1476 WINHELP_DeleteBackSet(win);
1477
1478 if (win->page) HLPFILE_FreeHlpFile(win->page->file);
1479 HeapFree(GetProcessHeap(), 0, win);
1480 }
1481
1482 /***********************************************************************
1483 *
1484 * WINHELP_InitFonts
1485 */
1486 static void WINHELP_InitFonts(HWND hWnd)
1487 {
1488 WINHELP_WINDOW *win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1489 LOGFONT logfontlist[] = {
1490 {-10, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1491 {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1492 {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1493 {-12, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1494 {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1495 {-10, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
1496 { -8, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"}};
1497 #define FONTS_LEN (sizeof(logfontlist)/sizeof(*logfontlist))
1498
1499 static HFONT fonts[FONTS_LEN];
1500 static BOOL init = 0;
1501
1502 win->fonts_len = FONTS_LEN;
1503 win->fonts = fonts;
1504
1505 if (!init)
1506 {
1507 UINT i;
1508
1509 for (i = 0; i < FONTS_LEN; i++)
1510 {
1511 fonts[i] = CreateFontIndirect(&logfontlist[i]);
1512 }
1513
1514 init = 1;
1515 }
1516 }
1517
1518 /***********************************************************************
1519 *
1520 * WINHELP_MessageBoxIDS_s
1521 */
1522 INT WINHELP_MessageBoxIDS_s(UINT ids_text, LPCSTR str, UINT ids_title, WORD type)
1523 {
1524 CHAR text[MAX_STRING_LEN];
1525 CHAR newtext[MAX_STRING_LEN + MAX_PATH];
1526
1527 LoadString(Globals.hInstance, ids_text, text, sizeof(text));
1528 wsprintf(newtext, text, str);
1529
1530 return MessageBox(0, newtext, MAKEINTRESOURCE(ids_title), type);
1531 }
1532
1533 /**************************************************************************
1534 * cb_KWBTree
1535 *
1536 * HLPFILE_BPTreeCallback enumeration function for '|KWBTREE' internal file.
1537 *
1538 */
1539 static void cb_KWBTree(void *p, void **next, void *cookie)
1540 {
1541 HWND hListWnd = (HWND)cookie;
1542 int count;
1543
1544 WINE_TRACE("Adding '%s' to search list\n", (char *)p);
1545 SendMessage(hListWnd, LB_INSERTSTRING, -1, (LPARAM)p);
1546 count = SendMessage(hListWnd, LB_GETCOUNT, 0, 0);
1547 SendMessage(hListWnd, LB_SETITEMDATA, count-1, (LPARAM)p);
1548 *next = (char*)p + strlen((char*)p) + 7;
1549 }
1550
1551 struct index_data
1552 {
1553 HLPFILE* hlpfile;
1554 BOOL jump;
1555 ULONG offset;
1556 };
1557
1558 /**************************************************************************
1559 * WINHELP_IndexDlgProc
1560 *
1561 * Index dialog callback function.
1562 *
1563 * nResult passed to EndDialog:
1564 * 1: CANCEL button
1565 * >1: valid offset value +2.
1566 * EndDialog itself can return 0 (error).
1567 */
1568 INT_PTR CALLBACK WINHELP_IndexDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1569 {
1570 static struct index_data* id;
1571 int sel;
1572
1573 switch (msg)
1574 {
1575 case WM_INITDIALOG:
1576 id = (struct index_data*)((PROPSHEETPAGE*)lParam)->lParam;
1577 HLPFILE_BPTreeEnum(id->hlpfile->kwbtree, cb_KWBTree,
1578 GetDlgItem(hWnd, IDC_INDEXLIST));
1579 id->jump = FALSE;
1580 id->offset = 1;
1581 return TRUE;
1582 case WM_NOTIFY:
1583 switch (((NMHDR*)lParam)->code)
1584 {
1585 case PSN_APPLY:
1586 sel = SendDlgItemMessage(hWnd, IDC_INDEXLIST, LB_GETCURSEL, 0, 0);
1587 if (sel != LB_ERR)
1588 {
1589 BYTE *p;
1590 int count;
1591
1592 p = (BYTE*)SendDlgItemMessage(hWnd, IDC_INDEXLIST,
1593 LB_GETITEMDATA, sel, 0);
1594 count = *(short*)((char *)p + strlen((char *)p) + 1);
1595 if (count > 1)
1596 {
1597 MessageBox(hWnd, "count > 1 not supported yet", "Error", MB_OK | MB_ICONSTOP);
1598 SetWindowLongPtr(hWnd, DWLP_MSGRESULT, PSNRET_INVALID);
1599 return TRUE;
1600 }
1601 id->offset = *(ULONG*)((char *)p + strlen((char *)p) + 3);
1602 id->offset = *(long*)(id->hlpfile->kwdata + id->offset + 9);
1603 if (id->offset == 0xFFFFFFFF)
1604 {
1605 MessageBox(hWnd, "macro keywords not supported yet", "Error", MB_OK | MB_ICONSTOP);
1606 SetWindowLongPtr(hWnd, DWLP_MSGRESULT, PSNRET_INVALID);
1607 return TRUE;
1608 }
1609 id->jump = TRUE;
1610 SetWindowLongPtr(hWnd, DWLP_MSGRESULT, PSNRET_NOERROR);
1611 }
1612 return TRUE;
1613 default:
1614 return FALSE;
1615 }
1616 break;
1617 default:
1618 break;
1619 }
1620 return FALSE;
1621 }
1622
1623 /**************************************************************************
1624 * WINHELP_SearchDlgProc
1625 *
1626 */
1627 INT_PTR CALLBACK WINHELP_SearchDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1628 {
1629 static struct index_data* id;
1630
1631 switch (msg)
1632 {
1633 case WM_INITDIALOG:
1634 id = (struct index_data*)((PROPSHEETPAGE*)lParam)->lParam;
1635 return TRUE;
1636 case WM_NOTIFY:
1637 switch (((NMHDR*)lParam)->code)
1638 {
1639 case PSN_APPLY:
1640 SetWindowLongPtr(hWnd, DWLP_MSGRESULT, PSNRET_NOERROR);
1641 return TRUE;
1642 default:
1643 return FALSE;
1644 }
1645 break;
1646 default:
1647 break;
1648 }
1649 return FALSE;
1650 }
1651
1652 /**************************************************************************
1653 * WINHELP_CreateIndexWindow
1654 *
1655 * Displays a dialog with keywords of current help file.
1656 *
1657 */
1658 BOOL WINHELP_CreateIndexWindow(BOOL is_search)
1659 {
1660 HPROPSHEETPAGE psPage[3];
1661 PROPSHEETPAGE psp;
1662 PROPSHEETHEADER psHead;
1663 struct index_data id;
1664 char buf[256];
1665
1666 if (Globals.active_win && Globals.active_win->page && Globals.active_win->page->file)
1667 id.hlpfile = Globals.active_win->page->file;
1668 else
1669 return FALSE;
1670
1671 if (id.hlpfile->kwbtree == NULL)
1672 {
1673 WINE_TRACE("No index provided\n");
1674 return FALSE;
1675 }
1676
1677 InitCommonControls();
1678
1679 id.jump = FALSE;
1680 memset(&psp, 0, sizeof(psp));
1681 psp.dwSize = sizeof(psp);
1682 psp.dwFlags = 0;
1683 psp.hInstance = Globals.hInstance;
1684
1685 psp.u.pszTemplate = MAKEINTRESOURCE(IDD_INDEX);
1686 psp.lParam = (LPARAM)&id;
1687 psp.pfnDlgProc = WINHELP_IndexDlgProc;
1688 psPage[0] = CreatePropertySheetPage(&psp);
1689
1690 psp.u.pszTemplate = MAKEINTRESOURCE(IDD_SEARCH);
1691 psp.lParam = (LPARAM)&id;
1692 psp.pfnDlgProc = WINHELP_SearchDlgProc;
1693 psPage[1] = CreatePropertySheetPage(&psp);
1694
1695 memset(&psHead, 0, sizeof(psHead));
1696 psHead.dwSize = sizeof(psHead);
1697
1698 LoadString(Globals.hInstance, STID_PSH_INDEX, buf, sizeof(buf));
1699 strcat(buf, Globals.active_win->info->caption);
1700
1701 psHead.pszCaption = buf;
1702 psHead.nPages = 2;
1703 psHead.u2.nStartPage = is_search ? 1 : 0;
1704 psHead.hwndParent = Globals.active_win->hMainWnd;
1705 psHead.u3.phpage = psPage;
1706 psHead.dwFlags = PSH_NOAPPLYNOW;
1707
1708 PropertySheet(&psHead);
1709 if (id.jump)
1710 {
1711 WINE_TRACE("got %d as an offset\n", id.offset);
1712 WINHELP_OpenHelpWindow(HLPFILE_PageByOffset, id.hlpfile, id.offset,
1713 Globals.active_win->info, SW_NORMAL);
1714 }
1715 return TRUE;
1716 }