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