From 6a8f495318c936c82af17197210b2f702b498f6f Mon Sep 17 00:00:00 2001 From: Nathan Kinsinger Date: Sun, 21 Mar 2010 15:52:46 -0600 Subject: [PATCH] Add support for QuickLook in 10.6+ In 10.6 Apple removed the private QL API that existed in 10.5 and added a new public API. However they did not port the new API back to 10.5 so we have to do some work to get it working in both. This patch has GitX choose the correct version at run time. - The delegate code is based on Apple's QuickLookDownloader example project - added three of the public API methods to CWQuickLook.h to avoid warnings about unknown method calls - In ApplicationController try to load the public API first then load the private one if it fails - Created PBQLTextView, a subclass of NSTextView to allow the space key event to toggle the preview panel - PBGitHistoryView.xib: - set the text view's class to PBQLTextView - connected the history controller to the controller outlet - bound the quick look button's enabled binding to File's Owner.selectedCommitDetailsIndex - added "Quick Look" to the quick look button's tooltip - The commit list table view toggles the panel if the tree view is active - changed name of the toggle IBAction method which caused MainMenu.xib and PBGitHistoryView.xib to update --- ApplicationController.m | 5 +- CWQuickLook.h | 7 ++ English.lproj/MainMenu.xib | 20 ++++- GitX.xcodeproj/project.pbxproj | 6 ++ PBCommitList.m | 13 ++-- PBGitHistoryController.h | 4 +- PBGitHistoryController.m | 138 +++++++++++++++++++++++++++------ PBGitHistoryView.xib | 83 ++++++++++++++++---- PBQLOutlineView.m | 2 +- PBQLTextView.h | 19 +++++ PBQLTextView.m | 25 ++++++ 11 files changed, 272 insertions(+), 50 deletions(-) create mode 100644 PBQLTextView.h create mode 100644 PBQLTextView.m diff --git a/ApplicationController.m b/ApplicationController.m index ddf66c2..d964bf6 100644 --- a/ApplicationController.m +++ b/ApplicationController.m @@ -28,8 +28,9 @@ #endif if(self = [super init]) { - if(![[NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/QuickLookUI.framework"] load]) - NSLog(@"Could not load QuickLook"); + if(![[NSBundle bundleWithPath:@"/System/Library/Frameworks/Quartz.framework/Frameworks/QuickLookUI.framework"] load]) + if(![[NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/QuickLookUI.framework"] load]) + NSLog(@"Could not load QuickLook"); self.cliProxy = [PBCLIProxy new]; } diff --git a/CWQuickLook.h b/CWQuickLook.h index df1de9c..21d0315 100644 --- a/CWQuickLook.h +++ b/CWQuickLook.h @@ -1,5 +1,12 @@ @interface QLPreviewPanel : NSPanel + (id)sharedPreviewPanel; + +// part of the public QL API ++ (BOOL)sharedPreviewPanelExists; +- (void)reloadData; +- (void)setDataSource:(id)source; + +// the private QL API + (id)_previewPanel; + (BOOL)isSharedPreviewPanelLoaded; - (id)initWithContentRect:(struct _NSRect)fp8 styleMask:(unsigned int)fp24 backing:(unsigned int)fp28 defer:(BOOL)fp32; diff --git a/English.lproj/MainMenu.xib b/English.lproj/MainMenu.xib index c35b2c1..bec5dd0 100644 --- a/English.lproj/MainMenu.xib +++ b/English.lproj/MainMenu.xib @@ -3,16 +3,15 @@ 1050 10C540 - 759 + 762 1038.25 458.00 com.apple.InterfaceBuilder.CocoaPlugin - 759 + 762 YES - YES @@ -2723,7 +2722,7 @@ showAddRemoteSheet: showCommitsFromTree: showInFinderAction: - toggleQuickView: + toggleQLPreviewPanel: YES @@ -3888,5 +3887,18 @@ YES ../GitX.xcodeproj 3 + + YES + + YES + NSMenuCheckmark + NSMenuMixedState + + + YES + {9, 8} + {7, 2} + + diff --git a/GitX.xcodeproj/project.pbxproj b/GitX.xcodeproj/project.pbxproj index 7d0196f..34a4edb 100644 --- a/GitX.xcodeproj/project.pbxproj +++ b/GitX.xcodeproj/project.pbxproj @@ -70,6 +70,7 @@ D8A4BD071134AD2900E92D51 /* CherryPickTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = D8A4BD041134AD2900E92D51 /* CherryPickTemplate.png */; }; D8A4BD081134AD2900E92D51 /* MergeTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = D8A4BD051134AD2900E92D51 /* MergeTemplate.png */; }; D8A4BD091134AD2900E92D51 /* RebaseTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = D8A4BD061134AD2900E92D51 /* RebaseTemplate.png */; }; + D8E105471157C18200FC28A4 /* PBQLTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E105461157C18200FC28A4 /* PBQLTextView.m */; }; D8E3B2B810DC9FB2001096A3 /* ScriptingBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8E3B2B710DC9FB2001096A3 /* ScriptingBridge.framework */; }; D8E3B34D10DCA958001096A3 /* PBCreateTagSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E3B34C10DCA958001096A3 /* PBCreateTagSheet.m */; }; D8FDD9F711432A12005647F6 /* PBCloneRepositoryPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8FDD9F511432A12005647F6 /* PBCloneRepositoryPanel.xib */; }; @@ -286,6 +287,8 @@ D8A4BD051134AD2900E92D51 /* MergeTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = MergeTemplate.png; path = Images/MergeTemplate.png; sourceTree = ""; }; D8A4BD061134AD2900E92D51 /* RebaseTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = RebaseTemplate.png; path = Images/RebaseTemplate.png; sourceTree = ""; }; D8C1B77210E875CF009B7F8B /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/PBRemoteProgressSheet.xib; sourceTree = ""; }; + D8E105451157C18200FC28A4 /* PBQLTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBQLTextView.h; sourceTree = ""; }; + D8E105461157C18200FC28A4 /* PBQLTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBQLTextView.m; sourceTree = ""; }; D8E3B2B710DC9FB2001096A3 /* ScriptingBridge.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ScriptingBridge.framework; path = /System/Library/Frameworks/ScriptingBridge.framework; sourceTree = ""; }; D8E3B34B10DCA958001096A3 /* PBCreateTagSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBCreateTagSheet.h; sourceTree = ""; }; D8E3B34C10DCA958001096A3 /* PBCreateTagSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBCreateTagSheet.m; sourceTree = ""; }; @@ -729,6 +732,8 @@ F53EE3590E06BBA00022B925 /* CWQuickLook.h */, F51308590E0740F2000C8BCD /* PBQLOutlineView.h */, F513085A0E0740F2000C8BCD /* PBQLOutlineView.m */, + D8E105451157C18200FC28A4 /* PBQLTextView.h */, + D8E105461157C18200FC28A4 /* PBQLTextView.m */, 91B103CA0E898EC300C84364 /* PBIconAndTextCell.h */, 91B103CB0E898EC300C84364 /* PBIconAndTextCell.m */, F5140DC70E8A8EB20091E9F3 /* RoundedRectangle.h */, @@ -1175,6 +1180,7 @@ D828A4111127B1C400F09D11 /* PBSourceViewBadge.m in Sources */, D8295D2A1130A1DC00C838E8 /* PBGitHistoryList.m in Sources */, D8295DE01130E43900C838E8 /* PBGitHistoryGrapher.m in Sources */, + D8E105471157C18200FC28A4 /* PBQLTextView.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/PBCommitList.m b/PBCommitList.m index 264b5e2..e2f6f23 100644 --- a/PBCommitList.m +++ b/PBCommitList.m @@ -28,12 +28,15 @@ return; } - if ([character isEqualToString:@" "]) - { - if ([event modifierFlags] & NSShiftKeyMask) - [webView scrollPageUp: self]; + if ([character isEqualToString:@" "]) { + if (controller.selectedCommitDetailsIndex == 0) { + if ([event modifierFlags] & NSShiftKeyMask) + [webView scrollPageUp:self]; + else + [webView scrollPageDown:self]; + } else - [webView scrollPageDown: self]; + [controller toggleQLPreviewPanel:self]; } else if ([character rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"jkcv"]].location == 0) [webController sendKey: character]; diff --git a/PBGitHistoryController.h b/PBGitHistoryController.h index 3b09d61..6dac4d4 100644 --- a/PBGitHistoryController.h +++ b/PBGitHistoryController.h @@ -15,6 +15,7 @@ @class PBGitSidebarController; @class PBGitGradientBarView; @class PBRefController; +@class QLPreviewPanel; @interface PBGitHistoryController : PBViewController { IBOutlet PBRefController *refController; @@ -24,6 +25,7 @@ IBOutlet NSOutlineView* fileBrowser; IBOutlet NSTableView* commitList; IBOutlet PBCollapsibleSplitView *historySplitView; + QLPreviewPanel* previewPanel; IBOutlet PBGitGradientBarView *upperToolbarView; IBOutlet NSButton *mergeButton; @@ -56,7 +58,7 @@ - (void) selectCommit: (NSString*) commit; - (IBAction) refresh: sender; -- (IBAction) toggleQuickView: sender; +- (IBAction) toggleQLPreviewPanel:(id)sender; - (IBAction) openSelectedFile: sender; - (void) updateQuicklookForce: (BOOL) force; diff --git a/PBGitHistoryController.m b/PBGitHistoryController.m index 7f950bf..5d04af9 100644 --- a/PBGitHistoryController.m +++ b/PBGitHistoryController.m @@ -245,36 +245,49 @@ } -- (IBAction) toggleQuickView: sender +- (IBAction) toggleQLPreviewPanel:(id)sender { - id panel = [QLPreviewPanel sharedPreviewPanel]; - if ([panel isOpen]) { - [panel closePanel]; - } else { - [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFrontWithEffect:1]; - [self updateQuicklookForce: YES]; + if ([[QLPreviewPanel sharedPreviewPanel] respondsToSelector:@selector(setDataSource:)]) { + // Public QL API + if ([QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible]) + [[QLPreviewPanel sharedPreviewPanel] orderOut:nil]; + else + [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil]; + } + else { + // Private QL API (10.5 only) + if ([[QLPreviewPanel sharedPreviewPanel] isOpen]) + [[QLPreviewPanel sharedPreviewPanel] closePanel]; + else { + [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFrontWithEffect:1]; + [self updateQuicklookForce:YES]; + } } } -- (void) updateQuicklookForce: (BOOL) force +- (void) updateQuicklookForce:(BOOL)force { if (!force && ![[QLPreviewPanel sharedPreviewPanel] isOpen]) return; - - NSArray* selectedFiles = [treeController selectedObjects]; - - if ([selectedFiles count] == 0) - return; - - NSMutableArray* fileNames = [NSMutableArray array]; - for (PBGitTree* tree in selectedFiles) { - NSString* s = [tree tmpFileNameForContents]; - if (s) - [fileNames addObject:[NSURL fileURLWithPath: s]]; + + if ([[QLPreviewPanel sharedPreviewPanel] respondsToSelector:@selector(setDataSource:)]) { + // Public QL API + [previewPanel reloadData]; + } + else { + // Private QL API (10.5 only) + NSArray *selectedFiles = [treeController selectedObjects]; + + NSMutableArray *fileNames = [NSMutableArray array]; + for (PBGitTree *tree in selectedFiles) { + NSString *filePath = [tree tmpFileNameForContents]; + if (filePath) + [fileNames addObject:[NSURL fileURLWithPath:filePath]]; + } + + if ([fileNames count]) + [[QLPreviewPanel sharedPreviewPanel] setURLs:fileNames currentIndex:0 preservingDisplayState:YES]; } - - [[QLPreviewPanel sharedPreviewPanel] setURLs:fileNames currentIndex:0 preservingDisplayState:YES]; - } - (IBAction) refresh: sender @@ -535,6 +548,85 @@ [repository rebaseBranch:headRef onRefish:selectedCommit]; } } - + +#pragma mark - +#pragma mark Quick Look Public API support + +@protocol QLPreviewItem; + +#pragma mark (QLPreviewPanelController) + +- (BOOL) acceptsPreviewPanelControl:(id)panel +{ + return YES; +} + +- (void)beginPreviewPanelControl:(id)panel +{ + // This document is now responsible of the preview panel + // It is allowed to set the delegate, data source and refresh panel. + previewPanel = panel; + [previewPanel setDelegate:self]; + [previewPanel setDataSource:self]; +} + +- (void)endPreviewPanelControl:(id)panel +{ + // This document loses its responsisibility on the preview panel + // Until the next call to -beginPreviewPanelControl: it must not + // change the panel's delegate, data source or refresh it. + previewPanel = nil; +} + +#pragma mark + +- (NSInteger)numberOfPreviewItemsInPreviewPanel:(id)panel +{ + return [[fileBrowser selectedRowIndexes] count]; +} + +- (id )previewPanel:(id)panel previewItemAtIndex:(NSInteger)index +{ + PBGitTree *treeItem = (PBGitTree *)[[treeController selectedObjects] objectAtIndex:index]; + NSURL *previewURL = [NSURL fileURLWithPath:[treeItem tmpFileNameForContents]]; + + return ()previewURL; +} + +#pragma mark + +- (BOOL)previewPanel:(id)panel handleEvent:(NSEvent *)event +{ + // redirect all key down events to the table view + if ([event type] == NSKeyDown) { + [fileBrowser keyDown:event]; + return YES; + } + return NO; +} + +// This delegate method provides the rect on screen from which the panel will zoom. +- (NSRect)previewPanel:(id)panel sourceFrameOnScreenForPreviewItem:(id )item +{ + NSInteger index = [fileBrowser rowForItem:[[treeController selectedNodes] objectAtIndex:0]]; + if (index == NSNotFound) { + return NSZeroRect; + } + + NSRect iconRect = [fileBrowser frameOfCellAtColumn:0 row:index]; + + // check that the icon rect is visible on screen + NSRect visibleRect = [fileBrowser visibleRect]; + + if (!NSIntersectsRect(visibleRect, iconRect)) { + return NSZeroRect; + } + + // convert icon rect to screen coordinates + iconRect = [fileBrowser convertRectToBase:iconRect]; + iconRect.origin = [[fileBrowser window] convertBaseToScreen:iconRect.origin]; + + return iconRect; +} @end diff --git a/PBGitHistoryView.xib b/PBGitHistoryView.xib index 6512013..179a855 100644 --- a/PBGitHistoryView.xib +++ b/PBGitHistoryView.xib @@ -3,7 +3,7 @@ 1050 10C540 - 759 + 762 1038.25 458.00 @@ -15,13 +15,13 @@ YES - 759 - 759 + 762 + 762 YES - + YES @@ -1400,14 +1400,6 @@ 62 - - - toggleQuickView: - - - - 66 - selectedIndex: selectedCommitDetailsIndex @@ -1926,6 +1918,38 @@ 393 + + + toggleQLPreviewPanel: + + + + 395 + + + + controller + + + + 396 + + + + enabled: selectedCommitDetailsIndex + + + + + + enabled: selectedCommitDetailsIndex + enabled + selectedCommitDetailsIndex + 2 + + + 398 + @@ -2480,6 +2504,7 @@ -3.IBPluginDependency 10.IBPluginDependency 11.IBPluginDependency + 12.CustomClassName 12.IBPluginDependency 13.IBPluginDependency 14.IBPluginDependency @@ -2568,6 +2593,7 @@ 46.IBEditorWindowLastContentRect 46.IBPluginDependency 48.IBPluginDependency + 49.IBAttributePlaceholdersKey 49.IBPluginDependency 50.IBPluginDependency 51.IBPluginDependency @@ -2581,6 +2607,7 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + PBQLTextView com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -2737,6 +2764,14 @@ {{627, 791}, {852, 432}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + + ToolTip + + ToolTip + + Quick Look + + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -2762,7 +2797,7 @@ - 393 + 398 @@ -2842,7 +2877,7 @@ showAddRemoteSheet: showCommitsFromTree: showInFinderAction: - toggleQuickView: + toggleQLPreviewPanel: YES @@ -2958,6 +2993,18 @@ PBQLOutlineView.h + + PBQLTextView + NSTextView + + controller + PBGitHistoryController + + + IBProjectSource + PBQLTextView.h + + PBRefController NSObject @@ -3049,6 +3096,10 @@ PBViewController NSViewController + + refresh: + id + IBProjectSource PBViewController.h @@ -3847,7 +3898,9 @@ AddBranchTemplate AddLabelTemplate CherryPickTemplate + DetailViewTemplate MergeTemplate + NSPathTemplate NSQuickLookTemplate RebaseTemplate @@ -3856,7 +3909,9 @@ {20, 12} {23, 12} {18.5143, 12.3429} + {17, 17} {16.4571, 12.3429} + {16, 9} {19, 11} {16.4571, 13.3714} diff --git a/PBQLOutlineView.m b/PBQLOutlineView.m index c3dde23..05f9790 100644 --- a/PBQLOutlineView.m +++ b/PBQLOutlineView.m @@ -28,7 +28,7 @@ - (void) keyDown: (NSEvent *) event { if ([[event characters] isEqualToString:@" "]) { - [controller toggleQuickView:self]; + [controller toggleQLPreviewPanel:self]; return; } diff --git a/PBQLTextView.h b/PBQLTextView.h new file mode 100644 index 0000000..715e74e --- /dev/null +++ b/PBQLTextView.h @@ -0,0 +1,19 @@ +// +// PBQLTextView.h +// GitX +// +// Created by Nathan Kinsinger on 3/22/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import + + +@class PBGitHistoryController; + + +@interface PBQLTextView : NSTextView { + IBOutlet PBGitHistoryController *controller; +} + +@end diff --git a/PBQLTextView.m b/PBQLTextView.m new file mode 100644 index 0000000..fccb3ad --- /dev/null +++ b/PBQLTextView.m @@ -0,0 +1,25 @@ +// +// PBQLTextView.m +// GitX +// +// Created by Nathan Kinsinger on 3/22/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "PBQLTextView.h" +#import "PBGitHistoryController.h" + + +@implementation PBQLTextView + +- (void) keyDown: (NSEvent *) event +{ + if ([[event characters] isEqualToString:@" "]) { + [controller toggleQLPreviewPanel:self]; + return; + } + + [super keyDown:event]; +} + +@end