dinput: Move SetEventNotification and associated event into base class.
[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 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 "winhelp.h"
35 #include "winhelp_res.h"
36 #include "shellapi.h"
37
38 #include "wine/debug.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(winhelp);
41
42 static BOOL    WINHELP_RegisterWinClasses(void);
43 static LRESULT CALLBACK WINHELP_MainWndProc(HWND, UINT, WPARAM, LPARAM);
44 static LRESULT CALLBACK WINHELP_TextWndProc(HWND, UINT, WPARAM, LPARAM);
45 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND, UINT, WPARAM, LPARAM);
46 static LRESULT CALLBACK WINHELP_ButtonWndProc(HWND, UINT, WPARAM, LPARAM);
47 static LRESULT CALLBACK WINHELP_HistoryWndProc(HWND, UINT, WPARAM, LPARAM);
48 static LRESULT CALLBACK WINHELP_ShadowWndProc(HWND, UINT, WPARAM, LPARAM);
49 static void    WINHELP_CheckPopup(UINT);
50 static BOOL    WINHELP_SplitLines(HWND hWnd, LPSIZE);
51 static void    WINHELP_InitFonts(HWND hWnd);
52 static void    WINHELP_DeleteLines(WINHELP_WINDOW*);
53 static void    WINHELP_DeleteWindow(WINHELP_WINDOW*);
54 static void    WINHELP_SetupText(HWND hWnd);
55 static WINHELP_LINE_PART* WINHELP_IsOverLink(WINHELP_WINDOW*, WPARAM, LPARAM);
56
57 WINHELP_GLOBALS Globals = {3, NULL, NULL, 0, TRUE, NULL, NULL, NULL, NULL};
58
59 /***********************************************************************
60  *
61  *           WINHELP_LookupHelpFile
62  */
63 HLPFILE* WINHELP_LookupHelpFile(LPCSTR lpszFile)
64 {
65     HLPFILE*        hlpfile;
66
67     hlpfile = HLPFILE_ReadHlpFile(lpszFile);
68
69     /* Add Suffix `.hlp' */
70     if (!hlpfile && lstrcmpi(lpszFile + strlen(lpszFile) - 4, ".hlp") != 0)
71     {
72         char      szFile_hlp[MAX_PATHNAME_LEN];
73
74         lstrcpyn(szFile_hlp, lpszFile, sizeof(szFile_hlp) - 4);
75         szFile_hlp[sizeof(szFile_hlp) - 5] = '\0';
76         lstrcat(szFile_hlp, ".hlp");
77
78         hlpfile = HLPFILE_ReadHlpFile(szFile_hlp);
79     }
80     if (!hlpfile)
81     {
82         WINHELP_MessageBoxIDS_s(STID_HLPFILE_ERROR_s, lpszFile, STID_WHERROR, MB_OK);
83         if (Globals.win_list) return NULL;
84     }
85     return hlpfile;
86 }
87
88 /******************************************************************
89  *              WINHELP_GetWindowInfo
90  *
91  *
92  */
93 HLPFILE_WINDOWINFO*     WINHELP_GetWindowInfo(HLPFILE* hlpfile, LPCSTR name)
94 {
95     static      HLPFILE_WINDOWINFO      mwi;
96     unsigned int     i;
97
98     if (!name || !name[0])
99         name = Globals.active_win->lpszName;
100
101     if (hlpfile)
102         for (i = 0; i < hlpfile->numWindows; i++)
103             if (!strcmp(hlpfile->windows[i].name, name))
104                 return &hlpfile->windows[i];
105
106     if (strcmp(name, "main") != 0)
107     {
108         WINE_FIXME("Couldn't find window info for %s\n", name);
109         assert(0);
110         return NULL;
111     }
112     if (!mwi.name[0])
113     {
114         strcpy(mwi.type, "primary");
115         strcpy(mwi.name, "main");
116         if (!LoadString(Globals.hInstance, STID_WINE_HELP, 
117                         mwi.caption, sizeof(mwi.caption)))
118             strcpy(mwi.caption, hlpfile->lpszTitle);
119         mwi.origin.x = mwi.origin.y = mwi.size.cx = mwi.size.cy = CW_USEDEFAULT;
120         mwi.style = SW_SHOW;
121         mwi.win_style = WS_OVERLAPPEDWINDOW;
122         mwi.sr_color = mwi.sr_color = 0xFFFFFF;
123     }
124     return &mwi;
125 }
126
127 /******************************************************************
128  *              HLPFILE_GetPopupWindowInfo
129  *
130  *
131  */
132 static HLPFILE_WINDOWINFO*     WINHELP_GetPopupWindowInfo(HLPFILE* hlpfile, HWND hParentWnd, POINT* mouse)
133 {
134     static      HLPFILE_WINDOWINFO      wi;
135
136     RECT parent_rect;
137     
138     wi.type[0] = wi.name[0] = wi.caption[0] = '\0';
139
140     /* Calculate horizontal size and position of a popup window */
141     GetWindowRect(hParentWnd, &parent_rect);
142     wi.size.cx = (parent_rect.right  - parent_rect.left) / 2;
143     wi.size.cy = 10; /* need a non null value, so that border are taken into account while computing */
144
145     wi.origin = *mouse;
146     ClientToScreen(hParentWnd, &wi.origin);
147     wi.origin.x -= wi.size.cx / 2;
148     wi.origin.x  = min(wi.origin.x, GetSystemMetrics(SM_CXSCREEN) - wi.size.cx);
149     wi.origin.x  = max(wi.origin.x, 0);
150
151     wi.style = SW_SHOW;
152     wi.win_style = WS_POPUPWINDOW;
153     wi.sr_color = wi.sr_color = 0xFFFFFF;
154
155     return &wi;
156 }
157
158 /***********************************************************************
159  *
160  *           WinMain
161  */
162 int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
163 {
164     MSG                 msg;
165     LONG                lHash = 0;
166     HLPFILE*            hlpfile;
167     static CHAR         default_wndname[] = "main";
168     LPSTR               wndname = default_wndname;
169     WINHELP_DLL*        dll;
170
171     Globals.hInstance = hInstance;
172
173     /* Get options */
174     while (*cmdline && (*cmdline == ' ' || *cmdline == '-'))
175     {
176         CHAR   option;
177         LPCSTR topic_id;
178         if (*cmdline++ == ' ') continue;
179
180         option = *cmdline;
181         if (option) cmdline++;
182         while (*cmdline && *cmdline == ' ') cmdline++;
183         switch (option)
184         {
185         case 'i':
186         case 'I':
187             topic_id = cmdline;
188             while (*cmdline && *cmdline != ' ') cmdline++;
189             if (*cmdline) *cmdline++ = '\0';
190             lHash = HLPFILE_Hash(topic_id);
191             break;
192
193         case '3':
194         case '4':
195             Globals.wVersion = option - '0';
196             break;
197
198         case 'x':
199             show = SW_HIDE; 
200             Globals.isBook = FALSE;
201             break;
202
203         default:
204             WINE_FIXME("Unsupported cmd line: %s\n", cmdline);
205             break;
206         }
207     }
208
209     /* Create primary window */
210     if (!WINHELP_RegisterWinClasses())
211     {
212         WINE_FIXME("Couldn't register classes\n");
213         return 0;
214     }
215
216     if (*cmdline)
217     {
218         char*   ptr;
219         if ((*cmdline == '"') && (ptr = strchr(cmdline+1, '"')))
220         {
221             cmdline++;
222             *ptr = '\0';
223         }
224         if ((ptr = strchr(cmdline, '>')))
225         {
226             *ptr = '\0';
227             wndname = ptr + 1;
228         }
229         hlpfile = WINHELP_LookupHelpFile(cmdline);
230         if (!hlpfile) return 0;
231     }
232     else hlpfile = NULL;
233     WINHELP_CreateHelpWindowByHash(hlpfile, lHash, 
234                                    WINHELP_GetWindowInfo(hlpfile, wndname), show);
235
236     /* Message loop */
237     while (GetMessage(&msg, 0, 0, 0))
238     {
239         TranslateMessage(&msg);
240         DispatchMessage(&msg);
241     }
242     for (dll = Globals.dlls; dll; dll = dll->next)
243     {
244         if (dll->class & DC_INITTERM) dll->handler(DW_TERM, 0, 0);
245     }
246     return 0;
247 }
248
249 /***********************************************************************
250  *
251  *           RegisterWinClasses
252  */
253 static BOOL WINHELP_RegisterWinClasses(void)
254 {
255     WNDCLASS class_main, class_button_box, class_text, class_shadow, class_history;
256
257     class_main.style               = CS_HREDRAW | CS_VREDRAW;
258     class_main.lpfnWndProc         = WINHELP_MainWndProc;
259     class_main.cbClsExtra          = 0;
260     class_main.cbWndExtra          = sizeof(LONG);
261     class_main.hInstance           = Globals.hInstance;
262     class_main.hIcon               = LoadIcon(0, IDI_APPLICATION);
263     class_main.hCursor             = LoadCursor(0, IDC_ARROW);
264     class_main.hbrBackground       = GetStockObject(WHITE_BRUSH);
265     class_main.lpszMenuName        = 0;
266     class_main.lpszClassName       = MAIN_WIN_CLASS_NAME;
267
268     class_button_box               = class_main;
269     class_button_box.lpfnWndProc   = WINHELP_ButtonBoxWndProc;
270     class_button_box.hbrBackground = GetStockObject(GRAY_BRUSH);
271     class_button_box.lpszClassName = BUTTON_BOX_WIN_CLASS_NAME;
272
273     class_text = class_main;
274     class_text.lpfnWndProc         = WINHELP_TextWndProc;
275     class_text.hbrBackground       = 0;
276     class_text.lpszClassName       = TEXT_WIN_CLASS_NAME;
277
278     class_shadow = class_main;
279     class_shadow.lpfnWndProc       = WINHELP_ShadowWndProc;
280     class_shadow.hbrBackground     = GetStockObject(GRAY_BRUSH);
281     class_shadow.lpszClassName     = SHADOW_WIN_CLASS_NAME;
282
283     class_history = class_main;
284     class_history.lpfnWndProc      = WINHELP_HistoryWndProc;
285     class_history.lpszClassName    = HISTORY_WIN_CLASS_NAME;
286
287     return (RegisterClass(&class_main) &&
288             RegisterClass(&class_button_box) &&
289             RegisterClass(&class_text) &&
290             RegisterClass(&class_shadow) &&
291             RegisterClass(&class_history));
292 }
293
294 typedef struct
295 {
296     WORD size;
297     WORD command;
298     LONG data;
299     LONG reserved;
300     WORD ofsFilename;
301     WORD ofsData;
302 } WINHELP,*LPWINHELP;
303
304 /******************************************************************
305  *              WINHELP_HandleCommand
306  *
307  *
308  */
309 static LRESULT  WINHELP_HandleCommand(HWND hSrcWnd, LPARAM lParam)
310 {
311     COPYDATASTRUCT*     cds = (COPYDATASTRUCT*)lParam;
312     WINHELP*            wh;
313
314     if (cds->dwData != 0xA1DE505)
315     {
316         WINE_FIXME("Wrong magic number (%08lx)\n", cds->dwData);
317         return 0;
318     }
319
320     wh = (WINHELP*)cds->lpData;
321
322     if (wh)
323     {
324         char*   ptr = (wh->ofsFilename) ? (LPSTR)wh + wh->ofsFilename : NULL;
325
326         WINE_TRACE("Got[%u]: cmd=%u data=%08x fn=%s\n",
327                    wh->size, wh->command, wh->data, ptr);
328         switch (wh->command)
329         {
330         case HELP_CONTEXT:
331             if (ptr)
332             {
333                 MACRO_JumpContext(ptr, "main", wh->data);
334             }
335             break;
336         case HELP_QUIT:
337             MACRO_Exit();
338             break;
339         case HELP_CONTENTS:
340             if (ptr)
341             {
342                 MACRO_JumpContents(ptr, "main");
343             }
344             break;
345         case HELP_HELPONHELP:
346             MACRO_HelpOn();
347             break;
348         /* case HELP_SETINDEX: */
349         case HELP_SETCONTENTS:
350             if (ptr)
351             {
352                 MACRO_SetContents(ptr, wh->data);
353             }
354             break;
355         case HELP_CONTEXTPOPUP:
356             if (ptr)
357             {
358                 MACRO_PopupContext(ptr, wh->data);
359             }
360             break;
361         /* case HELP_FORCEFILE:*/
362         /* case HELP_CONTEXTMENU: */
363         case HELP_FINDER:
364             /* in fact, should be the topic dialog box */
365             if (ptr)
366             {
367                 MACRO_JumpHash(ptr, "main", 0);
368             }
369             break;
370         /* case HELP_WM_HELP: */
371         /* case HELP_SETPOPUP_POS: */
372         /* case HELP_KEY: */
373         /* case HELP_COMMAND: */
374         /* case HELP_PARTIALKEY: */
375         /* case HELP_MULTIKEY: */
376         /* case HELP_SETWINPOS: */
377             WINE_FIXME("Unknown command (%x) for remote winhelp control\n", wh->command);
378             break;
379         }
380     }
381     return 0L;
382 }
383
384 /******************************************************************
385  *              WINHELP_ReuseWindow
386  *
387  *
388  */
389 static BOOL     WINHELP_ReuseWindow(WINHELP_WINDOW* win, WINHELP_WINDOW* oldwin, 
390                                     HLPFILE_PAGE* page, int nCmdShow)
391 {
392     unsigned int i;
393
394     win->hMainWnd      = oldwin->hMainWnd;
395     win->hButtonBoxWnd = oldwin->hButtonBoxWnd;
396     win->hTextWnd      = oldwin->hTextWnd;
397     win->hHistoryWnd   = oldwin->hHistoryWnd;
398     oldwin->hMainWnd = oldwin->hButtonBoxWnd = oldwin->hTextWnd = oldwin->hHistoryWnd = 0;
399     win->hBrush = oldwin->hBrush;
400
401     SetWindowLong(win->hMainWnd,      0, (LONG)win);
402     SetWindowLong(win->hButtonBoxWnd, 0, (LONG)win);
403     SetWindowLong(win->hTextWnd,      0, (LONG)win);
404     SetWindowLong(win->hHistoryWnd,   0, (LONG)win);
405
406     WINHELP_InitFonts(win->hMainWnd);
407
408     if (page)
409         SetWindowText(win->hMainWnd, page->file->lpszTitle);
410
411     WINHELP_SetupText(win->hTextWnd);
412     InvalidateRect(win->hTextWnd, NULL, TRUE);
413     SendMessage(win->hMainWnd, WM_USER, 0, 0);
414     ShowWindow(win->hMainWnd, nCmdShow);
415     UpdateWindow(win->hTextWnd);
416
417     if (!(win->info->win_style & WS_POPUP))
418     {
419         unsigned        num;
420
421         memcpy(win->history, oldwin->history, sizeof(win->history));
422         win->histIndex = oldwin->histIndex;
423
424         /* FIXME: when using back, we shouldn't update the history... */
425
426         if (page)
427         {
428             for (i = 0; i < win->histIndex; i++)
429                 if (win->history[i] == page) break;
430
431             /* if the new page is already in the history, do nothing */
432             if (i == win->histIndex)
433             {
434                 num = sizeof(win->history) / sizeof(win->history[0]);
435                 if (win->histIndex == num)
436                 {
437                     /* we're full, remove latest entry */
438                     HLPFILE_FreeHlpFile(win->history[0]->file);
439                     memmove(&win->history[0], &win->history[1], 
440                             (num - 1) * sizeof(win->history[0]));
441                     win->histIndex--;
442                 }
443                 win->history[win->histIndex++] = page;
444                 page->file->wRefCount++;
445                 if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE);
446             }
447         }
448
449         memcpy(win->back, oldwin->back, sizeof(win->back));
450         win->backIndex = oldwin->backIndex;
451
452         if (page)
453         {
454             num = sizeof(win->back) / sizeof(win->back[0]);
455             if (win->backIndex == num)
456             {
457                 /* we're full, remove latest entry */
458                 HLPFILE_FreeHlpFile(win->back[0]->file);
459                 memmove(&win->back[0], &win->back[1], 
460                         (num - 1) * sizeof(win->back[0]));
461                 win->backIndex--;
462             }
463             win->back[win->backIndex++] = page;
464             page->file->wRefCount++;
465         }
466     }
467     else
468         win->backIndex = win->histIndex = 0;
469
470     oldwin->histIndex = oldwin->backIndex = 0;
471     WINHELP_DeleteWindow(oldwin);
472     return TRUE;
473 }
474
475 /***********************************************************************
476  *
477  *           WINHELP_CreateHelpWindow
478  */
479 BOOL WINHELP_CreateHelpWindow(HLPFILE_PAGE* page, HLPFILE_WINDOWINFO* wi,
480                               int nCmdShow)
481 {
482     WINHELP_WINDOW *win, *oldwin;
483     HWND hWnd;
484     BOOL bPrimary;
485     BOOL bPopup;
486     LPSTR name;
487
488     bPrimary = !lstrcmpi(wi->name, "main");
489     bPopup = wi->win_style & WS_POPUP;
490
491     /* Initialize WINHELP_WINDOW struct */
492     win = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
493                     sizeof(WINHELP_WINDOW) + strlen(wi->name) + 1);
494     if (!win) return FALSE;
495
496     win->next = Globals.win_list;
497     Globals.win_list = win;
498
499     name = (char*)win + sizeof(WINHELP_WINDOW);
500     lstrcpy(name, wi->name);
501     win->lpszName = name;
502
503     win->page = page;
504
505     win->hArrowCur = LoadCursorA(0, (LPSTR)IDC_ARROW);
506     win->hHandCur = LoadCursorA(0, (LPSTR)IDC_HAND);
507
508     win->info = wi;
509
510     Globals.active_win = win;
511
512     /* Initialize default pushbuttons */
513     if (bPrimary && page)
514     {
515         CHAR    buffer[MAX_STRING_LEN];
516
517         LoadString(Globals.hInstance, STID_CONTENTS, buffer, sizeof(buffer));
518         MACRO_CreateButton("BTN_CONTENTS", buffer, "Contents()");
519         LoadString(Globals.hInstance, STID_SEARCH,buffer, sizeof(buffer));
520         MACRO_CreateButton("BTN_SEARCH", buffer, "Search()");
521         LoadString(Globals.hInstance, STID_BACK, buffer, sizeof(buffer));
522         MACRO_CreateButton("BTN_BACK", buffer, "Back()");
523         LoadString(Globals.hInstance, STID_HISTORY, buffer, sizeof(buffer));
524         MACRO_CreateButton("BTN_HISTORY", buffer, "History()");
525         LoadString(Globals.hInstance, STID_TOPICS, buffer, sizeof(buffer));
526         MACRO_CreateButton("BTN_TOPICS", buffer, "Finder()");
527     }
528
529     /* Initialize file specific pushbuttons */
530     if (!(wi->win_style & WS_POPUP) && page)
531     {
532         HLPFILE_MACRO  *macro;
533         for (macro = page->file->first_macro; macro; macro = macro->next)
534             MACRO_ExecuteMacro(macro->lpszMacro);
535
536         for (macro = page->first_macro; macro; macro = macro->next)
537             MACRO_ExecuteMacro(macro->lpszMacro);
538     }
539
540     /* Reuse existing window */
541     if (!bPopup)
542     {
543         for (oldwin = win->next; oldwin; oldwin = oldwin->next)
544         {
545             if (!lstrcmpi(oldwin->lpszName, wi->name))
546             {
547                 return WINHELP_ReuseWindow(win, oldwin, page, nCmdShow);
548             }
549         }
550         if (page)
551         {
552             win->histIndex = win->backIndex = 1;
553             win->history[0] = win->back[0] = page;
554             page->file->wRefCount += 2;
555             strcpy(wi->caption, page->file->lpszTitle);
556         }
557     }
558
559     hWnd = CreateWindow(bPopup ? TEXT_WIN_CLASS_NAME : MAIN_WIN_CLASS_NAME,
560                         wi->caption, 
561                         bPrimary ? WS_OVERLAPPEDWINDOW : wi->win_style,
562                         wi->origin.x, wi->origin.y, wi->size.cx, wi->size.cy,
563                         NULL, bPrimary ? LoadMenu(Globals.hInstance, MAKEINTRESOURCE(MAIN_MENU)) : 0,
564                         Globals.hInstance, win);
565
566     ShowWindow(hWnd, nCmdShow);
567     UpdateWindow(hWnd);
568
569     return TRUE;
570 }
571
572 /***********************************************************************
573  *
574  *           WINHELP_CreateHelpWindowByHash
575  */
576 BOOL WINHELP_CreateHelpWindowByHash(HLPFILE* hlpfile, LONG lHash, 
577                                     HLPFILE_WINDOWINFO* wi, int nCmdShow)
578 {
579     HLPFILE_PAGE*       page = NULL;
580
581     if (hlpfile)
582         page = lHash ? HLPFILE_PageByHash(hlpfile, lHash) : 
583             HLPFILE_Contents(hlpfile);
584     if (page) page->file->wRefCount++;
585     return WINHELP_CreateHelpWindow(page, wi, nCmdShow);
586 }
587
588 /***********************************************************************
589  *
590  *           WINHELP_MainWndProc
591  */
592 static LRESULT CALLBACK WINHELP_MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
593 {
594     WINHELP_WINDOW *win;
595     WINHELP_BUTTON *button;
596     RECT rect, button_box_rect;
597     INT  text_top, curPos, min, max, dy, keyDelta;
598
599     WINHELP_CheckPopup(msg);
600
601     switch (msg)
602     {
603     case WM_NCCREATE:
604         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
605         SetWindowLong(hWnd, 0, (LONG) win);
606         win->hMainWnd = hWnd;
607         break;
608
609     case WM_CREATE:
610         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
611
612         /* Create button box and text Window */
613         CreateWindow(BUTTON_BOX_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
614                      0, 0, 0, 0, hWnd, 0, Globals.hInstance, win);
615
616         CreateWindow(TEXT_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
617                      0, 0, 0, 0, hWnd, 0, Globals.hInstance, win);
618
619         /* Fall through */
620     case WM_USER:
621     case WM_WINDOWPOSCHANGED:
622         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
623         GetClientRect(hWnd, &rect);
624
625         /* Update button box and text Window */
626         SetWindowPos(win->hButtonBoxWnd, HWND_TOP,
627                      rect.left, rect.top,
628                      rect.right - rect.left,
629                      rect.bottom - rect.top, 0);
630
631         GetWindowRect(win->hButtonBoxWnd, &button_box_rect);
632         text_top = rect.top + button_box_rect.bottom - button_box_rect.top;
633
634         SetWindowPos(win->hTextWnd, HWND_TOP,
635                      rect.left, text_top,
636                      rect.right - rect.left,
637                      rect.bottom - text_top, 0);
638
639         break;
640
641     case WM_COMMAND:
642         Globals.active_win = win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
643         switch (wParam)
644         {
645             /* Menu FILE */
646         case MNID_FILE_OPEN:    MACRO_FileOpen();       break;
647         case MNID_FILE_PRINT:   MACRO_Print();          break;
648         case MNID_FILE_SETUP:   MACRO_PrinterSetup();   break;
649         case MNID_FILE_EXIT:    MACRO_Exit();           break;
650
651             /* Menu EDIT */
652         case MNID_EDIT_COPYDLG: MACRO_CopyDialog();     break;
653         case MNID_EDIT_ANNOTATE:MACRO_Annotate();       break;
654
655             /* Menu Bookmark */
656         case MNID_BKMK_DEFINE:  MACRO_BookmarkDefine(); break;
657
658             /* Menu Help */
659         case MNID_HELP_HELPON:  MACRO_HelpOn();         break;
660         case MNID_HELP_HELPTOP: MACRO_HelpOnTop();      break;
661         case MNID_HELP_ABOUT:   MACRO_About();          break;
662         case MNID_HELP_WINE:    ShellAbout(hWnd, "WINE", "Help", 0); break;
663
664         default:
665             /* Buttons */
666             for (button = win->first_button; button; button = button->next)
667                 if (wParam == button->wParam) break;
668             if (button)
669                 MACRO_ExecuteMacro(button->lpszMacro);
670             else
671                 WINHELP_MessageBoxIDS(STID_NOT_IMPLEMENTED, 0x121, MB_OK);
672             break;
673         }
674         break;
675     case WM_DESTROY:
676         if (Globals.hPopupWnd) DestroyWindow(Globals.hPopupWnd);
677         break;
678     case WM_COPYDATA:
679         return WINHELP_HandleCommand((HWND)wParam, lParam);
680
681     case WM_KEYDOWN:
682         keyDelta = 0;
683
684         switch (wParam)
685         {
686         case VK_UP:
687         case VK_DOWN:
688             keyDelta = GetSystemMetrics(SM_CXVSCROLL);
689             if (wParam == VK_UP)
690                 keyDelta = -keyDelta;
691
692         case VK_PRIOR:
693         case VK_NEXT:
694             win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
695             curPos = GetScrollPos(win->hTextWnd, SB_VERT);
696             GetScrollRange(win->hTextWnd, SB_VERT, &min, &max);
697
698             if (keyDelta == 0)
699             {            
700                 GetClientRect(win->hTextWnd, &rect);
701                 keyDelta = (rect.bottom - rect.top) / 2;
702                 if (wParam == VK_PRIOR)
703                     keyDelta = -keyDelta;
704             }
705
706             curPos += keyDelta;
707             if (curPos > max)
708                  curPos = max;
709             else if (curPos < min)
710                  curPos = min;
711
712             dy = GetScrollPos(win->hTextWnd, SB_VERT) - curPos;
713             SetScrollPos(win->hTextWnd, SB_VERT, curPos, TRUE);
714             ScrollWindow(win->hTextWnd, 0, dy, NULL, NULL);
715             UpdateWindow(win->hTextWnd);
716             return 0;
717
718         case VK_ESCAPE:
719             MACRO_Exit();
720             return 0;
721         }
722         break;
723     }
724     return DefWindowProc(hWnd, msg, wParam, lParam);
725 }
726
727 /***********************************************************************
728  *
729  *           WINHELP_ButtonBoxWndProc
730  */
731 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
732 {
733     WINDOWPOS      *winpos;
734     WINHELP_WINDOW *win;
735     WINHELP_BUTTON *button;
736     SIZE button_size;
737     INT  x, y;
738
739     WINHELP_CheckPopup(msg);
740
741     switch (msg)
742     {
743     case WM_NCCREATE:
744         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
745         SetWindowLong(hWnd, 0, (LONG) win);
746         win->hButtonBoxWnd = hWnd;
747         break;
748
749     case WM_WINDOWPOSCHANGING:
750         winpos = (WINDOWPOS*) lParam;
751         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
752
753         /* Update buttons */
754         button_size.cx = 0;
755         button_size.cy = 0;
756         for (button = win->first_button; button; button = button->next)
757         {
758             HDC  hDc;
759             SIZE textsize;
760             if (!button->hWnd)
761             {
762                 button->hWnd = CreateWindow(STRING_BUTTON, button->lpszName,
763                                             WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
764                                             0, 0, 0, 0,
765                                             hWnd, (HMENU) button->wParam,
766                                             Globals.hInstance, 0);
767                 if (button->hWnd) {
768                     if (Globals.button_proc == NULL)
769                         Globals.button_proc = (WNDPROC) GetWindowLongPtr(button->hWnd, GWLP_WNDPROC);
770                     SetWindowLongPtr(button->hWnd, GWLP_WNDPROC, (LONG_PTR) WINHELP_ButtonWndProc);
771                 }
772             }
773             hDc = GetDC(button->hWnd);
774             GetTextExtentPoint(hDc, button->lpszName,
775                                lstrlen(button->lpszName), &textsize);
776             ReleaseDC(button->hWnd, hDc);
777
778             button_size.cx = max(button_size.cx, textsize.cx + BUTTON_CX);
779             button_size.cy = max(button_size.cy, textsize.cy + BUTTON_CY);
780         }
781
782         x = 0;
783         y = 0;
784         for (button = win->first_button; button; button = button->next)
785         {
786             SetWindowPos(button->hWnd, HWND_TOP, x, y, button_size.cx, button_size.cy, 0);
787
788             if (x + 2 * button_size.cx <= winpos->cx)
789                 x += button_size.cx;
790             else
791                 x = 0, y += button_size.cy;
792         }
793         winpos->cy = y + (x ? button_size.cy : 0);
794         break;
795
796     case WM_COMMAND:
797         SendMessage(GetParent(hWnd), msg, wParam, lParam);
798         break;
799
800     case WM_KEYDOWN:
801         switch (wParam)
802         {
803         case VK_UP:
804         case VK_DOWN:
805         case VK_PRIOR:
806         case VK_NEXT:
807         case VK_ESCAPE:
808             return SendMessage(GetParent(hWnd), msg, wParam, lParam);
809         }
810         break;
811     }
812
813     return DefWindowProc(hWnd, msg, wParam, lParam);
814 }
815
816 /***********************************************************************
817  *
818  *           WINHELP_ButtonWndProc
819  */
820 static LRESULT CALLBACK WINHELP_ButtonWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
821 {
822     if (msg == WM_KEYDOWN)
823     {
824         switch (wParam)
825         {
826         case VK_UP:
827         case VK_DOWN:
828         case VK_PRIOR:
829         case VK_NEXT:
830         case VK_ESCAPE:
831             return SendMessage(GetParent(hWnd), msg, wParam, lParam);
832         }
833     }
834
835     return CallWindowProc(Globals.button_proc, hWnd, msg, wParam, lParam);
836 }
837
838 /***********************************************************************
839  *
840  *           WINHELP_TextWndProc
841  */
842 static LRESULT CALLBACK WINHELP_TextWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
843 {
844     WINHELP_WINDOW    *win;
845     WINHELP_LINE      *line;
846     WINHELP_LINE_PART *part;
847     WINDOWPOS         *winpos;
848     PAINTSTRUCT        ps;
849     HDC   hDc;
850     POINT mouse;
851     INT   scroll_pos;
852     HWND  hPopupWnd;
853     BOOL  bExit;
854
855     if (msg != WM_LBUTTONDOWN)
856         WINHELP_CheckPopup(msg);
857
858     switch (msg)
859     {
860     case WM_NCCREATE:
861         win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
862         SetWindowLong(hWnd, 0, (LONG) win);
863         win->hTextWnd = hWnd;
864         win->hBrush = CreateSolidBrush(win->info->sr_color);
865         if (win->info->win_style & WS_POPUP) Globals.hPopupWnd = win->hMainWnd = hWnd;
866         WINHELP_InitFonts(hWnd);
867         break;
868
869     case WM_CREATE:
870         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
871
872         /* Calculate vertical size and position of a popup window */
873         if (win->info->win_style & WS_POPUP)
874         {
875             POINT origin;
876             RECT old_window_rect;
877             RECT old_client_rect;
878             SIZE old_window_size;
879             SIZE old_client_size;
880             SIZE new_client_size;
881             SIZE new_window_size;
882
883             GetWindowRect(hWnd, &old_window_rect);
884             origin.x = old_window_rect.left;
885             origin.y = old_window_rect.top;
886             old_window_size.cx = old_window_rect.right  - old_window_rect.left;
887             old_window_size.cy = old_window_rect.bottom - old_window_rect.top;
888
889             GetClientRect(hWnd, &old_client_rect);
890             old_client_size.cx = old_client_rect.right  - old_client_rect.left;
891             old_client_size.cy = old_client_rect.bottom - old_client_rect.top;
892
893             new_client_size = old_client_size;
894             WINHELP_SplitLines(hWnd, &new_client_size);
895
896             if (origin.y + POPUP_YDISTANCE + new_client_size.cy <= GetSystemMetrics(SM_CYSCREEN))
897                 origin.y += POPUP_YDISTANCE;
898             else
899                 origin.y -= POPUP_YDISTANCE + new_client_size.cy;
900
901             new_window_size.cx = old_window_size.cx - old_client_size.cx + new_client_size.cx;
902             new_window_size.cy = old_window_size.cy - old_client_size.cy + new_client_size.cy;
903
904             win->hShadowWnd =
905                 CreateWindow(SHADOW_WIN_CLASS_NAME, "", WS_POPUP,
906                              origin.x + SHADOW_DX, origin.y + SHADOW_DY,
907                              new_window_size.cx, new_window_size.cy,
908                              0, 0, Globals.hInstance, 0);
909
910             SetWindowPos(hWnd, HWND_TOP, origin.x, origin.y,
911                          new_window_size.cx, new_window_size.cy,
912                          0);
913             SetWindowPos(win->hShadowWnd, hWnd, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
914             ShowWindow(win->hShadowWnd, SW_NORMAL);
915             SetActiveWindow(hWnd);
916         }
917         break;
918
919     case WM_WINDOWPOSCHANGED:
920         winpos = (WINDOWPOS*) lParam;
921
922         if (!(winpos->flags & SWP_NOSIZE)) WINHELP_SetupText(hWnd);
923         break;
924
925     case WM_MOUSEWHEEL:
926     {
927        int wheelDelta = 0;
928        UINT scrollLines = 3;
929        int curPos = GetScrollPos(hWnd, SB_VERT);
930        int min, max;
931
932        GetScrollRange(hWnd, SB_VERT, &min, &max);
933
934        SystemParametersInfo(SPI_GETWHEELSCROLLLINES,0, &scrollLines, 0);
935        if (wParam & (MK_SHIFT | MK_CONTROL))
936            return DefWindowProc(hWnd, msg, wParam, lParam);
937        wheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
938        if (abs(wheelDelta) >= WHEEL_DELTA && scrollLines) {
939            int dy;
940
941            curPos += wheelDelta;
942            if (curPos > max)
943                 curPos = max;
944            else if (curPos < min)
945                 curPos = min;
946
947            dy = GetScrollPos(hWnd, SB_VERT) - curPos;
948            SetScrollPos(hWnd, SB_VERT, curPos, TRUE);
949            ScrollWindow(hWnd, 0, dy, NULL, NULL);
950            UpdateWindow(hWnd);
951        }
952     }
953     break;
954
955     case WM_VSCROLL:
956     {
957         BOOL  update = TRUE;
958         RECT  rect;
959         INT   Min, Max;
960         INT   CurPos = GetScrollPos(hWnd, SB_VERT);
961         INT   dy;
962         
963         GetScrollRange(hWnd, SB_VERT, &Min, &Max);
964         GetClientRect(hWnd, &rect);
965
966         switch (wParam & 0xffff)
967         {
968         case SB_THUMBTRACK:
969         case SB_THUMBPOSITION: CurPos  = wParam >> 16;                   break;
970         case SB_TOP:           CurPos  = Min;                            break;
971         case SB_BOTTOM:        CurPos  = Max;                            break;
972         case SB_PAGEUP:        CurPos -= (rect.bottom - rect.top) / 2;   break;
973         case SB_PAGEDOWN:      CurPos += (rect.bottom - rect.top) / 2;   break;
974         case SB_LINEUP:        CurPos -= GetSystemMetrics(SM_CXVSCROLL); break;
975         case SB_LINEDOWN:      CurPos += GetSystemMetrics(SM_CXVSCROLL); break;
976         default: update = FALSE;
977         }
978         if (update)
979         {
980             if (CurPos > Max)
981                 CurPos = Max;
982             else if (CurPos < Min)
983                 CurPos = Min;
984             dy = GetScrollPos(hWnd, SB_VERT) - CurPos;
985             SetScrollPos(hWnd, SB_VERT, CurPos, TRUE);
986             ScrollWindow(hWnd, 0, dy, NULL, NULL);
987             UpdateWindow(hWnd);
988         }
989     }
990     break;
991
992     case WM_PAINT:
993         hDc = BeginPaint(hWnd, &ps);
994         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
995         scroll_pos = GetScrollPos(hWnd, SB_VERT);
996
997         /* No DPtoLP needed - MM_TEXT map mode */
998         if (ps.fErase) FillRect(hDc, &ps.rcPaint, win->hBrush);
999         for (line = win->first_line; line; line = line->next)
1000         {
1001             for (part = &line->first_part; part; part = part->next)
1002             {
1003                 switch (part->cookie)
1004                 {
1005                 case hlp_line_part_text:
1006                     SelectObject(hDc, part->u.text.hFont);
1007                     SetTextColor(hDc, part->u.text.color);
1008                     SetBkColor(hDc, win->info->sr_color);
1009                     TextOut(hDc, part->rect.left, part->rect.top - scroll_pos,
1010                             part->u.text.lpsText, part->u.text.wTextLen);
1011                     if (part->u.text.wUnderline)
1012                     {
1013                         HPEN    hPen;
1014
1015                         switch (part->u.text.wUnderline)
1016                         {
1017                         case 1: /* simple */
1018                         case 2: /* double */
1019                             hPen = CreatePen(PS_SOLID, 1, part->u.text.color);
1020                             break;
1021                         case 3: /* dotted */
1022                             hPen = CreatePen(PS_DOT, 1, part->u.text.color);
1023                             break;
1024                         default:
1025                             WINE_FIXME("Unknow underline type\n");
1026                             continue;
1027                         }
1028
1029                         SelectObject(hDc, hPen);
1030                         MoveToEx(hDc, part->rect.left, part->rect.bottom - scroll_pos - 1, NULL);
1031                         LineTo(hDc, part->rect.right, part->rect.bottom - scroll_pos - 1);
1032                         if (part->u.text.wUnderline == 2)
1033                         {
1034                             MoveToEx(hDc, part->rect.left, part->rect.bottom - scroll_pos + 1, NULL);
1035                             LineTo(hDc, part->rect.right, part->rect.bottom - scroll_pos + 1);
1036                         }
1037                         DeleteObject(hPen);
1038                     }
1039                     break;
1040                 case hlp_line_part_bitmap:
1041                     {
1042                         HDC hMemDC;
1043
1044                         hMemDC = CreateCompatibleDC(hDc);
1045                         SelectObject(hMemDC, part->u.bitmap.hBitmap);
1046                         BitBlt(hDc, part->rect.left, part->rect.top - scroll_pos,
1047                                part->rect.right - part->rect.left, part->rect.bottom - part->rect.top,
1048                                hMemDC, 0, 0, SRCCOPY);
1049                         DeleteDC(hMemDC);
1050                     }
1051                     break;
1052                 case hlp_line_part_metafile:
1053                     {
1054                         POINT   pt;
1055
1056                         SetViewportOrgEx(hDc, part->rect.left, part->rect.top - scroll_pos, &pt);
1057                         PlayMetaFile(hDc, part->u.metafile.hMetaFile);
1058                         SetViewportOrgEx(hDc, pt.x, pt.y, NULL);
1059                     }
1060                     break;
1061                 }
1062             }
1063         }
1064
1065         EndPaint(hWnd, &ps);
1066         break;
1067
1068     case WM_MOUSEMOVE:
1069         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1070
1071         if (WINHELP_IsOverLink(win, wParam, lParam))
1072             SetCursor(win->hHandCur); /* set to hand pointer cursor to indicate a link */
1073         else
1074             SetCursor(win->hArrowCur); /* set to hand pointer cursor to indicate a link */
1075
1076         break;
1077
1078     case WM_LBUTTONDOWN:
1079         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1080
1081         hPopupWnd = Globals.hPopupWnd;
1082         Globals.hPopupWnd = 0;
1083
1084         part = WINHELP_IsOverLink(win, wParam, lParam);
1085         if (part)
1086         {
1087             HLPFILE*            hlpfile;
1088             HLPFILE_WINDOWINFO* wi;
1089
1090             mouse.x = LOWORD(lParam);
1091             mouse.y = HIWORD(lParam);
1092
1093             if (part->link) switch (part->link->cookie)
1094             {
1095             case hlp_link_link:
1096                 hlpfile = WINHELP_LookupHelpFile(part->link->lpszString);
1097                 if (part->link->window == -1)
1098                     wi = win->info;
1099                 else if ((part->link->window >= 0) && (part->link->window < hlpfile->numWindows))
1100                     wi = &hlpfile->windows[part->link->window];
1101                 else
1102                 {
1103                     WINE_WARN("link to window %d/%d\n", part->link->window, hlpfile->numWindows);
1104                     break;
1105                 }
1106                 WINHELP_CreateHelpWindowByHash(hlpfile, part->link->lHash, wi,
1107                                                SW_NORMAL);
1108                 break;
1109             case hlp_link_popup:
1110                 hlpfile = WINHELP_LookupHelpFile(part->link->lpszString);
1111                 WINHELP_CreateHelpWindowByHash(hlpfile, part->link->lHash, 
1112                                                WINHELP_GetPopupWindowInfo(hlpfile, hWnd, &mouse),
1113                                                SW_NORMAL);
1114                 break;
1115             case hlp_link_macro:
1116                 MACRO_ExecuteMacro(part->link->lpszString);
1117                 break;
1118             default:
1119                 WINE_FIXME("Unknown link cookie %d\n", part->link->cookie);
1120             }
1121         }
1122
1123         if (hPopupWnd)
1124             DestroyWindow(hPopupWnd);
1125         break;
1126
1127     case WM_NCDESTROY:
1128         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1129
1130         if (hWnd == Globals.hPopupWnd) Globals.hPopupWnd = 0;
1131
1132         bExit = (Globals.wVersion >= 4 && !lstrcmpi(win->lpszName, "main"));
1133         DeleteObject(win->hBrush);
1134
1135         WINHELP_DeleteWindow(win);
1136
1137         if (bExit) MACRO_Exit();
1138
1139         if (!Globals.win_list)
1140             PostQuitMessage(0);
1141         break;
1142     }
1143
1144     return DefWindowProc(hWnd, msg, wParam, lParam);
1145 }
1146
1147 /******************************************************************
1148  *              WINHELP_HistoryWndProc
1149  *
1150  *
1151  */
1152 static LRESULT CALLBACK WINHELP_HistoryWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1153 {
1154     WINHELP_WINDOW*     win;
1155     PAINTSTRUCT         ps;
1156     HDC                 hDc;
1157     TEXTMETRIC          tm;
1158     unsigned int        i;
1159     RECT                r;
1160
1161     switch (msg)
1162     {
1163     case WM_NCCREATE:
1164         win = (WINHELP_WINDOW*)((LPCREATESTRUCT)lParam)->lpCreateParams;
1165         SetWindowLong(hWnd, 0, (LONG)win);
1166         win->hHistoryWnd = hWnd;
1167         break;
1168     case WM_CREATE:
1169         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1170         hDc = GetDC(hWnd);
1171         GetTextMetrics(hDc, &tm);
1172         GetWindowRect(hWnd, &r);
1173
1174         r.right = r.left + 30 * tm.tmAveCharWidth;
1175         r.bottom = r.top + (sizeof(win->history) / sizeof(win->history[0])) * tm.tmHeight;
1176         AdjustWindowRect(&r, GetWindowLong(hWnd, GWL_STYLE), FALSE);
1177         if (r.left < 0) {r.right -= r.left; r.left = 0;}
1178         if (r.top < 0) {r.bottom -= r.top; r.top = 0;}
1179
1180         MoveWindow(hWnd, r.left, r.top, r.right, r.bottom, TRUE);
1181         ReleaseDC(hWnd, hDc);
1182         break;
1183     case WM_LBUTTONDOWN:
1184         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1185         hDc = GetDC(hWnd);
1186         GetTextMetrics(hDc, &tm);
1187         i = HIWORD(lParam) / tm.tmHeight;
1188         if (i < win->histIndex)
1189             WINHELP_CreateHelpWindow(win->history[i], win->info, SW_SHOW);
1190         ReleaseDC(hWnd, hDc);
1191         break;
1192     case WM_PAINT:
1193         hDc = BeginPaint(hWnd, &ps);
1194         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1195         GetTextMetrics(hDc, &tm);
1196
1197         for (i = 0; i < win->histIndex; i++)
1198         {
1199             TextOut(hDc, 0, i * tm.tmHeight, win->history[i]->lpszTitle, 
1200                     strlen(win->history[i]->lpszTitle));
1201         }
1202         EndPaint(hWnd, &ps);
1203         break;
1204     case WM_DESTROY:
1205         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1206         if (hWnd == win->hHistoryWnd)
1207             win->hHistoryWnd = 0;
1208         break;
1209     }
1210     return DefWindowProc(hWnd, msg, wParam, lParam);
1211 }
1212
1213 /***********************************************************************
1214  *
1215  *           WINHELP_ShadowWndProc
1216  */
1217 static LRESULT CALLBACK WINHELP_ShadowWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1218 {
1219     WINHELP_CheckPopup(msg);
1220     return DefWindowProc(hWnd, msg, wParam, lParam);
1221 }
1222
1223 /***********************************************************************
1224  *
1225  *           SetupText
1226  */
1227 static void WINHELP_SetupText(HWND hWnd)
1228 {
1229     HDC  hDc = GetDC(hWnd);
1230     RECT rect;
1231     SIZE newsize;
1232
1233     ShowScrollBar(hWnd, SB_VERT, FALSE);
1234     if (!WINHELP_SplitLines(hWnd, NULL))
1235     {
1236         ShowScrollBar(hWnd, SB_VERT, TRUE);
1237         GetClientRect(hWnd, &rect);
1238
1239         WINHELP_SplitLines(hWnd, &newsize);
1240         SetScrollRange(hWnd, SB_VERT, 0, rect.top + newsize.cy - rect.bottom, TRUE);
1241     }
1242     else
1243     {
1244         SetScrollPos(hWnd, SB_VERT, 0, FALSE);
1245         SetScrollRange(hWnd, SB_VERT, 0, 0, FALSE);
1246     }
1247
1248     ReleaseDC(hWnd, hDc);
1249 }
1250
1251 /***********************************************************************
1252  *
1253  *           WINHELP_AppendText
1254  */
1255 static BOOL WINHELP_AppendText(WINHELP_LINE ***linep, WINHELP_LINE_PART ***partp,
1256                                LPSIZE space, LPSIZE textsize,
1257                                INT *line_ascent, INT ascent,
1258                                LPCSTR text, UINT textlen,
1259                                HFONT font, COLORREF color, HLPFILE_LINK *link,
1260                                unsigned underline)
1261 {
1262     WINHELP_LINE      *line;
1263     WINHELP_LINE_PART *part;
1264     LPSTR ptr;
1265
1266     if (!*partp) /* New line */
1267     {
1268         *line_ascent  = ascent;
1269
1270         line = HeapAlloc(GetProcessHeap(), 0,
1271                          sizeof(WINHELP_LINE) + textlen);
1272         if (!line) return FALSE;
1273
1274         line->next    = 0;
1275         part          = &line->first_part;
1276         ptr           = (char*)line + sizeof(WINHELP_LINE);
1277
1278         line->rect.top    = (**linep ? (**linep)->rect.bottom : 0) + space->cy;
1279         line->rect.bottom = line->rect.top;
1280         line->rect.left   = space->cx;
1281         line->rect.right  = space->cx;
1282
1283         if (**linep) *linep = &(**linep)->next;
1284         **linep = line;
1285         space->cy = 0;
1286     }
1287     else /* Same line */
1288     {
1289         line = **linep;
1290
1291         if (*line_ascent < ascent)
1292         {
1293             WINHELP_LINE_PART *p;
1294             for (p = &line->first_part; p; p = p->next)
1295             {
1296                 p->rect.top    += ascent - *line_ascent;
1297                 p->rect.bottom += ascent - *line_ascent;
1298             }
1299             line->rect.bottom += ascent - *line_ascent;
1300             *line_ascent = ascent;
1301         }
1302
1303         part = HeapAlloc(GetProcessHeap(), 0,
1304                          sizeof(WINHELP_LINE_PART) + textlen);
1305         if (!part) return FALSE;
1306         **partp = part;
1307         ptr     = (char*)part + sizeof(WINHELP_LINE_PART);
1308     }
1309
1310     memcpy(ptr, text, textlen);
1311     part->cookie            = hlp_line_part_text;
1312     part->rect.left         = line->rect.right + (*partp ? space->cx : 0);
1313     part->rect.right        = part->rect.left + textsize->cx;
1314     line->rect.right        = part->rect.right;
1315     part->rect.top          =
1316         ((*partp) ? line->rect.top : line->rect.bottom) + *line_ascent - ascent;
1317     part->rect.bottom       = part->rect.top + textsize->cy;
1318     line->rect.bottom       = max(line->rect.bottom, part->rect.bottom);
1319     part->u.text.lpsText    = ptr;
1320     part->u.text.wTextLen   = textlen;
1321     part->u.text.hFont      = font;
1322     part->u.text.color      = color;
1323     part->u.text.wUnderline = underline;
1324
1325     WINE_TRACE("Appended text '%*.*s'[%d] @ (%d,%d-%d,%d)\n",
1326                part->u.text.wTextLen,
1327                part->u.text.wTextLen,
1328                part->u.text.lpsText,
1329                part->u.text.wTextLen,
1330                part->rect.left, part->rect.top, part->rect.right, part->rect.bottom);
1331
1332     part->link = link;
1333     if (link) link->wRefCount++;
1334
1335     part->next          = 0;
1336     *partp              = &part->next;
1337
1338     space->cx = 0;
1339
1340     return TRUE;
1341 }
1342
1343 /***********************************************************************
1344  *
1345  *           WINHELP_AppendGfxObject
1346  */
1347 static WINHELP_LINE_PART* WINHELP_AppendGfxObject(WINHELP_LINE ***linep, WINHELP_LINE_PART ***partp,
1348                                                   LPSIZE space, LPSIZE gfxSize,
1349                                                   HLPFILE_LINK *link, unsigned pos)
1350 {
1351     WINHELP_LINE      *line;
1352     WINHELP_LINE_PART *part;
1353     LPSTR              ptr;
1354
1355     if (!*partp || pos == 1) /* New line */
1356     {
1357         line = HeapAlloc(GetProcessHeap(), 0, sizeof(WINHELP_LINE));
1358         if (!line) return NULL;
1359
1360         line->next    = NULL;
1361         part          = &line->first_part;
1362
1363         line->rect.top    = (**linep ? (**linep)->rect.bottom : 0) + space->cy;
1364         line->rect.bottom = line->rect.top;
1365         line->rect.left   = space->cx;
1366         line->rect.right  = space->cx;
1367
1368         if (**linep) *linep = &(**linep)->next;
1369         **linep = line;
1370         space->cy = 0;
1371         ptr = (char*)line + sizeof(WINHELP_LINE);
1372     }
1373     else /* Same line */
1374     {
1375         if (pos == 2) WINE_FIXME("Left alignment not handled\n");
1376         line = **linep;
1377
1378         part = HeapAlloc(GetProcessHeap(), 0, sizeof(WINHELP_LINE_PART));
1379         if (!part) return NULL;
1380         **partp = part;
1381         ptr = (char*)part + sizeof(WINHELP_LINE_PART);
1382     }
1383
1384     /* part->cookie should be set by caller (image or metafile) */
1385     part->rect.left       = line->rect.right + (*partp ? space->cx : 0);
1386     part->rect.right      = part->rect.left + gfxSize->cx;
1387     line->rect.right      = part->rect.right;
1388     part->rect.top        = (*partp) ? line->rect.top : line->rect.bottom;
1389     part->rect.bottom     = part->rect.top + gfxSize->cy;
1390     line->rect.bottom     = max(line->rect.bottom, part->rect.bottom);
1391
1392     WINE_TRACE("Appended gfx @ (%d,%d-%d,%d)\n",
1393                part->rect.left, part->rect.top, part->rect.right, part->rect.bottom);
1394
1395     part->link = link;
1396     if (link) link->wRefCount++;
1397
1398     part->next            = NULL;
1399     *partp                = &part->next;
1400
1401     space->cx = 0;
1402
1403     return part;
1404 }
1405
1406
1407 /***********************************************************************
1408  *
1409  *           WINHELP_SplitLines
1410  */
1411 static BOOL WINHELP_SplitLines(HWND hWnd, LPSIZE newsize)
1412 {
1413     WINHELP_WINDOW     *win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1414     HLPFILE_PARAGRAPH  *p;
1415     WINHELP_LINE      **line = &win->first_line;
1416     WINHELP_LINE_PART **part = 0;
1417     INT                 line_ascent = 0;
1418     SIZE                space;
1419     RECT                rect;
1420     HDC                 hDc;
1421
1422     if (newsize) newsize->cx = newsize->cy = 0;
1423
1424     if (!win->page) return TRUE;
1425
1426     WINHELP_DeleteLines(win);
1427
1428     GetClientRect(hWnd, &rect);
1429
1430     rect.top    += INTERNAL_BORDER_WIDTH;
1431     rect.left   += INTERNAL_BORDER_WIDTH;
1432     rect.right  -= INTERNAL_BORDER_WIDTH;
1433     rect.bottom -= INTERNAL_BORDER_WIDTH;
1434
1435     space.cy = rect.top;
1436     space.cx = rect.left;
1437
1438     hDc = GetDC(hWnd);
1439
1440     for (p = win->page->first_paragraph; p; p = p->next)
1441     {
1442         switch (p->cookie)
1443         {
1444         case para_normal_text:
1445         case para_debug_text:
1446             {
1447                 TEXTMETRIC tm;
1448                 SIZE textsize    = {0, 0};
1449                 LPCSTR text      = p->u.text.lpszText;
1450                 UINT indent      = 0;
1451                 UINT len         = strlen(text);
1452                 unsigned underline = 0;
1453
1454                 HFONT hFont = 0;
1455                 COLORREF color = RGB(0, 0, 0);
1456
1457                 if (p->u.text.wFont < win->page->file->numFonts)
1458                 {
1459                     HLPFILE*    hlpfile = win->page->file;
1460
1461                     if (!hlpfile->fonts[p->u.text.wFont].hFont)
1462                         hlpfile->fonts[p->u.text.wFont].hFont = CreateFontIndirect(&hlpfile->fonts[p->u.text.wFont].LogFont);
1463                     hFont = hlpfile->fonts[p->u.text.wFont].hFont;
1464                     color = hlpfile->fonts[p->u.text.wFont].color;
1465                 }
1466                 else
1467                 {
1468                     UINT  wFont = (p->u.text.wFont < win->fonts_len) ? p->u.text.wFont : 0;
1469
1470                     hFont = win->fonts[wFont];
1471                 }
1472
1473                 if (p->link && p->link->bClrChange)
1474                 {
1475                     underline = (p->link->cookie == hlp_link_popup) ? 3 : 1;
1476                     color = RGB(0, 0x80, 0);
1477                 }
1478                 if (p->cookie == para_debug_text) color = RGB(0xff, 0, 0);
1479
1480                 SelectObject(hDc, hFont);
1481
1482                 GetTextMetrics(hDc, &tm);
1483
1484                 if (p->u.text.wIndent)
1485                 {
1486                     indent = p->u.text.wIndent * 5 * tm.tmAveCharWidth;
1487                     if (!part)
1488                         space.cx = rect.left + indent - 2 * tm.tmAveCharWidth;
1489                 }
1490
1491                 if (p->u.text.wVSpace)
1492                 {
1493                     part = 0;
1494                     space.cx = rect.left + indent;
1495                     space.cy += (p->u.text.wVSpace - 1) * tm.tmHeight;
1496                 }
1497
1498                 if (p->u.text.wHSpace)
1499                 {
1500                     space.cx += p->u.text.wHSpace * 2 * tm.tmAveCharWidth;
1501                 }
1502
1503                 WINE_TRACE("splitting text %s\n", text);
1504
1505                 while (len)
1506                 {
1507                     INT free_width = rect.right - (part ? (*line)->rect.right : rect.left) - space.cx;
1508                     UINT low = 0, curr = len, high = len, textlen = 0;
1509
1510                     if (free_width > 0)
1511                     {
1512                         while (1)
1513                         {
1514                             GetTextExtentPoint(hDc, text, curr, &textsize);
1515
1516                             if (textsize.cx <= free_width) low = curr;
1517                             else high = curr;
1518
1519                             if (high <= low + 1) break;
1520
1521                             if (textsize.cx) curr = (curr * free_width) / textsize.cx;
1522                             if (curr <= low) curr = low + 1;
1523                             else if (curr >= high) curr = high - 1;
1524                         }
1525                         textlen = low;
1526                         while (textlen && text[textlen] && text[textlen] != ' ') textlen--;
1527                     }
1528                     if (!part && !textlen) textlen = max(low, 1);
1529
1530                     if (free_width <= 0 || !textlen)
1531                     {
1532                         part = 0;
1533                         space.cx = rect.left + indent;
1534                         space.cx = min(space.cx, rect.right - rect.left - 1);
1535                         continue;
1536                     }
1537
1538                     WINE_TRACE("\t => %d %*s\n", textlen, textlen, text);
1539
1540                     if (!WINHELP_AppendText(&line, &part, &space, &textsize,
1541                                             &line_ascent, tm.tmAscent,
1542                                             text, textlen, hFont, color, p->link, underline) ||
1543                         (!newsize && (*line)->rect.bottom > rect.bottom))
1544                     {
1545                         ReleaseDC(hWnd, hDc);
1546                         return FALSE;
1547                     }
1548
1549                     if (newsize)
1550                         newsize->cx = max(newsize->cx, (*line)->rect.right + INTERNAL_BORDER_WIDTH);
1551
1552                     len -= textlen;
1553                     text += textlen;
1554                     if (text[0] == ' ') text++, len--;
1555                 }
1556             }
1557             break;
1558         case para_bitmap:
1559         case para_metafile:
1560             {
1561                 SIZE                    gfxSize;
1562                 INT                     free_width;
1563                 WINHELP_LINE_PART*      ref_part;
1564
1565                 if (p->u.gfx.pos & 0x8000)
1566                 {
1567                     space.cx = rect.left;
1568                     if (*line)
1569                         space.cy += (*line)->rect.bottom - (*line)->rect.top;
1570                     part = 0;
1571                 }
1572
1573                 if (p->cookie == para_bitmap)
1574                 {
1575                     DIBSECTION              dibs;
1576                     
1577                     GetObject(p->u.gfx.u.bmp.hBitmap, sizeof(dibs), &dibs);
1578                     gfxSize.cx = dibs.dsBm.bmWidth;
1579                     gfxSize.cy = dibs.dsBm.bmHeight;
1580                 }
1581                 else gfxSize = p->u.gfx.u.mf.mfSize;
1582                     
1583                 free_width = rect.right - ((part && *line) ? (*line)->rect.right : rect.left) - space.cx;
1584                 if (free_width <= 0)
1585                 {
1586                     part = NULL;
1587                     space.cx = rect.left;
1588                     space.cx = min(space.cx, rect.right - rect.left - 1);
1589                 }
1590                 ref_part = WINHELP_AppendGfxObject(&line, &part, &space, &gfxSize,
1591                                                    p->link, p->u.gfx.pos);
1592                 if (!ref_part || (!newsize && (*line)->rect.bottom > rect.bottom))
1593                 {
1594                     return FALSE;
1595                 }
1596                 if (p->cookie == para_bitmap)
1597                 {
1598                     ref_part->cookie = hlp_line_part_bitmap;
1599                     ref_part->u.bitmap.hBitmap = p->u.gfx.u.bmp.hBitmap;
1600                 }
1601                 else
1602                 {
1603                     ref_part->cookie = hlp_line_part_metafile;
1604                     ref_part->u.metafile.hMetaFile = p->u.gfx.u.mf.hMetaFile;
1605                 }
1606             }
1607             break;
1608         }
1609     }
1610
1611     if (newsize)
1612         newsize->cy = (*line)->rect.bottom + INTERNAL_BORDER_WIDTH;
1613
1614     ReleaseDC(hWnd, hDc);
1615     return TRUE;
1616 }
1617
1618 /***********************************************************************
1619  *
1620  *           WINHELP_CheckPopup
1621  */
1622 static void WINHELP_CheckPopup(UINT msg)
1623 {
1624     if (!Globals.hPopupWnd) return;
1625
1626     switch (msg)
1627     {
1628     case WM_COMMAND:
1629     case WM_LBUTTONDOWN:
1630     case WM_MBUTTONDOWN:
1631     case WM_RBUTTONDOWN:
1632     case WM_NCLBUTTONDOWN:
1633     case WM_NCMBUTTONDOWN:
1634     case WM_NCRBUTTONDOWN:
1635         DestroyWindow(Globals.hPopupWnd);
1636         Globals.hPopupWnd = 0;
1637     }
1638 }
1639
1640 /***********************************************************************
1641  *
1642  *           WINHELP_DeleteLines
1643  */
1644 static void WINHELP_DeleteLines(WINHELP_WINDOW *win)
1645 {
1646     WINHELP_LINE      *line, *next_line;
1647     WINHELP_LINE_PART *part, *next_part;
1648     for (line = win->first_line; line; line = next_line)
1649     {
1650         next_line = line->next;
1651         for (part = &line->first_part; part; part = next_part)
1652         {
1653             next_part = part->next;
1654             HLPFILE_FreeLink(part->link);
1655             HeapFree(GetProcessHeap(), 0, part);
1656         }
1657     }
1658     win->first_line = 0;
1659 }
1660
1661 /***********************************************************************
1662  *
1663  *           WINHELP_DeleteWindow
1664  */
1665 static void WINHELP_DeleteWindow(WINHELP_WINDOW* win)
1666 {
1667     WINHELP_WINDOW**    w;
1668     unsigned int        i;
1669     WINHELP_BUTTON*     b;
1670     WINHELP_BUTTON*     bp;
1671
1672     for (w = &Globals.win_list; *w; w = &(*w)->next)
1673     {
1674         if (*w == win)
1675         {
1676             *w = win->next;
1677             break;
1678         }
1679     }
1680
1681     for (b = win->first_button; b; b = bp)
1682     {
1683         DestroyWindow(b->hWnd);
1684         bp = b->next;
1685         HeapFree(GetProcessHeap(), 0, b);
1686     }
1687
1688     if (win->hShadowWnd) DestroyWindow(win->hShadowWnd);
1689     if (win->hHistoryWnd) DestroyWindow(win->hHistoryWnd);
1690
1691     for (i = 0; i < win->histIndex; i++)
1692     {
1693         HLPFILE_FreeHlpFile(win->history[i]->file);
1694     }
1695
1696     for (i = 0; i < win->backIndex; i++)
1697         HLPFILE_FreeHlpFile(win->back[i]->file);
1698
1699     if (win->page) HLPFILE_FreeHlpFile(win->page->file);
1700     WINHELP_DeleteLines(win);
1701     HeapFree(GetProcessHeap(), 0, win);
1702 }
1703
1704 /***********************************************************************
1705  *
1706  *           WINHELP_InitFonts
1707  */
1708 static void WINHELP_InitFonts(HWND hWnd)
1709 {
1710     WINHELP_WINDOW *win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1711     LOGFONT logfontlist[] = {
1712         {-10, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1713         {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1714         {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1715         {-12, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1716         {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1717         {-10, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1718         { -8, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"}};
1719 #define FONTS_LEN (sizeof(logfontlist)/sizeof(*logfontlist))
1720
1721     static HFONT fonts[FONTS_LEN];
1722     static BOOL init = 0;
1723
1724     win->fonts_len = FONTS_LEN;
1725     win->fonts = fonts;
1726
1727     if (!init)
1728     {
1729         UINT i;
1730
1731         for (i = 0; i < FONTS_LEN; i++)
1732         {
1733             fonts[i] = CreateFontIndirect(&logfontlist[i]);
1734         }
1735
1736         init = 1;
1737     }
1738 }
1739
1740 /***********************************************************************
1741  *
1742  *           WINHELP_MessageBoxIDS
1743  */
1744 INT WINHELP_MessageBoxIDS(UINT ids_text, UINT ids_title, WORD type)
1745 {
1746     CHAR text[MAX_STRING_LEN];
1747     CHAR title[MAX_STRING_LEN];
1748
1749     LoadString(Globals.hInstance, ids_text, text, sizeof(text));
1750     LoadString(Globals.hInstance, ids_title, title, sizeof(title));
1751
1752     return MessageBox(0, text, title, type);
1753 }
1754
1755 /***********************************************************************
1756  *
1757  *           MAIN_MessageBoxIDS_s
1758  */
1759 INT WINHELP_MessageBoxIDS_s(UINT ids_text, LPCSTR str, UINT ids_title, WORD type)
1760 {
1761     CHAR text[MAX_STRING_LEN];
1762     CHAR title[MAX_STRING_LEN];
1763     CHAR newtext[MAX_STRING_LEN + MAX_PATHNAME_LEN];
1764
1765     LoadString(Globals.hInstance, ids_text, text, sizeof(text));
1766     LoadString(Globals.hInstance, ids_title, title, sizeof(title));
1767     wsprintf(newtext, text, str);
1768
1769     return MessageBox(0, newtext, title, type);
1770 }
1771
1772 /******************************************************************
1773  *              WINHELP_IsOverLink
1774  *
1775  *
1776  */
1777 WINHELP_LINE_PART* WINHELP_IsOverLink(WINHELP_WINDOW* win, WPARAM wParam, LPARAM lParam)
1778 {
1779     POINT mouse;
1780     WINHELP_LINE      *line;
1781     WINHELP_LINE_PART *part;
1782     int scroll_pos = GetScrollPos(win->hTextWnd, SB_VERT);
1783
1784     mouse.x = LOWORD(lParam);
1785     mouse.y = HIWORD(lParam);
1786     for (line = win->first_line; line; line = line->next)
1787     {
1788         for (part = &line->first_part; part; part = part->next)
1789         {
1790             if (part->link && 
1791                 part->link->lpszString &&
1792                 part->rect.left   <= mouse.x &&
1793                 part->rect.right  >= mouse.x &&
1794                 part->rect.top    <= mouse.y + scroll_pos &&
1795                 part->rect.bottom >= mouse.y + scroll_pos)
1796             {
1797                 return part;
1798             }
1799         }
1800     }
1801
1802     return NULL;
1803 }