Implemented the More Windows... menu item for MDI.

diff --git a/include/mdi.h b/include/mdi.h
index 2ced23a..3b22a2f 100644
--- a/include/mdi.h
+++ b/include/mdi.h
@@ -20,6 +20,12 @@
 
 #define WM_MDICALCCHILDSCROLL   0x10AC /* this is exactly what Windows uses */
 
+/* "More Windows..." definitions */
+#define MDI_MOREWINDOWSLIMIT    9       /* after this number of windows, a "More Windows..." 
+                                           option will appear under the Windows menu */
+#define MDI_IDC_LISTBOX         100
+#define MDI_IDS_MOREWINDOWS     13
+
 extern LRESULT WINAPI MDIClientWndProc( HWND hwnd, UINT message, 
                                         WPARAM wParam, LPARAM lParam );
 
diff --git a/resources/sysres_En.rc b/resources/sysres_En.rc
index 3bb497a..67fc806 100644
--- a/resources/sysres_En.rc
+++ b/resources/sysres_En.rc
@@ -46,3 +46,18 @@
         PUSHBUTTON "&No", 7, 304, 56, 40, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP
 END
 
+MDI_MOREWINDOWS DIALOG FIXED IMPURE 20, 20, 232, 122
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Select Window"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LISTBOX         MDI_IDC_LISTBOX, 5, 7, 222, 90, WS_VSCROLL | WS_HSCROLL /* defined in mdi.h */
+    DEFPUSHBUTTON   "OK", IDOK, 75, 100, 35, 14
+    PUSHBUTTON      "Cancel", IDCANCEL, 120, 100, 35, 14
+END
+
+
+STRINGTABLE DISCARDABLE 
+{ 
+    MDI_IDS_MOREWINDOWS  "&More Windows..." /* defined in mdi.h */
+}
diff --git a/resources/user32.rc b/resources/user32.rc
index 3518df0..1b54261 100644
--- a/resources/user32.rc
+++ b/resources/user32.rc
@@ -7,6 +7,7 @@
 #include "winuser.h"
 #include "winnls.h"
 #include "dlgs.h"
+#include "mdi.h"
 
 /*
  * Everything that does not depend on language,
diff --git a/windows/mdi.c b/windows/mdi.c
index c143aa3..3d5cabf 100644
--- a/windows/mdi.c
+++ b/windows/mdi.c
@@ -6,11 +6,64 @@
  * This file contains routines to support MDI (Multiple Document
  * Interface) features .
  *
- * Notes: Fairly complete implementation. Any volunteers for 
- *	  "More windows..." stuff?
- *
+ * Notes: Fairly complete implementation.
  *        Also, Excel and WinWord do _not_ use MDI so if you're trying
  *	  to fix them look elsewhere. 
+ *
+ * Notes on how the "More Windows..." is implemented:
+ *
+ *      When we have more than 9 opened windows, a "More Windows..."
+ *      option appears in the "Windows" menu. Each child window has
+ *      a WND* associated with it, accesible via the children list of
+ *      the parent window. This WND* has a wIDmenu member, which reflects
+ *      the position of the child in the window list. For example, with
+ *      9 child windows, we could have the following pattern:
+ *
+ *
+ *
+ *                Name of the child window    pWndChild->wIDmenu
+ *                     Doc1                       5000
+ *                     Doc2                       5001
+ *                     Doc3                       5002
+ *                     Doc4                       5003
+ *                     Doc5                       5004
+ *                     Doc6                       5005
+ *                     Doc7                       5006
+ *                     Doc8                       5007
+ *                     Doc9                       5008
+ *
+ *
+ *       The "Windows" menu, as the "More windows..." dialog, are constructed
+ *       in this order. If we add a child, we would have the following list:
+ *
+ *
+ *               Name of the child window    pWndChild->wIDmenu
+ *                     Doc1                       5000
+ *                     Doc2                       5001
+ *                     Doc3                       5002
+ *                     Doc4                       5003
+ *                     Doc5                       5004
+ *                     Doc6                       5005
+ *                     Doc7                       5006
+ *                     Doc8                       5007
+ *                     Doc9                       5008
+ *                     Doc10                      5009
+ *
+ *       But only 5000 to 5008 would be displayed in the "Windows" menu. We want
+ *       the last created child to be in the menu, so we swap the last child with
+ *       the 9th... Doc9 will be accessible via the "More Windows..." option.
+ *
+ *                     Doc1                       5000
+ *                     Doc2                       5001
+ *                     Doc3                       5002
+ *                     Doc4                       5003
+ *                     Doc5                       5004
+ *                     Doc6                       5005
+ *                     Doc7                       5006
+ *                     Doc8                       5007
+ *                     Doc9                       5009
+ *                     Doc10                      5008
+ *
  */
 
 #include <stdlib.h>
@@ -30,6 +83,7 @@
 #include "struct32.h"
 #include "tweak.h"
 #include "debugtools.h"
+#include "dlgs.h"
 
 DEFAULT_DEBUG_CHANNEL(mdi);
 
@@ -45,6 +99,8 @@
 
 static LONG MDI_ChildActivate( WND*, HWND );
 
+static HWND MDI_MoreWindowsDialog(WND*);
+static void MDI_SwapMenuItems(WND *, UINT, UINT);
 /* -------- Miscellaneous service functions ----------
  *
  *			MDI_GetChildByID
@@ -134,15 +190,28 @@
 	/* set correct id */
 	tmpWnd->wIDmenu--;
 
-	n = sprintf(buffer, "%d ",index - clientInfo->idFirstChild);
+	n = sprintf(buffer, "&%d ",index - clientInfo->idFirstChild);
 	if (tmpWnd->text)
             lstrcpynA(buffer + n, tmpWnd->text, sizeof(buffer) - n );	
 
-	/* change menu */
+	/*  change menu if the current child is to be shown in the 
+         *  "Windows" menu
+         */
+        if (index <= clientInfo->idFirstChild + MDI_MOREWINDOWSLIMIT) 
 	ModifyMenuA(clientInfo->hWindowMenu ,index ,MF_BYCOMMAND | MF_STRING,
                       index - 1 , buffer ); 
         WIN_ReleaseWndPtr(tmpWnd);
     }
+
+    /*  We must restore the "More Windows..." option if there is enough child 
+     */
+    if (clientInfo->nActiveChildren - 1 > MDI_MOREWINDOWSLIMIT)
+    {
+        char szTmp[50];
+        LoadStringA(GetModuleHandleA("USER32"), MDI_IDS_MOREWINDOWS, szTmp, 50);
+
+        AppendMenuA(clientInfo->hWindowMenu ,MF_STRING ,clientInfo->idFirstChild + MDI_MOREWINDOWSLIMIT, szTmp );
+    }
     retvalue = TRUE;
 END:
     WIN_ReleaseWndPtr(wndPtr);
@@ -248,9 +317,17 @@
 
         if( ci->nActiveChildren )
         {
-            INT j = i - ci->nActiveChildren + 1;
+            INT j;
             LPWSTR buffer = NULL;
 	    MENUITEMINFOW mii;
+            INT nbWindowsMenuItems; /* num of documents shown + "More Windows..." if present */
+
+            if (ci->nActiveChildren <= MDI_MOREWINDOWSLIMIT)
+                nbWindowsMenuItems = ci->nActiveChildren;
+            else
+                nbWindowsMenuItems = MDI_MOREWINDOWSLIMIT + 1;
+
+            j = i - nbWindowsMenuItems + 1;
 
             for( ; i >= j ; i-- )
             {
@@ -355,6 +432,7 @@
 	    SendMessageA(w->hwndSelf, WM_SETREDRAW, TRUE, 0L );
     }
 
+    if (ci->nActiveChildren <= MDI_MOREWINDOWSLIMIT)
     /* this menu is needed to set a check mark in MDI_ChildActivate */
     if (ci->hWindowMenu != 0)
         AppendMenuA(ci->hWindowMenu ,MF_STRING ,wIDmenu, lpstrDef );
@@ -405,7 +483,29 @@
 	/* All MDI child windows have the WS_EX_MDICHILD style */
 	wnd->dwExStyle |= WS_EX_MDICHILD;
 
+        /*  If we have more than 9 windows, we must insert the new one at the
+         *  9th position in order to see it in the "Windows" menu
+         */
+        if (ci->nActiveChildren > MDI_MOREWINDOWSLIMIT)
+            MDI_SwapMenuItems(wnd->parent, wnd->wIDmenu, ci->idFirstChild + MDI_MOREWINDOWSLIMIT - 1);
+
 	MDI_MenuModifyItem(w ,hwnd); 
+
+        /* Have we hit the "More Windows..." limit? If so, we must 
+         * add a "More Windows..." option 
+         */
+        if (ci->nActiveChildren == MDI_MOREWINDOWSLIMIT + 1)
+        {
+            char szTmp[50];
+            LoadStringA(GetModuleHandleA("USER32"), MDI_IDS_MOREWINDOWS, szTmp, 50);
+
+            ModifyMenuA(ci->hWindowMenu,
+                        ci->idFirstChild + MDI_MOREWINDOWSLIMIT, 
+                        MF_BYCOMMAND | MF_STRING, 
+                        ci->idFirstChild + MDI_MOREWINDOWSLIMIT, 
+                        szTmp);
+        }
+        
 	if( wnd->dwStyle & WS_MINIMIZE && ci->hwndActiveChild )
 	    ShowWindow( hwnd, SW_SHOWMINNOACTIVE );
 	else
@@ -540,6 +640,8 @@
 
     if( childPtr )
     {
+        MDI_MenuDeleteItem(w_parent, child);
+
         if( child == ci->hwndActiveChild )
         {
 	    MDI_SwitchActiveChild(parent, child, TRUE);
@@ -557,7 +659,6 @@
                 MDI_ChildActivate(w_parent, 0);
 	    }
 	}
-        MDI_MenuDeleteItem(w_parent, child);
 
         WIN_ReleaseWndPtr(childPtr);
 	
@@ -621,8 +722,16 @@
                         (LPARAM)hWndChild);
         /* uncheck menu item */
        	if( clientInfo->hWindowMenu )
+        {
+            WORD wPrevID = wndPrev->wIDmenu - clientInfo->idFirstChild;
+
+            if (wPrevID < MDI_MOREWINDOWSLIMIT)
        	        CheckMenuItem( clientInfo->hWindowMenu,
                                  wndPrev->wIDmenu, 0);
+            else
+       	        CheckMenuItem( clientInfo->hWindowMenu,
+                               clientInfo->idFirstChild + MDI_MOREWINDOWSLIMIT - 1, 0);
+    }
     }
 
     /* set appearance */
@@ -650,9 +759,16 @@
 	
     /* check menu item */
     if( clientInfo->hWindowMenu )
-              CheckMenuItem( clientInfo->hWindowMenu,
-                               wndPtr->wIDmenu, MF_CHECKED);
+    {
+          /* The window to be activated must be displayed in the "Windows" menu */
+          if (wndPtr->wIDmenu >= clientInfo->idFirstChild + MDI_MOREWINDOWSLIMIT)
+          {
+              MDI_SwapMenuItems(wndPtr->parent, wndPtr->wIDmenu, clientInfo->idFirstChild + MDI_MOREWINDOWSLIMIT - 1);    
+              MDI_MenuModifyItem(wndPtr->parent ,wndPtr->hwndSelf); 
+          }
 
+          CheckMenuItem(clientInfo->hWindowMenu, wndPtr->wIDmenu, MF_CHECKED);
+    }
     /* bring active child to the top */
     SetWindowPos( hWndChild, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
 
@@ -1349,9 +1465,16 @@
 	    else
 	      {
                 wndPtr = WIN_FindWndPtr(hwndMDIClient);
-                childHwnd = MDI_GetChildByID(wndPtr,wParam );
-                WIN_ReleaseWndPtr(wndPtr);
+                ci     = (MDICLIENTINFO*)wndPtr->wExtra;
 
+                if (wParam - ci->idFirstChild == MDI_MOREWINDOWSLIMIT)
+                    /* User chose "More Windows..." */
+                    childHwnd = MDI_MoreWindowsDialog(wndPtr);
+                else
+                    /* User chose one of the windows listed in the "Windows" menu */
+                childHwnd = MDI_GetChildByID(wndPtr,wParam );
+
+                WIN_ReleaseWndPtr(wndPtr);
  	    	if( childHwnd )
 	            SendMessage16(hwndMDIClient, WM_MDIACTIVATE,
                                   (WPARAM16)childHwnd , 0L);
@@ -2170,3 +2293,162 @@
     return 0;
 }
 
+/************************************************************************
+ *              "More Windows..." functionality
+ */
+
+/*              MDI_MoreWindowsDlgProc
+ *
+ *    This function will process the messages sent to the "More Windows..."
+ *    dialog.
+ *    Return values:  0    = cancel pressed
+ *                    HWND = ok pressed or double-click in the list...
+ *
+ */
+
+static BOOL WINAPI MDI_MoreWindowsDlgProc (HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch (iMsg)
+    {
+       case WM_INITDIALOG:
+       {
+           WND  *pWnd;
+           UINT widest       = 0;
+           UINT length;
+           UINT i;
+           WND  *pParentWnd  = (WND *)lParam;
+           MDICLIENTINFO *ci = (MDICLIENTINFO*)pParentWnd->wExtra;
+           HWND hListBox = GetDlgItem(hDlg, MDI_IDC_LISTBOX);
+
+           /* Fill the list, sorted by id... */
+           for (i = 0; i < ci->nActiveChildren; i++)
+           {
+
+               /* Find the window with the current ID */
+               for (pWnd = WIN_LockWndPtr(pParentWnd->child); pWnd; WIN_UpdateWndPtr(&pWnd, pWnd->next))
+                   if (pWnd->wIDmenu == ci->idFirstChild + i)
+                       break;
+
+               SendMessageA(hListBox, LB_ADDSTRING, 0, (LPARAM) pWnd->text);
+               SendMessageA(hListBox, LB_SETITEMDATA, i, (LPARAM) pWnd);
+               length = strlen(pWnd->text);
+               WIN_ReleaseWndPtr(pWnd);
+
+               if (length > widest)
+                   widest = length;
+           }
+           /* Make sure the horizontal scrollbar scrolls ok */
+           SendMessageA(hListBox, LB_SETHORIZONTALEXTENT, widest * 6, 0);
+
+           /* Set the current selection */
+           SendMessageA(hListBox, LB_SETCURSEL, MDI_MOREWINDOWSLIMIT, 0);
+           return TRUE;
+       }
+
+       case WM_COMMAND:
+           switch (LOWORD(wParam))
+           {
+                case IDOK:
+                {
+                    /*  windows are sorted by menu ID, so we must return the
+                     *  window associated to the given id
+                     */
+                    HWND hListBox     = GetDlgItem(hDlg, MDI_IDC_LISTBOX);
+                    UINT index        = SendMessageA(hListBox, LB_GETCURSEL, 0, 0);
+                    WND* pWnd         = (WND*) SendMessageA(hListBox, LB_GETITEMDATA, index, 0);
+
+                    EndDialog(hDlg, pWnd->hwndSelf);
+                    return TRUE;
+                }
+                case IDCANCEL:
+                    EndDialog(hDlg, 0);
+                    return TRUE;
+
+                default:
+                    switch (HIWORD(wParam))
+                    {
+                        case LBN_DBLCLK:
+                        {
+                            /*  windows are sorted by menu ID, so we must return the
+                             *  window associated to the given id
+                             */
+                            HWND hListBox     = GetDlgItem(hDlg, MDI_IDC_LISTBOX);
+                            UINT index        = SendMessageA(hListBox, LB_GETCURSEL, 0, 0);
+                            WND* pWnd         = (WND*) SendMessageA(hListBox, LB_GETITEMDATA, index, 0);
+
+                            EndDialog(hDlg, pWnd->hwndSelf);
+                            return TRUE;
+                        }
+                    }
+                    break;
+           }
+           break;
+    }
+    return FALSE;
+}
+
+/*
+ *
+ *                      MDI_MoreWindowsDialog
+ *
+ *     Prompts the user with a listbox containing the opened
+ *     documents. The user can then choose a windows and click
+ *     on OK to set the current window to the one selected, or
+ *     CANCEL to cancel. The function returns a handle to the
+ *     selected window.
+ */
+
+static HWND MDI_MoreWindowsDialog(WND* wndPtr)
+{
+    LPCVOID template;
+    HRSRC hRes;
+    HANDLE hDlgTmpl;
+
+    hRes = FindResourceA(GetModuleHandleA("USER32"), "MDI_MOREWINDOWS", RT_DIALOGA);
+
+    if (hRes == 0)
+        return 0;
+
+    hDlgTmpl = LoadResource(GetModuleHandleA("USER32"), hRes );
+
+    if (hDlgTmpl == 0)
+        return 0;
+
+    template = LockResource( hDlgTmpl );
+
+    if (template == 0)
+        return 0;
+
+    return (HWND) DialogBoxIndirectParamA(GetModuleHandleA("USER32"),
+                                          (LPDLGTEMPLATEA) template,
+                                          wndPtr->hwndSelf,
+                                          (DLGPROC) MDI_MoreWindowsDlgProc,
+                                          (LPARAM) wndPtr);
+}
+
+/*
+ *
+ *                      MDI_SwapMenuItems
+ *
+ *      Will swap the menu IDs for the given 2 positions.
+ *      pos1 and pos2 are menu IDs
+ *     
+ *    
+ */
+
+static void MDI_SwapMenuItems(WND *parentWnd, UINT pos1, UINT pos2)
+{
+    WND *pWnd;
+
+    for (pWnd = WIN_LockWndPtr(parentWnd->child); pWnd; WIN_UpdateWndPtr(&pWnd,pWnd->next))
+    {
+        if (pWnd->wIDmenu == pos1)
+            pWnd->wIDmenu = pos2;
+        else
+            if (pWnd->wIDmenu == pos2)
+                pWnd->wIDmenu = pos1;
+    }
+
+    WIN_ReleaseWndPtr(pWnd);
+}
+