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