quartz: Make dwSamplesProcessed a longlong.
[wine] / programs / winhelp / winhelp.c
1 /*
2  * Help Viewer
3  *
4  * Copyright    1996 Ulrich Schmid <uschmid@mail.hh.provi.de>
5  *              2002 Sylvain Petreolle <spetreolle@yahoo.fr>
6  *              2002, 2008 Eric Pouech <eric.pouech@wanadoo.fr>
7  *              2004 Ken Belleau <jamez@ivic.qc.ca>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include <assert.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <stdlib.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "wingdi.h"
33 #include "winuser.h"
34 #include "commdlg.h"
35 #include "winhelp.h"
36 #include "winhelp_res.h"
37 #include "shellapi.h"
38 #include "richedit.h"
39
40 #include "wine/debug.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(winhelp);
43
44 static BOOL    WINHELP_RegisterWinClasses(void);
45 static LRESULT CALLBACK WINHELP_MainWndProc(HWND, UINT, WPARAM, LPARAM);
46 static LRESULT CALLBACK WINHELP_TextWndProc(HWND, UINT, WPARAM, LPARAM);
47 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND, UINT, WPARAM, LPARAM);
48 static LRESULT CALLBACK WINHELP_ButtonWndProc(HWND, UINT, WPARAM, LPARAM);
49 static LRESULT CALLBACK WINHELP_HistoryWndProc(HWND, UINT, WPARAM, LPARAM);
50 static LRESULT CALLBACK WINHELP_ShadowWndProc(HWND, UINT, WPARAM, LPARAM);
51 static BOOL    WINHELP_CheckPopup(HWND, UINT, WPARAM, LPARAM, LRESULT*);
52 static BOOL    WINHELP_SplitLines(HWND hWnd, LPSIZE);
53 static void    WINHELP_InitFonts(HWND hWnd);
54 static void    WINHELP_DeleteLines(WINHELP_WINDOW*);
55 static void    WINHELP_DeleteWindow(WINHELP_WINDOW*);
56 static void    WINHELP_DeleteButtons(WINHELP_WINDOW*);
57 static void    WINHELP_SetupText(HWND hWnd, WINHELP_WINDOW *win, ULONG relative);
58 static void    WINHELP_DeletePageLinks(HLPFILE_PAGE* page);
59 static WINHELP_LINE_PART* WINHELP_IsOverLink(WINHELP_WINDOW*, WPARAM, LPARAM);
60
61 WINHELP_GLOBALS Globals = {3, NULL, TRUE, NULL, NULL, NULL, NULL, NULL, {{{NULL,NULL}},0}};
62
63 static BOOL use_richedit;
64
65 #define CTL_ID_BUTTON   0x700
66 #define CTL_ID_TEXT     0x701
67
68 /***********************************************************************
69  *
70  *           WINHELP_GetOpenFileName
71  */
72 BOOL WINHELP_GetOpenFileName(LPSTR lpszFile, int len)
73 {
74     OPENFILENAME openfilename;
75     CHAR szDir[MAX_PATH];
76     CHAR szzFilter[2 * MAX_STRING_LEN + 100];
77     LPSTR p = szzFilter;
78
79     WINE_TRACE("()\n");
80
81     LoadString(Globals.hInstance, STID_HELP_FILES_HLP, p, MAX_STRING_LEN);
82     p += strlen(p) + 1;
83     lstrcpy(p, "*.hlp");
84     p += strlen(p) + 1;
85     LoadString(Globals.hInstance, STID_ALL_FILES, p, MAX_STRING_LEN);
86     p += strlen(p) + 1;
87     lstrcpy(p, "*.*");
88     p += strlen(p) + 1;
89     *p = '\0';
90
91     GetCurrentDirectory(sizeof(szDir), szDir);
92
93     lpszFile[0]='\0';
94
95     openfilename.lStructSize       = sizeof(OPENFILENAME);
96     openfilename.hwndOwner         = NULL;
97     openfilename.hInstance         = Globals.hInstance;
98     openfilename.lpstrFilter       = szzFilter;
99     openfilename.lpstrCustomFilter = 0;
100     openfilename.nMaxCustFilter    = 0;
101     openfilename.nFilterIndex      = 1;
102     openfilename.lpstrFile         = lpszFile;
103     openfilename.nMaxFile          = len;
104     openfilename.lpstrFileTitle    = 0;
105     openfilename.nMaxFileTitle     = 0;
106     openfilename.lpstrInitialDir   = szDir;
107     openfilename.lpstrTitle        = 0;
108     openfilename.Flags             = 0;
109     openfilename.nFileOffset       = 0;
110     openfilename.nFileExtension    = 0;
111     openfilename.lpstrDefExt       = 0;
112     openfilename.lCustData         = 0;
113     openfilename.lpfnHook          = 0;
114     openfilename.lpTemplateName    = 0;
115
116     return GetOpenFileName(&openfilename);
117 }
118
119 static char* WINHELP_GetCaption(WINHELP_WNDPAGE* wpage)
120 {
121     if (wpage->wininfo->caption[0]) return wpage->wininfo->caption;
122     return wpage->page->file->lpszTitle;
123 }
124
125 /***********************************************************************
126  *
127  *           WINHELP_LookupHelpFile
128  */
129 HLPFILE* WINHELP_LookupHelpFile(LPCSTR lpszFile)
130 {
131     HLPFILE*        hlpfile;
132     char szFullName[MAX_PATH];
133     char szAddPath[MAX_PATH];
134     char *p;
135
136     /*
137      * NOTE: This is needed by popup windows only.
138      * In other cases it's not needed but does not hurt though.
139      */
140     if (Globals.active_win && Globals.active_win->page && Globals.active_win->page->file)
141     {
142         strcpy(szAddPath, Globals.active_win->page->file->lpszPath);
143         p = strrchr(szAddPath, '\\');
144         if (p) *p = 0;
145     }
146
147     /*
148      * FIXME: Should we swap conditions?
149      */
150     if (!SearchPath(NULL, lpszFile, ".hlp", MAX_PATH, szFullName, NULL) &&
151         !SearchPath(szAddPath, lpszFile, ".hlp", MAX_PATH, szFullName, NULL))
152     {
153         if (WINHELP_MessageBoxIDS_s(STID_FILE_NOT_FOUND_s, lpszFile, STID_WHERROR,
154                                     MB_YESNO|MB_ICONQUESTION) != IDYES)
155             return NULL;
156         if (!WINHELP_GetOpenFileName(szFullName, MAX_PATH))
157             return NULL;
158     }
159     hlpfile = HLPFILE_ReadHlpFile(szFullName);
160     if (!hlpfile)
161         WINHELP_MessageBoxIDS_s(STID_HLPFILE_ERROR_s, lpszFile,
162                                 STID_WHERROR, MB_OK|MB_ICONSTOP);
163     return hlpfile;
164 }
165
166 /******************************************************************
167  *              WINHELP_GetWindowInfo
168  *
169  *
170  */
171 HLPFILE_WINDOWINFO*     WINHELP_GetWindowInfo(HLPFILE* hlpfile, LPCSTR name)
172 {
173     static      HLPFILE_WINDOWINFO      mwi;
174     unsigned int     i;
175
176     if (!name || !name[0])
177         name = Globals.active_win->lpszName;
178
179     if (hlpfile)
180         for (i = 0; i < hlpfile->numWindows; i++)
181             if (!strcmp(hlpfile->windows[i].name, name))
182                 return &hlpfile->windows[i];
183
184     if (strcmp(name, "main") != 0)
185     {
186         WINE_FIXME("Couldn't find window info for %s\n", name);
187         assert(0);
188         return NULL;
189     }
190     if (!mwi.name[0])
191     {
192         strcpy(mwi.type, "primary");
193         strcpy(mwi.name, "main");
194         if (!LoadString(Globals.hInstance, STID_WINE_HELP, 
195                         mwi.caption, sizeof(mwi.caption)))
196             strcpy(mwi.caption, hlpfile->lpszTitle);
197         mwi.origin.x = mwi.origin.y = mwi.size.cx = mwi.size.cy = CW_USEDEFAULT;
198         mwi.style = SW_SHOW;
199         mwi.win_style = WS_OVERLAPPEDWINDOW;
200         mwi.sr_color = mwi.sr_color = 0xFFFFFF;
201     }
202     return &mwi;
203 }
204
205 /******************************************************************
206  *              HLPFILE_GetPopupWindowInfo
207  *
208  *
209  */
210 static HLPFILE_WINDOWINFO*     WINHELP_GetPopupWindowInfo(HLPFILE* hlpfile,
211                                                           WINHELP_WINDOW* parent, LPARAM mouse)
212 {
213     static      HLPFILE_WINDOWINFO      wi;
214
215     RECT parent_rect;
216     
217     wi.type[0] = wi.name[0] = wi.caption[0] = '\0';
218
219     /* Calculate horizontal size and position of a popup window */
220     GetWindowRect(parent->hMainWnd, &parent_rect);
221     wi.size.cx = (parent_rect.right  - parent_rect.left) / 2;
222     wi.size.cy = 10; /* need a non null value, so that border are taken into account while computing */
223
224     wi.origin.x = (short)LOWORD(mouse);
225     wi.origin.y = (short)HIWORD(mouse);
226     ClientToScreen(parent->hMainWnd, &wi.origin);
227     wi.origin.x -= wi.size.cx / 2;
228     wi.origin.x  = min(wi.origin.x, GetSystemMetrics(SM_CXSCREEN) - wi.size.cx);
229     wi.origin.x  = max(wi.origin.x, 0);
230
231     wi.style = SW_SHOW;
232     wi.win_style = WS_POPUP | WS_BORDER;
233     wi.sr_color = parent->info->sr_color;
234     wi.nsr_color = 0xFFFFFF;
235
236     return &wi;
237 }
238
239 /***********************************************************************
240  *
241  *           WinMain
242  */
243 int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
244 {
245     MSG                 msg;
246     LONG                lHash = 0;
247     HLPFILE*            hlpfile;
248     static CHAR         default_wndname[] = "main";
249     LPSTR               wndname = default_wndname;
250     WINHELP_DLL*        dll;
251
252     Globals.hInstance = hInstance;
253
254     use_richedit = getenv("WINHELP_RICHEDIT") != NULL;
255     if (use_richedit && LoadLibrary("riched20.dll") == NULL)
256         return MessageBox(0, MAKEINTRESOURCE(STID_NO_RICHEDIT),
257                           MAKEINTRESOURCE(STID_WHERROR), MB_OK);
258
259     /* Get options */
260     while (*cmdline && (*cmdline == ' ' || *cmdline == '-'))
261     {
262         CHAR   option;
263         LPCSTR topic_id;
264         if (*cmdline++ == ' ') continue;
265
266         option = *cmdline;
267         if (option) cmdline++;
268         while (*cmdline && *cmdline == ' ') cmdline++;
269         switch (option)
270         {
271         case 'i':
272         case 'I':
273             topic_id = cmdline;
274             while (*cmdline && *cmdline != ' ') cmdline++;
275             if (*cmdline) *cmdline++ = '\0';
276             lHash = HLPFILE_Hash(topic_id);
277             break;
278
279         case '3':
280         case '4':
281             Globals.wVersion = option - '0';
282             break;
283
284         case 'x':
285             show = SW_HIDE; 
286             Globals.isBook = FALSE;
287             break;
288
289         default:
290             WINE_FIXME("Unsupported cmd line: %s\n", cmdline);
291             break;
292         }
293     }
294
295     /* Create primary window */
296     if (!WINHELP_RegisterWinClasses())
297     {
298         WINE_FIXME("Couldn't register classes\n");
299         return 0;
300     }
301
302     if (*cmdline)
303     {
304         char*   ptr;
305         if ((*cmdline == '"') && (ptr = strchr(cmdline+1, '"')))
306         {
307             cmdline++;
308             *ptr = '\0';
309         }
310         if ((ptr = strchr(cmdline, '>')))
311         {
312             *ptr = '\0';
313             wndname = ptr + 1;
314         }
315         hlpfile = WINHELP_LookupHelpFile(cmdline);
316         if (!hlpfile) return 0;
317     }
318     else hlpfile = NULL;
319     WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, lHash,
320                            WINHELP_GetWindowInfo(hlpfile, wndname), show);
321
322     /* Message loop */
323     while (GetMessage(&msg, 0, 0, 0))
324     {
325         TranslateMessage(&msg);
326         DispatchMessage(&msg);
327     }
328     for (dll = Globals.dlls; dll; dll = dll->next)
329     {
330         if (dll->class & DC_INITTERM) dll->handler(DW_TERM, 0, 0);
331     }
332     return 0;
333 }
334
335 /***********************************************************************
336  *
337  *           RegisterWinClasses
338  */
339 static BOOL WINHELP_RegisterWinClasses(void)
340 {
341     WNDCLASS class_main, class_button_box, class_text, class_shadow, class_history;
342
343     class_main.style               = CS_HREDRAW | CS_VREDRAW;
344     class_main.lpfnWndProc         = WINHELP_MainWndProc;
345     class_main.cbClsExtra          = 0;
346     class_main.cbWndExtra          = sizeof(LONG);
347     class_main.hInstance           = Globals.hInstance;
348     class_main.hIcon               = LoadIcon(Globals.hInstance, MAKEINTRESOURCE(IDI_WINHELP));
349     class_main.hCursor             = LoadCursor(0, IDC_ARROW);
350     class_main.hbrBackground       = (HBRUSH)(COLOR_WINDOW+1);
351     class_main.lpszMenuName        = 0;
352     class_main.lpszClassName       = MAIN_WIN_CLASS_NAME;
353
354     class_button_box               = class_main;
355     class_button_box.lpfnWndProc   = WINHELP_ButtonBoxWndProc;
356     class_button_box.cbWndExtra    = 0;
357     class_button_box.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
358     class_button_box.lpszClassName = BUTTON_BOX_WIN_CLASS_NAME;
359
360     class_text                     = class_main;
361     class_text.lpfnWndProc         = WINHELP_TextWndProc;
362     class_text.hbrBackground       = 0;
363     class_text.lpszClassName       = TEXT_WIN_CLASS_NAME;
364
365     class_shadow                   = class_main;
366     class_shadow.lpfnWndProc       = WINHELP_ShadowWndProc;
367     class_shadow.cbWndExtra        = 0;
368     class_shadow.hbrBackground     = (HBRUSH)(COLOR_3DDKSHADOW+1);
369     class_shadow.lpszClassName     = SHADOW_WIN_CLASS_NAME;
370
371     class_history                  = class_main;
372     class_history.lpfnWndProc      = WINHELP_HistoryWndProc;
373     class_history.lpszClassName    = HISTORY_WIN_CLASS_NAME;
374
375     return (RegisterClass(&class_main) &&
376             RegisterClass(&class_button_box) &&
377             RegisterClass(&class_text) &&
378             RegisterClass(&class_shadow) &&
379             RegisterClass(&class_history));
380 }
381
382 typedef struct
383 {
384     WORD size;
385     WORD command;
386     LONG data;
387     LONG reserved;
388     WORD ofsFilename;
389     WORD ofsData;
390 } WINHELP,*LPWINHELP;
391
392 /******************************************************************
393  *              WINHELP_HandleCommand
394  *
395  *
396  */
397 static LRESULT  WINHELP_HandleCommand(HWND hSrcWnd, LPARAM lParam)
398 {
399     COPYDATASTRUCT*     cds = (COPYDATASTRUCT*)lParam;
400     WINHELP*            wh;
401
402     if (cds->dwData != 0xA1DE505)
403     {
404         WINE_FIXME("Wrong magic number (%08lx)\n", cds->dwData);
405         return 0;
406     }
407
408     wh = (WINHELP*)cds->lpData;
409
410     if (wh)
411     {
412         char*   ptr = (wh->ofsFilename) ? (LPSTR)wh + wh->ofsFilename : NULL;
413
414         WINE_TRACE("Got[%u]: cmd=%u data=%08x fn=%s\n",
415                    wh->size, wh->command, wh->data, ptr);
416         switch (wh->command)
417         {
418         case HELP_CONTEXT:
419             if (ptr)
420             {
421                 MACRO_JumpContext(ptr, "main", wh->data);
422             }
423             break;
424         case HELP_QUIT:
425             MACRO_Exit();
426             break;
427         case HELP_CONTENTS:
428             if (ptr)
429             {
430                 MACRO_JumpContents(ptr, "main");
431             }
432             break;
433         case HELP_HELPONHELP:
434             MACRO_HelpOn();
435             break;
436         /* case HELP_SETINDEX: */
437         case HELP_SETCONTENTS:
438             if (ptr)
439             {
440                 MACRO_SetContents(ptr, wh->data);
441             }
442             break;
443         case HELP_CONTEXTPOPUP:
444             if (ptr)
445             {
446                 MACRO_PopupContext(ptr, wh->data);
447             }
448             break;
449         /* case HELP_FORCEFILE:*/
450         /* case HELP_CONTEXTMENU: */
451         case HELP_FINDER:
452             /* in fact, should be the topic dialog box */
453             WINE_FIXME("HELP_FINDER: stub\n");
454             if (ptr)
455             {
456                 MACRO_JumpHash(ptr, "main", 0);
457             }
458             break;
459         /* case HELP_WM_HELP: */
460         /* case HELP_SETPOPUP_POS: */
461         /* case HELP_KEY: */
462         /* case HELP_COMMAND: */
463         /* case HELP_PARTIALKEY: */
464         /* case HELP_MULTIKEY: */
465         /* case HELP_SETWINPOS: */
466         default:
467             WINE_FIXME("Unhandled command (%x) for remote winhelp control\n", wh->command);
468             break;
469         }
470     }
471     /* Always return success for now */
472     return 1;
473 }
474
475 void            WINHELP_LayoutMainWindow(WINHELP_WINDOW* win)
476 {
477     RECT        rect, button_box_rect;
478     INT         text_top = 0;
479     HWND        hButtonBoxWnd = GetDlgItem(win->hMainWnd, CTL_ID_BUTTON);
480     HWND        hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
481
482     GetClientRect(win->hMainWnd, &rect);
483
484     /* Update button box and text Window */
485     SetWindowPos(hButtonBoxWnd, HWND_TOP,
486                  rect.left, rect.top,
487                  rect.right - rect.left,
488                  rect.bottom - rect.top, 0);
489
490     if (GetWindowRect(hButtonBoxWnd, &button_box_rect))
491         text_top = rect.top + button_box_rect.bottom - button_box_rect.top;
492
493     SetWindowPos(hTextWnd, HWND_TOP,
494                  rect.left, text_top,
495                  rect.right - rect.left,
496                  rect.bottom - text_top, 0);
497
498 }
499
500 static void     WINHELP_RememberPage(WINHELP_WINDOW* win, WINHELP_WNDPAGE* wpage)
501 {
502     unsigned        num;
503
504     if (!Globals.history.index || Globals.history.set[0].page != wpage->page)
505     {
506         num = sizeof(Globals.history.set) / sizeof(Globals.history.set[0]);
507         /* we're full, remove latest entry */
508         if (Globals.history.index == num)
509         {
510             HLPFILE_FreeHlpFile(Globals.history.set[num - 1].page->file);
511             Globals.history.index--;
512         }
513         memmove(&Globals.history.set[1], &Globals.history.set[0],
514                 Globals.history.index * sizeof(Globals.history.set[0]));
515         Globals.history.set[0] = *wpage;
516         Globals.history.index++;
517         wpage->page->file->wRefCount++;
518     }
519     if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE);
520
521     num = sizeof(win->back.set) / sizeof(win->back.set[0]);
522     if (win->back.index == num)
523     {
524         /* we're full, remove latest entry */
525         HLPFILE_FreeHlpFile(win->back.set[0].page->file);
526         memmove(&win->back.set[0], &win->back.set[1],
527                 (num - 1) * sizeof(win->back.set[0]));
528         win->back.index--;
529     }
530     win->back.set[win->back.index++] = *wpage;
531     wpage->page->file->wRefCount++;
532 }
533
534 /***********************************************************************
535  *
536  *           WINHELP_CreateHelpWindow
537  */
538 BOOL WINHELP_CreateHelpWindow(WINHELP_WNDPAGE* wpage, int nCmdShow, BOOL remember)
539 {
540     WINHELP_WINDOW*     win = NULL;
541     BOOL                bPrimary, bPopup, bReUsed = FALSE;
542     LPSTR               name;
543     HICON               hIcon;
544     HWND                hTextWnd = NULL;
545
546     bPrimary = !lstrcmpi(wpage->wininfo->name, "main");
547     bPopup = !bPrimary && (wpage->wininfo->win_style & WS_POPUP);
548
549     if (wpage->page && !wpage->page->first_paragraph) HLPFILE_BrowsePage(wpage->page, NULL);
550
551     if (!bPopup)
552     {
553         for (win = Globals.win_list; win; win = win->next)
554         {
555             if (!lstrcmpi(win->lpszName, wpage->wininfo->name))
556             {
557                 POINT   pt = {0, 0};
558                 SIZE    sz = {0, 0};
559                 DWORD   flags = SWP_NOSIZE | SWP_NOMOVE;
560
561                 WINHELP_DeleteButtons(win);
562                 bReUsed = TRUE;
563                 SetWindowText(win->hMainWnd, WINHELP_GetCaption(wpage));
564                 if (wpage->wininfo->origin.x != CW_USEDEFAULT &&
565                     wpage->wininfo->origin.y != CW_USEDEFAULT)
566                 {
567                     pt = wpage->wininfo->origin;
568                     flags &= ~SWP_NOSIZE;
569                 }
570                 if (wpage->wininfo->size.cx != CW_USEDEFAULT &&
571                     wpage->wininfo->size.cy != CW_USEDEFAULT)
572                 {
573                     sz = wpage->wininfo->size;
574                     flags &= ~SWP_NOMOVE;
575                 }
576                 SetWindowPos(win->hMainWnd, HWND_TOP, pt.x, pt.y, sz.cx, sz.cy, flags);
577
578                 if (wpage->page && wpage->page->file != win->page->file)
579                     WINHELP_DeleteBackSet(win);
580                 WINHELP_InitFonts(win->hMainWnd);
581
582                 win->page = wpage->page;
583                 win->info = wpage->wininfo;
584                 hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
585                 WINHELP_SetupText(hTextWnd, win, wpage->relative);
586
587                 InvalidateRect(win->hMainWnd, NULL, TRUE);
588                 if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE);
589                 break;
590             }
591         }
592     }
593
594     if (!win)
595     {
596         /* Initialize WINHELP_WINDOW struct */
597         win = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
598                         sizeof(WINHELP_WINDOW) + strlen(wpage->wininfo->name) + 1);
599         if (!win) return FALSE;
600         win->next = Globals.win_list;
601         Globals.win_list = win;
602
603         name = (char*)win + sizeof(WINHELP_WINDOW);
604         lstrcpy(name, wpage->wininfo->name);
605         win->lpszName = name;
606         win->hHandCur = LoadCursorW(0, (LPWSTR)IDC_HAND);
607         win->back.index = 0;
608     }
609     win->page = wpage->page;
610     win->info = wpage->wininfo;
611
612     win->hArrowCur = LoadCursorA(0, (LPSTR)IDC_ARROW);
613     win->hHandCur = LoadCursorA(0, (LPSTR)IDC_HAND);
614
615     if (!bPopup && wpage->page && remember)
616     {
617         WINHELP_RememberPage(win, wpage);
618     }
619
620     if (bPopup)
621         Globals.active_popup = win;
622     else
623         Globals.active_win = win;
624
625     /* Initialize default pushbuttons */
626     if (bPrimary && wpage->page)
627     {
628         CHAR    buffer[MAX_STRING_LEN];
629
630         LoadString(Globals.hInstance, STID_CONTENTS, buffer, sizeof(buffer));
631         MACRO_CreateButton("BTN_CONTENTS", buffer, "Contents()");
632         LoadString(Globals.hInstance, STID_SEARCH,buffer, sizeof(buffer));
633         MACRO_CreateButton("BTN_SEARCH", buffer, "Search()");
634         LoadString(Globals.hInstance, STID_BACK, buffer, sizeof(buffer));
635         MACRO_CreateButton("BTN_BACK", buffer, "Back()");
636         if (win->back.index <= 1) MACRO_DisableButton("BTN_BACK");
637         LoadString(Globals.hInstance, STID_HISTORY, buffer, sizeof(buffer));
638         MACRO_CreateButton("BTN_HISTORY", buffer, "History()");
639         LoadString(Globals.hInstance, STID_TOPICS, buffer, sizeof(buffer));
640         MACRO_CreateButton("BTN_TOPICS", buffer, "Finder()");
641     }
642
643     if (!bReUsed)
644     {
645         win->hMainWnd = CreateWindowEx((bPopup) ? WS_EX_TOOLWINDOW : 0, MAIN_WIN_CLASS_NAME,
646                                        WINHELP_GetCaption(wpage),
647                                        bPrimary ? WS_OVERLAPPEDWINDOW : wpage->wininfo->win_style,
648                                        wpage->wininfo->origin.x, wpage->wininfo->origin.y,
649                                        wpage->wininfo->size.cx, wpage->wininfo->size.cy,
650                                        bPopup ? Globals.active_win->hMainWnd : NULL,
651                                        bPrimary ? LoadMenu(Globals.hInstance, MAKEINTRESOURCE(MAIN_MENU)) : 0,
652                                        Globals.hInstance, win);
653         if (!bPopup)
654             /* Create button box and text Window */
655             CreateWindow(BUTTON_BOX_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
656                          0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_BUTTON, Globals.hInstance, NULL);
657
658         if (!use_richedit)
659             hTextWnd = CreateWindow(TEXT_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
660                                     0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_TEXT, Globals.hInstance, win);
661         else
662         {
663             hTextWnd = CreateWindow(RICHEDIT_CLASS, NULL,
664                                     ES_MULTILINE | ES_READONLY | WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_VISIBLE,
665                                     0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_TEXT, Globals.hInstance, NULL);
666             SendMessage(hTextWnd, EM_SETEVENTMASK, 0,
667                         SendMessage(hTextWnd, EM_GETEVENTMASK, 0, 0) | ENM_MOUSEEVENTS);
668         }
669     }
670
671     hIcon = (wpage->page) ? wpage->page->file->hIcon : NULL;
672     if (!hIcon) hIcon = LoadIcon(Globals.hInstance, MAKEINTRESOURCE(IDI_WINHELP));
673     SendMessage(win->hMainWnd, WM_SETICON, ICON_SMALL, (DWORD_PTR)hIcon);
674
675     /* Initialize file specific pushbuttons */
676     if (!(wpage->wininfo->win_style & WS_POPUP) && wpage->page)
677     {
678         HLPFILE_MACRO  *macro;
679         for (macro = wpage->page->file->first_macro; macro; macro = macro->next)
680             MACRO_ExecuteMacro(macro->lpszMacro);
681
682         for (macro = wpage->page->first_macro; macro; macro = macro->next)
683             MACRO_ExecuteMacro(macro->lpszMacro);
684     }
685
686     if (bPopup && use_richedit)
687     {
688         DWORD   mask = SendMessage(hTextWnd, EM_GETEVENTMASK, 0, 0);
689         RECT    rect;
690
691         WINHELP_SetupText(hTextWnd, win, wpage->relative);
692
693         /* we need the window to be shown for richedit to compute the size */
694         ShowWindow(win->hMainWnd, nCmdShow);
695         SendMessage(hTextWnd, EM_SETEVENTMASK, 0, mask | ENM_REQUESTRESIZE);
696         SendMessage(hTextWnd, EM_REQUESTRESIZE, 0, 0);
697         SendMessage(hTextWnd, EM_SETEVENTMASK, 0, mask);
698
699         GetWindowRect(win->hMainWnd, &rect);
700         win->hShadowWnd = CreateWindowEx(WS_EX_TOOLWINDOW, SHADOW_WIN_CLASS_NAME,
701                                          "", WS_POPUP | WS_VISIBLE,
702                                          rect.left + SHADOW_DX, rect.top + SHADOW_DY,
703                                          rect.right - rect.left,
704                                          rect.bottom - rect.top,
705                                          Globals.active_win->hMainWnd, 0,
706                                          Globals.hInstance, NULL);
707         SetWindowPos(win->hMainWnd, win->hShadowWnd, 0, 0, 0, 0,
708                      SWP_NOSIZE | SWP_NOMOVE);
709     }
710     else
711     {
712         WINHELP_SetupText(hTextWnd, win, wpage->relative);
713         WINHELP_LayoutMainWindow(win);
714         ShowWindow(win->hMainWnd, nCmdShow);
715     }
716
717     return TRUE;
718 }
719
720 /******************************************************************
721  *             WINHELP_OpenHelpWindow
722  * Main function to search for a page and display it in a window
723  */
724 BOOL WINHELP_OpenHelpWindow(HLPFILE_PAGE* (*lookup)(HLPFILE*, LONG, ULONG*),
725                             HLPFILE* hlpfile, LONG val, HLPFILE_WINDOWINFO* wi,
726                             int nCmdShow)
727 {
728     WINHELP_WNDPAGE     wpage;
729
730     wpage.page = lookup(hlpfile, val, &wpage.relative);
731     if (wpage.page) wpage.page->file->wRefCount++;
732     wpage.wininfo = wi;
733     return WINHELP_CreateHelpWindow(&wpage, nCmdShow, TRUE);
734 }
735
736 /***********************************************************************
737  *
738  *           WINHELP_FindLink
739  */
740 static HLPFILE_LINK* WINHELP_FindLink(WINHELP_WINDOW* win, LPARAM pos)
741 {
742     HLPFILE_LINK*           link;
743     POINTL                  mouse_ptl, char_ptl, char_next_ptl;
744     DWORD                   cp;
745
746     if (!win->page) return NULL;
747
748     mouse_ptl.x = (short)LOWORD(pos);
749     mouse_ptl.y = (short)HIWORD(pos);
750     cp = SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_CHARFROMPOS,
751                       0, (LPARAM)&mouse_ptl);
752
753     for (link = win->page->first_link; link; link = link->next)
754     {
755         if (link->cpMin <= cp && cp <= link->cpMax)
756         {
757             /* check whether we're at end of line */
758             SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_POSFROMCHAR,
759                          (LPARAM)&char_ptl, cp);
760             SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_POSFROMCHAR,
761                          (LPARAM)&char_next_ptl, cp + 1);
762             if (char_next_ptl.y != char_ptl.y || mouse_ptl.x >= char_next_ptl.x)
763                 link = NULL;
764             break;
765         }
766     }
767     return link;
768 }
769
770 /******************************************************************
771  *             WINHELP_HandleTextMouse
772  *
773  */
774 static BOOL WINHELP_HandleTextMouse(WINHELP_WINDOW* win, UINT msg, LPARAM lParam)
775 {
776     HLPFILE*                hlpfile;
777     HLPFILE_LINK*           link;
778     BOOL                    ret = FALSE;
779
780     switch (msg)
781     {
782     case WM_MOUSEMOVE:
783         if (WINHELP_FindLink(win, lParam))
784             SetCursor(win->hHandCur);
785         else
786             SetCursor(LoadCursor(0, IDC_ARROW));
787         break;
788
789      case WM_LBUTTONDOWN:
790          if ((win->current_link = WINHELP_FindLink(win, lParam)))
791              ret = TRUE;
792          break;
793
794     case WM_LBUTTONUP:
795         if ((link = WINHELP_FindLink(win, lParam)) && link == win->current_link)
796         {
797             HLPFILE_WINDOWINFO*     wi;
798
799             switch (link->cookie)
800             {
801             case hlp_link_link:
802                 if ((hlpfile = WINHELP_LookupHelpFile(link->string)))
803                 {
804                     if (link->window == -1)
805                         wi = win->info;
806                     else if ((link->window >= 0) && (link->window < hlpfile->numWindows))
807                         wi = &hlpfile->windows[link->window];
808                     else
809                     {
810                         WINE_WARN("link to window %d/%d\n", link->window, hlpfile->numWindows);
811                         break;
812                     }
813                     WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, link->hash, wi, SW_NORMAL);
814                 }
815                 break;
816             case hlp_link_popup:
817                 if ((hlpfile = WINHELP_LookupHelpFile(link->string)))
818                     WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, link->hash,
819                                            WINHELP_GetPopupWindowInfo(hlpfile, win, lParam),
820                                            SW_NORMAL);
821                 break;
822             case hlp_link_macro:
823                 MACRO_ExecuteMacro(link->string);
824                 break;
825             default:
826                 WINE_FIXME("Unknown link cookie %d\n", link->cookie);
827             }
828             ret = TRUE;
829         }
830         win->current_link = NULL;
831         break;
832     }
833     return ret;
834 }
835
836 /***********************************************************************
837  *
838  *           WINHELP_MainWndProc
839  */
840 static LRESULT CALLBACK WINHELP_MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
841 {
842     WINHELP_WINDOW *win;
843     WINHELP_BUTTON *button;
844     RECT rect;
845     INT  curPos, min, max, dy, keyDelta;
846     HWND hTextWnd;
847     LRESULT ret;
848
849     if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, &ret)) return ret;
850
851     switch (msg)
852     {
853     case WM_NCCREATE:
854         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
855         SetWindowLongPtr(hWnd, 0, (ULONG_PTR) win);
856         if (!win->page && Globals.isBook)
857             PostMessage(hWnd, WM_COMMAND, MNID_FILE_OPEN, 0);
858         win->hMainWnd = hWnd;
859         break;
860
861     case WM_WINDOWPOSCHANGED:
862         WINHELP_LayoutMainWindow((WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0));
863         break;
864
865     case WM_COMMAND:
866         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
867         switch (wParam)
868         {
869             /* Menu FILE */
870         case MNID_FILE_OPEN:    MACRO_FileOpen();       break;
871         case MNID_FILE_PRINT:   MACRO_Print();          break;
872         case MNID_FILE_SETUP:   MACRO_PrinterSetup();   break;
873         case MNID_FILE_EXIT:    MACRO_Exit();           break;
874
875             /* Menu EDIT */
876         case MNID_EDIT_COPYDLG: MACRO_CopyDialog();     break;
877         case MNID_EDIT_ANNOTATE:MACRO_Annotate();       break;
878
879             /* Menu Bookmark */
880         case MNID_BKMK_DEFINE:  MACRO_BookmarkDefine(); break;
881
882             /* Menu Help */
883         case MNID_HELP_HELPON:  MACRO_HelpOn();         break;
884         case MNID_HELP_HELPTOP: MACRO_HelpOnTop();      break;
885         case MNID_HELP_ABOUT:   MACRO_About();          break;
886         case MNID_HELP_WINE:    ShellAbout(hWnd, "WINE", "Help", 0); break;
887
888         default:
889             /* Buttons */
890             for (button = win->first_button; button; button = button->next)
891                 if (wParam == button->wParam) break;
892             if (button)
893                 MACRO_ExecuteMacro(button->lpszMacro);
894             else if (!HIWORD(wParam))
895                 MessageBox(0, MAKEINTRESOURCE(STID_NOT_IMPLEMENTED),
896                            MAKEINTRESOURCE(STID_WHERROR), MB_OK);
897             break;
898         }
899         break;
900 /* EPP     case WM_DESTROY: */
901 /* EPP         if (Globals.hPopupWnd) DestroyWindow(Globals.hPopupWnd); */
902 /* EPP         break; */
903     case WM_COPYDATA:
904         return WINHELP_HandleCommand((HWND)wParam, lParam);
905
906     case WM_KEYDOWN:
907         keyDelta = 0;
908
909         switch (wParam)
910         {
911         case VK_UP:
912         case VK_DOWN:
913             keyDelta = GetSystemMetrics(SM_CXVSCROLL);
914             if (wParam == VK_UP)
915                 keyDelta = -keyDelta;
916
917         case VK_PRIOR:
918         case VK_NEXT:
919             win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
920             hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
921             curPos = GetScrollPos(hTextWnd, SB_VERT);
922             GetScrollRange(hTextWnd, SB_VERT, &min, &max);
923
924             if (keyDelta == 0)
925             {            
926                 GetClientRect(hTextWnd, &rect);
927                 keyDelta = (rect.bottom - rect.top) / 2;
928                 if (wParam == VK_PRIOR)
929                     keyDelta = -keyDelta;
930             }
931
932             curPos += keyDelta;
933             if (curPos > max)
934                  curPos = max;
935             else if (curPos < min)
936                  curPos = min;
937
938             dy = GetScrollPos(hTextWnd, SB_VERT) - curPos;
939             SetScrollPos(hTextWnd, SB_VERT, curPos, TRUE);
940             ScrollWindow(hTextWnd, 0, dy, NULL, NULL);
941             UpdateWindow(hTextWnd);
942             return 0;
943
944         case VK_ESCAPE:
945             MACRO_Exit();
946             return 0;
947         }
948         break;
949
950     case WM_NOTIFY:
951         if (wParam == CTL_ID_TEXT)
952         {
953             RECT        rc;
954
955             switch (((NMHDR*)lParam)->code)
956             {
957             case EN_MSGFILTER:
958                 {
959                     const MSGFILTER*    msgf = (const MSGFILTER*)lParam;
960                     return WINHELP_HandleTextMouse((WINHELP_WINDOW*)GetWindowLong(hWnd, 0),
961                                                    msgf->msg, msgf->lParam);
962                 }
963                 break;
964
965             case EN_REQUESTRESIZE:
966                 rc = ((REQRESIZE*)lParam)->rc;
967                 win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
968                 AdjustWindowRect(&rc, GetWindowLong(win->hMainWnd, GWL_STYLE),
969                                  FALSE);
970                 SetWindowPos(win->hMainWnd, HWND_TOP, 0, 0,
971                              rc.right - rc.left, rc.bottom - rc.top,
972                              SWP_NOMOVE | SWP_NOZORDER);
973                 WINHELP_LayoutMainWindow(win);
974                 break;
975             }
976         }
977         break;
978
979     case WM_NCDESTROY:
980         {
981             BOOL bExit;
982             win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
983             bExit = (Globals.wVersion >= 4 && !lstrcmpi(win->lpszName, "main"));
984             WINHELP_DeleteWindow(win);
985
986             if (bExit) MACRO_Exit();
987             if (!Globals.win_list)
988                 PostQuitMessage(0);
989         }
990         break;
991     }
992     return DefWindowProc(hWnd, msg, wParam, lParam);
993 }
994
995 static DWORD CALLBACK WINHELP_RtfStreamIn(DWORD_PTR cookie, BYTE* buff,
996                                           LONG cb, LONG* pcb)
997 {
998     struct RtfData*     rd = (struct RtfData*)cookie;
999
1000     if (rd->where >= rd->ptr) return 1;
1001     if (rd->where + cb > rd->ptr)
1002         cb = rd->ptr - rd->where;
1003     memcpy(buff, rd->where, cb);
1004     rd->where += cb;
1005     *pcb = cb;
1006     return 0;
1007 }
1008
1009 static void WINHELP_FillRichEdit(HWND hTextWnd, WINHELP_WINDOW *win, ULONG relative)
1010 {
1011     SendMessage(hTextWnd, WM_SETREDRAW, FALSE, 0);
1012     SendMessage(hTextWnd, EM_SETBKGNDCOLOR, 0, (LPARAM)win->info->sr_color);
1013     /* set word-wrap to window size (undocumented) */
1014     SendMessage(hTextWnd, EM_SETTARGETDEVICE, 0, 0);
1015     if (win->page)
1016     {
1017         struct RtfData  rd;
1018         EDITSTREAM      es;
1019
1020         if (HLPFILE_BrowsePage(win->page, &rd))
1021         {
1022             rd.where = rd.data;
1023             es.dwCookie = (DWORD_PTR)&rd;
1024             es.dwError = 0;
1025             es.pfnCallback = WINHELP_RtfStreamIn;
1026
1027             SendMessageW(hTextWnd, EM_STREAMIN, SF_RTF, (LPARAM)&es);
1028         }
1029         /* FIXME: else leaking potentially the rd.first_link chain */
1030         HeapFree(GetProcessHeap(), 0, rd.data);
1031     }
1032     else
1033     {
1034         SendMessage(hTextWnd, WM_SETTEXT, 0, (LPARAM)"");
1035     }
1036     SendMessage(hTextWnd, WM_SETREDRAW, TRUE, 0);
1037     SendMessage(hTextWnd, EM_SETSEL, 0, 0);
1038     SendMessage(hTextWnd, EM_SCROLLCARET, 0, 0);
1039     InvalidateRect(hTextWnd, NULL, TRUE);
1040 }
1041
1042 /***********************************************************************
1043  *
1044  *           WINHELP_ButtonBoxWndProc
1045  */
1046 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1047 {
1048     WINDOWPOS      *winpos;
1049     WINHELP_WINDOW *win;
1050     WINHELP_BUTTON *button;
1051     SIZE button_size;
1052     INT  x, y;
1053
1054     if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0L;
1055
1056     switch (msg)
1057     {
1058     case WM_WINDOWPOSCHANGING:
1059         winpos = (WINDOWPOS*) lParam;
1060         win = (WINHELP_WINDOW*) GetWindowLongPtr(GetParent(hWnd), 0);
1061
1062         /* Update buttons */
1063         button_size.cx = 0;
1064         button_size.cy = 0;
1065         for (button = win->first_button; button; button = button->next)
1066         {
1067             HDC  hDc;
1068             SIZE textsize;
1069             if (!button->hWnd)
1070             {
1071                 button->hWnd = CreateWindow(STRING_BUTTON, button->lpszName,
1072                                             WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
1073                                             0, 0, 0, 0,
1074                                             hWnd, (HMENU) button->wParam,
1075                                             Globals.hInstance, 0);
1076                 if (button->hWnd) {
1077                     if (Globals.button_proc == NULL)
1078                         Globals.button_proc = (WNDPROC) GetWindowLongPtr(button->hWnd, GWLP_WNDPROC);
1079                     SetWindowLongPtr(button->hWnd, GWLP_WNDPROC, (LONG_PTR) WINHELP_ButtonWndProc);
1080                 }
1081             }
1082             hDc = GetDC(button->hWnd);
1083             GetTextExtentPoint(hDc, button->lpszName,
1084                                lstrlen(button->lpszName), &textsize);
1085             ReleaseDC(button->hWnd, hDc);
1086
1087             button_size.cx = max(button_size.cx, textsize.cx + BUTTON_CX);
1088             button_size.cy = max(button_size.cy, textsize.cy + BUTTON_CY);
1089         }
1090
1091         x = 0;
1092         y = 0;
1093         for (button = win->first_button; button; button = button->next)
1094         {
1095             SetWindowPos(button->hWnd, HWND_TOP, x, y, button_size.cx, button_size.cy, 0);
1096
1097             if (x + 2 * button_size.cx <= winpos->cx)
1098                 x += button_size.cx;
1099             else
1100                 x = 0, y += button_size.cy;
1101         }
1102         winpos->cy = y + (x ? button_size.cy : 0);
1103         break;
1104
1105     case WM_COMMAND:
1106         SendMessage(GetParent(hWnd), msg, wParam, lParam);
1107         break;
1108
1109     case WM_KEYDOWN:
1110         switch (wParam)
1111         {
1112         case VK_UP:
1113         case VK_DOWN:
1114         case VK_PRIOR:
1115         case VK_NEXT:
1116         case VK_ESCAPE:
1117             return SendMessage(GetParent(hWnd), msg, wParam, lParam);
1118         }
1119         break;
1120     }
1121
1122     return DefWindowProc(hWnd, msg, wParam, lParam);
1123 }
1124
1125 /***********************************************************************
1126  *
1127  *           WINHELP_ButtonWndProc
1128  */
1129 static LRESULT CALLBACK WINHELP_ButtonWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1130 {
1131     if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0;
1132
1133     if (msg == WM_KEYDOWN)
1134     {
1135         switch (wParam)
1136         {
1137         case VK_UP:
1138         case VK_DOWN:
1139         case VK_PRIOR:
1140         case VK_NEXT:
1141         case VK_ESCAPE:
1142             return SendMessage(GetParent(hWnd), msg, wParam, lParam);
1143         }
1144     }
1145
1146     return CallWindowProc(Globals.button_proc, hWnd, msg, wParam, lParam);
1147 }
1148
1149 /***********************************************************************
1150  *
1151  *           WINHELP_TextWndProc
1152  */
1153 static LRESULT CALLBACK WINHELP_TextWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1154 {
1155     WINHELP_WINDOW    *win;
1156     WINHELP_LINE      *line;
1157     WINHELP_LINE_PART *part;
1158     WINDOWPOS         *winpos;
1159     PAINTSTRUCT        ps;
1160     HDC   hDc;
1161     INT   scroll_pos;
1162     HWND  hPopupWnd;
1163
1164     if (msg != WM_LBUTTONDOWN)
1165         WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL);
1166
1167     switch (msg)
1168     {
1169     case WM_NCCREATE:
1170         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
1171         SetWindowLongPtr(hWnd, 0, (ULONG_PTR) win);
1172         win->hBrush = CreateSolidBrush(win->info->sr_color);
1173         WINHELP_InitFonts(hWnd);
1174         break;
1175
1176     case WM_CREATE:
1177         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1178
1179         /* Calculate vertical size and position of a popup window */
1180         if (win->info->win_style & WS_POPUP)
1181         {
1182             POINT origin;
1183             RECT old_window_rect;
1184             RECT old_client_rect;
1185             SIZE old_window_size;
1186             SIZE old_client_size;
1187             SIZE new_client_size;
1188             SIZE new_window_size;
1189
1190             GetWindowRect(GetParent(hWnd), &old_window_rect);
1191             origin.x = old_window_rect.left;
1192             origin.y = old_window_rect.top;
1193             old_window_size.cx = old_window_rect.right  - old_window_rect.left;
1194             old_window_size.cy = old_window_rect.bottom - old_window_rect.top;
1195
1196             WINHELP_LayoutMainWindow(win);
1197             GetClientRect(hWnd, &old_client_rect);
1198             old_client_size.cx = old_client_rect.right  - old_client_rect.left;
1199             old_client_size.cy = old_client_rect.bottom - old_client_rect.top;
1200
1201             new_client_size = old_client_size;
1202             WINHELP_SplitLines(hWnd, &new_client_size);
1203
1204             if (origin.y + POPUP_YDISTANCE + new_client_size.cy <= GetSystemMetrics(SM_CYSCREEN))
1205                 origin.y += POPUP_YDISTANCE;
1206             else
1207                 origin.y -= POPUP_YDISTANCE + new_client_size.cy;
1208
1209             new_window_size.cx = old_window_size.cx - old_client_size.cx + new_client_size.cx;
1210             new_window_size.cy = old_window_size.cy - old_client_size.cy + new_client_size.cy;
1211
1212             win->hShadowWnd =
1213                 CreateWindowEx(WS_EX_TOOLWINDOW, SHADOW_WIN_CLASS_NAME, "", WS_POPUP,
1214                              origin.x + SHADOW_DX, origin.y + SHADOW_DY,
1215                              new_window_size.cx, new_window_size.cy,
1216                              0, 0, Globals.hInstance, 0);
1217
1218             SetWindowPos(GetParent(hWnd), HWND_TOP, origin.x, origin.y,
1219                          new_window_size.cx, new_window_size.cy,
1220                          0);
1221             SetWindowPos(win->hShadowWnd, hWnd, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
1222             ShowWindow(win->hShadowWnd, SW_NORMAL);
1223             SetActiveWindow(hWnd);
1224         }
1225         break;
1226
1227     case WM_WINDOWPOSCHANGED:
1228         winpos = (WINDOWPOS*) lParam;
1229
1230         if (!(winpos->flags & SWP_NOSIZE)) WINHELP_SetupText(hWnd, NULL, 0);
1231         break;
1232
1233     case WM_MOUSEWHEEL:
1234     {
1235        int wheelDelta = 0;
1236        UINT scrollLines = 3;
1237        int curPos = GetScrollPos(hWnd, SB_VERT);
1238        int min, max;
1239
1240        GetScrollRange(hWnd, SB_VERT, &min, &max);
1241
1242        SystemParametersInfo(SPI_GETWHEELSCROLLLINES,0, &scrollLines, 0);
1243        if (wParam & (MK_SHIFT | MK_CONTROL))
1244            return DefWindowProc(hWnd, msg, wParam, lParam);
1245        wheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
1246        if (abs(wheelDelta) >= WHEEL_DELTA && scrollLines) {
1247            int dy;
1248
1249            curPos += wheelDelta;
1250            if (curPos > max)
1251                 curPos = max;
1252            else if (curPos < min)
1253                 curPos = min;
1254
1255            dy = GetScrollPos(hWnd, SB_VERT) - curPos;
1256            SetScrollPos(hWnd, SB_VERT, curPos, TRUE);
1257            ScrollWindow(hWnd, 0, dy, NULL, NULL);
1258            UpdateWindow(hWnd);
1259        }
1260     }
1261     break;
1262
1263     case WM_VSCROLL:
1264     {
1265         BOOL  update = TRUE;
1266         RECT  rect;
1267         INT   Min, Max;
1268         INT   CurPos = GetScrollPos(hWnd, SB_VERT);
1269         INT   dy;
1270         
1271         GetScrollRange(hWnd, SB_VERT, &Min, &Max);
1272         GetClientRect(hWnd, &rect);
1273
1274         switch (wParam & 0xffff)
1275         {
1276         case SB_THUMBTRACK:
1277         case SB_THUMBPOSITION: CurPos  = wParam >> 16;                   break;
1278         case SB_TOP:           CurPos  = Min;                            break;
1279         case SB_BOTTOM:        CurPos  = Max;                            break;
1280         case SB_PAGEUP:        CurPos -= (rect.bottom - rect.top) / 2;   break;
1281         case SB_PAGEDOWN:      CurPos += (rect.bottom - rect.top) / 2;   break;
1282         case SB_LINEUP:        CurPos -= GetSystemMetrics(SM_CXVSCROLL); break;
1283         case SB_LINEDOWN:      CurPos += GetSystemMetrics(SM_CXVSCROLL); break;
1284         default: update = FALSE;
1285         }
1286         if (update)
1287         {
1288             if (CurPos > Max)
1289                 CurPos = Max;
1290             else if (CurPos < Min)
1291                 CurPos = Min;
1292             dy = GetScrollPos(hWnd, SB_VERT) - CurPos;
1293             SetScrollPos(hWnd, SB_VERT, CurPos, TRUE);
1294             ScrollWindow(hWnd, 0, dy, NULL, NULL);
1295             UpdateWindow(hWnd);
1296         }
1297     }
1298     break;
1299
1300     case WM_PAINT:
1301         hDc = BeginPaint(hWnd, &ps);
1302         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1303         scroll_pos = GetScrollPos(hWnd, SB_VERT);
1304
1305         /* No DPtoLP needed - MM_TEXT map mode */
1306         if (ps.fErase) FillRect(hDc, &ps.rcPaint, win->hBrush);
1307         for (line = win->first_line; line; line = line->next)
1308         {
1309             for (part = &line->first_part; part; part = part->next)
1310             {
1311                 switch (part->cookie)
1312                 {
1313                 case hlp_line_part_text:
1314                     SelectObject(hDc, part->u.text.hFont);
1315                     SetTextColor(hDc, part->u.text.color);
1316                     SetBkColor(hDc, win->info->sr_color);
1317                     TextOut(hDc, part->rect.left, part->rect.top - scroll_pos,
1318                             part->u.text.lpsText, part->u.text.wTextLen);
1319                     if (part->u.text.wUnderline)
1320                     {
1321                         HPEN    hPen;
1322
1323                         switch (part->u.text.wUnderline)
1324                         {
1325                         case 1: /* simple */
1326                         case 2: /* double */
1327                             hPen = CreatePen(PS_SOLID, 1, part->u.text.color);
1328                             break;
1329                         case 3: /* dotted */
1330                             hPen = CreatePen(PS_DOT, 1, part->u.text.color);
1331                             break;
1332                         default:
1333                             WINE_FIXME("Unknow underline type\n");
1334                             continue;
1335                         }
1336
1337                         SelectObject(hDc, hPen);
1338                         MoveToEx(hDc, part->rect.left, part->rect.bottom - scroll_pos - 1, NULL);
1339                         LineTo(hDc, part->rect.right, part->rect.bottom - scroll_pos - 1);
1340                         if (part->u.text.wUnderline == 2)
1341                         {
1342                             MoveToEx(hDc, part->rect.left, part->rect.bottom - scroll_pos + 1, NULL);
1343                             LineTo(hDc, part->rect.right, part->rect.bottom - scroll_pos + 1);
1344                         }
1345                         DeleteObject(hPen);
1346                     }
1347                     break;
1348                 case hlp_line_part_bitmap:
1349                     {
1350                         HDC hMemDC;
1351
1352                         hMemDC = CreateCompatibleDC(hDc);
1353                         SelectObject(hMemDC, part->u.bitmap.hBitmap);
1354                         BitBlt(hDc, part->rect.left, part->rect.top - scroll_pos,
1355                                part->rect.right - part->rect.left, part->rect.bottom - part->rect.top,
1356                                hMemDC, 0, 0, SRCCOPY);
1357                         DeleteDC(hMemDC);
1358                     }
1359                     break;
1360                 case hlp_line_part_metafile:
1361                     {
1362                         HDC hMemDC;
1363                         HBITMAP hBitmap;
1364                         SIZE sz;
1365                         RECT rc;
1366
1367                         sz.cx = part->rect.right - part->rect.left;
1368                         sz.cy = part->rect.bottom - part->rect.top;
1369                         hMemDC = CreateCompatibleDC(hDc);
1370                         hBitmap = CreateCompatibleBitmap(hDc, sz.cx, sz.cy);
1371                         SelectObject(hMemDC, hBitmap);
1372                         SelectObject(hMemDC, win->hBrush);
1373                         rc.left = 0;
1374                         rc.top = 0;
1375                         rc.right = sz.cx;
1376                         rc.bottom = sz.cy;
1377                         FillRect(hMemDC, &rc, win->hBrush);
1378                         SetMapMode(hMemDC, part->u.metafile.mm);
1379                         SetWindowExtEx(hMemDC, sz.cx, sz.cy, 0);
1380                         SetViewportExtEx(hMemDC, sz.cx, sz.cy, 0);
1381                         SetWindowOrgEx(hMemDC, 0, 0, 0);
1382                         SetViewportOrgEx(hMemDC, 0, 0, 0);
1383                         PlayMetaFile(hMemDC, part->u.metafile.hMetaFile);
1384                         SetMapMode(hMemDC, MM_TEXT);
1385                         SetWindowOrgEx(hMemDC, 0, 0, 0);
1386                         SetViewportOrgEx(hMemDC, 0, 0, 0);
1387                         BitBlt(hDc, part->rect.left, part->rect.top - scroll_pos,
1388                                sz.cx, sz.cy, hMemDC, 0, 0, SRCCOPY);
1389                         DeleteDC(hMemDC);
1390                         DeleteObject(hBitmap);
1391                     }
1392                     break;
1393                 }
1394             }
1395         }
1396
1397         EndPaint(hWnd, &ps);
1398         break;
1399
1400     case WM_MOUSEMOVE:
1401         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1402
1403         if (WINHELP_IsOverLink(win, wParam, lParam))
1404             SetCursor(win->hHandCur); /* set to hand pointer cursor to indicate a link */
1405         else
1406             SetCursor(win->hArrowCur); /* set to hand pointer cursor to indicate a link */
1407
1408         break;
1409
1410     case WM_LBUTTONDOWN:
1411         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1412
1413         if (Globals.active_popup)
1414         {
1415             hPopupWnd = Globals.active_popup->hMainWnd;
1416             Globals.active_popup = NULL;
1417         }
1418         else hPopupWnd = NULL;
1419
1420         part = WINHELP_IsOverLink(win, wParam, lParam);
1421         if (part)
1422         {
1423             HLPFILE*            hlpfile;
1424             HLPFILE_WINDOWINFO* wi;
1425
1426             if (part->link) switch (part->link->cookie)
1427             {
1428             case hlp_link_link:
1429                 hlpfile = WINHELP_LookupHelpFile(part->link->string);
1430                 if (part->link->window == -1)
1431                     wi = win->info;
1432                 else if (part->link->window < hlpfile->numWindows)
1433                     wi = &hlpfile->windows[part->link->window];
1434                 else
1435                 {
1436                     WINE_WARN("link to window %d/%d\n", part->link->window, hlpfile->numWindows);
1437                     break;
1438                 }
1439                 WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, part->link->hash,
1440                                        wi, SW_NORMAL);
1441                 break;
1442             case hlp_link_popup:
1443                 hlpfile = WINHELP_LookupHelpFile(part->link->string);
1444                 if (hlpfile) WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, part->link->hash,
1445                                                     WINHELP_GetPopupWindowInfo(hlpfile, win, lParam),
1446                                                     SW_NORMAL);
1447                 break;
1448             case hlp_link_macro:
1449                 MACRO_ExecuteMacro(part->link->string);
1450                 break;
1451             default:
1452                 WINE_FIXME("Unknown link cookie %d\n", part->link->cookie);
1453             }
1454         }
1455
1456         if (hPopupWnd)
1457             DestroyWindow(hPopupWnd);
1458         break;
1459
1460     }
1461
1462     return DefWindowProc(hWnd, msg, wParam, lParam);
1463 }
1464
1465 /******************************************************************
1466  *              WINHELP_HistoryWndProc
1467  *
1468  *
1469  */
1470 static LRESULT CALLBACK WINHELP_HistoryWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1471 {
1472     WINHELP_WINDOW*     win;
1473     PAINTSTRUCT         ps;
1474     HDC                 hDc;
1475     TEXTMETRIC          tm;
1476     unsigned int        i;
1477     RECT                r;
1478
1479     switch (msg)
1480     {
1481     case WM_NCCREATE:
1482         win = (WINHELP_WINDOW*)((LPCREATESTRUCT)lParam)->lpCreateParams;
1483         SetWindowLongPtr(hWnd, 0, (ULONG_PTR)win);
1484         win->hHistoryWnd = hWnd;
1485         break;
1486     case WM_CREATE:
1487         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1488         hDc = GetDC(hWnd);
1489         GetTextMetrics(hDc, &tm);
1490         GetWindowRect(hWnd, &r);
1491
1492         r.right = r.left + 30 * tm.tmAveCharWidth;
1493         r.bottom = r.top + (sizeof(Globals.history.set) / sizeof(Globals.history.set[0])) * tm.tmHeight;
1494         AdjustWindowRect(&r, GetWindowLong(hWnd, GWL_STYLE), FALSE);
1495         if (r.left < 0) {r.right -= r.left; r.left = 0;}
1496         if (r.top < 0) {r.bottom -= r.top; r.top = 0;}
1497
1498         MoveWindow(hWnd, r.left, r.top, r.right, r.bottom, TRUE);
1499         ReleaseDC(hWnd, hDc);
1500         break;
1501     case WM_LBUTTONDOWN:
1502         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1503         hDc = GetDC(hWnd);
1504         GetTextMetrics(hDc, &tm);
1505         i = HIWORD(lParam) / tm.tmHeight;
1506         if (i < Globals.history.index)
1507             WINHELP_CreateHelpWindow(&Globals.history.set[i], SW_SHOW, TRUE);
1508         ReleaseDC(hWnd, hDc);
1509         break;
1510     case WM_PAINT:
1511         hDc = BeginPaint(hWnd, &ps);
1512         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1513         GetTextMetrics(hDc, &tm);
1514
1515         for (i = 0; i < Globals.history.index; i++)
1516         {
1517             if (Globals.history.set[i].page->file == Globals.active_win->page->file)
1518             {
1519                 TextOut(hDc, 0, i * tm.tmHeight,
1520                         Globals.history.set[i].page->lpszTitle,
1521                         strlen(Globals.history.set[i].page->lpszTitle));
1522             }
1523             else
1524             {
1525                 char        buffer[1024];
1526                 const char* ptr1;
1527                 const char* ptr2;
1528                 unsigned    len;
1529
1530                 ptr1 = strrchr(Globals.history.set[i].page->file->lpszPath, '\\');
1531                 if (!ptr1) ptr1 = Globals.history.set[i].page->file->lpszPath;
1532                 else ptr1++;
1533                 ptr2 = strrchr(ptr1, '.');
1534                 len = ptr2 ? ptr2 - ptr1 : strlen(ptr1);
1535                 if (len > sizeof(buffer)) len = sizeof(buffer);
1536                 memcpy(buffer, ptr1, len);
1537                 if (len < sizeof(buffer)) buffer[len++] = ':';
1538                 strncpy(&buffer[len], Globals.history.set[i].page->lpszTitle, sizeof(buffer) - len);
1539                 buffer[sizeof(buffer) - 1] = '\0';
1540                 TextOut(hDc, 0, i * tm.tmHeight, buffer, strlen(buffer));
1541             }
1542         }
1543         EndPaint(hWnd, &ps);
1544         break;
1545     case WM_DESTROY:
1546         win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1547         if (hWnd == win->hHistoryWnd)
1548             win->hHistoryWnd = 0;
1549         break;
1550     }
1551     return DefWindowProc(hWnd, msg, wParam, lParam);
1552 }
1553
1554 /***********************************************************************
1555  *
1556  *           WINHELP_ShadowWndProc
1557  */
1558 static LRESULT CALLBACK WINHELP_ShadowWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1559 {
1560     if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0;
1561     return WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL) ? 0L : DefWindowProc(hWnd, msg, wParam, lParam);
1562 }
1563
1564 /***********************************************************************
1565  *
1566  *           SetupText
1567  */
1568 static void WINHELP_SetupText(HWND hWnd, WINHELP_WINDOW* win, ULONG relative)
1569 {
1570     HDC  hDc;
1571     RECT rect;
1572     SIZE newsize;
1573
1574     if (!win) win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1575     if (use_richedit) return WINHELP_FillRichEdit(hWnd, win, relative);
1576     hDc = GetDC(hWnd);
1577     ShowScrollBar(hWnd, SB_VERT, FALSE);
1578     if (!WINHELP_SplitLines(hWnd, NULL))
1579     {
1580         ShowScrollBar(hWnd, SB_VERT, TRUE);
1581         GetClientRect(hWnd, &rect);
1582
1583         WINHELP_SplitLines(hWnd, &newsize);
1584         SetScrollRange(hWnd, SB_VERT, 0, rect.top + newsize.cy - rect.bottom, TRUE);
1585     }
1586     else
1587     {
1588         SetScrollPos(hWnd, SB_VERT, 0, FALSE);
1589         SetScrollRange(hWnd, SB_VERT, 0, 0, FALSE);
1590     }
1591
1592     ReleaseDC(hWnd, hDc);
1593 }
1594
1595 /***********************************************************************
1596  *
1597  *           WINHELP_AppendText
1598  */
1599 static BOOL WINHELP_AppendText(WINHELP_LINE ***linep, WINHELP_LINE_PART ***partp,
1600                                LPSIZE space, LPSIZE textsize,
1601                                INT *line_ascent, INT ascent,
1602                                LPCSTR text, UINT textlen,
1603                                HFONT font, COLORREF color, HLPFILE_LINK *link,
1604                                unsigned underline)
1605 {
1606     WINHELP_LINE      *line;
1607     WINHELP_LINE_PART *part;
1608     LPSTR ptr;
1609
1610     if (!*partp) /* New line */
1611     {
1612         *line_ascent  = ascent;
1613
1614         line = HeapAlloc(GetProcessHeap(), 0,
1615                          sizeof(WINHELP_LINE) + textlen);
1616         if (!line) return FALSE;
1617
1618         line->next    = 0;
1619         part          = &line->first_part;
1620         ptr           = (char*)line + sizeof(WINHELP_LINE);
1621
1622         line->rect.top    = (**linep ? (**linep)->rect.bottom : 0) + space->cy;
1623         line->rect.bottom = line->rect.top;
1624         line->rect.left   = space->cx;
1625         line->rect.right  = space->cx;
1626
1627         if (**linep) *linep = &(**linep)->next;
1628         **linep = line;
1629         space->cy = 0;
1630     }
1631     else /* Same line */
1632     {
1633         line = **linep;
1634
1635         if (*line_ascent < ascent)
1636         {
1637             WINHELP_LINE_PART *p;
1638             for (p = &line->first_part; p; p = p->next)
1639             {
1640                 p->rect.top    += ascent - *line_ascent;
1641                 p->rect.bottom += ascent - *line_ascent;
1642             }
1643             line->rect.bottom += ascent - *line_ascent;
1644             *line_ascent = ascent;
1645         }
1646
1647         part = HeapAlloc(GetProcessHeap(), 0,
1648                          sizeof(WINHELP_LINE_PART) + textlen);
1649         if (!part) return FALSE;
1650         **partp = part;
1651         ptr     = (char*)part + sizeof(WINHELP_LINE_PART);
1652     }
1653
1654     memcpy(ptr, text, textlen);
1655     part->cookie            = hlp_line_part_text;
1656     part->rect.left         = line->rect.right + (*partp ? space->cx : 0);
1657     part->rect.right        = part->rect.left + textsize->cx;
1658     line->rect.right        = part->rect.right;
1659     part->rect.top          =
1660         ((*partp) ? line->rect.top : line->rect.bottom) + *line_ascent - ascent;
1661     part->rect.bottom       = part->rect.top + textsize->cy;
1662     line->rect.bottom       = max(line->rect.bottom, part->rect.bottom);
1663     part->u.text.lpsText    = ptr;
1664     part->u.text.wTextLen   = textlen;
1665     part->u.text.hFont      = font;
1666     part->u.text.color      = color;
1667     part->u.text.wUnderline = underline;
1668
1669     WINE_TRACE("Appended text '%*.*s'[%d] @ (%d,%d-%d,%d)\n",
1670                part->u.text.wTextLen,
1671                part->u.text.wTextLen,
1672                part->u.text.lpsText,
1673                part->u.text.wTextLen,
1674                part->rect.left, part->rect.top, part->rect.right, part->rect.bottom);
1675
1676     part->link = link;
1677     if (link) link->wRefCount++;
1678
1679     part->next          = 0;
1680     *partp              = &part->next;
1681
1682     space->cx = 0;
1683
1684     return TRUE;
1685 }
1686
1687 /***********************************************************************
1688  *
1689  *           WINHELP_AppendGfxObject
1690  */
1691 static WINHELP_LINE_PART* WINHELP_AppendGfxObject(WINHELP_LINE ***linep, WINHELP_LINE_PART ***partp,
1692                                                   LPSIZE space, LPSIZE gfxSize,
1693                                                   HLPFILE_LINK *link, unsigned pos)
1694 {
1695     WINHELP_LINE      *line;
1696     WINHELP_LINE_PART *part;
1697     LPSTR              ptr;
1698
1699     if (!*partp || pos == 1) /* New line */
1700     {
1701         line = HeapAlloc(GetProcessHeap(), 0, sizeof(WINHELP_LINE));
1702         if (!line) return NULL;
1703
1704         line->next    = NULL;
1705         part          = &line->first_part;
1706
1707         line->rect.top    = (**linep ? (**linep)->rect.bottom : 0) + space->cy;
1708         line->rect.bottom = line->rect.top;
1709         line->rect.left   = space->cx;
1710         line->rect.right  = space->cx;
1711
1712         if (**linep) *linep = &(**linep)->next;
1713         **linep = line;
1714         space->cy = 0;
1715         ptr = (char*)line + sizeof(WINHELP_LINE);
1716     }
1717     else /* Same line */
1718     {
1719         if (pos == 2) WINE_FIXME("Left alignment not handled\n");
1720         line = **linep;
1721
1722         part = HeapAlloc(GetProcessHeap(), 0, sizeof(WINHELP_LINE_PART));
1723         if (!part) return NULL;
1724         **partp = part;
1725         ptr = (char*)part + sizeof(WINHELP_LINE_PART);
1726     }
1727
1728     /* part->cookie should be set by caller (image or metafile) */
1729     part->rect.left       = line->rect.right + (*partp ? space->cx : 0);
1730     part->rect.right      = part->rect.left + gfxSize->cx;
1731     line->rect.right      = part->rect.right;
1732     part->rect.top        = (*partp) ? line->rect.top : line->rect.bottom;
1733     part->rect.bottom     = part->rect.top + gfxSize->cy;
1734     line->rect.bottom     = max(line->rect.bottom, part->rect.bottom);
1735
1736     WINE_TRACE("Appended gfx @ (%d,%d-%d,%d)\n",
1737                part->rect.left, part->rect.top, part->rect.right, part->rect.bottom);
1738
1739     part->link = link;
1740     if (link) link->wRefCount++;
1741
1742     part->next            = NULL;
1743     *partp                = &part->next;
1744
1745     space->cx = 0;
1746
1747     return part;
1748 }
1749
1750
1751 /***********************************************************************
1752  *
1753  *           WINHELP_SplitLines
1754  */
1755 static BOOL WINHELP_SplitLines(HWND hWnd, LPSIZE newsize)
1756 {
1757     WINHELP_WINDOW     *win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
1758     HLPFILE_PARAGRAPH  *p;
1759     WINHELP_LINE      **line = &win->first_line;
1760     WINHELP_LINE_PART **part = 0;
1761     INT                 line_ascent = 0;
1762     SIZE                space;
1763     RECT                rect;
1764     HDC                 hDc;
1765
1766     if (newsize) newsize->cx = newsize->cy = 0;
1767
1768     if (!win->page) return TRUE;
1769
1770     WINHELP_DeleteLines(win);
1771
1772     GetClientRect(hWnd, &rect);
1773
1774     rect.top    += INTERNAL_BORDER_WIDTH;
1775     rect.left   += INTERNAL_BORDER_WIDTH;
1776     rect.right  -= INTERNAL_BORDER_WIDTH;
1777     rect.bottom -= INTERNAL_BORDER_WIDTH;
1778
1779     space.cy = rect.top;
1780     space.cx = rect.left;
1781
1782     hDc = GetDC(hWnd);
1783
1784     for (p = win->page->first_paragraph; p; p = p->next)
1785     {
1786         switch (p->cookie)
1787         {
1788         case para_normal_text:
1789         case para_debug_text:
1790             {
1791                 TEXTMETRIC tm;
1792                 SIZE textsize    = {0, 0};
1793                 LPCSTR text      = p->u.text.lpszText;
1794                 UINT indent      = 0;
1795                 UINT len         = strlen(text);
1796                 unsigned underline = 0;
1797
1798                 HFONT hFont = 0;
1799                 COLORREF color = RGB(0, 0, 0);
1800
1801                 if (p->u.text.wFont < win->page->file->numFonts)
1802                 {
1803                     HLPFILE*    hlpfile = win->page->file;
1804
1805                     if (!hlpfile->fonts[p->u.text.wFont].hFont)
1806                     {
1807                         LOGFONT lf = hlpfile->fonts[p->u.text.wFont].LogFont;
1808                         lf.lfHeight = -lf.lfHeight / 2 - 3;
1809                         hlpfile->fonts[p->u.text.wFont].hFont = CreateFontIndirect(&lf);
1810                     }
1811                     hFont = hlpfile->fonts[p->u.text.wFont].hFont;
1812                     color = hlpfile->fonts[p->u.text.wFont].color;
1813                 }
1814                 else
1815                 {
1816                     UINT  wFont = (p->u.text.wFont < win->fonts_len) ? p->u.text.wFont : 0;
1817
1818                     hFont = win->fonts[wFont];
1819                 }
1820
1821                 if (p->link && p->link->bClrChange)
1822                 {
1823                     underline = (p->link->cookie == hlp_link_popup) ? 3 : 1;
1824                     color = RGB(0, 0x80, 0);
1825                 }
1826                 if (p->cookie == para_debug_text) color = RGB(0xff, 0, 0);
1827
1828                 SelectObject(hDc, hFont);
1829
1830                 GetTextMetrics(hDc, &tm);
1831
1832                 if (p->u.text.wIndent)
1833                 {
1834                     indent = p->u.text.wIndent * 5 * tm.tmAveCharWidth;
1835                     if (!part)
1836                         space.cx = rect.left + indent - 2 * tm.tmAveCharWidth;
1837                 }
1838
1839                 if (p->u.text.wVSpace)
1840                 {
1841                     part = 0;
1842                     space.cx = rect.left + indent;
1843                     space.cy += (p->u.text.wVSpace - 1) * tm.tmHeight;
1844                 }
1845
1846                 if (p->u.text.wHSpace)
1847                 {
1848                     space.cx += p->u.text.wHSpace * 2 * tm.tmAveCharWidth;
1849                 }
1850
1851                 WINE_TRACE("splitting text %s\n", text);
1852
1853                 while (len)
1854                 {
1855                     INT free_width = rect.right - (part ? (*line)->rect.right : rect.left) - space.cx;
1856                     UINT low = 0, curr = len, high = len, textlen = 0;
1857
1858                     if (free_width > 0)
1859                     {
1860                         while (1)
1861                         {
1862                             GetTextExtentPoint(hDc, text, curr, &textsize);
1863
1864                             if (textsize.cx <= free_width) low = curr;
1865                             else high = curr;
1866
1867                             if (high <= low + 1) break;
1868
1869                             if (textsize.cx) curr = (curr * free_width) / textsize.cx;
1870                             if (curr <= low) curr = low + 1;
1871                             else if (curr >= high) curr = high - 1;
1872                         }
1873                         textlen = low;
1874                         while (textlen && text[textlen] && text[textlen] != ' ') textlen--;
1875                     }
1876                     if (!part && !textlen) textlen = max(low, 1);
1877
1878                     if (free_width <= 0 || !textlen)
1879                     {
1880                         part = 0;
1881                         space.cx = rect.left + indent;
1882                         space.cx = min(space.cx, rect.right - rect.left - 1);
1883                         continue;
1884                     }
1885
1886                     WINE_TRACE("\t => %d %*s\n", textlen, textlen, text);
1887
1888                     if (!WINHELP_AppendText(&line, &part, &space, &textsize,
1889                                             &line_ascent, tm.tmAscent,
1890                                             text, textlen, hFont, color, p->link, underline) ||
1891                         (!newsize && (*line)->rect.bottom > rect.bottom))
1892                     {
1893                         ReleaseDC(hWnd, hDc);
1894                         return FALSE;
1895                     }
1896
1897                     if (newsize)
1898                         newsize->cx = max(newsize->cx, (*line)->rect.right + INTERNAL_BORDER_WIDTH);
1899
1900                     len -= textlen;
1901                     text += textlen;
1902                     if (text[0] == ' ') text++, len--;
1903                 }
1904             }
1905             break;
1906         case para_bitmap:
1907         case para_metafile:
1908             {
1909                 SIZE                    gfxSize;
1910                 INT                     free_width;
1911                 WINHELP_LINE_PART*      ref_part;
1912
1913                 if (p->u.gfx.pos & 0x8000)
1914                 {
1915                     space.cx = rect.left;
1916                     if (*line)
1917                         space.cy += (*line)->rect.bottom - (*line)->rect.top;
1918                     part = 0;
1919                 }
1920
1921                 if (p->cookie == para_bitmap)
1922                 {
1923                     DIBSECTION              dibs;
1924                     
1925                     GetObject(p->u.gfx.u.bmp.hBitmap, sizeof(dibs), &dibs);
1926                     gfxSize.cx = dibs.dsBm.bmWidth;
1927                     gfxSize.cy = dibs.dsBm.bmHeight;
1928                 }
1929                 else
1930                 {
1931                     LPMETAFILEPICT lpmfp = &p->u.gfx.u.mfp;
1932                     if (lpmfp->mm == MM_ANISOTROPIC || lpmfp->mm == MM_ISOTROPIC)
1933                     {
1934                         gfxSize.cx = MulDiv(lpmfp->xExt, GetDeviceCaps(hDc, HORZRES),
1935                                             100*GetDeviceCaps(hDc, HORZSIZE));
1936                         gfxSize.cy = MulDiv(lpmfp->yExt, GetDeviceCaps(hDc, VERTRES),
1937                                             100*GetDeviceCaps(hDc, VERTSIZE));
1938                     }
1939                     else
1940                     {
1941                         gfxSize.cx = lpmfp->xExt;
1942                         gfxSize.cy = lpmfp->yExt;
1943                     }
1944                 }
1945
1946                 free_width = rect.right - ((part && *line) ? (*line)->rect.right : rect.left) - space.cx;
1947                 if (free_width <= 0)
1948                 {
1949                     part = NULL;
1950                     space.cx = rect.left;
1951                     space.cx = min(space.cx, rect.right - rect.left - 1);
1952                 }
1953                 ref_part = WINHELP_AppendGfxObject(&line, &part, &space, &gfxSize,
1954                                                    p->link, p->u.gfx.pos);
1955                 if (!ref_part || (!newsize && (*line)->rect.bottom > rect.bottom))
1956                 {
1957                     return FALSE;
1958                 }
1959                 if (p->cookie == para_bitmap)
1960                 {
1961                     ref_part->cookie = hlp_line_part_bitmap;
1962                     ref_part->u.bitmap.hBitmap = p->u.gfx.u.bmp.hBitmap;
1963                 }
1964                 else
1965                 {
1966                     ref_part->cookie = hlp_line_part_metafile;
1967                     ref_part->u.metafile.hMetaFile = p->u.gfx.u.mfp.hMF;
1968                     ref_part->u.metafile.mm = p->u.gfx.u.mfp.mm;
1969                 }
1970             }
1971             break;
1972         }
1973     }
1974
1975     if (newsize)
1976         newsize->cy = (*line)->rect.bottom + INTERNAL_BORDER_WIDTH;
1977
1978     ReleaseDC(hWnd, hDc);
1979     return TRUE;
1980 }
1981
1982 /***********************************************************************
1983  *
1984  *           WINHELP_CheckPopup
1985  */
1986 static BOOL WINHELP_CheckPopup(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT* lret)
1987 {
1988     HWND        hPopup;
1989
1990     if (!Globals.active_popup) return FALSE;
1991
1992     switch (msg)
1993     {
1994     case WM_NOTIFY:
1995         {
1996             MSGFILTER*  msgf = (MSGFILTER*)lParam;
1997             if (msgf->nmhdr.code == EN_MSGFILTER)
1998             {
1999                 if (!WINHELP_CheckPopup(hWnd, msgf->msg, msgf->wParam, msgf->lParam, NULL))
2000                     return FALSE;
2001                 if (lret) *lret = 1;
2002                 return TRUE;
2003             }
2004         }
2005         break;
2006     case WM_ACTIVATE:
2007         if (wParam != WA_INACTIVE || (HWND)lParam == Globals.active_win->hMainWnd ||
2008             (HWND)lParam == Globals.active_popup->hMainWnd ||
2009             GetWindow((HWND)lParam, GW_OWNER) == Globals.active_win->hMainWnd)
2010             break;
2011     case WM_COMMAND:
2012         if (use_richedit) break;
2013         goto doit;
2014     case WM_LBUTTONUP:
2015     case WM_LBUTTONDOWN:
2016         if (WINHELP_HandleTextMouse(Globals.active_popup, msg, lParam) && msg == WM_LBUTTONDOWN)
2017             return FALSE;
2018         /* fall through */
2019     case WM_MBUTTONDOWN:
2020     case WM_RBUTTONDOWN:
2021     case WM_NCLBUTTONDOWN:
2022     case WM_NCMBUTTONDOWN:
2023     case WM_NCRBUTTONDOWN:
2024 doit:
2025         hPopup = Globals.active_popup->hMainWnd;
2026         Globals.active_popup = NULL;
2027         DestroyWindow(hPopup);
2028         return TRUE;
2029     }
2030     return FALSE;
2031 }
2032
2033 /***********************************************************************
2034  *
2035  *           WINHELP_DeleteLines
2036  */
2037 static void WINHELP_DeleteLines(WINHELP_WINDOW *win)
2038 {
2039     WINHELP_LINE      *line, *next_line;
2040     WINHELP_LINE_PART *part, *next_part;
2041     for (line = win->first_line; line; line = next_line)
2042     {
2043         next_line = line->next;
2044         for (part = &line->first_part; part; part = next_part)
2045         {
2046             next_part = part->next;
2047             HLPFILE_FreeLink(part->link);
2048             HeapFree(GetProcessHeap(), 0, part);
2049         }
2050     }
2051     win->first_line = 0;
2052 }
2053
2054 /******************************************************************
2055  *              WINHELP_DeleteButtons
2056  *
2057  */
2058 static void WINHELP_DeleteButtons(WINHELP_WINDOW* win)
2059 {
2060     WINHELP_BUTTON*     b;
2061     WINHELP_BUTTON*     bp;
2062
2063     for (b = win->first_button; b; b = bp)
2064     {
2065         DestroyWindow(b->hWnd);
2066         bp = b->next;
2067         HeapFree(GetProcessHeap(), 0, b);
2068     }
2069     win->first_button = NULL;
2070 }
2071
2072 /******************************************************************
2073  *              WINHELP_DeleteBackSet
2074  *
2075  */
2076 void WINHELP_DeleteBackSet(WINHELP_WINDOW* win)
2077 {
2078     unsigned int i;
2079
2080     for (i = 0; i < win->back.index; i++)
2081     {
2082         HLPFILE_FreeHlpFile(win->back.set[i].page->file);
2083         win->back.set[i].page = NULL;
2084     }
2085     win->back.index = 0;
2086 }
2087
2088 /******************************************************************
2089  *             WINHELP_DeletePageLinks
2090  *
2091  */
2092 static void WINHELP_DeletePageLinks(HLPFILE_PAGE* page)
2093 {
2094     HLPFILE_LINK*       curr;
2095     HLPFILE_LINK*       next;
2096
2097     for (curr = page->first_link; curr; curr = next)
2098     {
2099         next = curr->next;
2100         HeapFree(GetProcessHeap(), 0, curr);
2101     }
2102 }
2103
2104 /***********************************************************************
2105  *
2106  *           WINHELP_DeleteWindow
2107  */
2108 static void WINHELP_DeleteWindow(WINHELP_WINDOW* win)
2109 {
2110     WINHELP_WINDOW**    w;
2111
2112     for (w = &Globals.win_list; *w; w = &(*w)->next)
2113     {
2114         if (*w == win)
2115         {
2116             *w = win->next;
2117             break;
2118         }
2119     }
2120
2121     if (Globals.active_win == win)
2122     {
2123         Globals.active_win = Globals.win_list;
2124         if (Globals.win_list)
2125             SetActiveWindow(Globals.win_list->hMainWnd);
2126     }
2127
2128     if (win == Globals.active_popup)
2129         Globals.active_popup = NULL;
2130
2131     WINHELP_DeleteButtons(win);
2132
2133     if (win->page) WINHELP_DeletePageLinks(win->page);
2134     if (win->hShadowWnd) DestroyWindow(win->hShadowWnd);
2135     if (win->hHistoryWnd) DestroyWindow(win->hHistoryWnd);
2136
2137     DeleteObject(win->hBrush);
2138
2139     WINHELP_DeleteBackSet(win);
2140
2141     if (win->page) HLPFILE_FreeHlpFile(win->page->file);
2142     WINHELP_DeleteLines(win);
2143     HeapFree(GetProcessHeap(), 0, win);
2144 }
2145
2146 /***********************************************************************
2147  *
2148  *           WINHELP_InitFonts
2149  */
2150 static void WINHELP_InitFonts(HWND hWnd)
2151 {
2152     WINHELP_WINDOW *win = (WINHELP_WINDOW*) GetWindowLongPtr(hWnd, 0);
2153     LOGFONT logfontlist[] = {
2154         {-10, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
2155         {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
2156         {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
2157         {-12, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
2158         {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
2159         {-10, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"},
2160         { -8, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, "Helv"}};
2161 #define FONTS_LEN (sizeof(logfontlist)/sizeof(*logfontlist))
2162
2163     static HFONT fonts[FONTS_LEN];
2164     static BOOL init = 0;
2165
2166     win->fonts_len = FONTS_LEN;
2167     win->fonts = fonts;
2168
2169     if (!init)
2170     {
2171         UINT i;
2172
2173         for (i = 0; i < FONTS_LEN; i++)
2174         {
2175             fonts[i] = CreateFontIndirect(&logfontlist[i]);
2176         }
2177
2178         init = 1;
2179     }
2180 }
2181
2182 /***********************************************************************
2183  *
2184  *           WINHELP_MessageBoxIDS_s
2185  */
2186 INT WINHELP_MessageBoxIDS_s(UINT ids_text, LPCSTR str, UINT ids_title, WORD type)
2187 {
2188     CHAR text[MAX_STRING_LEN];
2189     CHAR newtext[MAX_STRING_LEN + MAX_PATH];
2190
2191     LoadString(Globals.hInstance, ids_text, text, sizeof(text));
2192     wsprintf(newtext, text, str);
2193
2194     return MessageBox(0, newtext, MAKEINTRESOURCE(ids_title), type);
2195 }
2196
2197 /******************************************************************
2198  *              WINHELP_IsOverLink
2199  *
2200  *
2201  */
2202 WINHELP_LINE_PART* WINHELP_IsOverLink(WINHELP_WINDOW* win, WPARAM wParam, LPARAM lParam)
2203 {
2204     POINT mouse;
2205     WINHELP_LINE      *line;
2206     WINHELP_LINE_PART *part;
2207     int scroll_pos = GetScrollPos(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), SB_VERT);
2208
2209     mouse.x = LOWORD(lParam);
2210     mouse.y = HIWORD(lParam);
2211     for (line = win->first_line; line; line = line->next)
2212     {
2213         for (part = &line->first_part; part; part = part->next)
2214         {
2215             if (part->link && 
2216                 part->link->string &&
2217                 part->rect.left   <= mouse.x &&
2218                 part->rect.right  >= mouse.x &&
2219                 part->rect.top    <= mouse.y + scroll_pos &&
2220                 part->rect.bottom >= mouse.y + scroll_pos)
2221             {
2222                 return part;
2223             }
2224         }
2225     }
2226
2227     return NULL;
2228 }
2229
2230 /**************************************************************************
2231  * cb_KWBTree
2232  *
2233  * HLPFILE_BPTreeCallback enumeration function for '|KWBTREE' internal file.
2234  *
2235  */
2236 static void cb_KWBTree(void *p, void **next, void *cookie)
2237 {
2238     HWND hListWnd = (HWND)cookie;
2239     int count;
2240
2241     WINE_TRACE("Adding '%s' to search list\n", (char *)p);
2242     SendMessage(hListWnd, LB_INSERTSTRING, -1, (LPARAM)p);
2243     count = SendMessage(hListWnd, LB_GETCOUNT, 0, 0);
2244     SendMessage(hListWnd, LB_SETITEMDATA, count-1, (LPARAM)p);
2245     *next = (char*)p + strlen((char*)p) + 7;
2246 }
2247
2248 /**************************************************************************
2249  * WINHELP_IndexDlgProc
2250  *
2251  * Index dialog callback function.
2252  *
2253  * nResult passed to EndDialog:
2254  *   1: CANCEL button
2255  *  >1: valid offset value +2.
2256  *  EndDialog itself can return 0 (error).
2257  */
2258 INT_PTR CALLBACK WINHELP_SearchDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
2259 {
2260     static HLPFILE *file;
2261     int sel;
2262     ULONG offset = 1;
2263
2264     switch (msg)
2265     {
2266     case WM_INITDIALOG:
2267         file = (HLPFILE *)lParam;
2268         HLPFILE_BPTreeEnum(file->kwbtree, cb_KWBTree,
2269                            GetDlgItem(hWnd, IDC_INDEXLIST));
2270         return TRUE;
2271     case WM_COMMAND:
2272         switch (LOWORD(wParam))
2273         {
2274         case IDOK:
2275             sel = SendDlgItemMessage(hWnd, IDC_INDEXLIST, LB_GETCURSEL, 0, 0);
2276             if (sel != LB_ERR)
2277             {
2278                 BYTE *p;
2279                 int count;
2280
2281                 p = (BYTE*)SendDlgItemMessage(hWnd, IDC_INDEXLIST,
2282                                               LB_GETITEMDATA, sel, 0);
2283                 count = *(short*)((char *)p + strlen((char *)p) + 1);
2284                 if (count > 1)
2285                 {
2286                     MessageBox(hWnd, "count > 1 not supported yet", "Error", MB_OK | MB_ICONSTOP);
2287                     return TRUE;
2288                 }
2289                 offset = *(ULONG*)((char *)p + strlen((char *)p) + 3);
2290                 offset = *(long*)(file->kwdata + offset + 9);
2291                 if (offset == 0xFFFFFFFF)
2292                 {
2293                     MessageBox(hWnd, "macro keywords not supported yet", "Error", MB_OK | MB_ICONSTOP);
2294                     return TRUE;
2295                 }
2296                 offset += 2;
2297             }
2298             /* Fall through */
2299         case IDCANCEL:
2300             EndDialog(hWnd, offset);
2301             return TRUE;
2302         default:
2303             break;
2304         }
2305     default:
2306         break;
2307     }
2308     return FALSE;
2309 }
2310
2311 /**************************************************************************
2312  * WINHELP_CreateIndexWindow
2313  *
2314  * Displays a dialog with keywords of current help file.
2315  *
2316  */
2317 BOOL WINHELP_CreateIndexWindow(void)
2318 {
2319     int ret;
2320     HLPFILE *hlpfile;
2321
2322     if (Globals.active_win && Globals.active_win->page && Globals.active_win->page->file)
2323         hlpfile = Globals.active_win->page->file;
2324     else
2325         return FALSE;
2326
2327     if (hlpfile->kwbtree == NULL)
2328     {
2329         WINE_TRACE("No index provided\n");
2330         return FALSE;
2331     }
2332
2333     ret = DialogBoxParam(Globals.hInstance, MAKEINTRESOURCE(IDD_INDEX),
2334                          Globals.active_win->hMainWnd, WINHELP_SearchDlgProc,
2335                          (LPARAM)hlpfile);
2336     if (ret > 1)
2337     {
2338         ret -= 2;
2339         WINE_TRACE("got %d as an offset\n", ret);
2340         WINHELP_OpenHelpWindow(HLPFILE_PageByOffset, hlpfile, ret,
2341                                Globals.active_win->info, SW_NORMAL);
2342     }
2343     return TRUE;
2344 }