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