// // $Id$ // // SPFieldEditorController.m // sequel-pro // // Created by Hans-J√∂rg Bibiko on July 16, 2009 // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // More info at #import "SPFieldEditorController.h" #ifndef SP_REFACTOR #import "QLPreviewPanel.h" #endif #import "SPDataCellFormatter.h" #import "RegexKitLite.h" #import "SPDataCellFormatter.h" #import "SPTooltip.h" #import "SPGeometryDataView.h" #import "SPCopyTable.h" #include #import "SPCustomQuery.h" #import "SPTableContent.h" #ifndef SP_REFACTOR #import "SPMySQLGeometryData.h" #else #import #endif @interface SPFieldEditorController (SPFieldEditorControllerDelegate) - (void)processFieldEditorResult:(id)data contextInfo:(NSDictionary*)contextInfo; @end #ifdef SP_REFACTOR /* Suppress deprecation warning for beginSheetForDirectory: until Sequel Pro team can migrate */ #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @implementation SPFieldEditorController @synthesize editedFieldInfo; /** * Initialise an instance of SPFieldEditorController using the XIB “FieldEditorSheet.xib”. Init the available Quciklook format by reading * EditorQuickLookTypes.plist and if given user-defined format store in the Preferences for key (SPQuickLookTypes). */ - (id)init { #ifndef SP_REFACTOR if ((self = [super initWithWindowNibName:@"FieldEditorSheet"])) #else if ((self = [super initWithWindowNibName:@"SQLFieldEditorSheet"])) #endif { // force the nib to be loaded (void) [self window]; counter = 0; maxTextLength = 0; stringValue = nil; _isEditable = NO; _isBlob = NO; _allowNULL = YES; _isGeometry = NO; contextInfo = nil; callerInstance = nil; doGroupDueToChars = NO; prefs = [NSUserDefaults standardUserDefaults]; // Used for max text length recognition if last typed char is a non-space char editTextViewWasChanged = NO; // Allow the user to enter cmd+return to close the edit sheet in addition to fn+return [editSheetOkButton setKeyEquivalentModifierMask:NSCommandKeyMask]; allowUndo = NO; selectionChanged = NO; tmpDirPath = [NSTemporaryDirectory() retain]; tmpFileName = nil; NSMenu *menu = [editSheetQuickLookButton menu]; [menu setAutoenablesItems:NO]; NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Interpret data as:", @"Interpret data as:") action:NULL keyEquivalent:@""]; [menuItem setTag:1]; [menuItem setEnabled:NO]; [menu addItem:menuItem]; [menuItem release]; #ifndef SP_REFACTOR NSUInteger tag = 2; // Load default QL types NSMutableArray *qlTypesItems = [[NSMutableArray alloc] init]; NSError *readError = nil; NSString *convError = nil; NSPropertyListFormat format; NSData *defaultTypeData = [NSData dataWithContentsOfFile:[NSBundle pathForResource:@"EditorQuickLookTypes.plist" ofType:nil inDirectory:[[NSBundle mainBundle] bundlePath]] options:NSMappedRead error:&readError]; NSDictionary *defaultQLTypes = [NSPropertyListSerialization propertyListFromData:defaultTypeData mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError]; if(defaultQLTypes == nil || readError != nil || convError != nil) NSLog(@"Error while reading 'EditorQuickLookTypes.plist':\n%@\n%@", [readError localizedDescription], convError); if(defaultQLTypes != nil && [defaultQLTypes objectForKey:@"QuickLookTypes"]) { for(id type in [defaultQLTypes objectForKey:@"QuickLookTypes"]) { NSMenuItem *aMenuItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithString:[type objectForKey:@"MenuLabel"]] action:NULL keyEquivalent:@""]; [aMenuItem setTag:tag]; [aMenuItem setAction:@selector(quickLookFormatButton:)]; [menu addItem:aMenuItem]; [aMenuItem release]; tag++; [qlTypesItems addObject:type]; } } // Load user-defined QL types if([prefs objectForKey:SPQuickLookTypes]) { for(id type in [prefs objectForKey:SPQuickLookTypes]) { NSMenuItem *aMenuItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithString:[type objectForKey:@"MenuLabel"]] action:NULL keyEquivalent:@""]; [aMenuItem setTag:tag]; [aMenuItem setAction:@selector(quickLookFormatButton:)]; [menu addItem:aMenuItem]; [aMenuItem release]; tag++; [qlTypesItems addObject:type]; } } qlTypes = [[NSDictionary dictionaryWithObject:qlTypesItems forKey:SPQuickLookTypes] retain]; [qlTypesItems release]; #endif fieldType = @""; fieldEncoding = @""; } return self; } /** * Dealloc SPFieldEditorController and closes Quicklook window if visible. */ - (void)dealloc { [NSObject cancelPreviousPerformRequestsWithTarget:self]; #ifndef SP_REFACTOR // On Mac OSX 10.6 QuickLook runs non-modal thus order out the panel // if still visible if([[NSClassFromString(@"QLPreviewPanel") sharedPreviewPanel] isVisible]) [[NSClassFromString(@"QLPreviewPanel") sharedPreviewPanel] orderOut:nil]; #endif if ( sheetEditData ) [sheetEditData release]; #ifndef SP_REFACTOR if ( qlTypes ) [qlTypes release]; #endif if ( tmpFileName ) [tmpFileName release]; if ( tmpDirPath ) [tmpDirPath release]; if ( esUndoManager ) [esUndoManager release]; if ( contextInfo ) [contextInfo release]; [super dealloc]; } #pragma mark - /** * Main method for editing data. It will validate several settings and display a modal sheet for theWindow whioch waits until the user closes the sheet. * * @param data The to be edited table field data. * * @param fieldName The name of the currently edited table field. * * @param anEncoding The used encoding while editing. * * @param isFieldBlob If YES the underlying table field is a TEXT/BLOB field. This setting handles several controls which are offered in the sheet to the user. * * @param isEditable If YES the underlying table field is editable, if NO the field is not editable and the SPFieldEditorController sheet do not show a "OK" button for saving. * * @param theWindow The window for displaying the sheet. * * @param sender The calling instance. * * @param contextInfo context info for processing the edited data in sender. * */ - (void)editWithObject:(id)data fieldName:(NSString*)fieldName usingEncoding:(NSStringEncoding)anEncoding isObjectBlob:(BOOL)isFieldBlob isEditable:(BOOL)isEditable withWindow:(NSWindow *)theWindow sender:(id)sender contextInfo:(NSDictionary*)theContextInfo { usedSheet = nil; _isEditable = isEditable; contextInfo = [theContextInfo retain]; callerInstance = sender; _isGeometry = ([[fieldType uppercaseString] isEqualToString:@"GEOMETRY"]) ? YES : NO; // Set field label NSMutableString *label = [NSMutableString string]; [label appendFormat:@"“%@”", fieldName]; if([fieldType length] || maxTextLength > 0 || [fieldEncoding length] || !_allowNULL) [label appendString:@" – "]; if([fieldType length]) [label appendString:fieldType]; if(maxTextLength > 0) [label appendFormat:@"(%ld) ", maxTextLength]; if(!_allowNULL) [label appendString:@"NOT NULL "]; if([fieldEncoding length]) [label appendString:fieldEncoding]; if([fieldType length] && [[fieldType uppercaseString] isEqualToString:@"BIT"]) { sheetEditData = [(NSString*)data retain]; [bitSheetNULLButton setEnabled:_allowNULL]; // Check for NULL if([sheetEditData isEqualToString:[prefs objectForKey:SPNullValue]]) { [bitSheetNULLButton setState:NSOnState]; [self setToNull:bitSheetNULLButton]; } else { [bitSheetNULLButton setState:NSOffState]; } [bitSheetFieldName setStringValue:label]; // Init according bit check boxes NSUInteger i = 0; NSUInteger maxBit = (NSUInteger)((maxTextLength > 64) ? 64 : maxTextLength); if([bitSheetNULLButton state] == NSOffState && maxBit <= [(NSString*)sheetEditData length]) for( i = 0; i