Release 960805
[wine] / programs / winhelp / winhelp.c
1 /*
2  * Help Viewer
3  *
4  * Copyright 1996 Ulrich Schmid <uschmid@mail.hh.provi.de>
5  */
6
7 #include <stdio.h>
8 #include <windows.h>
9 #ifdef WINELIB
10 #include <resource.h>
11 #include <options.h>
12 #include <shell.h>
13 extern const char people[];
14 #endif
15 #include "winhelp.h"
16
17 VOID LIBWINE_Register_De(void);
18 VOID LIBWINE_Register_En(void);
19 VOID LIBWINE_Register_Fi(void);
20 VOID LIBWINE_Register_Fr(void);
21
22 static BOOL    WINHELP_RegisterWinClasses();
23 static LRESULT WINHELP_MainWndProc(HWND, UINT, WPARAM, LPARAM);
24 static LRESULT WINHELP_TextWndProc(HWND, UINT, WPARAM, LPARAM);
25 static LRESULT WINHELP_ButtonBoxWndProc(HWND, UINT, WPARAM, LPARAM);
26 static VOID    WINHELP_CheckPopup(UINT);
27 static BOOL    WINHELP_SplitLines(HWND hWnd, LPSIZE);
28 static VOID    WINHELP_InitFonts(HWND hWnd);
29 static VOID    WINHELP_DeleteLines(WINHELP_WINDOW*);
30 static VOID    WINHELP_DeleteWindow(WINHELP_WINDOW*);
31 static VOID    WINHELP_SetupText(HWND hWnd);
32 static BOOL    WINHELP_AppendText(WINHELP_LINE***, WINHELP_LINE_PART***,
33                                   LPSIZE, LPSIZE, INT*, INT, LPCSTR, UINT,
34                                   HFONT, COLORREF, HLPFILE_LINK*);
35
36 WINHELP_GLOBALS Globals = {3, 0, 0, 0, 0, 0};
37
38 static BOOL MacroTest = FALSE;
39
40 /***********************************************************************
41  *
42  *           WinMain
43  */
44
45 int PASCAL WinMain (HANDLE hInstance, HANDLE prev, LPSTR cmdline, int show)
46 {
47   LPCSTR opt_lang = "En";
48   CHAR   lang[3];
49   MSG    msg;
50   LONG   lHash = 0;
51   INT    langnum;
52
53 #if defined(WINELIB) && !defined(HAVE_WINE_CONSTRUCTOR)
54   /* Register resources */
55   LIBWINE_Register_De();
56   LIBWINE_Register_En();
57   LIBWINE_Register_Fi();
58   LIBWINE_Register_Fr();
59 #endif
60
61   Globals.hInstance = hInstance;
62
63   /* Get options */
64   while (*cmdline && (*cmdline == ' ' || *cmdline == '-'))
65     {
66       CHAR   option;
67       LPCSTR topic_id;
68       if (*cmdline++ == ' ') continue;
69
70       option = *cmdline;
71       if (option) cmdline++;
72       while (*cmdline && *cmdline == ' ') cmdline++;
73       switch(option)
74         {
75         case 'i':
76         case 'I':
77           topic_id = cmdline;
78           while (*cmdline && *cmdline != ' ') cmdline++;
79           if (*cmdline) *cmdline++ = '\0';
80           lHash = HLPFILE_Hash(topic_id);
81           break;
82
83         case '3':
84         case '4':
85           Globals.wVersion = option - '0';
86           break;
87
88         case 't':
89           MacroTest = TRUE;
90           break;
91         }
92     }
93
94 #ifdef WINELIB
95   opt_lang = langNames[Options.language];
96 #endif
97
98   /* Find language specific string table */
99   for (langnum = 0; langnum <= MAX_LANGUAGE_NUMBER; langnum++)
100     {
101       Globals.wStringTableOffset = langnum * 0x100;
102       if (LoadString(hInstance, IDS_LANGUAGE_ID, lang, sizeof(lang)) &&
103           !lstrcmp(opt_lang, lang))
104         break;
105     }
106   if (langnum > MAX_LANGUAGE_NUMBER)
107     {
108       /* Find fallback language */
109       for (langnum = 0; langnum <= MAX_LANGUAGE_NUMBER; langnum++)
110         {
111           Globals.wStringTableOffset = langnum * 0x100;
112           if (LoadString(hInstance, IDS_LANGUAGE_ID, lang, sizeof(lang)))
113             break;
114         }
115       if (langnum > MAX_LANGUAGE_NUMBER)
116         {
117         MessageBox(0, "No language found", "FATAL ERROR", MB_OK);
118         return(1);
119         }
120     }
121
122   /* Change Resource names */
123   lstrcpyn(STRING_MENU_Xx + lstrlen(STRING_MENU_Xx) - 2, lang, 3);
124
125   /* Create primary window */
126   WINHELP_RegisterWinClasses();
127   WINHELP_CreateHelpWindow(cmdline, lHash, "main", FALSE, NULL, NULL, show);
128
129   /* Message loop */
130   while (GetMessage (&msg, 0, 0, 0))
131     {
132       TranslateMessage (&msg);
133       DispatchMessage (&msg);
134     }
135   return 0;
136 }
137
138 /***********************************************************************
139  *
140  *           RegisterWinClasses
141  */
142
143 static BOOL WINHELP_RegisterWinClasses()
144 {
145   WNDCLASS class_main, class_button_box, class_text, class_shadow;
146
147   class_main.style               = CS_HREDRAW | CS_VREDRAW;
148   class_main.lpfnWndProc         = WINHELP_MainWndProc;
149   class_main.cbClsExtra          = 0;
150   class_main.cbWndExtra          = sizeof(LONG);
151   class_main.hInstance           = Globals.hInstance;
152   class_main.hIcon               = LoadIcon (0, IDI_APPLICATION);
153   class_main.hCursor             = LoadCursor (0, IDC_ARROW);
154   class_main.hbrBackground       = GetStockObject (WHITE_BRUSH);
155   class_main.lpszMenuName        = 0;
156   class_main.lpszClassName       = MAIN_WIN_CLASS_NAME;
157
158   class_button_box               = class_main;
159   class_button_box.lpfnWndProc   = WINHELP_ButtonBoxWndProc;
160   class_button_box.hbrBackground = GetStockObject(GRAY_BRUSH);
161   class_button_box.lpszClassName = BUTTON_BOX_WIN_CLASS_NAME;
162
163   class_text = class_main;
164   class_text.lpfnWndProc         = WINHELP_TextWndProc;
165   class_text.lpszClassName       = TEXT_WIN_CLASS_NAME;
166
167   class_shadow = class_main;
168   class_shadow.lpfnWndProc       = DefWindowProc;
169   class_shadow.hbrBackground     = GetStockObject(GRAY_BRUSH);
170   class_shadow.lpszClassName     = SHADOW_WIN_CLASS_NAME;
171
172   return (RegisterClass(&class_main) &&
173           RegisterClass(&class_button_box) &&
174           RegisterClass(&class_text) &&
175           RegisterClass(&class_shadow));
176 }
177
178 /***********************************************************************
179  *
180  *           WINHELP_CreateHelpWindow
181  */
182
183 VOID WINHELP_CreateHelpWindow(LPCSTR lpszFile, LONG lHash, LPCSTR lpszWindow,
184                               BOOL bPopup, HWND hParentWnd, LPPOINT mouse, INT nCmdShow)
185 {
186   CHAR    szCaption[MAX_STRING_LEN];
187   CHAR    szContents[MAX_STRING_LEN];
188   CHAR    szSearch[MAX_STRING_LEN];
189   CHAR    szBack[MAX_STRING_LEN];
190   CHAR    szHistory[MAX_STRING_LEN];
191   SIZE    size   = {CW_USEDEFAULT, CW_USEDEFAULT};
192   POINT   origin = {240, 0};
193   LPSTR   ptr;
194   HGLOBAL handle;
195   WINHELP_WINDOW *win, *oldwin;
196   HLPFILE_PAGE   *page;
197   HLPFILE_MACRO  *macro;
198   HWND hWnd;
199   BOOL bPrimary;
200
201   if (bPopup)
202     lpszWindow = NULL;
203   else if (!lpszWindow || !lpszWindow[0])
204     lpszWindow = Globals.active_win->lpszName;
205   bPrimary = lpszWindow && !lstrcmpi(lpszWindow, "main");
206
207   /* Read help file */
208   if (lpszFile[0])
209     {
210       page = lHash ? HLPFILE_PageByHash(lpszFile, lHash) : HLPFILE_Contents(lpszFile);
211
212       /* Add Suffix `.hlp' */
213       if (!page && lstrcmpi(lpszFile + strlen(lpszFile) - 4, ".hlp"))
214         {
215           CHAR      szFile_hlp[MAX_PATHNAME_LEN];
216
217           lstrcpyn(szFile_hlp, lpszFile, sizeof(szFile_hlp) - 4);
218           szFile_hlp[sizeof(szFile_hlp) - 5] = '\0';
219           lstrcat(szFile_hlp, ".hlp");
220
221           page = lHash ? HLPFILE_PageByHash(szFile_hlp, lHash) : HLPFILE_Contents(szFile_hlp);
222           if (!page)
223             {
224               WINHELP_MessageBoxIDS_s(IDS_HLPFILE_ERROR_s, lpszFile, IDS_ERROR, MB_OK);
225               if (Globals.win_list) return;
226             }
227         }
228     }
229   else page = 0;
230
231   /* Calculate horizontal size and position of a popup window */
232   if (bPopup)
233     {
234       RECT parent_rect;
235       GetWindowRect(hParentWnd, &parent_rect);
236       size.cx = (parent_rect.right  - parent_rect.left) / 2;
237
238       origin = *mouse;
239       ClientToScreen(hParentWnd, &origin);
240       origin.x -= size.cx / 2;
241       origin.x  = MIN(origin.x, GetSystemMetrics(SM_CXSCREEN) - size.cx);
242       origin.x  = MAX(origin.x, 0);
243     }
244
245   /* Initialize WINHELP_WINDOW struct */
246   handle = GlobalAlloc(GMEM_FIXED, sizeof(WINHELP_WINDOW) +
247                        (lpszWindow ? strlen(lpszWindow) + 1 : 0));
248   if (!handle) return;
249   win = GlobalLock(handle);
250   win->hSelf = handle;
251   win->next  = Globals.win_list;
252   Globals.win_list = win;
253   if (lpszWindow)
254     {
255       ptr = GlobalLock(handle);
256       ptr += sizeof(WINHELP_WINDOW);
257       lstrcpy(ptr, (LPSTR) lpszWindow);
258       win->lpszName = ptr;
259     }
260   else win->lpszName = NULL;
261   win->page = page;
262   win->first_button = 0;
263   win->first_line = 0;
264   win->hMainWnd = 0;
265   win->hButtonBoxWnd = 0;
266   win->hTextWnd = 0;
267   win->hShadowWnd = 0;
268
269   Globals.active_win = win;
270
271   /* Initialize default pushbuttons */
272   if (MacroTest && !bPopup)
273     MACRO_CreateButton("BTN_TEST", "&Test", "MacroTest");
274   if (bPrimary && page)
275     {
276       LoadString(Globals.hInstance, IDS_CONTENTS, szContents, sizeof(szContents));
277       LoadString(Globals.hInstance, IDS_SEARCH,   szSearch,   sizeof(szSearch));
278       LoadString(Globals.hInstance, IDS_BACK,     szBack,     sizeof(szBack));
279       LoadString(Globals.hInstance, IDS_HISTORY,  szHistory,  sizeof(szHistory));
280       MACRO_CreateButton("BTN_CONTENTS", szContents, "Contents()");
281       MACRO_CreateButton("BTN_SEARCH",   szSearch,   "Search()");
282       MACRO_CreateButton("BTN_BACK",     szBack,     "Back()");
283       MACRO_CreateButton("BTN_HISTORY",  szHistory,  "History()");
284     }
285
286   /* Initialize file specific pushbuttons */
287   if (!bPopup && page)
288     for (macro = page->file->first_macro; macro; macro = macro->next)
289       MACRO_ExecuteMacro(macro->lpszMacro);
290
291   /* Reuse existing window */
292   if (lpszWindow)
293     for (oldwin = win->next; oldwin; oldwin = oldwin->next)
294       if (oldwin->lpszName && !lstrcmpi(oldwin->lpszName, lpszWindow))
295         {
296           WINHELP_BUTTON *button;
297
298           win->hMainWnd      = oldwin->hMainWnd;
299           win->hButtonBoxWnd = oldwin->hButtonBoxWnd;
300           win->hTextWnd      = oldwin->hTextWnd;
301           oldwin->hMainWnd = oldwin->hButtonBoxWnd = oldwin->hTextWnd = 0;
302
303           SetWindowLong(win->hMainWnd,      0, (LONG) win);
304           SetWindowLong(win->hButtonBoxWnd, 0, (LONG) win);
305           SetWindowLong(win->hTextWnd,      0, (LONG) win);
306
307           WINHELP_InitFonts(win->hMainWnd);
308
309           SetWindowText(win->hMainWnd, page->file->lpszTitle);
310
311           WINHELP_SetupText(win->hTextWnd);
312           InvalidateRect(win->hTextWnd, NULL, TRUE);
313           SendMessage(win->hMainWnd, WM_USER, 0, 0);
314           UpdateWindow(win->hTextWnd);
315
316
317           for (button = oldwin->first_button; button; button = button->next)
318             DestroyWindow(button->hWnd);
319   
320           WINHELP_DeleteWindow(oldwin);
321           return;
322         }
323
324   /* Create main Window */
325   if (!page) LoadString(Globals.hInstance, IDS_WINE_HELP, szCaption, sizeof(szCaption));
326   hWnd = CreateWindow (bPopup ? TEXT_WIN_CLASS_NAME : MAIN_WIN_CLASS_NAME,
327                        page ? page->file->lpszTitle : szCaption,
328                        bPopup ? WS_POPUPWINDOW | WS_BORDER : WS_OVERLAPPEDWINDOW,
329                        origin.x, origin.y, size.cx, size.cy,
330                        0, bPrimary ? LoadMenu(Globals.hInstance, STRING_MENU_Xx) : 0,
331                        Globals.hInstance, win);
332
333   ShowWindow (hWnd, nCmdShow);
334   UpdateWindow (hWnd);
335 }
336
337 /***********************************************************************
338  *
339  *           WINHELP_MainWndProc
340  */
341
342 static LRESULT WINHELP_MainWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
343 {
344   WINHELP_WINDOW *win;
345   WINHELP_BUTTON *button;
346   RECT rect, button_box_rect;
347   INT  text_top;
348
349   WINHELP_CheckPopup(msg);
350
351   switch (msg)
352     {
353     case WM_NCCREATE:
354       win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
355       SetWindowLong(hWnd, 0, (LONG) win);
356       win->hMainWnd = hWnd;
357       break;
358
359     case WM_CREATE:
360       win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
361
362       /* Create button box and text Window */
363       CreateWindow(BUTTON_BOX_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
364                    0, 0, 0, 0, hWnd, 0, Globals.hInstance, win);
365
366       CreateWindow(TEXT_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
367                    0, 0, 0, 0, hWnd, 0, Globals.hInstance, win);
368
369       /* Fall through */
370     case WM_USER:
371     case WM_WINDOWPOSCHANGED:
372       win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
373       GetClientRect(hWnd, &rect);
374
375       /* Update button box and text Window */
376       SetWindowPos(win->hButtonBoxWnd, HWND_TOP,
377                    rect.left, rect.top,
378                    rect.right - rect.left,
379                    rect.bottom - rect.top, 0);
380
381       GetWindowRect(win->hButtonBoxWnd, &button_box_rect);
382       text_top = rect.top + button_box_rect.bottom - button_box_rect.top;
383
384       SetWindowPos(win->hTextWnd, HWND_TOP,
385                    rect.left, text_top,
386                    rect.right - rect.left,
387                    rect.bottom - text_top, 0);
388
389       break;
390
391     case WM_COMMAND:
392       Globals.active_win = win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
393       switch (wParam)
394         {
395           /* Menu FILE */
396         case WH_OPEN:            MACRO_FileOpen();       break;
397         case WH_PRINT:           MACRO_Print();          break;
398         case WH_PRINTER_SETUP:   MACRO_PrinterSetup();   break;
399         case WH_EXIT:            MACRO_Exit();           break;
400
401           /* Menu EDIT */
402         case WH_COPY_DIALOG:     MACRO_CopyDialog();     break;
403         case WH_ANNOTATE:        MACRO_Annotate();       break;
404
405           /* Menu Bookmark */
406         case WH_BOOKMARK_DEFINE: MACRO_BookmarkDefine(); break;
407
408           /* Menu Help */
409         case WH_HELP_ON_HELP:    MACRO_HelpOn();         break;
410         case WH_HELP_ON_TOP:     MACRO_HelpOnTop();      break;
411
412           /* Menu Info */
413         case WH_ABOUT:           MACRO_About();          break;
414 #ifdef WINELIB
415         case WH_ABOUT_WINE: 
416           ShellAbout(hWnd, "WINE", people, 0);
417           break;
418 #endif
419
420         default:
421           /* Buttons */
422           for (button = win->first_button; button; button = button->next)
423             if (wParam == button->wParam) break;
424           if (button)
425             MACRO_ExecuteMacro(button->lpszMacro);
426           else
427             WINHELP_MessageBoxIDS(IDS_NOT_IMPLEMENTED, IDS_ERROR, MB_OK);
428           break;
429         }
430       break;
431     }
432
433   return DefWindowProc (hWnd, msg, wParam, lParam);
434 }
435
436 /***********************************************************************
437  *
438  *           WINHELP_ButtonBoxWndProc
439  */
440
441 static LRESULT WINHELP_ButtonBoxWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
442 {
443   WINDOWPOS      *winpos;
444   WINHELP_WINDOW *win;
445   WINHELP_BUTTON *button;
446   SIZE button_size;
447   INT  x, y;
448
449   WINHELP_CheckPopup(msg);
450
451   switch(msg)
452     {
453     case WM_NCCREATE:
454       win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
455       SetWindowLong(hWnd, 0, (LONG) win);
456       win->hButtonBoxWnd = hWnd;
457       break;
458
459     case WM_WINDOWPOSCHANGING:
460       winpos = (WINDOWPOS*) lParam;
461       win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
462
463       /* Update buttons */
464       button_size.cx = 0;
465       button_size.cy = 0;
466       for (button = win->first_button; button; button = button->next)
467         {
468           HDC  hDc;
469           SIZE textsize;
470           if (!button->hWnd)
471             button->hWnd = CreateWindow(STRING_BUTTON, (LPSTR) button->lpszName,
472                                         WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
473                                         0, 0, 0, 0,
474                                         hWnd, (HMENU) button->wParam,
475                                         Globals.hInstance, 0);
476           hDc = GetDC(button->hWnd);
477           GetTextExtentPoint(hDc, button->lpszName,
478                              lstrlen(button->lpszName), &textsize);
479           ReleaseDC(button->hWnd, hDc);
480
481           button_size.cx = MAX(button_size.cx, textsize.cx + BUTTON_CX);
482           button_size.cy = MAX(button_size.cy, textsize.cy + BUTTON_CY);
483         }
484
485       x = 0;
486       y = 0;
487       for (button = win->first_button; button; button = button->next)
488         {
489           SetWindowPos(button->hWnd, HWND_TOP, x, y, button_size.cx, button_size.cy, 0);
490
491           if (x + 2 * button_size.cx <= winpos->cx)
492             x += button_size.cx;
493           else
494             x = 0, y += button_size.cy;
495         }
496       winpos->cy = y + (x ? button_size.cy : 0);
497       break;
498
499     case WM_COMMAND:
500       SendMessage(GetParent(hWnd), msg, wParam, lParam);
501       break;
502     }
503
504   return(DefWindowProc(hWnd, msg, wParam, lParam));
505 }
506
507 /***********************************************************************
508  *
509  *           WINHELP_TextWndProc
510  */
511
512 static LRESULT WINHELP_TextWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
513 {
514   WINHELP_WINDOW    *win;
515   WINHELP_LINE      *line;
516   WINHELP_LINE_PART *part;
517   WINDOWPOS         *winpos;
518   PAINTSTRUCT        ps;
519   HDC   hDc;
520   POINT mouse;
521   INT   scroll_pos;
522   HWND  hPopupWnd;
523   BOOL  bExit;
524
525   if (msg != WM_LBUTTONDOWN)
526     WINHELP_CheckPopup(msg);
527
528   switch (msg)
529     {
530     case WM_NCCREATE:
531       win = (WINHELP_WINDOW*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
532       SetWindowLong(hWnd, 0, (LONG) win);
533       win->hTextWnd = hWnd;
534       if (!win->lpszName) Globals.hPopupWnd = win->hMainWnd = hWnd;
535       WINHELP_InitFonts(hWnd);
536       break;
537
538     case WM_CREATE:
539       win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
540
541       /* Calculate vertical size and position of a popup window */
542       if (!win->lpszName)
543         {
544           POINT origin;
545           RECT old_window_rect;
546           RECT old_client_rect;
547           SIZE old_window_size;
548           SIZE old_client_size;
549           SIZE new_client_size;
550           SIZE new_window_size;
551
552           GetWindowRect(hWnd, &old_window_rect);
553           origin.x = old_window_rect.left;
554           origin.y = old_window_rect.top;
555           old_window_size.cx = old_window_rect.right  - old_window_rect.left;
556           old_window_size.cy = old_window_rect.bottom - old_window_rect.top;
557
558           GetClientRect(hWnd, &old_client_rect);
559           old_client_size.cx = old_client_rect.right  - old_client_rect.left;
560           old_client_size.cy = old_client_rect.bottom - old_client_rect.top;
561
562           new_client_size = old_client_size;
563           WINHELP_SplitLines(hWnd, &new_client_size);
564
565           if (origin.y + POPUP_YDISTANCE + new_client_size.cy <= GetSystemMetrics(SM_CYSCREEN))
566             origin.y += POPUP_YDISTANCE;
567           else
568             origin.y -= POPUP_YDISTANCE + new_client_size.cy;
569
570           new_window_size.cx = old_window_size.cx - old_client_size.cx + new_client_size.cx;
571           new_window_size.cy = old_window_size.cy - old_client_size.cy + new_client_size.cy;
572
573           win->hShadowWnd = 
574             CreateWindow(SHADOW_WIN_CLASS_NAME, "", WS_POPUP | WS_VISIBLE,
575                          origin.x + SHADOW_DX, origin.y + SHADOW_DY,
576                          new_window_size.cx, new_window_size.cy,
577                          0, 0, Globals.hInstance, 0);
578
579           SetWindowPos(hWnd, HWND_TOP, origin.x, origin.y,
580                        new_window_size.cx, new_window_size.cy,
581                        SWP_NOZORDER | SWP_NOACTIVATE);
582           ShowWindow(win->hShadowWnd, SW_NORMAL);
583         }
584       break;
585
586     case WM_WINDOWPOSCHANGED:
587       winpos = (WINDOWPOS*) lParam;
588       if (!(winpos->flags & SWP_NOSIZE)) WINHELP_SetupText(hWnd);
589       break;
590
591     case WM_VSCROLL:
592       {
593         BOOL  update = TRUE;
594         RECT  rect;
595         INT16 Min, Max;
596         INT   CurPos = GetScrollPos(hWnd, SB_VERT);
597         GetScrollRange(hWnd, SB_VERT, &Min, &Max);
598         GetClientRect(hWnd, &rect);
599
600         switch (wParam & 0xffff)
601           {
602           case SB_THUMBTRACK:
603           case SB_THUMBPOSITION: CurPos  = wParam >> 16;                   break;
604           case SB_TOP:           CurPos  = Min;                            break;
605           case SB_BOTTOM:        CurPos  = Max;                            break;
606           case SB_PAGEUP:        CurPos -= (rect.bottom - rect.top) / 2;   break;
607           case SB_PAGEDOWN:      CurPos += (rect.bottom - rect.top) / 2;   break;
608           case SB_LINEUP:        CurPos -= GetSystemMetrics(SM_CXVSCROLL); break;
609           case SB_LINEDOWN:      CurPos += GetSystemMetrics(SM_CXVSCROLL); break;
610           default: update = FALSE;
611           }
612         if (update)
613           {
614             INT dy = GetScrollPos(hWnd, SB_VERT) - CurPos;
615             SetScrollPos(hWnd, SB_VERT, CurPos, TRUE);
616             ScrollWindow(hWnd, 0, dy, NULL, NULL);
617             UpdateWindow(hWnd);
618           }
619       }
620       break;
621
622     case WM_PAINT:
623       hDc = BeginPaint (hWnd, &ps);
624       win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
625       scroll_pos = GetScrollPos(hWnd, SB_VERT);
626
627       for (line = win->first_line; line; line = line->next)
628         for (part = &line->first_part; part; part = part->next)
629           {
630             SelectObject(hDc, part->hFont);
631             SetTextColor(hDc, part->color);
632             TextOut(hDc, part->rect.left, part->rect.top - scroll_pos,
633                     (LPSTR) part->lpsText, part->wTextLen);
634           }
635
636       EndPaint (hWnd, &ps);
637       break;
638
639     case WM_LBUTTONDOWN:
640       win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
641       scroll_pos = GetScrollPos(hWnd, SB_VERT);
642
643       hPopupWnd = Globals.hPopupWnd;
644       Globals.hPopupWnd = 0;
645
646       mouse.x = LOWORD(lParam);
647       mouse.y = HIWORD(lParam);
648       for (line = win->first_line; line; line = line->next)
649         for (part = &line->first_part; part; part = part->next)
650           if (part->link.lpszPath &&
651               part->rect.left   <= mouse.x &&
652               part->rect.right  >= mouse.x &&
653               part->rect.top    <= mouse.y + scroll_pos &&
654               part->rect.bottom >= mouse.y + scroll_pos)
655             WINHELP_CreateHelpWindow(part->link.lpszPath, part->link.lHash, NULL,
656                                      part->link.bPopup, hWnd, &mouse,  SW_NORMAL);
657       if (hPopupWnd)
658         DestroyWindow(hPopupWnd);
659       break;
660
661     case WM_NCDESTROY:
662       win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
663
664       if (hWnd == Globals.hPopupWnd) Globals.hPopupWnd = 0;
665
666       bExit = (Globals.wVersion >= 4 && !lstrcmpi(win->lpszName, "main"));
667
668       WINHELP_DeleteWindow(win);
669
670       if (bExit) MACRO_Exit();
671
672       if (!Globals.win_list)
673         PostQuitMessage (0);
674       break;
675     }
676
677   return DefWindowProc (hWnd, msg, wParam, lParam);
678 }
679
680 /***********************************************************************
681  *
682  *           SetupText
683  */
684
685 static VOID WINHELP_SetupText(HWND hWnd)
686 {
687   HDC  hDc = GetDC(hWnd);
688   RECT rect;
689   SIZE newsize;
690
691   ShowScrollBar(hWnd, SB_VERT, FALSE);
692   if (!WINHELP_SplitLines(hWnd, NULL))
693     {
694       ShowScrollBar(hWnd, SB_VERT, TRUE);
695       GetClientRect(hWnd, &rect);
696
697       WINHELP_SplitLines(hWnd, &newsize);
698       SetScrollRange(hWnd, SB_VERT, 0, rect.top + newsize.cy - rect.bottom, TRUE);
699     }
700   else SetScrollPos(hWnd, SB_VERT, 0, FALSE);
701
702   ReleaseDC(hWnd, hDc);
703 }
704
705 /***********************************************************************
706  *
707  *           WINHELP_SplitLines
708  */
709
710 static BOOL WINHELP_SplitLines(HWND hWnd, LPSIZE newsize)
711 {
712   WINHELP_WINDOW     *win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
713   HLPFILE_PARAGRAPH  *p;
714   WINHELP_LINE      **line = &win->first_line;
715   WINHELP_LINE_PART **part = 0;
716   INT                 line_ascent = 0;
717   SIZE                space;
718   RECT                rect;
719   HDC                 hDc;
720
721   if (newsize) newsize->cx = newsize->cy = 0;
722
723   if (!win->page) return TRUE;
724
725   WINHELP_DeleteLines(win);
726
727   GetClientRect(hWnd, &rect);
728
729   rect.top    += INTERNAL_BORDER_WIDTH;
730   rect.left   += INTERNAL_BORDER_WIDTH;
731   rect.right  -= INTERNAL_BORDER_WIDTH;
732   rect.bottom -= INTERNAL_BORDER_WIDTH;
733
734
735   space.cy = rect.top;
736   space.cx = rect.left;
737
738   hDc = GetDC(hWnd);
739
740   for (p = win->page->first_paragraph; p; p = p->next)
741     {
742       TEXTMETRIC tm;
743       SIZE textsize = {0, 0};
744       LPCSTR text    = p->lpszText;
745       UINT len    = strlen(text);
746       UINT indent = 0;
747
748       UINT  wFont      = (p->wFont < win->fonts_len) ? p->wFont : 0;
749       BOOL  bUnderline = p->link && !p->link->bPopup;
750       HFONT hFont      = win->fonts[wFont][bUnderline ? 1 : 0];
751
752       COLORREF       color = RGB(0, 0, 0);
753       if (p->link)   color = RGB(0, 0x80, 0);
754       if (p->bDebug) color = RGB(0xff, 0, 0);
755
756       SelectObject(hDc, hFont);
757
758       GetTextMetrics (hDc, &tm);
759
760       if (p->wIndent)
761         {
762           indent = p->wIndent * 5 * tm.tmAveCharWidth;
763           if (!part)
764             space.cx = rect.left + indent - 2 * tm.tmAveCharWidth;
765         }
766
767       if (p->wVSpace)
768         {
769           part = 0;
770           space.cx = rect.left + indent;
771           space.cy += (p->wVSpace - 1) * tm.tmHeight;
772         }
773
774       if (p->wHSpace)
775         {
776           space.cx += p->wHSpace * 2 * tm.tmAveCharWidth;
777         }
778
779       while (len)
780         {
781           INT free_width = rect.right - (part ? (*line)->rect.right : rect.left) - space.cx;
782           UINT low = 0, curr = len, high = len, textlen = 0;
783
784           if (free_width > 0)
785             {
786               while (1)
787                 {
788                   GetTextExtentPoint(hDc, text, curr, &textsize);
789
790                   if (textsize.cx <= free_width) low = curr;
791                   else high = curr;
792
793                   if (high <= low + 1) break;
794
795                   if (textsize.cx) curr = (curr * free_width) / textsize.cx;
796                   if (curr <= low) curr = low + 1;
797                   else if (curr >= high) curr = high - 1;
798                 }
799               textlen = low;
800               while (textlen && text[textlen] && text[textlen] != ' ') textlen--;
801             }
802           if (!part && !textlen) textlen = MAX(low, 1);
803
804           if (free_width <= 0 || !textlen)
805             {
806               part = 0;
807               space.cx = rect.left + indent;
808               space.cx = MIN(space.cx, rect.right - rect.left - 1);
809               continue;
810             }
811
812           if (!WINHELP_AppendText(&line, &part, &space, &textsize,
813                                   &line_ascent, tm.tmAscent,
814                                   text, textlen, hFont, color, p->link) ||
815               (!newsize && (*line)->rect.bottom > rect.bottom))
816             {
817               ReleaseDC(hWnd, hDc);
818               return FALSE;
819             }
820
821           if (newsize)
822             newsize->cx = MAX(newsize->cx, (*line)->rect.right + INTERNAL_BORDER_WIDTH);
823
824           len -= textlen;
825           text += textlen;
826           if (text[0] == ' ') text++, len--;
827         }
828     }
829
830   if (newsize)
831     newsize->cy = (*line)->rect.bottom + INTERNAL_BORDER_WIDTH;
832
833   ReleaseDC(hWnd, hDc);
834   return TRUE;
835 }
836
837 /***********************************************************************
838  *
839  *           WINHELP_AppendText
840  */
841
842 static BOOL WINHELP_AppendText(WINHELP_LINE ***linep, WINHELP_LINE_PART ***partp,
843                                LPSIZE space, LPSIZE textsize,
844                                INT *line_ascent, INT ascent,
845                                LPCSTR text, UINT textlen,
846                                HFONT font, COLORREF color, HLPFILE_LINK *link)
847 {
848   HGLOBAL handle;
849   WINHELP_LINE      *line;
850   WINHELP_LINE_PART *part;
851   LPSTR ptr;
852
853   if (!*partp) /* New line */
854     {
855       *line_ascent  = ascent;
856
857       handle = GlobalAlloc(GMEM_FIXED, sizeof(WINHELP_LINE) + textlen +
858                            (link ? lstrlen(link->lpszPath) + 1 : 0));
859       if (!handle) return FALSE;
860       line          = GlobalLock(handle);
861       line->next    = 0;
862       part          = &line->first_part;
863       ptr           = GlobalLock(handle);
864       ptr          += sizeof(WINHELP_LINE);
865
866       line->rect.top    = (**linep ? (**linep)->rect.bottom : 0) + space->cy;
867       line->rect.bottom = line->rect.top;
868       line->rect.left   = space->cx;
869       line->rect.right  = space->cx;
870
871       if (**linep) *linep = &(**linep)->next; 
872       **linep = line;
873       space->cy = 0;
874     }
875   else /* Same line */
876     {
877       line = **linep;
878
879       if (*line_ascent < ascent)
880         {
881           WINHELP_LINE_PART *p;
882           for (p = &line->first_part; p; p = p->next)
883             {
884               p->rect.top    += ascent - *line_ascent;
885               p->rect.bottom += ascent - *line_ascent;
886             }
887           line->rect.bottom += ascent - *line_ascent;
888           *line_ascent = ascent;
889         }
890
891       handle = GlobalAlloc(GMEM_FIXED, sizeof(WINHELP_LINE_PART) + textlen +
892                            (link ? lstrlen(link->lpszPath) + 1 : 0));
893       if (!handle) return FALSE;
894       part    = GlobalLock(handle);
895       **partp = part;
896       ptr     = GlobalLock(handle);
897       ptr    += sizeof(WINHELP_LINE_PART);
898     }
899
900   hmemcpy(ptr, text, textlen);
901   part->rect.left     = line->rect.right + (*partp ? space->cx : 0);
902   part->rect.right    = part->rect.left + textsize->cx;
903   line->rect.right    = part->rect.right;
904   part->rect.top      =
905     ((*partp) ? line->rect.top : line->rect.bottom) + *line_ascent - ascent;
906   part->rect.bottom   = part->rect.top + textsize->cy;
907   line->rect.bottom   = MAX(line->rect.bottom, part->rect.bottom);
908   part->hSelf         = handle;
909   part->lpsText       = ptr;
910   part->wTextLen      = textlen;
911   part->hFont         = font;
912   part->color         = color;
913   if (link)
914     {
915       strcpy(ptr + textlen, link->lpszPath);
916       part->link.lpszPath = ptr + textlen;
917       part->link.lHash    = link->lHash;
918       part->link.bPopup   = link->bPopup;
919     }
920   else part->link.lpszPath = 0;
921
922   part->next          = 0;
923   *partp              = &part->next;
924
925   space->cx = 0;
926
927   return TRUE;
928 }
929
930 /***********************************************************************
931  *
932  *           WINHELP_CheckPopup
933  */
934
935 static VOID WINHELP_CheckPopup(UINT msg)
936 {
937   if (!Globals.hPopupWnd) return;
938
939   switch (msg)
940     {
941     case WM_COMMAND:
942     case WM_LBUTTONDOWN:
943     case WM_MBUTTONDOWN:
944     case WM_RBUTTONDOWN:
945     case WM_NCLBUTTONDOWN:
946     case WM_NCMBUTTONDOWN:
947     case WM_NCRBUTTONDOWN:
948       DestroyWindow(Globals.hPopupWnd);
949       Globals.hPopupWnd = 0;
950     }
951 }
952
953 /***********************************************************************
954  *
955  *           WINHELP_DeleteLines
956  */
957
958 static VOID WINHELP_DeleteLines(WINHELP_WINDOW *win)
959 {
960   WINHELP_LINE      *line, *next_line;
961   WINHELP_LINE_PART *part, *next_part;
962   for(line = win->first_line; line; line = next_line)
963     {
964       next_line = line->next;
965       for(part = &line->first_part; part; part = next_part)
966         {
967           next_part = part->next;
968           GlobalFree(part->hSelf);
969         }
970     }
971   win->first_line = 0;
972 }
973
974 /***********************************************************************
975  *
976  *           WINHELP_DeleteWindow
977  */
978
979 static VOID WINHELP_DeleteWindow(WINHELP_WINDOW *win)
980 {
981   WINHELP_WINDOW **w;
982
983   for (w = &Globals.win_list; *w; w = &(*w)->next)
984     if (*w == win)
985       {
986         *w = win->next;
987         break;
988       }
989
990   if (win->hShadowWnd) DestroyWindow(win->hShadowWnd);
991   HLPFILE_FreeHlpFilePage(win->page);
992   WINHELP_DeleteLines(win);
993   GlobalFree(win->hSelf);
994 }
995
996 /***********************************************************************
997  *
998  *           WINHELP_InitFonts
999  */
1000
1001 static VOID WINHELP_InitFonts(HWND hWnd)
1002 {
1003   WINHELP_WINDOW *win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
1004   LOGFONT logfontlist[] = {
1005     {-10, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1006     {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1007     {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1008     {-12, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1009     {-12, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1010     {-10, 0, 0, 0, 700, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"},
1011     { -8, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 32, "Helv"}};
1012 #define FONTS_LEN (sizeof(logfontlist)/sizeof(*logfontlist))
1013
1014   static HFONT fonts[FONTS_LEN][2];
1015   static BOOL init = 0;
1016
1017   win->fonts_len = FONTS_LEN;
1018   win->fonts = fonts;
1019
1020   if (!init)
1021     {
1022       INT i;
1023
1024       for(i = 0; i < FONTS_LEN; i++)
1025         {
1026           LOGFONT logfont = logfontlist[i];
1027
1028           fonts[i][0] = CreateFontIndirect(&logfont);
1029           logfont.lfUnderline = 1;
1030           fonts[i][1] = CreateFontIndirect(&logfont);
1031         }
1032
1033       init = 1;
1034     }
1035 }
1036
1037 /***********************************************************************
1038  *
1039  *           WINHELP_MessageBoxIDS
1040  */
1041
1042 INT WINHELP_MessageBoxIDS(UINT ids_text, UINT ids_title, WORD type)
1043 {
1044   CHAR text[MAX_STRING_LEN];
1045   CHAR title[MAX_STRING_LEN];
1046
1047   LoadString(Globals.hInstance, ids_text, text, sizeof(text));
1048   LoadString(Globals.hInstance, ids_title, title, sizeof(title));
1049
1050   return(MessageBox(0, text, title, type));
1051 }
1052
1053 /***********************************************************************
1054  *
1055  *           MAIN_MessageBoxIDS_s
1056  */
1057
1058 INT WINHELP_MessageBoxIDS_s(UINT ids_text, LPCSTR str, UINT ids_title, WORD type)
1059 {
1060   CHAR text[MAX_STRING_LEN];
1061   CHAR title[MAX_STRING_LEN];
1062   CHAR newtext[MAX_STRING_LEN + MAX_PATHNAME_LEN];
1063
1064   LoadString(Globals.hInstance, ids_text, text, sizeof(text));
1065   LoadString(Globals.hInstance, ids_title, title, sizeof(title));
1066   wsprintf(newtext, text, str);
1067
1068   return(MessageBox(0, newtext, title, type));
1069 }
1070
1071 /* Local Variables:    */
1072 /* c-file-style: "GNU" */
1073 /* End:                */