Files
textmate/Frameworks/DocumentWindow/src/DocumentSaveHelper.mm
Allan Odgaard 521b981f09 fixup! Fix warning for literal strings used with NSAlert
Since we use another way to initialize the alert, the return codes are different.
2012-09-07 16:17:18 +02:00

242 lines
9.3 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#import "DocumentSaveHelper.h"
#import "DocumentCommand.h"
#import <OakFoundation/NSString Additions.h>
#import <OakAppKit/NSAlert Additions.h>
#import <OakAppKit/OakSavePanel.h>
#import <OakAppKit/OakEncodingPopUpButton.h>
#import <ns/ns.h>
#import <text/parse.h>
#import <regexp/glob.h>
#import <authorization/constants.h>
#import <document/collection.h>
#import <file/encoding.h>
#import <bundles/bundles.h>
OAK_DEBUG_VAR(DocumentController_SaveHelper);
NSString* DefaultSaveNameForDocument (document::document_ptr const& aDocument)
{
citerate(item, bundles::query(bundles::kFieldGrammarScope, aDocument->file_type()))
{
std::string const& ext = (*item)->value_for_field(bundles::kFieldGrammarExtension);
if(ext != NULL_STR)
return [NSString stringWithCxxString:aDocument->display_name() + "." + ext];
}
return [NSString stringWithCxxString:aDocument->display_name()];
}
@interface DocumentSaveHelper ()
- (void)trySaveDocuments:(std::vector<document::document_ptr> const&)someDocuments forWindow:(NSWindow*)aWindow defaultDirectory:(NSString*)aFolder andCallback:(document_save_callback_t*)aCallback;
- (void)didSaveDocument:(document::document_ptr const&)aDocument success:(BOOL)flag error:(std::string const&)aMessage usingFilter:(oak::uuid_t const&)aFilter;
- (file::save_context_ptr const&)context;
- (void)setContext:(file::save_context_ptr const&)newContext;
@property (nonatomic, retain) NSString* saveFolder;
@property (nonatomic, assign) BOOL userAbort;
@end
namespace
{
struct save_callback_t : document::save_callback_t
{
save_callback_t (document::document_ptr document, DocumentSaveHelper* self, NSWindow* window) : _document(document), _self(self), _window(window) { }
void select_path (std::string const& path, io::bytes_ptr content, file::save_context_ptr context)
{
D(DBF_DocumentController_SaveHelper, bug("\n"););
init(context);
[OakSavePanel showWithPath:DefaultSaveNameForDocument(_document) directory:_self.saveFolder fowWindow:_window delegate:_self encoding:_document->encoding_for_save_as_path(to_s([_self.saveFolder stringByAppendingPathComponent:DefaultSaveNameForDocument(_document)]))];
}
void select_make_writable (std::string const& path, io::bytes_ptr content, file::save_context_ptr context)
{
D(DBF_DocumentController_SaveHelper, bug("\n"););
init(context);
// TODO “unlock file” checkbox (presently implied)
NSAlert* alert = [[NSAlert tmAlertWithMessageText:[NSString stringWithCxxString:text::format("The file “%s” is locked.", _document->display_name().c_str())] informativeText:@"Do you want to overwrite it anyway?" buttons:@"Overwrite", @"Cancel", nil] retain];
[alert beginSheetModalForWindow:_window modalDelegate:_self didEndSelector:@selector(makeWritableSheetDidEnd:returnCode:contextInfo:) contextInfo:NULL];
}
void obtain_authorization (std::string const& path, io::bytes_ptr content, osx::authorization_t auth, file::save_context_ptr context)
{
D(DBF_DocumentController_SaveHelper, bug("\n"););
if(auth.obtain_right(kAuthRightName))
context->set_authorization(auth);
else _self.userAbort = YES;
}
void select_charset (std::string const& path, io::bytes_ptr content, std::string const& charset, file::save_context_ptr context)
{
D(DBF_DocumentController_SaveHelper, bug("\n"););
init(context);
if(charset != kCharsetNoEncoding)
{
// TODO transliteration / BOM check box
NSAlert* alert = [[NSAlert tmAlertWithMessageText:[NSString stringWithCxxString:text::format("Unable to save document using “%s” as encoding.", charset.c_str())] informativeText:@"Please choose another encoding:" buttons:@"Retry", @"Cancel", nil] retain];
[alert setAccessoryView:[[OakEncodingPopUpButton new] autorelease]];
[alert beginSheetModalForWindow:_window modalDelegate:_self didEndSelector:@selector(encodingSheetDidEnd:returnCode:contextInfo:) contextInfo:NULL];
[[alert window] recalculateKeyViewLoop];
}
else
{
context->set_charset(kCharsetUTF8);
}
}
void did_save_document (document::document_ptr document, std::string const& path, bool success, std::string const& message, oak::uuid_t const& filter)
{
D(DBF_DocumentController_SaveHelper, bug("%s, %s\n", path.c_str(), BSTR(success)););
[_self didSaveDocument:document success:success error:message usingFilter:filter];
}
private:
void init (file::save_context_ptr context)
{
document::show(_document);
for(NSWindow* window in [NSApp orderedWindows])
{
id delegate = [window delegate];
if(![window isMiniaturized] && [delegate isKindOfClass:[[_window delegate] class]])
{
_window = [delegate window];
break;
}
}
[_window.attachedSheet orderOut:_self];
[_self setContext:context];
}
document::document_ptr _document;
DocumentSaveHelper* _self;
NSWindow* _window;
};
}
@implementation DocumentSaveHelper
@synthesize saveFolder, userAbort;
- (id)init
{
D(DBF_DocumentController_SaveHelper, bug("\n"););
if(self = [super init])
{
}
return self;
}
+ (void)trySaveDocuments:(std::vector<document::document_ptr> const&)someDocuments forWindow:(NSWindow*)aWindow defaultDirectory:(NSString*)aFolder andCallback:(document_save_callback_t*)aCallback
{
[[[[DocumentSaveHelper alloc] init] autorelease] trySaveDocuments:someDocuments forWindow:aWindow defaultDirectory:aFolder andCallback:aCallback];
}
+ (void)trySaveDocument:(document::document_ptr const&)aDocument forWindow:(NSWindow*)aWindow defaultDirectory:(NSString*)aFolder andCallback:(document_save_callback_t*)aCallback
{
[DocumentSaveHelper trySaveDocuments:std::vector<document::document_ptr>(1, aDocument) forWindow:aWindow defaultDirectory:aFolder andCallback:aCallback];
}
- (void)dealloc
{
D(DBF_DocumentController_SaveHelper, bug("\n"););
[window release];
[saveFolder release];
[super dealloc];
}
- (file::save_context_ptr const&)context { return context; }
- (void)setContext:(file::save_context_ptr const&)newContext { context = newContext; }
- (void)saveNextDocument
{
if(documents.empty())
return;
[self retain]; // keep us retained until document is saved
document::document_ptr document = documents.back();
D(DBF_DocumentController_SaveHelper, bug("%s (%zu total)\n", document->display_name().c_str(), documents.size()););
document->try_save(document::save_callback_ptr((document::save_callback_t*)new save_callback_t(document, self, window)));
}
- (void)trySaveDocuments:(std::vector<document::document_ptr> const&)someDocuments forWindow:(NSWindow*)aWindow defaultDirectory:(NSString*)aFolder andCallback:(document_save_callback_t*)aCallback
{
documents = someDocuments;
window = [aWindow retain];
saveFolder = [aFolder retain];
callback = aCallback;
std::reverse(documents.begin(), documents.end());
[[NSNotificationCenter defaultCenter] postNotificationName:@"OakDocumentNotificationWillSave" object:self];
[self saveNextDocument];
}
- (void)didSaveDocument:(document::document_ptr const&)aDocument success:(BOOL)flag error:(std::string const&)aMessage usingFilter:(oak::uuid_t const&)aFilter
{
D(DBF_DocumentController_SaveHelper, bug("%s, success %s, user abort %s\n", aDocument->path().c_str(), BSTR(flag), BSTR(userAbort)););
if(!flag && !userAbort)
{
[window.attachedSheet orderOut:self];
if(aFilter)
show_command_error(aMessage, aFilter, window);
else [[NSAlert tmAlertWithMessageText:[NSString stringWithCxxString:text::format("The document “%s” could not be saved.", aDocument->display_name().c_str())] informativeText:([NSString stringWithCxxString:aMessage] ?: @"Please check Console output for reason.") buttons:@"OK", nil] beginSheetModalForWindow:window modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
}
if(callback)
callback->did_save_document(aDocument, flag, aMessage, aFilter);
documents.pop_back();
if(flag)
[self saveNextDocument];
if(flag && [[window delegate] respondsToSelector:@selector(updateProxyIcon)])
[[window delegate] performSelector:@selector(updateProxyIcon)]; // FIXME The delegate needs to update proxy icon based on “exists on disk” notifications from document_t
[self release];
}
// ===================
// = Sheet Callbacks =
// ===================
- (void)savePanelDidEnd:(OakSavePanel*)sheet path:(NSString*)aPath encoding:(encoding::type const&)encoding
{
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);
context->set_path(to_s(aPath));
}
else
{
userAbort = YES;
}
context.reset();
}
- (void)makeWritableSheetDidEnd:(NSAlert*)alert returnCode:(NSInteger)returnCode contextInfo:(void*)info
{
D(DBF_DocumentController_SaveHelper, bug("%s\n", BSTR(returnCode == NSAlertDefaultReturn)););
file::save_context_ptr ctxt = context;
[self setContext:file::save_context_ptr()];
if(returnCode == NSAlertFirstButtonReturn)
ctxt->set_make_writable(true);
else userAbort = YES;
[alert release];
}
- (void)encodingSheetDidEnd:(NSAlert*)alert returnCode:(NSInteger)returnCode contextInfo:(void*)info
{
D(DBF_DocumentController_SaveHelper, bug("\n"););
file::save_context_ptr ctxt = context;
[self setContext:file::save_context_ptr()];
userAbort = returnCode != NSAlertFirstButtonReturn;
if(!userAbort)
{
OakEncodingPopUpButton* popUp = (OakEncodingPopUpButton*)[alert accessoryView];
ctxt->set_charset(to_s(popUp.encoding));
}
[alert release];
}
@end