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