From 001aa2d54de8732d9d3eae6d92f640f393f19ebb Mon Sep 17 00:00:00 2001 From: ashok Date: Fri, 12 Sep 2014 03:23:54 +0000 Subject: Started on new Vista-style file dialogs. Refactored option processing. --- win/tkWinDialog.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) diff --git a/win/tkWinDialog.c b/win/tkWinDialog.c index baebfc9..ef4f7f6 100644 --- a/win/tkWinDialog.c +++ b/win/tkWinDialog.c @@ -145,6 +145,29 @@ typedef struct { } ChooseDir; /* + * The following structure is used to gather options used by various + * file dialogs + */ +typedef struct OFNOpts { + Tk_Window tkwin; /* Owner window for dialog */ + char *extension; /* Default extension */ + char *title; /* Title for dialog */ + Tcl_Obj *filterObj; /* File type filter list */ + Tcl_Obj *typeVariableObj; /* Variable in which to store type selected */ + Tcl_Obj *initialTypeObj; /* Initial value of above, or NULL */ + Tcl_DString utfDirString; /* Initial dir */ + int multi; /* Multiple selection enabled */ + int confirmOverwrite; /* Multiple selection enabled */ + TCHAR file[TK_MULTI_MAX_PATH]; /* File name + XXX - fixed size because it was so + historically. Why not malloc'ed ? + XXX - also, TCHAR should really be WCHAR + because TkWinGetUnicodeEncoding is always + UCS2. + */ +} OFNOpts; + +/* * The following structure is used to pass information between GetFileName * function and OFN dialog hook procedures. [Bug 2896501, Patch 2898255] */ @@ -520,6 +543,178 @@ Tk_GetSaveFileObjCmd( /* *---------------------------------------------------------------------- * + * CleanupOFNOptions -- + * + * Cleans up any storage allocated by ParseOFNOptions + * + * Results: + * None. + * + * Side effects: + * Releases resources held by *optsPtr + *---------------------------------------------------------------------- + */ +static void CleanupOFNOptions(OFNOpts *optsPtr) +{ + Tcl_DStringFree(&optsPtr->utfDirString); +} + + + +/* + *---------------------------------------------------------------------- + * + * ParseOFNOptions -- + * + * Option parsing for tk_get{Open,Save}File + * + * Results: + * TCL_OK on success, TCL_ERROR otherwise + * + * Side effects: + * Returns option values in *optsPtr. Note these may include string + * pointers into objv[] + *---------------------------------------------------------------------- + */ + +static int +ParseOFNOptions( + ClientData clientData, /* Main window associated with interpreter. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[], /* Argument objects. */ + int open, /* 1 for Open, 0 for Save */ + OFNOpts *optsPtr) /* Output, uninitialized on entry */ +{ + int i; + Tcl_DString ds; + enum options { + FILE_DEFAULT, FILE_TYPES, FILE_INITDIR, FILE_INITFILE, FILE_PARENT, + FILE_TITLE, FILE_TYPEVARIABLE, FILE_MULTIPLE, FILE_CONFIRMOW + }; + struct Options { + const char *name; + enum options value; + }; + static const struct Options saveOptions[] = { + {"-confirmoverwrite", FILE_CONFIRMOW}, + {"-defaultextension", FILE_DEFAULT}, + {"-filetypes", FILE_TYPES}, + {"-initialdir", FILE_INITDIR}, + {"-initialfile", FILE_INITFILE}, + {"-parent", FILE_PARENT}, + {"-title", FILE_TITLE}, + {"-typevariable", FILE_TYPEVARIABLE}, + {NULL, FILE_DEFAULT/*ignored*/ } + }; + static const struct Options openOptions[] = { + {"-defaultextension", FILE_DEFAULT}, + {"-filetypes", FILE_TYPES}, + {"-initialdir", FILE_INITDIR}, + {"-initialfile", FILE_INITFILE}, + {"-multiple", FILE_MULTIPLE}, + {"-parent", FILE_PARENT}, + {"-title", FILE_TITLE}, + {"-typevariable", FILE_TYPEVARIABLE}, + {NULL, FILE_DEFAULT/*ignored*/ } + }; + const struct Options *const options = open ? openOptions : saveOptions; + + optsPtr->tkwin = clientData; + optsPtr->extension = NULL; + optsPtr->title = NULL; + optsPtr->filterObj = NULL; + optsPtr->typeVariableObj = NULL; + optsPtr->initialTypeObj = NULL; + optsPtr->multi = 0; + optsPtr->confirmOverwrite = 1; /* By default we ask for confirmation */ + Tcl_DStringInit(&optsPtr->utfDirString); + optsPtr->file[0] = 0; + + for (i = 1; i < objc; i += 2) { + int index; + const char *string; + Tcl_Obj *valuePtr = objv[i + 1]; + + if (Tcl_GetIndexFromObjStruct(interp, objv[i], options, + sizeof(struct Options), "option", 0, &index) != TCL_OK) { + goto end; + } else if (i + 1 == objc) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "value for \"%s\" missing", options[index].name)); + Tcl_SetErrorCode(interp, "TK", "FILEDIALOG", "VALUE", NULL); + goto end; + } + + string = Tcl_GetString(valuePtr); + switch (options[index].value) { + case FILE_DEFAULT: + if (string[0] == '.') { + string++; + } + optsPtr->extension = string; + break; + case FILE_TYPES: + optsPtr->filterObj = valuePtr; + break; + case FILE_INITDIR: + Tcl_DStringFree(&optsPtr->utfDirString); + if (Tcl_TranslateFileName(interp, string, + &optsPtr->utfDirString) == NULL) { + goto end; + } + break; + case FILE_INITFILE: + if (Tcl_TranslateFileName(interp, string, &ds) == NULL) { + goto end; + } + Tcl_UtfToExternal(NULL, TkWinGetUnicodeEncoding(), + Tcl_DStringValue(&ds), Tcl_DStringLength(&ds), 0, NULL, + (char *) &optsPtr->file[0], sizeof(optsPtr->file), + NULL, NULL, NULL); + Tcl_DStringFree(&ds); + break; + case FILE_PARENT: + /* XXX - check */ + optsPtr->tkwin = Tk_NameToWindow(interp, string, clientData); + if (optsPtr->tkwin == NULL) { + goto end; + } + break; + case FILE_TITLE: + optsPtr->title = string; + break; + case FILE_TYPEVARIABLE: + optsPtr->typeVariableObj = valuePtr; + optsPtr->initialTypeObj = Tcl_ObjGetVar2(interp, valuePtr, + NULL, TCL_GLOBAL_ONLY); + break; + case FILE_MULTIPLE: + if (Tcl_GetBooleanFromObj(interp, valuePtr, &optsPtr->multi) != TCL_OK) { + return TCL_ERROR; + } + break; + case FILE_CONFIRMOW: + if (Tcl_GetBooleanFromObj(interp, valuePtr, + &optsPtr->confirmOverwrite) != TCL_OK) { + return TCL_ERROR; + } + break; + } + } + + return TCL_OK; + +end: /* interp should already hold error */ + CleanupOFNOptions(optsPtr); + return TCL_ERROR; +} + + + +/* + *---------------------------------------------------------------------- + * * GetFileName -- * * Calls GetOpenFileName() or GetSaveFileName(). -- cgit v0.12 From 034144aa3bb40a60ddf23e96380f1bb2a478bbae Mon Sep 17 00:00:00 2001 From: ashok Date: Fri, 12 Sep 2014 13:11:16 +0000 Subject: Get basic new style dialogs working. --- win/tkWinDialog.c | 840 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 695 insertions(+), 145 deletions(-) diff --git a/win/tkWinDialog.c b/win/tkWinDialog.c index ef4f7f6..241ae12 100644 --- a/win/tkWinDialog.c +++ b/win/tkWinDialog.c @@ -21,6 +21,8 @@ #include /* includes the common dialog error codes */ #include /* includes SHBrowseForFolder */ +#include + #ifdef _MSC_VER # pragma comment (lib, "shell32.lib") #endif @@ -57,6 +59,10 @@ typedef struct ThreadSpecificData { HHOOK hMsgBoxHook; /* Hook proc for tk_messageBox and the */ HICON hSmallIcon; /* icons used by a parent to be used in */ HICON hBigIcon; /* the message box */ + int useNewFileDialogs; +#define FDLG_STATE_INIT 0 /* Uninitialized */ +#define FDLG_STATE_USE_NEW 1 /* Use the new dialogs */ +#define FDLG_STATE_USE_OLD 2 /* Use the old dialogs */ } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; @@ -145,13 +151,27 @@ typedef struct { } ChooseDir; /* + * The following structure is used to pass information between GetFileName + * function 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 */ + TCHAR *dynFileBuffer; /* Dynamic filename buffer */ +} OFNData; + +/* * The following structure is used to gather options used by various * file dialogs */ typedef struct OFNOpts { Tk_Window tkwin; /* Owner window for dialog */ - char *extension; /* Default extension */ - char *title; /* Title for dialog */ + const char *extension; /* Default extension */ + const char *title; /* Title for dialog */ Tcl_Obj *filterObj; /* File type filter list */ Tcl_Obj *typeVariableObj; /* Variable in which to store type selected */ Tcl_Obj *initialTypeObj; /* Initial value of above, or NULL */ @@ -168,18 +188,569 @@ typedef struct OFNOpts { } OFNOpts; /* - * The following structure is used to pass information between GetFileName - * function and OFN dialog hook procedures. [Bug 2896501, Patch 2898255] + * The following definitions are required when using older versions of + * Visual C++ (like 6.0) and possibly MingW. Those headers do not contain + * required definitions for interfaces new to Vista that we need for + * the new file dialogs. Duplicating definitions is OK because they + * should forever remain unchanged. + * + * XXX - is there a better/easier way to use new data definitions with + * older compilers? Should we prefix definitions with Tcl_ instead + * of using the same names as in the SDK? */ +#ifndef __IShellItemArray_INTERFACE_DEFINED__ +#define __IShellItemArray_INTERFACE_DEFINED__ + +typedef enum SIATTRIBFLAGS { + SIATTRIBFLAGS_AND = 0x1, + SIATTRIBFLAGS_OR = 0x2, + SIATTRIBFLAGS_APPCOMPAT = 0x3, + SIATTRIBFLAGS_MASK = 0x3, + SIATTRIBFLAGS_ALLITEMS = 0x4000 +} SIATTRIBFLAGS; +typedef struct IShellItemArray IShellItemArray; +typedef struct IShellItemArrayVtbl +{ + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IShellItemArray * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IShellItemArray * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IShellItemArray * This); + + HRESULT ( STDMETHODCALLTYPE *BindToHandler )( + IShellItemArray * This, + /* [unique][in] */ IBindCtx *pbc, + /* [in] */ REFGUID bhid, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvOut); + + HRESULT ( STDMETHODCALLTYPE *GetPropertyStore )( + IShellItemArray * This, + /* Actually enum GETPROPERTYSTOREFLAGS, but we do not use this call */ + /* [in] */ int flags, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppv); + + HRESULT ( STDMETHODCALLTYPE *GetPropertyDescriptionList )( + IShellItemArray * This, + /* Actually REFPROPERTYKEY, but this call is not used */ + /* [in] */ void* keyType, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppv); + + HRESULT ( STDMETHODCALLTYPE *GetAttributes )( + IShellItemArray * This, + /* [in] */ SIATTRIBFLAGS AttribFlags, + /* [in] */ SFGAOF sfgaoMask, + /* [out] */ SFGAOF *psfgaoAttribs); + + HRESULT ( STDMETHODCALLTYPE *GetCount )( + IShellItemArray * This, + /* [out] */ DWORD *pdwNumItems); + + HRESULT ( STDMETHODCALLTYPE *GetItemAt )( + IShellItemArray * This, + /* [in] */ DWORD dwIndex, + /* [out] */ IShellItem **ppsi); + + HRESULT ( STDMETHODCALLTYPE *EnumItems )( + IShellItemArray * This, + /* Actually IEnumShellItems **, but we do not use this call */ + /* [out] */ void **ppenumShellItems); + + END_INTERFACE +} IShellItemArrayVtbl; + +struct IShellItemArray +{ + CONST_VTBL struct IShellItemArrayVtbl *lpVtbl; +}; -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 */ - TCHAR *dynFileBuffer; /* Dynamic filename buffer */ -} OFNData; +#endif /* __IShellItemArray_INTERFACE_DEFINED__ */ + +#ifndef __IFileDialog_INTERFACE_DEFINED__ + +/* Forward declarations for structs that are referenced but not used */ +typedef struct IPropertyStore IPropertyStore; +typedef struct IPropertyDescriptionList IPropertyDescriptionList; +typedef struct IFileOperationProgressSink IFileOperationProgressSink; +typedef enum FDAP { + FDAP_BOTTOM = 0, + FDAP_TOP = 1 +} FDAP; + +typedef struct _COMDLG_FILTERSPEC { + LPCWSTR pszName; + LPCWSTR pszSpec; +} COMDLG_FILTERSPEC; + +static CLSID CLSID_FileOpenDialog = { + 0xDC1C5A9C, 0xE88A, 0X4DDE, 0xA5, 0xA1, 0x60, 0xF8, 0x2A, 0x20, 0xAE, 0xF7 +}; + +static IID IID_IFileOpenDialog = { + 0xD57C7288, 0xD4AD, 0x4768, 0xBE, 0x02, 0x9D, 0x96, 0x95, 0x32, 0xD9, 0x60 +}; + +static CLSID CLSID_FileSaveDialog = { + 0xC0B4E2F3, 0xBA21, 0x4773, 0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B +}; + +static IID IID_IFileSaveDialog = { + 0x84BCCD23, 0x5FDE, 0x4CDB, 0xAE, 0xA4, 0xAF, 0x64, 0xB8, 0x3D, 0x78, 0xAB +}; + +enum _FILEOPENDIALOGOPTIONS { + FOS_OVERWRITEPROMPT = 0x2, + FOS_STRICTFILETYPES = 0x4, + FOS_NOCHANGEDIR = 0x8, + FOS_PICKFOLDERS = 0x20, + FOS_FORCEFILESYSTEM = 0x40, + FOS_ALLNONSTORAGEITEMS = 0x80, + FOS_NOVALIDATE = 0x100, + FOS_ALLOWMULTISELECT = 0x200, + FOS_PATHMUSTEXIST = 0x800, + FOS_FILEMUSTEXIST = 0x1000, + FOS_CREATEPROMPT = 0x2000, + FOS_SHAREAWARE = 0x4000, + FOS_NOREADONLYRETURN = 0x8000, + FOS_NOTESTFILECREATE = 0x10000, + FOS_HIDEMRUPLACES = 0x20000, + FOS_HIDEPINNEDPLACES = 0x40000, + FOS_NODEREFERENCELINKS = 0x100000, + FOS_DONTADDTORECENT = 0x2000000, + FOS_FORCESHOWHIDDEN = 0x10000000, + FOS_DEFAULTNOMINIMODE = 0x20000000, + FOS_FORCEPREVIEWPANEON = 0x40000000 +} ; +typedef DWORD FILEOPENDIALOGOPTIONS; + +typedef struct IFileDialog IFileDialog; +typedef struct IFileDialogVtbl +{ + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IFileDialog * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IFileDialog * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IFileDialog * This); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE *Show )( + IFileDialog * This, + /* [annotation][unique][in] */ + HWND hwndOwner); + + HRESULT ( STDMETHODCALLTYPE *SetFileTypes )( + IFileDialog * This, + /* [in] */ UINT cFileTypes, + /* [size_is][in] */ const COMDLG_FILTERSPEC *rgFilterSpec); + + HRESULT ( STDMETHODCALLTYPE *SetFileTypeIndex )( + IFileDialog * This, + /* [in] */ UINT iFileType); + + HRESULT ( STDMETHODCALLTYPE *GetFileTypeIndex )( + IFileDialog * This, + /* [out] */ UINT *piFileType); + + HRESULT ( STDMETHODCALLTYPE *Advise )( + IFileDialog * This, + /* XXX - Actually pfde is IFileDialogEvents* but we do not use + this call and do not want to define IFileDialogEvents as that + pulls in a whole bunch of other stuff. */ + /* [in] */ void *pfde, + /* [out] */ DWORD *pdwCookie); + + HRESULT ( STDMETHODCALLTYPE *Unadvise )( + IFileDialog * This, + /* [in] */ DWORD dwCookie); + + HRESULT ( STDMETHODCALLTYPE *SetOptions )( + IFileDialog * This, + /* [in] */ FILEOPENDIALOGOPTIONS fos); + + HRESULT ( STDMETHODCALLTYPE *GetOptions )( + IFileDialog * This, + /* [out] */ FILEOPENDIALOGOPTIONS *pfos); + + HRESULT ( STDMETHODCALLTYPE *SetDefaultFolder )( + IFileDialog * This, + /* [in] */ IShellItem *psi); + + HRESULT ( STDMETHODCALLTYPE *SetFolder )( + IFileDialog * This, + /* [in] */ IShellItem *psi); + + HRESULT ( STDMETHODCALLTYPE *GetFolder )( + IFileDialog * This, + /* [out] */ IShellItem **ppsi); + + HRESULT ( STDMETHODCALLTYPE *GetCurrentSelection )( + IFileDialog * This, + /* [out] */ IShellItem **ppsi); + + HRESULT ( STDMETHODCALLTYPE *SetFileName )( + IFileDialog * This, + /* [string][in] */ LPCWSTR pszName); + + HRESULT ( STDMETHODCALLTYPE *GetFileName )( + IFileDialog * This, + /* [string][out] */ LPWSTR *pszName); + + HRESULT ( STDMETHODCALLTYPE *SetTitle )( + IFileDialog * This, + /* [string][in] */ LPCWSTR pszTitle); + + HRESULT ( STDMETHODCALLTYPE *SetOkButtonLabel )( + IFileDialog * This, + /* [string][in] */ LPCWSTR pszText); + + HRESULT ( STDMETHODCALLTYPE *SetFileNameLabel )( + IFileDialog * This, + /* [string][in] */ LPCWSTR pszLabel); + + HRESULT ( STDMETHODCALLTYPE *GetResult )( + IFileDialog * This, + /* [out] */ IShellItem **ppsi); + + HRESULT ( STDMETHODCALLTYPE *AddPlace )( + IFileDialog * This, + /* [in] */ IShellItem *psi, + /* [in] */ FDAP fdap); + + HRESULT ( STDMETHODCALLTYPE *SetDefaultExtension )( + IFileDialog * This, + /* [string][in] */ LPCWSTR pszDefaultExtension); + + HRESULT ( STDMETHODCALLTYPE *Close )( + IFileDialog * This, + /* [in] */ HRESULT hr); + + HRESULT ( STDMETHODCALLTYPE *SetClientGuid )( + IFileDialog * This, + /* [in] */ REFGUID guid); + + HRESULT ( STDMETHODCALLTYPE *ClearClientData )( + IFileDialog * This); + + HRESULT ( STDMETHODCALLTYPE *SetFilter )( + IFileDialog * This, + /* XXX - Actually IShellItemFilter. But deprecated in Win7 AND we do + not use it anyways. So define as void* */ + /* [in] */ void *pFilter); + + END_INTERFACE +} IFileDialogVtbl; + +struct IFileDialog { + CONST_VTBL struct IFileDialogVtbl *lpVtbl; +}; + + +typedef struct IFileSaveDialog IFileSaveDialog; +typedef struct IFileSaveDialogVtbl { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IFileSaveDialog * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IFileSaveDialog * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IFileSaveDialog * This); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE *Show )( + IFileSaveDialog * This, + /* [annotation][unique][in] */ + HWND hwndOwner); + + HRESULT ( STDMETHODCALLTYPE *SetFileTypes )( + IFileSaveDialog * This, + /* [in] */ UINT cFileTypes, + /* [size_is][in] */ const COMDLG_FILTERSPEC *rgFilterSpec); + + HRESULT ( STDMETHODCALLTYPE *SetFileTypeIndex )( + IFileSaveDialog * This, + /* [in] */ UINT iFileType); + + HRESULT ( STDMETHODCALLTYPE *GetFileTypeIndex )( + IFileSaveDialog * This, + /* [out] */ UINT *piFileType); + + HRESULT ( STDMETHODCALLTYPE *Advise )( + IFileSaveDialog * This, + /* XXX - Actually pfde is IFileSaveDialogEvents* but we do not use + this call and do not want to define IFileSaveDialogEvents as that + pulls in a whole bunch of other stuff. */ + /* [in] */ void *pfde, + /* [out] */ DWORD *pdwCookie); + + HRESULT ( STDMETHODCALLTYPE *Unadvise )( + IFileSaveDialog * This, + /* [in] */ DWORD dwCookie); + + HRESULT ( STDMETHODCALLTYPE *SetOptions )( + IFileSaveDialog * This, + /* [in] */ FILEOPENDIALOGOPTIONS fos); + + HRESULT ( STDMETHODCALLTYPE *GetOptions )( + IFileSaveDialog * This, + /* [out] */ FILEOPENDIALOGOPTIONS *pfos); + + HRESULT ( STDMETHODCALLTYPE *SetDefaultFolder )( + IFileSaveDialog * This, + /* [in] */ IShellItem *psi); + + HRESULT ( STDMETHODCALLTYPE *SetFolder )( + IFileSaveDialog * This, + /* [in] */ IShellItem *psi); + + HRESULT ( STDMETHODCALLTYPE *GetFolder )( + IFileSaveDialog * This, + /* [out] */ IShellItem **ppsi); + + HRESULT ( STDMETHODCALLTYPE *GetCurrentSelection )( + IFileSaveDialog * This, + /* [out] */ IShellItem **ppsi); + + HRESULT ( STDMETHODCALLTYPE *SetFileName )( + IFileSaveDialog * This, + /* [string][in] */ LPCWSTR pszName); + + HRESULT ( STDMETHODCALLTYPE *GetFileName )( + IFileSaveDialog * This, + /* [string][out] */ LPWSTR *pszName); + + HRESULT ( STDMETHODCALLTYPE *SetTitle )( + IFileSaveDialog * This, + /* [string][in] */ LPCWSTR pszTitle); + + HRESULT ( STDMETHODCALLTYPE *SetOkButtonLabel )( + IFileSaveDialog * This, + /* [string][in] */ LPCWSTR pszText); + + HRESULT ( STDMETHODCALLTYPE *SetFileNameLabel )( + IFileSaveDialog * This, + /* [string][in] */ LPCWSTR pszLabel); + + HRESULT ( STDMETHODCALLTYPE *GetResult )( + IFileSaveDialog * This, + /* [out] */ IShellItem **ppsi); + + HRESULT ( STDMETHODCALLTYPE *AddPlace )( + IFileSaveDialog * This, + /* [in] */ IShellItem *psi, + /* [in] */ FDAP fdap); + + HRESULT ( STDMETHODCALLTYPE *SetDefaultExtension )( + IFileSaveDialog * This, + /* [string][in] */ LPCWSTR pszDefaultExtension); + + HRESULT ( STDMETHODCALLTYPE *Close )( + IFileSaveDialog * This, + /* [in] */ HRESULT hr); + + HRESULT ( STDMETHODCALLTYPE *SetClientGuid )( + IFileSaveDialog * This, + /* [in] */ REFGUID guid); + + HRESULT ( STDMETHODCALLTYPE *ClearClientData )( + IFileSaveDialog * This); + + HRESULT ( STDMETHODCALLTYPE *SetFilter )( + IFileSaveDialog * This, + /* XXX - Actually IShellItemFilter. But deprecated in Win7 AND we do + not use it anyways. So define as void* */ + /* [in] */ void *pFilter); + + HRESULT ( STDMETHODCALLTYPE *SetSaveAsItem )( + IFileSaveDialog * This, + /* [in] */ IShellItem *psi); + + HRESULT ( STDMETHODCALLTYPE *SetProperties )( + IFileSaveDialog * This, + /* [in] */ IPropertyStore *pStore); + + HRESULT ( STDMETHODCALLTYPE *SetCollectedProperties )( + IFileSaveDialog * This, + /* [in] */ IPropertyDescriptionList *pList, + /* [in] */ BOOL fAppendDefault); + + HRESULT ( STDMETHODCALLTYPE *GetProperties )( + IFileSaveDialog * This, + /* [out] */ IPropertyStore **ppStore); + + HRESULT ( STDMETHODCALLTYPE *ApplyProperties )( + IFileSaveDialog * This, + /* [in] */ IShellItem *psi, + /* [in] */ IPropertyStore *pStore, + /* [unique][in] */ HWND hwnd, + /* [unique][in] */ IFileOperationProgressSink *pSink); + + END_INTERFACE + +} IFileSaveDialogVtbl; + +struct IFileSaveDialog { + CONST_VTBL struct IFileSaveDialogVtbl *lpVtbl; +}; + +typedef struct IFileOpenDialog IFileOpenDialog; +typedef struct IFileOpenDialogVtbl { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IFileOpenDialog * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IFileOpenDialog * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IFileOpenDialog * This); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE *Show )( + IFileOpenDialog * This, + /* [annotation][unique][in] */ + HWND hwndOwner); + + HRESULT ( STDMETHODCALLTYPE *SetFileTypes )( + IFileOpenDialog * This, + /* [in] */ UINT cFileTypes, + /* [size_is][in] */ const COMDLG_FILTERSPEC *rgFilterSpec); + + HRESULT ( STDMETHODCALLTYPE *SetFileTypeIndex )( + IFileOpenDialog * This, + /* [in] */ UINT iFileType); + + HRESULT ( STDMETHODCALLTYPE *GetFileTypeIndex )( + IFileOpenDialog * This, + /* [out] */ UINT *piFileType); + + HRESULT ( STDMETHODCALLTYPE *Advise )( + IFileOpenDialog * This, + /* XXX - Actually pfde is IFileDialogEvents* but we do not use + this call and do not want to define IFileDialogEvents as that + pulls in a whole bunch of other stuff. */ + /* [in] */ void *pfde, + /* [out] */ DWORD *pdwCookie); + + HRESULT ( STDMETHODCALLTYPE *Unadvise )( + IFileOpenDialog * This, + /* [in] */ DWORD dwCookie); + + HRESULT ( STDMETHODCALLTYPE *SetOptions )( + IFileOpenDialog * This, + /* [in] */ FILEOPENDIALOGOPTIONS fos); + + HRESULT ( STDMETHODCALLTYPE *GetOptions )( + IFileOpenDialog * This, + /* [out] */ FILEOPENDIALOGOPTIONS *pfos); + + HRESULT ( STDMETHODCALLTYPE *SetDefaultFolder )( + IFileOpenDialog * This, + /* [in] */ IShellItem *psi); + + HRESULT ( STDMETHODCALLTYPE *SetFolder )( + IFileOpenDialog * This, + /* [in] */ IShellItem *psi); + + HRESULT ( STDMETHODCALLTYPE *GetFolder )( + IFileOpenDialog * This, + /* [out] */ IShellItem **ppsi); + + HRESULT ( STDMETHODCALLTYPE *GetCurrentSelection )( + IFileOpenDialog * This, + /* [out] */ IShellItem **ppsi); + + HRESULT ( STDMETHODCALLTYPE *SetFileName )( + IFileOpenDialog * This, + /* [string][in] */ LPCWSTR pszName); + + HRESULT ( STDMETHODCALLTYPE *GetFileName )( + IFileOpenDialog * This, + /* [string][out] */ LPWSTR *pszName); + + HRESULT ( STDMETHODCALLTYPE *SetTitle )( + IFileOpenDialog * This, + /* [string][in] */ LPCWSTR pszTitle); + + HRESULT ( STDMETHODCALLTYPE *SetOkButtonLabel )( + IFileOpenDialog * This, + /* [string][in] */ LPCWSTR pszText); + + HRESULT ( STDMETHODCALLTYPE *SetFileNameLabel )( + IFileOpenDialog * This, + /* [string][in] */ LPCWSTR pszLabel); + + HRESULT ( STDMETHODCALLTYPE *GetResult )( + IFileOpenDialog * This, + /* [out] */ IShellItem **ppsi); + + HRESULT ( STDMETHODCALLTYPE *AddPlace )( + IFileOpenDialog * This, + /* [in] */ IShellItem *psi, + /* [in] */ FDAP fdap); + + HRESULT ( STDMETHODCALLTYPE *SetDefaultExtension )( + IFileOpenDialog * This, + /* [string][in] */ LPCWSTR pszDefaultExtension); + + HRESULT ( STDMETHODCALLTYPE *Close )( + IFileOpenDialog * This, + /* [in] */ HRESULT hr); + + HRESULT ( STDMETHODCALLTYPE *SetClientGuid )( + IFileOpenDialog * This, + /* [in] */ REFGUID guid); + + HRESULT ( STDMETHODCALLTYPE *ClearClientData )( + IFileOpenDialog * This); + + HRESULT ( STDMETHODCALLTYPE *SetFilter )( + IFileOpenDialog * This, + /* XXX - Actually IShellItemFilter. But deprecated in Win7 AND we do + not use it anyways. So define as void* */ + /* [in] */ void *pFilter); + + HRESULT ( STDMETHODCALLTYPE *GetResults )( + IFileOpenDialog * This, + /* [out] */ IShellItemArray **ppenum); + + HRESULT ( STDMETHODCALLTYPE *GetSelectedItems )( + IFileOpenDialog * This, + /* [out] */ IShellItemArray **ppsai); + + END_INTERFACE +} IFileOpenDialogVtbl; + +struct IFileOpenDialog +{ + CONST_VTBL struct IFileOpenDialogVtbl *lpVtbl; +}; + +#endif /* __IFileDialog_INTERFACE_DEFINED__ */ /* * Definitions of functions used only in this file. @@ -189,6 +760,10 @@ static UINT APIENTRY ChooseDirectoryValidateProc(HWND hdlg, UINT uMsg, LPARAM wParam, LPARAM lParam); static UINT CALLBACK ColorDlgHookProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); +static void CleanupOFNOptions(OFNOpts *optsPtr); +static int ParseOFNOptions(ClientData clientData, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[], int open, OFNOpts *optsPtr); static int GetFileName(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], int isOpen); @@ -710,6 +1285,80 @@ end: /* interp should already hold error */ return TCL_ERROR; } + +/* + *---------------------------------------------------------------------- + * + * GetFileNameVista -- + * + * Displays the new file dialogs on Vista and later. + * + * Results: + * TCL_OK - if dialog was successfully displayed + * TCL_ERROR - error return + * TCL_CONTINUE - new dialogs not available. Caller should go + * on to display the old style dialogs. + * + * Side effects: + * Dialogs is displayed and results returned in interpreter on success. + * COM subsystem is initialized if not already done. + *---------------------------------------------------------------------- + */ +static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) +{ + HRESULT hr; + IFileDialog *fdlgPtr = NULL; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (tsdPtr->useNewFileDialogs == FDLG_STATE_USE_OLD) + return TCL_CONTINUE; /* Not an error, go try old style dialogs */ + + if (tsdPtr->useNewFileDialogs == FDLG_STATE_INIT) { + tsdPtr->useNewFileDialogs = FDLG_STATE_USE_OLD; + + hr = CoInitialize(0); + /* On failures we do not error. Instead we fall back to old method */ + if (FAILED(hr)) + return TCL_CONTINUE; + + /* Verify interfaces are available */ + if (open) { + hr = CoCreateInstance(&CLSID_FileOpenDialog, NULL, + CLSCTX_INPROC_SERVER, &IID_IFileOpenDialog, &fdlgPtr); + } else { + hr = CoCreateInstance(&CLSID_FileSaveDialog, NULL, + CLSCTX_INPROC_SERVER, &IID_IFileSaveDialog, &fdlgPtr); + } + + if (FAILED(hr)) { + CoUninitialize(); + return TCL_CONTINUE; + } + + tsdPtr->useNewFileDialogs = FDLG_STATE_USE_NEW; + /* + * XXX - need to arrange for CoUninitialize to be called on thread + * exit if useNewFileDialogs is FDLG_STATE_USE_NEW. + */ + } else { + /* FDLG_STATE_USE_NEW */ + if (open) { + hr = CoCreateInstance(&CLSID_FileOpenDialog, NULL, + CLSCTX_INPROC_SERVER, &IID_IFileOpenDialog, &fdlgPtr); + } else { + hr = CoCreateInstance(&CLSID_FileSaveDialog, NULL, + CLSCTX_INPROC_SERVER, &IID_IFileSaveDialog, &fdlgPtr); + } + } + + /* At this point new interfaces are supposed to be available */ + fdlgPtr->lpVtbl->Show(fdlgPtr, NULL); + fdlgPtr->lpVtbl->Release(fdlgPtr); + return TCL_OK; +} + + /* @@ -738,143 +1387,44 @@ GetFileName( * GetSaveFileName(). */ { OPENFILENAME ofn; - TCHAR file[TK_MULTI_MAX_PATH]; OFNData ofnData; + OFNOpts ofnOpts; int cdlgerr; - int filterIndex = 0, result = TCL_ERROR, winCode, oldMode, i, multi = 0; - int confirmOverwrite = 1; - const char *extension = NULL, *title = NULL; - Tk_Window tkwin = clientData; + int filterIndex = 0, result = TCL_ERROR, winCode, oldMode; HWND hWnd; - Tcl_Obj *filterObj = NULL, *initialTypeObj = NULL, *typeVariableObj = NULL; - Tcl_DString utfFilterString, utfDirString, ds; + Tcl_DString utfFilterString, ds; Tcl_DString extString, filterString, dirString, titleString; ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); - enum options { - FILE_DEFAULT, FILE_TYPES, FILE_INITDIR, FILE_INITFILE, FILE_PARENT, - FILE_TITLE, FILE_TYPEVARIABLE, FILE_MULTIPLE, FILE_CONFIRMOW - }; - struct Options { - const char *name; - enum options value; - }; - static const struct Options saveOptions[] = { - {"-confirmoverwrite", FILE_CONFIRMOW}, - {"-defaultextension", FILE_DEFAULT}, - {"-filetypes", FILE_TYPES}, - {"-initialdir", FILE_INITDIR}, - {"-initialfile", FILE_INITFILE}, - {"-parent", FILE_PARENT}, - {"-title", FILE_TITLE}, - {"-typevariable", FILE_TYPEVARIABLE}, - {NULL, FILE_DEFAULT/*ignored*/ } - }; - static const struct Options openOptions[] = { - {"-defaultextension", FILE_DEFAULT}, - {"-filetypes", FILE_TYPES}, - {"-initialdir", FILE_INITDIR}, - {"-initialfile", FILE_INITFILE}, - {"-multiple", FILE_MULTIPLE}, - {"-parent", FILE_PARENT}, - {"-title", FILE_TITLE}, - {"-typevariable", FILE_TYPEVARIABLE}, - {NULL, FILE_DEFAULT/*ignored*/ } - }; - const struct Options *const options = open ? openOptions : saveOptions; - file[0] = '\0'; ZeroMemory(&ofnData, sizeof(OFNData)); Tcl_DStringInit(&utfFilterString); - Tcl_DStringInit(&utfDirString); - /* - * Parse the arguments. - */ - - for (i = 1; i < objc; i += 2) { - int index; - const char *string; - Tcl_Obj *valuePtr = objv[i + 1]; + /* Parse the arguments. */ - if (Tcl_GetIndexFromObjStruct(interp, objv[i], options, - sizeof(struct Options), "option", 0, &index) != TCL_OK) { - goto end; - } else if (i + 1 == objc) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "value for \"%s\" missing", options[index].name)); - Tcl_SetErrorCode(interp, "TK", "FILEDIALOG", "VALUE", NULL); - goto end; - } + result = ParseOFNOptions(clientData, interp, objc, objv, open, &ofnOpts); + if (result != TCL_OK) + return result; - string = Tcl_GetString(valuePtr); - switch (options[index].value) { - case FILE_DEFAULT: - if (string[0] == '.') { - string++; - } - extension = string; - break; - case FILE_TYPES: - filterObj = valuePtr; - break; - case FILE_INITDIR: - Tcl_DStringFree(&utfDirString); - if (Tcl_TranslateFileName(interp, string, - &utfDirString) == NULL) { - goto end; - } - break; - case FILE_INITFILE: - if (Tcl_TranslateFileName(interp, string, &ds) == NULL) { - goto end; - } - Tcl_UtfToExternal(NULL, TkWinGetUnicodeEncoding(), - Tcl_DStringValue(&ds), Tcl_DStringLength(&ds), 0, NULL, - (char *) file, sizeof(file), NULL, NULL, NULL); - Tcl_DStringFree(&ds); - break; - case FILE_PARENT: - tkwin = Tk_NameToWindow(interp, string, tkwin); - if (tkwin == NULL) { - goto end; - } - break; - case FILE_TITLE: - title = string; - break; - case FILE_TYPEVARIABLE: - typeVariableObj = valuePtr; - initialTypeObj = Tcl_ObjGetVar2(interp, typeVariableObj, NULL, - TCL_GLOBAL_ONLY); - break; - case FILE_MULTIPLE: - if (Tcl_GetBooleanFromObj(interp, valuePtr, &multi) != TCL_OK) { - return TCL_ERROR; - } - break; - case FILE_CONFIRMOW: - if (Tcl_GetBooleanFromObj(interp, valuePtr, - &confirmOverwrite) != TCL_OK) { - return TCL_ERROR; - } - break; - } + result = GetFileNameVista(interp, &ofnOpts, open); + if (result != TCL_CONTINUE) { + CleanupOFNOptions(&ofnOpts); + return result; } - if (MakeFilter(interp, filterObj, &utfFilterString, initialTypeObj, - &filterIndex) != TCL_OK) { + if (MakeFilter(interp, ofnOpts.filterObj, &utfFilterString, + ofnOpts.initialTypeObj, &filterIndex) != TCL_OK) { goto end; } - Tk_MakeWindowExist(tkwin); - hWnd = Tk_GetHWND(Tk_WindowId(tkwin)); + Tk_MakeWindowExist(ofnOpts.tkwin); + hWnd = Tk_GetHWND(Tk_WindowId(ofnOpts.tkwin)); ZeroMemory(&ofn, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hWnd; ofn.hInstance = TkWinGetHInstance(ofn.hwndOwner); - ofn.lpstrFile = file; + ofn.lpstrFile = ofnOpts.file; ofn.nMaxFile = TK_MULTI_MAX_PATH; ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_ENABLEHOOK| OFN_ENABLESIZING; @@ -883,13 +1433,13 @@ GetFileName( if (open != 0) { ofn.Flags |= OFN_FILEMUSTEXIST; - } else if (confirmOverwrite) { + } else if (ofnOpts.confirmOverwrite) { ofn.Flags |= OFN_OVERWRITEPROMPT; } if (tsdPtr->debugFlag != 0) { ofnData.interp = interp; } - if (multi != 0) { + if (ofnOpts.multi != 0) { ofn.Flags |= OFN_ALLOWMULTISELECT; /* @@ -901,8 +1451,8 @@ GetFileName( ofnData.dynFileBuffer = ckalloc(512 * sizeof(TCHAR)); } - if (extension != NULL) { - Tcl_WinUtfToTChar(extension, -1, &extString); + if (ofnOpts.extension != NULL) { + Tcl_WinUtfToTChar(ofnOpts.extension, -1, &extString); ofn.lpstrDefExt = (TCHAR *) Tcl_DStringValue(&extString); } @@ -911,9 +1461,9 @@ GetFileName( ofn.lpstrFilter = (TCHAR *) Tcl_DStringValue(&filterString); ofn.nFilterIndex = filterIndex; - if (Tcl_DStringValue(&utfDirString)[0] != '\0') { - Tcl_WinUtfToTChar(Tcl_DStringValue(&utfDirString), - Tcl_DStringLength(&utfDirString), &dirString); + if (Tcl_DStringValue(&ofnOpts.utfDirString)[0] != '\0') { + Tcl_WinUtfToTChar(Tcl_DStringValue(&ofnOpts.utfDirString), + Tcl_DStringLength(&ofnOpts.utfDirString), &dirString); } else { /* * NT 5.0 changed the meaning of lpstrInitialDir, so we have to ensure @@ -922,10 +1472,10 @@ GetFileName( Tcl_DString cwd; - Tcl_DStringFree(&utfDirString); - if ((Tcl_GetCwd(interp, &utfDirString) == NULL) || + Tcl_DStringFree(&ofnOpts.utfDirString); + if ((Tcl_GetCwd(interp, &ofnOpts.utfDirString) == NULL) || (Tcl_TranslateFileName(interp, - Tcl_DStringValue(&utfDirString), &cwd) == NULL)) { + Tcl_DStringValue(&ofnOpts.utfDirString), &cwd) == NULL)) { Tcl_ResetResult(interp); } else { Tcl_WinUtfToTChar(Tcl_DStringValue(&cwd), @@ -935,8 +1485,8 @@ GetFileName( } ofn.lpstrInitialDir = (TCHAR *) Tcl_DStringValue(&dirString); - if (title != NULL) { - Tcl_WinUtfToTChar(title, -1, &titleString); + if (ofnOpts.title != NULL) { + Tcl_WinUtfToTChar(ofnOpts.title, -1, &titleString); ofn.lpstrTitle = (TCHAR *) Tcl_DStringValue(&titleString); } @@ -1054,21 +1604,21 @@ GetFileName( Tcl_DStringFree(&ds); } result = TCL_OK; - if ((ofn.nFilterIndex > 0) && gotFilename && typeVariableObj - && filterObj) { + if ((ofn.nFilterIndex > 0) && gotFilename && ofnOpts.typeVariableObj + && ofnOpts.filterObj) { int listObjc, count; Tcl_Obj **listObjv = NULL; Tcl_Obj **typeInfo = NULL; - if (Tcl_ListObjGetElements(interp, filterObj, + if (Tcl_ListObjGetElements(interp, ofnOpts.filterObj, &listObjc, &listObjv) != TCL_OK) { result = TCL_ERROR; } else if (Tcl_ListObjGetElements(interp, listObjv[ofn.nFilterIndex - 1], &count, &typeInfo) != TCL_OK) { result = TCL_ERROR; - } else if (Tcl_ObjSetVar2(interp, typeVariableObj, NULL, - typeInfo[0], TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) { + } else if (Tcl_ObjSetVar2(interp, ofnOpts.typeVariableObj, NULL, + typeInfo[0], TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) { result = TCL_ERROR; } } @@ -1095,12 +1645,12 @@ GetFileName( } end: - Tcl_DStringFree(&utfDirString); Tcl_DStringFree(&utfFilterString); if (ofnData.dynFileBuffer != NULL) { ckfree(ofnData.dynFileBuffer); ofnData.dynFileBuffer = NULL; } + CleanupOFNOptions(&ofnOpts); return result; } -- cgit v0.12 From 93218ddaa94d9b9ce6319da38dba137a0c80912c Mon Sep 17 00:00:00 2001 From: ashok Date: Sat, 13 Sep 2014 14:23:33 +0000 Subject: Implemented more options for new tk_get{Open,Save} file dialogs Renamed Win32ErrorObj to TkWin32ErrorObj and moved it from tkWinSend.c to be generally available. --- win/tkWinDialog.c | 403 +++++++++++++++++++++++++++++++++++++++--------------- win/tkWinInit.c | 51 +++++++ win/tkWinInt.h | 6 + win/tkWinSend.c | 55 +------- 4 files changed, 349 insertions(+), 166 deletions(-) diff --git a/win/tkWinDialog.c b/win/tkWinDialog.c index 241ae12..d510654 100644 --- a/win/tkWinDialog.c +++ b/win/tkWinDialog.c @@ -59,7 +59,7 @@ typedef struct ThreadSpecificData { HHOOK hMsgBoxHook; /* Hook proc for tk_messageBox and the */ HICON hSmallIcon; /* icons used by a parent to be used in */ HICON hBigIcon; /* the message box */ - int useNewFileDialogs; + int newFileDialogsAvailable; #define FDLG_STATE_INIT 0 /* Uninitialized */ #define FDLG_STATE_USE_NEW 1 /* Use the new dialogs */ #define FDLG_STATE_USE_OLD 2 /* Use the old dialogs */ @@ -170,14 +170,15 @@ typedef struct OFNData { */ typedef struct OFNOpts { Tk_Window tkwin; /* Owner window for dialog */ - const char *extension; /* Default extension */ - const char *title; /* Title for dialog */ + Tcl_Obj *extObj; /* Default extension */ + Tcl_Obj *titleObj; /* Title for dialog */ Tcl_Obj *filterObj; /* File type filter list */ Tcl_Obj *typeVariableObj; /* Variable in which to store type selected */ Tcl_Obj *initialTypeObj; /* Initial value of above, or NULL */ Tcl_DString utfDirString; /* Initial dir */ int multi; /* Multiple selection enabled */ - int confirmOverwrite; /* Multiple selection enabled */ + int confirmOverwrite; /* Multiple selection enabled */ + int forceXPStyle; /* XXX - Force XP style even on newer systems */ TCHAR file[TK_MULTI_MAX_PATH]; /* File name XXX - fixed size because it was so historically. Why not malloc'ed ? @@ -776,6 +777,69 @@ static LRESULT CALLBACK MsgBoxCBTProc(int nCode, WPARAM wParam, LPARAM lParam); static void SetTkDialog(ClientData clientData); static const char *ConvertExternalFilename(TCHAR *filename, Tcl_DString *dsPtr); +static void LoadShellProcs(void); + + +/* Definitions of dynamically loaded Win32 calls */ +typedef HRESULT (STDAPICALLTYPE SHCreateItemFromParsingNameProc)( + PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv); +struct ShellProcPointers { + SHCreateItemFromParsingNameProc *SHCreateItemFromParsingName; +} ShellProcs; + + + + + +/* + *------------------------------------------------------------------------- + * + * LoadShellProcs -- + * + * Some shell functions are not available on older versions of + * Windows. This function dynamically loads them and stores pointers + * to them in ShellProcs. Any function that is not available has + * the corresponding pointer set to NULL. + * + * Note this call never fails. Unavailability of a function is not + * a reason for failure. Caller should check whether a particular + * function pointer is NULL or not. Once loaded a function stays + * forever loaded. + * + * XXX - we load the function pointers into global memory. This implies + * there is a potential (however small) for race conditions between + * threads. However, Tk is in any case meant to be loaded in exactly + * one thread so this should not be an issue and saves us from + * unnecessary bookkeeping. + * + * Return value: + * None. + * + * Side effects: + * ShellProcs is populated. + *------------------------------------------------------------------------- + */ +static void LoadShellProcs() +{ + static HMODULE shell32_handle = NULL; + + if (shell32_handle != NULL) + return; /* We have already been through here. */ + + /* + * XXX - Note we never call FreeLibrary. There is no point because + * shell32.dll is loaded at startup anyways and stays for the duration + * of the process so why bother with keeping track of when to unload + */ + shell32_handle = LoadLibrary(TEXT("shell32.dll")); + if (shell32_handle == NULL) /* Should never happen but check anyways. */ + return; + + ShellProcs.SHCreateItemFromParsingName = + (SHCreateItemFromParsingNameProc*) GetProcAddress(shell32_handle, + "SHCreateItemFromParsingName"); +} + /* *------------------------------------------------------------------------- @@ -1165,7 +1229,8 @@ ParseOFNOptions( Tcl_DString ds; enum options { FILE_DEFAULT, FILE_TYPES, FILE_INITDIR, FILE_INITFILE, FILE_PARENT, - FILE_TITLE, FILE_TYPEVARIABLE, FILE_MULTIPLE, FILE_CONFIRMOW + FILE_TITLE, FILE_TYPEVARIABLE, FILE_MULTIPLE, FILE_CONFIRMOW, + FILE_UNDOCUMENTED_XP_STYLE /* XXX - force XP - style dialogs */ }; struct Options { const char *name; @@ -1180,6 +1245,7 @@ ParseOFNOptions( {"-parent", FILE_PARENT}, {"-title", FILE_TITLE}, {"-typevariable", FILE_TYPEVARIABLE}, + {"-xpstyle", FILE_UNDOCUMENTED_XP_STYLE}, /* XXX */ {NULL, FILE_DEFAULT/*ignored*/ } }; static const struct Options openOptions[] = { @@ -1191,17 +1257,13 @@ ParseOFNOptions( {"-parent", FILE_PARENT}, {"-title", FILE_TITLE}, {"-typevariable", FILE_TYPEVARIABLE}, + {"-xpstyle", FILE_UNDOCUMENTED_XP_STYLE}, /* XXX */ {NULL, FILE_DEFAULT/*ignored*/ } }; const struct Options *const options = open ? openOptions : saveOptions; + ZeroMemory(optsPtr, sizeof(*optsPtr)); optsPtr->tkwin = clientData; - optsPtr->extension = NULL; - optsPtr->title = NULL; - optsPtr->filterObj = NULL; - optsPtr->typeVariableObj = NULL; - optsPtr->initialTypeObj = NULL; - optsPtr->multi = 0; optsPtr->confirmOverwrite = 1; /* By default we ask for confirmation */ Tcl_DStringInit(&optsPtr->utfDirString); optsPtr->file[0] = 0; @@ -1224,10 +1286,7 @@ ParseOFNOptions( string = Tcl_GetString(valuePtr); switch (options[index].value) { case FILE_DEFAULT: - if (string[0] == '.') { - string++; - } - optsPtr->extension = string; + optsPtr->extObj = valuePtr; break; case FILE_TYPES: optsPtr->filterObj = valuePtr; @@ -1257,7 +1316,7 @@ ParseOFNOptions( } break; case FILE_TITLE: - optsPtr->title = string; + optsPtr->titleObj = valuePtr; break; case FILE_TYPEVARIABLE: optsPtr->typeVariableObj = valuePtr; @@ -1275,6 +1334,12 @@ ParseOFNOptions( return TCL_ERROR; } break; + case FILE_UNDOCUMENTED_XP_STYLE: + if (Tcl_GetBooleanFromObj(interp, valuePtr, + &optsPtr->forceXPStyle) != TCL_OK) { + return TCL_ERROR; + } + break; } } @@ -1288,143 +1353,209 @@ end: /* interp should already hold error */ /* *---------------------------------------------------------------------- + * VistaFileDialogsAvailable + * + * Checks whether the new (Vista) file dialogs can be used on + * the system. + * + * Returns: + * 1 if new dialogs are available, 0 otherwise + * + * Side effects: + * Loads required procedures dynamically if available. + * If new dialogs are available, COM is also initialized. + *---------------------------------------------------------------------- + */ +static int VistaFileDialogsAvailable() +{ + HRESULT hr; + IFileDialog *fdlgPtr = NULL; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (tsdPtr->newFileDialogsAvailable == FDLG_STATE_INIT) { + tsdPtr->newFileDialogsAvailable = FDLG_STATE_USE_OLD; + LoadShellProcs(); + if (ShellProcs.SHCreateItemFromParsingName != NULL) { + hr = CoInitialize(0); + /* XXX - need we schedule CoUninitialize at thread shutdown ? */ + + /* Ensure all COM interfaces we use are available */ + if (SUCCEEDED(hr)) { + hr = CoCreateInstance(&CLSID_FileOpenDialog, NULL, + CLSCTX_INPROC_SERVER, &IID_IFileOpenDialog, &fdlgPtr); + if (SUCCEEDED(hr)) { + fdlgPtr->lpVtbl->Release(fdlgPtr); + hr = CoCreateInstance(&CLSID_FileSaveDialog, NULL, + CLSCTX_INPROC_SERVER, &IID_IFileSaveDialog, + &fdlgPtr); + if (SUCCEEDED(hr)) { + fdlgPtr->lpVtbl->Release(fdlgPtr); + + /* Looks like we have all we need */ + tsdPtr->newFileDialogsAvailable = FDLG_STATE_USE_NEW; + } + } + } + } + } + + return (tsdPtr->newFileDialogsAvailable == FDLG_STATE_USE_NEW); +} + +/* + *---------------------------------------------------------------------- * * GetFileNameVista -- * * Displays the new file dialogs on Vista and later. * * Results: - * TCL_OK - if dialog was successfully displayed + * TCL_OK - dialog was successfully displayed, results returned in interp * TCL_ERROR - error return - * TCL_CONTINUE - new dialogs not available. Caller should go - * on to display the old style dialogs. * * Side effects: - * Dialogs is displayed and results returned in interpreter on success. - * COM subsystem is initialized if not already done. + * Dialogs is displayed *---------------------------------------------------------------------- */ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) { HRESULT hr; + HWND hWnd; + DWORD flags; IFileDialog *fdlgPtr = NULL; + LPWSTR wstr; ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); - if (tsdPtr->useNewFileDialogs == FDLG_STATE_USE_OLD) - return TCL_CONTINUE; /* Not an error, go try old style dialogs */ - - if (tsdPtr->useNewFileDialogs == FDLG_STATE_INIT) { - tsdPtr->useNewFileDialogs = FDLG_STATE_USE_OLD; - - hr = CoInitialize(0); - /* On failures we do not error. Instead we fall back to old method */ + if (open) + hr = CoCreateInstance(&CLSID_FileOpenDialog, NULL, + CLSCTX_INPROC_SERVER, &IID_IFileOpenDialog, &fdlgPtr); + else + hr = CoCreateInstance(&CLSID_FileSaveDialog, NULL, + CLSCTX_INPROC_SERVER, &IID_IFileSaveDialog, &fdlgPtr); + + /* + * At this point new interfaces are supposed to be available. + * fdlgPtr is actually a IFileOpenDialog or IFileSaveDialog + * both of which inherit from IFileDialog. We use the common + * IFileDialog interface for the most part, casting only for + * type-specific calls. + */ + Tk_MakeWindowExist(optsPtr->tkwin); + hWnd = Tk_GetHWND(Tk_WindowId(optsPtr->tkwin)); + + /* + * Get current settings first because we want to preserve existing + * settings like whether to show hidden files etc. based on the + * user's existing preference + */ + hr = fdlgPtr->lpVtbl->GetOptions(fdlgPtr, &flags); + if (FAILED(hr)) + goto error_return; + + /* Flags are equivalent to those we used in the older API */ + + /* + * Following flags must be set irrespective of original setting + * XXX - should FOS_NOVALIDATE be there ? Note FOS_NOVALIDATE has different + * semantics than OFN_NOVALIDATE in the old API. + */ + flags |= + FOS_FORCEFILESYSTEM | /* Only want files, not other shell items */ + FOS_NOVALIDATE | /* Don't check for access denied etc. */ + FOS_PATHMUSTEXIST; /* The *directory* path must exist */ + + + if (optsPtr->multi) + flags |= FOS_ALLOWMULTISELECT; + else + flags &= ~FOS_ALLOWMULTISELECT; + + if (optsPtr->confirmOverwrite) + flags |= FOS_OVERWRITEPROMPT; + else + flags &= ~FOS_OVERWRITEPROMPT; + + if (optsPtr->extObj != NULL) { + wstr = Tcl_GetUnicode(optsPtr->extObj); + if (wstr[0] == L'.') + ++wstr; + hr = fdlgPtr->lpVtbl->SetDefaultExtension(fdlgPtr, wstr); if (FAILED(hr)) - return TCL_CONTINUE; - - /* Verify interfaces are available */ - if (open) { - hr = CoCreateInstance(&CLSID_FileOpenDialog, NULL, - CLSCTX_INPROC_SERVER, &IID_IFileOpenDialog, &fdlgPtr); - } else { - hr = CoCreateInstance(&CLSID_FileSaveDialog, NULL, - CLSCTX_INPROC_SERVER, &IID_IFileSaveDialog, &fdlgPtr); - } - - if (FAILED(hr)) { - CoUninitialize(); - return TCL_CONTINUE; - } + goto error_return; + } - tsdPtr->useNewFileDialogs = FDLG_STATE_USE_NEW; - /* - * XXX - need to arrange for CoUninitialize to be called on thread - * exit if useNewFileDialogs is FDLG_STATE_USE_NEW. - */ - } else { - /* FDLG_STATE_USE_NEW */ - if (open) { - hr = CoCreateInstance(&CLSID_FileOpenDialog, NULL, - CLSCTX_INPROC_SERVER, &IID_IFileOpenDialog, &fdlgPtr); - } else { - hr = CoCreateInstance(&CLSID_FileSaveDialog, NULL, - CLSCTX_INPROC_SERVER, &IID_IFileSaveDialog, &fdlgPtr); - } + if (optsPtr->titleObj != NULL) { + hr = fdlgPtr->lpVtbl->SetTitle(fdlgPtr, + Tcl_GetUnicode(optsPtr->titleObj)); + if (FAILED(hr)) + goto error_return; + } + + if (optsPtr->file[0]) { + hr = fdlgPtr->lpVtbl->SetFileName(fdlgPtr, optsPtr->file); + if (FAILED(hr)) + goto error_return; } - /* At this point new interfaces are supposed to be available */ - fdlgPtr->lpVtbl->Show(fdlgPtr, NULL); + + fdlgPtr->lpVtbl->Show(fdlgPtr, hWnd); fdlgPtr->lpVtbl->Release(fdlgPtr); return TCL_OK; -} - +error_return: + if (fdlgPtr) + fdlgPtr->lpVtbl->Release(fdlgPtr); + Tcl_SetObjResult(interp, TkWin32ErrorObj(hr)); + return TCL_ERROR; +} /* *---------------------------------------------------------------------- * - * GetFileName -- + * GetFileNameXP -- * - * Calls GetOpenFileName() or GetSaveFileName(). + * Displays the old pre-Vista file dialogs. * * Results: - * See user documentation. + * TCL_OK - if dialog was successfully displayed + * TCL_ERROR - error return * * Side effects: - * See user documentation. - * + * See user documentation. *---------------------------------------------------------------------- */ - -static int -GetFileName( - ClientData clientData, /* Main window associated with interpreter. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[], /* Argument objects. */ - int open) /* 1 to call GetOpenFileName(), 0 to call - * GetSaveFileName(). */ +static int GetFileNameXP(Tcl_Interp *interp, OFNOpts *optsPtr, int open) { OPENFILENAME ofn; OFNData ofnData; - OFNOpts ofnOpts; int cdlgerr; int filterIndex = 0, result = TCL_ERROR, winCode, oldMode; HWND hWnd; Tcl_DString utfFilterString, ds; Tcl_DString extString, filterString, dirString, titleString; + const char *str; ThreadSpecificData *tsdPtr = - Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); ZeroMemory(&ofnData, sizeof(OFNData)); Tcl_DStringInit(&utfFilterString); - /* Parse the arguments. */ - - result = ParseOFNOptions(clientData, interp, objc, objv, open, &ofnOpts); - if (result != TCL_OK) - return result; - - result = GetFileNameVista(interp, &ofnOpts, open); - if (result != TCL_CONTINUE) { - CleanupOFNOptions(&ofnOpts); - return result; - } - - if (MakeFilter(interp, ofnOpts.filterObj, &utfFilterString, - ofnOpts.initialTypeObj, &filterIndex) != TCL_OK) { + if (MakeFilter(interp, optsPtr->filterObj, &utfFilterString, + optsPtr->initialTypeObj, &filterIndex) != TCL_OK) { goto end; } - Tk_MakeWindowExist(ofnOpts.tkwin); - hWnd = Tk_GetHWND(Tk_WindowId(ofnOpts.tkwin)); + Tk_MakeWindowExist(optsPtr->tkwin); + hWnd = Tk_GetHWND(Tk_WindowId(optsPtr->tkwin)); ZeroMemory(&ofn, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hWnd; ofn.hInstance = TkWinGetHInstance(ofn.hwndOwner); - ofn.lpstrFile = ofnOpts.file; + ofn.lpstrFile = optsPtr->file; ofn.nMaxFile = TK_MULTI_MAX_PATH; ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_ENABLEHOOK| OFN_ENABLESIZING; @@ -1433,13 +1564,13 @@ GetFileName( if (open != 0) { ofn.Flags |= OFN_FILEMUSTEXIST; - } else if (ofnOpts.confirmOverwrite) { + } else if (optsPtr->confirmOverwrite) { ofn.Flags |= OFN_OVERWRITEPROMPT; } if (tsdPtr->debugFlag != 0) { ofnData.interp = interp; } - if (ofnOpts.multi != 0) { + if (optsPtr->multi != 0) { ofn.Flags |= OFN_ALLOWMULTISELECT; /* @@ -1451,8 +1582,11 @@ GetFileName( ofnData.dynFileBuffer = ckalloc(512 * sizeof(TCHAR)); } - if (ofnOpts.extension != NULL) { - Tcl_WinUtfToTChar(ofnOpts.extension, -1, &extString); + if (optsPtr->extObj != NULL) { + str = Tcl_GetString(optsPtr->extObj); + if (str[0] == '.') + ++str; + Tcl_WinUtfToTChar(str, -1, &extString); ofn.lpstrDefExt = (TCHAR *) Tcl_DStringValue(&extString); } @@ -1461,9 +1595,9 @@ GetFileName( ofn.lpstrFilter = (TCHAR *) Tcl_DStringValue(&filterString); ofn.nFilterIndex = filterIndex; - if (Tcl_DStringValue(&ofnOpts.utfDirString)[0] != '\0') { - Tcl_WinUtfToTChar(Tcl_DStringValue(&ofnOpts.utfDirString), - Tcl_DStringLength(&ofnOpts.utfDirString), &dirString); + if (Tcl_DStringValue(&optsPtr->utfDirString)[0] != '\0') { + Tcl_WinUtfToTChar(Tcl_DStringValue(&optsPtr->utfDirString), + Tcl_DStringLength(&optsPtr->utfDirString), &dirString); } else { /* * NT 5.0 changed the meaning of lpstrInitialDir, so we have to ensure @@ -1472,10 +1606,10 @@ GetFileName( Tcl_DString cwd; - Tcl_DStringFree(&ofnOpts.utfDirString); - if ((Tcl_GetCwd(interp, &ofnOpts.utfDirString) == NULL) || + Tcl_DStringFree(&optsPtr->utfDirString); + if ((Tcl_GetCwd(interp, &optsPtr->utfDirString) == NULL) || (Tcl_TranslateFileName(interp, - Tcl_DStringValue(&ofnOpts.utfDirString), &cwd) == NULL)) { + Tcl_DStringValue(&optsPtr->utfDirString), &cwd) == NULL)) { Tcl_ResetResult(interp); } else { Tcl_WinUtfToTChar(Tcl_DStringValue(&cwd), @@ -1485,8 +1619,8 @@ GetFileName( } ofn.lpstrInitialDir = (TCHAR *) Tcl_DStringValue(&dirString); - if (ofnOpts.title != NULL) { - Tcl_WinUtfToTChar(ofnOpts.title, -1, &titleString); + if (optsPtr->titleObj != NULL) { + Tcl_WinUtfToTChar(Tcl_GetString(optsPtr->titleObj), -1, &titleString); ofn.lpstrTitle = (TCHAR *) Tcl_DStringValue(&titleString); } @@ -1604,20 +1738,20 @@ GetFileName( Tcl_DStringFree(&ds); } result = TCL_OK; - if ((ofn.nFilterIndex > 0) && gotFilename && ofnOpts.typeVariableObj - && ofnOpts.filterObj) { + if ((ofn.nFilterIndex > 0) && gotFilename && optsPtr->typeVariableObj + && optsPtr->filterObj) { int listObjc, count; Tcl_Obj **listObjv = NULL; Tcl_Obj **typeInfo = NULL; - if (Tcl_ListObjGetElements(interp, ofnOpts.filterObj, + if (Tcl_ListObjGetElements(interp, optsPtr->filterObj, &listObjc, &listObjv) != TCL_OK) { result = TCL_ERROR; } else if (Tcl_ListObjGetElements(interp, listObjv[ofn.nFilterIndex - 1], &count, &typeInfo) != TCL_OK) { result = TCL_ERROR; - } else if (Tcl_ObjSetVar2(interp, ofnOpts.typeVariableObj, NULL, + } else if (Tcl_ObjSetVar2(interp, optsPtr->typeVariableObj, NULL, typeInfo[0], TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) { result = TCL_ERROR; } @@ -1644,16 +1778,59 @@ GetFileName( Tcl_DStringFree(&extString); } - end: +end: Tcl_DStringFree(&utfFilterString); if (ofnData.dynFileBuffer != NULL) { ckfree(ofnData.dynFileBuffer); ofnData.dynFileBuffer = NULL; } - CleanupOFNOptions(&ofnOpts); return result; } + + +/* + *---------------------------------------------------------------------- + * + * GetFileName -- + * + * Calls GetOpenFileName() or GetSaveFileName(). + * + * Results: + * See user documentation. + * + * Side effects: + * See user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +GetFileName( + ClientData clientData, /* Main window associated with interpreter. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[], /* Argument objects. */ + int open) /* 1 to call GetOpenFileName(), 0 to call + * GetSaveFileName(). */ +{ + OFNOpts ofnOpts; + int result; + + /* Parse the arguments. */ + result = ParseOFNOptions(clientData, interp, objc, objv, open, &ofnOpts); + if (result != TCL_OK) + return result; + + if (VistaFileDialogsAvailable() && ! ofnOpts.forceXPStyle) + result = GetFileNameVista(interp, &ofnOpts, open); + else + result = GetFileNameXP(interp, &ofnOpts, open); + + CleanupOFNOptions(&ofnOpts); + return result; +} + /* *------------------------------------------------------------------------- diff --git a/win/tkWinInit.c b/win/tkWinInit.c index 4a327a2..b1b2d6b 100644 --- a/win/tkWinInit.c +++ b/win/tkWinInit.c @@ -159,6 +159,57 @@ TkpDisplayWarning( } /* + * ---------------------------------------------------------------------- + * + * Win32ErrorObj -- + * + * Returns a string object containing text from a COM or Win32 error code + * + * Results: + * A Tcl_Obj containing the Win32 error message. + * + * Side effects: + * Removed the error message from the COM threads error object. + * + * ---------------------------------------------------------------------- + */ + +Tcl_Obj* +TkWin32ErrorObj( + HRESULT hrError) +{ + LPTSTR lpBuffer = NULL, p = NULL; + TCHAR sBuffer[30]; + Tcl_Obj* errPtr = NULL; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD)hrError, + LANG_NEUTRAL, (LPTSTR)&lpBuffer, 0, NULL); + + if (lpBuffer == NULL) { + lpBuffer = sBuffer; + wsprintf(sBuffer, TEXT("Error Code: %08lX"), hrError); + } + + if ((p = _tcsrchr(lpBuffer, TEXT('\r'))) != NULL) { + *p = TEXT('\0'); + } + +#ifdef _UNICODE + errPtr = Tcl_NewUnicodeObj(lpBuffer, (int)wcslen(lpBuffer)); +#else + errPtr = Tcl_NewStringObj(lpBuffer, (int)strlen(lpBuffer)); +#endif /* _UNICODE */ + + if (lpBuffer != sBuffer) { + LocalFree((HLOCAL)lpBuffer); + } + + return errPtr; +} + + +/* * Local Variables: * mode: c * c-basic-offset: 4 diff --git a/win/tkWinInt.h b/win/tkWinInt.h index 6a3978f..0e2c844 100644 --- a/win/tkWinInt.h +++ b/win/tkWinInt.h @@ -201,6 +201,12 @@ MODULE_SCOPE void TkpWinToplevelDetachWindow(TkWindow *winPtr); MODULE_SCOPE int TkpWmGetState(TkWindow *winPtr); /* + * Common routines used in Windows implementation + */ +MODULE_SCOPE Tcl_Obj * TkWin32ErrorObj(HRESULT hrError); + + +/* * The following functions are not present in old versions of Windows * API headers but are used in the Tk source to ensure 64bit * compatibility. diff --git a/win/tkWinSend.c b/win/tkWinSend.c index 7fde655..6c4731a 100644 --- a/win/tkWinSend.c +++ b/win/tkWinSend.c @@ -77,7 +77,6 @@ static int FindInterpreterObject(Tcl_Interp *interp, static int Send(LPDISPATCH pdispInterp, Tcl_Interp *interp, int async, ClientData clientData, int objc, Tcl_Obj *const objv[]); -static Tcl_Obj * Win32ErrorObj(HRESULT hrError); static void SendTrace(const char *format, ...); static Tcl_EventProc SendEventProc; @@ -281,7 +280,7 @@ TkGetInterpNames( if (objList != NULL) { Tcl_DecrRefCount(objList); } - Tcl_SetObjResult(interp, Win32ErrorObj(hr)); + Tcl_SetObjResult(interp, TkWin32ErrorObj(hr)); result = TCL_ERROR; } @@ -451,7 +450,7 @@ FindInterpreterObject( pROT->lpVtbl->Release(pROT); } if (FAILED(hr) && result == TCL_OK) { - Tcl_SetObjResult(interp, Win32ErrorObj(hr)); + Tcl_SetObjResult(interp, TkWin32ErrorObj(hr)); result = TCL_ERROR; } return result; @@ -809,56 +808,6 @@ Send( /* * ---------------------------------------------------------------------- * - * Win32ErrorObj -- - * - * Returns a string object containing text from a COM or Win32 error code - * - * Results: - * A Tcl_Obj containing the Win32 error message. - * - * Side effects: - * Removed the error message from the COM threads error object. - * - * ---------------------------------------------------------------------- - */ - -static Tcl_Obj* -Win32ErrorObj( - HRESULT hrError) -{ - LPTSTR lpBuffer = NULL, p = NULL; - TCHAR sBuffer[30]; - Tcl_Obj* errPtr = NULL; - - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM - | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD)hrError, - LANG_NEUTRAL, (LPTSTR)&lpBuffer, 0, NULL); - - if (lpBuffer == NULL) { - lpBuffer = sBuffer; - wsprintf(sBuffer, TEXT("Error Code: %08lX"), hrError); - } - - if ((p = _tcsrchr(lpBuffer, TEXT('\r'))) != NULL) { - *p = TEXT('\0'); - } - -#ifdef _UNICODE - errPtr = Tcl_NewUnicodeObj(lpBuffer, (int)wcslen(lpBuffer)); -#else - errPtr = Tcl_NewStringObj(lpBuffer, (int)strlen(lpBuffer)); -#endif /* _UNICODE */ - - if (lpBuffer != sBuffer) { - LocalFree((HLOCAL)lpBuffer); - } - - return errPtr; -} - -/* - * ---------------------------------------------------------------------- - * * TkWinSend_SetExcepInfo -- * * Convert the error information from a Tcl interpreter into a COM -- cgit v0.12 From e8166f021c14670eed922b9f413059d3d5f729f0 Mon Sep 17 00:00:00 2001 From: ashok Date: Sun, 14 Sep 2014 09:43:57 +0000 Subject: Implemented multiselect in new file dialogs. --- win/tkWinDialog.c | 144 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 116 insertions(+), 28 deletions(-) diff --git a/win/tkWinDialog.c b/win/tkWinDialog.c index d510654..0b77469 100644 --- a/win/tkWinDialog.c +++ b/win/tkWinDialog.c @@ -28,6 +28,7 @@ #endif /* These needed for compilation with VC++ 5.2 */ +/* XXX - remove these since need at least VC 6 */ #ifndef BIF_EDITBOX #define BIF_EDITBOX 0x10 #endif @@ -36,6 +37,7 @@ #define BIF_VALIDATE 0x0020 #endif +/* This "new" dialog style is now actually the "old" dialog style post-Vista */ #ifndef BIF_NEWDIALOGSTYLE #define BIF_NEWDIALOGSTYLE 0x0040 #endif @@ -59,7 +61,7 @@ typedef struct ThreadSpecificData { HHOOK hMsgBoxHook; /* Hook proc for tk_messageBox and the */ HICON hSmallIcon; /* icons used by a parent to be used in */ HICON hBigIcon; /* the message box */ - int newFileDialogsAvailable; + int newFileDialogsState; #define FDLG_STATE_INIT 0 /* Uninitialized */ #define FDLG_STATE_USE_NEW 1 /* Use the new dialogs */ #define FDLG_STATE_USE_OLD 2 /* Use the old dialogs */ @@ -778,7 +780,8 @@ static void SetTkDialog(ClientData clientData); static const char *ConvertExternalFilename(TCHAR *filename, Tcl_DString *dsPtr); static void LoadShellProcs(void); - +static int GetFileNameXP(Tcl_Interp *interp, OFNOpts *optsPtr, int open); +static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open); /* Definitions of dynamically loaded Win32 calls */ typedef HRESULT (STDAPICALLTYPE SHCreateItemFromParsingNameProc)( @@ -1373,8 +1376,8 @@ static int VistaFileDialogsAvailable() ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); - if (tsdPtr->newFileDialogsAvailable == FDLG_STATE_INIT) { - tsdPtr->newFileDialogsAvailable = FDLG_STATE_USE_OLD; + if (tsdPtr->newFileDialogsState == FDLG_STATE_INIT) { + tsdPtr->newFileDialogsState = FDLG_STATE_USE_OLD; LoadShellProcs(); if (ShellProcs.SHCreateItemFromParsingName != NULL) { hr = CoInitialize(0); @@ -1393,14 +1396,14 @@ static int VistaFileDialogsAvailable() fdlgPtr->lpVtbl->Release(fdlgPtr); /* Looks like we have all we need */ - tsdPtr->newFileDialogsAvailable = FDLG_STATE_USE_NEW; + tsdPtr->newFileDialogsState = FDLG_STATE_USE_NEW; } } } } } - return (tsdPtr->newFileDialogsAvailable == FDLG_STATE_USE_NEW); + return (tsdPtr->newFileDialogsState == FDLG_STATE_USE_NEW); } /* @@ -1409,6 +1412,9 @@ static int VistaFileDialogsAvailable() * GetFileNameVista -- * * Displays the new file dialogs on Vista and later. + * This function must generally not be called unless the + * tsdPtr->newFileDialogsState is FDLG_STATE_USE_NEW but if + * it is, it will just pass the call to the older GetFileNameXP * * Results: * TCL_OK - dialog was successfully displayed, results returned in interp @@ -1423,21 +1429,27 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) HRESULT hr; HWND hWnd; DWORD flags; - IFileDialog *fdlgPtr = NULL; + IFileDialog *fdlgIf = NULL; + IShellItem *dirIf = NULL; LPWSTR wstr; ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + if (tsdPtr->newFileDialogsState != FDLG_STATE_USE_NEW) { + /* Should not be called in this condition but be nice about it */ + return GetFileNameXP(interp, optsPtr, open); + } + if (open) hr = CoCreateInstance(&CLSID_FileOpenDialog, NULL, - CLSCTX_INPROC_SERVER, &IID_IFileOpenDialog, &fdlgPtr); + CLSCTX_INPROC_SERVER, &IID_IFileOpenDialog, &fdlgIf); else hr = CoCreateInstance(&CLSID_FileSaveDialog, NULL, - CLSCTX_INPROC_SERVER, &IID_IFileSaveDialog, &fdlgPtr); + CLSCTX_INPROC_SERVER, &IID_IFileSaveDialog, &fdlgIf); /* * At this point new interfaces are supposed to be available. - * fdlgPtr is actually a IFileOpenDialog or IFileSaveDialog + * fdlgIf is actually a IFileOpenDialog or IFileSaveDialog * both of which inherit from IFileDialog. We use the common * IFileDialog interface for the most part, casting only for * type-specific calls. @@ -1450,9 +1462,9 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) * settings like whether to show hidden files etc. based on the * user's existing preference */ - hr = fdlgPtr->lpVtbl->GetOptions(fdlgPtr, &flags); + hr = fdlgIf->lpVtbl->GetOptions(fdlgIf, &flags); if (FAILED(hr)) - goto error_return; + goto vamoose; /* Flags are equivalent to those we used in the older API */ @@ -1477,38 +1489,109 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) else flags &= ~FOS_OVERWRITEPROMPT; + hr = fdlgIf->lpVtbl->SetOptions(fdlgIf, flags); + if (FAILED(hr)) + goto vamoose; + if (optsPtr->extObj != NULL) { wstr = Tcl_GetUnicode(optsPtr->extObj); if (wstr[0] == L'.') ++wstr; - hr = fdlgPtr->lpVtbl->SetDefaultExtension(fdlgPtr, wstr); + hr = fdlgIf->lpVtbl->SetDefaultExtension(fdlgIf, wstr); if (FAILED(hr)) - goto error_return; + goto vamoose; } if (optsPtr->titleObj != NULL) { - hr = fdlgPtr->lpVtbl->SetTitle(fdlgPtr, + hr = fdlgIf->lpVtbl->SetTitle(fdlgIf, Tcl_GetUnicode(optsPtr->titleObj)); if (FAILED(hr)) - goto error_return; + goto vamoose; } if (optsPtr->file[0]) { - hr = fdlgPtr->lpVtbl->SetFileName(fdlgPtr, optsPtr->file); + hr = fdlgIf->lpVtbl->SetFileName(fdlgIf, optsPtr->file); if (FAILED(hr)) - goto error_return; + goto vamoose; } + if (Tcl_DStringValue(&optsPtr->utfDirString)[0] != '\0') { + Tcl_DString dirString; + Tcl_WinUtfToTChar(Tcl_DStringValue(&optsPtr->utfDirString), + Tcl_DStringLength(&optsPtr->utfDirString), &dirString); + hr = ShellProcs.SHCreateItemFromParsingName( + (TCHAR *) Tcl_DStringValue(&dirString), NULL, + &IID_IShellItem, &dirIf); + /* XXX - Note on failure we do not raise error, simply ignore ini dir */ + if (SUCCEEDED(hr)) { + /* Note we use SetFolder, not SetDefaultFolder - see MSDN docs */ + fdlgIf->lpVtbl->SetFolder(fdlgIf, dirIf); /* Ignore errors */ + } + Tcl_DStringFree(&dirString); + } + + hr = fdlgIf->lpVtbl->Show(fdlgIf, hWnd); + if (SUCCEEDED(hr)) { + if (open && optsPtr->multi) { + IShellItemArray *multiIf; + DWORD dw, count; + IFileOpenDialog *fodIf = (IFileOpenDialog *) fdlgIf; + hr = fodIf->lpVtbl->GetResults(fodIf, &multiIf); + if (SUCCEEDED(hr)) { + Tcl_Obj *multiObj = Tcl_NewListObj(count, NULL); + hr = multiIf->lpVtbl->GetCount(multiIf, &count); + if (SUCCEEDED(hr)) { + IShellItem *itemIf; + for (dw = 0; dw < count; ++dw) { + hr = multiIf->lpVtbl->GetItemAt(multiIf, dw, &itemIf); + if (FAILED(hr)) + break; + hr = itemIf->lpVtbl->GetDisplayName(itemIf, + SIGDN_FILESYSPATH, &wstr); + if (SUCCEEDED(hr)) { + Tcl_ListObjAppendElement(interp, multiObj, + Tcl_NewUnicodeObj(wstr, -1)); + } + itemIf->lpVtbl->Release(itemIf); + if (FAILED(hr)) + break; + } + } + multiIf->lpVtbl->Release(multiIf); + if (SUCCEEDED(hr)) + Tcl_SetObjResult(interp, multiObj); + else + Tcl_DecrRefCount(multiObj); + } + } else { + IShellItem *resultIf; + hr = fdlgIf->lpVtbl->GetResult(fdlgIf, &resultIf); + if (SUCCEEDED(hr)) { + hr = resultIf->lpVtbl->GetDisplayName(resultIf, SIGDN_FILESYSPATH, + &wstr); + if (SUCCEEDED(hr)) { + Tcl_SetObjResult(interp, Tcl_NewUnicodeObj(wstr, -1)); + CoTaskMemFree(wstr); + } + resultIf->lpVtbl->Release(resultIf); + } + } + } else { + if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) + hr = 0; /* User cancelled, return empty string */ + } - fdlgPtr->lpVtbl->Show(fdlgPtr, hWnd); - fdlgPtr->lpVtbl->Release(fdlgPtr); - return TCL_OK; - -error_return: - if (fdlgPtr) - fdlgPtr->lpVtbl->Release(fdlgPtr); - Tcl_SetObjResult(interp, TkWin32ErrorObj(hr)); - return TCL_ERROR; +vamoose: /* (hr != 0) => error */ + if (dirIf) + dirIf->lpVtbl->Release(dirIf); + if (fdlgIf) + fdlgIf->lpVtbl->Release(fdlgIf); + if (hr == 0) + return TCL_OK; + else { + Tcl_SetObjResult(interp, TkWin32ErrorObj(hr)); + return TCL_ERROR; + } } @@ -1542,6 +1625,9 @@ static int GetFileNameXP(Tcl_Interp *interp, OFNOpts *optsPtr, int open) ZeroMemory(&ofnData, sizeof(OFNData)); Tcl_DStringInit(&utfFilterString); + Tcl_DStringInit(&dirString); /* XXX - original code was missing this + leaving dirString uninitialized for + the unlikely code path where cwd failed */ if (MakeFilter(interp, optsPtr->filterObj, &utfFilterString, optsPtr->initialTypeObj, &filterIndex) != TCL_OK) { @@ -1558,7 +1644,7 @@ static int GetFileNameXP(Tcl_Interp *interp, OFNOpts *optsPtr, int open) ofn.lpstrFile = optsPtr->file; ofn.nMaxFile = TK_MULTI_MAX_PATH; ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR - | OFN_EXPLORER | OFN_ENABLEHOOK| OFN_ENABLESIZING; + | OFN_EXPLORER| OFN_ENABLEHOOK| OFN_ENABLESIZING; ofn.lpfnHook = (LPOFNHOOKPROC) OFNHookProc; ofn.lCustData = (LPARAM) &ofnData; @@ -1771,6 +1857,8 @@ static int GetFileNameXP(Tcl_Interp *interp, OFNOpts *optsPtr, int open) Tcl_DStringFree(&titleString); } if (ofn.lpstrInitialDir != NULL) { + /* XXX - huh? lpstrInitialDir is set from Tcl_DStringValue which + can never return NULL */ Tcl_DStringFree(&dirString); } Tcl_DStringFree(&filterString); -- cgit v0.12 From 012193adf627e66fafc0ef0e18e8e90feddfce89 Mon Sep 17 00:00:00 2001 From: ashok Date: Sun, 14 Sep 2014 17:32:49 +0000 Subject: Implemented -filetypes and -typevariable for new Vista file dialogs. --- win/tkWinDialog.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 207 insertions(+), 13 deletions(-) diff --git a/win/tkWinDialog.c b/win/tkWinDialog.c index 0b77469..485b9ff 100644 --- a/win/tkWinDialog.c +++ b/win/tkWinDialog.c @@ -780,8 +780,13 @@ static void SetTkDialog(ClientData clientData); static const char *ConvertExternalFilename(TCHAR *filename, Tcl_DString *dsPtr); static void LoadShellProcs(void); + static int GetFileNameXP(Tcl_Interp *interp, OFNOpts *optsPtr, int open); static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open); +static int MakeFilterVista(Tcl_Interp *interp, OFNOpts *optsPtr, + DWORD *countPtr, COMDLG_FILTERSPEC **dlgFilterPtrPtr, + DWORD *defaultFilterIndexPtr); +static void FreeFilterVista(DWORD count, COMDLG_FILTERSPEC *dlgFilterPtr); /* Definitions of dynamically loaded Win32 calls */ typedef HRESULT (STDAPICALLTYPE SHCreateItemFromParsingNameProc)( @@ -1428,10 +1433,12 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) { HRESULT hr; HWND hWnd; - DWORD flags; + DWORD flags, nfilters, defaultFilterIndex; + COMDLG_FILTERSPEC *filterPtr = NULL; IFileDialog *fdlgIf = NULL; IShellItem *dirIf = NULL; LPWSTR wstr; + Tcl_Obj *resultObj = NULL; ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); @@ -1440,13 +1447,6 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) return GetFileNameXP(interp, optsPtr, open); } - if (open) - hr = CoCreateInstance(&CLSID_FileOpenDialog, NULL, - CLSCTX_INPROC_SERVER, &IID_IFileOpenDialog, &fdlgIf); - else - hr = CoCreateInstance(&CLSID_FileSaveDialog, NULL, - CLSCTX_INPROC_SERVER, &IID_IFileSaveDialog, &fdlgIf); - /* * At this point new interfaces are supposed to be available. * fdlgIf is actually a IFileOpenDialog or IFileSaveDialog @@ -1458,6 +1458,38 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) hWnd = Tk_GetHWND(Tk_WindowId(optsPtr->tkwin)); /* + * The only validation we need to do w.r.t caller supplied data + * is the filter specification so do that before creating + */ + if (MakeFilterVista(interp, optsPtr, &nfilters, &filterPtr, + &defaultFilterIndex) != TCL_OK) + return TCL_ERROR; + + /* + * Beyond this point, do not just return on error as there will be + * resources that need to be released/freed. + */ + + if (open) + hr = CoCreateInstance(&CLSID_FileOpenDialog, NULL, + CLSCTX_INPROC_SERVER, &IID_IFileOpenDialog, &fdlgIf); + else + hr = CoCreateInstance(&CLSID_FileSaveDialog, NULL, + CLSCTX_INPROC_SERVER, &IID_IFileSaveDialog, &fdlgIf); + + if (FAILED(hr)) + goto vamoose; + + if (filterPtr) { + hr = fdlgIf->lpVtbl->SetFileTypes(fdlgIf, nfilters, filterPtr); + if (FAILED(hr)) + goto vamoose; + hr = fdlgIf->lpVtbl->SetFileTypeIndex(fdlgIf, defaultFilterIndex); + if (FAILED(hr)) + goto vamoose; + } + + /* * Get current settings first because we want to preserve existing * settings like whether to show hidden files etc. based on the * user's existing preference @@ -1538,8 +1570,9 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) IFileOpenDialog *fodIf = (IFileOpenDialog *) fdlgIf; hr = fodIf->lpVtbl->GetResults(fodIf, &multiIf); if (SUCCEEDED(hr)) { - Tcl_Obj *multiObj = Tcl_NewListObj(count, NULL); + Tcl_Obj *multiObj; hr = multiIf->lpVtbl->GetCount(multiIf, &count); + multiObj = Tcl_NewListObj(count, NULL); if (SUCCEEDED(hr)) { IShellItem *itemIf; for (dw = 0; dw < count; ++dw) { @@ -1559,7 +1592,7 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) } multiIf->lpVtbl->Release(multiIf); if (SUCCEEDED(hr)) - Tcl_SetObjResult(interp, multiObj); + resultObj = multiObj; else Tcl_DecrRefCount(multiObj); } @@ -1570,12 +1603,26 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) hr = resultIf->lpVtbl->GetDisplayName(resultIf, SIGDN_FILESYSPATH, &wstr); if (SUCCEEDED(hr)) { - Tcl_SetObjResult(interp, Tcl_NewUnicodeObj(wstr, -1)); + resultObj = Tcl_NewUnicodeObj(wstr, -1); CoTaskMemFree(wstr); } resultIf->lpVtbl->Release(resultIf); } } + if (SUCCEEDED(hr)) { + if (filterPtr && optsPtr->typeVariableObj) { + UINT ftix; + hr = fdlgIf->lpVtbl->GetFileTypeIndex(fdlgIf, &ftix); + if (SUCCEEDED(hr)) { + /* Note ftix is a 1-based index */ + if (ftix > 0 && ftix <= nfilters) { + Tcl_ObjSetVar2(interp, optsPtr->typeVariableObj, NULL, + Tcl_NewUnicodeObj(filterPtr[ftix-1].pszName, -1), + TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG); + } + } + } + } } else { if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) hr = 0; /* User cancelled, return empty string */ @@ -1586,9 +1633,17 @@ vamoose: /* (hr != 0) => error */ dirIf->lpVtbl->Release(dirIf); if (fdlgIf) fdlgIf->lpVtbl->Release(fdlgIf); - if (hr == 0) + + if (filterPtr) + FreeFilterVista(nfilters, filterPtr); + + if (hr == 0) { + if (resultObj) /* May be NULL if user cancelled */ + Tcl_SetObjResult(interp, resultObj); return TCL_OK; - else { + } else { + if (resultObj) + Tcl_DecrRefCount(resultObj); Tcl_SetObjResult(interp, TkWin32ErrorObj(hr)); return TCL_ERROR; } @@ -2242,6 +2297,145 @@ MakeFilter( /* *---------------------------------------------------------------------- * + * FreeFilterVista + * + * Frees storage previously allocated by MakeFilterVista. + * count is the number of elements in dlgFilterPtr[] + */ +static void FreeFilterVista(DWORD count, COMDLG_FILTERSPEC *dlgFilterPtr) +{ + if (dlgFilterPtr != NULL) { + DWORD dw; + for (dw = 0; dw < count; ++dw) { + if (dlgFilterPtr[dw].pszName != NULL) + ckfree(dlgFilterPtr[dw].pszName); + if (dlgFilterPtr[dw].pszSpec != NULL) + ckfree(dlgFilterPtr[dw].pszSpec); + } + ckfree(dlgFilterPtr); + } +} + +/* + *---------------------------------------------------------------------- + * + * MakeFilterVista -- + * + * Returns file type filters in a format required + * by the Vista file dialogs. + * + * Results: + * A standard TCL return value. + * + * Side effects: + * Various values are returned through the parameters as + * described in the comments below. + *---------------------------------------------------------------------- + */ +static int MakeFilterVista( + Tcl_Interp *interp, /* Current interpreter. */ + OFNOpts *optsPtr, /* Caller specified options */ + DWORD *countPtr, /* Will hold number of filters */ + COMDLG_FILTERSPEC **dlgFilterPtrPtr, /* Will hold pointer to filter array. + Set to NULL if no filters specified. + Must be freed by calling + FreeFilterVista */ + DWORD *initialIndexPtr) /* Will hold index of default type */ +{ + COMDLG_FILTERSPEC *dlgFilterPtr; + const char *initial = NULL; + FileFilterList flist; + FileFilter *filterPtr; + DWORD initialIndex = 0; + Tcl_DString ds, patterns; + int i; + + if (optsPtr->filterObj == NULL) { + *dlgFilterPtrPtr = NULL; + *countPtr = 0; + return TCL_OK; + } + + if (optsPtr->initialTypeObj) + initial = Tcl_GetString(optsPtr->initialTypeObj); + + TkInitFileFilters(&flist); + if (TkGetFileFilters(interp, &flist, optsPtr->filterObj, 1) != TCL_OK) + return TCL_ERROR; + + if (flist.filters == NULL) { + *dlgFilterPtrPtr = NULL; + *countPtr = 0; + return TCL_OK; + } + + Tcl_DStringInit(&ds); + Tcl_DStringInit(&patterns); + dlgFilterPtr = ckalloc(flist.numFilters * sizeof(*dlgFilterPtr)); + + for (i = 0, filterPtr = flist.filters; + filterPtr; + filterPtr = filterPtr->next, ++i) { + const char *sep; + FileFilterClause *clausePtr; + int nbytes; + + /* Check if this entry should be shown as the default */ + if (initial && strcmp(initial, filterPtr->name) == 0) + initialIndex = i+1; /* Windows filter indices are 1-based */ + + /* First stash away the text description of the pattern */ + Tcl_WinUtfToTChar(filterPtr->name, -1, &ds); + nbytes = Tcl_DStringLength(&ds); /* # bytes, not Unicode chars */ + nbytes += sizeof(WCHAR); /* Terminating \0 */ + dlgFilterPtr[i].pszName = ckalloc(nbytes); + memmove(dlgFilterPtr[i].pszName, Tcl_DStringValue(&ds), nbytes); + Tcl_DStringFree(&ds); + + /* + * Loop through and join patterns with a ";" Each "clause" + * corresponds to a single textual description (called typename) + * in the tk_getOpenFile docs. Each such typename may occur + * multiple times and all these form a single filter entry + * with one clause per occurence. Further each clause may specify + * multiple patterns. Hence the nested loop here. + */ + sep = ""; + for (clausePtr=filterPtr->clauses ; clausePtr; + clausePtr=clausePtr->next) { + GlobPattern *globPtr; + for (globPtr = clausePtr->patterns; globPtr; + globPtr = globPtr->next) { + Tcl_DStringAppend(&patterns, sep, -1); + Tcl_DStringAppend(&patterns, globPtr->pattern, -1); + sep = ";"; + } + } + + /* Again we need a Unicode form of the string */ + Tcl_WinUtfToTChar(Tcl_DStringValue(&patterns), -1, &ds); + nbytes = Tcl_DStringLength(&ds); /* # bytes, not Unicode chars */ + nbytes += sizeof(WCHAR); /* Terminating \0 */ + dlgFilterPtr[i].pszSpec = ckalloc(nbytes); + memmove(dlgFilterPtr[i].pszSpec, Tcl_DStringValue(&ds), nbytes); + Tcl_DStringFree(&ds); + Tcl_DStringFree(&patterns); + } + + if (initialIndex == 0) + initialIndex = 1; /* If no default, show first entry */ + *initialIndexPtr = initialIndex; + *dlgFilterPtrPtr = dlgFilterPtr; + *countPtr = flist.numFilters; + + TkFreeFileFilters(&flist); + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * * Tk_ChooseDirectoryObjCmd -- * * This function implements the "tk_chooseDirectory" dialog box for the -- cgit v0.12 From ac7bfc0e538bc33acfb017888c77a458b586c160 Mon Sep 17 00:00:00 2001 From: ashok Date: Wed, 17 Sep 2014 17:18:53 +0000 Subject: Implemented Vista+ tk_chooseDirectory dialogs --- win/tkWinDialog.c | 717 ++++++++++++++++++------------------------------------ 1 file changed, 239 insertions(+), 478 deletions(-) diff --git a/win/tkWinDialog.c b/win/tkWinDialog.c index 485b9ff..6b7035d 100644 --- a/win/tkWinDialog.c +++ b/win/tkWinDialog.c @@ -179,7 +179,8 @@ typedef struct OFNOpts { Tcl_Obj *initialTypeObj; /* Initial value of above, or NULL */ Tcl_DString utfDirString; /* Initial dir */ int multi; /* Multiple selection enabled */ - int confirmOverwrite; /* Multiple selection enabled */ + int confirmOverwrite; /* Confirm before overwriting */ + int mustExist; /* Used only for */ int forceXPStyle; /* XXX - Force XP style even on newer systems */ TCHAR file[TK_MULTI_MAX_PATH]; /* File name XXX - fixed size because it was so @@ -190,6 +191,14 @@ typedef struct OFNOpts { */ } OFNOpts; +/* Define the operation for which option parsing is to be done. */ +enum OFNOper { + OFN_FILE_SAVE, /* tk_getOpenFile */ + OFN_FILE_OPEN, /* tk_getSaveFile */ + OFN_DIR_CHOOSE /* tk_chooseDirectory */ +}; + + /* * The following definitions are required when using older versions of * Visual C++ (like 6.0) and possibly MingW. Those headers do not contain @@ -217,63 +226,31 @@ typedef struct IShellItemArrayVtbl BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( - IShellItemArray * This, - /* [in] */ REFIID riid, - /* [annotation][iid_is][out] */ - void **ppvObject); - - ULONG ( STDMETHODCALLTYPE *AddRef )( - IShellItemArray * This); - - ULONG ( STDMETHODCALLTYPE *Release )( - IShellItemArray * This); - - HRESULT ( STDMETHODCALLTYPE *BindToHandler )( - IShellItemArray * This, - /* [unique][in] */ IBindCtx *pbc, - /* [in] */ REFGUID bhid, - /* [in] */ REFIID riid, - /* [iid_is][out] */ void **ppvOut); - - HRESULT ( STDMETHODCALLTYPE *GetPropertyStore )( - IShellItemArray * This, - /* Actually enum GETPROPERTYSTOREFLAGS, but we do not use this call */ - /* [in] */ int flags, - /* [in] */ REFIID riid, - /* [iid_is][out] */ void **ppv); - + IShellItemArray * this, REFIID riid,void **ppvObject); + ULONG ( STDMETHODCALLTYPE *AddRef )(IShellItemArray * this); + ULONG ( STDMETHODCALLTYPE *Release )(IShellItemArray * this); + HRESULT ( STDMETHODCALLTYPE *BindToHandler )(IShellItemArray * this, + IBindCtx *pbc, REFGUID bhid, REFIID riid, void **ppvOut); + /* flags is actually is enum GETPROPERTYSTOREFLAGS */ + HRESULT ( STDMETHODCALLTYPE *GetPropertyStore )( + IShellItemArray * this, int flags, REFIID riid, void **ppv); + /* keyType actually REFPROPERTYKEY */ HRESULT ( STDMETHODCALLTYPE *GetPropertyDescriptionList )( - IShellItemArray * This, - /* Actually REFPROPERTYKEY, but this call is not used */ - /* [in] */ void* keyType, - /* [in] */ REFIID riid, - /* [iid_is][out] */ void **ppv); - - HRESULT ( STDMETHODCALLTYPE *GetAttributes )( - IShellItemArray * This, - /* [in] */ SIATTRIBFLAGS AttribFlags, - /* [in] */ SFGAOF sfgaoMask, - /* [out] */ SFGAOF *psfgaoAttribs); - - HRESULT ( STDMETHODCALLTYPE *GetCount )( - IShellItemArray * This, - /* [out] */ DWORD *pdwNumItems); - + IShellItemArray * this, void* keyType, REFIID riid, void **ppv); + HRESULT ( STDMETHODCALLTYPE *GetAttributes )(IShellItemArray * this, + SIATTRIBFLAGS AttribFlags, SFGAOF sfgaoMask, SFGAOF *psfgaoAttribs); + HRESULT ( STDMETHODCALLTYPE *GetCount )( + IShellItemArray * this, DWORD *pdwNumItems); HRESULT ( STDMETHODCALLTYPE *GetItemAt )( - IShellItemArray * This, - /* [in] */ DWORD dwIndex, - /* [out] */ IShellItem **ppsi); - - HRESULT ( STDMETHODCALLTYPE *EnumItems )( - IShellItemArray * This, - /* Actually IEnumShellItems **, but we do not use this call */ - /* [out] */ void **ppenumShellItems); + IShellItemArray * this, DWORD dwIndex, IShellItem **ppsi); + /* ppenumShellItems actually (IEnumShellItems **) */ + HRESULT ( STDMETHODCALLTYPE *EnumItems )( + IShellItemArray * this, void **ppenumShellItems); END_INTERFACE } IShellItemArrayVtbl; -struct IShellItemArray -{ +struct IShellItemArray { CONST_VTBL struct IShellItemArrayVtbl *lpVtbl; }; @@ -342,120 +319,56 @@ typedef struct IFileDialogVtbl BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( - IFileDialog * This, - /* [in] */ REFIID riid, - /* [annotation][iid_is][out] */ - void **ppvObject); - - ULONG ( STDMETHODCALLTYPE *AddRef )( - IFileDialog * This); - - ULONG ( STDMETHODCALLTYPE *Release )( - IFileDialog * This); - - /* [local] */ HRESULT ( STDMETHODCALLTYPE *Show )( - IFileDialog * This, - /* [annotation][unique][in] */ - HWND hwndOwner); - - HRESULT ( STDMETHODCALLTYPE *SetFileTypes )( - IFileDialog * This, - /* [in] */ UINT cFileTypes, - /* [size_is][in] */ const COMDLG_FILTERSPEC *rgFilterSpec); - - HRESULT ( STDMETHODCALLTYPE *SetFileTypeIndex )( - IFileDialog * This, - /* [in] */ UINT iFileType); - - HRESULT ( STDMETHODCALLTYPE *GetFileTypeIndex )( - IFileDialog * This, - /* [out] */ UINT *piFileType); - + IFileDialog * this, REFIID riid, void **ppvObject); + ULONG ( STDMETHODCALLTYPE *AddRef )( IFileDialog * this); + ULONG ( STDMETHODCALLTYPE *Release )( IFileDialog * this); + HRESULT ( STDMETHODCALLTYPE *Show )( IFileDialog * this, HWND hwndOwner); + HRESULT ( STDMETHODCALLTYPE *SetFileTypes )( IFileDialog * this, + UINT cFileTypes, const COMDLG_FILTERSPEC *rgFilterSpec); + HRESULT ( STDMETHODCALLTYPE *SetFileTypeIndex )(IFileDialog * this, UINT); + HRESULT ( STDMETHODCALLTYPE *GetFileTypeIndex )(IFileDialog * this, UINT *); + /* XXX - Actually pfde is IFileDialogEvents* but we do not use + this call and do not want to define IFileDialogEvents as that + pulls in a whole bunch of other stuff. */ HRESULT ( STDMETHODCALLTYPE *Advise )( - IFileDialog * This, - /* XXX - Actually pfde is IFileDialogEvents* but we do not use - this call and do not want to define IFileDialogEvents as that - pulls in a whole bunch of other stuff. */ - /* [in] */ void *pfde, - /* [out] */ DWORD *pdwCookie); - - HRESULT ( STDMETHODCALLTYPE *Unadvise )( - IFileDialog * This, - /* [in] */ DWORD dwCookie); - + IFileDialog * this, void *pfde, DWORD *pdwCookie); + HRESULT ( STDMETHODCALLTYPE *Unadvise )(IFileDialog * this, DWORD dwCookie); HRESULT ( STDMETHODCALLTYPE *SetOptions )( - IFileDialog * This, - /* [in] */ FILEOPENDIALOGOPTIONS fos); - + IFileDialog * this, FILEOPENDIALOGOPTIONS fos); HRESULT ( STDMETHODCALLTYPE *GetOptions )( - IFileDialog * This, - /* [out] */ FILEOPENDIALOGOPTIONS *pfos); - - HRESULT ( STDMETHODCALLTYPE *SetDefaultFolder )( - IFileDialog * This, - /* [in] */ IShellItem *psi); - - HRESULT ( STDMETHODCALLTYPE *SetFolder )( - IFileDialog * This, - /* [in] */ IShellItem *psi); - + IFileDialog * this, FILEOPENDIALOGOPTIONS *pfos); + HRESULT ( STDMETHODCALLTYPE *SetDefaultFolder )( + IFileDialog * this, IShellItem *psi); + HRESULT ( STDMETHODCALLTYPE *SetFolder )( + IFileDialog * this, IShellItem *psi); HRESULT ( STDMETHODCALLTYPE *GetFolder )( - IFileDialog * This, - /* [out] */ IShellItem **ppsi); - + IFileDialog * this, IShellItem **ppsi); HRESULT ( STDMETHODCALLTYPE *GetCurrentSelection )( - IFileDialog * This, - /* [out] */ IShellItem **ppsi); - + IFileDialog * this, IShellItem **ppsi); HRESULT ( STDMETHODCALLTYPE *SetFileName )( - IFileDialog * This, - /* [string][in] */ LPCWSTR pszName); - + IFileDialog * this, LPCWSTR pszName); HRESULT ( STDMETHODCALLTYPE *GetFileName )( - IFileDialog * This, - /* [string][out] */ LPWSTR *pszName); - - HRESULT ( STDMETHODCALLTYPE *SetTitle )( - IFileDialog * This, - /* [string][in] */ LPCWSTR pszTitle); - + IFileDialog * this, LPWSTR *pszName); + HRESULT ( STDMETHODCALLTYPE *SetTitle )( + IFileDialog * this, LPCWSTR pszTitle); HRESULT ( STDMETHODCALLTYPE *SetOkButtonLabel )( - IFileDialog * This, - /* [string][in] */ LPCWSTR pszText); - + IFileDialog * this, LPCWSTR pszText); HRESULT ( STDMETHODCALLTYPE *SetFileNameLabel )( - IFileDialog * This, - /* [string][in] */ LPCWSTR pszLabel); - - HRESULT ( STDMETHODCALLTYPE *GetResult )( - IFileDialog * This, - /* [out] */ IShellItem **ppsi); - + IFileDialog * this, LPCWSTR pszLabel); + HRESULT ( STDMETHODCALLTYPE *GetResult )( + IFileDialog * this, IShellItem **ppsi); HRESULT ( STDMETHODCALLTYPE *AddPlace )( - IFileDialog * This, - /* [in] */ IShellItem *psi, - /* [in] */ FDAP fdap); - + IFileDialog * this, IShellItem *psi, FDAP fdap); HRESULT ( STDMETHODCALLTYPE *SetDefaultExtension )( - IFileDialog * This, - /* [string][in] */ LPCWSTR pszDefaultExtension); - - HRESULT ( STDMETHODCALLTYPE *Close )( - IFileDialog * This, - /* [in] */ HRESULT hr); - - HRESULT ( STDMETHODCALLTYPE *SetClientGuid )( - IFileDialog * This, - /* [in] */ REFGUID guid); - - HRESULT ( STDMETHODCALLTYPE *ClearClientData )( - IFileDialog * This); - + IFileDialog * this, LPCWSTR pszDefaultExtension); + HRESULT ( STDMETHODCALLTYPE *Close )( IFileDialog * this, HRESULT hr); + HRESULT ( STDMETHODCALLTYPE *SetClientGuid )( + IFileDialog * this, REFGUID guid); + HRESULT ( STDMETHODCALLTYPE *ClearClientData )( IFileDialog * this); + /* pFilter actually IShellItemFilter. But deprecated in Win7 AND we do + not use it anyways. So define as void* */ HRESULT ( STDMETHODCALLTYPE *SetFilter )( - IFileDialog * This, - /* XXX - Actually IShellItemFilter. But deprecated in Win7 AND we do - not use it anyways. So define as void* */ - /* [in] */ void *pFilter); + IFileDialog * this, void *pFilter); END_INTERFACE } IFileDialogVtbl; @@ -470,144 +383,68 @@ typedef struct IFileSaveDialogVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( - IFileSaveDialog * This, - /* [in] */ REFIID riid, - /* [annotation][iid_is][out] */ - void **ppvObject); - - ULONG ( STDMETHODCALLTYPE *AddRef )( - IFileSaveDialog * This); - - ULONG ( STDMETHODCALLTYPE *Release )( - IFileSaveDialog * This); - - /* [local] */ HRESULT ( STDMETHODCALLTYPE *Show )( - IFileSaveDialog * This, - /* [annotation][unique][in] */ - HWND hwndOwner); - - HRESULT ( STDMETHODCALLTYPE *SetFileTypes )( - IFileSaveDialog * This, - /* [in] */ UINT cFileTypes, - /* [size_is][in] */ const COMDLG_FILTERSPEC *rgFilterSpec); - - HRESULT ( STDMETHODCALLTYPE *SetFileTypeIndex )( - IFileSaveDialog * This, - /* [in] */ UINT iFileType); - + IFileSaveDialog * this, REFIID riid, void **ppvObject); + ULONG ( STDMETHODCALLTYPE *AddRef )( IFileSaveDialog * this); + ULONG ( STDMETHODCALLTYPE *Release )( IFileSaveDialog * this); + HRESULT ( STDMETHODCALLTYPE *Show )( + IFileSaveDialog * this, HWND hwndOwner); + HRESULT ( STDMETHODCALLTYPE *SetFileTypes )( IFileSaveDialog * this, + UINT cFileTypes, const COMDLG_FILTERSPEC *rgFilterSpec); + HRESULT ( STDMETHODCALLTYPE *SetFileTypeIndex )( + IFileSaveDialog * this, UINT iFileType); HRESULT ( STDMETHODCALLTYPE *GetFileTypeIndex )( - IFileSaveDialog * This, - /* [out] */ UINT *piFileType); - + IFileSaveDialog * this, UINT *piFileType); + /* Actually pfde is IFileSaveDialogEvents* */ HRESULT ( STDMETHODCALLTYPE *Advise )( - IFileSaveDialog * This, - /* XXX - Actually pfde is IFileSaveDialogEvents* but we do not use - this call and do not want to define IFileSaveDialogEvents as that - pulls in a whole bunch of other stuff. */ - /* [in] */ void *pfde, - /* [out] */ DWORD *pdwCookie); - - HRESULT ( STDMETHODCALLTYPE *Unadvise )( - IFileSaveDialog * This, - /* [in] */ DWORD dwCookie); - + IFileSaveDialog * this, void *pfde, DWORD *pdwCookie); + HRESULT ( STDMETHODCALLTYPE *Unadvise )( IFileSaveDialog * this, DWORD); HRESULT ( STDMETHODCALLTYPE *SetOptions )( - IFileSaveDialog * This, - /* [in] */ FILEOPENDIALOGOPTIONS fos); - + IFileSaveDialog * this, FILEOPENDIALOGOPTIONS fos); HRESULT ( STDMETHODCALLTYPE *GetOptions )( - IFileSaveDialog * This, - /* [out] */ FILEOPENDIALOGOPTIONS *pfos); - + IFileSaveDialog * this, FILEOPENDIALOGOPTIONS *pfos); HRESULT ( STDMETHODCALLTYPE *SetDefaultFolder )( - IFileSaveDialog * This, - /* [in] */ IShellItem *psi); - - HRESULT ( STDMETHODCALLTYPE *SetFolder )( - IFileSaveDialog * This, - /* [in] */ IShellItem *psi); - + IFileSaveDialog * this, IShellItem *psi); + HRESULT ( STDMETHODCALLTYPE *SetFolder )( + IFileSaveDialog * this, IShellItem *psi); HRESULT ( STDMETHODCALLTYPE *GetFolder )( - IFileSaveDialog * This, - /* [out] */ IShellItem **ppsi); - + IFileSaveDialog * this, IShellItem **ppsi); HRESULT ( STDMETHODCALLTYPE *GetCurrentSelection )( - IFileSaveDialog * This, - /* [out] */ IShellItem **ppsi); - + IFileSaveDialog * this, IShellItem **ppsi); HRESULT ( STDMETHODCALLTYPE *SetFileName )( - IFileSaveDialog * This, - /* [string][in] */ LPCWSTR pszName); - + IFileSaveDialog * this, LPCWSTR pszName); HRESULT ( STDMETHODCALLTYPE *GetFileName )( - IFileSaveDialog * This, - /* [string][out] */ LPWSTR *pszName); - + IFileSaveDialog * this, LPWSTR *pszName); HRESULT ( STDMETHODCALLTYPE *SetTitle )( - IFileSaveDialog * This, - /* [string][in] */ LPCWSTR pszTitle); - + IFileSaveDialog * this, LPCWSTR pszTitle); HRESULT ( STDMETHODCALLTYPE *SetOkButtonLabel )( - IFileSaveDialog * This, - /* [string][in] */ LPCWSTR pszText); - + IFileSaveDialog * this, LPCWSTR pszText); HRESULT ( STDMETHODCALLTYPE *SetFileNameLabel )( - IFileSaveDialog * This, - /* [string][in] */ LPCWSTR pszLabel); - + IFileSaveDialog * this, LPCWSTR pszLabel); HRESULT ( STDMETHODCALLTYPE *GetResult )( - IFileSaveDialog * This, - /* [out] */ IShellItem **ppsi); - + IFileSaveDialog * this, IShellItem **ppsi); HRESULT ( STDMETHODCALLTYPE *AddPlace )( - IFileSaveDialog * This, - /* [in] */ IShellItem *psi, - /* [in] */ FDAP fdap); - + IFileSaveDialog * this, IShellItem *psi, FDAP fdap); HRESULT ( STDMETHODCALLTYPE *SetDefaultExtension )( - IFileSaveDialog * This, - /* [string][in] */ LPCWSTR pszDefaultExtension); - - HRESULT ( STDMETHODCALLTYPE *Close )( - IFileSaveDialog * This, - /* [in] */ HRESULT hr); - - HRESULT ( STDMETHODCALLTYPE *SetClientGuid )( - IFileSaveDialog * This, - /* [in] */ REFGUID guid); - - HRESULT ( STDMETHODCALLTYPE *ClearClientData )( - IFileSaveDialog * This); - + IFileSaveDialog * this, LPCWSTR pszDefaultExtension); + HRESULT ( STDMETHODCALLTYPE *Close )( IFileSaveDialog * this, HRESULT hr); + HRESULT ( STDMETHODCALLTYPE *SetClientGuid )( + IFileSaveDialog * this, REFGUID guid); + HRESULT ( STDMETHODCALLTYPE *ClearClientData )( IFileSaveDialog * this); + /* pFilter Actually IShellItemFilter* */ HRESULT ( STDMETHODCALLTYPE *SetFilter )( - IFileSaveDialog * This, - /* XXX - Actually IShellItemFilter. But deprecated in Win7 AND we do - not use it anyways. So define as void* */ - /* [in] */ void *pFilter); - + IFileSaveDialog * this, void *pFilter); HRESULT ( STDMETHODCALLTYPE *SetSaveAsItem )( - IFileSaveDialog * This, - /* [in] */ IShellItem *psi); - + IFileSaveDialog * this, IShellItem *psi); HRESULT ( STDMETHODCALLTYPE *SetProperties )( - IFileSaveDialog * This, - /* [in] */ IPropertyStore *pStore); - + IFileSaveDialog * this, IPropertyStore *pStore); HRESULT ( STDMETHODCALLTYPE *SetCollectedProperties )( - IFileSaveDialog * This, - /* [in] */ IPropertyDescriptionList *pList, - /* [in] */ BOOL fAppendDefault); - + IFileSaveDialog * this, IPropertyDescriptionList *pList, + BOOL fAppendDefault); HRESULT ( STDMETHODCALLTYPE *GetProperties )( - IFileSaveDialog * This, - /* [out] */ IPropertyStore **ppStore); - + IFileSaveDialog * this, IPropertyStore **ppStore); HRESULT ( STDMETHODCALLTYPE *ApplyProperties )( - IFileSaveDialog * This, - /* [in] */ IShellItem *psi, - /* [in] */ IPropertyStore *pStore, - /* [unique][in] */ HWND hwnd, - /* [unique][in] */ IFileOperationProgressSink *pSink); + IFileSaveDialog * this, IShellItem *psi, IPropertyStore *pStore, + HWND hwnd, IFileOperationProgressSink *pSink); END_INTERFACE @@ -622,128 +459,61 @@ typedef struct IFileOpenDialogVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( - IFileOpenDialog * This, - /* [in] */ REFIID riid, - /* [annotation][iid_is][out] */ - void **ppvObject); - - ULONG ( STDMETHODCALLTYPE *AddRef )( - IFileOpenDialog * This); - - ULONG ( STDMETHODCALLTYPE *Release )( - IFileOpenDialog * This); - - /* [local] */ HRESULT ( STDMETHODCALLTYPE *Show )( - IFileOpenDialog * This, - /* [annotation][unique][in] */ - HWND hwndOwner); - - HRESULT ( STDMETHODCALLTYPE *SetFileTypes )( - IFileOpenDialog * This, - /* [in] */ UINT cFileTypes, - /* [size_is][in] */ const COMDLG_FILTERSPEC *rgFilterSpec); - + IFileOpenDialog * this, REFIID riid, void **ppvObject); + ULONG ( STDMETHODCALLTYPE *AddRef )( IFileOpenDialog * this); + ULONG ( STDMETHODCALLTYPE *Release )( IFileOpenDialog * this); + HRESULT ( STDMETHODCALLTYPE *Show )( IFileOpenDialog * this, HWND); + HRESULT ( STDMETHODCALLTYPE *SetFileTypes )( IFileOpenDialog * this, + UINT cFileTypes, const COMDLG_FILTERSPEC *rgFilterSpec); HRESULT ( STDMETHODCALLTYPE *SetFileTypeIndex )( - IFileOpenDialog * This, - /* [in] */ UINT iFileType); - + IFileOpenDialog * this, UINT iFileType); HRESULT ( STDMETHODCALLTYPE *GetFileTypeIndex )( - IFileOpenDialog * This, - /* [out] */ UINT *piFileType); - + IFileOpenDialog * this, UINT *piFileType); + /* Actually pfde is IFileDialogEvents* */ HRESULT ( STDMETHODCALLTYPE *Advise )( - IFileOpenDialog * This, - /* XXX - Actually pfde is IFileDialogEvents* but we do not use - this call and do not want to define IFileDialogEvents as that - pulls in a whole bunch of other stuff. */ - /* [in] */ void *pfde, - /* [out] */ DWORD *pdwCookie); - - HRESULT ( STDMETHODCALLTYPE *Unadvise )( - IFileOpenDialog * This, - /* [in] */ DWORD dwCookie); - + IFileOpenDialog * this, void *pfde, DWORD *pdwCookie); + HRESULT ( STDMETHODCALLTYPE *Unadvise )( IFileOpenDialog * this, DWORD); HRESULT ( STDMETHODCALLTYPE *SetOptions )( - IFileOpenDialog * This, - /* [in] */ FILEOPENDIALOGOPTIONS fos); - + IFileOpenDialog * this, FILEOPENDIALOGOPTIONS fos); HRESULT ( STDMETHODCALLTYPE *GetOptions )( - IFileOpenDialog * This, - /* [out] */ FILEOPENDIALOGOPTIONS *pfos); - + IFileOpenDialog * this, FILEOPENDIALOGOPTIONS *pfos); HRESULT ( STDMETHODCALLTYPE *SetDefaultFolder )( - IFileOpenDialog * This, - /* [in] */ IShellItem *psi); - + IFileOpenDialog * this, IShellItem *psi); HRESULT ( STDMETHODCALLTYPE *SetFolder )( - IFileOpenDialog * This, - /* [in] */ IShellItem *psi); - + IFileOpenDialog * this, IShellItem *psi); HRESULT ( STDMETHODCALLTYPE *GetFolder )( - IFileOpenDialog * This, - /* [out] */ IShellItem **ppsi); - + IFileOpenDialog * this, IShellItem **ppsi); HRESULT ( STDMETHODCALLTYPE *GetCurrentSelection )( - IFileOpenDialog * This, - /* [out] */ IShellItem **ppsi); - + IFileOpenDialog * this, IShellItem **ppsi); HRESULT ( STDMETHODCALLTYPE *SetFileName )( - IFileOpenDialog * This, - /* [string][in] */ LPCWSTR pszName); - + IFileOpenDialog * this, LPCWSTR pszName); HRESULT ( STDMETHODCALLTYPE *GetFileName )( - IFileOpenDialog * This, - /* [string][out] */ LPWSTR *pszName); - + IFileOpenDialog * this, LPWSTR *pszName); HRESULT ( STDMETHODCALLTYPE *SetTitle )( - IFileOpenDialog * This, - /* [string][in] */ LPCWSTR pszTitle); - + IFileOpenDialog * this, LPCWSTR pszTitle); HRESULT ( STDMETHODCALLTYPE *SetOkButtonLabel )( - IFileOpenDialog * This, - /* [string][in] */ LPCWSTR pszText); - + IFileOpenDialog * this, LPCWSTR pszText); HRESULT ( STDMETHODCALLTYPE *SetFileNameLabel )( - IFileOpenDialog * This, - /* [string][in] */ LPCWSTR pszLabel); - + IFileOpenDialog * this, LPCWSTR pszLabel); HRESULT ( STDMETHODCALLTYPE *GetResult )( - IFileOpenDialog * This, - /* [out] */ IShellItem **ppsi); - + IFileOpenDialog * this, IShellItem **ppsi); HRESULT ( STDMETHODCALLTYPE *AddPlace )( - IFileOpenDialog * This, - /* [in] */ IShellItem *psi, - /* [in] */ FDAP fdap); - + IFileOpenDialog * this, IShellItem *psi, FDAP fdap); HRESULT ( STDMETHODCALLTYPE *SetDefaultExtension )( - IFileOpenDialog * This, - /* [string][in] */ LPCWSTR pszDefaultExtension); - - HRESULT ( STDMETHODCALLTYPE *Close )( - IFileOpenDialog * This, - /* [in] */ HRESULT hr); - + IFileOpenDialog * this, LPCWSTR pszDefaultExtension); + HRESULT ( STDMETHODCALLTYPE *Close )( IFileOpenDialog * this, HRESULT hr); HRESULT ( STDMETHODCALLTYPE *SetClientGuid )( - IFileOpenDialog * This, - /* [in] */ REFGUID guid); - + IFileOpenDialog * this, REFGUID guid); HRESULT ( STDMETHODCALLTYPE *ClearClientData )( - IFileOpenDialog * This); - + IFileOpenDialog * this); HRESULT ( STDMETHODCALLTYPE *SetFilter )( - IFileOpenDialog * This, - /* XXX - Actually IShellItemFilter. But deprecated in Win7 AND we do - not use it anyways. So define as void* */ - /* [in] */ void *pFilter); - + IFileOpenDialog * this, + /* pFilter is actually IShellItemFilter */ + void *pFilter); HRESULT ( STDMETHODCALLTYPE *GetResults )( - IFileOpenDialog * This, - /* [out] */ IShellItemArray **ppenum); - + IFileOpenDialog * this, IShellItemArray **ppenum); HRESULT ( STDMETHODCALLTYPE *GetSelectedItems )( - IFileOpenDialog * This, - /* [out] */ IShellItemArray **ppsai); + IFileOpenDialog * this, IShellItemArray **ppsai); END_INTERFACE } IFileOpenDialogVtbl; @@ -1157,7 +927,7 @@ Tk_GetOpenFileObjCmd( int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - return GetFileName(clientData, interp, objc, objv, 1); + return GetFileName(clientData, interp, objc, objv, OFN_FILE_OPEN); } /* @@ -1184,7 +954,7 @@ Tk_GetSaveFileObjCmd( int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - return GetFileName(clientData, interp, objc, objv, 0); + return GetFileName(clientData, interp, objc, objv, OFN_FILE_SAVE); } /* @@ -1230,7 +1000,7 @@ ParseOFNOptions( Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[], /* Argument objects. */ - int open, /* 1 for Open, 0 for Save */ + enum OFNOper oper, /* 1 for Open, 0 for Save */ OFNOpts *optsPtr) /* Output, uninitialized on entry */ { int i; @@ -1238,6 +1008,7 @@ ParseOFNOptions( enum options { FILE_DEFAULT, FILE_TYPES, FILE_INITDIR, FILE_INITFILE, FILE_PARENT, FILE_TITLE, FILE_TYPEVARIABLE, FILE_MULTIPLE, FILE_CONFIRMOW, + FILE_MUSTEXIST, FILE_UNDOCUMENTED_XP_STYLE /* XXX - force XP - style dialogs */ }; struct Options { @@ -1268,8 +1039,23 @@ ParseOFNOptions( {"-xpstyle", FILE_UNDOCUMENTED_XP_STYLE}, /* XXX */ {NULL, FILE_DEFAULT/*ignored*/ } }; - const struct Options *const options = open ? openOptions : saveOptions; + static const struct Options dirOptions[] = { + {"-initialdir", FILE_INITDIR}, + {"-mustexist", FILE_MUSTEXIST}, + {"-parent", FILE_PARENT}, + {"-title", FILE_TITLE}, + {"-xpstyle", FILE_UNDOCUMENTED_XP_STYLE}, /* XXX */ + {NULL, FILE_DEFAULT/*ignored*/ } + }; + + const struct Options *options = NULL; + switch (oper) { + case OFN_FILE_SAVE: options = saveOptions; break; + case OFN_DIR_CHOOSE: options = dirOptions; break; + case OFN_FILE_OPEN: options = openOptions; break; + } + ZeroMemory(optsPtr, sizeof(*optsPtr)); optsPtr->tkwin = clientData; optsPtr->confirmOverwrite = 1; /* By default we ask for confirmation */ @@ -1342,6 +1128,12 @@ ParseOFNOptions( return TCL_ERROR; } break; + case FILE_MUSTEXIST: + if (Tcl_GetBooleanFromObj(interp, valuePtr, + &optsPtr->mustExist) != TCL_OK) { + return TCL_ERROR; + } + break; case FILE_UNDOCUMENTED_XP_STYLE: if (Tcl_GetBooleanFromObj(interp, valuePtr, &optsPtr->forceXPStyle) != TCL_OK) { @@ -1429,7 +1221,8 @@ static int VistaFileDialogsAvailable() * Dialogs is displayed *---------------------------------------------------------------------- */ -static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) +static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, + enum OFNOper oper) { HRESULT hr; HWND hWnd; @@ -1441,10 +1234,12 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) Tcl_Obj *resultObj = NULL; ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + int oldMode; if (tsdPtr->newFileDialogsState != FDLG_STATE_USE_NEW) { - /* Should not be called in this condition but be nice about it */ - return GetFileNameXP(interp, optsPtr, open); + /* XXX - should be an assert but Tcl does not seem to have one? */ + Tcl_SetResult(interp, "Internal error: GetFileNameVista: IFileDialog API not available", TCL_STATIC); + return TCL_ERROR; } /* @@ -1470,7 +1265,7 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) * resources that need to be released/freed. */ - if (open) + if (oper == OFN_FILE_OPEN || oper == OFN_DIR_CHOOSE) hr = CoCreateInstance(&CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, &IID_IFileOpenDialog, &fdlgIf); else @@ -1480,15 +1275,6 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) if (FAILED(hr)) goto vamoose; - if (filterPtr) { - hr = fdlgIf->lpVtbl->SetFileTypes(fdlgIf, nfilters, filterPtr); - if (FAILED(hr)) - goto vamoose; - hr = fdlgIf->lpVtbl->SetFileTypeIndex(fdlgIf, defaultFilterIndex); - if (FAILED(hr)) - goto vamoose; - } - /* * Get current settings first because we want to preserve existing * settings like whether to show hidden files etc. based on the @@ -1498,6 +1284,16 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) if (FAILED(hr)) goto vamoose; + if (filterPtr) { + flags |= FOS_STRICTFILETYPES; /* XXX - does this match old behaviour? */ + hr = fdlgIf->lpVtbl->SetFileTypes(fdlgIf, nfilters, filterPtr); + if (FAILED(hr)) + goto vamoose; + hr = fdlgIf->lpVtbl->SetFileTypeIndex(fdlgIf, defaultFilterIndex); + if (FAILED(hr)) + goto vamoose; + } + /* Flags are equivalent to those we used in the older API */ /* @@ -1511,6 +1307,13 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) FOS_PATHMUSTEXIST; /* The *directory* path must exist */ + if (oper == OFN_DIR_CHOOSE) { + flags |= FOS_PICKFOLDERS; + if (optsPtr->mustExist) + flags |= FOS_FILEMUSTEXIST; /* XXX - check working */ + } else + flags &= ~ FOS_PICKFOLDERS; + if (optsPtr->multi) flags |= FOS_ALLOWMULTISELECT; else @@ -1562,9 +1365,12 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open) Tcl_DStringFree(&dirString); } + oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); hr = fdlgIf->lpVtbl->Show(fdlgIf, hWnd); + Tcl_SetServiceMode(oldMode); + if (SUCCEEDED(hr)) { - if (open && optsPtr->multi) { + if ((oper == OFN_FILE_OPEN) && optsPtr->multi) { IShellItemArray *multiIf; DWORD dw, count; IFileOpenDialog *fodIf = (IFileOpenDialog *) fdlgIf; @@ -1665,7 +1471,7 @@ vamoose: /* (hr != 0) => error */ * See user documentation. *---------------------------------------------------------------------- */ -static int GetFileNameXP(Tcl_Interp *interp, OFNOpts *optsPtr, int open) +static int GetFileNameXP(Tcl_Interp *interp, OFNOpts *optsPtr, enum OFNOper oper) { OPENFILENAME ofn; OFNData ofnData; @@ -1703,7 +1509,7 @@ static int GetFileNameXP(Tcl_Interp *interp, OFNOpts *optsPtr, int open) ofn.lpfnHook = (LPOFNHOOKPROC) OFNHookProc; ofn.lCustData = (LPARAM) &ofnData; - if (open != 0) { + if (oper != OFN_FILE_SAVE) { ofn.Flags |= OFN_FILEMUSTEXIST; } else if (optsPtr->confirmOverwrite) { ofn.Flags |= OFN_OVERWRITEPROMPT; @@ -1770,7 +1576,7 @@ static int GetFileNameXP(Tcl_Interp *interp, OFNOpts *optsPtr, int open) */ oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); - if (open != 0) { + if (oper != OFN_FILE_SAVE) { winCode = GetOpenFileName(&ofn); } else { winCode = GetSaveFileName(&ofn); @@ -1954,21 +1760,20 @@ GetFileName( Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[], /* Argument objects. */ - int open) /* 1 to call GetOpenFileName(), 0 to call + enum OFNOper oper) /* 1 to call GetOpenFileName(), 0 to call * GetSaveFileName(). */ { OFNOpts ofnOpts; int result; - /* Parse the arguments. */ - result = ParseOFNOptions(clientData, interp, objc, objv, open, &ofnOpts); + result = ParseOFNOptions(clientData, interp, objc, objv, oper, &ofnOpts); if (result != TCL_OK) return result; if (VistaFileDialogsAvailable() && ! ofnOpts.forceXPStyle) - result = GetFileNameVista(interp, &ofnOpts, open); + result = GetFileNameVista(interp, &ofnOpts, oper); else - result = GetFileNameXP(interp, &ofnOpts, open); + result = GetFileNameXP(interp, &ofnOpts, oper); CleanupOFNOptions(&ofnOpts); return result; @@ -2511,102 +2316,60 @@ Tk_ChooseDirectoryObjCmd( Tcl_Obj *const objv[]) /* Argument objects. */ { TCHAR path[MAX_PATH]; - int oldMode, result = TCL_ERROR, i; + int oldMode, result; LPCITEMIDLIST pidl; /* Returned by browser */ BROWSEINFO bInfo; /* Used by browser */ ChooseDir cdCBData; /* Structure to pass back and forth */ LPMALLOC pMalloc; /* Used by shell */ - Tk_Window tkwin = clientData; HWND hWnd; - const char *utfTitle = NULL;/* Title for window */ TCHAR saveDir[MAX_PATH]; Tcl_DString titleString; /* Title */ - Tcl_DString initDirString; /* Initial directory */ Tcl_DString tempString; /* temporary */ Tcl_Obj *objPtr; - static const char *const optionStrings[] = { - "-initialdir", "-mustexist", "-parent", "-title", NULL - }; - enum options { - DIR_INITIAL, DIR_EXIST, DIR_PARENT, FILE_TITLE - }; + OFNOpts ofnOpts; + const char *utfDir; - /* - * Initialize - */ + result = ParseOFNOptions(clientData, interp, objc, objv, + OFN_DIR_CHOOSE, &ofnOpts); + if (result != TCL_OK) + return result; + + /* Use new dialogs if available */ + if (VistaFileDialogsAvailable() && ! ofnOpts.forceXPStyle) { + result = GetFileNameVista(interp, &ofnOpts, OFN_DIR_CHOOSE); + CleanupOFNOptions(&ofnOpts); + return result; + } + + /* Older dialogs */ path[0] = '\0'; ZeroMemory(&cdCBData, sizeof(ChooseDir)); cdCBData.interp = interp; + cdCBData.mustExist = ofnOpts.mustExist; - /* - * Process the command line options - */ - - for (i = 1; i < objc; i += 2) { - int index; - const char *string; + utfDir = Tcl_DStringValue(&ofnOpts.utfDirString); + if (utfDir[0] != '\0') { const TCHAR *uniStr; - Tcl_Obj *optionPtr, *valuePtr; - optionPtr = objv[i]; - valuePtr = objv[i + 1]; + Tcl_WinUtfToTChar(Tcl_DStringValue(&ofnOpts.utfDirString), -1, + &tempString); + uniStr = (TCHAR *) Tcl_DStringValue(&tempString); - if (Tcl_GetIndexFromObjStruct(interp, optionPtr, optionStrings, - sizeof(char *), "option", 0, &index) != TCL_OK) { - goto cleanup; - } - if (i + 1 == objc) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "value for \"%s\" missing", Tcl_GetString(optionPtr))); - Tcl_SetErrorCode(interp, "TK", "DIRDIALOG", "VALUE", NULL); - goto cleanup; - } + /* Convert possible relative path to full path to keep dialog happy. */ - string = Tcl_GetString(valuePtr); - switch ((enum options) index) { - case DIR_INITIAL: - if (Tcl_TranslateFileName(interp,string,&initDirString) == NULL) { - goto cleanup; - } - Tcl_WinUtfToTChar(Tcl_DStringValue(&initDirString), -1, - &tempString); - uniStr = (TCHAR *) Tcl_DStringValue(&tempString); - - /* - * Convert possible relative path to full path to keep dialog - * happy. - */ - - GetFullPathName(uniStr, MAX_PATH, saveDir, NULL); - _tcsncpy(cdCBData.initDir, saveDir, MAX_PATH); - Tcl_DStringFree(&initDirString); - Tcl_DStringFree(&tempString); - break; - case DIR_EXIST: - if (Tcl_GetBooleanFromObj(interp, valuePtr, - &cdCBData.mustExist) != TCL_OK) { - goto cleanup; - } - break; - case DIR_PARENT: - tkwin = Tk_NameToWindow(interp, string, tkwin); - if (tkwin == NULL) { - goto cleanup; - } - break; - case FILE_TITLE: - utfTitle = string; - break; - } + GetFullPathName(uniStr, MAX_PATH, saveDir, NULL); + _tcsncpy(cdCBData.initDir, saveDir, MAX_PATH); } + /* XXX - rest of this (original) code has no error checks at all. */ + /* * Get ready to call the browser */ - Tk_MakeWindowExist(tkwin); - hWnd = Tk_GetHWND(Tk_WindowId(tkwin)); + Tk_MakeWindowExist(ofnOpts.tkwin); + hWnd = Tk_GetHWND(Tk_WindowId(ofnOpts.tkwin)); /* * Setup the parameters used by SHBrowseForFolder @@ -2620,8 +2383,8 @@ Tk_ChooseDirectoryObjCmd( } bInfo.lParam = (LPARAM) &cdCBData; - if (utfTitle != NULL) { - Tcl_WinUtfToTChar(utfTitle, -1, &titleString); + if (ofnOpts.titleObj != NULL) { + Tcl_WinUtfToTChar(Tcl_GetString(ofnOpts.titleObj), -1, &titleString); bInfo.lpszTitle = (LPTSTR) Tcl_DStringValue(&titleString); } else { bInfo.lpszTitle = TEXT("Please choose a directory, then select OK."); @@ -2659,6 +2422,10 @@ Tk_ChooseDirectoryObjCmd( oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); GetCurrentDirectory(MAX_PATH, saveDir); if (SHGetMalloc(&pMalloc) == NOERROR) { + /* + * XXX - MSDN says CoInitialize must have been called before + * SHBrowseForFolder can be used but don't see that called anywhere. + */ pidl = SHBrowseForFolder(&bInfo); /* @@ -2710,14 +2477,8 @@ Tk_ChooseDirectoryObjCmd( Tcl_DStringFree(&ds); } - result = TCL_OK; - - if (utfTitle != NULL) { - Tcl_DStringFree(&titleString); - } - - cleanup: - return result; + CleanupOFNOptions(&ofnOpts); + return TCL_OK; } /* -- cgit v0.12 From defa6162f12bf44d8df24a341dd4727e3a23e3d0 Mon Sep 17 00:00:00 2001 From: ashok Date: Sat, 20 Sep 2014 02:56:06 +0000 Subject: Make -xpstyle a hidden option --- win/tkWinDialog.c | 59 +++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/win/tkWinDialog.c b/win/tkWinDialog.c index 6b7035d..3130cf2 100644 --- a/win/tkWinDialog.c +++ b/win/tkWinDialog.c @@ -1024,7 +1024,6 @@ ParseOFNOptions( {"-parent", FILE_PARENT}, {"-title", FILE_TITLE}, {"-typevariable", FILE_TYPEVARIABLE}, - {"-xpstyle", FILE_UNDOCUMENTED_XP_STYLE}, /* XXX */ {NULL, FILE_DEFAULT/*ignored*/ } }; static const struct Options openOptions[] = { @@ -1036,7 +1035,6 @@ ParseOFNOptions( {"-parent", FILE_PARENT}, {"-title", FILE_TITLE}, {"-typevariable", FILE_TYPEVARIABLE}, - {"-xpstyle", FILE_UNDOCUMENTED_XP_STYLE}, /* XXX */ {NULL, FILE_DEFAULT/*ignored*/ } }; static const struct Options dirOptions[] = { @@ -1044,7 +1042,6 @@ ParseOFNOptions( {"-mustexist", FILE_MUSTEXIST}, {"-parent", FILE_PARENT}, {"-title", FILE_TITLE}, - {"-xpstyle", FILE_UNDOCUMENTED_XP_STYLE}, /* XXX */ {NULL, FILE_DEFAULT/*ignored*/ } }; @@ -1057,6 +1054,7 @@ ParseOFNOptions( } ZeroMemory(optsPtr, sizeof(*optsPtr)); + // optsPtr->forceXPStyle = 1; optsPtr->tkwin = clientData; optsPtr->confirmOverwrite = 1; /* By default we ask for confirmation */ Tcl_DStringInit(&optsPtr->utfDirString); @@ -1069,12 +1067,23 @@ ParseOFNOptions( if (Tcl_GetIndexFromObjStruct(interp, objv[i], options, sizeof(struct Options), "option", 0, &index) != TCL_OK) { - goto end; + /* + * XXX -xpstyle is explicitly checked for as it is undocumented + * and we do not want it to show in option error messages. + */ + if (strcmp(Tcl_GetString(objv[i]), "-xpstyle")) + goto error_return; + if (Tcl_GetBooleanFromObj(interp, valuePtr, + &optsPtr->forceXPStyle) != TCL_OK) + goto error_return; + + continue; + } else if (i + 1 == objc) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "value for \"%s\" missing", options[index].name)); Tcl_SetErrorCode(interp, "TK", "FILEDIALOG", "VALUE", NULL); - goto end; + goto error_return; } string = Tcl_GetString(valuePtr); @@ -1088,14 +1097,12 @@ ParseOFNOptions( case FILE_INITDIR: Tcl_DStringFree(&optsPtr->utfDirString); if (Tcl_TranslateFileName(interp, string, - &optsPtr->utfDirString) == NULL) { - goto end; - } + &optsPtr->utfDirString) == NULL) + goto error_return; break; case FILE_INITFILE: - if (Tcl_TranslateFileName(interp, string, &ds) == NULL) { - goto end; - } + if (Tcl_TranslateFileName(interp, string, &ds) == NULL) + goto error_return; Tcl_UtfToExternal(NULL, TkWinGetUnicodeEncoding(), Tcl_DStringValue(&ds), Tcl_DStringLength(&ds), 0, NULL, (char *) &optsPtr->file[0], sizeof(optsPtr->file), @@ -1105,9 +1112,8 @@ ParseOFNOptions( case FILE_PARENT: /* XXX - check */ optsPtr->tkwin = Tk_NameToWindow(interp, string, clientData); - if (optsPtr->tkwin == NULL) { - goto end; - } + if (optsPtr->tkwin == NULL) + goto error_return; break; case FILE_TITLE: optsPtr->titleObj = valuePtr; @@ -1118,34 +1124,27 @@ ParseOFNOptions( NULL, TCL_GLOBAL_ONLY); break; case FILE_MULTIPLE: - if (Tcl_GetBooleanFromObj(interp, valuePtr, &optsPtr->multi) != TCL_OK) { - return TCL_ERROR; - } + if (Tcl_GetBooleanFromObj(interp, valuePtr, + &optsPtr->multi) != TCL_OK) + goto error_return; break; case FILE_CONFIRMOW: if (Tcl_GetBooleanFromObj(interp, valuePtr, - &optsPtr->confirmOverwrite) != TCL_OK) { - return TCL_ERROR; - } + &optsPtr->confirmOverwrite) != TCL_OK) + goto error_return; break; case FILE_MUSTEXIST: if (Tcl_GetBooleanFromObj(interp, valuePtr, - &optsPtr->mustExist) != TCL_OK) { - return TCL_ERROR; - } - break; - case FILE_UNDOCUMENTED_XP_STYLE: - if (Tcl_GetBooleanFromObj(interp, valuePtr, - &optsPtr->forceXPStyle) != TCL_OK) { - return TCL_ERROR; - } + &optsPtr->mustExist) != TCL_OK) + goto error_return; break; } } return TCL_OK; -end: /* interp should already hold error */ +error_return: /* interp should already hold error */ + /* On error, we need to clean up anything we might have allocated */ CleanupOFNOptions(optsPtr); return TCL_ERROR; } -- cgit v0.12 From a0426df76aa736ba2d618441232d72e4ba38a380 Mon Sep 17 00:00:00 2001 From: ashok Date: Sat, 20 Sep 2014 03:17:35 +0000 Subject: Convert native paths returned from file dialogs to Tcl canonical paths. --- tests/winDialog.test | 56 ++++++++++++++++++++++++++++++++--------- win/tkWinDialog.c | 14 ++++++++--- win/tkWinTest.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 119 insertions(+), 21 deletions(-) diff --git a/tests/winDialog.test b/tests/winDialog.test index 8aa9ac3..357349e 100644 --- a/tests/winDialog.test +++ b/tests/winDialog.test @@ -22,11 +22,30 @@ testConstraint english [expr { && (([testwinlocale] & 0xff) == 9) }] -proc start {arg} { +proc start {widgetcommand args} { set ::tk_dialog 0 set ::iter_after 0 - after 1 $arg + # On newer versions of Windows, we need to find the dialog window + # based on the title + if {[llength $args]} { + set ::dialogtitle [lindex $args 0] + set ::dialogclass "#32770" + if {$::dialogtitle eq ""} { + switch $widgetcommand { + tk_getOpenFile { + set ::dialogtitle Open + } + tk_getSaveFile { + set ::dialogtitle "Save As" + } + tk_chooseDirectory { + set ::dialogtitle "Select Folder" + } + } + } + } + after 1 $widgetcommand } proc then {cmd} { @@ -34,19 +53,32 @@ proc then {cmd} { set ::dialogresult {} set ::testfont {} - afterbody + after 100 afterbody vwait ::dialogresult return $::dialogresult } proc afterbody {} { - if {$::tk_dialog == 0} { - if {[incr ::iter_after] > 30} { - set ::dialogresult ">30 iterations waiting on tk_dialog" + # On Vista and later, using the new file dialogs we have to find + # the window using its title as tk_dialog will not be set at the C level + if {$::dialogtitle ne "" && [string match 6.* $::tcl_platform(osVersion)]} { + if {[catch {testfindwindow "" $::dialogclass} ::tk_dialog]} { + if {[incr ::iter_after] > 10} { + set ::dialogresult ">30 iterations waiting on tk_dialog" + return + } + after 150 {afterbody} + return + } + } else { + if {$::tk_dialog == 0} { + if {[incr ::iter_after] > 30} { + set ::dialogresult ">30 iterations waiting on tk_dialog" + return + } + after 150 {afterbody} return } - after 150 {afterbody} - return } uplevel #0 {set dialogresult [eval $command]} } @@ -205,7 +237,7 @@ test winDialog-5.2 {GetFileName: one argument} -constraints { test winDialog-5.3 {GetFileName: many arguments} -constraints { nt testwinevent } -body { - start {tk_getOpenFile -initialdir c:/ -parent . -title test -initialfile foo} + start {tk_getOpenFile -initialdir c:/ -parent . -title test -initialfile foo} test then { Click cancel } @@ -218,7 +250,7 @@ test winDialog-5.4 {GetFileName: Tcl_GetIndexFromObj() != TCL_OK} -constraints { test winDialog-5.5 {GetFileName: Tcl_GetIndexFromObj() == TCL_OK} -constraints { nt testwinevent } -body { - start {tk_getOpenFile -title bar} + start {tk_getOpenFile -title bar} bar then { Click cancel } @@ -235,10 +267,10 @@ test winDialog-5.7 {GetFileName: extension begins with .} -constraints { # string++; # } - start {set x [tk_getSaveFile -defaultextension .foo -title Save]} + start {set x [tk_getSaveFile -defaultextension .foo -title Save]} Save set msg {} then { - if {[catch {SetText 0x47C bar} msg]} { + if {[catch {SetText 0x3e9 bar} msg]} { Click cancel } else { Click ok diff --git a/win/tkWinDialog.c b/win/tkWinDialog.c index 3130cf2..67f0df4 100644 --- a/win/tkWinDialog.c +++ b/win/tkWinDialog.c @@ -1387,8 +1387,13 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, hr = itemIf->lpVtbl->GetDisplayName(itemIf, SIGDN_FILESYSPATH, &wstr); if (SUCCEEDED(hr)) { - Tcl_ListObjAppendElement(interp, multiObj, - Tcl_NewUnicodeObj(wstr, -1)); + Tcl_DString fnds; + ConvertExternalFilename(wstr, &fnds); + CoTaskMemFree(wstr); + Tcl_ListObjAppendElement( + interp, multiObj, + Tcl_NewStringObj(Tcl_DStringValue(&fnds), + Tcl_DStringLength(&fnds))); } itemIf->lpVtbl->Release(itemIf); if (FAILED(hr)) @@ -1408,7 +1413,10 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, hr = resultIf->lpVtbl->GetDisplayName(resultIf, SIGDN_FILESYSPATH, &wstr); if (SUCCEEDED(hr)) { - resultObj = Tcl_NewUnicodeObj(wstr, -1); + Tcl_DString fnds; + ConvertExternalFilename(wstr, &fnds); + resultObj = Tcl_NewStringObj(Tcl_DStringValue(&fnds), + Tcl_DStringLength(&fnds)); CoTaskMemFree(wstr); } resultIf->lpVtbl->Release(resultIf); diff --git a/win/tkWinTest.c b/win/tkWinTest.c index 9fa956c..2c38d71 100644 --- a/win/tkWinTest.c +++ b/win/tkWinTest.c @@ -79,6 +79,42 @@ TkplatformtestInit( return TCL_OK; } +struct TestFindControlState { + int id; + HWND control; +}; + +/* Callback for window enumeration - used for TestFindControl */ +BOOL CALLBACK TestFindControlCallback( + HWND hwnd, + LPARAM lParam +) +{ + struct TestFindControlState *fcsPtr = (struct TestFindControlState *)lParam; + fcsPtr->control = GetDlgItem(hwnd, fcsPtr->id); + /* If we have found the control, return FALSE to stop the enumeration */ + return fcsPtr->control == NULL ? TRUE : FALSE; +} + +/* + * Finds the descendent control window with the specified ID and returns + * its HWND. + */ +HWND TestFindControl(HWND root, int id) +{ + struct TestFindControlState fcs; + + fcs.control = GetDlgItem(root, id); + if (fcs.control == NULL) { + /* Control is not a direct child. Look in descendents */ + fcs.id = id; + fcs.control = NULL; + EnumChildWindows(root, TestFindControlCallback, (LPARAM) &fcs); + } + return fcs.control; +} + + /* *---------------------------------------------------------------------- * @@ -244,11 +280,13 @@ TestwineventObjCmd( { HWND hwnd = 0; HWND child = 0; + HWND control; int id; char *rest; UINT message; WPARAM wParam; LPARAM lParam; + LRESULT result; static const TkStateMap messageMap[] = { {WM_LBUTTONDOWN, "WM_LBUTTONDOWN"}, {WM_LBUTTONUP, "WM_LBUTTONUP"}, @@ -302,6 +340,7 @@ TestwineventObjCmd( return TCL_ERROR; } } + message = TkFindStateNum(NULL, NULL, messageMap, Tcl_GetString(objv[3])); wParam = 0; lParam = 0; @@ -318,7 +357,19 @@ TestwineventObjCmd( Tcl_DString ds; char buf[256]; +#if 0 GetDlgItemTextA(hwnd, id, buf, 256); +#else + control = TestFindControl(hwnd, id); + if (control == NULL) { + Tcl_SetObjResult(interp, + Tcl_ObjPrintf("Could not find control with id %d", id)); + return TCL_ERROR; + } + buf[0] = 0; + SendMessageA(control, WM_GETTEXT, (WPARAM)sizeof(buf), + (LPARAM) buf); +#endif Tcl_ExternalToUtfDString(NULL, buf, -1, &ds); Tcl_AppendResult(interp, Tcl_DStringValue(&ds), NULL); Tcl_DStringFree(&ds); @@ -326,15 +377,21 @@ TestwineventObjCmd( } case WM_SETTEXT: { Tcl_DString ds; - BOOL result; + control = TestFindControl(hwnd, id); + if (control == NULL) { + Tcl_SetObjResult(interp, + Tcl_ObjPrintf("Could not find control with id %d", id)); + return TCL_ERROR; + } Tcl_UtfToExternalDString(NULL, Tcl_GetString(objv[4]), -1, &ds); - result = SetDlgItemTextA(hwnd, id, Tcl_DStringValue(&ds)); + result = SendMessageA(control, WM_SETTEXT, 0, + (LPARAM) Tcl_DStringValue(&ds)); Tcl_DStringFree(&ds); if (result == 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("failed to send text to dialog: ", -1)); - AppendSystemError(interp, GetLastError()); - return TCL_ERROR; + Tcl_SetObjResult(interp, Tcl_NewStringObj("failed to send text to dialog: ", -1)); + AppendSystemError(interp, GetLastError()); + return TCL_ERROR; } break; } @@ -395,7 +452,8 @@ TestfindwindowObjCmd( if (objc == 3) { class = Tcl_WinUtfToTChar(Tcl_GetString(objv[2]), -1, &classString); } - + if (title[0] == 0) + title = NULL; hwnd = FindWindow(class, title); if (hwnd == NULL) { -- cgit v0.12 From c274f24adf15e98a9fbf65571984e7b0c30c40da Mon Sep 17 00:00:00 2001 From: ashok Date: Sat, 20 Sep 2014 12:30:12 +0000 Subject: Make findwindow more robust by ensuring it is a window belonging to the same process. --- win/tkWinTest.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/win/tkWinTest.c b/win/tkWinTest.c index 2c38d71..8a92f5a 100644 --- a/win/tkWinTest.c +++ b/win/tkWinTest.c @@ -439,6 +439,7 @@ TestfindwindowObjCmd( Tcl_DString titleString, classString; HWND hwnd = NULL; int r = TCL_OK; + DWORD myPid; Tcl_DStringInit(&classString); Tcl_DStringInit(&titleString); @@ -454,7 +455,28 @@ TestfindwindowObjCmd( } if (title[0] == 0) title = NULL; +#if 0 hwnd = FindWindow(class, title); +#else + /* We want find a window the belongs to us and not some other process */ + hwnd = NULL; + myPid = GetCurrentProcessId(); + while (1) { + DWORD pid, tid; + hwnd = FindWindowEx(NULL, hwnd, class, title); + if (hwnd == NULL) + break; + tid = GetWindowThreadProcessId(hwnd, &pid); + if (tid == 0) { + /* Window has gone */ + hwnd = NULL; + break; + } + if (pid == myPid) + break; /* Found it */ + } + +#endif if (hwnd == NULL) { Tcl_SetObjResult(interp, Tcl_NewStringObj("failed to find window: ", -1)); -- cgit v0.12 From be417f72bc54b109f4c8d70e4935478b6e410fab Mon Sep 17 00:00:00 2001 From: ashok Date: Sat, 20 Sep 2014 12:31:24 +0000 Subject: Update test suite for compatibility with new Vista file dialogs. Some tests still fail pending what we decide about behaviour when -initialdir is not specified. --- tests/winDialog.test | 151 ++++++++++++++++++++++++++++----------------------- 1 file changed, 83 insertions(+), 68 deletions(-) diff --git a/tests/winDialog.test b/tests/winDialog.test index 357349e..f21fa18 100644 --- a/tests/winDialog.test +++ b/tests/winDialog.test @@ -22,30 +22,17 @@ testConstraint english [expr { && (([testwinlocale] & 0xff) == 9) }] -proc start {widgetcommand args} { +proc vista? {{prevista 0} {postvista 1}} { + lassign [split $::tcl_platform(osVersion) .] major + return [expr {$major >= 6 ? $postvista : $prevista}] +} + +proc start {arg} { set ::tk_dialog 0 set ::iter_after 0 + set ::dialogclass "#32770" - # On newer versions of Windows, we need to find the dialog window - # based on the title - if {[llength $args]} { - set ::dialogtitle [lindex $args 0] - set ::dialogclass "#32770" - if {$::dialogtitle eq ""} { - switch $widgetcommand { - tk_getOpenFile { - set ::dialogtitle Open - } - tk_getSaveFile { - set ::dialogtitle "Save As" - } - tk_chooseDirectory { - set ::dialogtitle "Select Folder" - } - } - } - } - after 1 $widgetcommand + after 1 $arg } proc then {cmd} { @@ -53,7 +40,10 @@ proc then {cmd} { set ::dialogresult {} set ::testfont {} - after 100 afterbody + # Do not make the delay too short. The newer Vista dialogs take + # time to come up. Even if the testforwindow returns true, the + # controls are not ready to accept messages + after 500 afterbody vwait ::dialogresult return $::dialogresult } @@ -61,9 +51,9 @@ proc then {cmd} { proc afterbody {} { # On Vista and later, using the new file dialogs we have to find # the window using its title as tk_dialog will not be set at the C level - if {$::dialogtitle ne "" && [string match 6.* $::tcl_platform(osVersion)]} { + if {[vista?]} { if {[catch {testfindwindow "" $::dialogclass} ::tk_dialog]} { - if {[incr ::iter_after] > 10} { + if {[incr ::iter_after] > 30} { set ::dialogresult ">30 iterations waiting on tk_dialog" return } @@ -237,7 +227,7 @@ test winDialog-5.2 {GetFileName: one argument} -constraints { test winDialog-5.3 {GetFileName: many arguments} -constraints { nt testwinevent } -body { - start {tk_getOpenFile -initialdir c:/ -parent . -title test -initialfile foo} test + start {tk_getOpenFile -initialdir c:/ -parent . -title test -initialfile foo} then { Click cancel } @@ -250,7 +240,7 @@ test winDialog-5.4 {GetFileName: Tcl_GetIndexFromObj() != TCL_OK} -constraints { test winDialog-5.5 {GetFileName: Tcl_GetIndexFromObj() == TCL_OK} -constraints { nt testwinevent } -body { - start {tk_getOpenFile -title bar} bar + start {tk_getOpenFile -title bar} then { Click cancel } @@ -267,10 +257,10 @@ test winDialog-5.7 {GetFileName: extension begins with .} -constraints { # string++; # } - start {set x [tk_getSaveFile -defaultextension .foo -title Save]} Save + start {set x [tk_getSaveFile -defaultextension .foo -title Save]} set msg {} then { - if {[catch {SetText 0x3e9 bar} msg]} { + if {[catch {SetText [vista? 0x47C 0x3e9] bar} msg]} { Click cancel } else { Click ok @@ -286,7 +276,7 @@ test winDialog-5.8 {GetFileName: extension doesn't begin with .} -constraints { start {set x [tk_getSaveFile -defaultextension foo -title Save]} set msg {} then { - if {[catch {SetText 0x47C bar} msg]} { + if {[catch {SetText [vista? 0x47C 0x3e9] bar} msg]} { Click cancel } else { Click ok @@ -296,18 +286,23 @@ test winDialog-5.8 {GetFileName: extension doesn't begin with .} -constraints { } -cleanup { unset msg } -result [string totitle [file join [pwd] bar.foo]] -test winDialog-5.9 {GetFileName: file types} -constraints { - nt testwinevent -} -body { -# case FILE_TYPES: - - start {tk_getSaveFile -filetypes {{"foo files" .foo FOOF}} -title Foo} - then { - set x [GetText 0x470] - Click cancel - } - return $x -} -result {foo files (*.foo)} +if {![vista?]} { + # XXX - currently disabled for vista style dialogs because the file + # types control has no control ID and we don't have a mechanism to + # locate it. + test winDialog-5.9 {GetFileName: file types} -constraints { + nt testwinevent + } -body { + # case FILE_TYPES: + + start {tk_getSaveFile -filetypes {{"foo files" .foo FOOF}} -title Foo} + then { + set x [GetText 0x470] + Click cancel + } + return $x + } -result {foo files (*.foo)} +} test winDialog-5.10 {GetFileName: file types: MakeFilter() fails} -constraints { nt } -body { @@ -320,7 +315,7 @@ test winDialog-5.11 {GetFileName: initial directory} -constraints { nt testwinevent } -body { # case FILE_INITDIR: - + unset -nocomplain x start {set x [tk_getSaveFile \ -initialdir [file normalize $::env(TEMP)] \ -initialfile "12x 455" -title Foo]} @@ -354,19 +349,23 @@ test winDialog-5.14 {GetFileName: initial file: Tcl_TranslateFileName()} -constr # if (Tcl_TranslateFileName(interp, string, &ds) == NULL) tk_getOpenFile -initialfile ~12x/455 } -returnCodes error -result {user "12x" doesn't exist} -test winDialog-5.15 {GetFileName: initial file: long name} -constraints { - nt testwinevent -} -body { - start { - set dialogresult [catch { - tk_getSaveFile -initialfile [string repeat a 1024] -title Long - } x] - } - then { - Click ok - } - list $dialogresult [string match "invalid filename *" $x] -} -result {1 1} +if {![vista?]} { + # XXX - disabled for Vista because the new dialogs allow long file + # names to be specified but force the user to change it. + test winDialog-5.15 {GetFileName: initial file: long name} -constraints { + nt testwinevent + } -body { + start { + set dialogresult [catch { + tk_getSaveFile -initialfile [string repeat a 1024] -title Long + } x] + } + then { + Click ok + } + list $dialogresult [string match "invalid filename *" $x] + } -result {1 1} +} test winDialog-5.16 {GetFileName: parent} -constraints { nt } -body { @@ -390,18 +389,34 @@ test winDialog-5.17 {GetFileName: title} -constraints { Click cancel } } -result {0} -test winDialog-5.18 {GetFileName: no filter specified} -constraints { - nt testwinevent -} -body { -# if (ofn.lpstrFilter == NULL) - - start {tk_getOpenFile -title Filter} - then { - set x [GetText 0x470] - Click cancel - } - return $x -} -result {All Files (*.*)} +if {[vista?]} { + # In the newer file dialogs, the file type widget does not even exist + # if no file types specified + test winDialog-5.18 {GetFileName: no filter specified} -constraints { + nt testwinevent + } -body { + # if (ofn.lpstrFilter == NULL) + start {tk_getOpenFile -title Filter} + then { + catch {set x [GetText 0x470]} y + Click cancel + } + return $y + } -result {Could not find control with id 1136} +} else { + test winDialog-5.18 {GetFileName: no filter specified} -constraints { + nt testwinevent + } -body { + # if (ofn.lpstrFilter == NULL) + + start {tk_getOpenFile -title Filter} + then { + set x [GetText 0x470] + Click cancel + } + return $x + } -result {All Files (*.*)} +} test winDialog-5.19 {GetFileName: parent HWND doesn't yet exist} -constraints { nt } -setup { @@ -458,7 +473,7 @@ test winDialog-5.23 {GetFileName: convert \ to /} -constraints { set msg {} start {set x [tk_getSaveFile -title Back]} then { - if {[catch {SetText 0x47C [file nativename \ + if {[catch {SetText [vista? 0x47C 0x3e9] [file nativename \ [file join [file normalize $::env(TEMP)] "12x 457"]]} msg]} { Click cancel } else { -- cgit v0.12 From 5511931d970d4ff124e84f8875949010793f52ce Mon Sep 17 00:00:00 2001 From: ashok Date: Wed, 15 Oct 2014 15:28:41 +0000 Subject: Updated documentation for tk_getOpen/SaveFile -initialdir option --- doc/getOpenFile.n | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/getOpenFile.n b/doc/getOpenFile.n index 95884bb..30e7cd5 100644 --- a/doc/getOpenFile.n +++ b/doc/getOpenFile.n @@ -65,8 +65,12 @@ discussion on the contents of \fIfilePatternList\fR. \fB\-initialdir\fR \fIdirectory\fR . Specifies that the files in \fIdirectory\fR should be displayed -when the dialog pops up. If this parameter is not specified, then -the files in the current working directory are displayed. If the +when the dialog pops up. If this parameter is not specified, +the initial directory defaults to the current working directory +on non-Windows systems and on Windows systems prior to Vista. +On Vista and later systems, the initial directory defaults to the last +user-selected directory for the application. +If the parameter specifies a relative path, the return value will convert the relative path to an absolute path. .TP -- cgit v0.12 From 10acb72b597836cabb2913980aae7df88ab2af4a Mon Sep 17 00:00:00 2001 From: ashok Date: Thu, 16 Oct 2014 02:45:40 +0000 Subject: Updated chooseDirectory docs for Vista --- doc/chooseDirectory.n | 7 +++++-- doc/getOpenFile.n | 3 +-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/chooseDirectory.n b/doc/chooseDirectory.n index 295c75b..86c593d 100644 --- a/doc/chooseDirectory.n +++ b/doc/chooseDirectory.n @@ -19,8 +19,11 @@ possible as command line arguments: .TP \fB\-initialdir\fR \fIdirname\fR Specifies that the directories in \fIdirectory\fR should be displayed -when the dialog pops up. If this parameter is not specified, then -the directories in the current working directory are displayed. If the +when the dialog pops up. If this parameter is not specified, +the initial directory defaults to the current working directory +on non-Windows systems and on Windows systems prior to Vista. +On Vista and later systems, the initial directory defaults to the last +user-selected directory for the application. If the parameter specifies a relative path, the return value will convert the relative path to an absolute path. .TP diff --git a/doc/getOpenFile.n b/doc/getOpenFile.n index 30e7cd5..f5e92ff 100644 --- a/doc/getOpenFile.n +++ b/doc/getOpenFile.n @@ -69,8 +69,7 @@ when the dialog pops up. If this parameter is not specified, the initial directory defaults to the current working directory on non-Windows systems and on Windows systems prior to Vista. On Vista and later systems, the initial directory defaults to the last -user-selected directory for the application. -If the +user-selected directory for the application. If the parameter specifies a relative path, the return value will convert the relative path to an absolute path. .TP -- cgit v0.12 From 7ce8f8785fa58840a3a8cc90c6b3dab49b4f06bd Mon Sep 17 00:00:00 2001 From: ashok Date: Thu, 16 Oct 2014 02:46:56 +0000 Subject: Fixes to compile with newer Visual Studio versions --- win/tkWinDialog.c | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/win/tkWinDialog.c b/win/tkWinDialog.c index 67f0df4..036f9a9 100644 --- a/win/tkWinDialog.c +++ b/win/tkWinDialog.c @@ -272,19 +272,21 @@ typedef struct _COMDLG_FILTERSPEC { LPCWSTR pszSpec; } COMDLG_FILTERSPEC; -static CLSID CLSID_FileOpenDialog = { +/* + * Older compilers do not define these CLSIDs so we do so here under + * a slightly different name so as to not clash with the definitions + * in new compilers + */ +static const CLSID ClsidFileOpenDialog = { 0xDC1C5A9C, 0xE88A, 0X4DDE, 0xA5, 0xA1, 0x60, 0xF8, 0x2A, 0x20, 0xAE, 0xF7 }; - -static IID IID_IFileOpenDialog = { - 0xD57C7288, 0xD4AD, 0x4768, 0xBE, 0x02, 0x9D, 0x96, 0x95, 0x32, 0xD9, 0x60 -}; - -static CLSID CLSID_FileSaveDialog = { +static const CLSID ClsidFileSaveDialog = { 0xC0B4E2F3, 0xBA21, 0x4773, 0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B }; - -static IID IID_IFileSaveDialog = { +static const IID IIDIFileOpenDialog = { + 0xD57C7288, 0xD4AD, 0x4768, 0xBE, 0x02, 0x9D, 0x96, 0x95, 0x32, 0xD9, 0x60 +}; +static const IID IIDIFileSaveDialog = { 0x84BCCD23, 0x5FDE, 0x4CDB, 0xAE, 0xA4, 0xAF, 0x64, 0xB8, 0x3D, 0x78, 0xAB }; @@ -1110,7 +1112,6 @@ ParseOFNOptions( Tcl_DStringFree(&ds); break; case FILE_PARENT: - /* XXX - check */ optsPtr->tkwin = Tk_NameToWindow(interp, string, clientData); if (optsPtr->tkwin == NULL) goto error_return; @@ -1181,12 +1182,12 @@ static int VistaFileDialogsAvailable() /* Ensure all COM interfaces we use are available */ if (SUCCEEDED(hr)) { - hr = CoCreateInstance(&CLSID_FileOpenDialog, NULL, - CLSCTX_INPROC_SERVER, &IID_IFileOpenDialog, &fdlgPtr); + hr = CoCreateInstance(&ClsidFileOpenDialog, NULL, + CLSCTX_INPROC_SERVER, &IIDIFileOpenDialog, &fdlgPtr); if (SUCCEEDED(hr)) { fdlgPtr->lpVtbl->Release(fdlgPtr); - hr = CoCreateInstance(&CLSID_FileSaveDialog, NULL, - CLSCTX_INPROC_SERVER, &IID_IFileSaveDialog, + hr = CoCreateInstance(&ClsidFileSaveDialog, NULL, + CLSCTX_INPROC_SERVER, &IIDIFileSaveDialog, &fdlgPtr); if (SUCCEEDED(hr)) { fdlgPtr->lpVtbl->Release(fdlgPtr); @@ -1265,11 +1266,11 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, */ if (oper == OFN_FILE_OPEN || oper == OFN_DIR_CHOOSE) - hr = CoCreateInstance(&CLSID_FileOpenDialog, NULL, - CLSCTX_INPROC_SERVER, &IID_IFileOpenDialog, &fdlgIf); + hr = CoCreateInstance(&ClsidFileOpenDialog, NULL, + CLSCTX_INPROC_SERVER, &IIDIFileOpenDialog, &fdlgIf); else - hr = CoCreateInstance(&CLSID_FileSaveDialog, NULL, - CLSCTX_INPROC_SERVER, &IID_IFileSaveDialog, &fdlgIf); + hr = CoCreateInstance(&ClsidFileSaveDialog, NULL, + CLSCTX_INPROC_SERVER, &IIDIFileSaveDialog, &fdlgIf); if (FAILED(hr)) goto vamoose; @@ -1284,7 +1285,7 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, goto vamoose; if (filterPtr) { - flags |= FOS_STRICTFILETYPES; /* XXX - does this match old behaviour? */ + flags |= FOS_STRICTFILETYPES; hr = fdlgIf->lpVtbl->SetFileTypes(fdlgIf, nfilters, filterPtr); if (FAILED(hr)) goto vamoose; -- cgit v0.12 From c92b7c501167ca465f16ca7a00d876719a781e07 Mon Sep 17 00:00:00 2001 From: ashok Date: Thu, 16 Oct 2014 02:48:20 +0000 Subject: Changed to not use c:/ as initialdir --- tests/winDialog.test | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/winDialog.test b/tests/winDialog.test index f21fa18..fc1114a 100644 --- a/tests/winDialog.test +++ b/tests/winDialog.test @@ -27,6 +27,16 @@ proc vista? {{prevista 0} {postvista 1}} { return [expr {$major >= 6 ? $postvista : $prevista}] } +# What directory to use in initialdir tests. Old code used to use +# c:/. However, on Vista/later that is a protected directory if you +# are not running privileged. Moreover, not everyone has a drive c: +# but not having a TEMP would break a lot Windows programs +proc initialdir {} { + # file join to return in Tcl canonical format (/ separator, not \) + return [file join $::env(TEMP)] +} + + proc start {arg} { set ::tk_dialog 0 set ::iter_after 0 @@ -227,7 +237,7 @@ test winDialog-5.2 {GetFileName: one argument} -constraints { test winDialog-5.3 {GetFileName: many arguments} -constraints { nt testwinevent } -body { - start {tk_getOpenFile -initialdir c:/ -parent . -title test -initialfile foo} + start {tk_getOpenFile -initialdir [initialdir] -parent . -title test -initialfile foo} then { Click cancel } @@ -539,7 +549,7 @@ test winDialog-9.3 {Tk_ChooseDirectoryObjCmd: many arguments} -constraints { nt testwinevent } -body { start { - tk_chooseDirectory -initialdir c:/ -mustexist 1 -parent . -title test + tk_chooseDirectory -initialdir [initialdir] -mustexist 1 -parent . -title test } then { Click cancel @@ -568,12 +578,12 @@ test winDialog-9.7 {Tk_ChooseDirectoryObjCmd: -initialdir} -constraints { } -body { # case DIR_INITIAL: - start {set x [tk_chooseDirectory -initialdir c:/ -title Foo]} + start {set x [tk_chooseDirectory -initialdir [initialdir] -title Foo]} then { Click ok } string tolower [set x] -} -result {c:/} +} -result [initialdir] test winDialog-9.8 {Tk_ChooseDirectoryObjCmd: initial directory: Tcl_TranslateFilename()} -constraints { nt } -body { -- cgit v0.12 From 77374e15178cdd57ecc3c37945eb1532474f61b9 Mon Sep 17 00:00:00 2001 From: ashok Date: Thu, 16 Oct 2014 16:36:58 +0000 Subject: Make tkWinDialog.c buildable with gcc 4.8.1, vc6, vs2012. Passes all tests --- win/tkWinDialog.c | 78 +++++++++++++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/win/tkWinDialog.c b/win/tkWinDialog.c index 036f9a9..51e98f9 100644 --- a/win/tkWinDialog.c +++ b/win/tkWinDialog.c @@ -256,6 +256,24 @@ struct IShellItemArray { #endif /* __IShellItemArray_INTERFACE_DEFINED__ */ +/* + * Older compilers do not define these CLSIDs so we do so here under + * a slightly different name so as to not clash with the definitions + * in new compilers + */ +static const CLSID ClsidFileOpenDialog = { + 0xDC1C5A9C, 0xE88A, 0X4DDE, {0xA5, 0xA1, 0x60, 0xF8, 0x2A, 0x20, 0xAE, 0xF7} +}; +static const CLSID ClsidFileSaveDialog = { + 0xC0B4E2F3, 0xBA21, 0x4773, {0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B} +}; +static const IID IIDIFileOpenDialog = { + 0xD57C7288, 0xD4AD, 0x4768, {0xBE, 0x02, 0x9D, 0x96, 0x95, 0x32, 0xD9, 0x60} +}; +static const IID IIDIFileSaveDialog = { + 0x84BCCD23, 0x5FDE, 0x4CDB, {0xAE, 0xA4, 0xAF, 0x64, 0xB8, 0x3D, 0x78, 0xAB} +}; + #ifndef __IFileDialog_INTERFACE_DEFINED__ /* Forward declarations for structs that are referenced but not used */ @@ -272,24 +290,6 @@ typedef struct _COMDLG_FILTERSPEC { LPCWSTR pszSpec; } COMDLG_FILTERSPEC; -/* - * Older compilers do not define these CLSIDs so we do so here under - * a slightly different name so as to not clash with the definitions - * in new compilers - */ -static const CLSID ClsidFileOpenDialog = { - 0xDC1C5A9C, 0xE88A, 0X4DDE, 0xA5, 0xA1, 0x60, 0xF8, 0x2A, 0x20, 0xAE, 0xF7 -}; -static const CLSID ClsidFileSaveDialog = { - 0xC0B4E2F3, 0xBA21, 0x4773, 0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B -}; -static const IID IIDIFileOpenDialog = { - 0xD57C7288, 0xD4AD, 0x4768, 0xBE, 0x02, 0x9D, 0x96, 0x95, 0x32, 0xD9, 0x60 -}; -static const IID IIDIFileSaveDialog = { - 0x84BCCD23, 0x5FDE, 0x4CDB, 0xAE, 0xA4, 0xAF, 0x64, 0xB8, 0x3D, 0x78, 0xAB -}; - enum _FILEOPENDIALOGOPTIONS { FOS_OVERWRITEPROMPT = 0x2, FOS_STRICTFILETYPES = 0x4, @@ -538,10 +538,18 @@ static UINT CALLBACK ColorDlgHookProc(HWND hDlg, UINT uMsg, WPARAM wParam, static void CleanupOFNOptions(OFNOpts *optsPtr); static int ParseOFNOptions(ClientData clientData, Tcl_Interp *interp, int objc, - Tcl_Obj *const objv[], int open, OFNOpts *optsPtr); + Tcl_Obj *const objv[], enum OFNOper oper, OFNOpts *optsPtr); +static int GetFileNameXP(Tcl_Interp *interp, OFNOpts *optsPtr, + enum OFNOper oper); +static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, + enum OFNOper oper); static int GetFileName(ClientData clientData, - Tcl_Interp *interp, int objc, - Tcl_Obj *const objv[], int isOpen); + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[], enum OFNOper oper); +static int MakeFilterVista(Tcl_Interp *interp, OFNOpts *optsPtr, + DWORD *countPtr, COMDLG_FILTERSPEC **dlgFilterPtrPtr, + DWORD *defaultFilterIndexPtr); +static void FreeFilterVista(DWORD count, COMDLG_FILTERSPEC *dlgFilterPtr); static int MakeFilter(Tcl_Interp *interp, Tcl_Obj *valuePtr, Tcl_DString *dsPtr, Tcl_Obj *initialPtr, int *indexPtr); @@ -553,12 +561,6 @@ static const char *ConvertExternalFilename(TCHAR *filename, Tcl_DString *dsPtr); static void LoadShellProcs(void); -static int GetFileNameXP(Tcl_Interp *interp, OFNOpts *optsPtr, int open); -static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, int open); -static int MakeFilterVista(Tcl_Interp *interp, OFNOpts *optsPtr, - DWORD *countPtr, COMDLG_FILTERSPEC **dlgFilterPtrPtr, - DWORD *defaultFilterIndexPtr); -static void FreeFilterVista(DWORD count, COMDLG_FILTERSPEC *dlgFilterPtr); /* Definitions of dynamically loaded Win32 calls */ typedef HRESULT (STDAPICALLTYPE SHCreateItemFromParsingNameProc)( @@ -567,9 +569,6 @@ struct ShellProcPointers { SHCreateItemFromParsingNameProc *SHCreateItemFromParsingName; } ShellProcs; - - - /* *------------------------------------------------------------------------- @@ -1011,7 +1010,6 @@ ParseOFNOptions( FILE_DEFAULT, FILE_TYPES, FILE_INITDIR, FILE_INITFILE, FILE_PARENT, FILE_TITLE, FILE_TYPEVARIABLE, FILE_MULTIPLE, FILE_CONFIRMOW, FILE_MUSTEXIST, - FILE_UNDOCUMENTED_XP_STYLE /* XXX - force XP - style dialogs */ }; struct Options { const char *name; @@ -1183,12 +1181,12 @@ static int VistaFileDialogsAvailable() /* Ensure all COM interfaces we use are available */ if (SUCCEEDED(hr)) { hr = CoCreateInstance(&ClsidFileOpenDialog, NULL, - CLSCTX_INPROC_SERVER, &IIDIFileOpenDialog, &fdlgPtr); + CLSCTX_INPROC_SERVER, &IIDIFileOpenDialog, (void **) &fdlgPtr); if (SUCCEEDED(hr)) { fdlgPtr->lpVtbl->Release(fdlgPtr); hr = CoCreateInstance(&ClsidFileSaveDialog, NULL, CLSCTX_INPROC_SERVER, &IIDIFileSaveDialog, - &fdlgPtr); + (void **) &fdlgPtr); if (SUCCEEDED(hr)) { fdlgPtr->lpVtbl->Release(fdlgPtr); @@ -1267,10 +1265,10 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, if (oper == OFN_FILE_OPEN || oper == OFN_DIR_CHOOSE) hr = CoCreateInstance(&ClsidFileOpenDialog, NULL, - CLSCTX_INPROC_SERVER, &IIDIFileOpenDialog, &fdlgIf); + CLSCTX_INPROC_SERVER, &IIDIFileOpenDialog, (void **) &fdlgIf); else hr = CoCreateInstance(&ClsidFileSaveDialog, NULL, - CLSCTX_INPROC_SERVER, &IIDIFileSaveDialog, &fdlgIf); + CLSCTX_INPROC_SERVER, &IIDIFileSaveDialog, (void **) &fdlgIf); if (FAILED(hr)) goto vamoose; @@ -1356,7 +1354,7 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, Tcl_DStringLength(&optsPtr->utfDirString), &dirString); hr = ShellProcs.SHCreateItemFromParsingName( (TCHAR *) Tcl_DStringValue(&dirString), NULL, - &IID_IShellItem, &dirIf); + &IID_IShellItem, (void **) &dirIf); /* XXX - Note on failure we do not raise error, simply ignore ini dir */ if (SUCCEEDED(hr)) { /* Note we use SetFolder, not SetDefaultFolder - see MSDN docs */ @@ -1851,8 +1849,8 @@ OFNHookProc( buffer = ofnData->dynFileBuffer; hdlg = GetParent(hdlg); - selsize = SendMessage(hdlg, CDM_GETSPEC, 0, 0); - dirsize = SendMessage(hdlg, CDM_GETFOLDERPATH, 0, 0); + selsize = (int) SendMessage(hdlg, CDM_GETSPEC, 0, 0); + dirsize = (int) SendMessage(hdlg, CDM_GETFOLDERPATH, 0, 0); buffersize = (selsize + dirsize + 1); /* @@ -2202,7 +2200,7 @@ static int MakeFilterVista( nbytes = Tcl_DStringLength(&ds); /* # bytes, not Unicode chars */ nbytes += sizeof(WCHAR); /* Terminating \0 */ dlgFilterPtr[i].pszName = ckalloc(nbytes); - memmove(dlgFilterPtr[i].pszName, Tcl_DStringValue(&ds), nbytes); + memmove((void *) dlgFilterPtr[i].pszName, Tcl_DStringValue(&ds), nbytes); Tcl_DStringFree(&ds); /* @@ -2230,7 +2228,7 @@ static int MakeFilterVista( nbytes = Tcl_DStringLength(&ds); /* # bytes, not Unicode chars */ nbytes += sizeof(WCHAR); /* Terminating \0 */ dlgFilterPtr[i].pszSpec = ckalloc(nbytes); - memmove(dlgFilterPtr[i].pszSpec, Tcl_DStringValue(&ds), nbytes); + memmove((void *)dlgFilterPtr[i].pszSpec, Tcl_DStringValue(&ds), nbytes); Tcl_DStringFree(&ds); Tcl_DStringFree(&patterns); } -- cgit v0.12 From be604a17415f8e93fa88b4a00fb136732fc5b0e4 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 17 Oct 2014 14:24:09 +0000 Subject: Fix symbol conflict when compiling with latest (??) MinGW-w64. --- win/tkWinDialog.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/win/tkWinDialog.c b/win/tkWinDialog.c index 51e98f9..211875e 100644 --- a/win/tkWinDialog.c +++ b/win/tkWinDialog.c @@ -285,10 +285,10 @@ typedef enum FDAP { FDAP_TOP = 1 } FDAP; -typedef struct _COMDLG_FILTERSPEC { +typedef struct { LPCWSTR pszName; LPCWSTR pszSpec; -} COMDLG_FILTERSPEC; +} TCLCOMDLG_FILTERSPEC; enum _FILEOPENDIALOGOPTIONS { FOS_OVERWRITEPROMPT = 0x2, @@ -326,7 +326,7 @@ typedef struct IFileDialogVtbl ULONG ( STDMETHODCALLTYPE *Release )( IFileDialog * this); HRESULT ( STDMETHODCALLTYPE *Show )( IFileDialog * this, HWND hwndOwner); HRESULT ( STDMETHODCALLTYPE *SetFileTypes )( IFileDialog * this, - UINT cFileTypes, const COMDLG_FILTERSPEC *rgFilterSpec); + UINT cFileTypes, const TCLCOMDLG_FILTERSPEC *rgFilterSpec); HRESULT ( STDMETHODCALLTYPE *SetFileTypeIndex )(IFileDialog * this, UINT); HRESULT ( STDMETHODCALLTYPE *GetFileTypeIndex )(IFileDialog * this, UINT *); /* XXX - Actually pfde is IFileDialogEvents* but we do not use @@ -391,7 +391,7 @@ typedef struct IFileSaveDialogVtbl { HRESULT ( STDMETHODCALLTYPE *Show )( IFileSaveDialog * this, HWND hwndOwner); HRESULT ( STDMETHODCALLTYPE *SetFileTypes )( IFileSaveDialog * this, - UINT cFileTypes, const COMDLG_FILTERSPEC *rgFilterSpec); + UINT cFileTypes, const TCLCOMDLG_FILTERSPEC *rgFilterSpec); HRESULT ( STDMETHODCALLTYPE *SetFileTypeIndex )( IFileSaveDialog * this, UINT iFileType); HRESULT ( STDMETHODCALLTYPE *GetFileTypeIndex )( @@ -466,7 +466,7 @@ typedef struct IFileOpenDialogVtbl { ULONG ( STDMETHODCALLTYPE *Release )( IFileOpenDialog * this); HRESULT ( STDMETHODCALLTYPE *Show )( IFileOpenDialog * this, HWND); HRESULT ( STDMETHODCALLTYPE *SetFileTypes )( IFileOpenDialog * this, - UINT cFileTypes, const COMDLG_FILTERSPEC *rgFilterSpec); + UINT cFileTypes, const TCLCOMDLG_FILTERSPEC *rgFilterSpec); HRESULT ( STDMETHODCALLTYPE *SetFileTypeIndex )( IFileOpenDialog * this, UINT iFileType); HRESULT ( STDMETHODCALLTYPE *GetFileTypeIndex )( @@ -547,9 +547,9 @@ static int GetFileName(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], enum OFNOper oper); static int MakeFilterVista(Tcl_Interp *interp, OFNOpts *optsPtr, - DWORD *countPtr, COMDLG_FILTERSPEC **dlgFilterPtrPtr, + DWORD *countPtr, TCLCOMDLG_FILTERSPEC **dlgFilterPtrPtr, DWORD *defaultFilterIndexPtr); -static void FreeFilterVista(DWORD count, COMDLG_FILTERSPEC *dlgFilterPtr); +static void FreeFilterVista(DWORD count, TCLCOMDLG_FILTERSPEC *dlgFilterPtr); static int MakeFilter(Tcl_Interp *interp, Tcl_Obj *valuePtr, Tcl_DString *dsPtr, Tcl_Obj *initialPtr, int *indexPtr); @@ -1225,7 +1225,7 @@ static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, HRESULT hr; HWND hWnd; DWORD flags, nfilters, defaultFilterIndex; - COMDLG_FILTERSPEC *filterPtr = NULL; + TCLCOMDLG_FILTERSPEC *filterPtr = NULL; IFileDialog *fdlgIf = NULL; IShellItem *dirIf = NULL; LPWSTR wstr; @@ -2113,7 +2113,7 @@ MakeFilter( * Frees storage previously allocated by MakeFilterVista. * count is the number of elements in dlgFilterPtr[] */ -static void FreeFilterVista(DWORD count, COMDLG_FILTERSPEC *dlgFilterPtr) +static void FreeFilterVista(DWORD count, TCLCOMDLG_FILTERSPEC *dlgFilterPtr) { if (dlgFilterPtr != NULL) { DWORD dw; @@ -2147,13 +2147,13 @@ static int MakeFilterVista( Tcl_Interp *interp, /* Current interpreter. */ OFNOpts *optsPtr, /* Caller specified options */ DWORD *countPtr, /* Will hold number of filters */ - COMDLG_FILTERSPEC **dlgFilterPtrPtr, /* Will hold pointer to filter array. + TCLCOMDLG_FILTERSPEC **dlgFilterPtrPtr, /* Will hold pointer to filter array. Set to NULL if no filters specified. Must be freed by calling FreeFilterVista */ DWORD *initialIndexPtr) /* Will hold index of default type */ { - COMDLG_FILTERSPEC *dlgFilterPtr; + TCLCOMDLG_FILTERSPEC *dlgFilterPtr; const char *initial = NULL; FileFilterList flist; FileFilter *filterPtr; -- cgit v0.12 From dfc524e359974ceccac12721ce48089adf8818b7 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 17 Oct 2014 14:49:00 +0000 Subject: Previous commit probably broke higher VS versions (>2012) compilation. Fix that. --- win/tkWinDialog.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/win/tkWinDialog.c b/win/tkWinDialog.c index 211875e..ee30806 100644 --- a/win/tkWinDialog.c +++ b/win/tkWinDialog.c @@ -274,7 +274,9 @@ static const IID IIDIFileSaveDialog = { 0x84BCCD23, 0x5FDE, 0x4CDB, {0xAE, 0xA4, 0xAF, 0x64, 0xB8, 0x3D, 0x78, 0xAB} }; -#ifndef __IFileDialog_INTERFACE_DEFINED__ +#ifdef __IFileDialog_INTERFACE_DEFINED__ +# define TCLCOMDLG_FILTERSPEC COMDLG_FILTERSPEC +#else /* Forward declarations for structs that are referenced but not used */ typedef struct IPropertyStore IPropertyStore; -- cgit v0.12