From 2b855deddba9576b715cc5496845cd2c3d34fa60 Mon Sep 17 00:00:00 2001
From: Eric Pouech <eric.pouech@wanadoo.fr>
Date: Wed, 20 Nov 2002 19:46:18 +0000
Subject: [PATCH] Wrote back & history support. Correctly implemented << and >>
 buttons. Added support for page level macros.

---
 programs/winhelp/hlpfile.c | 187 ++++++++++++++++------------
 programs/winhelp/hlpfile.h |  27 ++--
 programs/winhelp/macro.c   |  41 +++++-
 programs/winhelp/string.c  |   1 +
 programs/winhelp/winhelp.c | 249 +++++++++++++++++++++++++++++++------
 programs/winhelp/winhelp.h |   8 ++
 6 files changed, 375 insertions(+), 138 deletions(-)

diff --git a/programs/winhelp/hlpfile.c b/programs/winhelp/hlpfile.c
index ea53270363d..60e815934e9 100644
--- a/programs/winhelp/hlpfile.c
+++ b/programs/winhelp/hlpfile.c
@@ -84,23 +84,6 @@ static BOOL  HLPFILE_Uncompress3(char*, const char*, const BYTE*, const BYTE*);
 static void  HLPFILE_UncompressRLE(const BYTE* src, unsigned sz, BYTE** dst);
 static BOOL  HLPFILE_ReadFont(HLPFILE* hlpfile);
 
-/***********************************************************************
- *
- *           HLPFILE_Contents
- */
-HLPFILE_PAGE *HLPFILE_Contents(HLPFILE *hlpfile)
-{
-    if (!hlpfile) return 0;
-#if 1
-    return hlpfile->first_page;
-#else
-    if (hlpfile->contents_start) 
-        return HLPFILE_PageByHash(hlpfile, hlpfile->contents_start);
-    else
-        return hlpfile->first_page;
-#endif
-}
-
 /***********************************************************************
  *
  *           HLPFILE_PageByNumber
@@ -119,53 +102,80 @@ HLPFILE_PAGE *HLPFILE_PageByNumber(LPCSTR lpszPath, UINT wNum)
     return page;
 }
 
+/* FIXME:
+ * this finds the page containing the offset. The offset can either
+ * refer to the top of the page (offset == page->offset), or
+ * to some paragraph inside the page...
+ * As of today, we only return the page... we should also return
+ * a paragraph, and then, while opening a new page, compute the
+ * y-offset of the paragraph to be shown and scroll the window
+ * accordinly
+ */
+/******************************************************************
+ *		HLPFILE_PageByOffset
+ *
+ *
+ */
+HLPFILE_PAGE *HLPFILE_PageByOffset(HLPFILE* hlpfile, LONG offset)
+{
+    HLPFILE_PAGE*       page;
+    HLPFILE_PAGE*       found;
+
+    if (!hlpfile) return 0;
+
+    WINE_TRACE("<%s>[%lx]\n", hlpfile->lpszPath, offset);
+
+    if (offset == 0xFFFFFFFF) return NULL;
+    page = NULL;
+
+    for (found = NULL, page = hlpfile->first_page; page; page = page->next)
+    {
+        if (page->offset <= offset && (!found || found->offset < page->offset))
+            found = page;
+    }
+    if (!found)
+        WINE_ERR("Page of offset %lu not found in file %s\n",
+                 offset, hlpfile->lpszPath);
+    return found;
+}
+
 /***********************************************************************
  *
  *           HLPFILE_HlpFilePageByHash
  */
 HLPFILE_PAGE *HLPFILE_PageByHash(HLPFILE* hlpfile, LONG lHash)
 {
-    HLPFILE_PAGE*       page;
-    HLPFILE_PAGE*       found;
     int                 i;
 
-    WINE_TRACE("path<%s>[%lx]\n", hlpfile->lpszPath, lHash);
-
     if (!hlpfile) return 0;
 
-    page = NULL;
+    WINE_TRACE("<%s>[%lx]\n", hlpfile->lpszPath, lHash);
+
     for (i = 0; i < hlpfile->wContextLen; i++)
     {
-        if (hlpfile->Context[i].lHash != lHash) continue;
-
-        /* FIXME:
-         * this finds the page containing the offset. The offset can either
-         * refer to the top of the page (offset == page->offset), or
-         * to some paragraph inside the page...
-         * As of today, we only return the page... we should also return
-         * a paragraph, and then, while opening a new page, compute the
-         * y-offset of the paragraph to be shown and scroll the window
-         * accordinly
-         */
-        found = NULL;
-        for (page = hlpfile->first_page; page; page = page->next)
-        {
-            if (page->offset <= hlpfile->Context[i].offset)
-            {
-                if (!found || found->offset < page->offset)
-                    found = page;
-            }
-        }
-        if (found) return found;
-
-        WINE_ERR("Page of offset %lu not found in file %s\n",
-                  hlpfile->Context[i].offset, hlpfile->lpszPath);
-        return NULL;
+        if (hlpfile->Context[i].lHash == lHash)
+            return HLPFILE_PageByOffset(hlpfile, hlpfile->Context[i].offset);
     }
+
     WINE_ERR("Page of hash %lx not found in file %s\n", lHash, hlpfile->lpszPath);
     return NULL;
 }
 
+/***********************************************************************
+ *
+ *           HLPFILE_Contents
+ */
+HLPFILE_PAGE* HLPFILE_Contents(HLPFILE *hlpfile)
+{
+    HLPFILE_PAGE*       page = NULL;
+
+    if (!hlpfile) return NULL;
+
+    page = HLPFILE_PageByOffset(hlpfile, hlpfile->contents_start);
+    if (!page) page = hlpfile->first_page;
+    return page;
+}
+
 /***********************************************************************
  *
  *           HLPFILE_Hash
@@ -216,7 +226,7 @@ HLPFILE *HLPFILE_ReadHlpFile(LPCSTR lpszPath)
     hlpfile->first_macro        = NULL;
     hlpfile->wContextLen        = 0;
     hlpfile->Context            = NULL;
-    hlpfile->contents_start     = 0;
+    hlpfile->contents_start     = 0xFFFFFFFF;
     hlpfile->prev               = NULL;
     hlpfile->next               = first_hlpfile;
     hlpfile->wRefCount          = 1;
@@ -338,35 +348,37 @@ static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned off
     HLPFILE_PAGE* page;
     BYTE*         title;
     UINT          titlesize;
+    char*         ptr;
+    HLPFILE_MACRO*macro;
 
     if (buf + 0x31 > end) {WINE_WARN("page1\n"); return FALSE;};
     title = buf + GET_UINT(buf, 0x10);
     if (title > end) {WINE_WARN("page2\n"); return FALSE;};
 
-    titlesize = GET_UINT(buf, 4) + 1;
-    page = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_PAGE) + titlesize);
+    titlesize = GET_UINT(buf, 4);
+    page = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_PAGE) + titlesize + 1);
     if (!page) return FALSE;
     page->lpszTitle = (char*)page + sizeof(HLPFILE_PAGE);
 
     if (hlpfile->hasPhrases)
     {
-        HLPFILE_Uncompress2(title, end, page->lpszTitle, page->lpszTitle + titlesize - 1);
+        HLPFILE_Uncompress2(title, end, page->lpszTitle, page->lpszTitle + titlesize);
     }
     else
     {
         if (GET_UINT(buf, 0x4) > GET_UINT(buf, 0) - GET_UINT(buf, 0x10))
         {
             /* need to decompress */
-            HLPFILE_Uncompress3(page->lpszTitle, page->lpszTitle + titlesize - 1, 
+            HLPFILE_Uncompress3(page->lpszTitle, page->lpszTitle + titlesize, 
                                 title, end);
         }
         else
         {
-            memcpy(page->lpszTitle, title, titlesize - 1);
+            memcpy(page->lpszTitle, title, titlesize);
         }
     }
 
-    page->lpszTitle[titlesize - 1] = 0;
+    page->lpszTitle[titlesize] = '\0';
 
     if (hlpfile->first_page)
     {
@@ -385,14 +397,36 @@ static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned off
     page->file            = hlpfile;
     page->next            = NULL;
     page->first_paragraph = NULL;
+    page->first_macro     = NULL;
     page->wNumber         = GET_UINT(buf, 0x21);
     page->offset          = offset;
 
-    WINE_TRACE("Added page[%d]: title='%s' offset=%08x\n",
-               page->wNumber, page->lpszTitle, page->offset);
+    page->browse_bwd = GET_UINT(buf, 0x19);
+    page->browse_fwd = GET_UINT(buf, 0x1D);
+
+    WINE_TRACE("Added page[%d]: title='%s' %08lx << %08x >> %08lx\n",
+               page->wNumber, page->lpszTitle, 
+               page->browse_bwd, page->offset, page->browse_fwd);
 
     memset(&attributes, 0, sizeof(attributes));
 
+    /* now load macros */
+    ptr = page->lpszTitle + strlen(page->lpszTitle) + 1;
+    while (ptr < page->lpszTitle + titlesize)
+    {
+        unsigned len = strlen(ptr);
+        WINE_TRACE("macro: %s\n", ptr);
+        macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + len + 1);
+        macro->lpszMacro = (char*)(macro + 1);
+        memcpy((char*)macro->lpszMacro, ptr, len + 1);
+        /* FIXME: shall we really link macro in reverse order ??
+         * may produce strange results when played at page opening
+         */
+        macro->next = page->first_macro;
+        page->first_macro = macro;
+        ptr += len + 1;
+    }
+
     return TRUE;
 }
 
@@ -1764,34 +1798,35 @@ static void HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph)
 
 /***********************************************************************
  *
- *           DeletePage
+ *           DeleteMacro
  */
-static void HLPFILE_DeletePage(HLPFILE_PAGE* page)
+static void HLPFILE_DeleteMacro(HLPFILE_MACRO* macro)
 {
-    HLPFILE_PAGE* next;
+    HLPFILE_MACRO*      next;
 
-    while (page)
+    while (macro)
     {
-        next = page->next;
-        HLPFILE_DeleteParagraph(page->first_paragraph);
-        HeapFree(GetProcessHeap(), 0, page);
-        page = next;
+        next = macro->next;
+        HeapFree(GetProcessHeap(), 0, macro);
+        macro = next;
     }
 }
 
 /***********************************************************************
  *
- *           DeleteMacro
+ *           DeletePage
  */
-static void HLPFILE_DeleteMacro(HLPFILE_MACRO* macro)
+static void HLPFILE_DeletePage(HLPFILE_PAGE* page)
 {
-    HLPFILE_MACRO*      next;
+    HLPFILE_PAGE* next;
 
-    while (macro)
+    while (page)
     {
-        next = macro->next;
-        HeapFree(GetProcessHeap(), 0, macro);
-        macro = next;
+        next = page->next;
+        HLPFILE_DeleteParagraph(page->first_paragraph);
+        HLPFILE_DeleteMacro(page->first_macro);
+        HeapFree(GetProcessHeap(), 0, page);
+        page = next;
     }
 }
 
@@ -1836,13 +1871,3 @@ void HLPFILE_FreeHlpFile(HLPFILE* hlpfile)
     if (hlpfile->lpszCopyright) HeapFree(GetProcessHeap(), 0, hlpfile->lpszCopyright);
     HeapFree(GetProcessHeap(), 0, hlpfile);
 }
-
-/***********************************************************************
- *
- *           FreeHlpFilePage
- */
-void HLPFILE_FreeHlpFilePage(HLPFILE_PAGE* page)
-{
-    if (!page) return;
-    HLPFILE_FreeHlpFile(page->file);
-}
diff --git a/programs/winhelp/hlpfile.h b/programs/winhelp/hlpfile.h
index f8a6f07561b..f5a4ef94047 100644
--- a/programs/winhelp/hlpfile.h
+++ b/programs/winhelp/hlpfile.h
@@ -71,15 +71,26 @@ typedef struct tagHlpFileParagraph
     struct tagHlpFileParagraph* next;
 } HLPFILE_PARAGRAPH;
 
+typedef struct tagHlpFileMacro
+{
+    LPCSTR                      lpszMacro;
+    struct tagHlpFileMacro*     next;
+} HLPFILE_MACRO;
+
 typedef struct tagHlpFilePage
 {
     LPSTR                       lpszTitle;
     HLPFILE_PARAGRAPH*          first_paragraph;
+    HLPFILE_MACRO*              first_macro;
 
     unsigned                    wNumber;
     unsigned                    offset;
     struct tagHlpFilePage*      next;
     struct tagHlpFilePage*      prev;
+
+    DWORD                       browse_bwd;
+    DWORD                       browse_fwd;
+
     struct tagHlpFileFile*      file;
 } HLPFILE_PAGE;
 
@@ -89,12 +100,6 @@ typedef struct
     unsigned long               offset;
 } HLPFILE_CONTEXT;
 
-typedef struct tagHlpFileMacro
-{
-    LPCSTR                      lpszMacro;
-    struct tagHlpFileMacro*     next;
-} HLPFILE_MACRO;
-
 typedef struct
 {
     LOGFONT                     LogFont;
@@ -132,9 +137,9 @@ typedef struct tagHlpFileFile
     HLPFILE_WINDOWINFO*         windows;
 } HLPFILE;
 
-HLPFILE      *HLPFILE_ReadHlpFile(LPCSTR lpszPath);
-HLPFILE_PAGE *HLPFILE_Contents(HLPFILE* hlpfile);
-HLPFILE_PAGE *HLPFILE_PageByHash(HLPFILE* hlpfile, LONG wNum);
+HLPFILE*      HLPFILE_ReadHlpFile(LPCSTR lpszPath);
+HLPFILE_PAGE* HLPFILE_Contents(HLPFILE* hlpfile);
+HLPFILE_PAGE* HLPFILE_PageByHash(HLPFILE* hlpfile, LONG lHash);
+HLPFILE_PAGE* HLPFILE_PageByOffset(HLPFILE* hlpfile, LONG offset);
 LONG          HLPFILE_Hash(LPCSTR lpszContext);
-VOID          HLPFILE_FreeHlpFilePage(HLPFILE_PAGE*);
-VOID          HLPFILE_FreeHlpFile(HLPFILE*);
+void          HLPFILE_FreeHlpFile(HLPFILE*);
diff --git a/programs/winhelp/macro.c b/programs/winhelp/macro.c
index fb3b39696a3..dcf87502d47 100644
--- a/programs/winhelp/macro.c
+++ b/programs/winhelp/macro.c
@@ -222,12 +222,32 @@ void MACRO_AppendItem(LPCSTR str1, LPCSTR str2, LPCSTR str3, LPCSTR str4)
 
 void MACRO_Back(void)
 {
-    WINE_FIXME("()\n");
+    WINHELP_WINDOW* win = Globals.active_win;
+
+    WINE_TRACE("()\n");
+
+    if (win && win->backIndex >= 2)
+        WINHELP_CreateHelpWindow(win->back[--win->backIndex - 1],
+                                 win->info, SW_SHOW);
 }
 
 void MACRO_BackFlush(void)
 {
-    WINE_FIXME("()\n");
+    WINHELP_WINDOW* win = Globals.active_win;
+
+    WINE_TRACE("()\n");
+
+    if (win)
+    {
+        int     i;
+
+        for (i = 0; i < win->backIndex; i++)
+        {
+            HLPFILE_FreeHlpFile(win->back[i]->file);
+            win->back[i] = NULL;
+        }
+        win->backIndex = 0;
+    }
 }
 
 void MACRO_BookmarkDefine(void)
@@ -599,7 +619,14 @@ void MACRO_HelpOnTop(void)
 
 void MACRO_History(void)
 {
-    WINE_FIXME("()\n");
+    WINE_TRACE("()\n");
+
+    if (Globals.active_win && !Globals.active_win->hHistoryWnd)
+    {
+        HWND hWnd = CreateWindow(HISTORY_WIN_CLASS_NAME, "History", WS_OVERLAPPEDWINDOW,
+                                 0, 0, 0, 0, 0, 0, Globals.hInstance, Globals.active_win);
+        ShowWindow(hWnd, SW_NORMAL);
+    }
 }
 
 void MACRO_IfThen(BOOL b, LPCSTR t)
@@ -717,7 +744,9 @@ void MACRO_Next(void)
     HLPFILE_PAGE*   page;
 
     WINE_TRACE("()\n");
-    if ((page = Globals.active_win->page->next) != NULL)
+    page = Globals.active_win->page;
+    page = HLPFILE_PageByOffset(page->file, page->browse_fwd);
+    if (page)
     {
         page->file->wRefCount++;
         WINHELP_CreateHelpWindow(page, Globals.active_win->info, SW_NORMAL);
@@ -754,7 +783,9 @@ void MACRO_Prev(void)
     HLPFILE_PAGE*   page;
 
     WINE_TRACE("()\n");
-    if ((page = Globals.active_win->page->prev) != NULL)
+    page = Globals.active_win->page;
+    page = HLPFILE_PageByOffset(page->file, page->browse_bwd);
+    if (page)
     {
         page->file->wRefCount++;
         WINHELP_CreateHelpWindow(page, Globals.active_win->info, SW_NORMAL);
diff --git a/programs/winhelp/string.c b/programs/winhelp/string.c
index 17da0f88fd8..4809739d65c 100644
--- a/programs/winhelp/string.c
+++ b/programs/winhelp/string.c
@@ -24,6 +24,7 @@ char MAIN_WIN_CLASS_NAME[]       = "MS_WINHELP";
 char BUTTON_BOX_WIN_CLASS_NAME[] = "WHButtonBox";
 char TEXT_WIN_CLASS_NAME[]       = "WHText";
 char SHADOW_WIN_CLASS_NAME[]     = "WHShadow";
+char HISTORY_WIN_CLASS_NAME[]    = "WHHistory";
 char STRING_BUTTON[]             = "BUTTON";
 
 /* Resource names */
diff --git a/programs/winhelp/winhelp.c b/programs/winhelp/winhelp.c
index 8fa8446f544..3d9c08161f6 100644
--- a/programs/winhelp/winhelp.c
+++ b/programs/winhelp/winhelp.c
@@ -38,6 +38,7 @@ static BOOL    WINHELP_RegisterWinClasses(void);
 static LRESULT CALLBACK WINHELP_MainWndProc(HWND, UINT, WPARAM, LPARAM);
 static LRESULT CALLBACK WINHELP_TextWndProc(HWND, UINT, WPARAM, LPARAM);
 static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND, UINT, WPARAM, LPARAM);
+static LRESULT CALLBACK WINHELP_HistoryWndProc(HWND, UINT, WPARAM, LPARAM);
 static void    WINHELP_CheckPopup(UINT);
 static BOOL    WINHELP_SplitLines(HWND hWnd, LPSIZE);
 static void    WINHELP_InitFonts(HWND hWnd);
@@ -104,9 +105,9 @@ HLPFILE_WINDOWINFO*     WINHELP_GetWindowInfo(HLPFILE* hlpfile, LPCSTR name)
     {
         strcpy(mwi.type, "primary");
         strcpy(mwi.name, "main");
-        LoadString(Globals.hInstance, STID_WINE_HELP, 
-                   mwi.caption, sizeof(mwi.caption));
-        /*strcpy(mwi.caption, hlpfile->lpszTitle); */
+        if (!LoadString(Globals.hInstance, STID_WINE_HELP, 
+                        mwi.caption, sizeof(mwi.caption)))
+            strcpy(mwi.caption, hlpfile->lpszTitle);
         mwi.origin.x = mwi.origin.y = mwi.size.cx = mwi.size.cy = CW_USEDEFAULT;
         mwi.style = SW_SHOW;
         mwi.sr_color = mwi.sr_color = 0xFFFFFF;
@@ -214,7 +215,7 @@ int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
  */
 static BOOL WINHELP_RegisterWinClasses(void)
 {
-    WNDCLASS class_main, class_button_box, class_text, class_shadow;
+    WNDCLASS class_main, class_button_box, class_text, class_shadow, class_history;
 
     class_main.style               = CS_HREDRAW | CS_VREDRAW;
     class_main.lpfnWndProc         = WINHELP_MainWndProc;
@@ -241,10 +242,15 @@ static BOOL WINHELP_RegisterWinClasses(void)
     class_shadow.hbrBackground     = GetStockObject(GRAY_BRUSH);
     class_shadow.lpszClassName     = SHADOW_WIN_CLASS_NAME;
 
+    class_history = class_main;
+    class_history.lpfnWndProc      = WINHELP_HistoryWndProc;
+    class_history.lpszClassName    = HISTORY_WIN_CLASS_NAME;
+
     return (RegisterClass(&class_main) &&
             RegisterClass(&class_button_box) &&
             RegisterClass(&class_text) &&
-            RegisterClass(&class_shadow));
+            RegisterClass(&class_shadow) &&
+            RegisterClass(&class_history));
 }
 
 typedef struct
@@ -310,6 +316,96 @@ static LRESULT  WINHELP_HandleCommand(HWND hSrcWnd, LPARAM lParam)
     return 0L;
 }
 
+/******************************************************************
+ *		WINHELP_ReuseWindow
+ *
+ *
+ */
+static BOOL     WINHELP_ReuseWindow(WINHELP_WINDOW* win, WINHELP_WINDOW* oldwin, 
+                                    HLPFILE_PAGE* page, int nCmdShow)
+{
+    int                 i;
+
+    win->hMainWnd      = oldwin->hMainWnd;
+    win->hButtonBoxWnd = oldwin->hButtonBoxWnd;
+    win->hTextWnd      = oldwin->hTextWnd;
+    win->hHistoryWnd   = oldwin->hHistoryWnd;
+    oldwin->hMainWnd = oldwin->hButtonBoxWnd = oldwin->hTextWnd = oldwin->hHistoryWnd = 0;
+
+    SetWindowLong(win->hMainWnd,      0, (LONG)win);
+    SetWindowLong(win->hButtonBoxWnd, 0, (LONG)win);
+    SetWindowLong(win->hTextWnd,      0, (LONG)win);
+    SetWindowLong(win->hHistoryWnd,   0, (LONG)win);
+
+    WINHELP_InitFonts(win->hMainWnd);
+
+    if (page)
+        SetWindowText(win->hMainWnd, page->file->lpszTitle);
+
+    WINHELP_SetupText(win->hTextWnd);
+    InvalidateRect(win->hTextWnd, NULL, TRUE);
+    SendMessage(win->hMainWnd, WM_USER, 0, 0);
+    ShowWindow(win->hMainWnd, nCmdShow);
+    UpdateWindow(win->hTextWnd);
+
+    if (!(win->info->win_style & WS_POPUP))
+    {
+        unsigned        num;
+
+        memcpy(win->history, oldwin->history, sizeof(win->history));
+        win->histIndex = oldwin->histIndex;
+
+        /* FIXME: when using back, we shouldn't update the history... */
+
+        if (page)
+        {
+            for (i = 0; i < win->histIndex; i++)
+                if (win->history[i] == page) break;
+
+            /* if the new page is already in the history, do nothing */
+            if (i == win->histIndex)
+            {
+                num = sizeof(win->history) / sizeof(win->history[0]);
+                if (win->histIndex == num)
+                {
+                    /* we're full, remove latest entry */
+                    HLPFILE_FreeHlpFile(win->history[0]->file);
+                    memmove(&win->history[0], &win->history[1], 
+                            (num - 1) * sizeof(win->history[0]));
+                    win->histIndex--;
+                }
+                win->history[win->histIndex++] = page;
+                page->file->wRefCount++;
+                if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE);
+            }
+        }
+
+        memcpy(win->back, oldwin->back, sizeof(win->back));
+        win->backIndex = oldwin->backIndex;
+
+        if (page)
+        {
+            num = sizeof(win->back) / sizeof(win->back[0]);
+            if (win->backIndex == num)
+            {
+                /* we're full, remove latest entry */
+                HLPFILE_FreeHlpFile(win->back[0]->file);
+                memmove(&win->back[0], &win->back[1], 
+                        (num - 1) * sizeof(win->back[0]));
+                win->backIndex--;
+            }
+            win->back[win->backIndex++] = page;
+            page->file->wRefCount++;
+        }
+    }
+    else
+        win->backIndex = win->histIndex = 0;
+
+    oldwin->histIndex = oldwin->backIndex = 0;
+    WINHELP_DeleteWindow(oldwin);
+    return TRUE;
+}
+
 /***********************************************************************
  *
  *           WINHELP_CreateHelpWindow
@@ -320,8 +416,10 @@ BOOL WINHELP_CreateHelpWindow(HLPFILE_PAGE* page, HLPFILE_WINDOWINFO* wi,
     WINHELP_WINDOW *win, *oldwin;
     HWND hWnd;
     BOOL bPrimary;
+    BOOL bPopup;
 
     bPrimary = !lstrcmpi(wi->name, "main");
+    bPopup = wi->win_style & WS_POPUP;
 
     /* Initialize WINHELP_WINDOW struct */
     win = HeapAlloc(GetProcessHeap(), 0,
@@ -341,6 +439,7 @@ BOOL WINHELP_CreateHelpWindow(HLPFILE_PAGE* page, HLPFILE_WINDOWINFO* wi,
     win->hButtonBoxWnd = 0;
     win->hTextWnd = 0;
     win->hShadowWnd = 0;
+    win->hHistoryWnd = 0;
 
     win->hArrowCur = LoadCursorA(0, IDC_ARROWA);
     win->hHandCur = LoadCursorA(0, IDC_HANDA);
@@ -372,49 +471,30 @@ BOOL WINHELP_CreateHelpWindow(HLPFILE_PAGE* page, HLPFILE_WINDOWINFO* wi,
         HLPFILE_MACRO  *macro;
         for (macro = page->file->first_macro; macro; macro = macro->next)
             MACRO_ExecuteMacro(macro->lpszMacro);
+
+        for (macro = page->first_macro; macro; macro = macro->next)
+            MACRO_ExecuteMacro(macro->lpszMacro);
     }
 
     /* Reuse existing window */
-    if (!(wi->win_style & WS_POPUP))
+    if (!bPopup)
     {
         for (oldwin = win->next; oldwin; oldwin = oldwin->next)
         {
             if (!lstrcmpi(oldwin->lpszName, wi->name))
             {
-                WINHELP_BUTTON *button;
-
-                win->hMainWnd      = oldwin->hMainWnd;
-                win->hButtonBoxWnd = oldwin->hButtonBoxWnd;
-                win->hTextWnd      = oldwin->hTextWnd;
-                oldwin->hMainWnd = oldwin->hButtonBoxWnd = oldwin->hTextWnd = 0;
-
-                SetWindowLong(win->hMainWnd,      0, (LONG)win);
-                SetWindowLong(win->hButtonBoxWnd, 0, (LONG)win);
-                SetWindowLong(win->hTextWnd,      0, (LONG)win);
-
-                WINHELP_InitFonts(win->hMainWnd);
-
-                if (page) {
-                    SetWindowText(win->hMainWnd, page->file->lpszTitle);
-                }
-
-                WINHELP_SetupText(win->hTextWnd);
-                InvalidateRect(win->hTextWnd, NULL, TRUE);
-                SendMessage(win->hMainWnd, WM_USER, 0, 0);
-                ShowWindow(win->hMainWnd, nCmdShow);
-                UpdateWindow(win->hTextWnd);
-
-
-                for (button = oldwin->first_button; button; button = button->next)
-                    DestroyWindow(button->hWnd);
-
-                WINHELP_DeleteWindow(oldwin);
-                return TRUE;
+                return WINHELP_ReuseWindow(win, oldwin, page, nCmdShow);
             }
         }
+
+        win->histIndex = win->backIndex = 1;
+        win->history[0] = win->back[0] = page;
+        page->file->wRefCount += 2;
     }
+    else
+        win->histIndex = win->backIndex = 0;
 
-    hWnd = CreateWindow((wi->win_style & WS_POPUP) ? TEXT_WIN_CLASS_NAME : MAIN_WIN_CLASS_NAME,
+    hWnd = CreateWindow(bPopup ? TEXT_WIN_CLASS_NAME : MAIN_WIN_CLASS_NAME,
                         wi->caption, wi->win_style,
                         wi->origin.x, wi->origin.y, wi->size.cx, wi->size.cy,
                         0, bPrimary ? LoadMenu(Globals.hInstance, MAKEINTRESOURCE(MAIN_MENU)) : 0,
@@ -854,7 +934,6 @@ static LRESULT CALLBACK WINHELP_TextWndProc(HWND hWnd, UINT msg, WPARAM wParam,
         win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
 
         if (hWnd == Globals.hPopupWnd) Globals.hPopupWnd = 0;
-        if (win->hShadowWnd) DestroyWindow(win->hShadowWnd);
 
         bExit = (Globals.wVersion >= 4 && !lstrcmpi(win->lpszName, "main"));
 
@@ -870,6 +949,72 @@ static LRESULT CALLBACK WINHELP_TextWndProc(HWND hWnd, UINT msg, WPARAM wParam,
     return DefWindowProc(hWnd, msg, wParam, lParam);
 }
 
+/******************************************************************
+ *		WINHELP_HistoryWndProc
+ *
+ *
+ */
+static LRESULT CALLBACK WINHELP_HistoryWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    WINHELP_WINDOW*     win;
+    PAINTSTRUCT         ps;
+    HDC                 hDc;
+    TEXTMETRIC          tm;
+    int                 i;
+    RECT                r;
+
+    switch (msg)
+    {
+    case WM_NCCREATE:
+        win = (WINHELP_WINDOW*)((LPCREATESTRUCT)lParam)->lpCreateParams;
+        SetWindowLong(hWnd, 0, (LONG)win);
+        win->hHistoryWnd = hWnd;
+        break;
+    case WM_CREATE:
+        win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
+        hDc = GetDC(hWnd);
+        GetTextMetrics(hDc, &tm);
+        GetWindowRect(hWnd, &r);
+
+        r.right = r.left + 30 * tm.tmAveCharWidth;
+        r.bottom = r.top + (sizeof(win->history) / sizeof(win->history[0])) * tm.tmHeight;
+        AdjustWindowRect(&r, GetWindowLong(hWnd, GWL_STYLE), FALSE);
+        if (r.left < 0) {r.right -= r.left; r.left = 0;}
+        if (r.top < 0) {r.bottom -= r.top; r.top = 0;}
+
+        MoveWindow(hWnd, r.left, r.top, r.right, r.bottom, TRUE);
+        ReleaseDC(hWnd, hDc);
+        break;
+    case WM_LBUTTONDOWN:
+        win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
+        hDc = GetDC(hWnd);
+        GetTextMetrics(hDc, &tm);
+        i = HIWORD(lParam) / tm.tmHeight;
+        if (i < win->histIndex)
+            WINHELP_CreateHelpWindow(win->history[i], win->info, SW_SHOW);
+        ReleaseDC(hWnd, hDc);
+        break;
+    case WM_PAINT:
+        hDc = BeginPaint(hWnd, &ps);
+        win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
+        GetTextMetrics(hDc, &tm);
+
+        for (i = 0; i < win->histIndex; i++)
+        {
+            TextOut(hDc, 0, i * tm.tmHeight, win->history[i]->lpszTitle, 
+                    strlen(win->history[i]->lpszTitle));
+        }
+        EndPaint(hWnd, &ps);
+        break;
+    case WM_DESTROY:
+        win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0);
+        if (hWnd == win->hHistoryWnd)
+            win->hHistoryWnd = 0;
+        break;
+    }
+    return DefWindowProc(hWnd, msg, wParam, lParam);
+}
+
 /***********************************************************************
  *
  *           SetupText
@@ -1315,19 +1460,41 @@ static void WINHELP_DeleteLines(WINHELP_WINDOW *win)
  *
  *           WINHELP_DeleteWindow
  */
-static void WINHELP_DeleteWindow(WINHELP_WINDOW *win)
+static void WINHELP_DeleteWindow(WINHELP_WINDOW* win)
 {
-    WINHELP_WINDOW **w;
+    WINHELP_WINDOW**    w;
+    int                 i;
+    WINHELP_BUTTON*     b;
+    WINHELP_BUTTON*     bp;
 
     for (w = &Globals.win_list; *w; w = &(*w)->next)
+    {
         if (*w == win)
         {
             *w = win->next;
             break;
         }
+    }
+
+    for (b = win->first_button; b; b = bp)
+    {
+        DestroyWindow(b->hWnd);
+        bp = b->next;
+        HeapFree(GetProcessHeap(), 0, b);
+    }
 
     if (win->hShadowWnd) DestroyWindow(win->hShadowWnd);
-    HLPFILE_FreeHlpFilePage(win->page);
+    if (win->hHistoryWnd) DestroyWindow(win->hHistoryWnd);
+
+    for (i = 0; i < win->histIndex; i++)
+    {
+        HLPFILE_FreeHlpFile(win->history[i]->file);
+    }
+
+    for (i = 0; i < win->backIndex; i++)
+        HLPFILE_FreeHlpFile(win->back[i]->file);
+
+    if (win->page) HLPFILE_FreeHlpFile(win->page->file);
     WINHELP_DeleteLines(win);
     HeapFree(GetProcessHeap(), 0, win);
 }
@@ -1359,7 +1526,7 @@ static void WINHELP_InitFonts(HWND hWnd)
     {
         INT i;
 
-        for(i = 0; i < FONTS_LEN; i++)
+        for (i = 0; i < FONTS_LEN; i++)
 	{
             fonts[i] = CreateFontIndirect(&logfontlist[i]);
 	}
diff --git a/programs/winhelp/winhelp.h b/programs/winhelp/winhelp.h
index 9669561b955..cb21d1b6113 100644
--- a/programs/winhelp/winhelp.h
+++ b/programs/winhelp/winhelp.h
@@ -95,6 +95,7 @@ typedef struct tagWinHelp
     HWND                hButtonBoxWnd;
     HWND                hTextWnd;
     HWND                hShadowWnd;
+    HWND                hHistoryWnd;
 
     HFONT*              fonts;
     UINT                fonts_len;
@@ -104,6 +105,12 @@ typedef struct tagWinHelp
 
     HLPFILE_WINDOWINFO* info;
 
+    /* FIXME: for now it's a fixed size */
+    HLPFILE_PAGE*       history[40];
+    unsigned            histIndex;
+    HLPFILE_PAGE*       back[40];
+    unsigned            backIndex;
+
     struct tagWinHelp*  next;
 } WINHELP_WINDOW;
 
@@ -131,6 +138,7 @@ extern char MAIN_WIN_CLASS_NAME[];
 extern char BUTTON_BOX_WIN_CLASS_NAME[];
 extern char TEXT_WIN_CLASS_NAME[];
 extern char SHADOW_WIN_CLASS_NAME[];
+extern char HISTORY_WIN_CLASS_NAME[];
 extern char STRING_BUTTON[];
 extern char STRING_MENU_Xx[];
 extern char STRING_DIALOG_TEST[];
-- 
GitLab