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