summaryrefslogtreecommitdiffstats
path: root/win/tkWinDialog.c
diff options
context:
space:
mode:
Diffstat (limited to 'win/tkWinDialog.c')
-rw-r--r--win/tkWinDialog.c220
1 files 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