From 4e9d9414f15650d88cdb75886b09604aaca583de Mon Sep 17 00:00:00 2001 From: Allan Odgaard Date: Tue, 21 Aug 2012 23:45:15 +0200 Subject: [PATCH] Add encoding options to save dialogs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A minor caveat is that if there are encoding or newline (folder specific) settings in effect for the chosen path, these trump what’s selected in the save dialog. If we wish to solve this, the best would be to update the options (shown in the save panel) based on selected folder/filename (and the settings in effect for that). This closes issue #163. --- .../DocumentWindow/src/DocumentController.mm | 21 +- .../DocumentWindow/src/DocumentSaveHelper.mm | 7 +- .../English.lproj/EncodingSaveOptions.xib | 583 ++++++++++++++++++ Frameworks/OakAppKit/src/OakSavePanel.h | 9 +- Frameworks/OakAppKit/src/OakSavePanel.mm | 97 ++- 5 files changed, 698 insertions(+), 19 deletions(-) create mode 100644 Frameworks/OakAppKit/resources/English.lproj/EncodingSaveOptions.xib diff --git a/Frameworks/DocumentWindow/src/DocumentController.mm b/Frameworks/DocumentWindow/src/DocumentController.mm index 39758e2c..bed7a26e 100644 --- a/Frameworks/DocumentWindow/src/DocumentController.mm +++ b/Frameworks/DocumentWindow/src/DocumentController.mm @@ -837,7 +837,7 @@ NSString* const kUserDefaultsFileBrowserPlacementKey = @"fileBrowserPlacement"; // = Document Action Methods = // =========================== -- (void)savePanelDidEnd:(OakSavePanel*)sheet path:(NSString*)aPath contextInfo:(void*)info +- (void)savePanelDidEnd:(OakSavePanel*)sheet path:(NSString*)aPath encoding:(std::string const&)encoding newlines:(std::string const&)newlines useBOM:(BOOL)useBOM { if(!aPath) return; @@ -849,6 +849,10 @@ NSString* const kUserDefaultsFileBrowserPlacementKey = @"fileBrowserPlacement"; ASSERT_LT(0, paths.size()); [self selectedDocument]->set_path(paths[0]); // FIXME check if document already exists (overwrite) + [self selectedDocument]->set_disk_encoding(encoding); + [self selectedDocument]->set_disk_newlines(newlines); + [self selectedDocument]->set_disk_bom(useBOM); + [DocumentSaveHelper trySaveDocument:self.selectedDocument forWindow:self.window defaultDirectory:nil andCallback:NULL]; if(paths.size() > 1) @@ -858,6 +862,9 @@ NSString* const kUserDefaultsFileBrowserPlacementKey = @"fileBrowserPlacement"; { documents.push_back(document::create(paths[i])); documents.back()->open(); // so that we find this when going to counterpart + documents.back()->set_disk_encoding(encoding); + documents.back()->set_disk_newlines(newlines); + documents.back()->set_disk_bom(useBOM); } [self addDocuments:documents andSelect:kSelectDocumentNone closeOther:NO pruneTabBar:NO]; @@ -870,18 +877,20 @@ NSString* const kUserDefaultsFileBrowserPlacementKey = @"fileBrowserPlacement"; - (IBAction)saveDocument:(id)sender { D(DBF_DocumentController, bug("%s\n", [self selectedDocument]->path().c_str());); - if([self selectedDocument]->path() != NULL_STR) - [DocumentSaveHelper trySaveDocument:self.selectedDocument forWindow:self.window defaultDirectory:nil andCallback:NULL]; - else [OakSavePanel showWithPath:DefaultSaveNameForDocument([self selectedDocument]) directory:self.untitledSavePath fowWindow:self.window delegate:self contextInfo:NULL]; + document::document_ptr doc = [self selectedDocument]; + if(doc->path() != NULL_STR) + [DocumentSaveHelper trySaveDocument:doc forWindow:self.window defaultDirectory:nil andCallback:NULL]; + else [OakSavePanel showWithPath:DefaultSaveNameForDocument(doc) directory:self.untitledSavePath fowWindow:self.window delegate:self encoding:doc->disk_encoding() newlines:doc->disk_newlines() useBOM:doc->disk_bom()]; } - (IBAction)saveDocumentAs:(id)sender { D(DBF_DocumentController, bug("%s\n", [self selectedDocument]->path().c_str());); - std::string const documentPath = [self selectedDocument]->path(); + document::document_ptr doc = [self selectedDocument]; + std::string const documentPath = doc->path(); NSString* documentFolder = [NSString stringWithCxxString:path::parent(documentPath)]; NSString* documentName = [NSString stringWithCxxString:path::name(documentPath)]; - [OakSavePanel showWithPath:(documentName ?: DefaultSaveNameForDocument([self selectedDocument])) directory:(documentFolder ?: self.untitledSavePath) fowWindow:self.window delegate:self contextInfo:NULL]; + [OakSavePanel showWithPath:(documentName ?: DefaultSaveNameForDocument(doc)) directory:(documentFolder ?: self.untitledSavePath) fowWindow:self.window delegate:self encoding:doc->disk_encoding() newlines:doc->disk_newlines() useBOM:doc->disk_bom()]; } - (IBAction)saveAllDocuments:(id)sender diff --git a/Frameworks/DocumentWindow/src/DocumentSaveHelper.mm b/Frameworks/DocumentWindow/src/DocumentSaveHelper.mm index 0dc36b8b..945d1318 100644 --- a/Frameworks/DocumentWindow/src/DocumentSaveHelper.mm +++ b/Frameworks/DocumentWindow/src/DocumentSaveHelper.mm @@ -44,7 +44,7 @@ namespace D(DBF_DocumentController_SaveHelper, bug("\n");); init(context); - [OakSavePanel showWithPath:DefaultSaveNameForDocument(_document) directory:_self.saveFolder fowWindow:_window delegate:_self contextInfo:NULL]; + [OakSavePanel showWithPath:DefaultSaveNameForDocument(_document) directory:_self.saveFolder fowWindow:_window delegate:_self encoding:_document->disk_encoding() newlines:_document->disk_newlines() useBOM:_document->disk_bom()]; } void select_make_writable (std::string const& path, io::bytes_ptr content, file::save_context_ptr context) @@ -197,12 +197,15 @@ namespace // = Sheet Callbacks = // =================== -- (void)savePanelDidEnd:(OakSavePanel*)sheet path:(NSString*)aPath contextInfo:(void*)info +- (void)savePanelDidEnd:(OakSavePanel*)sheet path:(NSString*)aPath encoding:(std::string const&)encoding newlines:(std::string const&)newlines useBOM:(BOOL)useBOM { D(DBF_DocumentController_SaveHelper, bug("%s\n", to_s(aPath).c_str());); if(aPath) { documents.back()->set_path(to_s(aPath)); + documents.back()->set_disk_encoding(encoding); + documents.back()->set_disk_newlines(newlines); + documents.back()->set_disk_bom(useBOM); context->set_path(to_s(aPath)); } else diff --git a/Frameworks/OakAppKit/resources/English.lproj/EncodingSaveOptions.xib b/Frameworks/OakAppKit/resources/English.lproj/EncodingSaveOptions.xib new file mode 100644 index 00000000..1063898e --- /dev/null +++ b/Frameworks/OakAppKit/resources/English.lproj/EncodingSaveOptions.xib @@ -0,0 +1,583 @@ + + + + 1070 + 12A269 + 2549 + 1187 + 624.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 2549 + + + NSButton + NSButtonCell + NSCustomObject + NSCustomView + NSMenu + NSMenuItem + NSPopUpButton + NSPopUpButtonCell + NSTextField + NSTextFieldCell + NSUserDefaultsController + + + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + + OakEncodingSaveOptionsViewController + + + FirstResponder + + + NSApplication + + + + 301 + + + + 268 + {{17, 77}, {89, 17}} + + + + _NS:1535 + YES + + 68157504 + 272630784 + Line endings: + + LucidaGrande + 13 + 1044 + + _NS:1535 + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2NjY3AA + + + + 6 + System + controlTextColor + + 3 + MAA + + + + NO + + + + 268 + {{40, 46}, {66, 17}} + + + + _NS:1535 + YES + + 68157504 + 272630784 + Encoding: + + _NS:1535 + + + + + NO + + + + 268 + {{109, 18}, {153, 18}} + + + + _NS:9 + YES + + -2080374784 + 268435456 + Add byte order mark + + _NS:9 + + 1211912448 + 2 + + NSImage + NSSwitch + + + NSSwitch + + + + 200 + 25 + + NO + + + + 268 + {{108, 72}, {174, 26}} + + + + _NS:9 + YES + + -2076180416 + 2048 + + _NS:9 + + 109199360 + 129 + + + 400 + 75 + + + LF (recommended) + + 1048576 + 2147483647 + 1 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + _popUpItemAction: + + + YES + + OtherViews + + + + + CR (Classic Mac) + + 1048576 + 2147483647 + + + _popUpItemAction: + 1 + + + + + CRLF (MS-DOS) + + 1048576 + 2147483647 + + + _popUpItemAction: + 2 + + + + + + 1 + YES + YES + 2 + + NO + + + + 268 + {{108, 41}, {174, 26}} + + + + _NS:9 + YES + + -2076180416 + 2048 + + _NS:9 + + 109199360 + 129 + + + 400 + 75 + + + encoding + + 1048576 + 2147483647 + 1 + + + _popUpItemAction: + + + YES + + OtherViews + + + + + + 1 + YES + YES + 2 + + NO + + + {299, 116} + + + + NSView + + + YES + + + + + + + view + + + + 228 + + + + encodingPopUpButton + + + + 238 + + + + selectedTag: lineEndings + + + + + + selectedTag: lineEndings + selectedTag + lineEndings + + NSValueTransformerName + OakLineEndingsTransformer + + 2 + + + 247 + + + + value: useByteOrderMark + + + + + + value: useByteOrderMark + value + useByteOrderMark + 2 + + + 240 + + + + enabled: canUseByteOrderMark + + + + + + enabled: canUseByteOrderMark + enabled + canUseByteOrderMark + 2 + + + 242 + + + + + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 1 + + + + + + + + + + + + 33 + + + + + + + + 34 + + + + + 2 + + + + + + + + 3 + + + + + + + + 4 + + + + + + + + 5 + + + + + 11 + + + + + + + + 13 + + + + + + + + 14 + + + + + + + + + + 17 + + + + + 16 + + + + + 15 + + + + + 25 + + + + + + + + 26 + + + + + 29 + + + + + + + + 30 + + + + + 243 + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + Line Endings Pop Up Button + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + OakEncodingPopUpButton + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + + 247 + + + + + OakEncodingPopUpButton + NSPopUpButton + + IBProjectSource + ./Classes/OakEncodingPopUpButton.h + + + + OakEncodingSaveOptionsViewController + NSViewController + + encodingPopUpButton + OakEncodingPopUpButton + + + encodingPopUpButton + + encodingPopUpButton + OakEncodingPopUpButton + + + + IBProjectSource + ./Classes/OakEncodingSaveOptionsViewController.h + + + + + 0 + IBCocoaFramework + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + 3 + + {11, 11} + {10, 3} + {15, 15} + + + diff --git a/Frameworks/OakAppKit/src/OakSavePanel.h b/Frameworks/OakAppKit/src/OakSavePanel.h index f1c20c1e..02073d13 100644 --- a/Frameworks/OakAppKit/src/OakSavePanel.h +++ b/Frameworks/OakAppKit/src/OakSavePanel.h @@ -1,11 +1,12 @@ +@class OakEncodingSaveOptionsViewController; + @interface OakSavePanel : NSObject { - id delegate; - void* contextInfo; + OakEncodingSaveOptionsViewController* optionsViewController; } -+ (void)showWithPath:(NSString*)aPathSuggestion directory:(NSString*)aDirectorySuggestion fowWindow:(NSWindow*)aWindow delegate:(id)aDelegate contextInfo:(void*)info; ++ (void)showWithPath:(NSString*)aPathSuggestion directory:(NSString*)aDirectorySuggestion fowWindow:(NSWindow*)aWindow delegate:(id)aDelegate encoding:(std::string const&)encoding newlines:(std::string const&)newlines useBOM:(BOOL)useBOM; @end @interface NSObject (OakSavePanelDelegate) -- (void)savePanelDidEnd:(OakSavePanel*)sheet path:(NSString*)aPath contextInfo:(void*)info; +- (void)savePanelDidEnd:(OakSavePanel*)sheet path:(NSString*)aPath encoding:(std::string const&)encoding newlines:(std::string const&)newlines useBOM:(BOOL)useBOM; @end diff --git a/Frameworks/OakAppKit/src/OakSavePanel.mm b/Frameworks/OakAppKit/src/OakSavePanel.mm index 703797c8..84b8cc5d 100644 --- a/Frameworks/OakAppKit/src/OakSavePanel.mm +++ b/Frameworks/OakAppKit/src/OakSavePanel.mm @@ -1,13 +1,90 @@ -#import "NSSavePanel Additions.h" #import "OakSavePanel.h" +#import "OakEncodingPopUpButton.h" +#import "NSSavePanel Additions.h" +#import +#import +#import + +@interface OakEncodingSaveOptionsViewController : NSViewController +{ + IBOutlet OakEncodingPopUpButton* encodingPopUpButton; + + NSString* encoding; + NSString* lineEndings; + BOOL useByteOrderMark; +} +@property (nonatomic, retain) NSString* lineEndings; +@property (nonatomic, retain) NSString* encoding; +@property (nonatomic, assign) BOOL useByteOrderMark; +@property (nonatomic, readonly) BOOL canUseByteOrderMark; +@end + +@implementation OakEncodingSaveOptionsViewController +@synthesize encoding, lineEndings, useByteOrderMark; + ++ (NSSet*)keyPathsForValuesAffectingCanUseByteOrderMark { return [NSSet setWithObject:@"encoding"]; } + ++ (void)initialize +{ + [OakStringListTransformer createTransformerWithName:@"OakLineEndingsTransformer" andObjectsArray:@[ @"\n", @"\r", @"\r\n" ]]; + [OakStringListTransformer createTransformerWithName:@"OakLineEndingsListTransformer" andObjectsArray:@[ @"\n", @"\r", @"\r\n" ]]; +} + +- (id)init +{ + if(self = [super initWithNibName:@"EncodingSaveOptions" bundle:[NSBundle bundleForClass:[self class]]]) + { + self.encoding = @"UTF-8"; + self.lineEndings = @"\n"; + } + return self; +} + +- (void)dealloc +{ + [self unbind:@"encoding"]; + [super dealloc]; +} + +- (void)loadView +{ + [super loadView]; + encodingPopUpButton.encoding = encoding; + [self bind:@"encoding" toObject:encodingPopUpButton withKeyPath:@"encoding" options:nil]; +} + +- (BOOL)canUseByteOrderMark +{ + return [self.encoding hasPrefix:@"UTF-"]; +} + +- (void)setEncoding:(NSString*)newEncoding +{ + if(encoding != newEncoding && ![encoding isEqualToString:newEncoding]) + { + [encoding autorelease]; + encoding = [newEncoding retain]; + + self.useByteOrderMark = [self canUseByteOrderMark] && ![encoding isEqualToString:@"UTF-8"]; + } +} +@end @implementation OakSavePanel -- (id)initWithPath:(NSString*)aPathSuggestion directory:(NSString*)aDirectorySuggestion fowWindow:(NSWindow*)aWindow delegate:(id)aDelegate contextInfo:(void*)info +- (id)initWithPath:(NSString*)aPathSuggestion directory:(NSString*)aDirectorySuggestion fowWindow:(NSWindow*)aWindow delegate:(id)aDelegate encoding:(std::string const&)encoding newlines:(std::string const&)newlines useBOM:(BOOL)useBOM { if((self = [super init])) { - delegate = aDelegate; - contextInfo = info; + optionsViewController = [OakEncodingSaveOptionsViewController new]; + if(!optionsViewController) + { + [self release]; + return nil; + } + + optionsViewController.encoding = [NSString stringWithCxxString:encoding] ?: @"UTF-8"; + optionsViewController.lineEndings = [NSString stringWithCxxString:newlines] ?: @"\n"; + optionsViewController.useByteOrderMark = useBOM; [[aWindow attachedSheet] orderOut:self]; // incase there already is a sheet showing (like “Do you want to save?”) @@ -16,9 +93,10 @@ if(aDirectorySuggestion) [savePanel setDirectoryURL:[NSURL fileURLWithPath:aDirectorySuggestion]]; [savePanel setNameFieldStringValue:[aPathSuggestion lastPathComponent]]; + [savePanel setAccessoryView:optionsViewController.view]; [savePanel beginSheetModalForWindow:aWindow completionHandler:^(NSInteger result) { NSString* path = result == NSOKButton ? [[savePanel.URL filePathURL] path] : nil; - [delegate savePanelDidEnd:self path:path contextInfo:contextInfo]; + [aDelegate savePanelDidEnd:self path:path encoding:to_s(optionsViewController.encoding) newlines:to_s(optionsViewController.lineEndings) useBOM:optionsViewController.useByteOrderMark]; [self release]; }]; [savePanel deselectExtension]; @@ -26,9 +104,14 @@ return self; } -+ (void)showWithPath:(NSString*)aPathSuggestion directory:(NSString*)aDirectorySuggestion fowWindow:(NSWindow*)aWindow delegate:(id)aDelegate contextInfo:(void*)info +- (void)dealloc { - [[OakSavePanel alloc] initWithPath:aPathSuggestion directory:aDirectorySuggestion fowWindow:aWindow delegate:aDelegate contextInfo:info]; + [optionsViewController release]; + [super dealloc]; } ++ (void)showWithPath:(NSString*)aPathSuggestion directory:(NSString*)aDirectorySuggestion fowWindow:(NSWindow*)aWindow delegate:(id)aDelegate encoding:(std::string const&)encoding newlines:(std::string const&)newlines useBOM:(BOOL)useBOM +{ + [[OakSavePanel alloc] initWithPath:aPathSuggestion directory:aDirectorySuggestion fowWindow:aWindow delegate:aDelegate encoding:encoding newlines:newlines useBOM:useBOM]; +} @end