summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpatthoyts <patthoyts@noemail.net>2010-01-05 21:50:53 (GMT)
committerpatthoyts <patthoyts@noemail.net>2010-01-05 21:50:53 (GMT)
commit14d52450128a515ff22be6f1845cea9ceb3336c3 (patch)
tree3f85b109ef6004a2909d2cb58e7ca41ec0fed885
parente02304c731ca58ba5a1f51dfae7404cb86715ad7 (diff)
downloadtk-14d52450128a515ff22be6f1845cea9ceb3336c3.zip
tk-14d52450128a515ff22be6f1845cea9ceb3336c3.tar.gz
tk-14d52450128a515ff22be6f1845cea9ceb3336c3.tar.bz2
Patch 289825: Enable unlimited multiple file selection from the open files dialog
FossilOrigin-Name: e47383bdeb6909044a7f4e9647b20ad095f330a7
-rw-r--r--ChangeLog5
-rw-r--r--tests/winDialog.test3
-rw-r--r--win/tkWinDialog.c416
3 files changed, 310 insertions, 114 deletions
diff --git a/ChangeLog b/ChangeLog
index 4b8db2c..89590e1 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/tests/winDialog.test b/tests/winDialog.test
index f176e92..30649bf 100644
--- a/tests/winDialog.test
+++ b/tests/winDialog.test
@@ -7,7 +7,7 @@
# Copyright (c) 1998-1999 by Scriptics Corporation.
# Copyright (c) 1998-1999 ActiveState Corporation.
#
-# RCS: @(#) $Id: winDialog.test,v 1.25 2008/12/10 13:41:19 patthoyts Exp $
+# RCS: @(#) $Id: winDialog.test,v 1.26 2010/01/05 21:50:54 patthoyts Exp $
package require tcltest 2.2
namespace import ::tcltest::*
@@ -168,7 +168,6 @@ test winDialog-1.7 {Tk_ChooseColorObjCmd: -parent} -constraints {
test winDialog-2.1 {ColorDlgHookProc} -constraints {emptyTest nt} -body {}
-
test winDialog-3.1 {Tk_GetOpenFileObjCmd} -constraints {
nt testwinevent english
} -body {
diff --git a/win/tkWinDialog.c b/win/tkWinDialog.c
index 3c9312d..fd06861 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.67 2010/01/03 00:19:25 patthoyts Exp $
+ * RCS: @(#) $Id: tkWinDialog.c,v 1.68 2010/01/05 21:50:54 patthoyts Exp $
*
*/
@@ -171,6 +171,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.
*/
@@ -187,7 +202,7 @@ static int GetFileNameW(ClientData clientData,
static int MakeFilter(Tcl_Interp *interp, Tcl_Obj *valuePtr,
Tcl_DString *dsPtr, Tcl_Obj *initialPtr,
int *indexPtr);
-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);
@@ -571,12 +586,14 @@ GetFileNameW(
{
OPENFILENAMEW ofn;
WCHAR file[TK_MULTI_MAX_PATH];
+ OFNData ofnData;
+ int cdlgerr;
int filterIndex = 0, result = TCL_ERROR, winCode, oldMode, i, multi = 0;
const char *extension = NULL, *filter = NULL, *title = NULL;
Tk_Window tkwin = clientData;
HWND hWnd;
Tcl_Obj *filterObj=NULL, *initialTypeObj=NULL, *typeVariableObj=NULL;
- Tcl_DString utfFilterString, utfDirString;
+ Tcl_DString utfFilterString, utfDirString, ds;
Tcl_DString extString, filterString, dirString, titleString;
Tcl_Encoding unicodeEncoding = TkWinGetUnicodeEncoding();
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
@@ -596,6 +613,7 @@ GetFileNameW(
};
file[0] = '\0';
+ ZeroMemory(&ofnData, sizeof(OFNData));
Tcl_DStringInit(&utfFilterString);
Tcl_DStringInit(&utfDirString);
@@ -662,9 +680,7 @@ GetFileNameW(
goto end;
}
break;
- case FILE_INITFILE: {
- Tcl_DString ds;
-
+ case FILE_INITFILE:
if (Tcl_TranslateFileName(interp, string, &ds) == NULL) {
goto end;
}
@@ -673,7 +689,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;
@@ -712,9 +727,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;
@@ -722,10 +737,18 @@ GetFileNameW(
ofn.Flags |= OFN_OVERWRITEPROMPT;
}
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) {
@@ -799,32 +822,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') {
@@ -839,8 +873,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));
@@ -855,15 +889,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);
@@ -888,29 +919,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) {
@@ -927,6 +942,10 @@ GetFileNameW(
end:
Tcl_DStringFree(&utfDirString);
Tcl_DStringFree(&utfFilterString);
+ if (ofnData.dynFileBuffer != NULL) {
+ ckfree(ofnData.dynFileBuffer);
+ ofnData.dynFileBuffer = NULL;
+ }
return result;
}
@@ -936,8 +955,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.
@@ -958,9 +979,88 @@ OFNHookProcW(
ThreadSpecificData *tsdPtr =
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
@@ -970,9 +1070,12 @@ OFNHookProcW(
ofnPtr = (OPENFILENAMEW *) TkWinGetUserData(hdlg);
if (ofnPtr != NULL) {
- hdlg = GetParent(hdlg);
- tsdPtr->debugInterp = (Tcl_Interp *) ofnPtr->lCustData;
- Tcl_DoWhenIdle(SetTkDialog, hdlg);
+ ofnData = (OFNData *) ofnPtr->lCustData;
+ if (ofnData->interp != NULL) {
+ hdlg = GetParent(hdlg);
+ tsdPtr->debugInterp = ofnData->interp;
+ Tcl_DoWhenIdle(SetTkDialog, hdlg);
+ }
TkWinSetUserData(hdlg, NULL);
}
}
@@ -1006,12 +1109,14 @@ GetFileNameA(
{
OPENFILENAME ofn;
TCHAR file[TK_MULTI_MAX_PATH], savePath[MAX_PATH];
+ OFNData ofnData;
+ int cdlgerr;
int filterIndex = 0, result = TCL_ERROR, winCode, oldMode, i, multi = 0;
const char *extension = NULL, *filter = NULL, *title = NULL;
Tk_Window tkwin = clientData;
HWND hWnd;
Tcl_Obj *filterObj=NULL, *initialTypeObj=NULL, *typeVariableObj=NULL;
- Tcl_DString utfFilterString, utfDirString;
+ Tcl_DString utfFilterString, utfDirString, ds;
Tcl_DString extString, filterString, dirString, titleString;
ThreadSpecificData *tsdPtr =
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
@@ -1030,6 +1135,7 @@ GetFileNameA(
};
file[0] = '\0';
+ ZeroMemory(&ofnData, sizeof(OFNData));
Tcl_DStringInit(&utfFilterString);
Tcl_DStringInit(&utfDirString);
@@ -1095,9 +1201,7 @@ GetFileNameA(
goto end;
}
break;
- case FILE_INITFILE: {
- Tcl_DString ds;
-
+ case FILE_INITFILE:
if (Tcl_TranslateFileName(interp, string, &ds) == NULL) {
goto end;
}
@@ -1106,7 +1210,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;
@@ -1138,6 +1241,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);
@@ -1151,12 +1255,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) {
@@ -1166,11 +1270,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) {
@@ -1243,28 +1355,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.
*/
@@ -1301,11 +1424,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);
@@ -1330,29 +1450,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) {
@@ -1369,6 +1473,10 @@ GetFileNameA(
end:
Tcl_DStringFree(&utfDirString);
Tcl_DStringFree(&utfFilterString);
+ if (ofnData.dynFileBuffer != NULL) {
+ ckfree(ofnData.dynFileBuffer);
+ ofnData.dynFileBuffer = NULL;
+ }
return result;
}
@@ -1376,10 +1484,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.
@@ -1391,7 +1501,7 @@ GetFileNameA(
*/
static UINT APIENTRY
-OFNHookProc(
+OFNHookProcA(
HWND hdlg, /* handle to child dialog window */
UINT uMsg, /* message identifier */
WPARAM wParam, /* message parameter */
@@ -1400,9 +1510,88 @@ OFNHookProc(
ThreadSpecificData *tsdPtr =
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
@@ -1413,11 +1602,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, hdlg);
TkWinSetUserData(hdlg, NULL);
}
}