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