SKBookmark.m 25 KB

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