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