SKBookmark.m 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. //
  2. // SKBookmark.m
  3. // Skim
  4. //
  5. // Created by Christiaan Hofman on 9/15/07.
  6. /*
  7. This software is Copyright (c) 2007-2018
  8. Christiaan Hofman. All rights reserved.
  9. Redistribution and use in source and binary forms, with or without
  10. modification, are permitted provided that the following conditions
  11. are met:
  12. - Redistributions of source code must retain the above copyright
  13. notice, this list of conditions and the following disclaimer.
  14. - Redistributions in binary form must reproduce the above copyright
  15. notice, this list of conditions and the following disclaimer in
  16. the documentation and/or other materials provided with the
  17. distribution.
  18. - Neither the name of Christiaan Hofman nor the names of any
  19. contributors may be used to endorse or promote products derived
  20. from this software without specific prior written permission.
  21. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  25. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  26. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  27. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  28. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  29. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  31. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. */
  33. #import "SKBookmark.h"
  34. #import "SKAlias.h"
  35. #import "NSImage_SKExtensions.h"
  36. //#import "NSDocument_SKExtensions.h"
  37. #import "SKDocumentController.h"
  38. //#import "NSURL_SKExtensions.h"
  39. //#import "SKMainDocument.h"
  40. //#import "SKMainWindowController.h"
  41. //#import "NSError_SKExtensions.h"
  42. //#import "NSShadow_SKExtensions.h"
  43. #define BOOKMARK_STRING @"bookmark"
  44. #define SESSION_STRING @"session"
  45. #define FOLDER_STRING @"folder"
  46. #define SEPARATOR_STRING @"separator"
  47. #define PROPERTIES_KEY @"properties"
  48. #define CHILDREN_KEY @"children"
  49. #define LABEL_KEY @"label"
  50. #define PAGEINDEX_KEY @"pageIndex"
  51. #define ALIASDATA_KEY @"_BDAlias"
  52. #define TYPE_KEY @"type"
  53. NSString *SKFolderDocumentType = @"public.folder";
  54. @interface SKFileBookmark : SKBookmark {
  55. SKAlias *alias;
  56. NSData *aliasData;
  57. NSString *label;
  58. NSUInteger pageIndex;
  59. NSDictionary *setup;
  60. }
  61. - (id)initWithAliasData:(NSData *)aData pageIndex:(NSUInteger)aPageIndex label:(NSString *)aLabel;
  62. - (SKAlias *)alias;
  63. - (NSData *)aliasData;
  64. @end
  65. @interface SKFolderBookmark : SKBookmark {
  66. NSString *label;
  67. NSMutableArray *children;
  68. }
  69. @end
  70. @interface SKRootBookmark : SKFolderBookmark
  71. @end
  72. @interface SKSessionBookmark : SKFolderBookmark
  73. @end
  74. @interface SKSeparatorBookmark : SKBookmark
  75. @end
  76. #pragma mark -
  77. @implementation SKBookmark
  78. + (id)bookmarkWithURL:(NSURL *)aURL pageIndex:(NSUInteger)aPageIndex label:(NSString *)aLabel {
  79. return [[self alloc] initWithURL:aURL pageIndex:aPageIndex label:aLabel];
  80. }
  81. + (id)bookmarkWithSetup:(NSDictionary *)aSetupDict label:(NSString *)aLabel {
  82. return [[self alloc] initWithSetup:aSetupDict label:aLabel];
  83. }
  84. + (id)bookmarkFolderWithLabel:(NSString *)aLabel {
  85. return [[self alloc] initFolderWithLabel:aLabel];
  86. }
  87. + (id)bookmarkSessionWithSetups:(NSArray *)aSetupDicts label:(NSString *)aLabel {
  88. return [[self alloc] initSessionWithSetups:aSetupDicts label:aLabel];
  89. }
  90. + (id)bookmarkSeparator {
  91. return [[self alloc] initSeparator];
  92. }
  93. + (NSArray *)bookmarksForURLs:(NSArray *)urls {
  94. NSFileManager *fm = [NSFileManager defaultManager];
  95. NSDocumentController *dc = [NSDocumentController sharedDocumentController];
  96. NSMutableArray *array = [NSMutableArray array];
  97. for (NSURL *url in urls) {
  98. NSString *fileType = [dc typeForContentsOfURL:url error:NULL];
  99. Class docClass;
  100. SKBookmark *bookmark;
  101. NSString *label = nil;
  102. [url getResourceValue:&label forKey:NSURLLocalizedNameKey error:NULL];
  103. if ([[NSWorkspace sharedWorkspace] type:fileType conformsToType:SKFolderDocumentType]) {
  104. NSArray *children = [self bookmarksForURLs:[fm contentsOfDirectoryAtURL:url includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsHiddenFiles error:NULL]];
  105. if ([children count] && (bookmark = [[self alloc] initFolderWithChildren:children label:label])) {
  106. [array addObject:bookmark];
  107. }
  108. } else if ((docClass = [dc documentClassForType:fileType])) {
  109. if ((bookmark = [[self alloc] initWithURL:url pageIndex:0 label:label])) {
  110. [array addObject:bookmark];
  111. }
  112. }
  113. }
  114. return array;
  115. }
  116. - (id)initWithURL:(NSURL *)aURL pageIndex:(NSUInteger)aPageIndex label:(NSString *)aLabel {
  117. return [[SKFileBookmark alloc] initWithURL:aURL pageIndex:aPageIndex label:aLabel];
  118. }
  119. - (id)initWithSetup:(NSDictionary *)aSetupDict label:(NSString *)aLabel {
  120. return [[SKFileBookmark alloc] initWithSetup:aSetupDict label:aLabel];
  121. }
  122. - (id)initFolderWithChildren:(NSArray *)aChildren label:(NSString *)aLabel {
  123. return [[SKFolderBookmark alloc] initFolderWithChildren:aChildren label:aLabel];
  124. }
  125. - (id)initFolderWithLabel:(NSString *)aLabel {
  126. return [self initFolderWithChildren:nil label:aLabel];
  127. }
  128. - (id)initRootWithChildrenProperties:(NSArray *)childrenProperties {
  129. NSMutableArray *aChildren = [NSMutableArray array];
  130. SKBookmark *child;
  131. for (NSDictionary *dict in childrenProperties) {
  132. if ((child = [[SKBookmark alloc] initWithProperties:dict])) {
  133. [aChildren addObject:child];
  134. }
  135. }
  136. return [[SKRootBookmark alloc] initFolderWithChildren:aChildren label:NSLocalizedString(@"Bookmarks Menu", @"Menu item title")];
  137. }
  138. - (id)initSessionWithSetups:(NSArray *)aSetupDicts label:(NSString *)aLabel {
  139. NSMutableArray *aChildren = [NSMutableArray array];
  140. SKBookmark *child;
  141. for (NSDictionary *setup in aSetupDicts) {
  142. if ((child = [[SKBookmark alloc] initWithSetup:setup label:@""])) {
  143. [aChildren addObject:child];
  144. }
  145. }
  146. return [[SKSessionBookmark alloc] initFolderWithChildren:aChildren label:aLabel];
  147. }
  148. - (id)initSeparator {
  149. return [[SKSeparatorBookmark alloc] init];
  150. }
  151. - (id)initWithProperties:(NSDictionary *)dictionary {
  152. NSString *type = [dictionary objectForKey:TYPE_KEY];
  153. if ([type isEqualToString:SEPARATOR_STRING]) {
  154. return (id)[[SKSeparatorBookmark alloc] init];
  155. } else if ([type isEqualToString:FOLDER_STRING] || [type isEqualToString:SESSION_STRING]) {
  156. Class bookmarkClass = [type isEqualToString:FOLDER_STRING] ? [SKFolderBookmark class] : [SKSessionBookmark class];
  157. NSMutableArray *newChildren = [NSMutableArray array];
  158. SKBookmark *child;
  159. for (NSDictionary *dict in [dictionary objectForKey:CHILDREN_KEY]) {
  160. if ((child = [[SKBookmark alloc] initWithProperties:dict])) {
  161. [newChildren addObject:child];
  162. } else
  163. NSLog(@"Failed to read child bookmark: %@", dict);
  164. }
  165. return (id)[[bookmarkClass alloc] initFolderWithChildren:newChildren label:[dictionary objectForKey:LABEL_KEY]];
  166. } else if ([dictionary objectForKey:@"windowFrame"]) {
  167. return (id)[[SKFileBookmark alloc] initWithSetup:dictionary label:[dictionary objectForKey:LABEL_KEY]];
  168. } else {
  169. NSNumber *pageIndex = [dictionary objectForKey:PAGEINDEX_KEY];
  170. return (id)[[SKFileBookmark alloc] initWithAliasData:[dictionary objectForKey:ALIASDATA_KEY] pageIndex:(pageIndex ? [pageIndex unsignedIntegerValue] : NSNotFound) label:[dictionary objectForKey:LABEL_KEY]];
  171. }
  172. }
  173. - (id)copyWithZone:(NSZone *)aZone {
  174. return [[SKBookmark alloc] initWithProperties:[self properties]];
  175. }
  176. - (void)dealloc {
  177. parent = nil;
  178. }
  179. - (NSDictionary *)properties { return nil; }
  180. - (SKBookmarkType)bookmarkType { return SKBookmarkTypeSeparator; }
  181. - (NSImage *)icon { return nil; }
  182. - (NSImage *)alternateIcon { return [self icon]; }
  183. - (NSString *)label { return nil; }
  184. - (void)setLabel:(NSString *)newLabel {}
  185. - (NSURL *)fileURL { return nil; }
  186. - (NSUInteger)pageIndex { return NSNotFound; }
  187. - (void)setPageIndex:(NSUInteger)newPageIndex {}
  188. - (NSNumber *)pageNumber { return nil; }
  189. - (void)setPageNumber:(NSNumber *)newPageNumber {}
  190. - (NSURL *)previewItemURL { return [self fileURL]; }
  191. - (NSString *)previewItemTitle { return [self label]; }
  192. - (NSArray *)children { return nil; }
  193. - (NSUInteger)countOfChildren { return 0; }
  194. - (SKBookmark *)objectInChildrenAtIndex:(NSUInteger)anIndex { return nil; }
  195. - (void)insertObject:(SKBookmark *)child inChildrenAtIndex:(NSUInteger)anIndex {}
  196. - (void)removeObjectFromChildrenAtIndex:(NSUInteger)anIndex {}
  197. - (id)objectSpecifier {
  198. NSUInteger idx = [[parent children] indexOfObjectIdenticalTo:self];
  199. if (idx != NSNotFound) {
  200. NSScriptObjectSpecifier *containerRef = nil;
  201. NSScriptClassDescription *containerClassDescription = nil;
  202. if ([parent parent]) {
  203. containerRef = [parent objectSpecifier];
  204. containerClassDescription = [containerRef keyClassDescription];
  205. } else {
  206. containerClassDescription = [NSScriptClassDescription classDescriptionForClass:[NSApp class]];
  207. }
  208. return [[NSIndexSpecifier allocWithZone:CFBridgingRetain(self)] initWithContainerClassDescription:containerClassDescription containerSpecifier:containerRef key:@"bookmarks" index:idx];
  209. } else {
  210. return nil;
  211. }
  212. }
  213. - (SKBookmark *)scriptingParent {
  214. return [parent parent] == nil ? nil : parent;
  215. }
  216. - (NSArray *)entireContents { return nil; }
  217. - (NSArray *)bookmarks {
  218. return [self children];
  219. }
  220. - (void)insertObject:(SKBookmark *)bookmark inBookmarksAtIndex:(NSUInteger)anIndex {
  221. [self insertObject:bookmark inChildrenAtIndex:anIndex];
  222. }
  223. - (void)removeObjectFromBookmarksAtIndex:(NSUInteger)anIndex {
  224. [self removeObjectFromChildrenAtIndex:anIndex];
  225. }
  226. - (id)newScriptingObjectOfClass:(Class)objectClass forValueForKey:(NSString *)key withContentsValue:(id)contentsValue properties:(NSDictionary *)properties {
  227. if ([key isEqualToString:@"bookmarks"]) {
  228. [[NSScriptCommand currentCommand] setScriptErrorNumber:NSReceiversCantHandleCommandScriptError];
  229. [[NSScriptCommand currentCommand] setScriptErrorString:@"Invalid container for new bookmark."];
  230. return nil;
  231. }
  232. return [super newScriptingObjectOfClass:objectClass forValueForKey:key withContentsValue:contentsValue properties:properties];
  233. }
  234. - (BOOL)isDescendantOf:(SKBookmark *)bookmark {
  235. if (self == bookmark)
  236. return YES;
  237. for (SKBookmark *child in [bookmark children]) {
  238. if ([self isDescendantOf:child])
  239. return YES;
  240. }
  241. return NO;
  242. }
  243. - (BOOL)isDescendantOfArray:(NSArray *)bookmarks {
  244. for (SKBookmark *bm in bookmarks) {
  245. if ([self isDescendantOf:bm]) return YES;
  246. }
  247. return NO;
  248. }
  249. - (void)open {}
  250. @end
  251. #pragma mark -
  252. @implementation SKFileBookmark
  253. + (NSImage *)missingFileImage {
  254. static NSImage *image = nil;
  255. if (image == nil) {
  256. image = [NSImage imageWithSize:NSMakeSize(16.0, 16.0) drawingHandler:^(NSRect rect) {
  257. NSImage *genericDocImage = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericDocumentIcon)];
  258. NSImage *questionMark = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kQuestionMarkIcon)];
  259. [genericDocImage drawInRect:rect fromRect:NSZeroRect operation:NSCompositeCopy fraction:0.7];
  260. [questionMark drawInRect:NSMakeRect(3.0, 2.0, 10.0, 10.0) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:0.7];
  261. return YES;
  262. }];
  263. }
  264. return image;
  265. }
  266. - (id)initWithURL:(NSURL *)aURL pageIndex:(NSUInteger)aPageIndex label:(NSString *)aLabel {
  267. self = [super init];
  268. if (self) {
  269. alias = [[SKAlias alloc] initWithURL:aURL];
  270. if (alias) {
  271. aliasData = [alias data];
  272. pageIndex = aPageIndex;
  273. label = [aLabel copy];
  274. setup = nil;
  275. } else {
  276. self = nil;
  277. }
  278. }
  279. return self;
  280. }
  281. - (id)initWithAliasData:(NSData *)aData pageIndex:(NSUInteger)aPageIndex label:(NSString *)aLabel {
  282. self = [super init];
  283. if (self) {
  284. alias = [[SKAlias alloc] initWithData:aData];
  285. if (aData && alias) {
  286. aliasData = aData;
  287. pageIndex = aPageIndex;
  288. label = [aLabel copy];
  289. setup = nil;
  290. } else {
  291. self = nil;
  292. }
  293. }
  294. return self;
  295. }
  296. - (id)initWithSetup:(NSDictionary *)aSetupDict label:(NSString *)aLabel {
  297. NSNumber *pageIndexNumber = [aSetupDict objectForKey:PAGEINDEX_KEY];
  298. self = [self initWithAliasData:[aSetupDict objectForKey:ALIASDATA_KEY] pageIndex:(pageIndexNumber ? [pageIndexNumber unsignedIntegerValue] : NSNotFound) label:aLabel];
  299. if (self) {
  300. setup = [aSetupDict copy];
  301. }
  302. return self;
  303. }
  304. - (void)dealloc {
  305. // SKDESTROY(alias);
  306. // SKDESTROY(aliasData);
  307. // SKDESTROY(label);
  308. // SKDESTROY(setup);
  309. // [super dealloc];
  310. }
  311. - (NSString *)description {
  312. return [NSString stringWithFormat:@"<%@: label=%@, path=%@, page=%lu>", [self class], label, [[self fileURL] path], (unsigned long)pageIndex];
  313. }
  314. - (NSDictionary *)properties {
  315. NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary:setup];
  316. [properties addEntriesFromDictionary:[NSDictionary dictionaryWithObjectsAndKeys:BOOKMARK_STRING, TYPE_KEY, [self aliasData], ALIASDATA_KEY, [NSNumber numberWithUnsignedInteger:pageIndex], PAGEINDEX_KEY, label, LABEL_KEY, nil]];
  317. return properties;
  318. }
  319. - (SKBookmarkType)bookmarkType {
  320. return SKBookmarkTypeBookmark;
  321. }
  322. - (NSURL *)fileURL {
  323. return [alias fileURLNoUI];
  324. }
  325. - (SKAlias *)alias {
  326. return alias;
  327. }
  328. - (NSData *)aliasData {
  329. NSData *data = nil;
  330. if ([self fileURL])
  331. data = [alias data];
  332. return data ?: aliasData;
  333. }
  334. - (NSImage *)icon {
  335. NSURL *fileURL = [self fileURL];
  336. return fileURL ? [[NSWorkspace sharedWorkspace] iconForFile:[fileURL path]] : [[self class] missingFileImage];
  337. }
  338. - (NSUInteger)pageIndex {
  339. return pageIndex;
  340. }
  341. - (void)setPageIndex:(NSUInteger)newPageIndex { pageIndex = newPageIndex; }
  342. - (NSNumber *)pageNumber {
  343. return pageIndex == NSNotFound ? nil : [NSNumber numberWithUnsignedInteger:pageIndex + 1];
  344. }
  345. - (void)setPageNumber:(NSNumber *)newPageNumber {
  346. NSUInteger newNumber = [newPageNumber unsignedIntegerValue];
  347. if (newNumber > 0)
  348. [self setPageIndex:newNumber - 1];
  349. }
  350. - (NSString *)label {
  351. NSString *theLabel = label;
  352. if ([theLabel length] == 0)
  353. [[self fileURL] getResourceValue:&theLabel forKey:NSURLLocalizedNameKey error:NULL];
  354. return theLabel ?: @"";
  355. }
  356. - (void)setLabel:(NSString *)newLabel {
  357. if (label != newLabel) {
  358. label = newLabel;
  359. }
  360. }
  361. - (void)open {
  362. id document = nil;
  363. NSError *error = nil;
  364. if (setup) {
  365. document = [[NSDocumentController sharedDocumentController] openDocumentWithSetup:[self properties] error:&error];
  366. } else {
  367. // // we allow UI when resolving alias for opening the bookmark, so don't use -fileURL, also consistent with openDocumentWithSetup:error:
  368. // NSURL *fileURL = [alias fileURL];
  369. // if (fileURL && NO == [fileURL isTrashedFileURL] &&
  370. // (document = [[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:fileURL display:YES error:&error]) &&
  371. // [document isPDFDocument] && [self pageIndex] != NSNotFound)
  372. // [[document mainWindowController] setPageNumber:[self pageIndex] + 1];
  373. }
  374. // if (document == nil && error && [error isUserCancelledError] == NO)
  375. // [NSApp presentError:error];
  376. }
  377. @end
  378. #pragma mark -
  379. @implementation SKFolderBookmark
  380. - (id)initFolderWithChildren:(NSArray *)aChildren label:(NSString *)aLabel {
  381. self = [super init];
  382. if (self) {
  383. label = [aLabel copy];
  384. children = [[NSMutableArray alloc] initWithArray:aChildren];
  385. [children makeObjectsPerformSelector:@selector(setParent:) withObject:self];
  386. }
  387. return self;
  388. }
  389. - (void)dealloc {
  390. // SKDESTROY(label);
  391. // SKDESTROY(children);
  392. // [super dealloc];
  393. }
  394. - (NSString *)description {
  395. return [NSString stringWithFormat:@"<%@: label=%@, children=%@>", [self class], label, children];
  396. }
  397. - (NSDictionary *)properties {
  398. return [NSDictionary dictionaryWithObjectsAndKeys:FOLDER_STRING, TYPE_KEY, [children valueForKey:PROPERTIES_KEY], CHILDREN_KEY, label, LABEL_KEY, nil];
  399. }
  400. - (SKBookmarkType)bookmarkType {
  401. return SKBookmarkTypeFolder;
  402. }
  403. - (NSImage *)icon {
  404. return [NSImage imageNamed:NSImageNameFolder];
  405. }
  406. - (NSImage *)alternateIcon {
  407. return [NSImage imageNamed:NSImageNameMultipleDocuments];
  408. }
  409. - (NSString *)label {
  410. return label ?: @"";
  411. }
  412. - (void)setLabel:(NSString *)newLabel {
  413. if (label != newLabel) {
  414. label = newLabel;
  415. }
  416. }
  417. - (NSArray *)children {
  418. return [children copy];
  419. }
  420. - (NSUInteger)countOfChildren {
  421. return [children count];
  422. }
  423. - (SKBookmark *)objectInChildrenAtIndex:(NSUInteger)anIndex {
  424. return [children objectAtIndex:anIndex];
  425. }
  426. - (void)insertObject:(SKBookmark *)child inChildrenAtIndex:(NSUInteger)anIndex {
  427. [children insertObject:child atIndex:anIndex];
  428. [child setParent:self];
  429. }
  430. - (void)removeObjectFromChildrenAtIndex:(NSUInteger)anIndex {
  431. [[children objectAtIndex:anIndex] setParent:nil];
  432. [children removeObjectAtIndex:anIndex];
  433. }
  434. - (NSArray *)entireContents {
  435. NSMutableArray *contents = [NSMutableArray array];
  436. for (SKBookmark *bookmark in [self children]) {
  437. [contents addObject:bookmark];
  438. [contents addObjectsFromArray:[bookmark entireContents]];
  439. }
  440. return contents;
  441. }
  442. - (id)newScriptingObjectOfClass:(Class)objectClass forValueForKey:(NSString *)key withContentsValue:(id)contentsValue properties:(NSDictionary *)properties {
  443. if ([key isEqualToString:@"bookmarks"]) {
  444. SKBookmark *bookmark = nil;
  445. NSURL *aURL = [properties objectForKey:@"scriptingFile"] ?: contentsValue;
  446. NSString *aLabel = [properties objectForKey:@"label"];
  447. NSNumber *aType = [properties objectForKey:@"bookmarkType"];
  448. NSInteger type;
  449. if ([aType respondsToSelector:@selector(integerValue)])
  450. type = [aType integerValue];
  451. else if (aURL == nil)
  452. type = SKBookmarkTypeSession;
  453. else if ([[NSWorkspace sharedWorkspace] type:[[NSWorkspace sharedWorkspace] typeOfFile:[aURL path] error:NULL] conformsToType:(NSString *)kUTTypeFolder])
  454. type = SKBookmarkTypeFolder;
  455. else
  456. type = SKBookmarkTypeBookmark;
  457. switch (type) {
  458. case SKBookmarkTypeBookmark:
  459. {
  460. Class docClass;
  461. if (aURL == nil) {
  462. [[NSScriptCommand currentCommand] setScriptErrorNumber:NSRequiredArgumentsMissingScriptError];
  463. [[NSScriptCommand currentCommand] setScriptErrorString:@"New file bookmark requires a file."];
  464. } else if ([aURL checkResourceIsReachableAndReturnError:NULL] == NO) {
  465. [[NSScriptCommand currentCommand] setScriptErrorNumber:NSArgumentsWrongScriptError];
  466. [[NSScriptCommand currentCommand] setScriptErrorString:@"New file bookmark requires an existing file."];
  467. } else if ((docClass = [[NSDocumentController sharedDocumentController] documentClassForContentsOfURL:aURL])) {
  468. NSUInteger aPageNumber = [[properties objectForKey:@"pageNumber"] unsignedIntegerValue];
  469. if (aPageNumber > 0)
  470. aPageNumber--;
  471. // else
  472. // aPageNumber = [docClass isPDFDocument] ? 0 : NSNotFound;
  473. if (aLabel == nil)
  474. [aURL getResourceValue:&aLabel forKey:NSURLLocalizedNameKey error:NULL];
  475. bookmark = [[SKBookmark alloc] initWithURL:aURL pageIndex:aPageNumber label:aLabel ?: @""];
  476. } else {
  477. [[NSScriptCommand currentCommand] setScriptErrorNumber:NSArgumentsWrongScriptError];
  478. [[NSScriptCommand currentCommand] setScriptErrorString:@"Unsupported file type for new bookmark."];
  479. }
  480. break;
  481. }
  482. case SKBookmarkTypeFolder:
  483. {
  484. NSArray *aChildren = nil;
  485. if (aURL) {
  486. aChildren = [SKBookmark bookmarksForURLs:[[NSFileManager defaultManager] contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsHiddenFiles error:NULL]];
  487. if (aLabel == nil)
  488. [aURL getResourceValue:&aLabel forKey:NSURLLocalizedNameKey error:NULL];
  489. }
  490. bookmark = [[SKBookmark alloc] initFolderWithChildren:aChildren label:aLabel ?: @""];
  491. break;
  492. }
  493. case SKBookmarkTypeSession:
  494. {
  495. NSArray *setups = [[NSApp orderedDocuments] valueForKey:@"currentDocumentSetup"];
  496. bookmark = [[SKBookmark alloc] initSessionWithSetups:setups label:aLabel ?: @""];
  497. break;
  498. }
  499. case SKBookmarkTypeSeparator:
  500. bookmark = [[SKBookmark alloc] initSeparator];
  501. break;
  502. default:
  503. [[NSScriptCommand currentCommand] setScriptErrorNumber:NSArgumentsWrongScriptError];
  504. [[NSScriptCommand currentCommand] setScriptErrorString:@"New bookmark requires a supported bookmark type."];
  505. break;
  506. }
  507. return bookmark;
  508. }
  509. return [super newScriptingObjectOfClass:objectClass forValueForKey:key withContentsValue:contentsValue properties:properties];
  510. }
  511. - (void)open {
  512. NSInteger i = [children count];
  513. while (i--)
  514. [[children objectAtIndex:i] open];
  515. }
  516. @end
  517. #pragma mark -
  518. @implementation SKRootBookmark
  519. - (NSImage *)icon {
  520. static NSImage *menuIcon = nil;
  521. if (menuIcon == nil) {
  522. menuIcon = [NSImage imageWithSize:NSMakeSize(16.0, 16.0) drawingHandler:^(NSRect rect){
  523. [[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set];
  524. [NSBezierPath fillRect:NSMakeRect(1.0, 1.0, 14.0, 13.0)];
  525. [NSGraphicsContext saveGraphicsState];
  526. NSBezierPath *path = [NSBezierPath bezierPath];
  527. [path moveToPoint:NSMakePoint(2.0, 2.0)];
  528. [path lineToPoint:NSMakePoint(2.0, 15.0)];
  529. [path lineToPoint:NSMakePoint(7.0, 15.0)];
  530. [path lineToPoint:NSMakePoint(7.0, 13.0)];
  531. [path lineToPoint:NSMakePoint(14.0, 13.0)];
  532. [path lineToPoint:NSMakePoint(14.0, 2.0)];
  533. [path closePath];
  534. [[NSColor whiteColor] set];
  535. [NSShadow setShadowWithColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.33333] blurRadius:2.0 yOffset:-1.0];
  536. [path fill];
  537. [NSGraphicsContext restoreGraphicsState];
  538. [[NSColor colorWithCalibratedRed:0.162 green:0.304 blue:0.755 alpha:1.0] set];
  539. NSRectFill(NSMakeRect(2.0, 13.0, 5.0, 2.0));
  540. [[NSColor colorWithCalibratedRed:0.894 green:0.396 blue:0.202 alpha:1.0] set];
  541. NSRectFill(NSMakeRect(3.0, 4.0, 1.0, 1.0));
  542. NSRectFill(NSMakeRect(3.0, 7.0, 1.0, 1.0));
  543. NSRectFill(NSMakeRect(3.0, 10.0, 1.0, 1.0));
  544. [[NSColor colorWithCalibratedWhite:0.6 alpha:1.0] set];
  545. NSRectFill(NSMakeRect(5.0, 4.0, 1.0, 1.0));
  546. NSRectFill(NSMakeRect(5.0, 7.0, 1.0, 1.0));
  547. NSRectFill(NSMakeRect(5.0, 10.0, 1.0, 1.0));
  548. NSUInteger i, j;
  549. for (i = 0; i < 7; i++) {
  550. for (j = 0; j < 3; j++) {
  551. [[NSColor colorWithCalibratedWhite:0.45 + 0.1 * rand() / RAND_MAX alpha:1.0] set];
  552. NSRectFill(NSMakeRect(6.0 + i, 4.0 + 3.0 * j, 1.0, 1.0));
  553. }
  554. }
  555. NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.1] endingColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.0]];
  556. [gradient drawInRect:NSMakeRect(2.0, 2.0, 12.0,11.0) angle:90.0];
  557. return YES;
  558. }];
  559. }
  560. return menuIcon;
  561. }
  562. @end
  563. #pragma mark -
  564. @implementation SKSessionBookmark
  565. - (NSDictionary *)properties {
  566. return [NSDictionary dictionaryWithObjectsAndKeys:SESSION_STRING, TYPE_KEY, [children valueForKey:PROPERTIES_KEY], CHILDREN_KEY, label, LABEL_KEY, nil];
  567. }
  568. - (SKBookmarkType)bookmarkType {
  569. return SKBookmarkTypeSession;
  570. }
  571. - (NSImage *)icon {
  572. return [NSImage imageNamed:NSImageNameMultipleDocuments];
  573. }
  574. - (NSImage *)alternateIcon {
  575. return [NSImage imageNamed:NSImageNameFolder];
  576. }
  577. - (void)insertObject:(SKBookmark *)child inChildrenAtIndex:(NSUInteger)anIndex {}
  578. - (void)removeObjectFromChildrenAtIndex:(NSUInteger)anIndex {}
  579. - (NSArray *)entireContents { return nil; }
  580. - (id)newScriptingObjectOfClass:(Class)objectClass forValueForKey:(NSString *)key withContentsValue:(id)contentsValue properties:(NSDictionary *)properties {
  581. if ([key isEqualToString:@"bookmarks"]) {
  582. [[NSScriptCommand currentCommand] setScriptErrorNumber:NSReceiversCantHandleCommandScriptError];
  583. [[NSScriptCommand currentCommand] setScriptErrorString:@"Invalid container for new bookmark."];
  584. return nil;
  585. }
  586. return [super newScriptingObjectOfClass:objectClass forValueForKey:key withContentsValue:contentsValue properties:properties];
  587. }
  588. @end
  589. #pragma mark -
  590. @implementation SKSeparatorBookmark
  591. - (NSString *)description {
  592. return [NSString stringWithFormat:@"<%@: separator>", [self class]];
  593. }
  594. - (NSDictionary *)properties {
  595. return [NSDictionary dictionaryWithObjectsAndKeys:SEPARATOR_STRING, TYPE_KEY, nil];
  596. }
  597. - (SKBookmarkType)bookmarkType {
  598. return SKBookmarkTypeSeparator;
  599. }
  600. @end