forked from quicksilver/Plugins
-
Notifications
You must be signed in to change notification settings - Fork 0
/
QSYojimboPlugInSource.m
265 lines (247 loc) · 12.6 KB
/
QSYojimboPlugInSource.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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
//
// QSYojimboPlugInSource.m
// QSYojimboPlugIn
//
// Created by Nicholas Jitkoff on 5/14/06.
// Copyright __MyCompanyName__ 2006. All rights reserved.
//
#import "QSYojimboPlugInSource.h"
#import <QSCore/QSObject.h>
#import "QSYojimboPlugInDefines.h"
@implementation QSYojimboPlugInSource
- (BOOL)indexIsValidFromDate:(NSDate *)indexDate forEntry:(NSDictionary *)theEntry{
/* the bad news is we have to look at every frakking parent folder
(looking at individual items would miss deletions)
the good news is we can bail out if we find just one that's updated */
NSString *path = [@"~/Library/Caches/Metadata/com.barebones.yojimbo" stringByStandardizingPath];
NSFileManager *manager = [NSFileManager defaultManager];
NSArray *contents = [manager directoryContentsAtPath:path];
for (NSString *topLevelDir in contents) {
topLevelDir = [path stringByAppendingPathComponent:topLevelDir];
for (NSString *secondLevelDir in [manager directoryContentsAtPath:topLevelDir]) {
secondLevelDir = [topLevelDir stringByAppendingPathComponent:secondLevelDir];
NSDate *modified = [[manager attributesOfItemAtPath:secondLevelDir error:NULL] fileModificationDate];
if ([indexDate compare:modified] == NSOrderedAscending) {
// something new - trigger a rescan
return NO;
}
}
}
// none of the files are new or changed
return YES;
}
- (NSImage *) iconForEntry:(NSDictionary *)dict{
return [QSResourceManager imageNamed:@"com.barebones.yojimbo"];
}
- (BOOL)objectHasChildren:(QSObject *)object {
// indicate that tags can be arrowed into
if ([object containsType:kQSYojimboTagType])
{
return YES;
}
}
- (BOOL)loadChildrenForObject:(QSObject *)object{
if ([object containsType:kQSYojimboTagType])
{
// right-arrowed into a tag
// return a list of matching tags and items
NSMutableArray *matchingTags = [NSMutableArray arrayWithCapacity:1];
NSMutableArray *children = [NSMutableArray arrayWithCapacity:1];
// track which tags we're combining
NSMutableArray *navigationHistory = [NSMutableArray arrayWithArray:[object objectForMeta:@"navigationHistory"]];
if (navigationHistory)
{
[navigationHistory addObject:[object name]];
} else {
navigationHistory = [NSArray arrayWithObject:[object name]];
}
// NSLog(@"current navigation history: %@", navigationHistory);
/* on the assumption that it's easier to find tags by typing and
items by looking, we add items to the top of the list, then tags */
// add items to the list using objects already created by objectsForEntry
for (NSString *uuid in [object objectForMeta:@"items"])
{
QSObject *yojimboItem = [QSObject objectWithIdentifier:uuid];
BOOL matchesAllTags = true;
for (NSString *navTag in navigationHistory)
{
if (![[yojimboItem objectForMeta:@"tags"] containsObject:navTag])
{
matchesAllTags = false;
}
}
if (matchesAllTags) {
// add this item
[children addObject:yojimboItem];
// look for tags
for (NSString *tag in [yojimboItem objectForMeta:@"tags"])
{
// if we don't have it yet, and it wasn't already arrowed into
if (![matchingTags containsObject:tag] && ![navigationHistory containsObject:tag])
{
// we don't have this tag yet
[matchingTags addObject:tag];
}
}
} else if ([[object identifier] isEqualToString:@"yojimbotag:untagged"]
&& [[yojimboItem objectForMeta:@"tags"] count] == 0
&& ![yojimboItem containsType:kQSYojimboTagType]
)
// list items with no tags
{
[children addObject:yojimboItem];
}
}
// add tags to the list
for (NSString *tag in matchingTags)
{
// use the tag object that was created by objectsForEntry
NSString *ident = [NSString stringWithFormat:@"yojimbotag:%@", tag];
QSObject *tagObject = [QSObject objectWithIdentifier:ident];
// navigation history is transient and shouldn't be set on the
// actual tag, so we use a temporary stand-in with no identifier
NSString *tag = [tagObject name];
QSObject *transientTag = [QSObject objectWithName:tag];
[transientTag setObject:tag forType:kQSYojimboTagType];
[transientTag setObject:[tagObject objectForMeta:@"items"] forMeta:@"items"];
[transientTag setObject:kQSYojimboTagType forMeta:@"itemKind"];
[transientTag setDetails:@"Yojimbo Tag"];
[transientTag setObject:navigationHistory forMeta:@"navigationHistory"];
[children addObject:transientTag];
}
[object setChildren:children];
} else {
// right-arrowed into Yojimbo
// return a list of tags
NSMutableArray *tags = [QSLib scoredArrayForString:nil inSet:[QSLib arrayForType:kQSYojimboTagType]];
[object setChildren:tags];
}
return TRUE;
}
- (NSArray *) objectsForEntry:(NSDictionary *)theEntry{
NSString *path = [@"~/Library/Caches/Metadata/com.barebones.yojimbo" stringByStandardizingPath];
NSFileManager *manager = [NSFileManager defaultManager];
NSArray *contents = [manager directoryContentsAtPath:path];
// pretty names for various types of items Yojimbo stores
NSDictionary *typeTable = [NSDictionary dictionaryWithObjectsAndKeys:
@"Yojimbo Note", @"com.barebones.yojimbo.yojimbonote",
@"Yojimbo Bookmark", @"com.barebones.yojimbo.yojimbobookmark",
@"Yojimbo Web Archive", @"com.barebones.yojimbo.yojimbowebarchive",
@"Yojimbo PDF Archive", @"com.barebones.yojimbo.yojimbopdfarchive",
@"Yojimbo Serial Number", @"com.barebones.yojimbo.yojimboserialnumber",
@"Yojimbo Image", @"com.barebones.yojimbo.yojimboimage",
nil
];
NSMutableArray *objects=[NSMutableArray arrayWithCapacity:1];
QSObject *newObject = nil;
QSObject *tagObject = nil;
NSMutableDictionary *tags = [NSMutableDictionary dictionaryWithCapacity:1];
NSMutableArray *untaggedItems = [NSMutableArray arrayWithCapacity:1];
// NSLog(@"Yojimbo plug-in hitting the filesystem");
for (NSString *topLevelDir in contents) {
topLevelDir = [path stringByAppendingPathComponent:topLevelDir];
for (NSString *secondLevelDir in [manager directoryContentsAtPath:topLevelDir]) {
secondLevelDir = [topLevelDir stringByAppendingPathComponent:secondLevelDir];
for (NSString *yojimboItem in [manager directoryContentsAtPath:secondLevelDir]) {
if ([yojimboItem rangeOfString:@"yojimbo"].location == NSNotFound) continue;
yojimboItem = [secondLevelDir stringByAppendingPathComponent:yojimboItem];
NSDictionary *item = [NSDictionary dictionaryWithContentsOfFile:yojimboItem];
newObject = nil;
@try {
if ([[item valueForKey:@"itemKind"] isEqualToString:@"com.barebones.yojimbo.yojimbobookmark"]) {
// Christ, BareBones! You can't settle on a capitalization scheme for dictionary keys?
NSString *URLString;
if ([item valueForKey:@"URLString"]) {
URLString = [item valueForKey:@"URLString"];
} else if ([item valueForKey:@"urlString"]) {
URLString = [item valueForKey:@"urlString"];
}
newObject = [QSObject URLObjectWithURL:URLString title:[item valueForKey:@"name"]];
} else {
newObject = [QSObject objectWithName:[item valueForKey:@"name"]];
}
if ([[item valueForKey:@"encrypted"]boolValue]){
[newObject setDetails:[NSString stringWithFormat:@"%@ (Encrypted)", [typeTable valueForKey:[item valueForKey:@"itemKind"]]]];
} else {
[newObject setDetails:[typeTable valueForKey:[item valueForKey:@"itemKind"]]];
}
[newObject setIdentifier:[item valueForKey:@"uuid"]];
[newObject setPrimaryType:kQSYojimboPlugInType];
[newObject setObject:[item valueForKey:@"uuid"] forType:kQSYojimboPlugInType];
if ([[item valueForKey:@"itemKind"] isEqualToString:@"com.barebones.yojimbo.yojimbonote"] && [item valueForKey:@"content"])
{
// this will enable actions like "Paste" and "Large Type" for notes
[newObject setObject:[item valueForKey:@"content"] forType:QSTextType];
}
// store the type of Yojimbo item
[newObject setObject:[item valueForKey:@"itemKind"] forMeta:@"itemKind"];
// store this item's tags
[newObject setObject:[item valueForKey:@"tags"] forMeta:@"tags"];
if ([[item valueForKey:@"tags"] count] == 0)
{
[untaggedItems addObject:[item valueForKey:@"uuid"]];
} else {
// get a list of all tags and the associated items
for (NSString *tag in [item valueForKey:@"tags"])
{
if ([[tags allKeys] containsObject:tag])
{
// append to the list
[[tags objectForKey:tag] addObject:[item valueForKey:@"uuid"]];
} else {
// create a list of items for this tag
NSMutableArray *itemsForTag = [NSMutableArray arrayWithObject:[item valueForKey:@"uuid"]];
[tags setObject:itemsForTag forKey:tag];
}
}
}
if (newObject)
{
[objects addObject:newObject];
}
}
@catch (id theException) {
NSLog(@"error with: %@ %@", item, theException);
}
}
}
}
// add tags to the catalog
for (NSString *tag in [tags allKeys])
{
NSString *ident = [NSString stringWithFormat:@"yojimbotag:%@", tag];
tagObject = [QSObject objectWithName:tag];
[tagObject setIdentifier:ident];
[tagObject setObject:tag forType:kQSYojimboTagType];
[tagObject setObject:[tags objectForKey:tag] forMeta:@"items"];
// tags don't have an official itemKind, but I'm making one up for consistency
[tagObject setObject:kQSYojimboTagType forMeta:@"itemKind"];
[tagObject setDetails:@"Yojimbo Tag"];
[objects addObject:tagObject];
}
// create and register an "untagged items" object
// to allow access to items with no tags
QSObject *untagged = [QSObject objectWithName:@"Untagged Items"];
[untagged setObject:@"Untagged" forType:kQSYojimboTagType];
[untagged setObject:kQSYojimboTagType forMeta:@"itemKind"];
[untagged setObject:untaggedItems forMeta:@"items"];
[untagged setDetails:@"Items With No Tags"];
[untagged setIdentifier:@"yojimbotag:untagged"];
[objects addObject:untagged];
return objects;
}
// Object Handler Methods
- (void)setQuickIconForObject:(QSObject *)object{
// set some useful icons depending on the type of object
if ([[object objectForMeta:@"itemKind"] isEqualToString:kQSYojimboTagType])
{
[object setIcon:[QSResourceManager imageNamed:@"com.barebones.yojimbo"]];
} else if ([[object objectForMeta:@"itemKind"] isEqualToString:@"com.barebones.yojimbo.yojimbopdfarchive"]) {
[object setIcon:[[NSWorkspace sharedWorkspace] iconForFileType:@"pdf"]];
} else if ([[object objectForMeta:@"itemKind"] isEqualToString:@"com.barebones.yojimbo.yojimbobookmark"]) {
[object setIcon:[QSResourceManager imageNamed:@"DefaultBookmarkIcon"]];
} else {
[object setIcon:[[NSWorkspace sharedWorkspace] iconForFileType:@"yojimbonote"]];
}
}
@end