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