Skip to content

Commit 73c5603

Browse files
committed
Added thumbnail generation. First complete version. Localization does not yet work.
1 parent f154328 commit 73c5603

File tree

13 files changed

+1515
-603
lines changed

13 files changed

+1515
-603
lines changed

CSVObject.h renamed to CSVDocument.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// CSVObject.h
2+
// CSVDocument.h
33
// QuickLookCSV
44
//
55
// Created by Pascal Pfiffner on 03.07.09.
@@ -10,7 +10,7 @@
1010
#import <Cocoa/Cocoa.h>
1111

1212

13-
@interface CSVObject : NSObject {
13+
@interface CSVDocument : NSObject {
1414
NSString *separator;
1515
NSArray *rows;
1616
NSArray *columnKeys;
@@ -20,9 +20,11 @@
2020
@property (nonatomic, retain) NSArray *rows;
2121
@property (nonatomic, retain) NSArray *columnKeys;
2222

23-
+ (CSVObject *) csvObject;
23+
+ (CSVDocument *) csvDoc;
2424
- (NSUInteger) numRowsFromCSVString:(NSString *)string error:(NSError **)error;
2525
- (NSUInteger) numRowsFromCSVString:(NSString *)string maxRows:(NSUInteger)maxRows error:(NSError **)error;
2626

27+
- (BOOL) isFirstColumn:(NSString *)columnKey;
28+
2729

2830
@end

CSVObject.m renamed to CSVDocument.m

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
//
2-
// CSVObject.m
2+
// CSVDocument.m
33
// QuickLookCSV
44
//
55
// Created by Pascal Pfiffner on 03.07.09.
66
// This sourcecode is released under the Apache License, Version 2.0
77
// http://www.apache.org/licenses/LICENSE-2.0.html
88
//
99

10-
#import "CSVObject.h"
10+
#import "CSVDocument.h"
1111
#import "CSVRowObject.h"
1212

1313

14-
@implementation CSVObject
14+
@implementation CSVDocument
1515

1616
@synthesize separator, rows, columnKeys;
1717

@@ -26,9 +26,9 @@ - (id) init
2626
return self;
2727
}
2828

29-
+ (CSVObject *) csvObject
29+
+ (CSVDocument *) csvDoc
3030
{
31-
return [[[CSVObject alloc] init] autorelease];
31+
return [[[CSVDocument alloc] init] autorelease];
3232
}
3333

3434
- (void) dealloc
@@ -199,6 +199,19 @@ - (NSUInteger) numRowsFromCSVString:(NSString *)string maxRows:(NSUInteger)maxRo
199199

200200
return numRows;
201201
}
202+
#pragma mark -
203+
204+
205+
206+
#pragma mark Document Properties
207+
- (BOOL) isFirstColumn:(NSString *)columnKey
208+
{
209+
if((nil != columnKeys) && ([columnKeys count] > 0)) {
210+
return [columnKey isEqualToString:[columnKeys objectAtIndex:0]];
211+
}
212+
213+
return NO;
214+
}
202215

203216

204217
@end

CSVRowObject.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
+ (CSVRowObject *) row;
2020
+ (CSVRowObject *) rowFromDict:(NSMutableDictionary *)dict;
2121

22-
- (NSString *) columns:(NSArray *)columnKeys separatedByString:(NSString *)sepString;
23-
22+
- (NSString *) columns:(NSArray *)columnKeys combinedByString:(NSString *)sepString;
23+
- (NSString *) columnForKey:(NSString *)columnKey;
2424

2525
@end

CSVRowObject.m

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,27 @@ - (void) dealloc
4343

4444

4545
#pragma mark Returning Columns
46-
- (NSString *) columns:(NSArray *)columnKeys separatedByString:(NSString *)sepString
46+
- (NSString *) columns:(NSArray *)columnKeys combinedByString:(NSString *)sepString
4747
{
4848
NSString *rowString = nil;
4949

50-
if(nil != columnKeys && nil != columns) {
50+
if((nil != columnKeys) && (nil != columns)) {
5151
rowString = [[columns objectsForKeys:columnKeys notFoundMarker:@""] componentsJoinedByString:sepString];
5252
}
5353

5454
return rowString;
5555
}
5656

57+
- (NSString *) columnForKey:(NSString *)columnKey
58+
{
59+
NSString *cellString = nil;
60+
61+
if((nil != columnKey) && (nil != columns)) {
62+
cellString = [columns objectForKey:columnKey];
63+
}
64+
65+
return cellString;
66+
}
67+
5768

5869
@end

English.lproj/Localizable.strings

88 Bytes
Binary file not shown.

GeneratePreviewForURL.m

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#include <CoreServices/CoreServices.h>
1212
#include <QuickLook/QuickLook.h>
1313
#import <Cocoa/Cocoa.h>
14-
#import "CSVObject.h"
14+
#import "CSVDocument.h"
1515
#import "CSVRowObject.h"
1616

1717
#define MAX_ROWS 200
@@ -33,7 +33,8 @@ OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview,
3333
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3434
NSURL *myURL = (NSURL *)url;
3535

36-
// Load document data; guess file encoding using `file --brief --mime` ? Not for now...
36+
// Load document data using NSStrings house methods
37+
// For huge files, maybe guess file encoding using `file --brief --mime` and use NSFileHandle? Not for now...
3738
NSStringEncoding stringEncoding;
3839
NSString *fileString = [NSString stringWithContentsOfURL:myURL usedEncoding:&stringEncoding error:&theErr];
3940

@@ -56,13 +57,13 @@ OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview,
5657

5758
// Parse the data if still interested in the preview
5859
if(false == QLPreviewRequestIsCancelled(preview)) {
59-
CSVObject *csvObject = [CSVObject csvObject];
60-
NSUInteger numRowsParsed = [csvObject numRowsFromCSVString:fileString maxRows:MAX_ROWS error:NULL];
60+
CSVDocument *csvDoc = [CSVDocument csvDoc];
61+
NSUInteger numRowsParsed = [csvDoc numRowsFromCSVString:fileString maxRows:MAX_ROWS error:NULL];
6162

6263

6364
// Create HTML of the data if still interested in the preview
6465
if(false == QLPreviewRequestIsCancelled(preview)) {
65-
NSBundle *myBundle = [NSBundle bundleForClass:[CSVObject class]];
66+
NSBundle *myBundle = [NSBundle bundleForClass:[CSVDocument class]];
6667

6768
NSString *cssPath = [myBundle pathForResource:@"Style" ofType:@"css"];
6869
NSString *css = [[NSString alloc] initWithContentsOfFile:cssPath encoding:NSUTF8StringEncoding error:NULL];
@@ -81,14 +82,19 @@ OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview,
8182
[css release];
8283
}
8384
[html appendFormat:@"</style></head><body><h1>%@</h1>", fileName];
84-
[html appendFormat:@"<div class=\"file_info\">%@</div>", path];
85-
[html appendFormat:@"<div class=\"file_info\">%s, %s</div><table>", formatFilesize([[fileAttributes objectForKey:NSFileSize] floatValue]), humanReadableFileEncoding(stringEncoding)];
85+
//[html appendFormat:@"<div class=\"file_info\">%@</div>", path];
86+
[html appendFormat:@"<div class=\"file_info\"><b>%s</b>, %s, <b>%i</b> %@</div><table>",
87+
formatFilesize([[fileAttributes objectForKey:NSFileSize] floatValue]),
88+
humanReadableFileEncoding(stringEncoding),
89+
[csvDoc.columnKeys count],
90+
(1 == [csvDoc.columnKeys count]) ? NSLocalizedString(@"column", nil) : NSLocalizedString(@"columns", nil)
91+
];
8692

8793
// add the table rows
8894
BOOL altRow = NO;
89-
for(CSVRowObject *row in csvObject.rows) {
95+
for(CSVRowObject *row in csvDoc.rows) {
9096
[html appendFormat:@"<tr%@><td>", altRow ? @" class=\"alt_row\"" : @""];
91-
[html appendString:[row columns:csvObject.columnKeys separatedByString:@"</td><td>"]];
97+
[html appendString:[row columns:csvDoc.columnKeys combinedByString:@"</td><td>"]];
9298
[html appendString:@"</td></tr>\n"];
9399

94100
altRow = !altRow;
@@ -98,8 +104,8 @@ OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview,
98104

99105
// not all rows were parsed, show hint
100106
if(numRowsParsed > MAX_ROWS) {
101-
NSString *rowsHint = [NSString stringWithFormat:@"Only the first %i rows are being displayed", MAX_ROWS];
102-
[html appendFormat:@"<div class=\"truncated_rows\">%@</div>", [myBundle localizedStringForKey:rowsHint value:rowsHint table:nil]];
107+
NSString *rowsHint = [NSString stringWithFormat:NSLocalizedString(@"Only the first %i rows are being displayed", nil), MAX_ROWS];
108+
[html appendFormat:@"<div class=\"truncated_rows\">%@</div>", rowsHint];
103109
}
104110
[html appendString:@"</html>"];
105111

GenerateThumbnailForURL.m

Lines changed: 126 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#include <CoreFoundation/CoreFoundation.h>
22
#include <CoreServices/CoreServices.h>
33
#include <QuickLook/QuickLook.h>
4+
#import <Cocoa/Cocoa.h>
5+
#import "CSVDocument.h"
6+
#import "CSVRowObject.h"
7+
48

59
/* -----------------------------------------------------------------------------
610
Generate a thumbnail for file
@@ -10,11 +14,130 @@
1014

1115
OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize)
1216
{
13-
#warning To complete your generator please implement the function GenerateThumbnailForURL in GenerateThumbnailForURL.c
14-
return noErr;
17+
NSError *theErr = nil;
18+
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
19+
NSURL *myURL = (NSURL *)url;
20+
21+
// Load document data using NSStrings house methods
22+
// For huge files, maybe guess file encoding using `file --brief --mime` and use NSFileHandle? Not for now...
23+
NSStringEncoding stringEncoding;
24+
NSString *fileString = [NSString stringWithContentsOfURL:myURL usedEncoding:&stringEncoding error:&theErr];
25+
26+
// We could not open the file, probably unknown encoding; try ISO-8859-1
27+
if(nil == fileString) {
28+
stringEncoding = NSISOLatin1StringEncoding;
29+
fileString = [NSString stringWithContentsOfURL:myURL encoding:stringEncoding error:&theErr];
30+
31+
// Still no success, give up
32+
if(nil == fileString) {
33+
if(nil != theErr) {
34+
NSLog(@"Error opening the file: %@", theErr);
35+
}
36+
37+
[pool release];
38+
return noErr;
39+
}
40+
}
41+
42+
43+
// Parse the data if still interested in the thumbnail
44+
if(false == QLThumbnailRequestIsCancelled(thumbnail)) {
45+
CGFloat thumbnailSize = 256.0;
46+
CGFloat fontSize = 12.0;
47+
CGFloat rowHeight = 18.0;
48+
NSUInteger numRows = ceilf(thumbnailSize / rowHeight);
49+
50+
CSVDocument *csvDoc = [CSVDocument csvDoc];
51+
[csvDoc numRowsFromCSVString:fileString maxRows:numRows error:NULL];
52+
53+
54+
// Draw an icon if still interested in the thumbnail
55+
if(false == QLThumbnailRequestIsCancelled(thumbnail)) {
56+
CGRect myBounds = CGRectMake(0.0, 0.0, thumbnailSize, thumbnailSize);
57+
CGContextRef context = QLThumbnailRequestCreateContext(thumbnail, myBounds.size, false, NULL);
58+
59+
// Draw a mini table
60+
if(context) {
61+
CGContextSaveGState(context);
62+
63+
// Flip CoreGraphics coordinate system
64+
CGContextScaleCTM(context, 1.0, -1.0);
65+
CGContextTranslateCTM(context, 0, -myBounds.size.height);
66+
67+
// Create colors
68+
CGColorRef borderColor = CGColorCreateGenericRGB(0.67, 0.67, 0.67, 1.0);
69+
CGColorRef rowBG = CGColorCreateGenericRGB(1.0, 1.0, 1.0, 1.0);
70+
CGColorRef altRowBG = CGColorCreateGenericRGB(0.9, 0.9, 0.9, 1.0);
71+
72+
CGFloat borderWidth = 1.0;
73+
74+
// We use NSGraphicsContext for the strings due to easier string drawing :P
75+
NSGraphicsContext* nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:(void *)context flipped:YES];
76+
[NSGraphicsContext setCurrentContext:nsContext];
77+
if(nsContext) {
78+
NSFont *myFont = [NSFont systemFontOfSize:fontSize];
79+
NSColor *blackColor = [NSColor blackColor];
80+
NSDictionary *stringAttributes = [NSDictionary dictionaryWithObjectsAndKeys:myFont, NSFontAttributeName,
81+
blackColor, NSForegroundColorAttributeName, nil];
82+
83+
CGFloat textXPadding = 5.0;
84+
CGFloat cellX = -2 * textXPadding;
85+
CGFloat maxCellStringWidth = 0.0;
86+
87+
// We loop each cell, row by row for each column
88+
for(NSString *colKey in csvDoc.columnKeys) {
89+
cellX += maxCellStringWidth + 2 * textXPadding;
90+
CGRect rowRect = CGRectMake(cellX, 0.0, myBounds.size.width - cellX, rowHeight);
91+
maxCellStringWidth = 0.0;
92+
BOOL altRow = NO;
93+
94+
// loop rows
95+
for(CSVRowObject *row in csvDoc.rows) {
96+
CGContextSetFillColorWithColor(context, altRow ? altRowBG : rowBG);
97+
CGContextFillRect(context, rowRect);
98+
99+
if(![csvDoc isFirstColumn:colKey]) {
100+
CGContextMoveToPoint(context, cellX + borderWidth / 2, rowRect.origin.y);
101+
CGContextAddLineToPoint(context, cellX + borderWidth / 2, rowRect.origin.y + rowRect.size.height);
102+
CGContextSetStrokeColorWithColor(context, borderColor);
103+
CGContextStrokePath(context);
104+
}
105+
106+
// Draw text
107+
NSRect textRect = NSRectFromCGRect(rowRect);
108+
textRect.size.width -= 2 * textXPadding;
109+
textRect.origin.x += textXPadding;
110+
NSString *cellString = [row columnForKey:colKey];
111+
NSSize cellSize = [cellString sizeWithAttributes:stringAttributes];
112+
[cellString drawWithRect:textRect options:NSStringDrawingUsesLineFragmentOrigin attributes:stringAttributes];
113+
114+
if(cellSize.width > maxCellStringWidth) {
115+
maxCellStringWidth = cellSize.width;
116+
}
117+
altRow = !altRow;
118+
rowRect.origin.y += rowHeight;
119+
}
120+
}
121+
}
122+
123+
CGColorRelease(borderColor);
124+
CGColorRelease(rowBG);
125+
CGColorRelease(altRowBG);
126+
127+
// Clean up
128+
CGContextRestoreGState(context);
129+
QLThumbnailRequestFlushContext(thumbnail, context);
130+
CFRelease(context);
131+
}
132+
}
133+
}
134+
135+
// Clean up
136+
[pool release];
137+
return noErr;
15138
}
16139

17140
void CancelThumbnailGeneration(void* thisInterface, QLThumbnailRequestRef thumbnail)
18141
{
19-
// implement only if supported
20142
}
143+

German.lproj/Localizable.strings

-274 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)