// GeneratePreviewForURL.m
// sequel-pro
//
// Created by Hans-Jörg Bibiko on Aug 04, 2010
//
// 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
#include
#include
#include
#import
/* -----------------------------------------------------------------------------
Generate a preview for file
This function's job is to create preview for designated file
----------------------------------------------------------------------------- */
static char base64encodingTable[64] = {
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' };
@interface NSData (QLDataAdditions)
- (NSString *)base64EncodingWithLineLength:(NSUInteger)lineLength;
@end
@implementation NSData (QLDataAdditions)
/*
* Derived from http://colloquy.info/project/browser/trunk/NSDataAdditions.m?rev=1576
* Created by khammond on Mon Oct 29 2001.
* Formatted by Timothy Hatcher on Sun Jul 4 2004.
* Copyright (c) 2001 Kyle Hammond. All rights reserved.
* Original development by Dave Winer.
*
* Convert self to a base64 encoded NSString
*/
- (NSString *) base64EncodingWithLineLength:(NSUInteger)lineLength {
const unsigned char *bytes = [self bytes];
NSUInteger ixtext = 0;
NSUInteger lentext = [self length];
NSInteger ctremaining = 0;
unsigned char inbuf[3], outbuf[4];
short i = 0;
short charsonline = 0, ctcopy = 0;
NSUInteger ix = 0;
NSMutableString *base64 = [NSMutableString stringWithCapacity:lentext];
while(1) {
ctremaining = lentext - ixtext;
if( ctremaining <= 0 ) break;
for( i = 0; i < 3; i++ ) {
ix = ixtext + i;
if( ix < lentext ) inbuf[i] = bytes[ix];
else inbuf [i] = 0;
}
outbuf [0] = (inbuf [0] & 0xFC) >> 2;
outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4);
outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6);
outbuf [3] = inbuf [2] & 0x3F;
ctcopy = 4;
switch( ctremaining ) {
case 1:
ctcopy = 2;
break;
case 2:
ctcopy = 3;
break;
}
for( i = 0; i < ctcopy; i++ )
[base64 appendFormat:@"%c", base64encodingTable[outbuf[i]]];
for( i = ctcopy; i < 4; i++ )
[base64 appendFormat:@"%c",'='];
ixtext += 3;
charsonline += 4;
if( lineLength > 0 ) {
if (charsonline >= lineLength) {
charsonline = 0;
[base64 appendString:@"\n"];
}
}
}
return base64;
}
@end
@interface NSString (QLStringAdditions)
+ (NSString *)stringForByteSize:(long long)byteSize;
@end
@implementation NSString (QLStringAdditions)
/*
* Returns a human readable version string of the supplied byte size.
*/
+ (NSString *)stringForByteSize:(long long)byteSize
{
CGFloat size = byteSize;
NSNumberFormatter *numberFormatter = [[[NSNumberFormatter alloc] init] autorelease];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
if (size < 1023) {
[numberFormatter setFormat:@"#,##0 B"];
return [numberFormatter stringFromNumber:[NSNumber numberWithInteger:size]];
}
size = (size / 1024);
if (size < 1023) {
[numberFormatter setFormat:@"#,##0.0 KiB"];
return [numberFormatter stringFromNumber:[NSNumber numberWithDouble:size]];
}
size = (size / 1024);
if (size < 1023) {
[numberFormatter setFormat:@"#,##0.0 MiB"];
return [numberFormatter stringFromNumber:[NSNumber numberWithDouble:size]];
}
size = (size / 1024);
if (size < 1023) {
[numberFormatter setFormat:@"#,##0.0 GiB"];
return [numberFormatter stringFromNumber:[NSNumber numberWithDouble:size]];
}
size = (size / 1024);
[numberFormatter setFormat:@"#,##0.0 TiB"];
return [numberFormatter stringFromNumber:[NSNumber numberWithDouble:size]];
}
@end
OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options)
{
NSURL *myURL = (NSURL *)url;
NSString *urlExtension = [[[myURL path] pathExtension] lowercaseString];
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSError *templateReadError = nil;
NSImage *iconImage = [[NSWorkspace sharedWorkspace] iconForFile:[myURL path]];
NSMutableString *html;
NSString *template = nil;
if (false == QLPreviewRequestIsCancelled(preview)) {
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[myURL path] error:nil];
if([urlExtension isEqualToString:@"spf"]) {
NSError *readError = nil;
NSString *convError = nil;
NSPropertyListFormat format;
NSDictionary *spf = nil;
NSData *pData = [NSData dataWithContentsOfFile:[myURL path] options:NSUncachedRead error:&readError];
spf = [[NSPropertyListSerialization propertyListFromData:pData
mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError] retain];
if(!spf || readError != nil || [convError length] || !(format == NSPropertyListXMLFormat_v1_0 || format == NSPropertyListBinaryFormat_v1_0)) {
if(spf) [spf release];
[pool release];
return noErr;
}
if([[spf objectForKey:@"format"] isEqualToString:@"connection"]) {
template = [NSString stringWithContentsOfFile:[[NSBundle bundleWithIdentifier:@"com.google.code.sequel-pro.qlgenerator"] pathForResource:@"SPQLPluginConnectionTemplate" ofType:@"html"]
encoding:NSUTF8StringEncoding error:&templateReadError];
if (template == nil || ![template length] || templateReadError != nil) {
[pool release];
return noErr;
}
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setLocale:[NSLocale currentLocale]];
NSString *name = @"••••";
NSString *host = @"••••";
NSString *user = @"••••";
NSString *database = @"••••";
NSString *autoConnect = ([[spf objectForKey:@"auto_connect"] boolValue]) ? @"checked" : @"";
if([[spf objectForKey:@"data"] isKindOfClass:[NSDictionary class]]) {
if([[spf objectForKey:@"data"] objectForKey:@"connection"] && [[[spf objectForKey:@"data"] objectForKey:@"connection"] objectForKey:@"name"])
name = [[[spf objectForKey:@"data"] objectForKey:@"connection"] objectForKey:@"name"];
else
name = @"";
if([[spf objectForKey:@"data"] objectForKey:@"connection"] && [[[spf objectForKey:@"data"] objectForKey:@"connection"] objectForKey:@"host"])
host = [[[spf objectForKey:@"data"] objectForKey:@"connection"] objectForKey:@"host"];
else
host = @"";
if([[spf objectForKey:@"data"] objectForKey:@"connection"] && [[[spf objectForKey:@"data"] objectForKey:@"connection"] objectForKey:@"user"])
user = [[[spf objectForKey:@"data"] objectForKey:@"connection"] objectForKey:@"user"];
else
user = @"";
if([[spf objectForKey:@"data"] objectForKey:@"connection"] && [[[spf objectForKey:@"data"] objectForKey:@"connection"] objectForKey:@"database"])
database = [[[spf objectForKey:@"data"] objectForKey:@"connection"] objectForKey:@"database"];
else
database = @"";
}
// compose the html
html = [[NSMutableString alloc] initWithString:[NSString stringWithFormat:template,
[[iconImage TIFFRepresentationUsingCompression:NSTIFFCompressionJPEG factor:0.01] base64EncodingWithLineLength:0],
[spf objectForKey:@"rdbms_type"],
[spf objectForKey:@"rdbms_version"],
[name stringByReplacingOccurrencesOfString:@" " withString:@" "],
[host stringByReplacingOccurrencesOfString:@" " withString:@" "],
[user stringByReplacingOccurrencesOfString:@" " withString:@" "],
[database stringByReplacingOccurrencesOfString:@" " withString:@" "],
[NSString stringForByteSize:[[fileAttributes objectForKey:NSFileSize] longLongValue]],
[dateFormatter stringFromDate:[fileAttributes fileModificationDate]],
autoConnect
]];
[dateFormatter release];
[spf release];
}
else if([[spf objectForKey:@"format"] isEqualToString:@"content filters"]) {
template = [NSString stringWithContentsOfFile:[[NSBundle bundleWithIdentifier:@"com.google.code.sequel-pro.qlgenerator"] pathForResource:@"SPQLPluginContentFiltersTemplate" ofType:@"html"]
encoding:NSUTF8StringEncoding error:&templateReadError];
if (template == nil || ![template length] || templateReadError != nil) {
[pool release];
return noErr;
}
// compose the html
html = [[NSMutableString alloc] initWithString:[NSString stringWithFormat:template,
[[iconImage TIFFRepresentationUsingCompression:NSTIFFCompressionJPEG factor:0.01] base64EncodingWithLineLength:0],
[NSString stringWithContentsOfFile:[myURL path] encoding:NSUTF8StringEncoding error:nil]
]];
}
else if([[spf objectForKey:@"format"] isEqualToString:@"query favorites"]) {
template = [NSString stringWithContentsOfFile:[[NSBundle bundleWithIdentifier:@"com.google.code.sequel-pro.qlgenerator"] pathForResource:@"SPQLPluginQueryFavoritesTemplate" ofType:@"html"]
encoding:NSUTF8StringEncoding error:&templateReadError];
if (template == nil || ![template length] || templateReadError != nil) {
[pool release];
return noErr;
}
// compose the html
html = [[NSMutableString alloc] initWithString:[NSString stringWithFormat:template,
[[iconImage TIFFRepresentationUsingCompression:NSTIFFCompressionJPEG factor:0.01] base64EncodingWithLineLength:0],
[NSString stringWithContentsOfFile:[myURL path] encoding:NSUTF8StringEncoding error:nil]
]];
}
}
else if([urlExtension isEqualToString:@"sql"]) {
template = [NSString stringWithContentsOfFile:[[NSBundle bundleWithIdentifier:@"com.google.code.sequel-pro.qlgenerator"] pathForResource:@"SPQLPluginSQLTemplate" ofType:@"html"]
encoding:NSUTF8StringEncoding error:&templateReadError];
if (template == nil || ![template length] || templateReadError != nil) {
[pool release];
return noErr;
}
// compose the html
if(fileAttributes)
{
NSNumber *filesize = [fileAttributes objectForKey:NSFileSize];
// catch large files since Finder blocks
if([filesize unsignedLongValue] > 6000000) {
html = [[NSMutableString alloc] initWithString:[NSString stringWithFormat:template,
[[iconImage TIFFRepresentationUsingCompression:NSTIFFCompressionJPEG factor:0.01] base64EncodingWithLineLength:0],
[NSString stringForByteSize:[[fileAttributes objectForKey:NSFileSize] longLongValue]],
@"... SQL ..."
]];
} else {
html = [[NSMutableString alloc] initWithString:[NSString stringWithFormat:template,
[[iconImage TIFFRepresentationUsingCompression:NSTIFFCompressionJPEG factor:0.01] base64EncodingWithLineLength:0],
[NSString stringForByteSize:[[fileAttributes objectForKey:NSFileSize] longLongValue]],
[NSString stringWithContentsOfFile:[myURL path] encoding:NSUTF8StringEncoding error:nil]
]];
}
} else {
html = [[NSMutableString alloc] initWithString:[NSString stringWithFormat:template,
[[iconImage TIFFRepresentationUsingCompression:NSTIFFCompressionJPEG factor:0.01] base64EncodingWithLineLength:0],
[NSString stringForByteSize:[[fileAttributes objectForKey:NSFileSize] longLongValue]],
[NSString stringWithContentsOfFile:[myURL path] encoding:NSUTF8StringEncoding error:nil]
]];
}
}
CFDictionaryRef properties = (CFDictionaryRef)[NSDictionary dictionary];
QLPreviewRequestSetDataRepresentation(preview,
(CFDataRef)[html dataUsingEncoding:NSUTF8StringEncoding],
kUTTypeHTML,
properties
);
[html release];
}
[pool release];
return noErr;
}
void CancelPreviewGeneration(void* thisInterface, QLPreviewRequestRef preview)
{
// implement only if supported
}