Make it easy to use something other than wrc to compile resources.
[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(WINHELP_WINDOW*, WPARAM, 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_bitmap:
856                     {
857                         HDC hMemDC;
858
859                         hMemDC = CreateCompatibleDC(hDc);
860                         SelectObject(hMemDC, part->u.bitmap.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                 case hlp_line_part_metafile:
868                     {
869                         POINT   pt;
870
871                         SetViewportOrgEx(hDc, part->rect.left, part->rect.top - scroll_pos, &pt);
872                         PlayMetaFile(hDc, part->u.metafile.hMetaFile);
873                         SetViewportOrgEx(hDc, pt.x, pt.y, NULL);
874                     }
875                     break;
876                 }
877             }
878         }
879
880         EndPaint(hWnd, &ps);
881         break;
882
883     case WM_MOUSEMOVE:
884         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
885
886         if (WINHELP_IsOverLink(win, wParam, lParam))
887             SetCursor(win->hHandCur); /* set to hand pointer cursor to indicate a link */
888         else
889             SetCursor(win->hArrowCur); /* set to hand pointer cursor to indicate a link */
890
891         break;
892
893     case WM_LBUTTONDOWN:
894         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
895
896         hPopupWnd = Globals.hPopupWnd;
897         Globals.hPopupWnd = 0;
898
899         part = WINHELP_IsOverLink(win, wParam, lParam);
900         if (part)
901         {
902             HLPFILE*            hlpfile;
903             HLPFILE_WINDOWINFO* wi;
904
905             mouse.x = LOWORD(lParam);
906             mouse.y = HIWORD(lParam);
907
908             if (part->link) switch (part->link->cookie)
909             {
910             case hlp_link_link:
911                 hlpfile = WINHELP_LookupHelpFile(part->link->lpszString);
912                 if (part->link->window == -1)
913                     wi = win->info;
914                 else if (part->link->window < hlpfile->numWindows)
915                     wi = &hlpfile->windows[part->link->window];
916                 else
917                 {
918                     WINE_WARN("link to window %d/%d\n", part->link->window, hlpfile->numWindows);
919                     break;
920                 }
921                 WINHELP_CreateHelpWindowByHash(hlpfile, part->link->lHash, wi,
922                                                SW_NORMAL);
923                 break;
924             case hlp_link_popup:
925                 hlpfile = WINHELP_LookupHelpFile(part->link->lpszString);
926                 WINHELP_CreateHelpWindowByHash(hlpfile, part->link->lHash, 
927                                                WINHELP_GetPopupWindowInfo(hlpfile, hWnd, &mouse),
928                                                SW_NORMAL);
929                 break;
930             case hlp_link_macro:
931                 MACRO_ExecuteMacro(part->link->lpszString);
932                 break;
933             default:
934                 WINE_FIXME("Unknown link cookie %d\n", part->link->cookie);
935             }
936         }
937
938         if (hPopupWnd)
939             DestroyWindow(hPopupWnd);
940         break;
941
942     case WM_NCDESTROY:
943         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
944
945         if (hWnd == Globals.hPopupWnd) Globals.hPopupWnd = 0;
946
947         bExit = (Globals.wVersion >= 4 && !lstrcmpi(win->lpszName, "main"));
948
949         WINHELP_DeleteWindow(win);
950
951         if (bExit) MACRO_Exit();
952
953         if (!Globals.win_list)
954             PostQuitMessage(0);
955         break;
956     }
957
958     return DefWindowProc(hWnd, msg, wParam, lParam);
959 }
960
961 /******************************************************************
962  *              WINHELP_HistoryWndProc
963  *
964  *
965  */
966 static LRESULT CALLBACK WINHELP_HistoryWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
967 {
968     WINHELP_WINDOW*     win;
969     PAINTSTRUCT         ps;
970     HDC                 hDc;
971     TEXTMETRIC          tm;
972     int                 i;
973     RECT                r;
974
975     switch (msg)
976     {
977     case WM_NCCREATE:
978         win = (WINHELP_WINDOW*)((LPCREATESTRUCT)lParam)->lpCreateParams;
979         SetWindowLong(hWnd, 0, (LONG)win);
980         win->hHistoryWnd = hWnd;
981         break;
982     case WM_CREATE:
983         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
984         hDc = GetDC(hWnd);
985         GetTextMetrics(hDc, &tm);
986         GetWindowRect(hWnd, &r);
987
988         r.right = r.left + 30 * tm.tmAveCharWidth;
989         r.bottom = r.top + (sizeof(win->history) / sizeof(win->history[0])) * tm.tmHeight;
990         AdjustWindowRect(&r, GetWindowLong(hWnd, GWL_STYLE), FALSE);
991         if (r.left < 0) {r.right -= r.left; r.left = 0;}
992         if (r.top < 0) {r.bottom -= r.top; r.top = 0;}
993
994         MoveWindow(hWnd, r.left, r.top, r.right, r.bottom, TRUE);
995         ReleaseDC(hWnd, hDc);
996         break;
997     case WM_LBUTTONDOWN:
998         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
999         hDc = GetDC(hWnd);
1000         GetTextMetrics(hDc, &tm);
1001         i = HIWORD(lParam) / tm.tmHeight;
1002         if (i < win->histIndex)
1003             WINHELP_CreateHelpWindow(win->history[i], win->info, SW_SHOW);
1004         ReleaseDC(hWnd, hDc);
1005         break;
1006     case WM_PAINT:
1007         hDc = BeginPaint(hWnd, &ps);
1008         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1009         GetTextMetrics(hDc, &tm);
1010
1011         for (i = 0; i < win->histIndex; i++)
1012         {
1013             TextOut(hDc, 0, i * tm.tmHeight, win->history[i]->lpszTitle, 
1014                     strlen(win->history[i]->lpszTitle));
1015         }
1016         EndPaint(hWnd, &ps);
1017         break;
1018     case WM_DESTROY:
1019         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1020         if (hWnd == win->hHistoryWnd)
1021             win->hHistoryWnd = 0;
1022         break;
1023     }
1024     return DefWindowProc(hWnd, msg, wParam, lParam);
1025 }
1026
1027 /***********************************************************************
1028  *
1029  *           SetupText
1030  */
1031 static void WINHELP_SetupText(HWND hWnd)
1032 {
1033     HDC  hDc = GetDC(hWnd);
1034     RECT rect;
1035     SIZE newsize;
1036
1037     ShowScrollBar(hWnd, SB_VERT, FALSE);
1038     if (!WINHELP_SplitLines(hWnd, NULL))
1039     {
1040         ShowScrollBar(hWnd, SB_VERT, TRUE);
1041         GetClientRect(hWnd, &rect);
1042
1043         WINHELP_SplitLines(hWnd, &newsize);
1044         SetScrollRange(hWnd, SB_VERT, 0, rect.top + newsize.cy - rect.bottom, TRUE);
1045     }
1046     else SetScrollPos(hWnd, SB_VERT, 0, FALSE);
1047
1048     ReleaseDC(hWnd, hDc);
1049 }
1050
1051 /***********************************************************************
1052  *
1053  *           WINHELP_AppendText
1054  */
1055 static BOOL WINHELP_AppendText(WINHELP_LINE ***linep, WINHELP_LINE_PART ***partp,
1056                                LPSIZE space, LPSIZE textsize,
1057                                INT *line_ascent, INT ascent,
1058                                LPCSTR text, UINT textlen,
1059                                HFONT font, COLORREF color, HLPFILE_LINK *link,
1060                                unsigned underline)
1061 {
1062     WINHELP_LINE      *line;
1063     WINHELP_LINE_PART *part;
1064     LPSTR ptr;
1065
1066     if (!*partp) /* New line */
1067     {
1068         *line_ascent  = ascent;
1069
1070         line = HeapAlloc(GetProcessHeap(), 0,
1071                          sizeof(WINHELP_LINE) + textlen);
1072         if (!line) return FALSE;
1073
1074         line->next    = 0;
1075         part          = &line->first_part;
1076         ptr           = (char*)line + sizeof(WINHELP_LINE);
1077
1078         line->rect.top    = (**linep ? (**linep)->rect.bottom : 0) + space->cy;
1079         line->rect.bottom = line->rect.top;
1080         line->rect.left   = space->cx;
1081         line->rect.right  = space->cx;
1082
1083         if (**linep) *linep = &(**linep)->next;
1084         **linep = line;
1085         space->cy = 0;
1086     }
1087     else /* Same line */
1088     {
1089         line = **linep;
1090
1091         if (*line_ascent < ascent)
1092         {
1093             WINHELP_LINE_PART *p;
1094             for (p = &line->first_part; p; p = p->next)
1095             {
1096                 p->rect.top    += ascent - *line_ascent;
1097                 p->rect.bottom += ascent - *line_ascent;
1098             }
1099             line->rect.bottom += ascent - *line_ascent;
1100             *line_ascent = ascent;
1101         }
1102
1103         part = HeapAlloc(GetProcessHeap(), 0,
1104                          sizeof(WINHELP_LINE_PART) + textlen);
1105         if (!part) return FALSE;
1106         **partp = part;
1107         ptr     = (char*)part + sizeof(WINHELP_LINE_PART);
1108     }
1109
1110     memcpy(ptr, text, textlen);
1111     part->cookie            = hlp_line_part_text;
1112     part->rect.left         = line->rect.right + (*partp ? space->cx : 0);
1113     part->rect.right        = part->rect.left + textsize->cx;
1114     line->rect.right        = part->rect.right;
1115     part->rect.top          =
1116         ((*partp) ? line->rect.top : line->rect.bottom) + *line_ascent - ascent;
1117     part->rect.bottom       = part->rect.top + textsize->cy;
1118     line->rect.bottom       = max(line->rect.bottom, part->rect.bottom);
1119     part->u.text.lpsText    = ptr;
1120     part->u.text.wTextLen   = textlen;
1121     part->u.text.hFont      = font;
1122     part->u.text.color      = color;
1123     part->u.text.wUnderline = underline;
1124
1125     WINE_TRACE("Appended text '%*.*s'[%d] @ (%ld,%ld-%ld,%ld)\n",
1126                part->u.text.wTextLen,
1127                part->u.text.wTextLen,
1128                part->u.text.lpsText,
1129                part->u.text.wTextLen,
1130                part->rect.left, part->rect.top, part->rect.right, part->rect.bottom);
1131
1132     part->link = link;
1133     if (link) link->wRefCount++;
1134
1135     part->next          = 0;
1136     *partp              = &part->next;
1137
1138     space->cx = 0;
1139
1140     return TRUE;
1141 }
1142
1143 /***********************************************************************
1144  *
1145  *           WINHELP_AppendGfxObject
1146  */
1147 static WINHELP_LINE_PART* WINHELP_AppendGfxObject(WINHELP_LINE ***linep, WINHELP_LINE_PART ***partp,
1148                                                   LPSIZE space, LPSIZE gfxSize,
1149                                                   HLPFILE_LINK *link, unsigned pos)
1150 {
1151     WINHELP_LINE      *line;
1152     WINHELP_LINE_PART *part;
1153     LPSTR              ptr;
1154
1155     if (!*partp || pos == 1) /* New line */
1156     {
1157         line = HeapAlloc(GetProcessHeap(), 0, sizeof(WINHELP_LINE));
1158         if (!line) return NULL;
1159
1160         line->next    = NULL;
1161         part          = &line->first_part;
1162
1163         line->rect.top    = (**linep ? (**linep)->rect.bottom : 0) + space->cy;
1164         line->rect.bottom = line->rect.top;
1165         line->rect.left   = space->cx;
1166         line->rect.right  = space->cx;
1167
1168         if (**linep) *linep = &(**linep)->next;
1169         **linep = line;
1170         space->cy = 0;
1171         ptr = (char*)line + sizeof(WINHELP_LINE);
1172     }
1173     else /* Same line */
1174     {
1175         if (pos == 2) WINE_FIXME("Left alignment not handled\n");
1176         line = **linep;
1177
1178         part = HeapAlloc(GetProcessHeap(), 0, sizeof(WINHELP_LINE_PART));
1179         if (!part) return NULL;
1180         **partp = part;
1181         ptr = (char*)part + sizeof(WINHELP_LINE_PART);
1182     }
1183
1184     /* part->cookie should be set by caller (image or metafile) */
1185     part->rect.left       = line->rect.right + (*partp ? space->cx : 0);
1186     part->rect.right      = part->rect.left + gfxSize->cx;
1187     line->rect.right      = part->rect.right;
1188     part->rect.top        = (*partp) ? line->rect.top : line->rect.bottom;
1189     part->rect.bottom     = part->rect.top + gfxSize->cy;
1190     line->rect.bottom     = max(line->rect.bottom, part->rect.bottom);
1191
1192     WINE_TRACE("Appended gfx @ (%ld,%ld-%ld,%ld)\n",
1193                part->rect.left, part->rect.top, part->rect.right, part->rect.bottom);
1194
1195     part->link = link;
1196     if (link) link->wRefCount++;
1197
1198     part->next            = NULL;
1199     *partp                = &part->next;
1200
1201     space->cx = 0;
1202
1203     return part;
1204 }
1205
1206
1207 /***********************************************************************
1208  *
1209  *           WINHELP_SplitLines
1210  */
1211 static BOOL WINHELP_SplitLines(HWND hWnd, LPSIZE newsize)
1212 {
1213     WINHELP_WINDOW     *win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1214     HLPFILE_PARAGRAPH  *p;
1215     WINHELP_LINE      **line = &win->first_line;
1216     WINHELP_LINE_PART **part = 0;
1217     INT                 line_ascent = 0;
1218     SIZE                space;
1219     RECT                rect;
1220     HDC                 hDc;
1221
1222     if (newsize) newsize->cx = newsize->cy = 0;
1223
1224     if (!win->page) return TRUE;
1225
1226     WINHELP_DeleteLines(win);
1227
1228     GetClientRect(hWnd, &rect);
1229
1230     rect.top    += INTERNAL_BORDER_WIDTH;
1231     rect.left   += INTERNAL_BORDER_WIDTH;
1232     rect.right  -= INTERNAL_BORDER_WIDTH;
1233     rect.bottom -= INTERNAL_BORDER_WIDTH;
1234
1235     space.cy = rect.top;
1236     space.cx = rect.left;
1237
1238     hDc = GetDC(hWnd);
1239
1240     for (p = win->page->first_paragraph; p; p = p->next)
1241     {
1242         switch (p->cookie)
1243         {
1244         case para_normal_text:
1245         case para_debug_text:
1246             {
1247                 TEXTMETRIC tm;
1248                 SIZE textsize    = {0, 0};
1249                 LPCSTR text      = p->u.text.lpszText;
1250                 UINT indent      = 0;
1251                 UINT len         = strlen(text);
1252                 unsigned underline = 0;
1253
1254                 HFONT hFont = 0;
1255                 COLORREF color = RGB(0, 0, 0);
1256
1257                 if (p->u.text.wFont < win->page->file->numFonts)
1258                 {
1259                     HLPFILE*    hlpfile = win->page->file;
1260
1261                     if (!hlpfile->fonts[p->u.text.wFont].hFont)
1262                         hlpfile->fonts[p->u.text.wFont].hFont = CreateFontIndirect(&hlpfile->fonts[p->u.text.wFont].LogFont);
1263                     hFont = hlpfile->fonts[p->u.text.wFont].hFont;
1264                     color = hlpfile->fonts[p->u.text.wFont].color;
1265                 }
1266                 else
1267                 {
1268                     UINT  wFont = (p->u.text.wFont < win->fonts_len) ? p->u.text.wFont : 0;
1269
1270                     hFont = win->fonts[wFont];
1271                 }
1272
1273                 if (p->link && p->link->bClrChange)
1274                 {
1275                     underline = (p->link->cookie == hlp_link_popup) ? 3 : 1;
1276                     color = RGB(0, 0x80, 0);
1277                 }
1278                 if (p->cookie == para_debug_text) color = RGB(0xff, 0, 0);
1279
1280                 SelectObject(hDc, hFont);
1281
1282                 GetTextMetrics(hDc, &tm);
1283
1284                 if (p->u.text.wIndent)
1285                 {
1286                     indent = p->u.text.wIndent * 5 * tm.tmAveCharWidth;
1287                     if (!part)
1288                         space.cx = rect.left + indent - 2 * tm.tmAveCharWidth;
1289                 }
1290
1291                 if (p->u.text.wVSpace)
1292                 {
1293                     part = 0;
1294                     space.cx = rect.left + indent;
1295                     space.cy += (p->u.text.wVSpace - 1) * tm.tmHeight;
1296                 }
1297
1298                 if (p->u.text.wHSpace)
1299                 {
1300                     space.cx += p->u.text.wHSpace * 2 * tm.tmAveCharWidth;
1301                 }
1302
1303                 WINE_TRACE("splitting text %s\n", text);
1304
1305                 while (len)
1306                 {
1307                     INT free_width = rect.right - (part ? (*line)->rect.right : rect.left) - space.cx;
1308                     UINT low = 0, curr = len, high = len, textlen = 0;
1309
1310                     if (free_width > 0)
1311                     {
1312                         while (1)
1313                         {
1314                             GetTextExtentPoint(hDc, text, curr, &textsize);
1315
1316                             if (textsize.cx <= free_width) low = curr;
1317                             else high = curr;
1318
1319                             if (high <= low + 1) break;
1320
1321                             if (textsize.cx) curr = (curr * free_width) / textsize.cx;
1322                             if (curr <= low) curr = low + 1;
1323                             else if (curr >= high) curr = high - 1;
1324                         }
1325                         textlen = low;
1326                         while (textlen && text[textlen] && text[textlen] != ' ') textlen--;
1327                     }
1328                     if (!part && !textlen) textlen = max(low, 1);
1329
1330                     if (free_width <= 0 || !textlen)
1331                     {
1332                         part = 0;
1333                         space.cx = rect.left + indent;
1334                         space.cx = min(space.cx, rect.right - rect.left - 1);
1335                         continue;
1336                     }
1337
1338                     WINE_TRACE("\t => %d %*s\n", textlen, textlen, text);
1339
1340                     if (!WINHELP_AppendText(&line, &part, &space, &textsize,
1341                                             &line_ascent, tm.tmAscent,
1342                                             text, textlen, hFont, color, p->link, underline) ||
1343                         (!newsize && (*line)->rect.bottom > rect.bottom))
1344                     {
1345                         ReleaseDC(hWnd, hDc);
1346                         return FALSE;
1347                     }
1348
1349                     if (newsize)
1350                         newsize->cx = max(newsize->cx, (*line)->rect.right + INTERNAL_BORDER_WIDTH);
1351
1352                     len -= textlen;
1353                     text += textlen;
1354                     if (text[0] == ' ') text++, len--;
1355                 }
1356             }
1357             break;
1358         case para_bitmap:
1359         case para_metafile:
1360             {
1361                 SIZE                    gfxSize;
1362                 INT                     free_width;
1363                 WINHELP_LINE_PART*      ref_part;
1364
1365                 if (p->u.gfx.pos & 0x8000)
1366                 {
1367                     space.cx = rect.left;
1368                     if (*line)
1369                         space.cy += (*line)->rect.bottom - (*line)->rect.top;
1370                     part = 0;
1371                 }
1372
1373                 if (p->cookie == para_bitmap)
1374                 {
1375                     DIBSECTION              dibs;
1376                     
1377                     GetObject(p->u.gfx.u.bmp.hBitmap, sizeof(dibs), &dibs);
1378                     gfxSize.cx = dibs.dsBm.bmWidth;
1379                     gfxSize.cy = dibs.dsBm.bmHeight;
1380                 }
1381                 else gfxSize = p->u.gfx.u.mf.mfSize;
1382                     
1383                 free_width = rect.right - ((part && *line) ? (*line)->rect.right : rect.left) - space.cx;
1384                 if (free_width <= 0)
1385                 {
1386                     part = NULL;
1387                     space.cx = rect.left;
1388                     space.cx = min(space.cx, rect.right - rect.left - 1);
1389                 }
1390                 ref_part = WINHELP_AppendGfxObject(&line, &part, &space, &gfxSize,
1391                                                    p->link, p->u.gfx.pos);
1392                 if (!ref_part || (!newsize && (*line)->rect.bottom > rect.bottom))
1393                 {
1394                     return FALSE;
1395                 }
1396                 if (p->cookie == para_bitmap)
1397                 {
1398                     ref_part->cookie = hlp_line_part_bitmap;
1399                     ref_part->u.bitmap.hBitmap = p->u.gfx.u.bmp.hBitmap;
1400                 }
1401                 else
1402                 {
1403                     ref_part->cookie = hlp_line_part_metafile;
1404                     ref_part->u.metafile.hMetaFile = p->u.gfx.u.mf.hMetaFile;
1405                 }
1406             }
1407             break;
1408         }
1409     }
1410
1411     if (newsize)
1412         newsize->cy = (*line)->rect.bottom + INTERNAL_BORDER_WIDTH;
1413
1414     ReleaseDC(hWnd, hDc);
1415     return TRUE;
1416 }
1417
1418 /***********************************************************************
1419  *
1420  *           WINHELP_CheckPopup
1421  */
1422 static void WINHELP_CheckPopup(UINT msg)
1423 {
1424     if (!Globals.hPopupWnd) return;
1425
1426     switch (msg)
1427     {
1428     case WM_COMMAND:
1429     case WM_LBUTTONDOWN:
1430     case WM_MBUTTONDOWN:
1431     case WM_RBUTTONDOWN:
1432     case WM_NCLBUTTONDOWN:
1433     case WM_NCMBUTTONDOWN:
1434     case WM_NCRBUTTONDOWN:
1435         DestroyWindow(Globals.hPopupWnd);
1436         Globals.hPopupWnd = 0;
1437     }
1438 }
1439
1440 /***********************************************************************
1441  *
1442  *           WINHELP_DeleteLines
1443  */
1444 static void WINHELP_DeleteLines(WINHELP_WINDOW *win)
1445 {
1446     WINHELP_LINE      *line, *next_line;
1447     WINHELP_LINE_PART *part, *next_part;
1448     for (line = win->first_line; line; line = next_line)
1449     {
1450         next_line = line->next;
1451         for (part = &line->first_part; part; part = next_part)
1452         {
1453             next_part = part->next;
1454             HLPFILE_FreeLink(part->link);
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(WINHELP_WINDOW* win, WPARAM wParam, LPARAM lParam)
1578 {
1579     POINT mouse;
1580     WINHELP_LINE      *line;
1581     WINHELP_LINE_PART *part;
1582     int scroll_pos = GetScrollPos(win->hMainWnd, SB_VERT);
1583
1584     mouse.x = LOWORD(lParam);
1585     mouse.y = HIWORD(lParam);
1586     for (line = win->first_line; line; line = line->next)
1587     {
1588         for (part = &line->first_part; part; part = part->next)
1589         {
1590             if (part->link && 
1591                 part->link->lpszString &&
1592                 part->rect.left   <= mouse.x &&
1593                 part->rect.right  >= mouse.x &&
1594                 part->rect.top    <= mouse.y + scroll_pos &&
1595                 part->rect.bottom >= mouse.y + scroll_pos)
1596             {
1597                 return part;
1598             }
1599         }
1600     }
1601
1602     return NULL;
1603 }