From 8d39ac13676ca900e69e4abfdf4604601c51fbf3 Mon Sep 17 00:00:00 2001
From: patthoyts <patthoyts@users.sourceforge.net>
Date: Tue, 5 Jan 2010 22:36:59 +0000
Subject: Patch 289825: Enable unlimited multiple	file selection from
 the open files dialog

---
 ChangeLog         |   5 +
 win/tkWinDialog.c | 416 +++++++++++++++++++++++++++++++++++++++---------------
 2 files changed, 309 insertions(+), 112 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index d3e2a58..f2096af 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2010-01-05  Pat Thoyts  <patthoyts@users.sourceforge.net>
+
+	* win/tkWinDialog.c: [Patch 2898255] Enable unlimited multiple
+	file selection from the open files dialog (pawlak,fellows,thoyts)
+
 2010-01-05  Donal K. Fellows  <dkf@users.sf.net>
 
 	* generic/tkMenu.c (MenuWidgetObjCmd): [Bug 220950]: Do not delete
diff --git a/win/tkWinDialog.c b/win/tkWinDialog.c
index 93d81b6..f22ea45 100644
--- a/win/tkWinDialog.c
+++ b/win/tkWinDialog.c
@@ -8,7 +8,7 @@
  * See the file "license.terms" for information on usage and redistribution of
  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
  *
- * RCS: @(#) $Id: tkWinDialog.c,v 1.50.2.4 2009/10/22 10:27:58 dkf Exp $
+ * RCS: @(#) $Id: tkWinDialog.c,v 1.50.2.5 2010/01/05 22:36:59 patthoyts Exp $
  *
  */
 
@@ -170,6 +170,21 @@ typedef struct ChooseDir {
 } ChooseDir;
 
 /*
+ * The following structure is used to pass information between GetFileName/W
+ * functions and OFN dialog hook procedures. [Bug 2896501, Patch 2898255]
+ */
+
+typedef struct OFNData {
+    Tcl_Interp *interp;		/* Interp, used only if debug is turned on,
+				 * for setting the "tk_dialog" variable. */
+    int dynFileBufferSize;	/* Dynamic filename buffer size, stored to
+				 * avoid shrinking and expanding the buffer
+				 * when selection changes */
+    char *dynFileBuffer;	/* Dynamic filename buffer, cast to WCHAR* in
+				 * UNICODE procedures */
+} OFNData;
+
+/*
  * Definitions of functions used only in this file.
  */
 
@@ -186,7 +201,7 @@ static int 		GetFileNameW(ClientData clientData,
 static int 		MakeFilter(Tcl_Interp *interp, Tcl_Obj *valuePtr,
 			    Tcl_DString *dsPtr, Tcl_Obj *initialPtr,
 			    int *index);
-static UINT APIENTRY	OFNHookProc(HWND hdlg, UINT uMsg, WPARAM wParam,
+static UINT APIENTRY	OFNHookProcA(HWND hdlg, UINT uMsg, WPARAM wParam,
 			    LPARAM lParam);
 static UINT APIENTRY	OFNHookProcW(HWND hdlg, UINT uMsg, WPARAM wParam,
 			    LPARAM lParam);
@@ -573,12 +588,14 @@ GetFileNameW(
 {
     OPENFILENAMEW ofn;
     WCHAR file[TK_MULTI_MAX_PATH];
+    OFNData ofnData;
+    int cdlgerr;
     int filterIndex, result, winCode, oldMode, i, multi = 0;
     char *extension, *filter, *title;
     Tk_Window tkwin;
     HWND hWnd;
     Tcl_Obj *filterObj, *initialTypeObj, *typeVariableObj;
-    Tcl_DString utfFilterString, utfDirString;
+    Tcl_DString utfFilterString, utfDirString, ds;
     Tcl_DString extString, filterString, dirString, titleString;
     Tcl_Encoding unicodeEncoding = TkWinGetUnicodeEncoding();
     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
@@ -600,6 +617,7 @@ GetFileNameW(
 
     result = TCL_ERROR;
     file[0] = '\0';
+    ZeroMemory(&ofnData, sizeof(OFNData));
 
     /*
      * Parse the arguments.
@@ -674,9 +692,7 @@ GetFileNameW(
 		goto end;
 	    }
 	    break;
-	case FILE_INITFILE: {
-	    Tcl_DString ds;
-
+	case FILE_INITFILE:
 	    if (Tcl_TranslateFileName(interp, string, &ds) == NULL) {
 		goto end;
 	    }
@@ -685,7 +701,6 @@ GetFileNameW(
 		    sizeof(file), NULL, NULL, NULL);
 	    Tcl_DStringFree(&ds);
 	    break;
-	}
 	case FILE_MULTIPLE:
 	    if (Tcl_GetBooleanFromObj(interp, valuePtr, &multi) != TCL_OK) {
 		return TCL_ERROR;
@@ -724,9 +739,9 @@ GetFileNameW(
     ofn.lpstrFile = (WCHAR *) file;
     ofn.nMaxFile = TK_MULTI_MAX_PATH;
     ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR
-	    | OFN_EXPLORER;
+	    | OFN_EXPLORER | OFN_ENABLEHOOK;
     ofn.lpfnHook = (LPOFNHOOKPROC) OFNHookProcW;
-    ofn.lCustData = (LPARAM) interp;
+    ofn.lCustData = (LPARAM) &ofnData;
 
     if (open != 0) {
 	ofn.Flags |= OFN_FILEMUSTEXIST;
@@ -735,11 +750,19 @@ GetFileNameW(
     }
 
     if (tsdPtr->debugFlag != 0) {
-	ofn.Flags |= OFN_ENABLEHOOK;
+	ofnData.interp = interp;
     }
 
     if (multi != 0) {
 	ofn.Flags |= OFN_ALLOWMULTISELECT;
+
+	/*
+	 * Starting buffer size. The buffer will be expanded by the OFN dialog
+	 * procedure when necessary
+	 */
+
+	ofnData.dynFileBufferSize = 1024;
+	ofnData.dynFileBuffer = ckalloc(1024);
     }
 
     if (extension != NULL) {
@@ -813,32 +836,43 @@ GetFileNameW(
 
     /*
      * Process the results.
+     *
+     * Use the CommDlgExtendedError() function to retrieve the error code.
+     * This function can return one of about two dozen codes; most of these
+     * indicate some sort of gross system failure (insufficient memory, bad
+     * window handles, etc.). Most of the error codes will be ignored; as we
+     * find we want more specific error messages for particular errors, we can
+     * extend the code as needed.
      */
 
-    if (winCode != 0) {
+    cdlgerr = CommDlgExtendedError();
+
+    /*
+     * We now allow FNERR_BUFFERTOOSMALL when multiselection is enabled. The
+     * filename buffer has been dynamically allocated by the OFN dialog
+     * procedure to accomodate all selected files.
+     */
+
+    if ((winCode != 0)
+	    || ((cdlgerr == FNERR_BUFFERTOOSMALL)
+		    && (ofn.Flags & OFN_ALLOWMULTISELECT))) {
 	if (ofn.Flags & OFN_ALLOWMULTISELECT) {
 	    /*
-	     * The result in custData->szFile contains many items, separated
-	     * with null characters. It is terminated with two nulls in a row.
-	     * The first element is the directory path.
+	     * The result in dynFileBuffer contains many items, separated by
+	     * NUL characters. It is terminated with two nulls in a row. The
+	     * first element is the directory path.
 	     */
 
-	    WCHAR *files;
-	    Tcl_DString dirBuf;
-	    Tcl_Obj *returnList;
+	    WCHAR *files = (WCHAR *) ofnData.dynFileBuffer;
+	    Tcl_Obj *returnList = Tcl_NewObj();
 	    int count = 0;
 
-	    returnList = Tcl_NewObj();
-	    Tcl_IncrRefCount(returnList);
-
-	    files = ofn.lpstrFile;
-
 	    /*
 	     * Get directory.
 	     */
 
 	    (void) ConvertExternalFilename(unicodeEncoding, (char *) files,
-		    &dirBuf);
+		    &ds);
 
 	    while (*files != '\0') {
 		while (*files != '\0') {
@@ -853,8 +887,8 @@ GetFileNameW(
 		    (void) ConvertExternalFilename(unicodeEncoding,
 			    (char *) files, &filenameBuf);
 
-		    fullnameObj = Tcl_NewStringObj(Tcl_DStringValue(&dirBuf),
-			    Tcl_DStringLength(&dirBuf));
+		    fullnameObj = Tcl_NewStringObj(Tcl_DStringValue(&ds),
+			    Tcl_DStringLength(&ds));
 		    Tcl_AppendToObj(fullnameObj, "/", -1);
 		    Tcl_AppendToObj(fullnameObj, Tcl_DStringValue(&filenameBuf),
 			    Tcl_DStringLength(&filenameBuf));
@@ -869,15 +903,12 @@ GetFileNameW(
 		 */
 
 		Tcl_ListObjAppendElement(NULL, returnList,
-			Tcl_NewStringObj(Tcl_DStringValue(&dirBuf),
-				Tcl_DStringLength(&dirBuf)));
+			Tcl_NewStringObj(Tcl_DStringValue(&ds),
+				Tcl_DStringLength(&ds)));
 	    }
 	    Tcl_SetObjResult(interp, returnList);
-	    Tcl_DecrRefCount(returnList);
-	    Tcl_DStringFree(&dirBuf);
+	    Tcl_DStringFree(&ds);
 	} else {
-	    Tcl_DString ds;
-
 	    Tcl_AppendResult(interp, ConvertExternalFilename(unicodeEncoding,
 		    (char *) ofn.lpstrFile, &ds), NULL);
 	    Tcl_DStringFree(&ds);
@@ -902,29 +933,13 @@ GetFileNameW(
 		result = TCL_ERROR;
 	    }
 	}
+    } else if (cdlgerr == FNERR_INVALIDFILENAME) {
+	Tcl_SetResult(interp, "invalid filename \"", TCL_STATIC);
+	Tcl_AppendResult(interp, ConvertExternalFilename(unicodeEncoding,
+		(char *) ofn.lpstrFile, &ds), "\"", NULL);
+	Tcl_DStringFree(&ds);
     } else {
-	/*
-	 * Use the CommDlgExtendedError() function to retrieve the error code.
-	 * This function can return one of about two dozen codes; most of
-	 * these indicate some sort of gross system failure (insufficient
-	 * memory, bad window handles, etc.). Most of the error codes will be
-	 * ignored; as we find we want more specific error messages for
-	 * particular errors, we can extend the code as needed.
-	 *
-	 * We could also check for FNERR_BUFFERTOOSMALL, but we can't really
-	 * do anything about it when it happens.
-	 */
-
-	if (CommDlgExtendedError() == FNERR_INVALIDFILENAME) {
-	    Tcl_DString ds;
-
-	    Tcl_SetResult(interp, "invalid filename \"", TCL_STATIC);
-	    Tcl_AppendResult(interp, ConvertExternalFilename(unicodeEncoding,
-		    (char *) ofn.lpstrFile, &ds), "\"", NULL);
-	    Tcl_DStringFree(&ds);
-	} else {
-	    result = TCL_OK;
-	}
+	result = TCL_OK;
     }
 
     if (ofn.lpstrTitle != NULL) {
@@ -941,6 +956,10 @@ GetFileNameW(
   end:
     Tcl_DStringFree(&utfDirString);
     Tcl_DStringFree(&utfFilterString);
+    if (ofnData.dynFileBuffer != NULL) {
+	ckfree(ofnData.dynFileBuffer);
+	ofnData.dynFileBuffer = NULL;
+    }
 
     return result;
 }
@@ -950,8 +969,10 @@ GetFileNameW(
  *
  * OFNHookProcW --
  *
- *	Hook function called only if debugging is turned on. Sets the
- *	"tk_dialog" variable when the dialog is ready to receive messages.
+ *	Dialog box hook function. This is used to sets the "tk_dialog"
+ *	variable for test/debugging when the dialog is ready to receive
+ *	messages. When multiple file selection is enabled this function
+ *	is used to process the list of names.
  *
  * Results:
  *	Returns 0 to allow default processing of messages to occur.
@@ -972,9 +993,88 @@ OFNHookProcW(
     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
 	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
     OPENFILENAMEW *ofnPtr;
+    OFNData *ofnData;
 
     if (uMsg == WM_INITDIALOG) {
 	TkWinSetUserData(hdlg, lParam);
+    } else if (uMsg == WM_NOTIFY) {
+	OFNOTIFYW *notifyPtr = (OFNOTIFYW *) lParam;
+
+	if (notifyPtr->hdr.code == CDN_SELCHANGE) {
+	    int dirsize, selsize;
+	    WCHAR *buffer;
+	    int buffersize;
+
+	    /*
+	     * Change of selection. Unscramble the unholy mess that's in the
+	     * selection buffer, resizing it if necessary.
+	     */
+
+	    ofnPtr = notifyPtr->lpOFN;
+	    ofnData = (OFNData *) ofnPtr->lCustData;
+	    buffer = (WCHAR *) ofnData->dynFileBuffer;
+	    hdlg = GetParent(hdlg);
+
+	    selsize = SendMessageW(hdlg, CDM_GETSPEC, 0, 0);
+	    dirsize = SendMessageW(hdlg, CDM_GETFOLDERPATH, 0, 0);
+	    buffersize = (selsize + dirsize + 1) * 2;
+
+	    if (selsize > 1) {
+		if (ofnData->dynFileBufferSize < buffersize) {
+		    buffer = (WCHAR *) ckrealloc((char *) buffer, buffersize);
+		    ofnData->dynFileBufferSize = buffersize;
+		    ofnData->dynFileBuffer = (char *) buffer;
+		}
+
+		SendMessageW(hdlg, CDM_GETFOLDERPATH, dirsize, (int) buffer);
+		buffer += dirsize;
+
+		SendMessageW(hdlg, CDM_GETSPEC, selsize, (int) buffer);
+
+		/*
+		 * If there are multiple files, delete the quotes and change
+		 * every second quote to NULL terminator
+		 */
+
+		if (buffer[0] == '"') {
+		    BOOL findquote = TRUE;
+		    WCHAR *tmp = buffer;
+
+		    while(*buffer != '\0') {
+			if (findquote) {
+			    if (*buffer == '"') {
+				findquote = FALSE;
+			    }
+			    buffer++;
+			} else {
+			    if (*buffer == '"') {
+				findquote = TRUE;
+				*buffer = '\0';
+			    }
+			    *tmp++ = *buffer++;
+			}
+		    }
+		    *tmp = '\0';		/* Second NULL terminator. */
+		} else {
+		    buffer[selsize] = '\0';	/* Second NULL terminator. */
+
+		    /* 
+		     * Replace directory terminating NULL with a backslash.
+		     */
+
+		    buffer--;
+		    *buffer = '\\';
+		}
+	    } else {
+		/*
+		 * Nothing is selected, so just empty the string.
+		 */
+
+		if (buffer != NULL) {
+		    *buffer = '\0';
+		}
+	    }
+	}
     } else if (uMsg == WM_WINDOWPOSCHANGED) {
 	/*
 	 * This message is delivered at the right time to enable Tk to set the
@@ -984,9 +1084,12 @@ OFNHookProcW(
 
 	ofnPtr = (OPENFILENAMEW *) TkWinGetUserData(hdlg);
 	if (ofnPtr != NULL) {
-	    hdlg = GetParent(hdlg);
-	    tsdPtr->debugInterp = (Tcl_Interp *) ofnPtr->lCustData;
-	    Tcl_DoWhenIdle(SetTkDialog, (ClientData) hdlg);
+	    ofnData = (OFNData *) ofnPtr->lCustData;
+	    if (ofnData->interp != NULL) {
+		hdlg = GetParent(hdlg);
+		tsdPtr->debugInterp = ofnData->interp;
+		Tcl_DoWhenIdle(SetTkDialog, hdlg);
+	    }
 	    TkWinSetUserData(hdlg, NULL);
 	}
     }
@@ -1020,12 +1123,14 @@ GetFileNameA(
 {
     OPENFILENAME ofn;
     TCHAR file[TK_MULTI_MAX_PATH], savePath[MAX_PATH];
+    OFNData ofnData;
+    int cdlgerr;
     int filterIndex, result, winCode, oldMode, i, multi = 0;
     char *extension, *filter, *title;
     Tk_Window tkwin;
     HWND hWnd;
     Tcl_Obj *filterObj, *initialTypeObj, *typeVariableObj;
-    Tcl_DString utfFilterString, utfDirString;
+    Tcl_DString utfFilterString, utfDirString, ds;
     Tcl_DString extString, filterString, dirString, titleString;
     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
 	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
@@ -1046,6 +1151,7 @@ GetFileNameA(
 
     result = TCL_ERROR;
     file[0] = '\0';
+    ZeroMemory(&ofnData, sizeof(OFNData));
 
     /*
      * Parse the arguments.
@@ -1119,9 +1225,7 @@ GetFileNameA(
 		goto end;
 	    }
 	    break;
-	case FILE_INITFILE: {
-	    Tcl_DString ds;
-
+	case FILE_INITFILE:
 	    if (Tcl_TranslateFileName(interp, string, &ds) == NULL) {
 		goto end;
 	    }
@@ -1130,7 +1234,6 @@ GetFileNameA(
 		    sizeof(file), NULL, NULL, NULL);
 	    Tcl_DStringFree(&ds);
 	    break;
-	}
 	case FILE_MULTIPLE:
 	    if (Tcl_GetBooleanFromObj(interp, valuePtr, &multi) != TCL_OK) {
 		return TCL_ERROR;
@@ -1162,6 +1265,7 @@ GetFileNameA(
     Tk_MakeWindowExist(tkwin);
     hWnd = Tk_GetHWND(Tk_WindowId(tkwin));
 
+    ZeroMemory(&ofn, sizeof(OPENFILENAMEA));
     ofn.lStructSize = sizeof(ofn);
     ofn.hwndOwner = hWnd;
     ofn.hInstance = TkWinGetHInstance(ofn.hwndOwner);
@@ -1176,12 +1280,12 @@ GetFileNameA(
     ofn.lpstrInitialDir = NULL;
     ofn.lpstrTitle = NULL;
     ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR
-	    | OFN_EXPLORER;
+	    | OFN_EXPLORER | OFN_ENABLEHOOK;
     ofn.nFileOffset = 0;
     ofn.nFileExtension = 0;
     ofn.lpstrDefExt = NULL;
-    ofn.lpfnHook = (LPOFNHOOKPROC) OFNHookProc;
-    ofn.lCustData = (LPARAM) interp;
+    ofn.lpfnHook = (LPOFNHOOKPROC) OFNHookProcA;
+    ofn.lCustData = (LPARAM) &ofnData;
     ofn.lpTemplateName = NULL;
 
     if (open != 0) {
@@ -1191,11 +1295,19 @@ GetFileNameA(
     }
 
     if (tsdPtr->debugFlag != 0) {
-	ofn.Flags |= OFN_ENABLEHOOK;
+	ofnData.interp = interp;
     }
 
     if (multi != 0) {
 	ofn.Flags |= OFN_ALLOWMULTISELECT;
+
+	/*
+	 * Starting buffer size. The buffer will be expanded by the OFN dialog
+	 * procedure when necessary
+	 */
+
+	ofnData.dynFileBufferSize = 1024;
+	ofnData.dynFileBuffer = ckalloc(1024);
     }
 
     if (extension != NULL) {
@@ -1267,28 +1379,39 @@ GetFileNameA(
 
     /*
      * Process the results.
+     *
+     * Use the CommDlgExtendedError() function to retrieve the error code.
+     * This function can return one of about two dozen codes; most of these
+     * indicate some sort of gross system failure (insufficient memory, bad
+     * window handles, etc.) Most of the error codes will be ignored; as we
+     * find we want specific error messages for particular errors, we can
+     * extend the code as needed.
+     */
+
+    cdlgerr = CommDlgExtendedError();
+
+    /*
+     * We now allow FNERR_BUFFERTOOSMALL when multiselection is enabled. The
+     * filename buffer has been dynamically allocated by the OFN dialog
+     * procedure to accomodate all selected files.
      */
 
-    if (winCode != 0) {
+    if ((winCode != 0)
+	    || ((cdlgerr == FNERR_BUFFERTOOSMALL)
+		    && (ofn.Flags & OFN_ALLOWMULTISELECT))) {
 	if (ofn.Flags & OFN_ALLOWMULTISELECT) {
 	    /*
-	     * The result in custData->szFile contains many items, separated
-	     * with null characters. It is terminated with two nulls in a row.
-	     * The first element is the directory path (if multiple files are
+	     * The result in dynFileBuffer contains many items, separated by
+	     * NUL characters. It is terminated with two nulls in a row. The
+	     * first element is the directory path (if multiple files are
 	     * selected) or the only returned file (if only a single file has
 	     * been chosen).
 	     */
 
-	    char *files;
-	    Tcl_DString ds;
-	    Tcl_Obj *returnList;
+	    char *files = ofnData.dynFileBuffer;
+	    Tcl_Obj *returnList = Tcl_NewObj();
 	    int count = 0;
 
-	    returnList = Tcl_NewObj();
-	    Tcl_IncrRefCount(returnList);
-
-	    files = ofn.lpstrFile;
-
 	    /*
 	     * Get directory.
 	     */
@@ -1325,11 +1448,8 @@ GetFileNameA(
 			Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)));
 	    }
 	    Tcl_SetObjResult(interp, returnList);
-	    Tcl_DecrRefCount(returnList);
 	    Tcl_DStringFree(&ds);
 	} else {
-	    Tcl_DString ds;
-
 	    Tcl_AppendResult(interp, ConvertExternalFilename(NULL,
 		    (char *) ofn.lpstrFile, &ds), NULL);
 	    Tcl_DStringFree(&ds);
@@ -1354,29 +1474,13 @@ GetFileNameA(
 		result = TCL_ERROR;
 	    }
 	}
+    } else if (cdlgerr == FNERR_INVALIDFILENAME) {
+	Tcl_SetResult(interp, "invalid filename \"", TCL_STATIC);
+	Tcl_AppendResult(interp, ConvertExternalFilename(NULL,
+		(char *) ofn.lpstrFile, &ds), "\"", NULL);
+	Tcl_DStringFree(&ds);
     } else {
-	/*
-	 * Use the CommDlgExtendedError() function to retrieve the error code.
-	 * This function can return one of about two dozen codes; most of
-	 * these indicate some sort of gross system failure (insufficient
-	 * memory, bad window handles, etc.) Most of the error codes will be
-	 * ignored; as we find we want specific error messages for particular
-	 * errors, we can extend the code as needed.
-	 *
-	 * We could also check for FNERR_BUFFERTOOSMALL, but we can't really
-	 * do anything about it when it happens.
-	 */
-
-	if (CommDlgExtendedError() == FNERR_INVALIDFILENAME) {
-	    Tcl_DString ds;
-
-	    Tcl_SetResult(interp, "invalid filename \"", TCL_STATIC);
-	    Tcl_AppendResult(interp, ConvertExternalFilename(NULL,
-		    (char *) ofn.lpstrFile, &ds), "\"", NULL);
-	    Tcl_DStringFree(&ds);
-	} else {
-	    result = TCL_OK;
-	}
+	result = TCL_OK;
     }
 
     if (ofn.lpstrTitle != NULL) {
@@ -1393,6 +1497,10 @@ GetFileNameA(
   end:
     Tcl_DStringFree(&utfDirString);
     Tcl_DStringFree(&utfFilterString);
+    if (ofnData.dynFileBuffer != NULL) {
+	ckfree(ofnData.dynFileBuffer);
+	ofnData.dynFileBuffer = NULL;
+    }
 
     return result;
 }
@@ -1400,10 +1508,12 @@ GetFileNameA(
 /*
  *-------------------------------------------------------------------------
  *
- * OFNHookProc --
+ * OFNHookProcA --
  *
- *	Hook function called only if debugging is turned on. Sets the
- *	"tk_dialog" variable when the dialog is ready to receive messages.
+ *	Dialog box hook function. This is used to sets the "tk_dialog"
+ *	variable for test/debugging when the dialog is ready to receive
+ *	messages. When multiple file selection is enabled this function
+ *	is used to process the list of names.
  *
  * Results:
  *	Returns 0 to allow default processing of messages to occur.
@@ -1415,7 +1525,7 @@ GetFileNameA(
  */
 
 static UINT APIENTRY
-OFNHookProc(
+OFNHookProcA(
     HWND hdlg,			/* handle to child dialog window */
     UINT uMsg,			/* message identifier */
     WPARAM wParam,		/* message parameter */
@@ -1424,9 +1534,88 @@ OFNHookProc(
     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
 	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
     OPENFILENAME *ofnPtr;
+    OFNData *ofnData;
 
     if (uMsg == WM_INITDIALOG) {
 	TkWinSetUserData(hdlg, lParam);
+    } else if (uMsg == WM_NOTIFY) {
+	OFNOTIFY *notifyPtr = (OFNOTIFY *) lParam;
+
+	if (notifyPtr->hdr.code == CDN_SELCHANGE) {
+	    int dirsize, selsize;
+	    char *buffer;
+	    int buffersize;
+
+	    /*
+	     * Change of selection. Unscramble the unholy mess that's in the
+	     * selection buffer, resizing it if necessary.
+	     */
+
+	    ofnPtr = notifyPtr->lpOFN;
+	    ofnData = (OFNData *) ofnPtr->lCustData;
+	    buffer = ofnData->dynFileBuffer;
+	    hdlg = GetParent(hdlg);
+
+	    selsize = SendMessage(hdlg, CDM_GETSPEC, 0, 0);
+	    dirsize = SendMessage(hdlg, CDM_GETFOLDERPATH, 0, 0);
+	    buffersize = selsize + dirsize + 1;
+
+	    if (selsize > 1) {
+		if (ofnData->dynFileBufferSize < buffersize) {
+		    buffer = ckrealloc(buffer, buffersize);
+		    ofnData->dynFileBufferSize = buffersize;
+		    ofnData->dynFileBuffer = buffer;
+		}
+
+		SendMessage(hdlg, CDM_GETFOLDERPATH, dirsize, (int) buffer);
+		buffer += dirsize;
+		SendMessage(hdlg, CDM_GETSPEC, selsize, (int) buffer);
+
+		/*
+		 * If there are multiple files, delete the quotes and change
+		 * every second quote to NULL terminator.
+		 */
+
+		if (buffer[0] == '"') {
+		    BOOL findquote = TRUE;
+		    char *tmp = buffer;
+
+		    while (*buffer != '\0') {
+			if (findquote) {
+			    if (*buffer == '"') {
+				findquote = FALSE;
+			    }
+			    buffer++;
+			} else {
+			    if (*buffer == '"') {
+				findquote = TRUE;
+				*buffer = '\0';
+			    }
+			    *tmp++ = *buffer++;
+			}
+		    }
+		    *tmp = '\0';		/* Second NULL terminator. */
+		} else {
+		    buffer[selsize] = '\0';	/* Second NULL terminator. */
+
+		    /*
+		     * Replace directory terminating NULL with a backslash.
+		     */
+
+		    buffer--;
+		    *buffer = '\\';
+		}
+
+	    } else {
+		/*
+		 * Nothing is selected, so just empty the string.
+		 */
+
+		if (buffer != NULL) {
+		    *buffer = '\0';
+		}
+	    }
+	}
     } else if (uMsg == WM_WINDOWPOSCHANGED) {
 	/*
 	 * This message is delivered at the right time to both old-style and
@@ -1437,11 +1626,14 @@ OFNHookProc(
 
 	ofnPtr = (OPENFILENAME *) TkWinGetUserData(hdlg);
 	if (ofnPtr != NULL) {
-	    if (ofnPtr->Flags & OFN_EXPLORER) {
-		hdlg = GetParent(hdlg);
+	    ofnData = (OFNData *) ofnPtr->lCustData;
+	    if (ofnData->interp != NULL) {
+		if (ofnPtr->Flags & OFN_EXPLORER) {
+		    hdlg = GetParent(hdlg);
+		}
+		tsdPtr->debugInterp = ofnData->interp;
+		Tcl_DoWhenIdle(SetTkDialog, hdlg);
 	    }
-	    tsdPtr->debugInterp = (Tcl_Interp *) ofnPtr->lCustData;
-	    Tcl_DoWhenIdle(SetTkDialog, (ClientData) hdlg);
 	    TkWinSetUserData(hdlg, NULL);
 	}
     }
-- 
cgit v0.12