diff options
-rw-r--r-- | Source/SPFieldEditor.h | 48 | ||||
-rw-r--r-- | Source/SPFieldEditor.m | 371 | ||||
-rw-r--r-- | sequel-pro.xcodeproj/project.pbxproj | 8 |
3 files changed, 426 insertions, 1 deletions
diff --git a/Source/SPFieldEditor.h b/Source/SPFieldEditor.h new file mode 100644 index 00000000..8b057f80 --- /dev/null +++ b/Source/SPFieldEditor.h @@ -0,0 +1,48 @@ +// +// SPFieldEditor.h +// sequel-pro +// +// Created by Bibiko on 25.06.09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import <Cocoa/Cocoa.h> + + +@interface SPFieldEditor : NSWindow { + + IBOutlet id editSheetProgressBar; + IBOutlet id editSheet; + IBOutlet id editSheetSegmentControl; + IBOutlet id editSheetQuickLookButton; + IBOutlet id editImage; + IBOutlet id editTextView; + IBOutlet id hexTextView; + IBOutlet id editTextScrollView; + IBOutlet id hexTextScrollView; + + id editData; + BOOL editSheetWillBeInitialized; + int quickLookCloseMarker; + NSStringEncoding encoding; + +} + +- (IBAction)closeEditSheet:(id)sender; +- (IBAction)openEditSheet:(id)sender; +- (IBAction)saveEditSheet:(id)sender; +- (IBAction)dropImage:(id)sender; +- (IBAction)segmentControllerChanged:(id)sender; +- (IBAction)quickLookFormatButton:(id)sender; + +- (void)setEditData:(id)data; + +- (void)setMySQLConnectionEncoding:(NSStringEncoding)anEncoding; + +- (void)processPasteImageData; +- (void)processUpdatedImageData:(NSData *)data; + +- (void)invokeQuickLookOfType:(NSString *)type treatAsText:(BOOL)isText; +- (void)removeQuickLooksTempFile:(NSString*)aPath; + +@end diff --git a/Source/SPFieldEditor.m b/Source/SPFieldEditor.m new file mode 100644 index 00000000..9d0bb75e --- /dev/null +++ b/Source/SPFieldEditor.m @@ -0,0 +1,371 @@ +// +// SPFieldEditor.m +// sequel-pro +// +// Created by Bibiko on 25.06.09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import "SPFieldEditor.h" +#import "SPStringAdditions.h" +#import "SPArrayAdditions.h" +#import "SPTextViewAdditions.h" +#import "SPDataAdditions.h" +#import "QLPreviewPanel.h" + + +@implementation SPFieldEditor + + +- (void)setEditData:(id)data +{ + editData = [data retain]; +} + +- (void)setMySQLConnectionEncoding:(NSStringEncoding)anEncoding; +{ + encoding = anEncoding; +} + + +- (IBAction)closeEditSheet:(id)sender +{ + [NSApp stopModalWithCode:[sender tag]]; +} + +- (IBAction)openEditSheet:(id)sender +{ + NSOpenPanel *panel = [NSOpenPanel openPanel]; + + if ( [panel runModal] == NSOKButton ) { + NSString *fileName = [panel filename]; + NSString *contents = nil; + + editSheetWillBeInitialized = YES; + + [editSheetProgressBar startAnimation:self]; + + // free old data + if ( editData != nil ) { + [editData release]; + } + + // load new data/images + editData = [[NSData alloc] initWithContentsOfFile:fileName]; + + NSImage *image = [[NSImage alloc] initWithData:editData]; + contents = [[NSString alloc] initWithData:editData encoding:encoding]; + if (contents == nil) + contents = [[NSString alloc] initWithData:editData encoding:NSASCIIStringEncoding]; + + // set the image preview, string contents and hex representation + [editImage setImage:image]; + + + if(contents) + [editTextView setString:contents]; + else + [editTextView setString:@""]; + + // Load hex data only if user has already displayed them + if(![[hexTextView string] isEqualToString:@""]) + [hexTextView setString:[editData dataToFormattedHexString]]; + + // If the image cell now contains a valid image, select the image view + if (image) { + [editSheetSegmentControl setSelectedSegment:1]; + [hexTextView setHidden:YES]; + [hexTextScrollView setHidden:YES]; + [editImage setHidden:NO]; + [editTextView setHidden:YES]; + [editTextScrollView setHidden:YES]; + + // Otherwise deselect the image view + } else { + [editSheetSegmentControl setSelectedSegment:0]; + [hexTextView setHidden:YES]; + [hexTextScrollView setHidden:YES]; + [editImage setHidden:YES]; + [editTextView setHidden:NO]; + [editTextScrollView setHidden:NO]; + } + + [image release]; + if(contents) + [contents release]; + [editSheetProgressBar stopAnimation:self]; + editSheetWillBeInitialized = NO; + } +} + +/* + * Segement controller for text/image/hex buttons in editSheet + */ +- (IBAction)segmentControllerChanged:(id)sender +{ + switch([sender selectedSegment]){ + case 0: // text + [editTextView setHidden:NO]; + [editTextScrollView setHidden:NO]; + [editImage setHidden:YES]; + [hexTextView setHidden:YES]; + [hexTextScrollView setHidden:YES]; + [editSheet makeFirstResponder:editTextView]; + break; + case 1: // image + [editTextView setHidden:YES]; + [editTextScrollView setHidden:YES]; + [editImage setHidden:NO]; + [hexTextView setHidden:YES]; + [hexTextScrollView setHidden:YES]; + [editSheet makeFirstResponder:editImage]; + break; + case 2: // hex - load on demand + if([editData length] && [[hexTextView string] isEqualToString:@""]) { + [editSheetProgressBar startAnimation:self]; + [hexTextView setString:[editData dataToFormattedHexString]]; + [editSheetProgressBar stopAnimation:self]; + } + [editTextView setHidden:YES]; + [editTextScrollView setHidden:YES]; + [editImage setHidden:YES]; + [hexTextView setHidden:NO]; + [hexTextScrollView setHidden:NO]; + [editSheet makeFirstResponder:hexTextView]; + break; + } +} + +/* + * Saves a file containing the content of the editSheet + */ +- (IBAction)saveEditSheet:(id)sender +{ + NSSavePanel *panel = [NSSavePanel savePanel]; + + if ( [panel runModal] == NSOKButton ) { + + [editSheetProgressBar startAnimation:self]; + + NSString *fileName = [panel filename]; + + // Write binary field types directly to the file + //// || [editSheetBinaryButton state] == NSOnState + if ( [editData isKindOfClass:[NSData class]] ) { + [editData writeToFile:fileName atomically:YES]; + + // Write other field types' representations to the file via the current encoding + } else { + [[editData description] writeToFile:fileName + atomically:YES + encoding:encoding + error:NULL]; + } + + [editSheetProgressBar stopAnimation:self]; + + } +} + + +#pragma mark - +#pragma mark QuickLook + +- (IBAction)quickLookFormatButton:(id)sender +{ + switch([sender tag]) { + case 0: [self invokeQuickLookOfType:@"pict" treatAsText:NO]; break; + case 1: [self invokeQuickLookOfType:@"m4a" treatAsText:NO]; break; + case 2: [self invokeQuickLookOfType:@"mp3" treatAsText:NO]; break; + case 3: [self invokeQuickLookOfType:@"wav" treatAsText:NO]; break; + case 4: [self invokeQuickLookOfType:@"mov" treatAsText:NO]; break; + case 5: [self invokeQuickLookOfType:@"pdf" treatAsText:NO]; break; + case 6: [self invokeQuickLookOfType:@"html" treatAsText:YES]; break; + case 7: [self invokeQuickLookOfType:@"doc" treatAsText:NO]; break; + case 8: [self invokeQuickLookOfType:@"rtf" treatAsText:YES]; break; + } +} + +/* + * Opens QuickLook for current data if QuickLook is available + */ +- (void)invokeQuickLookOfType:(NSString *)type treatAsText:(BOOL)isText +{ + + // Load private framework + if([[NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/QuickLookUI.framework"] load]) { + + [editSheetProgressBar startAnimation:self]; + + // Create a temporary file name to store the data as file + // since QuickLook only works on files. + NSString *tmpFileName = [NSString stringWithFormat:@"/tmp/SequelProQuickLook.%@", type]; + + // if data are binary + if ( [editData isKindOfClass:[NSData class]] || !isText) { + [editData writeToFile:tmpFileName atomically:YES]; + + // write other field types' representations to the file via the current encoding + } else { + [[editData description] writeToFile:tmpFileName + atomically:YES + encoding:encoding + error:NULL]; + } + + id ql = [NSClassFromString(@"QLPreviewPanel") sharedPreviewPanel]; + + // Init QuickLook + [[ql delegate] setDelegate:self]; + [ql setURLs:[NSArray arrayWithObject: + [NSURL fileURLWithPath:tmpFileName]] currentIndex:0 preservingDisplayState:YES]; + // TODO: No interaction with iChat and iPhoto due to .scriptSuite warning: + // for superclass of class 'MainController' in suite 'Sequel Pro': 'NSCoreSuite.NSAbstractObject' is not a valid class name. + [ql setShowsAddToiPhotoButton:NO]; + [ql setShowsiChatTheaterButton:NO]; + // Since we are inside of editSheet we have to avoid full-screen zooming + // otherwise QuickLook hangs + [ql setShowsFullscreenButton:NO]; + [ql setEnableDragNDrop:NO]; + // Order out QuickLook with animation effect according to self:previewPanel:frameForURL: + [ql makeKeyAndOrderFrontWithEffect:2]; // 1 = fade in + + // quickLookCloseMarker == 1 break the modal session + quickLookCloseMarker = 0; + + [editSheetProgressBar stopAnimation:self]; + + // Run QuickLook in its own modal seesion for event handling + NSModalSession session = [NSApp beginModalSessionForWindow:ql]; + for (;;) { + // Conditions for closing QuickLook + if ([NSApp runModalSession:session] != NSRunContinuesResponse + || quickLookCloseMarker == 1 + || ![ql isVisible]) + break; + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode + beforeDate:[NSDate distantFuture]]; + + } + [NSApp endModalSession:session]; + + // Remove temp file after closing the sheet to allow double-click event at the QuickLook preview. + // The afterDelay: time is a kind of dummy, because after double-clicking the model session loop + // will break (ql not visible) and returns the event handling back to the editSheet which by itself + // blocks the execution of removeQuickLooksTempFile: until the editSheet is closed. + [self performSelector:@selector(removeQuickLooksTempFile:) withObject:tmpFileName afterDelay:2]; + + // [[NSFileManager defaultManager] removeItemAtPath:tmpFileName error:NULL]; + + } + +} + +- (void)removeQuickLooksTempFile:(NSString*)aPath +{ + [[NSFileManager defaultManager] removeItemAtPath:aPath error:NULL]; +} + +// This is the delegate method +// It should return the frame for the item represented by the URL +// If an empty frame is returned then the panel will fade in/out instead +- (NSRect)previewPanel:(NSPanel*)panel frameForURL:(NSURL*)URL +{ + + // Close modal session defined in invokeQuickLookOfType: + // if user closes the QuickLook view + quickLookCloseMarker = 1; + + // Return the App's middle point + NSRect mwf = [[NSApp mainWindow] frame]; + return NSMakeRect( + mwf.origin.x+mwf.size.width/2, + mwf.origin.y+mwf.size.height/2, + 5, 5); + +} + +-(void)processPasteImageData +{ + editSheetWillBeInitialized = YES; + + NSImage *image = nil; + + image = [[[NSImage alloc] initWithPasteboard:[NSPasteboard generalPasteboard]] autorelease]; + if (image) { + + if (nil != editData) [editData release]; + + [editImage setImage:image]; + + editData = [[NSData alloc] initWithData:[image TIFFRepresentationUsingCompression:NSTIFFCompressionLZW factor:1]]; + + NSString *contents = [[NSString alloc] initWithData:editData encoding:encoding]; + if (contents == nil) + contents = [[NSString alloc] initWithData:editData encoding:NSASCIIStringEncoding]; + + // Set the string contents and hex representation + if(contents) + [editTextView setString:contents]; + if(![[hexTextView string] isEqualToString:@""]) + [hexTextView setString:[editData dataToFormattedHexString]]; + + [contents release]; + + } + + editSheetWillBeInitialized = NO; +} +/* + * Invoked when the imageView in the connection sheet has the contents deleted + * or a file dragged and dropped onto it. + */ +- (void)processUpdatedImageData:(NSData *)data +{ + + editSheetWillBeInitialized = YES; + + if (nil != editData) [editData release]; + + // If the image was not processed, set a blank string as the contents of the edit and hex views. + if ( data == nil ) { + editData = [[NSData alloc] init]; + [editTextView setString:@""]; + [hexTextView setString:@""]; + editSheetWillBeInitialized = NO; + return; + } + + // Process the provided image + editData = [[NSData alloc] initWithData:data]; + NSString *contents = [[NSString alloc] initWithData:data encoding:encoding]; + if (contents == nil) + contents = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]; + + // Set the string contents and hex representation + if(contents) + [editTextView setString:contents]; + if(![[hexTextView string] isEqualToString:@""]) + [hexTextView setString:[editData dataToFormattedHexString]]; + + [contents release]; + editSheetWillBeInitialized = NO; +} + +- (IBAction)dropImage:(id)sender +{ + + // If the image was deleted, set a blank string as the contents of the edit and hex views. + // The actual dropped image processing is handled by processUpdatedImageData:. + if ( [editImage image] == nil ) { + if (nil != editData) [editData release]; + editData = [[NSData alloc] init]; + [editTextView setString:@""]; + [hexTextView setString:@""]; + return; + } +} + + + +@end diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj index 07f4455d..61b6d333 100644 --- a/sequel-pro.xcodeproj/project.pbxproj +++ b/sequel-pro.xcodeproj/project.pbxproj @@ -156,6 +156,7 @@ BC1847EA0FE6EC8400094BFB /* SPEditSheetTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC1847E90FE6EC8400094BFB /* SPEditSheetTextView.m */; }; BC2C16D40FEBEDF10003993B /* SPDataAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = BC2C16D30FEBEDF10003993B /* SPDataAdditions.m */; }; BC2C8E220FA8C2DB008468C7 /* sequel-pro-mysql-help-template.html in Resources */ = {isa = PBXBuildFile; fileRef = BC2C8E210FA8C2DB008468C7 /* sequel-pro-mysql-help-template.html */; }; + BC99296E0FF3D001008B79AB /* SPFieldEditor.m in Sources */ = {isa = PBXBuildFile; fileRef = BC99296D0FF3D001008B79AB /* SPFieldEditor.m */; }; BCD0AD490FBBFC340066EA5C /* SPSQLTokenizer.l in Sources */ = {isa = PBXBuildFile; fileRef = BCD0AD480FBBFC340066EA5C /* SPSQLTokenizer.l */; }; /* End PBXBuildFile section */ @@ -199,7 +200,7 @@ isa = PBXContainerItemProxy; containerPortal = B5538CD70FF279AD00219803 /* BWToolkit.xcodeproj */; proxyType = 1; - remoteGlobalIDString = 8D1AC9600486D14A00FE50C9 /* BWToolkit */; + remoteGlobalIDString = 8D1AC9600486D14A00FE50C9; remoteInfo = BWToolkit; }; /* End PBXContainerItemProxy section */ @@ -445,6 +446,8 @@ BC2C16D20FEBEDF10003993B /* SPDataAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPDataAdditions.h; sourceTree = "<group>"; }; BC2C16D30FEBEDF10003993B /* SPDataAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPDataAdditions.m; sourceTree = "<group>"; }; BC2C8E210FA8C2DB008468C7 /* sequel-pro-mysql-help-template.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "sequel-pro-mysql-help-template.html"; sourceTree = "<group>"; }; + BC99296C0FF3D001008B79AB /* SPFieldEditor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPFieldEditor.h; sourceTree = "<group>"; }; + BC99296D0FF3D001008B79AB /* SPFieldEditor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPFieldEditor.m; sourceTree = "<group>"; }; BCD0AD480FBBFC340066EA5C /* SPSQLTokenizer.l */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.lex; path = SPSQLTokenizer.l; sourceTree = "<group>"; }; BCD0AD4A0FBBFC480066EA5C /* SPSQLTokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSQLTokenizer.h; sourceTree = "<group>"; }; /* End PBXFileReference section */ @@ -738,6 +741,8 @@ 5841423E0F97E11000A34B47 /* NoodleLineNumberView.m */, BC1847E80FE6EC8400094BFB /* SPEditSheetTextView.h */, BC1847E90FE6EC8400094BFB /* SPEditSheetTextView.m */, + BC99296C0FF3D001008B79AB /* SPFieldEditor.h */, + BC99296D0FF3D001008B79AB /* SPFieldEditor.m */, ); name = GUI; sourceTree = "<group>"; @@ -1248,6 +1253,7 @@ 29A1B7E50FD1293A000B88E8 /* SPPrintAccessory.m in Sources */, BC1847EA0FE6EC8400094BFB /* SPEditSheetTextView.m in Sources */, BC2C16D40FEBEDF10003993B /* SPDataAdditions.m in Sources */, + BC99296E0FF3D001008B79AB /* SPFieldEditor.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; |