From 18da6f580a2bb9daa22123c2ff6aaa6bd6609005 Mon Sep 17 00:00:00 2001 From: fvogel Date: Fri, 5 May 2017 21:25:29 +0000 Subject: Fix [a5ba1c9764]: Race condition in clipboard cleanup on Windows. Patch from Roman Donchenko (aka dpb). --- win/tkWinX.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/win/tkWinX.c b/win/tkWinX.c index 098fc6d..fca72c3 100644 --- a/win/tkWinX.c +++ b/win/tkWinX.c @@ -686,19 +686,6 @@ TkClipCleanup( TkDisplay *dispPtr) /* Display associated with clipboard. */ { if (dispPtr->clipWindow != NULL) { - /* - * Force the clipboard to be rendered if we are the clipboard owner. - */ - - HWND hwnd = Tk_GetHWND(Tk_WindowId(dispPtr->clipWindow)); - - if (GetClipboardOwner() == hwnd) { - OpenClipboard(hwnd); - EmptyClipboard(); - TkWinClipboardRender(dispPtr, CF_TEXT); - CloseClipboard(); - } - Tk_DeleteSelHandler(dispPtr->clipWindow, dispPtr->clipboardAtom, dispPtr->applicationAtom); Tk_DeleteSelHandler(dispPtr->clipWindow, dispPtr->clipboardAtom, @@ -875,6 +862,23 @@ Tk_TranslateWinEvent( return 1; } + case WM_RENDERALLFORMATS: { + TkWindow *winPtr = (TkWindow *) Tk_HWNDToWindow(hwnd); + + if (winPtr && OpenClipboard(hwnd)) { + /* + * Make sure that nobody had taken ownership of the clipboard + * before we opened it. + */ + + if (GetClipboardOwner() == hwnd) { + TkWinClipboardRender(winPtr->dispPtr, CF_TEXT); + } + CloseClipboard(); + } + return 1; + } + case WM_COMMAND: case WM_NOTIFY: case WM_VSCROLL: -- cgit v0.12 From 900ca0d4015ef35172971ffe2cbc49f8205f42e3 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Tue, 9 May 2017 13:50:24 +0000 Subject: unnecessary Tcl_DStringInit(), since Tcl_WinUtfToTChar() already does that. --- macosx/tkMacOSXDialog.c | 6 +++--- win/tkWinSend.c | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/macosx/tkMacOSXDialog.c b/macosx/tkMacOSXDialog.c index ca55c60..42cd1e5 100644 --- a/macosx/tkMacOSXDialog.c +++ b/macosx/tkMacOSXDialog.c @@ -755,15 +755,15 @@ Tk_GetSaveFileObjCmd( [label setBordered:NO]; [label setBezeled:NO]; [label setDrawsBackground:NO]; - + NSPopUpButton *popupButton = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(50.0, 2, 140, 22.0) pullsDown:NO]; [popupButton addItemsWithTitles:saveFileTypes]; [popupButton setAction:@selector(saveFormat:)]; - + [accessoryView addSubview:label]; [accessoryView addSubview:popupButton]; [savepanel setAllowedFileTypes:saveFileTypes]; - + [savepanel setAccessoryView:accessoryView]; [savepanel setAllowsOtherFileTypes:YES]; } diff --git a/win/tkWinSend.c b/win/tkWinSend.c index c999c0b..fca8561 100644 --- a/win/tkWinSend.c +++ b/win/tkWinSend.c @@ -619,7 +619,6 @@ BuildMoniker( LPMONIKER pmkItem = NULL; Tcl_DString dString; - Tcl_DStringInit(&dString); Tcl_WinUtfToTChar(name, -1, &dString); hr = CreateFileMoniker((LPOLESTR)Tcl_DStringValue(&dString), &pmkItem); Tcl_DStringFree(&dString); -- cgit v0.12 From e41b58b108a24a08463fbc083fbfae137d3a8897 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Wed, 10 May 2017 09:04:55 +0000 Subject: New internal function TkpCancelWarp(), which does a proper warp clean-up when the display is closed. Follow-up to [db8c541b6bff91848c53006b0fe352136fbd4be9|db8c541b6b]. Backported from androwish [http://www.androwish.org/index.html/info/0b3392c9134c228f|0b3392c9134c228f] (which was a little more than just a "add tk upstream changes" ...). Thanks, Christian! --- generic/tkBind.c | 38 ++++++++++++++++++++++++++++++-------- generic/tkInt.h | 1 + generic/tkWindow.c | 2 ++ 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/generic/tkBind.c b/generic/tkBind.c index 61b44df..285b3f7 100644 --- a/generic/tkBind.c +++ b/generic/tkBind.c @@ -1267,7 +1267,7 @@ Tk_BindEvent( */ if ((eventPtr->type >= TK_LASTEVENT) || !flagArray[eventPtr->type]) { - return; + return; } dispPtr = ((TkWindow *) tkwin)->dispPtr; @@ -2878,7 +2878,7 @@ GetAllVirtualEvents( * Any other fields in eventPtr which are not specified by the pattern * string or the optional arguments, are set to 0. * - * The event may be handled sychronously or asynchronously, depending on + * The event may be handled synchronously or asynchronously, depending on * the value specified by the optional "-when" option. The default * setting is synchronous. * @@ -3466,12 +3466,7 @@ HandleEventGenerate( if ((warp != 0) && Tk_IsMapped(tkwin)) { TkDisplay *dispPtr = TkGetDisplay(event.general.xmotion.display); - /* - * TODO: No protection is in place to handle dispPtr destruction - * before DoWarp is called back. - */ - - Tk_Window warpWindow = Tk_IdToWindow(dispPtr->display, +Tk_Window warpWindow = Tk_IdToWindow(dispPtr->display, event.general.xmotion.window); if (!(dispPtr->flags & TK_DISPLAY_IN_WARP)) { @@ -4321,6 +4316,33 @@ TkpGetBindingXEvent( } /* + *---------------------------------------------------------------------- + * + * TkpCancelWarp -- + * + * This function cancels an outstanding pointer warp and + * is called during tear down of the display. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkpCancelWarp( + TkDisplay *dispPtr) +{ + if (dispPtr->flags & TK_DISPLAY_IN_WARP) { + Tcl_CancelIdleCall(DoWarp, dispPtr); + dispPtr->flags &= ~TK_DISPLAY_IN_WARP; + } +} + +/* * Local Variables: * mode: c * c-basic-offset: 4 diff --git a/generic/tkInt.h b/generic/tkInt.h index f00d833..a28cae4 100644 --- a/generic/tkInt.h +++ b/generic/tkInt.h @@ -1223,6 +1223,7 @@ MODULE_SCOPE int TkInitTkCmd(Tcl_Interp *interp, MODULE_SCOPE int TkInitFontchooser(Tcl_Interp *interp, ClientData clientData); MODULE_SCOPE void TkpWarpPointer(TkDisplay *dispPtr); +MODULE_SCOPE void TkpCancelWarp(TkDisplay *dispPtr); MODULE_SCOPE int TkListCreateFrame(ClientData clientData, Tcl_Interp *interp, Tcl_Obj *listObj, int toplevel, Tcl_Obj *nameObj); diff --git a/generic/tkWindow.c b/generic/tkWindow.c index 690a841..2848ff5 100644 --- a/generic/tkWindow.c +++ b/generic/tkWindow.c @@ -239,6 +239,8 @@ TkCloseDisplay( { TkClipCleanup(dispPtr); + TkpCancelWarp(dispPtr); + if (dispPtr->name != NULL) { ckfree(dispPtr->name); } -- cgit v0.12 From 0feebd2bb96d0045ad6b67352539c022d2f148da Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 12 May 2017 08:57:19 +0000 Subject: Revert [f38091d0]: If TK_LAYOUT_WITH_BASE_CHUNKS is set (MacOSX), this change leads to crash, struct CharInfo is defined differently depending on TK_LAYOUT_WITH_BASE_CHUNKS (however strange that is ...) --- generic/tkTextDisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generic/tkTextDisp.c b/generic/tkTextDisp.c index 03d11e1..371e910 100644 --- a/generic/tkTextDisp.c +++ b/generic/tkTextDisp.c @@ -7561,7 +7561,7 @@ TkTextCharLayoutProc( ciPtr = &bciPtr->ci; } else { bciPtr = baseCharChunkPtr->clientData; - ciPtr = ckalloc(Tk_Offset(CharInfo, chars) + 1); + ciPtr = ckalloc(sizeof(CharInfo)); baseString = &bciPtr->baseChars; } -- cgit v0.12 From c257b74035bb102d5860b77c9484c8e7e5ee833c Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Sat, 13 May 2017 16:34:32 +0000 Subject: Major reworking of tkMacOSXDialog.c to support -typevariable, improve file filtering, fix for 3588462; thanks to Christian Gollwitzer for patch --- macosx/tkMacOSXDialog.c | 490 ++++++++++++++++++++++++++++++------------------ 1 file changed, 310 insertions(+), 180 deletions(-) diff --git a/macosx/tkMacOSXDialog.c b/macosx/tkMacOSXDialog.c index 42cd1e5..2670b32 100644 --- a/macosx/tkMacOSXDialog.c +++ b/macosx/tkMacOSXDialog.c @@ -6,6 +6,7 @@ * Copyright (c) 1996-1997 Sun Microsystems, Inc. * Copyright 2001-2009, Apple Inc. * Copyright (c) 2006-2009 Daniel A. Steffen + * Copyright (c) 2017 Christian Gollwitzer. * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. @@ -25,8 +26,19 @@ #define modalError -2 /*Vars for filtering in "open file" and "save file" dialogs.*/ -NSMutableArray *openFileTypes; -NSMutableArray *saveFileTypes; +typedef struct { + bool doFileTypes; // show the accessory view which displays the filter menu + bool preselectFilter; // a filter was selected by the typevariable + bool userHasSelectedFilter; // The user has changed the filter in the accessory view + NSMutableArray *fileTypeNames; // array of names, e.g. "Text document" + NSMutableArray *fileTypeExtensions; // array of allowed extensions per name, e.g. "txt", "doc" + NSMutableArray *fileTypeLabels; // displayed string, e.g. "Text document (.txt, .doc)" + NSMutableArray *allAllowedExtensions; // set of all allowed extensions + NSInteger fileTypeIndex; // index of currently selected filter +} filepanelFilterInfo; + +filepanelFilterInfo filterInfo; + NSOpenPanel *openpanel; NSSavePanel *savepanel; @@ -256,22 +268,18 @@ static NSURL *getFileURL(NSString *directory, NSString *filename) { - (void)selectFormat:(id)sender { NSPopUpButton *button = (NSPopUpButton *)sender; - NSInteger selectedItemIndex = [button indexOfSelectedItem]; - openFileTypes = nil; - openFileTypes = [NSMutableArray array]; - [openFileTypes addObject:[button titleOfSelectedItem]]; - [openpanel setAllowedFileTypes:openFileTypes]; + filterInfo.fileTypeIndex = [button indexOfSelectedItem]; + NSMutableArray *allowedtypes = filterInfo.fileTypeExtensions[filterInfo.fileTypeIndex]; + [openpanel setAllowedFileTypes:allowedtypes]; + filterInfo.userHasSelectedFilter = true; } - (void)saveFormat:(id)sender { NSPopUpButton *button = (NSPopUpButton *)sender; - NSInteger selectedItemIndex = [button indexOfSelectedItem]; - saveFileTypes = nil; - saveFileTypes = [NSMutableArray array]; - [saveFileTypes addObject:[button titleOfSelectedItem]]; - [savepanel setAllowedFileTypes:saveFileTypes]; - + filterInfo.fileTypeIndex = [button indexOfSelectedItem]; + NSMutableArray *allowedtypes = filterInfo.fileTypeExtensions[filterInfo.fileTypeIndex]; + [savepanel setAllowedFileTypes:allowedtypes]; } @end @@ -387,6 +395,98 @@ Tk_ChooseColorObjCmd( end: return result; } + +/* dissect the -filetype nested lists and store the information + * in the filterInfo structure */ +int parseFileFilters(Tcl_Interp *interp, Tcl_Obj *fileTypesPtr, Tcl_Obj *typeVariablePtr) { + + if (!fileTypesPtr) { + filterInfo.doFileTypes = false; + return TCL_OK; + } + + FileFilterList fl; + TkInitFileFilters(&fl); + if (TkGetFileFilters(interp, &fl, fileTypesPtr, 0) != TCL_OK) { + TkFreeFileFilters(&fl); + return TCL_ERROR; + } + + filterInfo.doFileTypes = (fl.filters != NULL); + + filterInfo.fileTypeIndex = 0; + filterInfo.fileTypeExtensions = [NSMutableArray array]; + filterInfo.fileTypeNames = [NSMutableArray array]; + filterInfo.fileTypeLabels = [NSMutableArray array]; + filterInfo.allAllowedExtensions = [NSMutableArray array]; + + if (filterInfo.doFileTypes) { + for (FileFilter *filterPtr = fl.filters; filterPtr; + filterPtr = filterPtr->next) { + NSString * name = [[NSString alloc] initWithUTF8String: filterPtr -> name]; + [filterInfo.fileTypeNames addObject:name]; + [name release]; + NSMutableArray * clauseextensions = [NSMutableArray array]; + NSMutableArray * displayextensions = [NSMutableArray array]; + + for (FileFilterClause *clausePtr = filterPtr->clauses; clausePtr; + clausePtr = clausePtr->next) { + for (GlobPattern *globPtr = clausePtr->patterns; globPtr; + globPtr = globPtr->next) { + const char *str = globPtr->pattern; + while (*str && (*str == '*' || *str == '.')) { + str++; + } + if (*str) { + NSString *extension = [[NSString alloc] initWithUTF8String:str]; + if (![filterInfo.allAllowedExtensions containsObject:extension]) { + [filterInfo.allAllowedExtensions addObject:extension]; + } + + [clauseextensions addObject:extension]; + [displayextensions addObject:[@"." stringByAppendingString:extension]]; + + [extension release]; + } + } + } + [filterInfo.fileTypeExtensions addObject:clauseextensions]; + + NSMutableString * label = [[NSMutableString alloc] initWithString:name]; + [label appendString:@" ("]; + [label appendString:[displayextensions componentsJoinedByString:@", "]]; + [label appendString:@")"]; + [filterInfo.fileTypeLabels addObject:label]; + [label release]; + + } + + /* Check if the typevariable exists and matches one of the names */ + filterInfo.preselectFilter = false; + filterInfo.userHasSelectedFilter = false; + if (typeVariablePtr) { + /* extract the variable content as a NSString */ + Tcl_Obj *selectedFileTypeObj = Tcl_ObjGetVar2(interp, typeVariablePtr, NULL, TCL_GLOBAL_ONLY); + + /* check that the typevariable exists */ + if (selectedFileTypeObj != NULL) { + const char *selectedFileType = Tcl_GetString(selectedFileTypeObj); + NSString *selectedFileTypeStr = [[NSString alloc] initWithUTF8String:selectedFileType]; + NSUInteger index = [filterInfo.fileTypeNames indexOfObject:selectedFileTypeStr]; + + if (index != NSNotFound) { + filterInfo.fileTypeIndex = index; + filterInfo.preselectFilter = true; + } + } + } + + } + + TkFreeFileFilters(&fl); + return TCL_OK; +} + /* *---------------------------------------------------------------------- @@ -415,18 +515,16 @@ Tk_GetOpenFileObjCmd( char *str; int i, result = TCL_ERROR, haveParentOption = 0; int index, len, multiple = 0; - FileFilterList fl; - Tcl_Obj *cmdObj = NULL, *typeVariablePtr = NULL; + Tcl_Obj *cmdObj = NULL, *typeVariablePtr = NULL, *fileTypesPtr = NULL; FilePanelCallbackInfo callbackInfoStruct; FilePanelCallbackInfo *callbackInfo = &callbackInfoStruct; NSString *directory = nil, *filename = nil; - NSString *message, *title, *type; + NSString *message = nil, *title = nil; NSWindow *parent; openpanel = [NSOpenPanel openPanel]; NSInteger modalReturnCode = modalError; BOOL parentIsKey = NO; - TkInitFileFilters(&fl); for (i = 1; i < objc; i += 2) { if (Tcl_GetIndexFromObjStruct(interp, objv[i], openOptionStrings, sizeof(char *), "option", TCL_EXACT, &index) != TCL_OK) { @@ -442,9 +540,7 @@ Tk_GetOpenFileObjCmd( case OPEN_DEFAULT: break; case OPEN_FILETYPES: - if (TkGetFileFilters(interp, &fl, objv[i + 1], 0) != TCL_OK) { - goto end; - } + fileTypesPtr = objv[i + 1]; break; case OPEN_INITDIR: str = Tcl_GetStringFromObj(objv[i + 1], &len); @@ -464,8 +560,6 @@ Tk_GetOpenFileObjCmd( case OPEN_MESSAGE: message = [[NSString alloc] initWithUTF8String: Tcl_GetString(objv[i + 1])]; - [openpanel setMessage:message]; - [message release]; break; case OPEN_MULTIPLE: if (Tcl_GetBooleanFromObj(interp, objv[i + 1], @@ -484,8 +578,6 @@ Tk_GetOpenFileObjCmd( case OPEN_TITLE: title = [[NSString alloc] initWithUTF8String: Tcl_GetString(objv[i + 1])]; - [openpanel setTitle:title]; - [title release]; break; case OPEN_TYPEVARIABLE: typeVariablePtr = objv[i + 1]; @@ -495,57 +587,70 @@ Tk_GetOpenFileObjCmd( break; } } - [openpanel setAllowsMultipleSelection:multiple]; - if (fl.filters) { - openFileTypes = [NSMutableArray array]; - for (FileFilter *filterPtr = fl.filters; filterPtr; - filterPtr = filterPtr->next) { - for (FileFilterClause *clausePtr = filterPtr->clauses; clausePtr; - clausePtr = clausePtr->next) { - for (GlobPattern *globPtr = clausePtr->patterns; globPtr; - globPtr = globPtr->next) { - str = globPtr->pattern; - while (*str && (*str == '*' || *str == '.')) { - str++; - } - if (*str) { - type = [[NSString alloc] initWithUTF8String:str]; - if (![openFileTypes containsObject:type]) { - [openFileTypes addObject:type]; - } - [type release]; - } - } - for (MacFileType *mfPtr = clausePtr->macTypes; mfPtr; - mfPtr = mfPtr->next) { - if (mfPtr->type) { - type = NSFileTypeForHFSTypeCode(mfPtr->type); - if (![openFileTypes containsObject:type]) { - /*Do nothing here, type and creator codes now ignored on macOS.*/ - } - } - } - } + + /* From OSX 10.11, the title string is silently ignored. + * Prepend the title to the message + * NOTE should be conditional on OSX version, but + * -mmacosx-version-min does not revert this behaviour*/ + if (title) { + [openpanel setTitle:title]; + if (message) { + NSString *fullmessage = [[NSString alloc] initWithFormat:@"%@\n%@",title,message]; + [message release]; + [title release]; + message = fullmessage; + } else { + message = title; } } - NSView *accessoryView = [[NSView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 200, 32.0)]; - NSTextField *label = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 60, 22)]; - [label setEditable:NO]; - [label setStringValue:@"Enable:"]; - [label setBordered:NO]; - [label setBezeled:NO]; - [label setDrawsBackground:NO]; + if (message) { + [openpanel setMessage:message]; + [message release]; + } - NSPopUpButton *popupButton = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(50.0, 2, 140, 22.0) pullsDown:NO]; - [popupButton addItemsWithTitles:openFileTypes]; - [popupButton setAction:@selector(selectFormat:)]; + [openpanel setAllowsMultipleSelection:multiple]; + + if (parseFileFilters(interp, fileTypesPtr, typeVariablePtr) != TCL_OK) { + goto end; + } - [accessoryView addSubview:label]; - [accessoryView addSubview:popupButton]; - [openpanel setAllowedFileTypes:openFileTypes]; + if (filterInfo.doFileTypes) { + NSView *accessoryView = [[NSView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 200, 32.0)]; + NSTextField *label = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 60, 22)]; + [label setEditable:NO]; + [label setStringValue:@"Enable:"]; + [label setBordered:NO]; + [label setBezeled:NO]; + [label setDrawsBackground:NO]; + + NSPopUpButton *popupButton = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(50.0, 2, 140, 22.0) pullsDown:NO]; + [popupButton addItemsWithTitles:filterInfo.fileTypeLabels]; + [popupButton setAction:@selector(selectFormat:)]; + + [accessoryView addSubview:label]; + [accessoryView addSubview:popupButton]; + + if (filterInfo.preselectFilter) { + /* A specific filter was selected from the typevariable. Select it and + * open the accessory view */ + [popupButton selectItemAtIndex:filterInfo.fileTypeIndex]; + /* on OSX > 10.11, the optons are not visible by default. Ergo allow all file types + [openpanel setAllowedFileTypes:filterInfo.fileTypeExtensions[filterInfo.fileTypeIndex]]; + */ + [openpanel setAllowedFileTypes:filterInfo.allAllowedExtensions]; + } else { + [openpanel setAllowedFileTypes:filterInfo.allAllowedExtensions]; + } + + [openpanel setAllowsOtherFileTypes:NO]; + + [openpanel setAccessoryView:accessoryView]; + } else { + /* No filters are given. Allow picking all files */ + [openpanel setAllowsOtherFileTypes:YES]; + } - [openpanel setAccessoryView:accessoryView]; if (cmdObj) { callbackInfo = ckalloc(sizeof(FilePanelCallbackInfo)); if (Tcl_IsShared(cmdObj)) { @@ -553,6 +658,7 @@ Tk_GetOpenFileObjCmd( } Tcl_IncrRefCount(cmdObj); } + callbackInfo->cmdObj = cmdObj; callbackInfo->interp = interp; callbackInfo->multiple = multiple; @@ -569,7 +675,6 @@ Tk_GetOpenFileObjCmd( @selector(tkFilePanelDidEnd:returnCode:contextInfo:) contextInfo:callbackInfo]; #else - [openpanel setAllowedFileTypes:openFileTypes]; [openpanel setDirectoryURL:getFileURL(directory, filename)]; [openpanel beginSheetModalForWindow:parent completionHandler:^(NSInteger returnCode) @@ -593,17 +698,22 @@ Tk_GetOpenFileObjCmd( if (parentIsKey) { [parent makeKeyWindow]; } - if (typeVariablePtr && result == TCL_OK) { + + if ((typeVariablePtr && (modalReturnCode == NSOKButton)) && + filterInfo.doFileTypes && filterInfo.userHasSelectedFilter) { /* - * The -typevariable option is not really supported. + * The -typevariable must be set to the selected file type, if the dialog was not cancelled */ - - Tcl_SetVar2(interp, Tcl_GetString(typeVariablePtr), NULL, - "", TCL_GLOBAL_ONLY); + #if 0 + NSLog(@"result: %i modal: %li", result, (long)modalReturnCode); + #endif + NSString * selectedFilter = filterInfo.fileTypeNames[filterInfo.fileTypeIndex]; + Tcl_ObjSetVar2(interp, typeVariablePtr, NULL, + Tcl_NewStringObj([selectedFilter UTF8String], -1), TCL_GLOBAL_ONLY); } + end: - TkFreeFileFilters(&fl); return result; } @@ -636,19 +746,16 @@ Tk_GetSaveFileObjCmd( int i, result = TCL_ERROR, haveParentOption = 0; int confirmOverwrite = 1; int index, len; - FileFilterList fl; - Tcl_Obj *cmdObj = NULL; + Tcl_Obj *cmdObj = NULL, *typeVariablePtr = NULL, *fileTypesPtr = NULL; FilePanelCallbackInfo callbackInfoStruct; FilePanelCallbackInfo *callbackInfo = &callbackInfoStruct; NSString *directory = nil, *filename = nil, *defaultType = nil; - NSString *message, *title, *type; + NSString *message = nil, *title = nil; NSWindow *parent; - NSMutableArray *fileTypes = nil; savepanel = [NSSavePanel savePanel]; NSInteger modalReturnCode = modalError; BOOL parentIsKey = NO; - TkInitFileFilters(&fl); for (i = 1; i < objc; i += 2) { if (Tcl_GetIndexFromObjStruct(interp, objv[i], saveOptionStrings, sizeof(char *), "option", TCL_EXACT, &index) != TCL_OK) { @@ -661,114 +768,120 @@ Tk_GetSaveFileObjCmd( goto end; } switch (index) { - case SAVE_DEFAULT: - str = Tcl_GetStringFromObj(objv[i + 1], &len); - while (*str && (*str == '*' || *str == '.')) { - str++; - } - if (*str) { - defaultType = [[[NSString alloc] initWithUTF8String:str] - autorelease]; - } - break; - case SAVE_FILETYPES: - if (TkGetFileFilters(interp, &fl, objv[i + 1], 0) != TCL_OK) { - goto end; - } - break; - case SAVE_INITDIR: - str = Tcl_GetStringFromObj(objv[i + 1], &len); - if (len) { - directory = [[[NSString alloc] initWithUTF8String:str] - autorelease]; - } - break; - case SAVE_INITFILE: - str = Tcl_GetStringFromObj(objv[i + 1], &len); - if (len) { - filename = [[[NSString alloc] initWithUTF8String:str] - autorelease]; - [savepanel setNameFieldStringValue:filename]; - } - break; - case SAVE_MESSAGE: - message = [[NSString alloc] initWithUTF8String: - Tcl_GetString(objv[i + 1])]; - [savepanel setMessage:message]; + case SAVE_DEFAULT: + str = Tcl_GetStringFromObj(objv[i + 1], &len); + while (*str && (*str == '*' || *str == '.')) { + str++; + } + if (*str) { + defaultType = [[[NSString alloc] initWithUTF8String:str] + autorelease]; + } + break; + case SAVE_FILETYPES: + fileTypesPtr = objv[i + 1]; + break; + case SAVE_INITDIR: + str = Tcl_GetStringFromObj(objv[i + 1], &len); + if (len) { + directory = [[[NSString alloc] initWithUTF8String:str] + autorelease]; + } + break; + case SAVE_INITFILE: + str = Tcl_GetStringFromObj(objv[i + 1], &len); + if (len) { + filename = [[[NSString alloc] initWithUTF8String:str] + autorelease]; + [savepanel setNameFieldStringValue:filename]; + } + break; + case SAVE_MESSAGE: + message = [[NSString alloc] initWithUTF8String: + Tcl_GetString(objv[i + 1])]; + break; + case SAVE_PARENT: + str = Tcl_GetStringFromObj(objv[i + 1], &len); + tkwin = Tk_NameToWindow(interp, str, tkwin); + if (!tkwin) { + goto end; + } + haveParentOption = 1; + break; + case SAVE_TITLE: + title = [[NSString alloc] initWithUTF8String: + Tcl_GetString(objv[i + 1])]; + break; + case SAVE_TYPEVARIABLE: + typeVariablePtr = objv[i + 1]; + break; + case SAVE_COMMAND: + cmdObj = objv[i+1]; + break; + case SAVE_CONFIRMOW: + if (Tcl_GetBooleanFromObj(interp, objv[i + 1], + &confirmOverwrite) != TCL_OK) { + goto end; + } + break; + } + } + + if (title) { + [savepanel setTitle:title]; + if (message) { + NSString *fullmessage = [[NSString alloc] initWithFormat:@"%@\n%@",title,message]; [message release]; - break; - case SAVE_PARENT: - str = Tcl_GetStringFromObj(objv[i + 1], &len); - tkwin = Tk_NameToWindow(interp, str, tkwin); - if (!tkwin) { - goto end; - } - haveParentOption = 1; - break; - case SAVE_TITLE: - title = [[NSString alloc] initWithUTF8String: - Tcl_GetString(objv[i + 1])]; - [savepanel setTitle:title]; [title release]; - break; - case SAVE_TYPEVARIABLE: - break; - case SAVE_COMMAND: - cmdObj = objv[i+1]; - break; - case SAVE_CONFIRMOW: - if (Tcl_GetBooleanFromObj(interp, objv[i + 1], - &confirmOverwrite) != TCL_OK) { - goto end; - } - break; + message = fullmessage; + } else { + message = title; } } - if (fl.filters || defaultType) { - saveFileTypes = [NSMutableArray array]; - for (FileFilter *filterPtr = fl.filters; filterPtr; - filterPtr = filterPtr->next) { - for (FileFilterClause *clausePtr = filterPtr->clauses; clausePtr; - clausePtr = clausePtr->next) { - for (GlobPattern *globPtr = clausePtr->patterns; globPtr; - globPtr = globPtr->next) { - str = globPtr->pattern; - while (*str && (*str == '*' || *str == '.')) { - str++; - } - if (*str) { - type = [[NSString alloc] initWithUTF8String:str]; - if (![saveFileTypes containsObject:type]) { - [saveFileTypes addObject:type]; - } - [type release]; - } - } - } - } - - - NSView *accessoryView = [[NSView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 200, 32.0)]; - NSTextField *label = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 60, 22)]; - [label setEditable:NO]; - [label setStringValue:@"Enable:"]; - [label setBordered:NO]; - [label setBezeled:NO]; - [label setDrawsBackground:NO]; - NSPopUpButton *popupButton = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(50.0, 2, 140, 22.0) pullsDown:NO]; - [popupButton addItemsWithTitles:saveFileTypes]; - [popupButton setAction:@selector(saveFormat:)]; + if (message) { + [savepanel setMessage:message]; + [message release]; + } - [accessoryView addSubview:label]; - [accessoryView addSubview:popupButton]; - [savepanel setAllowedFileTypes:saveFileTypes]; + if (parseFileFilters(interp, fileTypesPtr, typeVariablePtr) != TCL_OK) { + goto end; + } - [savepanel setAccessoryView:accessoryView]; - [savepanel setAllowsOtherFileTypes:YES]; + if (filterInfo.doFileTypes) { + NSView *accessoryView = [[NSView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 200, 32.0)]; + NSTextField *label = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 60, 22)]; + [label setEditable:NO]; + [label setStringValue:NSLocalizedString(@"Format:", nil)]; + [label setBordered:NO]; + [label setBezeled:NO]; + [label setDrawsBackground:NO]; + + NSPopUpButton *popupButton = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(50.0, 2, 140, 22.0) pullsDown:NO]; + [popupButton addItemsWithTitles:filterInfo.fileTypeLabels]; + [popupButton selectItemAtIndex:filterInfo.fileTypeIndex]; + [popupButton setAction:@selector(saveFormat:)]; + + [accessoryView addSubview:label]; + [accessoryView addSubview:popupButton]; + + [savepanel setAccessoryView:accessoryView]; + + [savepanel setAllowedFileTypes:filterInfo.fileTypeExtensions[filterInfo.fileTypeIndex]]; + [savepanel setAllowsOtherFileTypes:NO]; + } else if (defaultType) { + /* If no filetypes are given, defaultextension is an alternative way + * to specify the attached extension. Just propose this extension, + * but don't display an accessory view */ + NSMutableArray *AllowedFileTypes = [NSMutableArray array]; + [AllowedFileTypes addObject:defaultType]; + [savepanel setAllowedFileTypes:AllowedFileTypes]; + [savepanel setAllowsOtherFileTypes:YES]; } + [savepanel setCanSelectHiddenExtension:YES]; [savepanel setExtensionHidden:NO]; + if (cmdObj) { callbackInfo = ckalloc(sizeof(FilePanelCallbackInfo)); if (Tcl_IsShared(cmdObj)) { @@ -779,6 +892,7 @@ Tk_GetSaveFileObjCmd( callbackInfo->cmdObj = cmdObj; callbackInfo->interp = interp; callbackInfo->multiple = 0; + parent = TkMacOSXDrawableWindow(((TkWindow *) tkwin)->window); if (haveParentOption && parent && ![parent attachedSheet]) { parentIsKey = [parent isKeyWindow]; @@ -805,6 +919,9 @@ Tk_GetSaveFileObjCmd( #else [savepanel setDirectoryURL:getFileURL(directory, filename)]; modalReturnCode = [savepanel runModal]; + #if 0 + NSLog(@"modal: %li", modalReturnCode); + #endif #endif [NSApp tkFilePanelDidEnd:savepanel returnCode:modalReturnCode contextInfo:callbackInfo]; @@ -813,8 +930,21 @@ Tk_GetSaveFileObjCmd( if (parentIsKey) { [parent makeKeyWindow]; } + + if ((typeVariablePtr && (modalReturnCode == NSOKButton)) && filterInfo.doFileTypes) { + /* + * The -typevariable must be set to the selected file type, if the dialog was not cancelled + */ + #if 0 + NSLog(@"result: %i modal: %li", result, (long)modalReturnCode); + #endif + NSString * selectedFilter = filterInfo.fileTypeNames[filterInfo.fileTypeIndex]; + Tcl_ObjSetVar2(interp, typeVariablePtr, NULL, + Tcl_NewStringObj([selectedFilter UTF8String], -1), TCL_GLOBAL_ONLY); + } + + end: - TkFreeFileFilters(&fl); return result; } -- cgit v0.12