forked from quicksilver/Plugins
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathQSServiceAction.m
222 lines (177 loc) · 8.37 KB
/
QSServiceAction.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#import "QSServiceAction.h"
#import <QSCore/QSCore.h>
#import <QSCore/QSObject_Pasteboard.h>
#import <QSCore/QSLibrarian.h>
#import <QSCore/QSExecutor.h>
#import <QSFoundation/NSWorkspace_BLTRExtensions.h>
#define NSServicesKey @"NSServices"
#define NSMenuItemKey @"NSMenuItem"
#define NSMenuItemDisabledKey @"NSMenuItem (Disabled)"
#define NSSendTypesKey @"NSSendTypes"
#define NSReturnTypesKey @"NSReturnTypes"
#define DefaultKey @"default"
#define NSKeyEquivalentKey @"NSKeyEquivalent"
#define infoPath @"Contents/Info.plist"
NSMutableArray *servicesForBundle(NSString *path) {
if (path) {
NSString *dictPath = [path stringByAppendingPathComponent:infoPath];
NSMutableDictionary *infoDictionary = [NSMutableDictionary dictionaryWithContentsOfFile:dictPath];
return [infoDictionary objectForKey:NSServicesKey];
}
return nil;
}
NSArray *providersAtPath(NSString *path) {
NSFileManager *manager = [NSFileManager defaultManager];
NSMutableArray *providers = [NSMutableArray arrayWithCapacity:1];
NSString *itemPath;
NSArray *subPaths;
int i;
path = [path stringByStandardizingPath];
subPaths = [manager subpathsAtPath:path];
for (i = 0; i < [subPaths count]; i++){
itemPath = [subPaths objectAtIndex:i];
if ([itemPath hasSuffix:infoPath]) {
itemPath = [path stringByAppendingPathComponent:itemPath];
if ([[NSMutableDictionary dictionaryWithContentsOfFile:itemPath] objectForKey:NSServicesKey]) {
[providers addObject:[[itemPath stringByDeletingLastPathComponent] stringByDeletingLastPathComponent]];
}
}
}
return providers;
}
NSArray *applicationProviders() {
NSMutableArray *providers = [NSMutableArray arrayWithCapacity:1];
NSString *itemPath;
NSArray *apps = [[NSWorkspace sharedWorkspace] allApplications];
int i;
for (i = 0; i < [apps count]; i++){
itemPath = [apps objectAtIndex:i];
if ([[NSMutableDictionary dictionaryWithContentsOfFile:[itemPath stringByAppendingPathComponent:infoPath]] objectForKey:NSServicesKey]) {
[providers addObject:itemPath];
}
}
return providers;
}
@implementation QSServiceActions
+ (void)loadPlugIn {
[NSThread detachNewThreadSelector:@selector(loadServiceActions) toTarget:self withObject:nil];
}
+ (void)loadServiceActions {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[[QSTaskController sharedInstance] updateTask:@"Load Actions" status:@"Loading Application Services" progress:-1];
NSArray *serviceActions = [QSServiceActions allServiceActions];
int i;
for (i = 0; i < [serviceActions count]; i++) {
[QSExec performSelectorOnMainThread:@selector(addActions:) withObject:[[serviceActions objectAtIndex:i] actions] waitUntilDone:YES];
}
//NSLog(@"Services Loaded");
[[QSTaskController sharedInstance] removeTask:@"Load Actions"];
[pool release];
}
+ (NSArray *)allServiceActions {
NSMutableSet *providerSet = [NSMutableSet setWithCapacity:1];
[providerSet addObjectsFromArray:applicationProviders()];
[providerSet addObjectsFromArray:providersAtPath(@"/System/Library/Services/")];
[providerSet addObjectsFromArray:providersAtPath(@"/Library/Services/")];
[providerSet addObjectsFromArray:providersAtPath(@"~/Library/Services/")];
NSArray *providerArray = [providerSet allObjects];
NSMutableArray *actionObjects = [NSMutableArray arrayWithCapacity:[providerArray count]];
int i;
for (i = 0; i < [providerArray count]; i++)
[actionObjects addObject:[[self class] serviceActionsForBundle:[providerArray objectAtIndex:i]]];
return actionObjects;
}
+ (QSServiceActions *)serviceActionsForBundle:(NSString *)path {
//NSLog(@"Loading Actions for Bundle: %@",path);
return [[[[self class] alloc] initWithBundlePath:path] autorelease];
}
- (id)initWithBundlePath:(NSString *)path {
if (self = [super init]) {
serviceBundle = [path copy];
serviceArray = [servicesForBundle(path) retain];
NSString *bundleIdentifier = [[NSBundle bundleWithPath:path] bundleIdentifier];
modificationsDictionary = [[[NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"NSServiceModifications" ofType:@"plist"]] objectForKey:bundleIdentifier] retain];
}
return self;
}
- (NSArray *)types{ return nil; }
- (NSArray *)actions {
NSMutableArray *newActions = [NSMutableArray arrayWithCapacity:1];
NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile:serviceBundle];
[icon setSize:NSMakeSize(16, 16)];
int i;
for (i = 0; i < [serviceArray count]; i++) {
NSDictionary *thisService = [serviceArray objectAtIndex:i];
NSString *serviceString = [[thisService objectForKey:NSMenuItemKey] objectForKey:DefaultKey];
NSDictionary *serviceModifications = [modificationsDictionary objectForKey:serviceString];
if ([[serviceModifications objectForKey:@"disabled"] boolValue])
continue;
QSAction *serviceAction = [[QSAction alloc] init];
[serviceAction setIdentifier:serviceString];
if ([serviceModifications objectForKey:@"name"])
[serviceAction setName:[serviceModifications objectForKey:@"name"]];
NSArray *sendTypes = [thisService objectForKey:NSSendTypesKey];
if (sendTypes) {
[serviceAction setDirectTypes:sendTypes];
}
[serviceAction setIcon:icon];
[serviceAction setProvider:self];
[serviceAction setDisplaysResult:YES];
[serviceAction setDetails:[NSString stringWithFormat:@"A service of %@",[serviceBundle lastPathComponent]]];
[newActions addObject:serviceAction];
}
return newActions;
}
- (NSArray *)validActionsForDirectObject:(QSObject *)dObject indirectObject:(QSObject *)iObject {
BOOL fileType = [[dObject primaryType]isEqualToString:NSFilenamesPboardType];
if (fileType && ![dObject validPaths])
return nil;
NSMutableArray *newActions = [NSMutableArray arrayWithCapacity:1];
NSString *menuItem;
int i;
// NSLog(@"services%@", serviceArray);
for (i = 0; i < [serviceArray count]; i++) {
NSDictionary *thisService = [serviceArray objectAtIndex:i];
menuItem = [[thisService objectForKey:NSMenuItemKey] objectForKey:DefaultKey];
BOOL disabled = [[[modificationsDictionary objectForKey:menuItem] objectForKey:@"disabled"] boolValue];
if (menuItem && !disabled) {
NSSet *sendTypes = [NSSet setWithArray:[thisService objectForKey:NSSendTypesKey]];
NSSet *availableTypes = [NSSet setWithArray:[dObject types]];
// Add if they intersect, but ignore ex
if ([sendTypes intersectsSet:availableTypes]){
if (fileType && ![sendTypes containsObject:NSFilenamesPboardType])
continue;
[newActions addObject:menuItem];
}
}
}
return newActions;
}
- (QSObject *)performAction:(QSAction *)action directObject:(QSBasicObject *)dObject indirectObject:(QSBasicObject *)iObject {
NSPasteboard *pboard = [NSPasteboard pasteboardWithUniqueName];
NSDictionary *thisService = nil;
//NSLog(@"perform %@ %@ %@",[action actionDict],serviceArray,self);
int i;
for (i = 0; i < [serviceArray count]; i++) {
thisService = [serviceArray objectAtIndex:i];
// NSLog(@"'%@' '%@'",[action identifier],[[thisService objectForKey:NSMenuItemKey]objectForKey:DefaultKey]);
if ([[[thisService objectForKey:NSMenuItemKey] objectForKey:DefaultKey] isEqualToString:[action identifier]]) {
NSArray *sendTypes = [thisService objectForKey:NSSendTypesKey];
[dObject putOnPasteboard:pboard declareTypes:sendTypes includeDataForTypes:sendTypes];
break;
}
}
BOOL success = NSPerformService([action identifier], pboard);
if (success) {
QSObject *entry = nil;
if ([thisService objectForKey:NSReturnTypesKey])
entry = [[QSObject alloc] initWithPasteboard:pboard types:[thisService objectForKey:NSReturnTypesKey]];
return entry;
}
NSLog(@"PerformServiceFailed: %@, %@\r%@\r%@", action, dObject, serviceBundle, [[pboard types] componentsJoinedByString:@", "]);
return nil;
}
- (BOOL)performServiceWithNameAndPasteboard:(NSArray *)array {
return NSPerformService([array objectAtIndex:0], [array lastObject]);
}
@end