* Sync up to trunk head (r64716).
[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", 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 border 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, 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_RelaseWindow
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 return TRUE;
972 }
973 return FALSE;
974 }
975
976 /***********************************************************************
977 *
978 * WINHELP_ButtonWndProc
979 */
980 static LRESULT CALLBACK WINHELP_ButtonWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
981 {
982 if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0;
983
984 if (msg == WM_KEYDOWN)
985 {
986 switch (wParam)
987 {
988 case VK_UP:
989 case VK_DOWN:
990 case VK_PRIOR:
991 case VK_NEXT:
992 case VK_ESCAPE:
993 return SendMessageA(GetParent(hWnd), msg, wParam, lParam);
994 }
995 }
996
997 return CallWindowProcA(Globals.button_proc, hWnd, msg, wParam, lParam);
998 }
999
1000 /***********************************************************************
1001 *
1002 * WINHELP_ButtonBoxWndProc
1003 */
1004 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1005 {
1006 WINDOWPOS *winpos;
1007 WINHELP_WINDOW *win;
1008 WINHELP_BUTTON *button;
1009 SIZE button_size;
1010 INT x, y;
1011
1012 if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0L;
1013
1014 switch (msg)
1015 {
1016 case WM_WINDOWPOSCHANGING:
1017 winpos = (WINDOWPOS*) lParam;
1018 win = (WINHELP_WINDOW*) GetWindowLongPtrW(GetParent(hWnd), 0);
1019
1020 /* Update buttons */
1021 button_size.cx = 0;
1022 button_size.cy = 0;
1023 for (button = win->first_button; button; button = button->next)
1024 {
1025 HDC hDc;
1026 SIZE textsize;
1027 if (!button->hWnd)
1028 {
1029 button->hWnd = CreateWindowA(STRING_BUTTON, button->lpszName,
1030 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
1031 0, 0, 0, 0,
1032 hWnd, (HMENU) button->wParam,
1033 Globals.hInstance, 0);
1034 if (button->hWnd)
1035 {
1036 if (Globals.button_proc == NULL)
1037 {
1038 NONCLIENTMETRICSW ncm;
1039 Globals.button_proc = (WNDPROC) GetWindowLongPtrA(button->hWnd, GWLP_WNDPROC);
1040
1041 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
1042 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
1043 sizeof(NONCLIENTMETRICSW), &ncm, 0);
1044 Globals.hButtonFont = CreateFontIndirectW(&ncm.lfMenuFont);
1045 }
1046 SetWindowLongPtrA(button->hWnd, GWLP_WNDPROC, (LONG_PTR) WINHELP_ButtonWndProc);
1047 if (Globals.hButtonFont)
1048 SendMessageW(button->hWnd, WM_SETFONT, (WPARAM)Globals.hButtonFont, TRUE);
1049 }
1050 }
1051 hDc = GetDC(button->hWnd);
1052 GetTextExtentPointA(hDc, button->lpszName, strlen(button->lpszName), &textsize);
1053 ReleaseDC(button->hWnd, hDc);
1054
1055 button_size.cx = max(button_size.cx, textsize.cx + BUTTON_CX);
1056 button_size.cy = max(button_size.cy, textsize.cy + BUTTON_CY);
1057 }
1058
1059 x = 0;
1060 y = 0;
1061 for (button = win->first_button; button; button = button->next)
1062 {
1063 SetWindowPos(button->hWnd, HWND_TOP, x, y, button_size.cx, button_size.cy, 0);
1064
1065 if (x + 2 * button_size.cx <= winpos->cx)
1066 x += button_size.cx;
1067 else
1068 x = 0, y += button_size.cy;
1069 }
1070 winpos->cy = y + (x ? button_size.cy : 0);
1071 break;
1072
1073 case WM_COMMAND:
1074 SendMessageW(GetParent(hWnd), msg, wParam, lParam);
1075 break;
1076
1077 case WM_KEYDOWN:
1078 switch (wParam)
1079 {
1080 case VK_UP:
1081 case VK_DOWN:
1082 case VK_PRIOR:
1083 case VK_NEXT:
1084 case VK_ESCAPE:
1085 return SendMessageA(GetParent(hWnd), msg, wParam, lParam);
1086 }
1087 break;
1088 }
1089
1090 return DefWindowProcA(hWnd, msg, wParam, lParam);
1091 }
1092
1093 /******************************************************************
1094 * WINHELP_HistoryWndProc
1095 *
1096 *
1097 */
1098 static LRESULT CALLBACK WINHELP_HistoryWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1099 {
1100 WINHELP_WINDOW* win;
1101 PAINTSTRUCT ps;
1102 HDC hDc;
1103 TEXTMETRICW tm;
1104 unsigned int i;
1105 RECT r;
1106
1107 switch (msg)
1108 {
1109 case WM_NCCREATE:
1110 win = (WINHELP_WINDOW*)((LPCREATESTRUCTA)lParam)->lpCreateParams;
1111 SetWindowLongPtrW(hWnd, 0, (ULONG_PTR)win);
1112 win->hHistoryWnd = hWnd;
1113 break;
1114 case WM_CREATE:
1115 hDc = GetDC(hWnd);
1116 GetTextMetricsW(hDc, &tm);
1117 GetWindowRect(hWnd, &r);
1118
1119 r.right = r.left + 30 * tm.tmAveCharWidth;
1120 r.bottom = r.top + (sizeof(Globals.history.set) / sizeof(Globals.history.set[0])) * tm.tmHeight;
1121 AdjustWindowRect(&r, GetWindowLongW(hWnd, GWL_STYLE), FALSE);
1122 if (r.left < 0) {r.right -= r.left; r.left = 0;}
1123 if (r.top < 0) {r.bottom -= r.top; r.top = 0;}
1124
1125 MoveWindow(hWnd, r.left, r.top, r.right, r.bottom, TRUE);
1126 ReleaseDC(hWnd, hDc);
1127 break;
1128 case WM_LBUTTONDOWN:
1129 hDc = GetDC(hWnd);
1130 GetTextMetricsW(hDc, &tm);
1131 i = HIWORD(lParam) / tm.tmHeight;
1132 if (i < Globals.history.index)
1133 WINHELP_CreateHelpWindow(&Globals.history.set[i], SW_SHOW, TRUE);
1134 ReleaseDC(hWnd, hDc);
1135 break;
1136 case WM_PAINT:
1137 hDc = BeginPaint(hWnd, &ps);
1138 GetTextMetricsW(hDc, &tm);
1139
1140 for (i = 0; i < Globals.history.index; i++)
1141 {
1142 if (Globals.history.set[i].page->file == Globals.active_win->page->file)
1143 {
1144 TextOutA(hDc, 0, i * tm.tmHeight,
1145 Globals.history.set[i].page->lpszTitle,
1146 strlen(Globals.history.set[i].page->lpszTitle));
1147 }
1148 else
1149 {
1150 char buffer[1024];
1151 const char* ptr1;
1152 const char* ptr2;
1153 unsigned len;
1154
1155 ptr1 = strrchr(Globals.history.set[i].page->file->lpszPath, '\\');
1156 if (!ptr1) ptr1 = Globals.history.set[i].page->file->lpszPath;
1157 else ptr1++;
1158 ptr2 = strrchr(ptr1, '.');
1159 len = ptr2 ? ptr2 - ptr1 : strlen(ptr1);
1160 if (len > sizeof(buffer)) len = sizeof(buffer);
1161 memcpy(buffer, ptr1, len);
1162 if (len < sizeof(buffer)) buffer[len++] = ':';
1163 lstrcpynA(&buffer[len], Globals.history.set[i].page->lpszTitle, sizeof(buffer) - len);
1164 TextOutA(hDc, 0, i * tm.tmHeight, buffer, strlen(buffer));
1165 }
1166 }
1167 EndPaint(hWnd, &ps);
1168 break;
1169 case WM_DESTROY:
1170 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1171 if (hWnd == win->hHistoryWnd)
1172 win->hHistoryWnd = 0;
1173 break;
1174 }
1175 return DefWindowProcA(hWnd, msg, wParam, lParam);
1176 }
1177
1178 /**************************************************************************
1179 * cb_KWBTree
1180 *
1181 * HLPFILE_BPTreeCallback enumeration function for '|KWBTREE' internal file.
1182 *
1183 */
1184 static void cb_KWBTree(void *p, void **next, void *cookie)
1185 {
1186 HWND hListWnd = cookie;
1187 int count;
1188
1189 WINE_TRACE("Adding '%s' to search list\n", (char *)p);
1190 SendMessageA(hListWnd, LB_INSERTSTRING, -1, (LPARAM)p);
1191 count = SendMessageW(hListWnd, LB_GETCOUNT, 0, 0);
1192 SendMessageW(hListWnd, LB_SETITEMDATA, count-1, (LPARAM)p);
1193 *next = (char*)p + strlen((char*)p) + 7;
1194 }
1195
1196 struct index_data
1197 {
1198 HLPFILE* hlpfile;
1199 BOOL jump;
1200 ULONG offset;
1201 };
1202
1203 /**************************************************************************
1204 * WINHELP_IndexDlgProc
1205 *
1206 */
1207 static INT_PTR CALLBACK WINHELP_IndexDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1208 {
1209 static struct index_data* id;
1210 int sel;
1211
1212 switch (msg)
1213 {
1214 case WM_INITDIALOG:
1215 id = (struct index_data*)((PROPSHEETPAGEA*)lParam)->lParam;
1216 HLPFILE_BPTreeEnum(id->hlpfile->kwbtree, cb_KWBTree,
1217 GetDlgItem(hWnd, IDC_INDEXLIST));
1218 id->jump = FALSE;
1219 id->offset = 1;
1220 return TRUE;
1221 case WM_COMMAND:
1222 switch (HIWORD(wParam))
1223 {
1224 case LBN_DBLCLK:
1225 if (LOWORD(wParam) == IDC_INDEXLIST)
1226 SendMessageW(GetParent(hWnd), PSM_PRESSBUTTON, PSBTN_OK, 0);
1227 break;
1228 }
1229 break;
1230 case WM_NOTIFY:
1231 switch (((NMHDR*)lParam)->code)
1232 {
1233 case PSN_APPLY:
1234 sel = SendDlgItemMessageW(hWnd, IDC_INDEXLIST, LB_GETCURSEL, 0, 0);
1235 if (sel != LB_ERR)
1236 {
1237 BYTE *p;
1238 int count;
1239
1240 p = (BYTE*)SendDlgItemMessageW(hWnd, IDC_INDEXLIST, LB_GETITEMDATA, sel, 0);
1241 count = *(short*)((char *)p + strlen((char *)p) + 1);
1242 if (count > 1)
1243 {
1244 MessageBoxA(hWnd, "count > 1 not supported yet", "Error", MB_OK | MB_ICONSTOP);
1245 SetWindowLongPtrW(hWnd, DWLP_MSGRESULT, PSNRET_INVALID);
1246 return TRUE;
1247 }
1248 id->offset = *(ULONG*)((char *)p + strlen((char *)p) + 3);
1249 id->offset = *(long*)(id->hlpfile->kwdata + id->offset + 9);
1250 if (id->offset == 0xFFFFFFFF)
1251 {
1252 MessageBoxA(hWnd, "macro keywords not supported yet", "Error", MB_OK | MB_ICONSTOP);
1253 SetWindowLongPtrW(hWnd, DWLP_MSGRESULT, PSNRET_INVALID);
1254 return TRUE;
1255 }
1256 id->jump = TRUE;
1257 SetWindowLongPtrW(hWnd, DWLP_MSGRESULT, PSNRET_NOERROR);
1258 }
1259 return TRUE;
1260 default:
1261 return FALSE;
1262 }
1263 break;
1264 default:
1265 break;
1266 }
1267 return FALSE;
1268 }
1269
1270 /**************************************************************************
1271 * WINHELP_SearchDlgProc
1272 *
1273 */
1274 static INT_PTR CALLBACK WINHELP_SearchDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1275 {
1276 switch (msg)
1277 {
1278 case WM_INITDIALOG:
1279 return TRUE;
1280 case WM_NOTIFY:
1281 switch (((NMHDR*)lParam)->code)
1282 {
1283 case PSN_APPLY:
1284 SetWindowLongPtrW(hWnd, DWLP_MSGRESULT, PSNRET_NOERROR);
1285 return TRUE;
1286 default:
1287 return FALSE;
1288 }
1289 break;
1290 default:
1291 break;
1292 }
1293 return FALSE;
1294 }
1295
1296 /***********************************************************************
1297 *
1298 * WINHELP_MainWndProc
1299 */
1300 static LRESULT CALLBACK WINHELP_MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1301 {
1302 WINHELP_WINDOW *win;
1303 WINHELP_BUTTON *button;
1304 HWND hTextWnd;
1305 LRESULT ret;
1306
1307 if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, &ret)) return ret;
1308
1309 switch (msg)
1310 {
1311 case WM_NCCREATE:
1312 win = (WINHELP_WINDOW*) ((LPCREATESTRUCTA) lParam)->lpCreateParams;
1313 SetWindowLongPtrW(hWnd, 0, (ULONG_PTR) win);
1314 if (!win->page && Globals.isBook)
1315 PostMessageW(hWnd, WM_COMMAND, MNID_FILE_OPEN, 0);
1316 win->hMainWnd = hWnd;
1317 break;
1318
1319 case WM_WINDOWPOSCHANGED:
1320 WINHELP_LayoutMainWindow((WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0));
1321 break;
1322
1323 case WM_COMMAND:
1324 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1325 switch (LOWORD(wParam))
1326 {
1327 /* Menu FILE */
1328 case MNID_FILE_OPEN: MACRO_FileOpen(); break;
1329 case MNID_FILE_PRINT: MACRO_Print(); break;
1330 case MNID_FILE_SETUP: MACRO_PrinterSetup(); break;
1331 case MNID_FILE_EXIT: MACRO_Exit(); break;
1332
1333 /* Menu EDIT */
1334 case MNID_EDIT_COPYDLG:
1335 SendDlgItemMessageW(hWnd, CTL_ID_TEXT, WM_COPY, 0, 0);
1336 break;
1337 case MNID_EDIT_ANNOTATE:MACRO_Annotate(); break;
1338
1339 /* Menu Bookmark */
1340 case MNID_BKMK_DEFINE: MACRO_BookmarkDefine(); break;
1341
1342 /* Menu Help */
1343 case MNID_HELP_HELPON: MACRO_HelpOn(); break;
1344 case MNID_HELP_HELPTOP: MACRO_HelpOnTop(); break;
1345 case MNID_HELP_ABOUT: MACRO_About(); break;
1346
1347 /* Context help */
1348 case MNID_CTXT_ANNOTATE:MACRO_Annotate(); break;
1349 case MNID_CTXT_COPY: MACRO_CopyDialog(); break;
1350 case MNID_CTXT_PRINT: MACRO_Print(); break;
1351 case MNID_OPTS_HISTORY: MACRO_History(); break;
1352 case MNID_OPTS_FONTS_SMALL:
1353 case MNID_CTXT_FONTS_SMALL:
1354 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1355 if (win->font_scale != 0)
1356 {
1357 win->font_scale = 0;
1358 WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */);
1359 }
1360 break;
1361 case MNID_OPTS_FONTS_NORMAL:
1362 case MNID_CTXT_FONTS_NORMAL:
1363 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1364 if (win->font_scale != 1)
1365 {
1366 win->font_scale = 1;
1367 WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */);
1368 }
1369 break;
1370 case MNID_OPTS_FONTS_LARGE:
1371 case MNID_CTXT_FONTS_LARGE:
1372 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1373 if (win->font_scale != 2)
1374 {
1375 win->font_scale = 2;
1376 WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */);
1377 }
1378 break;
1379
1380 default:
1381 /* Buttons */
1382 for (button = win->first_button; button; button = button->next)
1383 if (wParam == button->wParam) break;
1384 if (button)
1385 MACRO_ExecuteMacro(win, button->lpszMacro);
1386 else if (!HIWORD(wParam))
1387 MessageBoxW(0, MAKEINTRESOURCEW(STID_NOT_IMPLEMENTED),
1388 MAKEINTRESOURCEW(STID_WHERROR), MB_OK);
1389 break;
1390 }
1391 break;
1392 /* EPP case WM_DESTROY: */
1393 /* EPP if (Globals.hPopupWnd) DestroyWindow(Globals.hPopupWnd); */
1394 /* EPP break; */
1395 case WM_COPYDATA:
1396 return WINHELP_HandleCommand((HWND)wParam, lParam);
1397
1398 case WM_CHAR:
1399 if (wParam == 3)
1400 {
1401 SendDlgItemMessageW(hWnd, CTL_ID_TEXT, WM_COPY, 0, 0);
1402 return 0;
1403 }
1404 break;
1405
1406 case WM_KEYDOWN:
1407 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1408 hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
1409
1410 switch (wParam)
1411 {
1412 case VK_UP:
1413 SendMessageW(hTextWnd, EM_SCROLL, SB_LINEUP, 0);
1414 return 0;
1415 case VK_DOWN:
1416 SendMessageW(hTextWnd, EM_SCROLL, SB_LINEDOWN, 0);
1417 return 0;
1418 case VK_PRIOR:
1419 SendMessageW(hTextWnd, EM_SCROLL, SB_PAGEUP, 0);
1420 return 0;
1421 case VK_NEXT:
1422 SendMessageW(hTextWnd, EM_SCROLL, SB_PAGEDOWN, 0);
1423 return 0;
1424 case VK_ESCAPE:
1425 MACRO_Exit();
1426 return 0;
1427 }
1428 break;
1429
1430 case WM_NOTIFY:
1431 if (wParam == CTL_ID_TEXT)
1432 {
1433 RECT rc;
1434
1435 switch (((NMHDR*)lParam)->code)
1436 {
1437 case EN_MSGFILTER:
1438 {
1439 const MSGFILTER* msgf = (const MSGFILTER*)lParam;
1440 switch (msgf->msg)
1441 {
1442 case WM_KEYUP:
1443 if (msgf->wParam == VK_ESCAPE)
1444 WINHELP_ReleaseWindow((WINHELP_WINDOW*)GetWindowLongPtrW(hWnd, 0));
1445 break;
1446 case WM_RBUTTONDOWN:
1447 {
1448 HMENU hMenu;
1449 POINT pt;
1450
1451 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1452 hMenu = LoadMenuW(Globals.hInstance, MAKEINTRESOURCEW(CONTEXT_MENU));
1453 switch (win->font_scale)
1454 {
1455 case 0:
1456 CheckMenuItem(hMenu, MNID_CTXT_FONTS_SMALL,
1457 MF_BYCOMMAND|MF_CHECKED);
1458 break;
1459 default:
1460 WINE_FIXME("Unsupported %d\n", win->font_scale);
1461 /* fall through */
1462 case 1:
1463 CheckMenuItem(hMenu, MNID_CTXT_FONTS_NORMAL,
1464 MF_BYCOMMAND|MF_CHECKED);
1465 break;
1466 case 2:
1467 CheckMenuItem(hMenu, MNID_CTXT_FONTS_LARGE,
1468 MF_BYCOMMAND|MF_CHECKED);
1469 break;
1470 }
1471 pt.x = (int)(short)LOWORD(msgf->lParam);
1472 pt.y = (int)(short)HIWORD(msgf->lParam);
1473 ClientToScreen(msgf->nmhdr.hwndFrom, &pt);
1474 TrackPopupMenu(GetSubMenu(hMenu, 0), TPM_LEFTALIGN|TPM_TOPALIGN,
1475 pt.x, pt.y, 0, hWnd, NULL);
1476 DestroyMenu(hMenu);
1477 }
1478 break;
1479 default:
1480 return WINHELP_HandleTextMouse((WINHELP_WINDOW*)GetWindowLongPtrW(hWnd, 0),
1481 msgf->msg, msgf->lParam);
1482 }
1483 }
1484 break;
1485
1486 case EN_REQUESTRESIZE:
1487 rc = ((REQRESIZE*)lParam)->rc;
1488 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1489 AdjustWindowRect(&rc, GetWindowLongW(win->hMainWnd, GWL_STYLE),
1490 FALSE);
1491 SetWindowPos(win->hMainWnd, HWND_TOP, 0, 0,
1492 rc.right - rc.left, rc.bottom - rc.top,
1493 SWP_NOMOVE | SWP_NOZORDER);
1494 WINHELP_LayoutMainWindow(win);
1495 break;
1496 }
1497 }
1498 break;
1499
1500 case WM_INITMENUPOPUP:
1501 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1502 CheckMenuItem((HMENU)wParam, MNID_OPTS_FONTS_SMALL,
1503 MF_BYCOMMAND | (win->font_scale == 0) ? MF_CHECKED : 0);
1504 CheckMenuItem((HMENU)wParam, MNID_OPTS_FONTS_NORMAL,
1505 MF_BYCOMMAND | (win->font_scale == 1) ? MF_CHECKED : 0);
1506 CheckMenuItem((HMENU)wParam, MNID_OPTS_FONTS_LARGE,
1507 MF_BYCOMMAND | (win->font_scale == 2) ? MF_CHECKED : 0);
1508 break;
1509 case WM_DESTROY:
1510 win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1511 WINHELP_DeleteWindow(win);
1512 break;
1513 }
1514 return DefWindowProcA(hWnd, msg, wParam, lParam);
1515 }
1516
1517 /**************************************************************************
1518 * WINHELP_CreateIndexWindow
1519 *
1520 * Displays a dialog with keywords of current help file.
1521 *
1522 */
1523 BOOL WINHELP_CreateIndexWindow(BOOL is_search)
1524 {
1525 HPROPSHEETPAGE psPage[3];
1526 PROPSHEETPAGEA psp;
1527 PROPSHEETHEADERA psHead;
1528 struct index_data id;
1529 char buf[256];
1530
1531 if (Globals.active_win && Globals.active_win->page && Globals.active_win->page->file)
1532 id.hlpfile = Globals.active_win->page->file;
1533 else
1534 return FALSE;
1535
1536 if (id.hlpfile->kwbtree == NULL)
1537 {
1538 WINE_TRACE("No index provided\n");
1539 return FALSE;
1540 }
1541
1542 InitCommonControls();
1543
1544 id.jump = FALSE;
1545 memset(&psp, 0, sizeof(psp));
1546 psp.dwSize = sizeof(psp);
1547 psp.dwFlags = 0;
1548 psp.hInstance = Globals.hInstance;
1549
1550 psp.u.pszTemplate = MAKEINTRESOURCEA(IDD_INDEX);
1551 psp.lParam = (LPARAM)&id;
1552 psp.pfnDlgProc = WINHELP_IndexDlgProc;
1553 psPage[0] = CreatePropertySheetPageA(&psp);
1554
1555 psp.u.pszTemplate = MAKEINTRESOURCEA(IDD_SEARCH);
1556 psp.lParam = (LPARAM)&id;
1557 psp.pfnDlgProc = WINHELP_SearchDlgProc;
1558 psPage[1] = CreatePropertySheetPageA(&psp);
1559
1560 memset(&psHead, 0, sizeof(psHead));
1561 psHead.dwSize = sizeof(psHead);
1562
1563 LoadStringA(Globals.hInstance, STID_PSH_INDEX, buf, sizeof(buf));
1564 strcat(buf, Globals.active_win->info->caption);
1565
1566 psHead.pszCaption = buf;
1567 psHead.nPages = 2;
1568 psHead.u2.nStartPage = is_search ? 1 : 0;
1569 psHead.hwndParent = Globals.active_win->hMainWnd;
1570 psHead.u3.phpage = psPage;
1571 psHead.dwFlags = PSH_NOAPPLYNOW;
1572
1573 PropertySheetA(&psHead);
1574 if (id.jump)
1575 {
1576 WINE_TRACE("got %d as an offset\n", id.offset);
1577 WINHELP_OpenHelpWindow(HLPFILE_PageByOffset, id.hlpfile, id.offset,
1578 Globals.active_win->info, SW_NORMAL);
1579 }
1580 return TRUE;
1581 }
1582
1583 /***********************************************************************
1584 *
1585 * RegisterWinClasses
1586 */
1587 static BOOL WINHELP_RegisterWinClasses(void)
1588 {
1589 WNDCLASSEXA class_main, class_button_box, class_history;
1590
1591 class_main.cbSize = sizeof(class_main);
1592 class_main.style = CS_HREDRAW | CS_VREDRAW;
1593 class_main.lpfnWndProc = WINHELP_MainWndProc;
1594 class_main.cbClsExtra = 0;
1595 class_main.cbWndExtra = sizeof(WINHELP_WINDOW *);
1596 class_main.hInstance = Globals.hInstance;
1597 class_main.hIcon = LoadIconW(Globals.hInstance, MAKEINTRESOURCEW(IDI_WINHELP));
1598 class_main.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1599 class_main.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
1600 class_main.lpszMenuName = 0;
1601 class_main.lpszClassName = MAIN_WIN_CLASS_NAME;
1602 class_main.hIconSm = LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDI_WINHELP), IMAGE_ICON,
1603 GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
1604 LR_SHARED);
1605
1606 class_button_box = class_main;
1607 class_button_box.lpfnWndProc = WINHELP_ButtonBoxWndProc;
1608 class_button_box.cbWndExtra = 0;
1609 class_button_box.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
1610 class_button_box.lpszClassName = BUTTON_BOX_WIN_CLASS_NAME;
1611
1612 class_history = class_main;
1613 class_history.lpfnWndProc = WINHELP_HistoryWndProc;
1614 class_history.lpszClassName = HISTORY_WIN_CLASS_NAME;
1615
1616 return (RegisterClassExA(&class_main) &&
1617 RegisterClassExA(&class_button_box) &&
1618 RegisterClassExA(&class_history));
1619 }
1620
1621 /***********************************************************************
1622 *
1623 * WinMain
1624 */
1625 int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
1626 {
1627 MSG msg;
1628 LONG lHash = 0;
1629 HLPFILE* hlpfile;
1630 static CHAR default_wndname[] = "main";
1631 LPSTR wndname = default_wndname;
1632 WINHELP_DLL* dll;
1633 HACCEL hAccel;
1634
1635 Globals.hInstance = hInstance;
1636
1637 if (LoadLibraryA("riched20.dll") == NULL)
1638 return MessageBoxW(0, MAKEINTRESOURCEW(STID_NO_RICHEDIT),
1639 MAKEINTRESOURCEW(STID_WHERROR), MB_OK);
1640
1641 /* Get options */
1642 while (*cmdline && (*cmdline == ' ' || *cmdline == '-'))
1643 {
1644 CHAR option;
1645 LPCSTR topic_id;
1646 if (*cmdline++ == ' ') continue;
1647
1648 option = *cmdline;
1649 if (option) cmdline++;
1650 while (*cmdline && *cmdline == ' ') cmdline++;
1651 switch (option)
1652 {
1653 case 'i':
1654 case 'I':
1655 topic_id = cmdline;
1656 while (*cmdline && *cmdline != ' ') cmdline++;
1657 if (*cmdline) *cmdline++ = '\0';
1658 lHash = HLPFILE_Hash(topic_id);
1659 break;
1660
1661 case '3':
1662 case '4':
1663 Globals.wVersion = option - '0';
1664 break;
1665
1666 case 'x':
1667 show = SW_HIDE;
1668 Globals.isBook = FALSE;
1669 break;
1670
1671 default:
1672 WINE_FIXME("Unsupported cmd line: %s\n", cmdline);
1673 break;
1674 }
1675 }
1676
1677 /* Create primary window */
1678 if (!WINHELP_RegisterWinClasses())
1679 {
1680 WINE_FIXME("Couldn't register classes\n");
1681 return 0;
1682 }
1683
1684 if (*cmdline)
1685 {
1686 char* ptr;
1687 if ((*cmdline == '"') && (ptr = strchr(cmdline+1, '"')))
1688 {
1689 cmdline++;
1690 *ptr = '\0';
1691 }
1692 if ((ptr = strchr(cmdline, '>')))
1693 {
1694 *ptr = '\0';
1695 wndname = ptr + 1;
1696 }
1697 hlpfile = WINHELP_LookupHelpFile(cmdline);
1698 if (!hlpfile) return 0;
1699 }
1700 else hlpfile = NULL;
1701 WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, lHash,
1702 WINHELP_GetWindowInfo(hlpfile, wndname), show);
1703
1704 /* Message loop */
1705 hAccel = LoadAcceleratorsW(hInstance, MAKEINTRESOURCEW(MAIN_ACCEL));
1706 while ((Globals.win_list || Globals.active_popup) && GetMessageW(&msg, 0, 0, 0))
1707 {
1708 HWND hWnd = Globals.active_win ? Globals.active_win->hMainWnd : NULL;
1709 if (!TranslateAcceleratorW(hWnd, hAccel, &msg))
1710 {
1711 TranslateMessage(&msg);
1712 DispatchMessageW(&msg);
1713 }
1714 }
1715 for (dll = Globals.dlls; dll; dll = dll->next)
1716 {
1717 if (dll->class & DC_INITTERM) dll->handler(DW_TERM, 0, 0);
1718 }
1719 return 0;
1720 }