├── .gitignore ├── LICENSE ├── NOTES ├── Phod.xcodeproj ├── .gitignore └── project.pbxproj ├── README.md ├── resources ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Phod-Info.plist ├── app-icon.icns ├── defaults.plist ├── en.lproj │ ├── InfoPlist.strings │ ├── MainMenu.xib │ ├── PDAdjustmentsView.xib │ ├── PDImageListView.xib │ ├── PDImageView.xib │ ├── PDImportView.xib │ ├── PDInfoView.xib │ ├── PDLibraryView.xib │ ├── PDPredicatePanel.xib │ ├── PDWindow.xib │ └── image-properties.strings └── import-icon.png └── src ├── PDAdjustmentsViewController.h ├── PDAdjustmentsViewController.m ├── PDAppDelegate.h ├── PDAppDelegate.m ├── PDAppKitExtensions.h ├── PDAppKitExtensions.m ├── PDColor.h ├── PDColor.m ├── PDFileCatalog.h ├── PDFileCatalog.m ├── PDFileManager.h ├── PDFileManager.m ├── PDFoundationExtensions.h ├── PDFoundationExtensions.m ├── PDImage.h ├── PDImage.m ├── PDImageGridView.h ├── PDImageGridView.m ├── PDImageLayer.h ├── PDImageLayer.m ├── PDImageLibrary.h ├── PDImageLibrary.m ├── PDImageListViewController.h ├── PDImageListViewController.m ├── PDImageProperty.h ├── PDImageProperty.m ├── PDImageRatingLayer.h ├── PDImageRatingLayer.m ├── PDImageTextCell.h ├── PDImageTextCell.m ├── PDImageUUID.h ├── PDImageUUID.m ├── PDImageView.h ├── PDImageView.m ├── PDImageViewController.h ├── PDImageViewController.m ├── PDImportViewController.h ├── PDImportViewController.m ├── PDInfoViewController.h ├── PDInfoViewController.m ├── PDLibraryAlbum.h ├── PDLibraryAlbum.m ├── PDLibraryDevice.h ├── PDLibraryDevice.m ├── PDLibraryDirectory.h ├── PDLibraryDirectory.m ├── PDLibraryFolder.h ├── PDLibraryFolder.m ├── PDLibraryGroup.h ├── PDLibraryGroup.m ├── PDLibraryItem.h ├── PDLibraryItem.m ├── PDLibraryQuery.h ├── PDLibraryQuery.m ├── PDLibraryView.h ├── PDLibraryView.m ├── PDLibraryViewController.h ├── PDLibraryViewController.m ├── PDLocalFileManager.h ├── PDLocalFileManager.m ├── PDMacros.h ├── PDMetadataItemView.h ├── PDMetadataItemView.m ├── PDMetadataView.h ├── PDMetadataView.m ├── PDPredicatePanelController.h ├── PDPredicatePanelController.m ├── PDSplitView.h ├── PDSplitView.m ├── PDThumbnailLayer.h ├── PDThumbnailLayer.m ├── PDViewController.h ├── PDViewController.m ├── PDWindowController.h ├── PDWindowController.m ├── PXSourceList ├── LICENSE ├── PXSourceList-Info.plist ├── PXSourceList.h ├── PXSourceList.m ├── PXSourceListDataSource.h └── PXSourceListDelegate.h ├── Phod-Prefix.pch └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | TAGS 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2013 John Harper 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a 7 | copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included 15 | in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | -------------------------------------------------------------------------------- /Phod.xcodeproj/.gitignore: -------------------------------------------------------------------------------- 1 | *.xcworkspace 2 | xcuserdata 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Photo App Project 3 | 4 | This is a work-in-progress Mac photo management app. 5 | 6 | ### Manifesto 7 | 8 | 1. Remove the "photo database as opaque bundle" concept. The database 9 | is the union of one or more image libraries (directory hierarchies of 10 | image files). Metadata is stored alongside each image. Image libraries 11 | can be edited externally, caches will be rebuilt as needed. 12 | 13 | 2. Adjustment algorithms will be published. Any edits made today should 14 | be able to reproduced in thirty years time. 15 | 16 | 3. Handle large images quickly. (My use case is 20MP JPEG+RAW files on 17 | a 3-year-old macbook air. Currently focusing on JPEG.) 18 | 19 | 4. Use standard Mac graphics frameworks wherever possible. 20 | 21 | ### Screenshot 22 | 23 | ![Screenshot](http://www.unfactored.org/images/phod-screen-2013-12-17.png) 24 | -------------------------------------------------------------------------------- /resources/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /resources/Phod-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | app-icon 11 | CFBundleIdentifier 12 | org.unfactored.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSApplicationCategoryType 26 | public.app-category.photography 27 | LSMinimumSystemVersion 28 | ${MACOSX_DEPLOYMENT_TARGET} 29 | NSHumanReadableCopyright 30 | Copyright © 2013 John Harper. All rights reserved. 31 | NSMainNibFile 32 | MainMenu 33 | NSPrincipalClass 34 | NSApplication 35 | UTExportedTypeDeclarations 36 | 37 | 38 | UTTypeIdentifier 39 | org.unfactored.phod-metadata 40 | UTTypeReferenceURL 41 | http://www.unfactored.org/phod/ 42 | UTTypeDescription 43 | Phod image metadata 44 | UTTypeIconFile 45 | app-icon.icns 46 | UTTypeConformsTo 47 | 48 | public.json 49 | public.plain-text 50 | 51 | UTTypeTagSpecification 52 | 53 | public.filename-extension 54 | 55 | phod 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /resources/app-icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsh1/phod/920729b91beac440cd7734dc7ea9a87815afc6e4/resources/app-icon.icns -------------------------------------------------------------------------------- /resources/defaults.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PDLibraryFixed 6 | 7 | 8 | name 9 | All Photos 10 | identifier 11 | allPhotos 12 | predicate 13 | 14 | 15 | 16 | name 17 | Last 12 Months 18 | identifier 19 | last12Months 20 | predicate 21 | date > CAST(CAST(now(), 'NSNumber') - 31536000, 'NSDate') 22 | 23 | 24 | name 25 | Flagged 26 | identifier 27 | flagged 28 | predicate 29 | flagged == 1 30 | 31 | 32 | name 33 | Rejected 34 | identifier 35 | rejected 36 | predicate 37 | rating < 0 38 | nilPredicateIncludesRejected 39 | 40 | icon 41 | NSStopProgressTemplate 42 | 43 | 44 | name 45 | Trash 46 | identifier 47 | trash 48 | predicate 49 | 50 | trashcan 51 | 52 | icon 53 | NSTrashEmpty 54 | 55 | 56 | PDImageLibraries 57 | 58 | 59 | path 60 | ~/Pictures/Photos 61 | name 62 | Photos 63 | 64 | 65 | PDLibraryAlbums 66 | 67 | 68 | PDImportProjectNameTemplate 69 | %Y-%m-%d Untitled 70 | PDMetadataGroups 71 | 72 | General 73 | 74 | file_name 75 | name 76 | title 77 | caption 78 | keywords 79 | copyright 80 | date 81 | pixel_size 82 | file_size 83 | iso_speed 84 | f_number 85 | exposure_length 86 | 87 | Photo Info 88 | 89 | name 90 | original_date 91 | digitized_date 92 | camera_make 93 | camera_model 94 | iso_speed 95 | focal_length 96 | focal_length_35mm 97 | exposure_bias 98 | f_number 99 | exposure_length 100 | flash 101 | white_balance 102 | exposure_program 103 | metering_mode 104 | exposure_mode 105 | focus_mode 106 | profile_name 107 | pixel_size 108 | orientation 109 | light_source 110 | sensitivity_type 111 | scene_type 112 | scene_capture_type 113 | 114 | GPS Info 115 | 116 | latitude 117 | longitude 118 | altitude 119 | direction 120 | 121 | File Info 122 | 123 | file_name 124 | file_path 125 | file_date 126 | file_size 127 | file_types 128 | active_type 129 | 130 | 131 | PDMetadataGroupOrder 132 | 133 | General 134 | Photo Info 135 | GPS Info 136 | File Info 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /resources/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /resources/en.lproj/PDAdjustmentsView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /resources/en.lproj/PDInfoView.xib: -------------------------------------------------------------------------------- 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 | 54 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /resources/en.lproj/PDWindow.xib: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /resources/en.lproj/image-properties.strings: -------------------------------------------------------------------------------- 1 | 2 | "name" = "Name"; 3 | "active_type" = "Active Type"; 4 | "file_types" = "File Types"; 5 | "pixel_width" = "Pixel Width"; 6 | "pixel_height" = "Pixel Height"; 7 | "pixel_size" = "Pixel Size"; 8 | "orientation" = "Orientation"; 9 | "color_model" = "Color Model"; 10 | "profile_name" = "Color Profile"; 11 | 12 | "title" = "Title"; 13 | "caption" = "Caption"; 14 | "keywords" = "Keywords"; 15 | "copyright" = "Copyright"; 16 | "rating" = "Rating"; 17 | "flagged" = "Flagged"; 18 | "hidden" = "Hidden"; 19 | "deleted" = "Deleted"; 20 | 21 | "altitude" = "Altitude"; 22 | "aperture" = "Aperture"; 23 | "camera_make" = "Camera Make"; 24 | "camera_model" = "Camera Model"; 25 | "camera_software" = "Camera Software"; 26 | "Contrast" = "Contrast"; 27 | "digitized_date" = "Digitized Date"; 28 | "direction" = "Direction"; 29 | "direction_ref" = "Direction Reference"; 30 | "exposure_bias" = "Exposure Bias"; 31 | "exposure_length" = "Shutter Speed"; 32 | "exposure_mode" = "Exposure Mode"; 33 | "exposure_program" = "Exposure Program"; 34 | "flash" = "Flash"; 35 | "flash_compensation" = "Flash Compensation"; 36 | "f_number" = "Aperture"; 37 | "focal_length" = "Focal Length"; 38 | "focal_length_35mm" = "Focal Length (35mm equivalent)"; 39 | "focus_mode" = "Focus Mode"; 40 | "iso_speed" = "ISO"; 41 | "image_stabilization" = "Image Stabilization"; 42 | "latitude" = "Latitude"; 43 | "light_source" = "Light Source"; 44 | "longitude" = "Longitude"; 45 | "max_aperture" = "Maximum Aperture"; 46 | "metering_mode" = "Metering Mode"; 47 | "original_date" = "Original Date"; 48 | "saturation" = "Saturation"; 49 | "scene_capture_type" = "Scene Capture Type"; 50 | "scene_type" = "Scene Type"; 51 | "sensitivity_type" = "Sensitivity Type"; 52 | "sharpness" = "Sharpness"; 53 | "white_balance" = "White Balance"; 54 | 55 | "date" = "Date"; 56 | "file_name" = "File Name"; 57 | "file_size" = "File Size"; 58 | "file_date" = "File Date"; 59 | "file_path" = "File Path"; 60 | "rejected" = "Rejected"; 61 | -------------------------------------------------------------------------------- /resources/import-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsh1/phod/920729b91beac440cd7734dc7ea9a87815afc6e4/resources/import-icon.png -------------------------------------------------------------------------------- /src/PDAdjustmentsViewController.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDViewController.h" 26 | 27 | @interface PDAdjustmentsViewController : PDViewController 28 | @end 29 | -------------------------------------------------------------------------------- /src/PDAdjustmentsViewController.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDAdjustmentsViewController.h" 26 | 27 | #import "PDWindowController.h" 28 | 29 | @implementation PDAdjustmentsViewController 30 | 31 | + (NSString *)viewNibName 32 | { 33 | return @"PDAdjustmentsView"; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /src/PDAppDelegate.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | extern NSString *const PDBackgroundActivityDidChange; 28 | 29 | @class PDWindowController; 30 | 31 | @interface PDAppDelegate : NSObject 32 | 33 | @property(nonatomic, strong) IBOutlet PDWindowController *windowController; 34 | 35 | @property(nonatomic, weak) IBOutlet NSMenu *photosMenu; 36 | @property(nonatomic, weak) IBOutlet NSMenu *viewMenu; 37 | @property(nonatomic, weak) IBOutlet NSMenu *windowMenu; 38 | 39 | - (IBAction)showWindow:(id)sender; 40 | 41 | @property(nonatomic, readonly) BOOL backgroundActivity; 42 | 43 | - (void)addBackgroundActivity:(id)obj; 44 | - (void)removeBackgroundActivity:(id)obj; 45 | 46 | - (void)popUpImageContextMenuWithEvent:(NSEvent *)e forView:(NSView *)view; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /src/PDAppDelegate.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDAppDelegate.h" 26 | 27 | #import "PDWindowController.h" 28 | 29 | NSString *const PDBackgroundActivityDidChange = @"PDBackgroundActivityDidChange"; 30 | 31 | @implementation PDAppDelegate 32 | { 33 | NSMenu *_imageContextMenu; 34 | NSMutableSet *_backgroundActivity; 35 | } 36 | 37 | @synthesize windowController = _windowController; 38 | @synthesize photosMenu = _photosMenu; 39 | @synthesize viewMenu = _viewMenu; 40 | @synthesize windowMenu = _windowMenu; 41 | 42 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 43 | { 44 | NSString *path = [[NSBundle mainBundle] 45 | pathForResource:@"defaults" ofType:@"plist"]; 46 | if (path != nil) 47 | { 48 | NSData *data = [NSData dataWithContentsOfFile:path]; 49 | 50 | if (data != nil) 51 | { 52 | NSDictionary *dict = [NSPropertyListSerialization 53 | propertyListWithData:data options: 54 | NSPropertyListImmutable format:nil 55 | error:nil]; 56 | if (dict != nil) 57 | [[NSUserDefaults standardUserDefaults] registerDefaults:dict]; 58 | } 59 | } 60 | 61 | [self showWindow:self]; 62 | } 63 | 64 | - (IBAction)showWindow:(id)sender 65 | { 66 | [_windowController showWindow:sender]; 67 | } 68 | 69 | - (BOOL)backgroundActivity 70 | { 71 | return _backgroundActivity.count != 0; 72 | } 73 | 74 | - (void)addBackgroundActivity:(NSString *)name 75 | { 76 | if (_backgroundActivity == nil) 77 | _backgroundActivity = [NSMutableSet set]; 78 | 79 | NSInteger count = _backgroundActivity.count; 80 | 81 | [_backgroundActivity addObject:name]; 82 | 83 | if (count == 0) 84 | { 85 | [[NSNotificationCenter defaultCenter] 86 | postNotificationName:PDBackgroundActivityDidChange object:self]; 87 | } 88 | } 89 | 90 | - (void)removeBackgroundActivity:(NSString *)name 91 | { 92 | if (_backgroundActivity == nil) 93 | return; 94 | 95 | NSInteger count = _backgroundActivity.count; 96 | 97 | [_backgroundActivity removeObject:name]; 98 | 99 | if (count != 0 && _backgroundActivity.count == 0) 100 | { 101 | [[NSNotificationCenter defaultCenter] 102 | postNotificationName:PDBackgroundActivityDidChange object:self]; 103 | } 104 | } 105 | 106 | - (void)popUpImageContextMenuWithEvent:(NSEvent *)e forView:(NSView *)view 107 | { 108 | if (_imageContextMenu == nil) 109 | { 110 | _imageContextMenu = [_photosMenu copy]; 111 | [_imageContextMenu setDelegate:self]; 112 | } 113 | 114 | [NSMenu popUpContextMenu:_imageContextMenu withEvent:e forView:view 115 | withFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; 116 | } 117 | 118 | // NSMenuDelegate methods 119 | 120 | - (void)menuNeedsUpdate:(NSMenu *)menu 121 | { 122 | if (menu == _photosMenu || menu == _imageContextMenu) 123 | { 124 | for (NSMenuItem *item in menu.itemArray) 125 | { 126 | SEL sel = item.action; 127 | if (sel == @selector(toggleFlaggedAction:)) 128 | item.state = _windowController.flaggedState; 129 | else if (sel == @selector(toggleHiddenAction:)) 130 | item.state = _windowController.hiddenState; 131 | else if (sel == @selector(toggleDeletedAction:)) 132 | item.state = _windowController.deletedState; 133 | else if (sel == @selector(toggleRawAction:)) 134 | item.state = _windowController.rawState; 135 | } 136 | } 137 | else if (menu == _viewMenu) 138 | { 139 | NSInteger sidebarMode = _windowController.sidebarMode; 140 | NSInteger contentMode = _windowController.contentMode; 141 | 142 | for (NSMenuItem *item in menu.itemArray) 143 | { 144 | SEL sel = item.action; 145 | if (sel == @selector(setSidebarModeAction:)) 146 | item.state = sidebarMode == item.tag; 147 | else if (sel == @selector(setContentModeAction:)) 148 | item.state = contentMode == item.tag; 149 | else if (sel == @selector(toggleListMetadata:)) 150 | item.state = _windowController.displaysListMetadata; 151 | else if (sel == @selector(toggleImageMetadata:)) 152 | item.state = _windowController.displaysImageMetadata; 153 | else if (sel == @selector(toggleShowsHiddenImages:)) 154 | item.state = _windowController.showsHiddenImages; 155 | } 156 | } 157 | else if (menu == _windowMenu) 158 | { 159 | BOOL sidebarVisible = _windowController.sidebarVisible; 160 | 161 | for (NSMenuItem *item in menu.itemArray) 162 | { 163 | SEL sel = item.action; 164 | if (sel == @selector(toggleSidebarAction:)) 165 | item.hidden = item.tag != sidebarVisible; 166 | } 167 | } 168 | } 169 | 170 | @end 171 | -------------------------------------------------------------------------------- /src/PDAppKitExtensions.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | @interface NSView (PDAppKitExtensions) 28 | 29 | - (void)scrollRectToVisible:(NSRect)rect animated:(BOOL)flag; 30 | 31 | - (void)flashScrollersIfNeeded; 32 | 33 | @end 34 | 35 | 36 | @interface NSCell (PDAppKitExtensions) 37 | 38 | @property(getter=isVerticallyCentered) BOOL verticallyCentered; 39 | 40 | @end 41 | 42 | 43 | @interface NSTableView (PDAppKitExtensions) 44 | 45 | - (void)reloadDataForRow:(NSInteger)row; 46 | 47 | @end 48 | 49 | @interface NSOutlineView (PDAppKitExtensions) 50 | 51 | - (NSArray *)selectedItems; 52 | - (void)setSelectedItems:(NSArray *)array; 53 | 54 | - (void)setSelectedRow:(NSInteger)row; 55 | 56 | - (void)callPreservingSelectedRows:(void (^)(void))thunk; 57 | 58 | - (void)reloadDataPreservingSelectedRows; 59 | 60 | @end 61 | 62 | enum 63 | { 64 | PDImage_Computer, 65 | PDImage_GenericFolder, 66 | PDImage_GenericHardDisk, 67 | PDImage_GenericRemovableDisk, 68 | PDImage_SmartFolder, 69 | PDImage_ImportFolder, 70 | PDImageCount, 71 | }; 72 | 73 | extern NSImage *PDImageWithName(NSInteger name); 74 | -------------------------------------------------------------------------------- /src/PDAppKitExtensions.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDAppKitExtensions.h" 26 | 27 | @implementation NSView (PDAppKitExtensions) 28 | 29 | /* -[NSView scrollRectToVisible:] seems to randomly pick animated or 30 | non-animated, I can't figure out how to control it.. */ 31 | 32 | - (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)flag 33 | { 34 | NSScrollView *scrollView = self.enclosingScrollView; 35 | NSClipView *clipView = scrollView.contentView; 36 | 37 | CGRect bounds = clipView.bounds; 38 | 39 | if (rect.origin.x < bounds.origin.x) 40 | bounds.origin.x = rect.origin.x; 41 | else if (rect.origin.x + rect.size.width > bounds.origin.x + bounds.size.width) 42 | bounds.origin.x = rect.origin.x + rect.size.width - bounds.size.width; 43 | 44 | if (rect.origin.y < bounds.origin.y) 45 | bounds.origin.y = rect.origin.y; 46 | else if (rect.origin.y + rect.size.height > bounds.origin.y + bounds.size.height) 47 | bounds.origin.y = rect.origin.y + rect.size.height - bounds.size.height; 48 | 49 | bounds = [clipView constrainBoundsRect:bounds]; 50 | 51 | if (flag) 52 | clipView.animator.bounds = bounds; 53 | else 54 | clipView.bounds = bounds; 55 | 56 | [scrollView reflectScrolledClipView:clipView]; 57 | } 58 | 59 | - (void)flashScrollersIfNeeded 60 | { 61 | NSScrollView *scrollView = self.enclosingScrollView; 62 | 63 | if (self.frame.size.height > scrollView.bounds.size.height) 64 | [scrollView flashScrollers]; 65 | } 66 | 67 | @end 68 | 69 | @implementation NSCell (PDAppKitExtensions) 70 | 71 | // vCentered is private, but it's impossible to resist.. 72 | 73 | - (BOOL)isVerticallyCentered 74 | { 75 | return _cFlags.vCentered; 76 | } 77 | 78 | - (void)setVerticallyCentered:(BOOL)flag 79 | { 80 | _cFlags.vCentered = flag ? YES : NO; 81 | } 82 | 83 | @end 84 | 85 | 86 | @implementation NSTableView (PDAppKitExtensions) 87 | 88 | - (void)reloadDataForRow:(NSInteger)row 89 | { 90 | NSIndexSet *rows = [NSIndexSet indexSetWithIndex:row]; 91 | NSIndexSet *cols = [NSIndexSet indexSetWithIndexesInRange: 92 | NSMakeRange(0, self.tableColumns.count)]; 93 | 94 | [self reloadDataForRowIndexes:rows columnIndexes:cols]; 95 | } 96 | 97 | @end 98 | 99 | @implementation NSOutlineView (PDAppKitExtensions) 100 | 101 | - (NSArray *)selectedItems 102 | { 103 | NSIndexSet *sel = self.selectedRowIndexes; 104 | 105 | if (sel.count == 0) 106 | return @[]; 107 | 108 | NSMutableArray *array = [NSMutableArray array]; 109 | 110 | NSInteger idx; 111 | for (idx = [sel firstIndex]; idx != NSNotFound; 112 | idx = [sel indexGreaterThanIndex:idx]) 113 | { 114 | [array addObject:[self itemAtRow:idx]]; 115 | } 116 | 117 | return array; 118 | } 119 | 120 | - (void)setSelectedItems:(NSArray *)array 121 | { 122 | if (array.count == 0) 123 | { 124 | [self selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO]; 125 | return; 126 | } 127 | 128 | NSMutableIndexSet *sel = [NSMutableIndexSet indexSet]; 129 | 130 | for (id item in array) 131 | { 132 | NSInteger idx = [self rowForItem:item]; 133 | if (idx >= 0) 134 | [sel addIndex:idx]; 135 | } 136 | 137 | [self selectRowIndexes:sel byExtendingSelection:NO]; 138 | } 139 | 140 | - (void)setSelectedRow:(NSInteger)row 141 | { 142 | [self selectRowIndexes:[NSIndexSet indexSetWithIndex:row] 143 | byExtendingSelection:NO]; 144 | } 145 | 146 | - (void)callPreservingSelectedRows:(void (^)(void))thunk; 147 | { 148 | NSArray *sel = self.selectedItems; 149 | 150 | thunk(); 151 | 152 | self.selectedItems = sel; 153 | } 154 | 155 | - (void)reloadDataPreservingSelectedRows 156 | { 157 | [self callPreservingSelectedRows:^{ 158 | [self reloadData]; 159 | }]; 160 | } 161 | 162 | @end 163 | 164 | NSImage * 165 | PDImageWithName(NSInteger name) 166 | { 167 | static NSImage *_images[PDImageCount]; 168 | 169 | if (name >= 0 && name < PDImageCount) 170 | { 171 | if (_images[name] == nil) 172 | { 173 | NSString *imageName = nil; 174 | OSType typeCode = 0; 175 | 176 | switch (name) 177 | { 178 | case PDImage_Computer: 179 | typeCode = kComputerIcon; 180 | break; 181 | 182 | case PDImage_GenericFolder: 183 | imageName = NSImageNameFolder; 184 | break; 185 | 186 | case PDImage_GenericHardDisk: 187 | typeCode = kGenericHardDiskIcon; 188 | break; 189 | 190 | case PDImage_GenericRemovableDisk: 191 | typeCode = kGenericRemovableMediaIcon; 192 | break; 193 | 194 | case PDImage_SmartFolder: 195 | imageName = NSImageNameFolderSmart; 196 | break; 197 | 198 | case PDImage_ImportFolder: 199 | imageName = @"import-icon"; 200 | break; 201 | } 202 | 203 | if (imageName != nil) 204 | { 205 | _images[name] = [NSImage imageNamed:imageName]; 206 | } 207 | else if (typeCode != 0) 208 | { 209 | _images[name] = [[NSWorkspace sharedWorkspace] iconForFileType: 210 | NSFileTypeForHFSTypeCode(typeCode)]; 211 | } 212 | } 213 | 214 | return _images[name]; 215 | } 216 | 217 | return nil; 218 | } 219 | -------------------------------------------------------------------------------- /src/PDColor.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | @interface PDColor : NSColor 28 | 29 | + (NSColor *)controlTextColor; 30 | + (NSColor *)disabledControlTextColor; 31 | + (NSColor *)controlTextColor:(BOOL)disabled; 32 | 33 | + (NSColor *)controlDetailTextColor; 34 | + (NSColor *)disabledControlDetailTextColor; 35 | + (NSColor *)controlDetailTextColor:(BOOL)disabled; 36 | 37 | + (NSColor *)controlBackgroundColor; 38 | + (NSColor *)darkControlBackgroundColor; 39 | + (NSArray *)controlAlternatingRowBackgroundColors; 40 | 41 | + (NSColor *)imageGridBackgroundColor; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /src/PDColor.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDColor.h" 26 | 27 | #define BG_HUE (202./360.) 28 | 29 | @implementation PDColor 30 | 31 | + (NSColor *)controlTextColor 32 | { 33 | static NSColor *color; 34 | 35 | if (color == nil) 36 | color = [NSColor colorWithDeviceWhite:.25 alpha:1]; 37 | 38 | return color; 39 | } 40 | 41 | + (NSColor *)disabledControlTextColor 42 | { 43 | static NSColor *color; 44 | 45 | if (color == nil) 46 | color = [NSColor colorWithDeviceWhite:.45 alpha:1]; 47 | 48 | return color; 49 | } 50 | 51 | + (NSColor *)controlTextColor:(BOOL)disabled 52 | { 53 | return !disabled ? [self controlTextColor] 54 | : [self disabledControlTextColor]; 55 | } 56 | 57 | + (NSColor *)controlDetailTextColor 58 | { 59 | static NSColor *color; 60 | 61 | if (color == nil) 62 | { 63 | color = [NSColor colorWithDeviceRed:197/255. green:56/255. 64 | blue:51/255. alpha:1]; 65 | } 66 | 67 | return color; 68 | } 69 | 70 | + (NSColor *)disabledControlDetailTextColor 71 | { 72 | static NSColor *color; 73 | 74 | if (color == nil) 75 | { 76 | color = [NSColor colorWithDeviceRed:197/255. green:121/255. 77 | blue:118/255. alpha:1]; 78 | } 79 | 80 | return color; 81 | } 82 | 83 | + (NSColor *)controlDetailTextColor:(BOOL)disabled 84 | { 85 | return !disabled ? [self controlDetailTextColor] 86 | : [self disabledControlDetailTextColor]; 87 | } 88 | 89 | + (NSColor *)controlBackgroundColor 90 | { 91 | static NSColor *color; 92 | 93 | if (color == nil) 94 | { 95 | color = [NSColor colorWithCalibratedHue:BG_HUE saturation:.01 96 | brightness:.96 alpha:1]; 97 | } 98 | 99 | return color; 100 | } 101 | 102 | + (NSColor *)darkControlBackgroundColor 103 | { 104 | static NSColor *color; 105 | 106 | if (color == nil) 107 | { 108 | color = [NSColor colorWithCalibratedHue:BG_HUE saturation:.03 109 | brightness:.91 alpha:1]; 110 | } 111 | 112 | return color; 113 | } 114 | 115 | + (NSArray *)controlAlternatingRowBackgroundColors 116 | { 117 | static NSArray *colors; 118 | 119 | if (colors == nil) 120 | { 121 | colors = @[[self controlBackgroundColor], 122 | [self darkControlBackgroundColor]]; 123 | } 124 | 125 | return colors; 126 | } 127 | 128 | + (NSColor *)imageGridBackgroundColor 129 | { 130 | static NSColor *color; 131 | 132 | if (color == nil) 133 | { 134 | color = [NSColor colorWithCalibratedHue:BG_HUE saturation:.03 135 | brightness:.4 alpha:1]; 136 | } 137 | 138 | return color; 139 | } 140 | 141 | @end 142 | -------------------------------------------------------------------------------- /src/PDFileCatalog.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | @interface PDFileCatalog : NSObject 28 | 29 | - (id)init; 30 | - (id)initWithContentsOfFile:(NSString *)path; 31 | 32 | - (void)invalidate; 33 | 34 | /* Writes the current contents of the catalog to 'path'. If the 35 | receiver was initialized from the contents of a file, 'path' should 36 | be the same file (i.e. no data will be written if the receiver 37 | believes that it has not changed since being initialized). */ 38 | 39 | - (void)synchronizeWithContentsOfFile:(NSString *)path; 40 | 41 | /* Returns the identifier of 'path'. The interpretation of 'path' is 42 | wholly defined by the caller, though if -renameDirectory:to: is 43 | later called, it should be formatted as a (possibly relative) file 44 | system path string, i.e. with components separated by the normal 45 | path separator string. */ 46 | 47 | - (uint32_t)fileIdForPath:(NSString *)path; 48 | 49 | /* The set of all ids currently stored in the catalog. */ 50 | 51 | @property(nonatomic, strong, readonly) NSIndexSet *allFileIds; 52 | 53 | /* Calling these methods is preferred but optional. If files are moved 54 | around and these methods are not called, new ids will be created for 55 | files when they are next seen. */ 56 | 57 | - (void)renameDirectory:(NSString *)oldName to:(NSString *)newName; 58 | - (void)renameFile:(NSString *)oldName to:(NSString *)newName; 59 | - (void)removeFileWithPath:(NSString *)path; 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /src/PDFileCatalog.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDFileCatalog.h" 26 | 27 | @implementation PDFileCatalog 28 | { 29 | dispatch_queue_t _queue; 30 | NSMutableDictionary *_dict[2]; 31 | uint32_t _lastFileId; 32 | BOOL _dirty; 33 | } 34 | 35 | - (id)init 36 | { 37 | self = [super init]; 38 | if (self != nil) 39 | { 40 | _queue = dispatch_queue_create("PDFileCatalog", DISPATCH_QUEUE_SERIAL); 41 | _dict[1] = [[NSMutableDictionary alloc] init]; 42 | } 43 | return self; 44 | } 45 | 46 | - (id)initWithContentsOfFile:(NSString *)path 47 | { 48 | self = [self init]; 49 | if (self != nil) 50 | { 51 | NSData *data = [[NSData alloc] initWithContentsOfFile:path]; 52 | if (data != nil) 53 | { 54 | id obj = [NSJSONSerialization JSONObjectWithData:data 55 | options:0 error:nil]; 56 | if ([obj isKindOfClass:[NSDictionary class]]) 57 | { 58 | _dict[0] = [obj[@"catalog"] mutableCopy]; 59 | _lastFileId = [obj[@"lastFileId"] unsignedIntValue]; 60 | } 61 | } 62 | } 63 | return self; 64 | } 65 | 66 | - (void)invalidate 67 | { 68 | if (_queue != nil) 69 | { 70 | dispatch_sync(_queue, ^{}); 71 | _queue = nil; 72 | } 73 | 74 | _dict[0] = nil; 75 | _dict[1] = nil; 76 | } 77 | 78 | - (void)dealloc 79 | { 80 | [self invalidate]; 81 | } 82 | 83 | - (void)synchronizeWithContentsOfFile:(NSString *)path 84 | { 85 | if (_queue == nil) 86 | return; 87 | 88 | /* _dirty is only set when files are renamed or new ids are added to 89 | _dict[1]. So we also check if _dict[0] is non-empty, in that case 90 | the current state is different to what was read from the file 91 | system. */ 92 | 93 | dispatch_sync(_queue, ^ 94 | { 95 | if (_dirty || _dict[0].count != 0) 96 | { 97 | NSDictionary *obj = @{ 98 | @"catalog": _dict[1], 99 | @"lastFileId": @(_lastFileId) 100 | }; 101 | 102 | NSData *data = [NSJSONSerialization 103 | dataWithJSONObject:obj options:0 error:nil]; 104 | 105 | if ([data writeToFile:path atomically:YES]) 106 | { 107 | _dirty = NO; 108 | _dict[0] = nil; 109 | } 110 | else 111 | [[NSFileManager defaultManager] removeItemAtPath:path error:nil]; 112 | } 113 | }); 114 | } 115 | 116 | - (void)renameDirectory:(NSString *)oldName to:(NSString *)newName 117 | { 118 | if (_queue == NULL) 119 | return; 120 | 121 | /* Calling this method is preferred but optional. If directories are 122 | renamed without doing so we'd just recreate the caches under the 123 | new names and purge the old state after relaunching a couple of 124 | times. */ 125 | 126 | dispatch_async(_queue, ^ 127 | { 128 | NSInteger old_len = oldName.length; 129 | 130 | for (int pass = 0; pass < 2; pass++) 131 | { 132 | NSMutableDictionary *catalog = _dict[pass]; 133 | 134 | /* Cons up the list of known files under the moved directory 135 | (can't modify the dictionary while iterating over its keys). */ 136 | 137 | NSMutableArray *matches = [[NSMutableArray alloc] init]; 138 | 139 | NSString *oldDir = [oldName stringByAppendingString:@"/"]; 140 | 141 | for (NSString *key in catalog) 142 | { 143 | if ([key hasPrefix:oldDir]) 144 | [matches addObject:key]; 145 | } 146 | 147 | if (matches.count != 0) 148 | { 149 | for (NSString *key in matches) 150 | { 151 | NSString *new_key = [newName stringByAppendingPathComponent: 152 | [key substringFromIndex:old_len + 1]]; 153 | catalog[new_key] = catalog[key]; 154 | [catalog removeObjectForKey:key]; 155 | } 156 | 157 | _dirty = YES; 158 | } 159 | } 160 | }); 161 | } 162 | 163 | - (void)renameFile:(NSString *)oldName to:(NSString *)newName 164 | { 165 | if (_queue == NULL) 166 | return; 167 | 168 | dispatch_async(_queue, ^ 169 | { 170 | for (int pass = 0; pass < 2; pass++) 171 | { 172 | NSMutableDictionary *catalog = _dict[pass]; 173 | 174 | id value = catalog[oldName]; 175 | if (value != nil) 176 | { 177 | catalog[newName] = value; 178 | [catalog removeObjectForKey:oldName]; 179 | } 180 | } 181 | }); 182 | } 183 | 184 | - (void)removeFileWithPath:(NSString *)path 185 | { 186 | if (_queue == NULL) 187 | return; 188 | 189 | dispatch_async(_queue, ^ 190 | { 191 | for (int pass = 0; pass < 2; pass++) 192 | { 193 | NSMutableDictionary *catalog = _dict[pass]; 194 | 195 | if (!_dirty && catalog[path] != nil) 196 | _dirty = YES; 197 | 198 | [catalog removeObjectForKey:path]; 199 | 200 | /* FIXME: also remove anything in the cache for these ids? */ 201 | } 202 | }); 203 | } 204 | 205 | - (uint32_t)fileIdForPath:(NSString *)path 206 | { 207 | if (_queue == NULL) 208 | return 0; 209 | 210 | __block uint32_t fid = 0; 211 | 212 | dispatch_sync(_queue, ^ 213 | { 214 | NSNumber *obj = _dict[1][path]; 215 | 216 | if (obj == nil) 217 | { 218 | obj = _dict[0][path]; 219 | 220 | /* We place the deserialized dictionary in _dict[0], and the 221 | current dictionary in _dict[1]. We know that all extant 222 | files will have their ids queried at least once when the 223 | library is scanned on startup, so by moving entries from 224 | the old to new dictionaries we effectively remove stale 225 | entries from the current version. */ 226 | 227 | if (obj != nil) 228 | { 229 | _dict[1][path] = obj; 230 | [_dict[0] removeObjectForKey:path]; 231 | } 232 | } 233 | 234 | if (obj != nil) 235 | fid = [obj unsignedIntValue]; 236 | else 237 | { 238 | fid = ++_lastFileId; 239 | 240 | [_dict[1] setObject:@(fid) forKey:path]; 241 | _dirty = YES; 242 | } 243 | }); 244 | 245 | return fid; 246 | } 247 | 248 | - (NSIndexSet *)allFileIds 249 | { 250 | if (_queue == NULL) 251 | return nil; 252 | 253 | __block NSIndexSet *ret = nil; 254 | 255 | dispatch_sync(_queue, ^ 256 | { 257 | NSMutableIndexSet *catalog = [NSMutableIndexSet indexSet]; 258 | 259 | for (int i = 0; i < 2; i++) 260 | for (NSString *key in _dict[i]) 261 | [catalog addIndex:[_dict[i][key] unsignedIntValue]]; 262 | 263 | ret = catalog; 264 | }); 265 | 266 | return ret; 267 | } 268 | 269 | @end 270 | -------------------------------------------------------------------------------- /src/PDFileManager.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | @protocol PDFileManagerDelegate; 28 | 29 | @interface PDFileManager : NSObject 30 | 31 | + (PDFileManager *)fileManagerWithPath:(NSString *)path; 32 | + (PDFileManager *)fileManagerWithPropertyListRepresentation:(id)obj; 33 | 34 | /* May return a non-null plist-compatible object that can be passed 35 | to fileManagerWithPropertyListRepresentation: to reinstantiate the 36 | file manager. */ 37 | 38 | - (id)propertyListRepresentation; 39 | 40 | - (BOOL)isEqualToPath:(NSString *)path; 41 | - (BOOL)isEqualToPropertyListRepresentation:(id)obj; 42 | 43 | @property(nonatomic, weak) id delegate; 44 | 45 | /* Some kind of default name for the location. E.g. last path component. */ 46 | 47 | - (NSString *)name; 48 | 49 | /* String describing this file manager. Used in error reporting. */ 50 | 51 | - (NSString *)localizedDescription; 52 | 53 | /* An icon representing the referenced location, or nil. */ 54 | 55 | - (NSImage *)iconImage; 56 | 57 | /* Will be called by -dealloc. Can be called at any time once no more 58 | file operations will be performed. */ 59 | 60 | - (void)invalidate; 61 | 62 | /* Unmount the file system if possible. */ 63 | 64 | - (void)unmount; 65 | 66 | /* Constructs an absolute URL referencing the named file. This operation 67 | may fail, e.g. when direct file access is not supported. */ 68 | 69 | - (NSURL *)fileURLWithPath:(NSString *)path; 70 | 71 | /* Operations for reading file attributes. */ 72 | 73 | - (BOOL)fileExistsAtPath:(NSString *)path; 74 | - (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(BOOL *)dirp; 75 | - (time_t)mtimeOfFileAtPath:(NSString *)path; 76 | - (size_t)sizeOfFileAtPath:(NSString *)path; 77 | 78 | /* Operations for reading file content. */ 79 | 80 | - (NSData *)contentsOfFileAtPath:(NSString *)path; 81 | - (NSArray *)contentsOfDirectoryAtPath:(NSString *)path; 82 | - (CGImageSourceRef)copyImageSourceAtPath:(NSString *)path; 83 | 84 | /* Operations for writing file content. */ 85 | 86 | - (BOOL)writeData:(NSData *)data toFile:(NSString *)path 87 | options:(NSDataWritingOptions)options error:(NSError **)err; 88 | - (BOOL)createDirectoryAtPath:(NSString *)path 89 | withIntermediateDirectories:(BOOL)flag attributes:(NSDictionary *)dict 90 | error:(NSError **)err; 91 | 92 | /* Operations for moving, copying and deleting files. */ 93 | 94 | - (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath 95 | error:(NSError **)err; 96 | - (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath 97 | error:(NSError **)err; 98 | - (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)err; 99 | 100 | @end 101 | 102 | @protocol PDFileManagerDelegate 103 | @optional 104 | 105 | /* FIXME add methods here. E.g. mount/unmount notifications? */ 106 | 107 | @end 108 | -------------------------------------------------------------------------------- /src/PDFileManager.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDFileManager.h" 26 | 27 | #import "PDLocalFileManager.h" 28 | 29 | #define ERROR_DOMAIN @"org.unfactored.PDFileManager" 30 | 31 | @implementation PDFileManager 32 | 33 | + (PDFileManager *)fileManagerWithPath:(NSString *)path 34 | { 35 | return [[PDLocalFileManager alloc] initWithPath:path]; 36 | } 37 | 38 | + (PDFileManager *)fileManagerWithPropertyListRepresentation:(id)obj 39 | { 40 | PDFileManager *fm; 41 | 42 | fm = [[PDLocalFileManager alloc] initWithPropertyListRepresentation:obj]; 43 | if (fm != nil) 44 | return fm; 45 | 46 | return nil; 47 | } 48 | 49 | - (id)propertyListRepresentation 50 | { 51 | return nil; 52 | } 53 | 54 | - (BOOL)isEqualToPath:(NSString *)path 55 | { 56 | return NO; 57 | } 58 | 59 | - (BOOL)isEqualToPropertyListRepresentation:(id)obj 60 | { 61 | return NO; 62 | } 63 | 64 | - (id)delegate 65 | { 66 | return nil; 67 | } 68 | 69 | - (void)setDelegate:(id)delegate 70 | { 71 | } 72 | 73 | - (NSString *)name 74 | { 75 | return @"Unknown"; 76 | } 77 | 78 | - (NSString *)localizedDescription 79 | { 80 | return @"Unknown"; 81 | } 82 | 83 | - (NSImage *)iconImage 84 | { 85 | return nil; 86 | } 87 | 88 | - (void)invalidate 89 | { 90 | } 91 | 92 | - (void)unmount 93 | { 94 | } 95 | 96 | - (NSURL *)fileURLWithPath:(NSString *)path 97 | { 98 | return nil; 99 | } 100 | 101 | - (BOOL)fileExistsAtPath:(NSString *)path 102 | { 103 | return NO; 104 | } 105 | 106 | - (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(BOOL *)dirp 107 | { 108 | return NO; 109 | } 110 | 111 | - (time_t)mtimeOfFileAtPath:(NSString *)path 112 | { 113 | return 0; 114 | } 115 | 116 | - (size_t)sizeOfFileAtPath:(NSString *)path 117 | { 118 | return 0; 119 | } 120 | 121 | - (NSData *)contentsOfFileAtPath:(NSString *)path 122 | { 123 | return nil; 124 | } 125 | 126 | - (NSArray *)contentsOfDirectoryAtPath:(NSString *)path 127 | { 128 | return nil; 129 | } 130 | 131 | - (CGImageSourceRef)copyImageSourceAtPath:(NSString *)path 132 | { 133 | return NULL; 134 | } 135 | 136 | static BOOL 137 | unsupported_operation(PDFileManager *self, SEL sel, NSError **err) 138 | { 139 | if (err != NULL) 140 | { 141 | NSString *str = [NSString stringWithFormat: 142 | @"File manager <%@> does not support operation %s", 143 | [self localizedDescription], sel_getName(sel)]; 144 | *err = [NSError errorWithDomain:ERROR_DOMAIN code:1 145 | userInfo:@{NSLocalizedDescriptionKey: str}]; 146 | } 147 | 148 | return NO; 149 | } 150 | 151 | - (BOOL)writeData:(NSData *)data toFile:(NSString *)path 152 | options:(NSDataWritingOptions)options error:(NSError **)err 153 | { 154 | return unsupported_operation(self, _cmd, err); 155 | } 156 | 157 | - (BOOL)createDirectoryAtPath:(NSString *)path 158 | withIntermediateDirectories:(BOOL)flag attributes:(NSDictionary *)dict 159 | error:(NSError **)err 160 | { 161 | return unsupported_operation(self, _cmd, err); 162 | } 163 | 164 | - (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath 165 | error:(NSError **)err 166 | { 167 | return unsupported_operation(self, _cmd, err); 168 | } 169 | 170 | - (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath 171 | error:(NSError **)err 172 | { 173 | return unsupported_operation(self, _cmd, err); 174 | } 175 | 176 | - (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)err 177 | { 178 | return unsupported_operation(self, _cmd, err); 179 | } 180 | 181 | @end 182 | -------------------------------------------------------------------------------- /src/PDFoundationExtensions.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | @interface NSObject (FoundationExtensions) 28 | 29 | /* Using this to avoid ARC warnings when calling -performSelector:. */ 30 | 31 | - (void)performVoidSelector:(SEL)sel withObject:(id)arg; 32 | 33 | @end 34 | 35 | @interface NSArray (PDFoundationExtensions) 36 | 37 | - (NSArray *)mappedArray:(id (^)(id))f; 38 | - (NSArray *)filteredArray:(BOOL (^)(id))f; 39 | 40 | @end 41 | 42 | 43 | @interface NSString (PDFoundationExtensions) 44 | 45 | - (BOOL)hasPathPrefix:(NSString *)path; 46 | 47 | - (NSString *)stringByRemovingPathPrefix:(NSString *)path; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /src/PDFoundationExtensions.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #if __has_feature(objc_arc) 26 | #error "Must be compiled with -fno-objc-arc" 27 | #endif 28 | 29 | #import "PDFoundationExtensions.h" 30 | 31 | #import "PDMacros.h" 32 | 33 | @implementation NSObject (PDFoundationExtensions) 34 | 35 | - (void)performVoidSelector:(SEL)sel withObject:(id)arg 36 | { 37 | [self performSelector:sel withObject:arg]; 38 | } 39 | 40 | @end 41 | 42 | @implementation NSArray (PDFoundationExtensions) 43 | 44 | - (NSArray *)mappedArray:(id (^)(id))f 45 | { 46 | NSInteger count = self.count; 47 | if (count == 0) 48 | return @[]; 49 | 50 | id *objects = STACK_ALLOC(id, count); 51 | 52 | NSInteger i = 0; 53 | for (id obj in self) 54 | objects[i++] = f(obj); 55 | 56 | NSArray *ret = [NSArray arrayWithObjects:objects count:count]; 57 | 58 | STACK_FREE(id, count, objects); 59 | 60 | return ret; 61 | } 62 | 63 | - (NSArray *)filteredArray:(BOOL (^)(id))f 64 | { 65 | NSInteger count = self.count; 66 | if (count == 0) 67 | return @[]; 68 | 69 | id *objects = STACK_ALLOC(id, count); 70 | 71 | NSInteger idx = 0; 72 | for (id obj in self) 73 | { 74 | if (f(obj)) 75 | objects[idx++] = obj; 76 | } 77 | 78 | NSArray *ret; 79 | if (idx == 0) 80 | ret = @[]; 81 | else 82 | ret = [NSArray arrayWithObjects:objects count:idx]; 83 | 84 | STACK_FREE(id, count, objects); 85 | 86 | return ret; 87 | } 88 | 89 | @end 90 | 91 | 92 | @implementation NSString (PDFoundationExtensions) 93 | 94 | - (BOOL)hasPathPrefix:(NSString *)path 95 | { 96 | NSInteger l1 = self.length; 97 | NSInteger l2 = path.length; 98 | 99 | if (l2 == 0) 100 | return YES; 101 | else if (l1 == l2) 102 | return [self isEqualToString:path]; 103 | else if (l2 > l1) 104 | return NO; 105 | else 106 | return [self characterAtIndex:l2] == '/' && [self hasPrefix:path]; 107 | } 108 | 109 | - (NSString *)stringByRemovingPathPrefix:(NSString *)path 110 | { 111 | NSInteger l1 = self.length; 112 | NSInteger l2 = path.length; 113 | 114 | if (l2 == 0) 115 | return self; 116 | else if (l1 == l2) 117 | return [self isEqualToString:path] ? @"" : self; 118 | else if (l2 > l1) 119 | return self; 120 | else 121 | return ([self characterAtIndex:l2] == '/' && [self hasPrefix:path] 122 | ? [self substringFromIndex:l2+1] : self); 123 | } 124 | 125 | @end 126 | -------------------------------------------------------------------------------- /src/PDImageGridView.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDViewController.h" 26 | 27 | @class PDImageListViewController, PDImage; 28 | 29 | @interface PDImageGridView : NSView 30 | 31 | @property(nonatomic, weak) IBOutlet PDImageListViewController *controller; 32 | 33 | @property(nonatomic, copy) NSArray *images; 34 | @property(nonatomic, assign) NSInteger primarySelection; 35 | @property(nonatomic, copy) NSIndexSet *selection; 36 | @property(nonatomic, assign) CGFloat scale; 37 | @property(nonatomic, assign) BOOL displaysMetadata; 38 | 39 | - (NSRect)boundingRectOfItemAtIndex:(NSInteger)idx; 40 | 41 | - (void)scrollToPrimaryAnimated:(BOOL)flag; 42 | 43 | - (PDImage *)imageAtSuperviewPoint:(NSPoint)p; 44 | 45 | - (BOOL)imageMayBeVisible:(PDImage *)image; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /src/PDImageLayer.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | #import "PDImage.h" 28 | 29 | @interface PDImageLayer : CALayer 30 | 31 | @property(nonatomic, retain) PDImage *image; 32 | 33 | @property(nonatomic, getter=isThumbnail) BOOL thumbnail; 34 | 35 | @property(nonatomic, strong) __attribute__((NSObject)) CGColorSpaceRef colorSpace; 36 | 37 | - (void)invalidate; 38 | 39 | - (void)removeContent; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /src/PDImageLayer.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDImageLayer.h" 26 | 27 | #import "PDAppDelegate.h" 28 | #import "PDImage.h" 29 | 30 | #import 31 | 32 | CA_HIDDEN @interface PDImageLayerLayer : CALayer 33 | @end 34 | 35 | @implementation PDImageLayer 36 | { 37 | BOOL _imageUsesRAW; 38 | CGColorSpaceRef _colorSpace; 39 | 40 | BOOL _addedImageHost; 41 | CGSize _imageSize; 42 | OSSpinLock _imageLock; 43 | } 44 | 45 | @synthesize image = _image; 46 | @synthesize thumbnail = _thumbnail; 47 | 48 | + (id)defaultValueForKey:(NSString *)key 49 | { 50 | if ([key isEqualToString:@"backgroundColor"]) 51 | return (id)[[NSColor darkGrayColor] CGColor]; 52 | else 53 | return [super defaultValueForKey:key]; 54 | } 55 | 56 | - (id)initWithLayer:(PDImageLayer *)src 57 | { 58 | self = [super initWithLayer:src]; 59 | if (self != nil) 60 | { 61 | _image = src->_image; 62 | _thumbnail = src->_thumbnail; 63 | _colorSpace = CGColorSpaceRetain(src->_colorSpace); 64 | } 65 | return self; 66 | } 67 | 68 | - (void)invalidate 69 | { 70 | if (_addedImageHost) 71 | { 72 | [_image removeImageHost:self]; 73 | _addedImageHost = NO; 74 | } 75 | } 76 | 77 | - (void)removeContent 78 | { 79 | [self invalidate]; 80 | 81 | self.sublayers = @[]; 82 | } 83 | 84 | - (void)dealloc 85 | { 86 | [self invalidate]; 87 | CGColorSpaceRelease(_colorSpace); 88 | } 89 | 90 | - (void)setImage:(PDImage *)im 91 | { 92 | BOOL usesRAW = [im usesRAW]; 93 | 94 | if (_image != im || _imageUsesRAW != usesRAW) 95 | { 96 | if (_addedImageHost) 97 | { 98 | [_image removeImageHost:self]; 99 | _addedImageHost = NO; 100 | } 101 | 102 | /* To make -image:setHostedImage: synchronized. `old_image` is so 103 | that we release the old image outside the spin-lock. The 104 | __block qualifier isn't needed (I think) but without it the 105 | clang static analyser complains that old_image is never read. */ 106 | 107 | __block __strong id old_image = nil; 108 | 109 | OSSpinLockLock(&_imageLock); 110 | 111 | if (_image != im) 112 | { 113 | old_image = _image; 114 | _image = im; 115 | } 116 | 117 | _imageUsesRAW = usesRAW; 118 | 119 | OSSpinLockUnlock(&_imageLock); 120 | 121 | old_image = nil; 122 | 123 | ((CALayer *)[self.sublayers firstObject]).contents = nil; 124 | [self setNeedsLayout]; 125 | } 126 | } 127 | 128 | - (CGColorSpaceRef)colorSpace 129 | { 130 | return _colorSpace; 131 | } 132 | 133 | - (void)setColorSpace:(CGColorSpaceRef)space 134 | { 135 | if (_colorSpace != space) 136 | { 137 | CGColorSpaceRelease(_colorSpace); 138 | _colorSpace = CGColorSpaceRetain(space); 139 | } 140 | } 141 | 142 | - (void)layoutSublayers 143 | { 144 | if (_image == nil) 145 | return; 146 | 147 | CGRect bounds = self.bounds; 148 | CGFloat scale = self.contentsScale; 149 | 150 | CGSize size = CGSizeMake(ceil(bounds.size.width * scale), 151 | ceil(bounds.size.height * scale)); 152 | 153 | unsigned int orientation = _image.orientation; 154 | 155 | if (orientation > 4) 156 | { 157 | CGFloat t = size.width; 158 | size.width = size.height; 159 | size.height = t; 160 | } 161 | 162 | /* Use a nested layer to host the image so we can apply the 163 | orientation transform to it, without the owner of this layer 164 | needing to care. */ 165 | 166 | CALayer *image_layer = [self.sublayers firstObject]; 167 | 168 | if (image_layer == nil) 169 | { 170 | image_layer = [PDImageLayerLayer layer]; 171 | image_layer.delegate = self.delegate; 172 | [self addSublayer:image_layer]; 173 | } 174 | 175 | /* Don't call -addImageHost: etc until the image layer exists -- the 176 | images are supplied asynchronously via a concurrent queue. */ 177 | 178 | if (!_addedImageHost) 179 | { 180 | _imageSize = size; 181 | [_image addImageHost:self]; 182 | _addedImageHost = YES; 183 | } 184 | else if (!CGSizeEqualToSize(_imageSize, size)) 185 | { 186 | _imageSize = size; 187 | [_image updateImageHost:self]; 188 | } 189 | 190 | CGAffineTransform m; 191 | if (orientation >= 1 && orientation <= 8) 192 | { 193 | static const CGFloat mat[8*4] = 194 | { 195 | 1, 0, 0, 1, 196 | -1, 0, 0, 1, 197 | -1, 0, 0, -1, 198 | 1, 0, 0, -1, 199 | 0, 1, 1, 0, 200 | 0, 1, -1, 0, 201 | 0, -1, -1, 0, 202 | 0, -1, 1, 0 203 | }; 204 | 205 | m.a = mat[(orientation-1)*4+0]; 206 | m.b = mat[(orientation-1)*4+1]; 207 | m.c = mat[(orientation-1)*4+2]; 208 | m.d = mat[(orientation-1)*4+3]; 209 | m.tx = m.ty = 0; 210 | } 211 | else 212 | m = CGAffineTransformIdentity; 213 | 214 | image_layer.affineTransform = m; 215 | image_layer.frame = bounds; 216 | image_layer.contentsScale = scale; 217 | } 218 | 219 | - (NSDictionary *)imageHostOptions 220 | { 221 | NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 222 | 223 | dict[PDImageHost_Size] = [NSValue valueWithSize:_imageSize]; 224 | 225 | if (_thumbnail) 226 | dict[PDImageHost_Thumbnail] = @YES; 227 | 228 | if (_colorSpace != NULL) 229 | dict[PDImageHost_ColorSpace] = (__bridge id)_colorSpace; 230 | 231 | if (!_thumbnail) 232 | { 233 | CALayer *image_layer = [self.sublayers firstObject]; 234 | if (image_layer.contents != nil) 235 | dict[PDImageHost_NoPreview] = @YES; 236 | } 237 | 238 | return dict; 239 | } 240 | 241 | - (dispatch_queue_t)imageHostQueue 242 | { 243 | return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); 244 | } 245 | 246 | - (void)image:(PDImage *)image setHostedImage:(CGImageRef)im 247 | { 248 | /* Due to -imageHostQueue above, will be called on a background queue. */ 249 | 250 | OSSpinLockLock(&_imageLock); 251 | 252 | if (_image == image) 253 | { 254 | CALayer *image_layer = [self.sublayers firstObject]; 255 | image_layer.contents = (__bridge id)im; 256 | } 257 | 258 | OSSpinLockUnlock(&_imageLock); 259 | } 260 | 261 | @end 262 | 263 | @implementation PDImageLayerLayer 264 | 265 | + (id)defaultValueForKey:(NSString *)key 266 | { 267 | if ([key isEqualToString:@"magnificationFilter"]) 268 | return kCAFilterNearest; 269 | else if ([key isEqualToString:@"edgeAntialiasingMask"]) 270 | return @0; 271 | else 272 | return [super defaultValueForKey:key]; 273 | } 274 | 275 | @end 276 | -------------------------------------------------------------------------------- /src/PDImageLibrary.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | @class PDFileCatalog, PDFileManager, PDImage, NSImage; 28 | 29 | extern NSString *const PDImageLibraryDirectoryDidChange; 30 | 31 | @interface PDImageLibrary : NSObject 32 | 33 | + (void)removeInvalidLibraries; 34 | 35 | + (NSArray *)allLibraries; 36 | 37 | + (PDImageLibrary *)libraryWithId:(uint32_t)lid; 38 | 39 | /* Creates a new library if no existing library is found. */ 40 | 41 | + (PDImageLibrary *)libraryWithPath:(NSString *)path; 42 | + (PDImageLibrary *)libraryWithPath:(NSString *)path onlyIfExists:(BOOL)flag; 43 | 44 | + (PDImageLibrary *)libraryWithPropertyListRepresentation:(id)obj; 45 | 46 | - (id)propertyListRepresentation; 47 | 48 | - (void)invalidate; 49 | 50 | @property(nonatomic, readonly) uint32_t libraryId; 51 | 52 | @property(nonatomic, copy) NSString *name; 53 | @property(nonatomic, readonly) NSImage *iconImage; 54 | 55 | @property(nonatomic, getter=isTransient) BOOL transient; 56 | 57 | /* The path is interpreted relative to the root of the library. */ 58 | 59 | - (uint32_t)uniqueIdOfFile:(NSString *)path; 60 | 61 | /* Return the path of the cache file for object with 'file_id'. The 62 | filename will end with 'str'. */ 63 | 64 | - (NSString *)cachePathForFileId:(uint32_t)file_id base:(NSString *)str; 65 | 66 | /* Write catalog to disk (if it has changed). */ 67 | 68 | - (void)synchronize; 69 | 70 | /* Delete all cached data. */ 71 | 72 | - (void)emptyCaches; 73 | 74 | /* Unmount if possible. */ 75 | 76 | - (void)unmount; 77 | 78 | /* Constructs an absolute URL referencing the named library file. This 79 | operation may fail, e.g. for non-file libraries. */ 80 | 81 | - (NSURL *)fileURLWithPath:(NSString *)path; 82 | 83 | /* Notifications to the library that files under its path have been 84 | moved externally. */ 85 | 86 | - (void)didRenameDirectory:(NSString *)oldName to:(NSString *)newName; 87 | - (void)didRenameFile:(NSString *)oldName to:(NSString *)newName; 88 | - (void)didRemoveFileWithPath:(NSString *)rel_path; 89 | 90 | @end 91 | 92 | /** High-level image operations. These will present any errors direct 93 | to the UI, and make any updates required (the -didFoo methods). */ 94 | 95 | @interface PDImageLibrary (ImageOperations) 96 | 97 | - (void)loadImagesInSubdirectory:(NSString *)dir 98 | recursively:(BOOL)flag handler:(void (^)(PDImage *))block; 99 | 100 | + (void)removeImages:(NSArray *)images; 101 | 102 | - (void)copyImages:(NSArray *)images toDirectory:(NSString *)dir; 103 | - (void)moveImages:(NSArray *)images toDirectory:(NSString *)dir; 104 | - (void)renameDirectory:(NSString *)old_dir to:(NSString *)new_dir; 105 | - (void)createDirectory:(NSString *)dir; 106 | 107 | - (void)importImages:(NSArray *)images toDirectory:(NSString *)dir 108 | fileTypes:(NSSet *)types preferredType:(NSString *)type 109 | filenameMap:(NSString *(^)(PDImage *src, NSString *name))f 110 | properties:(NSDictionary *)dict deleteSourceImages:(BOOL)flag; 111 | 112 | @end 113 | 114 | @interface PDImageLibrary (FileOperations) 115 | 116 | /** Low-level file access. All paths are relative to the root of the 117 | library. **/ 118 | 119 | - (BOOL)fileExistsAtPath:(NSString *)path; 120 | - (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(BOOL *)dirp; 121 | - (time_t)mtimeOfFileAtPath:(NSString *)path; 122 | - (size_t)sizeOfFileAtPath:(NSString *)path; 123 | 124 | - (NSData *)contentsOfFileAtPath:(NSString *)path; 125 | - (NSArray *)contentsOfDirectoryAtPath:(NSString *)path; 126 | - (CGImageSourceRef)copyImageSourceAtPath:(NSString *)path; 127 | 128 | - (BOOL)writeData:(NSData *)data toFile:(NSString *)path 129 | options:(NSDataWritingOptions)options error:(NSError **)err; 130 | - (BOOL)createDirectoryAtPath:(NSString *)path 131 | withIntermediateDirectories:(BOOL)flag attributes:(NSDictionary *)dict 132 | error:(NSError **)err; 133 | - (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath 134 | error:(NSError **)error; 135 | - (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath 136 | error:(NSError **)error; 137 | - (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)err; 138 | 139 | - (void)foreachSubdirectoryOfDirectory:(NSString *)dir 140 | handler:(void (^)(NSString *dir_name))block; 141 | 142 | @end 143 | -------------------------------------------------------------------------------- /src/PDImageListViewController.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDViewController.h" 26 | 27 | @class PDImageGridView; 28 | 29 | @interface PDImageListViewController : PDViewController 30 | 31 | @property(nonatomic) BOOL displaysMetadata; 32 | 33 | @property(nonatomic, weak) IBOutlet NSScrollView *scrollView; 34 | @property(nonatomic, weak) IBOutlet PDImageGridView *gridView; 35 | @property(nonatomic, weak) IBOutlet NSPopUpButton *sortButton; 36 | @property(nonatomic, weak) IBOutlet NSMenu *sortMenu; 37 | @property(nonatomic, weak) IBOutlet NSTextField *titleLabel; 38 | @property(nonatomic, weak) IBOutlet NSSearchField *searchField; 39 | @property(nonatomic, weak) IBOutlet NSMenu *searchMenu; 40 | @property(nonatomic, weak) IBOutlet NSButton *predicateButton; 41 | @property(nonatomic, weak) IBOutlet NSButton *rotateLeftButton; 42 | @property(nonatomic, weak) IBOutlet NSButton *rotateRightButton; 43 | @property(nonatomic, weak) IBOutlet NSSlider *scaleSlider; 44 | 45 | - (IBAction)toggleMetadata:(id)sender; 46 | 47 | - (IBAction)sortKeyAction:(id)sender; 48 | - (IBAction)sortOrderAction:(id)sender; 49 | 50 | - (IBAction)controlAction:(id)sender; 51 | 52 | - (BOOL)performKeyEquivalent:(NSEvent *)e; 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /src/PDImageProperty.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDImage.h" 26 | 27 | extern NSDictionary *PDImageSourceCopyProperties(CGImageSourceRef src); 28 | 29 | extern NSString *PDImageLocalizedNameOfProperty(NSString *key); 30 | 31 | extern NSString *PDImageLocalizedPropertyValue(NSString *key, 32 | id value, PDImage *im); 33 | 34 | extern id PDImageUnlocalizedPropertyValue(NSString *key, NSString *str, 35 | PDImage *im); 36 | 37 | extern NSDate *PDImageParseEXIFDateString(NSString *str); 38 | 39 | extern id PDImageExpressionValues(PDImage *im); 40 | 41 | extern NSArray *PDImagePredicateEditorRowTemplates(void); 42 | -------------------------------------------------------------------------------- /src/PDImageRatingLayer.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | @interface PDImageRatingLayer : CATextLayer 28 | 29 | @property(nonatomic) int rating; 30 | 31 | @property(nonatomic, getter=isFlagged) BOOL flagged; 32 | 33 | @property(nonatomic, getter=hiddenState) BOOL hiddenState; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /src/PDImageRatingLayer.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDImageRatingLayer.h" 26 | 27 | #import 28 | 29 | @implementation PDImageRatingLayer 30 | 31 | @dynamic rating, flagged, hiddenState; 32 | 33 | + (id)defaultValueForKey:(NSString *)key 34 | { 35 | if ([key isEqualToString:@"font"]) 36 | return [NSFont boldSystemFontOfSize:[NSFont systemFontSize]]; 37 | else if ([key isEqualToString:@"fontSize"]) 38 | return @([NSFont systemFontSize]); 39 | else if ([key isEqualToString:@"truncationMode"]) 40 | return @"start"; 41 | else if ([key isEqualToString:@"anchorPoint"]) 42 | return [NSValue valueWithPoint:NSMakePoint(0, 1)]; 43 | else if ([key isEqualToString:@"backgroundColor"]) 44 | return (id)[[NSColor colorWithDeviceWhite:0 alpha:.3] CGColor]; 45 | else 46 | return [super defaultValueForKey:key]; 47 | } 48 | 49 | - (void)didChangeValueForKey:(NSString *)key 50 | { 51 | [super didChangeValueForKey:key]; 52 | 53 | if ([key isEqualToString:@"rating"] 54 | || [key isEqualToString:@"flagged"] 55 | || [key isEqualToString:@"hiddenState"]) 56 | { 57 | [self setNeedsLayout]; 58 | } 59 | } 60 | 61 | - (void)layoutSublayers 62 | { 63 | int rating = [self rating]; 64 | BOOL flagged = [self isFlagged]; 65 | BOOL hiddenState = [self hiddenState]; 66 | 67 | unichar buf[8]; 68 | size_t len = 0; 69 | 70 | if (rating > 0) 71 | { 72 | rating = rating <= 5 ? rating : 5; 73 | 74 | int i; 75 | for (i = 0; i < rating; i++) 76 | buf[len++] = 0x2605; /* BLACK STAR */ 77 | } 78 | else if (rating < 0) 79 | { 80 | buf[len++] = 0x2716; /* HEAVY MULTIPLICATION X */ 81 | } 82 | 83 | if (flagged) 84 | { 85 | if (len != 0) 86 | buf[len++] = ' '; 87 | buf[len++] = 0x2691; /* BLACK FLAG */ 88 | } 89 | 90 | if (hiddenState) 91 | { 92 | if (len != 0) 93 | buf[len++] = ' '; 94 | buf[len++] = 0x272a; /* CIRCLED WHITE STAR */ 95 | } 96 | 97 | if (len != 0) 98 | [self setString:[NSString stringWithCharacters:buf length:len]]; 99 | else 100 | [self setString:nil]; 101 | } 102 | 103 | - (CGSize)preferredFrameSize 104 | { 105 | [self layoutIfNeeded]; 106 | return [super preferredFrameSize]; 107 | } 108 | 109 | @end 110 | -------------------------------------------------------------------------------- /src/PDImageTextCell.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | @interface PDImageTextCell : NSTextFieldCell 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /src/PDImageTextCell.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDImageTextCell.h" 26 | 27 | #define BORDER 1 28 | #define SPACING 2 29 | 30 | @implementation PDImageTextCell 31 | 32 | - (CGFloat)imageWidthForHeight:(CGFloat)h 33 | { 34 | CGSize size = self.image.size; 35 | return size.width * (h / size.height); 36 | } 37 | 38 | - (CGSize)cellSize 39 | { 40 | CGSize size = [super cellSize]; 41 | if (self.image != nil) 42 | size.width += BORDER + [self imageWidthForHeight:size.height] + SPACING; 43 | return size; 44 | } 45 | 46 | - (void)drawWithFrame:(CGRect)frame inView:(NSView *)view 47 | { 48 | NSImage *image = self.image; 49 | if (image != nil) 50 | { 51 | CGRect imageFrame = frame; 52 | CGFloat width = [self imageWidthForHeight:frame.size.height]; 53 | imageFrame.origin.x += BORDER; 54 | imageFrame.size.width = fmin(width, imageFrame.size.width); 55 | frame.origin.x += BORDER + width + SPACING; 56 | frame.size.width -= width + SPACING; 57 | [image drawInRect:imageFrame fromRect:NSZeroRect operation: 58 | NSCompositeSourceOver fraction:1 respectFlipped:YES hints:nil]; 59 | } 60 | 61 | if (frame.size.width > 0) 62 | [super drawWithFrame:frame inView:view]; 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /src/PDImageUUID.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | extern NSString *const PDImageUUIDType; 28 | 29 | @class PDImage; 30 | 31 | @interface PDImageUUID : NSObject 32 | 33 | 34 | + (PDImageUUID *)imageUUIDWithUUID:(NSUUID *)uuid; 35 | + (PDImageUUID *)imageUUIDWithPropertyList:(id)obj; 36 | 37 | - (id)initWithUUID:(NSUUID *)uuid; 38 | - (id)initWithPropertyList:(id)obj; 39 | 40 | @property(nonatomic, copy, readonly) NSUUID *UUID; 41 | 42 | - (id)propertyList; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /src/PDImageUUID.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDImageUUID.h" 26 | 27 | #import "PDImage.h" 28 | #import "PDImageLibrary.h" 29 | 30 | NSString *const PDImageUUIDType = @"org.unfactored.Phod.PDImageUUID"; 31 | 32 | @implementation PDImageUUID 33 | 34 | @synthesize UUID = _uuid; 35 | 36 | + (PDImageUUID *)imageUUIDWithUUID:(NSUUID *)uuid 37 | { 38 | return [[PDImageUUID alloc] initWithUUID:uuid]; 39 | } 40 | 41 | + (PDImageUUID *)imageUUIDWithPropertyList:(id)obj 42 | { 43 | return [[PDImageUUID alloc] initWithPropertyList:obj]; 44 | } 45 | 46 | - (id)initWithUUID:(NSUUID *)uuid 47 | { 48 | self = [super init]; 49 | if (self != nil) 50 | _uuid = [uuid copy]; 51 | return self; 52 | } 53 | 54 | - (id)initWithPropertyList:(id)obj 55 | { 56 | self = [super init]; 57 | if (self != nil) 58 | { 59 | if (![obj isKindOfClass:[NSString class]]) 60 | return nil; 61 | _uuid = [[NSUUID alloc] initWithUUIDString:obj]; 62 | } 63 | return self; 64 | } 65 | 66 | - (BOOL)isEqual:(id)obj 67 | { 68 | if ([obj isKindOfClass:[PDImageUUID class]]) 69 | return [_uuid isEqual:((PDImageUUID *)obj)->_uuid]; 70 | else 71 | return NO; 72 | } 73 | 74 | - (NSUInteger)hash 75 | { 76 | return [_uuid hash]; 77 | } 78 | 79 | - (id)propertyList 80 | { 81 | return _uuid.UUIDString; 82 | } 83 | 84 | - (id)copyWithZone:(NSZone *)zone 85 | { 86 | return self; 87 | } 88 | 89 | // NSPasteboardWriting methods 90 | 91 | - (NSArray *)writableTypesForPasteboard:(NSPasteboard *)pboard 92 | { 93 | return @[PDImageUUIDType]; 94 | } 95 | 96 | - (id)pasteboardPropertyListForType:(NSString *)type 97 | { 98 | if ([type isEqualToString:PDImageUUIDType]) 99 | return [self propertyList]; 100 | else 101 | return nil; 102 | } 103 | 104 | // NSPasteboardReading methods 105 | 106 | + (NSArray *)readableTypesForPasteboard:(NSPasteboard *)pboard 107 | { 108 | return @[PDImageUUIDType]; 109 | } 110 | 111 | + (NSPasteboardReadingOptions)readingOptionsForType:(NSString *)type 112 | pasteboard:(NSPasteboard *)pboard 113 | { 114 | if ([type isEqualToString:PDImageUUIDType]) 115 | return NSPasteboardReadingAsPropertyList; 116 | else 117 | return 0; 118 | } 119 | 120 | - (id)initWithPasteboardPropertyList:(id)obj ofType:(NSString *)type 121 | { 122 | if ([type isEqualToString:PDImageUUIDType]) 123 | return [self initWithPropertyList:obj]; 124 | else 125 | return nil; 126 | } 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /src/PDImageView.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDViewController.h" 26 | 27 | @class PDImageViewController, PDImage, PDImageLayer, CALayer; 28 | 29 | @interface PDImageView : NSView 30 | 31 | @property(nonatomic, weak) IBOutlet PDImageViewController *controller; 32 | 33 | @property(nonatomic, strong) PDImage *image; 34 | @property(nonatomic, assign) CGFloat imageScale; 35 | @property(nonatomic, assign) CGPoint imageOrigin; 36 | @property(nonatomic, assign) BOOL displaysMetadata; 37 | 38 | @property(nonatomic, assign, readonly) CGFloat scaleToFitScale; 39 | @property(nonatomic, assign, readonly) CGFloat scaleToFillScale; 40 | @property(nonatomic, assign, readonly) CGFloat scaleToActualScale; 41 | 42 | - (void)setImageScale:(CGFloat)scale preserveOrigin:(BOOL)flag; 43 | 44 | - (void)viewDidDisappear; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /src/PDImageViewController.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDViewController.h" 26 | 27 | @class PDImageView; 28 | 29 | @interface PDImageViewController : PDViewController 30 | 31 | @property(nonatomic, assign) BOOL displaysMetadata; 32 | 33 | @property(nonatomic, weak) IBOutlet NSTextField *titleLabel; 34 | @property(nonatomic, weak) IBOutlet PDImageView *imageView; 35 | @property(nonatomic, weak) IBOutlet NSButton *rotateLeftButton; 36 | @property(nonatomic, weak) IBOutlet NSButton *rotateRightButton; 37 | @property(nonatomic, weak) IBOutlet NSSlider *scaleSlider; 38 | 39 | - (IBAction)toggleMetadata:(id)sender; 40 | 41 | - (IBAction)zoomIn:(id)sender; 42 | - (IBAction)zoomOut:(id)sender; 43 | - (IBAction)zoomActualSize:(id)sender; 44 | - (IBAction)zoomToFill:(id)sender; 45 | 46 | - (IBAction)controlAction:(id)sender; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /src/PDImportViewController.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDViewController.h" 26 | 27 | @class PDImageLibrary; 28 | 29 | @interface PDImportViewController : PDViewController 30 | 31 | - (void)setImportDestinationLibrary:(PDImageLibrary *)lib 32 | directory:(NSString *)dir; 33 | 34 | @property(nonatomic, weak) IBOutlet NSPopUpButton *libraryButton; 35 | @property(nonatomic, weak) IBOutlet NSTextField *directoryField; 36 | @property(nonatomic, weak) IBOutlet NSTextField *nameField; 37 | @property(nonatomic, weak) IBOutlet NSButton *renameButton; 38 | @property(nonatomic, weak) IBOutlet NSTextField *renameField; 39 | @property(nonatomic, weak) IBOutlet NSTextField *renameFieldLabel; 40 | @property(nonatomic, weak) IBOutlet NSPopUpButton *importButton; 41 | @property(nonatomic, weak) IBOutlet NSPopUpButton *activeTypeButton; 42 | @property(nonatomic, weak) IBOutlet NSTextField *keywordsField; 43 | @property(nonatomic, weak) IBOutlet NSButton *deleteAfterButton; 44 | @property(nonatomic, weak) IBOutlet NSTextField *descriptionLabel; 45 | @property(nonatomic, weak) IBOutlet NSButton *okButton; 46 | @property(nonatomic, weak) IBOutlet NSButton *cancelButton; 47 | 48 | - (IBAction)controlAction:(id)sender; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /src/PDInfoViewController.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDViewController.h" 26 | 27 | @class PDMetadataView; 28 | 29 | @interface PDInfoViewController : PDViewController 30 | 31 | @property(nonatomic, weak) IBOutlet PDMetadataView *metadataView; 32 | @property(nonatomic, weak) IBOutlet NSPopUpButton *popupButton; 33 | @property(nonatomic, strong) IBOutlet NSMenu *popupMenu; 34 | 35 | @property(nonatomic, copy) NSString *activeGroup; 36 | 37 | - (NSString *)localizedImagePropertyForKey:(NSString *)key; 38 | - (void)setLocalizedImageProperty:(NSString *)str forKey:(NSString *)key; 39 | 40 | - (IBAction)controlAction:(id)sender; 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /src/PDInfoViewController.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDInfoViewController.h" 26 | 27 | #import "PDImage.h" 28 | #import "PDMetadataView.h" 29 | #import "PDWindowController.h" 30 | 31 | @implementation PDInfoViewController 32 | { 33 | NSDictionary *_metadataGroups; 34 | NSArray *_metadataGroupOrder; 35 | NSString *_activeGroup; 36 | } 37 | 38 | @synthesize metadataView = _metadataView; 39 | @synthesize popupButton = _popupButton; 40 | @synthesize popupMenu = _popupMenu; 41 | 42 | + (NSString *)viewNibName 43 | { 44 | return @"PDInfoView"; 45 | } 46 | 47 | - (id)initWithController:(PDWindowController *)controller 48 | { 49 | return [super initWithController:controller]; 50 | } 51 | 52 | - (void)viewDidLoad 53 | { 54 | [super viewDidLoad]; 55 | 56 | [[NSNotificationCenter defaultCenter] 57 | addObserver:self selector:@selector(selectionChanged:) 58 | name:PDImageListDidChange object:_controller]; 59 | [[NSNotificationCenter defaultCenter] 60 | addObserver:self selector:@selector(selectionChanged:) 61 | name:PDSelectionDidChange object:_controller]; 62 | [[NSNotificationCenter defaultCenter] 63 | addObserver:self selector:@selector(imagePropertyChanged:) 64 | name:PDImagePropertyDidChange object:nil]; 65 | 66 | _metadataGroups = [[[NSUserDefaults standardUserDefaults] 67 | objectForKey:@"PDMetadataGroups"] copy]; 68 | _metadataGroupOrder = [[[NSUserDefaults standardUserDefaults] 69 | objectForKey:@"PDMetadataGroupOrder"] copy]; 70 | 71 | if (_metadataGroupOrder.count > 0) 72 | { 73 | [_popupMenu removeAllItems]; 74 | 75 | for (NSString *name in _metadataGroupOrder) 76 | { 77 | NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:name 78 | action:@selector(popupMenuAction:) keyEquivalent:@""]; 79 | item.target = self; 80 | item.representedObject = name; 81 | [_popupMenu addItem:item]; 82 | } 83 | 84 | if (_activeGroup == nil) 85 | [self setActiveGroup:[_metadataGroupOrder firstObject]]; 86 | else 87 | [self setActiveGroup:_activeGroup]; 88 | } 89 | } 90 | 91 | - (NSString *)activeGroup 92 | { 93 | return _activeGroup; 94 | } 95 | 96 | - (void)setActiveGroup:(NSString *)name 97 | { 98 | _activeGroup = [name copy]; 99 | 100 | [_metadataView setImageProperties:_metadataGroups[name]]; 101 | 102 | [_popupButton selectItemAtIndex: 103 | [_popupMenu indexOfItemWithRepresentedObject:name]]; 104 | } 105 | 106 | - (void)selectionChanged:(NSNotification *)note 107 | { 108 | [_metadataView update]; 109 | } 110 | 111 | - (void)imagePropertyChanged:(NSNotification *)note 112 | { 113 | if (note.object == _controller.primarySelectedImage) 114 | [_metadataView update]; 115 | } 116 | 117 | - (NSDictionary *)savedViewState 118 | { 119 | if (_activeGroup != nil) 120 | return @{@"ActiveGroup": _activeGroup}; 121 | else 122 | return nil; 123 | } 124 | 125 | - (void)applySavedViewState:(NSDictionary *)dict 126 | { 127 | id value = dict[@"ActiveGroup"]; 128 | if (value != nil) 129 | [self setActiveGroup:value]; 130 | } 131 | 132 | - (NSString *)localizedImagePropertyForKey:(NSString *)key 133 | { 134 | return [_controller.primarySelectedImage localizedImagePropertyForKey:key]; 135 | } 136 | 137 | - (void)setLocalizedImageProperty:(NSString *)str forKey:(NSString *)key 138 | { 139 | [_controller.primarySelectedImage setLocalizedImageProperty:str forKey:key]; 140 | } 141 | 142 | - (IBAction)controlAction:(id)sender 143 | { 144 | } 145 | 146 | - (IBAction)popupMenuAction:(NSMenuItem *)sender 147 | { 148 | self.activeGroup = sender.representedObject; 149 | } 150 | 151 | @end 152 | -------------------------------------------------------------------------------- /src/PDLibraryAlbum.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDLibraryGroup.h" 26 | 27 | @interface PDLibraryAlbum : PDLibraryGroup 28 | 29 | @property(nonatomic, copy) NSArray *imageUUIDs; 30 | 31 | - (void)addImageWithUUID:(NSUUID *)uuid; 32 | - (void)removeImageWithUUID:(NSUUID *)uuid; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /src/PDLibraryAlbum.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDLibraryAlbum.h" 26 | 27 | #import "PDAppDelegate.h" 28 | #import "PDAppKitExtensions.h" 29 | #import "PDImage.h" 30 | #import "PDWindowController.h" 31 | 32 | #import "PDMacros.h" 33 | 34 | @implementation PDLibraryAlbum 35 | { 36 | NSMutableArray *_imageUUIDs; 37 | NSMutableSet *_allUUIDs; 38 | } 39 | 40 | - (id)init 41 | { 42 | self = [super init]; 43 | if (self != nil) 44 | { 45 | _imageUUIDs = [[NSMutableArray alloc] init]; 46 | _allUUIDs = [[NSMutableSet alloc] init]; 47 | } 48 | return self; 49 | } 50 | 51 | - (NSArray *)imageUUIDs 52 | { 53 | return _imageUUIDs; 54 | } 55 | 56 | - (void)setImageUUIDs:(NSArray *)obj 57 | { 58 | if (_imageUUIDs != obj) 59 | { 60 | _imageUUIDs = [obj mutableCopy]; 61 | 62 | [_allUUIDs removeAllObjects]; 63 | 64 | for (NSUUID *uuid in _imageUUIDs) 65 | [_allUUIDs addObject:uuid]; 66 | } 67 | } 68 | 69 | - (void)addImageWithUUID:(NSUUID *)uuid 70 | { 71 | if (![_allUUIDs containsObject:uuid]) 72 | { 73 | [_imageUUIDs addObject:uuid]; 74 | [_allUUIDs addObject:uuid]; 75 | } 76 | } 77 | 78 | - (void)removeImageWithUUID:(NSUUID *)uuid 79 | { 80 | [_allUUIDs removeObject:uuid]; 81 | [_imageUUIDs removeObject:uuid]; 82 | } 83 | 84 | - (BOOL)foreachSubimage:(void (^)(PDImage *im, BOOL *stop))thunk 85 | { 86 | PDWindowController *controller 87 | = ((PDAppDelegate *)[NSApp delegate]).windowController; 88 | 89 | BOOL saw_all = [controller foreachImage:^(PDImage *im, BOOL *stop) { 90 | NSUUID *uuid = [im UUIDIfDefined]; 91 | if (uuid != nil && [_allUUIDs containsObject:uuid]) 92 | thunk(im, stop); 93 | }]; 94 | 95 | if (!saw_all) 96 | return NO; 97 | 98 | return [super foreachSubimage:thunk]; 99 | } 100 | 101 | - (BOOL)hasTitleImage 102 | { 103 | return YES; 104 | } 105 | 106 | - (NSImage *)titleImage 107 | { 108 | NSImage *image = [super titleImage]; 109 | return image != nil ? image : PDImageWithName(PDImage_GenericFolder); 110 | } 111 | 112 | - (BOOL)hasBadge 113 | { 114 | return YES; 115 | } 116 | 117 | - (NSInteger)badgeValue 118 | { 119 | return _imageUUIDs.count; 120 | } 121 | 122 | @end 123 | -------------------------------------------------------------------------------- /src/PDLibraryDevice.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDLibraryDirectory.h" 26 | 27 | @interface PDLibraryDevice : PDLibraryDirectory 28 | 29 | - (id)initWithLibrary:(PDImageLibrary *)lib; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /src/PDLibraryDevice.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDLibraryDevice.h" 26 | 27 | #import "PDAppKitExtensions.h" 28 | #import "PDImage.h" 29 | #import "PDImageLibrary.h" 30 | 31 | @implementation PDLibraryDevice 32 | { 33 | NSImage *_icon; 34 | } 35 | 36 | + (BOOL)flattensSubdirectories 37 | { 38 | return YES; 39 | } 40 | 41 | - (id)initWithLibrary:(PDImageLibrary *)lib 42 | { 43 | return [super initWithLibrary:lib directory:@"DCIM"]; 44 | } 45 | 46 | - (NSString *)titleString 47 | { 48 | return self.library.name; 49 | } 50 | 51 | - (BOOL)hasTitleImage 52 | { 53 | return YES; 54 | } 55 | 56 | - (NSImage *)titleImage 57 | { 58 | if (_icon == nil) 59 | { 60 | _icon = self.library.iconImage; 61 | if (_icon == nil) 62 | _icon = PDImageWithName(PDImage_GenericRemovableDisk); 63 | } 64 | 65 | return _icon; 66 | } 67 | 68 | - (NSString *)identifier 69 | { 70 | return nil; 71 | } 72 | 73 | - (void)unmount 74 | { 75 | [self.library unmount]; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /src/PDLibraryDirectory.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDLibraryItem.h" 26 | 27 | @class PDImageLibrary; 28 | 29 | @interface PDLibraryDirectory : PDLibraryItem 30 | 31 | + (BOOL)flattensSubdirectories; 32 | 33 | - (id)initWithLibrary:(PDImageLibrary *)lib directory:(NSString *)dir; 34 | 35 | - (PDLibraryDirectory *)newItemForSubdirectory:(NSString *)dir; 36 | 37 | @property(nonatomic, readonly) PDImageLibrary *library; 38 | @property(nonatomic, copy) NSString *libraryDirectory; 39 | 40 | - (PDLibraryDirectory *)subitemContainingDirectory:(NSString *)dir; 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /src/PDLibraryFolder.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDLibraryDirectory.h" 26 | 27 | @interface PDLibraryFolder : PDLibraryDirectory 28 | 29 | @property(nonatomic) NSInteger titleImageName; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /src/PDLibraryFolder.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDLibraryFolder.h" 26 | 27 | #import "PDAppKitExtensions.h" 28 | #import "PDImage.h" 29 | #import "PDImageLibrary.h" 30 | 31 | @implementation PDLibraryFolder 32 | { 33 | NSInteger _titleImageName; 34 | } 35 | 36 | @synthesize titleImageName = _titleImageName; 37 | 38 | - (id)initWithLibrary:(PDImageLibrary *)lib directory:(NSString *)dir 39 | { 40 | self = [super initWithLibrary:lib directory:dir]; 41 | if (self == nil) 42 | return nil; 43 | 44 | _titleImageName = PDImage_GenericFolder; 45 | 46 | return self; 47 | } 48 | 49 | - (BOOL)hasTitleImage 50 | { 51 | return YES; 52 | } 53 | 54 | - (NSImage *)titleImage 55 | { 56 | return PDImageWithName(_titleImageName); 57 | } 58 | 59 | - (NSString *)identifier 60 | { 61 | return (self.libraryDirectory.length == 0 62 | ? [NSString stringWithFormat:@"%08x", self.library.libraryId] 63 | : [self.libraryDirectory lastPathComponent]); 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /src/PDLibraryGroup.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDLibraryItem.h" 26 | 27 | @interface PDLibraryGroup : PDLibraryItem 28 | 29 | @property(nonatomic, copy) NSString *name; 30 | @property(nonatomic, copy) NSString *identifier; 31 | @property(nonatomic, strong) NSImage *iconImage; 32 | 33 | @property(nonatomic, copy) NSArray *subitems; 34 | 35 | - (void)addSubitem:(PDLibraryItem *)item; 36 | - (void)insertSubitem:(PDLibraryItem *)item atIndex:(NSInteger)idx; 37 | - (void)removeSubitem:(PDLibraryItem *)item; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /src/PDLibraryGroup.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDLibraryGroup.h" 26 | 27 | @implementation PDLibraryGroup 28 | { 29 | NSMutableArray *_subitems; 30 | } 31 | 32 | @synthesize name = _name; 33 | @synthesize identifier = _identifier; 34 | @synthesize iconImage = _iconImage; 35 | 36 | - (NSArray *)subitems 37 | { 38 | return _subitems == nil ? @[] : [_subitems copy]; 39 | } 40 | 41 | - (void)setSubitems:(NSArray *)array 42 | { 43 | if (_subitems != array) 44 | { 45 | for (PDLibraryItem *item in _subitems) 46 | item.parent = nil; 47 | 48 | _subitems = [array mutableCopy]; 49 | 50 | for (PDLibraryItem *item in _subitems) 51 | item.parent = self; 52 | } 53 | } 54 | 55 | - (void)addSubitem:(PDLibraryItem *)item 56 | { 57 | if (_subitems == nil 58 | || [_subitems indexOfObjectIdenticalTo:item] == NSNotFound) 59 | { 60 | if (_subitems == nil) 61 | _subitems = [NSMutableArray array]; 62 | [_subitems addObject:item]; 63 | item.parent = self; 64 | } 65 | } 66 | 67 | - (void)insertSubitem:(PDLibraryItem *)item atIndex:(NSInteger)idx 68 | { 69 | if (_subitems == nil 70 | || [_subitems indexOfObjectIdenticalTo:item] == NSNotFound) 71 | { 72 | if (_subitems == nil) 73 | _subitems = [NSMutableArray array]; 74 | if (idx < 0) 75 | idx = 0; 76 | else if (idx > _subitems.count) 77 | idx = _subitems.count; 78 | [_subitems insertObject:item atIndex:idx]; 79 | item.parent = self; 80 | } 81 | } 82 | 83 | - (void)removeSubitem:(PDLibraryItem *)item 84 | { 85 | if (_subitems != nil) 86 | { 87 | NSInteger idx = [_subitems indexOfObjectIdenticalTo:item]; 88 | if (idx != NSNotFound) 89 | { 90 | item.parent = nil; 91 | [_subitems removeObjectAtIndex:idx]; 92 | } 93 | } 94 | } 95 | 96 | - (NSString *)titleString 97 | { 98 | return _name; 99 | } 100 | 101 | - (BOOL)hasTitleImage 102 | { 103 | return _iconImage != nil; 104 | } 105 | 106 | - (NSImage *)titleImage 107 | { 108 | return _iconImage; 109 | } 110 | 111 | - (BOOL)isExpandable 112 | { 113 | return _subitems.count != 0; 114 | } 115 | 116 | - (NSString *)identifier 117 | { 118 | return _identifier != nil ? _identifier : _name; 119 | } 120 | 121 | - (void)setNeedsUpdate 122 | { 123 | for (PDLibraryItem *subitem in _subitems) 124 | [subitem setNeedsUpdate]; 125 | } 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /src/PDLibraryItem.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | extern NSString * const PDLibraryItemSubimagesDidChange; 28 | 29 | @class PDImage; 30 | 31 | @interface PDLibraryItem : NSObject 32 | 33 | @property(nonatomic, weak) PDLibraryItem *parent; 34 | 35 | @property(nonatomic, assign, getter=isHidden) BOOL hidden; 36 | 37 | /* Returns true if item or any subitem matches 'str'. */ 38 | 39 | - (BOOL)applySearchString:(NSString *)str; 40 | 41 | - (void)resetSearchState; 42 | 43 | /* Array of PDLibraryItem. Includes hidden items. */ 44 | 45 | @property(nonatomic, copy, readonly) NSArray *subitems; 46 | 47 | /* Iterates 'thunk' recursively over the receiver and all subitems. */ 48 | 49 | - (BOOL)foreachSubitem:(void (^)(PDLibraryItem *item, BOOL *stop))thunk; 50 | 51 | /* Iterates over all images recursively under self. Returns YES if saw 52 | all images (i.e. wasn't stopped prematurely). */ 53 | 54 | - (BOOL)foreachSubimage:(void (^)(PDImage *im, BOOL *stop))thunk; 55 | 56 | @property(nonatomic, assign, readonly, getter=isTrashcan) BOOL trashcan; 57 | @property(nonatomic, assign, readonly) BOOL nilPredicateIncludesRejected; 58 | 59 | @property(nonatomic, copy, readonly) NSString *titleString; 60 | 61 | @property(nonatomic, strong, readonly) NSImage *titleImage; 62 | 63 | @property(nonatomic, assign, readonly) BOOL hasTitleImage; 64 | 65 | @property(nonatomic, assign, readonly, getter=isExpandable) BOOL expandable; 66 | 67 | @property(nonatomic, assign, readonly) BOOL hasBadge; 68 | 69 | @property(nonatomic, assign, readonly) NSInteger badgeValue; 70 | 71 | @property(nonatomic, assign, readonly) BOOL badgeValueIsNumberOfSubimages; 72 | 73 | /* For saving view state. */ 74 | 75 | @property(nonatomic, copy, readonly) NSString *identifier; 76 | 77 | /* For removable devices, unmounts the volume. */ 78 | 79 | - (void)unmount; 80 | 81 | /* Mark any cached state as needing reloading. */ 82 | 83 | - (void)setNeedsUpdate; 84 | 85 | - (BOOL)isDescendantOf:(PDLibraryItem *)item; 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /src/PDLibraryItem.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDLibraryItem.h" 26 | 27 | NSString * const PDLibraryItemSubimagesDidChange = @"PDLibraryItemSubimagesDidChange"; 28 | 29 | @implementation PDLibraryItem 30 | 31 | @synthesize parent = _parent; 32 | @synthesize hidden = _hidden; 33 | 34 | - (BOOL)applySearchString:(NSString *)str 35 | { 36 | BOOL matches = NO; 37 | 38 | for (PDLibraryItem *item in self.subitems) 39 | { 40 | if ([item applySearchString:str]) 41 | matches = YES; 42 | } 43 | 44 | return matches; 45 | } 46 | 47 | - (void)resetSearchState 48 | { 49 | self.hidden = NO; 50 | 51 | for (PDLibraryItem *item in self.subitems) 52 | [item resetSearchState]; 53 | } 54 | 55 | - (void)recursivelyClearHiddenState 56 | { 57 | self.hidden = NO; 58 | 59 | for (PDLibraryItem *item in self.subitems) 60 | [item recursivelyClearHiddenState]; 61 | } 62 | 63 | - (NSArray *)subitems 64 | { 65 | return @[]; 66 | } 67 | 68 | - (BOOL)foreachSubitem:(void (^)(PDLibraryItem *item, BOOL *stop))thunk 69 | { 70 | BOOL stop = NO; 71 | thunk(self, &stop); 72 | if (stop) 73 | return NO; 74 | 75 | for (PDLibraryItem *subitem in self.subitems) 76 | { 77 | if (![subitem foreachSubitem:thunk]) 78 | return NO; 79 | } 80 | 81 | return YES; 82 | } 83 | 84 | - (BOOL)foreachSubimage:(void (^)(PDImage *im, BOOL *stop))thunk 85 | { 86 | for (PDLibraryItem *subitem in self.subitems) 87 | { 88 | if (!subitem.hidden) 89 | { 90 | if (![subitem foreachSubimage:thunk]) 91 | return NO; 92 | } 93 | } 94 | 95 | return YES; 96 | } 97 | 98 | - (BOOL)isTrashcan 99 | { 100 | return NO; 101 | } 102 | 103 | - (BOOL)nilPredicateIncludesRejected 104 | { 105 | return NO; 106 | } 107 | 108 | - (BOOL)hasTitleImage 109 | { 110 | return [self titleImage] != nil; 111 | } 112 | 113 | - (NSImage *)titleImage 114 | { 115 | return nil; 116 | } 117 | 118 | - (NSString *)titleString 119 | { 120 | return nil; 121 | } 122 | 123 | - (BOOL)isExpandable 124 | { 125 | return NO; 126 | } 127 | 128 | - (BOOL)hasBadge 129 | { 130 | return NO; 131 | } 132 | 133 | - (NSInteger)badgeValue 134 | { 135 | return 0; 136 | } 137 | 138 | - (BOOL)badgeValueIsNumberOfSubimages 139 | { 140 | return NO; 141 | } 142 | 143 | - (NSString *)identifier 144 | { 145 | return self.titleString; 146 | } 147 | 148 | - (void)unmount 149 | { 150 | } 151 | 152 | - (BOOL)isDescendantOf:(PDLibraryItem *)item 153 | { 154 | for (PDLibraryItem *tem = self; tem != nil; tem = tem.parent) 155 | { 156 | if (tem == item) 157 | return YES; 158 | } 159 | 160 | return NO; 161 | } 162 | 163 | - (void)setNeedsUpdate 164 | { 165 | } 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /src/PDLibraryQuery.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDLibraryGroup.h" 26 | 27 | @interface PDLibraryQuery : PDLibraryGroup 28 | 29 | @property(nonatomic, copy) NSPredicate *predicate; 30 | @property(nonatomic, assign, getter=isTrashcan) BOOL trashcan; 31 | @property(nonatomic, assign) BOOL nilPredicateIncludesRejected; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /src/PDLibraryQuery.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDLibraryQuery.h" 26 | 27 | #import "PDAppDelegate.h" 28 | #import "PDAppKitExtensions.h" 29 | #import "PDImage.h" 30 | #import "PDWindowController.h" 31 | 32 | @implementation PDLibraryQuery 33 | 34 | @synthesize predicate = _predicate; 35 | @synthesize trashcan = _trashcan; 36 | @synthesize nilPredicateIncludesRejected = _nilPredicateIncludesRejected; 37 | 38 | - (BOOL)foreachSubimage:(void (^)(PDImage *im, BOOL *stop))thunk 39 | { 40 | PDWindowController *controller 41 | = [(PDAppDelegate *)[NSApp delegate] windowController]; 42 | 43 | BOOL saw_all = [controller foreachImage:^(PDImage *im, BOOL *stop) 44 | { 45 | if (_predicate == nil 46 | || [_predicate evaluateWithObject:im.expressionValues]) 47 | { 48 | thunk(im, stop); 49 | } 50 | }]; 51 | 52 | if (!saw_all) 53 | return NO; 54 | 55 | return [super foreachSubimage:thunk]; 56 | } 57 | 58 | - (BOOL)hasTitleImage 59 | { 60 | return YES; 61 | } 62 | 63 | - (NSImage *)titleImage 64 | { 65 | NSImage *image = [super titleImage]; 66 | return image != nil ? image : PDImageWithName(PDImage_SmartFolder); 67 | } 68 | 69 | - (BOOL)hasBadge 70 | { 71 | return NO; 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /src/PDLibraryView.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PXSourceList.h" 26 | 27 | @class PDLibraryViewController; 28 | 29 | @interface PDLibraryView : PXSourceList 30 | 31 | @property(nonatomic, weak) IBOutlet PDLibraryViewController *controller; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /src/PDLibraryView.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDLibraryView.h" 26 | 27 | #import "PDLibraryViewController.h" 28 | #import "PDWindowController.h" 29 | 30 | @implementation PDLibraryView 31 | 32 | @synthesize controller = _controller; 33 | 34 | - (void)keyDown:(NSEvent *)e 35 | { 36 | NSString *chars = [e charactersIgnoringModifiers]; 37 | 38 | if (chars.length == 1) 39 | { 40 | switch ([chars characterAtIndex:0]) 41 | { 42 | case NSLeftArrowFunctionKey: 43 | case NSRightArrowFunctionKey: 44 | [[_controller controller] contentKeyDown:e makeKey:YES]; 45 | return; 46 | 47 | case NSHomeFunctionKey: 48 | case NSEndFunctionKey: 49 | case NSPageUpFunctionKey: 50 | case NSPageDownFunctionKey: 51 | [[_controller controller] contentKeyDown:e makeKey:NO]; 52 | return; 53 | } 54 | } 55 | 56 | [super keyDown:e]; 57 | } 58 | 59 | - (IBAction)selectAll:(id)sender 60 | { 61 | [[_controller controller] selectAll:sender]; 62 | } 63 | 64 | - (IBAction)deselectAll:(id)sender 65 | { 66 | [[_controller controller] deselectAll:sender]; 67 | } 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /src/PDLibraryViewController.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDViewController.h" 26 | 27 | #import "PXSourceListDataSource.h" 28 | #import "PXSourceListDelegate.h" 29 | 30 | /* posted to window controller. */ 31 | extern NSString *const PDLibrarySelectionDidChange; 32 | 33 | @class PDLibraryGroup, PDImage, PDImageLibrary, PDImageTextCell, PDLibraryItem; 34 | 35 | @interface PDLibraryViewController : PDViewController 36 | 37 | 38 | - (BOOL)foreachImage:(void (^)(PDImage *im, BOOL *stop))thunk; 39 | 40 | - (void)selectLibrary:(PDImageLibrary *)lib directory:(NSString *)dir; 41 | 42 | @property(nonatomic, weak) IBOutlet PXSourceList *outlineView; 43 | @property(nonatomic, weak) IBOutlet PDImageTextCell *normalCell; 44 | @property(nonatomic, weak) IBOutlet NSSearchField *searchField; 45 | @property(nonatomic, weak) IBOutlet NSButton *addButton; 46 | @property(nonatomic, weak) IBOutlet NSButton *removeButton; 47 | @property(nonatomic, weak) IBOutlet NSButton *importButton; 48 | @property(nonatomic, weak) IBOutlet NSButton *actionButton; 49 | 50 | - (IBAction)nextLibraryItemAction:(id)sender; 51 | - (IBAction)previousLibraryItemAction:(id)sender; 52 | - (IBAction)parentLibraryItemAction:(id)sender; 53 | - (IBAction)firstLibraryChildItemAction:(id)sender; 54 | - (IBAction)expandLibraryItemAction:(id)sender; 55 | - (IBAction)collapseLibraryItemAction:(id)sender; 56 | - (IBAction)expandCollapseLibraryItemAction:(id)sender; 57 | 58 | - (void)rescanVolumes; 59 | 60 | - (IBAction)addLibraryAction:(id)sender; 61 | - (IBAction)newFolderAction:(id)sender; 62 | - (IBAction)newAlbumAction:(id)sender; 63 | 64 | - (void)addSmartAlbum:(NSString *)name predicate:(NSPredicate *)pred; 65 | 66 | - (IBAction)reloadLibraries:(id)sender; 67 | 68 | - (IBAction)removeAction:(id)sender; 69 | - (IBAction)searchAction:(id)sender; 70 | - (IBAction)importAction:(id)sender; 71 | 72 | - (IBAction)delete:(id)sender; 73 | 74 | - (IBAction)controlAction:(id)sender; 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /src/PDLocalFileManager.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDFileManager.h" 26 | 27 | @interface PDLocalFileManager : PDFileManager 28 | 29 | - (id)initWithPath:(NSString *)path; 30 | - (id)initWithPropertyListRepresentation:(id)obj; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /src/PDLocalFileManager.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDLocalFileManager.h" 26 | 27 | #import 28 | 29 | #import 30 | 31 | #define ERROR_DOMAIN @"org.unfactored.PDFileManager" 32 | 33 | @implementation PDLocalFileManager 34 | { 35 | NSString *_path; 36 | NSFileManager *_manager; 37 | } 38 | 39 | @synthesize delegate = _delegate; 40 | 41 | - (id)initWithPath:(NSString *)path 42 | { 43 | self = [super init]; 44 | if (self != nil) 45 | { 46 | _path = [[path stringByStandardizingPath] copy]; 47 | _manager = [NSFileManager defaultManager]; 48 | } 49 | return self; 50 | } 51 | 52 | - (id)initWithPropertyListRepresentation:(id)obj 53 | { 54 | NSDictionary *dict = obj; 55 | NSString *path = dict[@"PDLocalFileManager.path"]; 56 | if (path == nil) 57 | return nil; 58 | path = [path stringByExpandingTildeInPath]; 59 | return [self initWithPath:path]; 60 | } 61 | 62 | - (id)propertyListRepresentation 63 | { 64 | NSString *path = [_path stringByAbbreviatingWithTildeInPath]; 65 | return @{@"PDLocalFileManager.path": path}; 66 | } 67 | 68 | - (BOOL)isEqualToPath:(NSString *)path 69 | { 70 | return [_path isEqualToString:[path stringByStandardizingPath]]; 71 | } 72 | 73 | - (BOOL)isEqualToPropertyListRepresentation:(id)obj 74 | { 75 | NSDictionary *dict = obj; 76 | NSString *path = dict[@"PDLocalFileManager.path"]; 77 | 78 | if (path != nil) 79 | return [self isEqualToPath:[path stringByExpandingTildeInPath]]; 80 | else 81 | return NO; 82 | } 83 | 84 | - (void)invalidate 85 | { 86 | } 87 | 88 | - (void)dealloc 89 | { 90 | [self invalidate]; 91 | } 92 | 93 | - (NSString *)name 94 | { 95 | return [_path lastPathComponent]; 96 | } 97 | 98 | - (NSString *)localizedDescription 99 | { 100 | /* This will return file://path or something, which seems ok. */ 101 | 102 | return [[NSURL fileURLWithPath:_path] absoluteString]; 103 | } 104 | 105 | - (NSImage *)iconImage 106 | { 107 | return [[NSWorkspace sharedWorkspace] iconForFile:_path]; 108 | } 109 | 110 | - (void)unmount 111 | { 112 | [[NSWorkspace sharedWorkspace] unmountAndEjectDeviceAtPath:_path]; 113 | } 114 | 115 | static NSString * 116 | absolute_path(PDLocalFileManager *self, NSString *path) 117 | { 118 | return [self->_path stringByAppendingPathComponent:path]; 119 | } 120 | 121 | - (NSURL *)fileURLWithPath:(NSString *)path 122 | { 123 | return [NSURL fileURLWithPath:absolute_path(self, path)]; 124 | } 125 | 126 | - (BOOL)fileExistsAtPath:(NSString *)path 127 | { 128 | return [_manager fileExistsAtPath:absolute_path(self, path)]; 129 | } 130 | 131 | - (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(BOOL *)dirp 132 | { 133 | return [_manager fileExistsAtPath: 134 | absolute_path(self, path) isDirectory:dirp]; 135 | } 136 | 137 | - (time_t)mtimeOfFileAtPath:(NSString *)path 138 | { 139 | struct stat st; 140 | if (stat([absolute_path(self, path) fileSystemRepresentation], &st) == 0) 141 | return st.st_mtime; 142 | else 143 | return 0; 144 | } 145 | 146 | - (size_t)sizeOfFileAtPath:(NSString *)path 147 | { 148 | struct stat st; 149 | if (stat([absolute_path(self, path) fileSystemRepresentation], &st) == 0) 150 | return st.st_size; 151 | else 152 | return 0; 153 | } 154 | 155 | - (NSData *)contentsOfFileAtPath:(NSString *)path 156 | { 157 | return [NSData dataWithContentsOfFile:absolute_path(self, path)]; 158 | } 159 | 160 | - (NSArray *)contentsOfDirectoryAtPath:(NSString *)path 161 | { 162 | return [_manager contentsOfDirectoryAtPath: 163 | absolute_path(self, path) error:nil]; 164 | } 165 | 166 | - (CGImageSourceRef)copyImageSourceAtPath:(NSString *)path 167 | { 168 | NSURL *url = [NSURL fileURLWithPath:absolute_path(self, path)]; 169 | return CGImageSourceCreateWithURL((CFURLRef)url, NULL); 170 | } 171 | 172 | - (BOOL)writeData:(NSData *)data toFile:(NSString *)path 173 | options:(NSDataWritingOptions)options error:(NSError **)err 174 | { 175 | return [data writeToFile:absolute_path(self, path) 176 | options:options error:err]; 177 | } 178 | 179 | - (BOOL)createDirectoryAtPath:(NSString *)path 180 | withIntermediateDirectories:(BOOL)flag attributes:(NSDictionary *)dict 181 | error:(NSError **)err 182 | { 183 | return [_manager createDirectoryAtPath:absolute_path(self, path) 184 | withIntermediateDirectories:flag attributes:dict error:err]; 185 | } 186 | 187 | - (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath 188 | error:(NSError **)err 189 | { 190 | return [_manager copyItemAtPath:absolute_path(self, srcPath) 191 | toPath:absolute_path(self, dstPath) error:err]; 192 | } 193 | 194 | - (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath 195 | error:(NSError **)err 196 | { 197 | return [_manager moveItemAtPath:absolute_path(self, srcPath) 198 | toPath:absolute_path(self, dstPath) error:err]; 199 | } 200 | 201 | - (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)err 202 | { 203 | return [_manager removeItemAtPath:absolute_path(self, path) error:err]; 204 | } 205 | 206 | @end 207 | -------------------------------------------------------------------------------- /src/PDMacros.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | /* Useful macros. */ 26 | 27 | #undef N_ELEMENTS 28 | #define N_ELEMENTS(x) (sizeof(x) / sizeof((x)[0])) 29 | 30 | #undef MIN 31 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 32 | 33 | #undef MAX 34 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 35 | 36 | #undef CLAMP 37 | #define CLAMP(a, b, c) MIN(MAX(a, b), c) 38 | 39 | #undef ABS 40 | #define ABS(a) ((a) > 0 ? (a) : -(a)) 41 | 42 | #undef MIX 43 | #define MIX(a, b, c) ((a) + ((b) - (a)) * (f)) 44 | 45 | #define POINTER_TO_INT(x) ((intptr_t)(x)) 46 | #define INT_TO_POINTER(x) ((void *)(intptr_t)(x)) 47 | 48 | #define POINTER_TO_UINT(x) ((uintptr_t)(x)) 49 | #define UINT_TO_POINTER(x) ((void *)(uintptr_t)(x)) 50 | 51 | /* Will use alloca() if safe, else malloc(). */ 52 | 53 | #define STACK_ALLOC(type, count) \ 54 | (sizeof(type) * (count) <= 4096 \ 55 | ? (type *)alloca(sizeof(type) * (count)) \ 56 | : (type *)malloc(sizeof(type) * (count))) 57 | 58 | #if __has_feature(objc_arc) 59 | #define STACK_ALLOC_ARC(type, count) \ 60 | (sizeof(type) * (count) <= 4096 \ 61 | ? (__unsafe_unretained type *)alloca(sizeof(type) * (count)) \ 62 | : (__unsafe_unretained type *)malloc(sizeof(type) * (count))) 63 | #endif 64 | 65 | #define STACK_FREE(type, count, ptr) \ 66 | do { \ 67 | if (sizeof(type) * (count) > 4096) \ 68 | free(ptr); \ 69 | } while (0) 70 | -------------------------------------------------------------------------------- /src/PDMetadataItemView.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | @class PDMetadataView; 28 | 29 | @interface PDMetadataItemView : NSView 30 | 31 | @property(nonatomic, weak) PDMetadataView *metadataView; 32 | 33 | @property(nonatomic, copy) NSString *imageProperty; 34 | 35 | - (void)update; 36 | 37 | - (CGFloat)preferredHeight; 38 | - (void)layoutSubviews; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /src/PDMetadataItemView.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDMetadataItemView.h" 26 | 27 | #import "PDColor.h" 28 | #import "PDImage.h" 29 | #import "PDMetadataView.h" 30 | 31 | #define LABEL_Y_OFFSET -4 32 | #define LABEL_WIDTH 120 33 | #define LABEL_HEIGHT 16 34 | #define SPACING 8 35 | 36 | #define CONTROL_HEIGHT 20 37 | 38 | @implementation PDMetadataItemView 39 | { 40 | NSTextField *_labelField; 41 | NSTextField *_valueField; 42 | 43 | NSString *_imageProperty; 44 | } 45 | 46 | @synthesize metadataView = _metadataView; 47 | 48 | - (id)initWithFrame:(CGRect)frame 49 | { 50 | self = [super initWithFrame:frame]; 51 | if (self == nil) 52 | return nil; 53 | 54 | NSFont *font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]; 55 | NSFont *font1 = [NSFont boldSystemFontOfSize:[NSFont smallSystemFontSize]]; 56 | 57 | _labelField = [[NSTextField alloc] initWithFrame: 58 | NSMakeRect(0, LABEL_Y_OFFSET, LABEL_WIDTH, LABEL_HEIGHT)]; 59 | _labelField.target = self; 60 | _labelField.action = @selector(controlAction:); 61 | _labelField.delegate = self; 62 | _labelField.drawsBackground = NO; 63 | _labelField.editable = NO; 64 | _labelField.alignment = NSRightTextAlignment; 65 | _labelField.autoresizingMask = NSViewMaxXMargin; 66 | NSTextFieldCell *label_cell = _labelField.cell; 67 | label_cell.bordered = NO; 68 | label_cell.font = font; 69 | label_cell.textColor = [PDColor controlTextColor]; 70 | [self addSubview:_labelField]; 71 | 72 | _valueField = [[NSTextField alloc] initWithFrame: 73 | NSMakeRect(LABEL_WIDTH + SPACING, 0, frame.size.width 74 | - LABEL_WIDTH, CONTROL_HEIGHT)]; 75 | _valueField.target = self; 76 | _valueField.action = @selector(controlAction:); 77 | _valueField.delegate = self; 78 | _valueField.editable = NO; 79 | _valueField.selectable = YES; 80 | _valueField.autoresizingMask = NSViewWidthSizable; 81 | NSTextFieldCell *value_cell = _valueField.cell; 82 | value_cell.bordered = NO; 83 | value_cell.bezeled = YES; 84 | value_cell.font = font1; 85 | value_cell.textColor = [PDColor controlTextColor]; 86 | value_cell.backgroundColor = [PDColor controlBackgroundColor]; 87 | [self addSubview:_valueField]; 88 | 89 | return self; 90 | } 91 | 92 | - (void)dealloc 93 | { 94 | _labelField.delegate = nil; 95 | _valueField.delegate = nil; 96 | } 97 | 98 | - (NSString *)imageProperty 99 | { 100 | return _imageProperty; 101 | } 102 | 103 | - (void)_updateImageProperty 104 | { 105 | BOOL editable = [PDImage imagePropertyIsEditableInUI:_imageProperty]; 106 | NSString *label = [PDImage localizedNameOfImageProperty:_imageProperty]; 107 | 108 | _labelField.stringValue = label; 109 | ((NSTextFieldCell *)_labelField.cell).truncatesLastVisibleLine = YES; 110 | 111 | _valueField.editable = editable; 112 | _valueField.drawsBackground = editable; 113 | ((NSTextFieldCell *)_valueField.cell).bezeled = editable; 114 | ((NSTextFieldCell *)_valueField.cell).truncatesLastVisibleLine = YES; 115 | } 116 | 117 | - (void)setImageProperty:(NSString *)name 118 | { 119 | if (_imageProperty != name) 120 | { 121 | _imageProperty = [name copy]; 122 | [self _updateImageProperty]; 123 | } 124 | } 125 | 126 | - (NSString *)fieldString 127 | { 128 | if ([_imageProperty isEqualToString:@"pixel_size"]) 129 | { 130 | double w = [[_metadataView localizedImagePropertyForKey: 131 | PDImage_PixelWidth] doubleValue]; 132 | double h = [[_metadataView localizedImagePropertyForKey: 133 | PDImage_PixelHeight] doubleValue]; 134 | 135 | if (w == 0 || h == 0) 136 | return nil; 137 | 138 | double mp = w * h * 1e-6; 139 | return [NSString stringWithFormat:@"%g x %g (%.1f MP)", w, h, mp]; 140 | } 141 | 142 | return [_metadataView localizedImagePropertyForKey:_imageProperty]; 143 | } 144 | 145 | - (void)update 146 | { 147 | // reload everything in case of dependent fields (pace, etc) 148 | 149 | [self _updateImageProperty]; 150 | 151 | NSString *value = self.fieldString; 152 | if (value == nil) 153 | value = @""; 154 | 155 | _valueField.stringValue = value; 156 | } 157 | 158 | - (CGFloat)preferredHeight 159 | { 160 | return _valueField.editable ? CONTROL_HEIGHT : LABEL_HEIGHT; 161 | } 162 | 163 | - (void)layoutSubviews 164 | { 165 | CGRect bounds = self.bounds; 166 | CGRect frame = bounds; 167 | BOOL editable = _valueField.editable; 168 | 169 | if (editable) 170 | frame.origin.y += LABEL_Y_OFFSET; 171 | frame.size.width = LABEL_WIDTH; 172 | _labelField.frame = frame; 173 | 174 | frame.origin.x += frame.size.width + SPACING; 175 | frame.origin.y = bounds.origin.y; 176 | frame.size.width = bounds.size.width - frame.origin.x; 177 | frame.size.height = editable ? CONTROL_HEIGHT : LABEL_HEIGHT; 178 | _valueField.frame = frame; 179 | } 180 | 181 | - (IBAction)controlAction:(id)sender 182 | { 183 | if (sender == _valueField) 184 | { 185 | [_metadataView setLocalizedImageProperty: 186 | _valueField.stringValue forKey:_imageProperty]; 187 | } 188 | } 189 | 190 | @end 191 | -------------------------------------------------------------------------------- /src/PDMetadataView.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | @class PDInfoViewController; 28 | 29 | @interface PDMetadataView : NSView 30 | 31 | @property(nonatomic, weak) IBOutlet PDInfoViewController *controller; 32 | 33 | @property(nonatomic, copy) NSArray *imageProperties; 34 | 35 | - (void)update; 36 | 37 | - (CGFloat)heightForWidth:(CGFloat)width; 38 | - (void)layoutSubviews; 39 | 40 | - (NSString *)localizedImagePropertyForKey:(NSString *)key; 41 | - (void)setLocalizedImageProperty:(NSString *)str forKey:(NSString *)key; 42 | 43 | - (IBAction)controlAction:(id)sender; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /src/PDMetadataView.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDMetadataView.h" 26 | 27 | #import "PDInfoViewController.h" 28 | #import "PDMetadataItemView.h" 29 | #import "PDWindowController.h" 30 | 31 | #define ITEM_Y_SPACING 2 32 | #define X_INSET 8 33 | #define Y_INSET 8 34 | 35 | @implementation PDMetadataView 36 | 37 | @synthesize controller = _controller; 38 | 39 | - (NSArray *)imageProperties 40 | { 41 | NSMutableArray *array = [NSMutableArray array]; 42 | 43 | for (PDMetadataItemView *item in self.subviews) 44 | { 45 | NSString *name = item.imageProperty; 46 | if (name.length != 0) 47 | [array addObject:name]; 48 | } 49 | 50 | return array; 51 | } 52 | 53 | - (void)setImageProperties:(NSArray *)array 54 | { 55 | NSMutableArray *old_subviews = [[self subviews] mutableCopy]; 56 | NSMutableArray *new_subviews = [NSMutableArray array]; 57 | 58 | for (NSString *key in array) 59 | { 60 | PDMetadataItemView *new_subview = nil; 61 | 62 | NSInteger old_idx = 0; 63 | for (PDMetadataItemView *old_subview in old_subviews) 64 | { 65 | if ([old_subview.imageProperty isEqualToString:key]) 66 | { 67 | new_subview = old_subview; 68 | [old_subviews removeObjectAtIndex:old_idx]; 69 | break; 70 | } 71 | old_idx++; 72 | } 73 | 74 | if (new_subview == nil) 75 | { 76 | new_subview = [[PDMetadataItemView alloc] initWithFrame:NSZeroRect]; 77 | new_subview.metadataView = self; 78 | new_subview.imageProperty = key; 79 | new_subview.autoresizingMask = NSViewWidthSizable; 80 | } 81 | 82 | [new_subviews addObject:new_subview]; 83 | } 84 | 85 | self.subviews = new_subviews; 86 | 87 | CGRect frame = self.frame; 88 | frame.size.height = [self heightForWidth:frame.size.width]; 89 | [self setFrameSize:frame.size]; 90 | [self layoutSubviews]; 91 | 92 | for (PDMetadataItemView *subview in self.subviews) 93 | [subview update]; 94 | } 95 | 96 | - (void)update 97 | { 98 | for (PDMetadataItemView *subview in self.subviews) 99 | [subview update]; 100 | } 101 | 102 | - (CGFloat)heightForWidth:(CGFloat)width 103 | { 104 | CGFloat h = 0; 105 | 106 | for (PDMetadataItemView *item in self.subviews) 107 | { 108 | if (h != 0) 109 | h += ITEM_Y_SPACING; 110 | h += [item preferredHeight]; 111 | } 112 | 113 | return h + Y_INSET * 2; 114 | } 115 | 116 | - (void)layoutSubviews 117 | { 118 | CGRect bounds = CGRectInset(self.bounds, X_INSET, Y_INSET); 119 | CGFloat y = 0; 120 | 121 | for (PDMetadataItemView *item in self.subviews) 122 | { 123 | CGRect frame; 124 | CGFloat h = [item preferredHeight]; 125 | frame.origin.x = bounds.origin.x; 126 | frame.origin.y = bounds.origin.y + y; 127 | frame.size.width = bounds.size.width; 128 | frame.size.height = h; 129 | item.frame = frame; 130 | [item layoutSubviews]; 131 | y += h + ITEM_Y_SPACING; 132 | } 133 | } 134 | 135 | - (NSString *)localizedImagePropertyForKey:(NSString *)key 136 | { 137 | return [_controller localizedImagePropertyForKey:key]; 138 | } 139 | 140 | - (void)setLocalizedImageProperty:(NSString *)str forKey:(NSString *)key 141 | { 142 | [_controller setLocalizedImageProperty:str forKey:key]; 143 | } 144 | 145 | - (IBAction)controlAction:(id)sender 146 | { 147 | } 148 | 149 | - (BOOL)isFlipped 150 | { 151 | return YES; 152 | } 153 | 154 | @end 155 | -------------------------------------------------------------------------------- /src/PDPredicatePanelController.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | extern NSString * const PDPredicateDidChange; 28 | 29 | @interface PDPredicatePanelController : NSWindowController 30 | 31 | - (id)init; 32 | 33 | @property(nonatomic, weak) IBOutlet NSPredicateEditor *predicateEditor; 34 | @property(nonatomic, weak) IBOutlet NSButton *addSmartFolderButton; 35 | @property(nonatomic, weak) IBOutlet NSButton *cancelButton; 36 | @property(nonatomic, weak) IBOutlet NSButton *okButton; 37 | 38 | @property(nonatomic, copy) NSPredicate *predicate; 39 | 40 | - (NSPredicate *)predicateWithFormat:(NSString *)str, ...; 41 | - (NSPredicate *)predicateWithFormat:(NSString *)str argv:(va_list)args; 42 | 43 | - (IBAction)controlAction:(id)sender; 44 | 45 | - (IBAction)newSmartAlbumAction:(id)sender; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /src/PDPredicatePanelController.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDPredicatePanelController.h" 26 | 27 | #import "PDAppDelegate.h" 28 | #import "PDImage.h" 29 | #import "PDImageProperty.h" 30 | #import "PDWindowController.h" 31 | 32 | NSString * const PDPredicateDidChange = @"PDPredicateDidChange"; 33 | 34 | @implementation PDPredicatePanelController 35 | 36 | @synthesize predicateEditor = _predicateEditor; 37 | @synthesize addSmartFolderButton = _addSmartFolderButton; 38 | @synthesize cancelButton = _cancelButton; 39 | @synthesize okButton = _okButton; 40 | @synthesize predicate = _predicate; 41 | 42 | - (NSString *)windowNibName 43 | { 44 | return @"PDPredicatePanel"; 45 | } 46 | 47 | - (id)init 48 | { 49 | self = [super initWithWindow:nil]; 50 | if (self != nil) 51 | { 52 | _predicate = [NSCompoundPredicate andPredicateWithSubpredicates: 53 | @[[NSPredicate predicateWithFormat:@"%K >= %@", 54 | PDImage_Rating, @0]]]; 55 | } 56 | return self; 57 | } 58 | 59 | - (void)windowDidLoad 60 | { 61 | /* Setting up templates in IB is painful, so do it by hand. */ 62 | 63 | [_predicateEditor setRowTemplates:PDImagePredicateEditorRowTemplates()]; 64 | [_predicateEditor setObjectValue:_predicate]; 65 | } 66 | 67 | - (void)setPredicate:(NSPredicate *)obj 68 | { 69 | /* Make sure it's always compound, else it can't be edited. */ 70 | 71 | if (obj == nil) 72 | obj = [NSCompoundPredicate andPredicateWithSubpredicates:@[]]; 73 | else if(![obj isKindOfClass:[NSCompoundPredicate class]]) 74 | obj = [NSCompoundPredicate andPredicateWithSubpredicates:@[obj]]; 75 | 76 | if (_predicate != obj) 77 | { 78 | _predicate = [obj copy]; 79 | [_predicateEditor setObjectValue:obj]; 80 | } 81 | } 82 | 83 | - (NSPredicate *)predicateWithFormat:(NSString *)str, ... 84 | { 85 | va_list args; 86 | va_start(args, str); 87 | 88 | NSPredicate *ret = [self predicateWithFormat:str argv:args]; 89 | 90 | va_end(args); 91 | return ret; 92 | } 93 | 94 | - (NSPredicate *)predicateWithFormat:(NSString *)str argv:(va_list)args 95 | { 96 | if (str.length == 0) 97 | return nil; 98 | 99 | @try { 100 | return [NSPredicate predicateWithFormat:str arguments:args]; 101 | } @catch (id exception) { 102 | return nil; 103 | } 104 | } 105 | 106 | - (IBAction)controlAction:(id)sender 107 | { 108 | if (sender == _predicateEditor) 109 | { 110 | _predicate = [[_predicateEditor objectValue] copy]; 111 | [[NSNotificationCenter defaultCenter] 112 | postNotificationName:PDPredicateDidChange object:self]; 113 | } 114 | else if (sender == _okButton) 115 | { 116 | [self close]; 117 | } 118 | else if (sender == _cancelButton) 119 | { 120 | _predicate = nil; 121 | [[NSNotificationCenter defaultCenter] 122 | postNotificationName:PDPredicateDidChange object:self]; 123 | 124 | [self close]; 125 | } 126 | else if (sender == _addSmartFolderButton) 127 | { 128 | /* FIXME: implement this correctly. */ 129 | 130 | NSBeep(); 131 | } 132 | } 133 | 134 | - (IBAction)newSmartAlbumAction:(id)sender 135 | { 136 | [[(PDAppDelegate *)[NSApp delegate] windowController] 137 | newSmartAlbumAction:sender]; 138 | } 139 | 140 | @end 141 | -------------------------------------------------------------------------------- /src/PDSplitView.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | @interface PDSplitView : NSSplitView 28 | 29 | @property(nonatomic) NSInteger indexOfResizableSubview; 30 | 31 | - (NSDictionary *)savedViewState; 32 | - (void)applySavedViewState:(NSDictionary *)dict; 33 | 34 | - (void)setSubview:(NSView *)subview collapsed:(BOOL)flag; 35 | 36 | - (BOOL)shouldAdjustSizeOfSubview:(NSView *)subview; 37 | 38 | - (CGFloat)minimumSizeOfSubview:(NSView *)subview; 39 | 40 | @end 41 | 42 | @protocol PDSplitViewDelegate 43 | @optional 44 | 45 | - (CGFloat)splitView:(PDSplitView *)splitView 46 | minimumSizeOfSubview:(NSView *)subview; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /src/PDSplitView.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDSplitView.h" 26 | 27 | #import "PDMacros.h" 28 | 29 | @implementation PDSplitView 30 | { 31 | NSInteger _indexOfResizableSubview; 32 | NSView *_collapsingSubview; 33 | } 34 | 35 | @synthesize indexOfResizableSubview = _indexOfResizableSubview; 36 | 37 | - (id)initWithFrame:(CGRect)frame 38 | { 39 | self = [super initWithFrame:frame]; 40 | if (self == nil) 41 | return nil; 42 | 43 | _indexOfResizableSubview = -1; 44 | 45 | return self; 46 | } 47 | 48 | - (id)initWithCoder:(NSCoder *)coder 49 | { 50 | self = [super initWithCoder:coder]; 51 | if (self == nil) 52 | return nil; 53 | 54 | _indexOfResizableSubview = -1; 55 | 56 | return self; 57 | } 58 | 59 | - (NSDictionary *)savedViewState 60 | { 61 | NSArray *subviews = self.subviews; 62 | CGRect bounds = self.bounds; 63 | BOOL vertical = self.vertical; 64 | CGFloat size = vertical ? bounds.size.width : bounds.size.height; 65 | 66 | NSMutableArray *data = [NSMutableArray array]; 67 | 68 | for (NSView *subview in subviews) 69 | { 70 | CGRect frame = subview.frame; 71 | CGFloat x = vertical ? frame.origin.x : frame.origin.y; 72 | CGFloat w = vertical ? frame.size.width : frame.size.height; 73 | [data addObject:@(x / size)]; 74 | [data addObject:@(w / size)]; 75 | [data addObject:@(subview.hidden)]; 76 | } 77 | 78 | return @{@"values": data}; 79 | } 80 | 81 | - (void)applySavedViewState:(NSDictionary *)dict 82 | { 83 | NSArray *data = dict[@"values"]; 84 | 85 | NSArray *subviews = self.subviews; 86 | NSInteger count = subviews.count; 87 | 88 | if (data.count != count * 3) 89 | return; 90 | 91 | CGRect bounds = self.bounds; 92 | BOOL vertical = self.vertical; 93 | CGFloat size = vertical ? bounds.size.width : bounds.size.height; 94 | 95 | for (NSInteger i = 0; i < count; i++) 96 | { 97 | CGFloat x = [data[i*3+0] doubleValue] * size; 98 | CGFloat w = [data[i*3+1] doubleValue] * size; 99 | BOOL flag = [data[i*3+2] boolValue]; 100 | 101 | NSView *subview = subviews[i]; 102 | CGRect frame = bounds; 103 | 104 | if (vertical) 105 | { 106 | frame.origin.x = x; 107 | frame.size.width = w; 108 | } 109 | else 110 | { 111 | frame.origin.y = x; 112 | frame.size.height = w; 113 | } 114 | 115 | subview.hidden = flag; 116 | subview.frame = frame; 117 | } 118 | 119 | [self adjustSubviews]; 120 | } 121 | 122 | - (void)setSubview:(NSView *)subview collapsed:(BOOL)flag 123 | { 124 | if (flag != subview.hidden) 125 | { 126 | subview.hidden = flag; 127 | 128 | _collapsingSubview = subview; 129 | [self adjustSubviews]; 130 | _collapsingSubview = nil; 131 | } 132 | } 133 | 134 | - (void)resizeSubviewsWithOldSize:(CGSize)sz 135 | { 136 | NSArray *subviews = self.subviews; 137 | 138 | if (_indexOfResizableSubview < 0 139 | || _indexOfResizableSubview >= subviews.count) 140 | { 141 | [self adjustSubviews]; 142 | return; 143 | } 144 | 145 | CGRect bounds = self.bounds; 146 | NSInteger count = subviews.count; 147 | BOOL vertical = self.vertical; 148 | CGFloat thick = self.dividerThickness; 149 | CGFloat p = vertical ? bounds.origin.x : bounds.origin.y; 150 | 151 | for (NSInteger idx = 0; idx < count; idx++) 152 | { 153 | NSView *view = subviews[idx]; 154 | if (view.hidden) 155 | continue; 156 | 157 | CGRect frame = view.frame; 158 | 159 | if (vertical) 160 | frame.origin.y = bounds.origin.y, frame.size.height=bounds.size.height; 161 | else 162 | frame.origin.x = bounds.origin.x, frame.size.width = bounds.size.width; 163 | 164 | if (vertical) 165 | frame.origin.x = p; 166 | else 167 | frame.origin.y = p; 168 | 169 | if (idx == _indexOfResizableSubview) 170 | { 171 | if (vertical) 172 | frame.size.width += bounds.size.width - sz.width; 173 | else 174 | frame.size.height += bounds.size.height - sz.height; 175 | } 176 | 177 | view.frame = frame; 178 | 179 | if (vertical) 180 | p += frame.size.width + thick; 181 | else 182 | p += frame.size.height + thick; 183 | } 184 | } 185 | 186 | - (BOOL)shouldAdjustSizeOfSubview:(NSView *)subview 187 | { 188 | if (_collapsingSubview != nil) 189 | { 190 | if (subview == _collapsingSubview) 191 | return NO; 192 | 193 | // If more than two subviews, only move those adjacent to the 194 | // [un]collapsing view. 195 | 196 | NSArray *subviews = self.subviews; 197 | NSInteger idx1 = [subviews indexOfObjectIdenticalTo:_collapsingSubview]; 198 | NSInteger idx2 = [subviews indexOfObjectIdenticalTo:subview]; 199 | 200 | if (ABS(idx1 - idx2) > 1) 201 | return NO; 202 | } 203 | 204 | return YES; 205 | } 206 | 207 | - (CGFloat)minimumSizeOfSubview:(NSView *)subview 208 | { 209 | id delegate = self.delegate; 210 | 211 | if ([delegate respondsToSelector:@selector(splitView:minimumSizeOfSubview:)]) 212 | return [delegate splitView:self minimumSizeOfSubview:subview]; 213 | else 214 | return 100; 215 | } 216 | 217 | @end 218 | -------------------------------------------------------------------------------- /src/PDThumbnailLayer.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | #import "PDImage.h" 28 | 29 | @interface PDThumbnailLayer : CALayer 30 | 31 | @property(nonatomic, strong) PDImage *image; 32 | 33 | @property(nonatomic, assign, getter=isSelected) BOOL selected; 34 | @property(nonatomic, assign, getter=isPrimary) BOOL primary; 35 | 36 | @property(nonatomic, assign) BOOL displaysMetadata; 37 | 38 | - (void)invalidate; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /src/PDViewController.h: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import 26 | 27 | @class PDWindowController; 28 | 29 | @interface PDViewController : NSViewController 30 | { 31 | PDWindowController *_controller; 32 | IBOutlet NSProgressIndicator *_progressIndicator; 33 | } 34 | 35 | + (NSString *)viewNibName; 36 | 37 | @property(nonatomic, copy, readonly) NSString *identifier; 38 | 39 | - (id)initWithController:(PDWindowController *)controller; 40 | 41 | - (void)invalidate; 42 | 43 | - (void)viewWillAppear; 44 | - (void)viewDidAppear; 45 | 46 | - (void)viewWillDisappear; 47 | - (void)viewDidDisappear; 48 | 49 | @property(nonatomic, readonly) PDWindowController *controller; 50 | 51 | - (PDViewController *)viewControllerWithClass:(Class)cls; 52 | 53 | @property(nonatomic, copy) NSArray *subviewControllers; 54 | 55 | - (void)addSubviewController:(PDViewController *)controller; 56 | - (void)removeSubviewController:(PDViewController *)controller; 57 | 58 | @property(nonatomic, readonly) NSView *initialFirstResponder; 59 | 60 | - (void)synchronize; 61 | 62 | - (NSDictionary *)savedViewState; 63 | - (void)applySavedViewState:(NSDictionary *)dict; 64 | 65 | - (void)addToContainerView:(NSView *)view; 66 | - (void)removeFromContainer; 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /src/PDViewController.m: -------------------------------------------------------------------------------- 1 | /* -*- c-style: gnu -*- 2 | 3 | Copyright (c) 2013 John Harper 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | #import "PDViewController.h" 26 | 27 | #import "PDAppDelegate.h" 28 | #import "PDWindowController.h" 29 | 30 | @implementation PDViewController 31 | { 32 | NSMutableArray *_subviewControllers; 33 | BOOL _pendingProgressUpdate; 34 | } 35 | 36 | @synthesize controller = _controller; 37 | 38 | + (NSString *)viewNibName 39 | { 40 | return nil; 41 | } 42 | 43 | - (NSString *)identifier 44 | { 45 | return NSStringFromClass([self class]); 46 | } 47 | 48 | - (id)initWithController:(PDWindowController *)controller 49 | { 50 | self = [super initWithNibName:[[self class] viewNibName] 51 | bundle:[NSBundle mainBundle]]; 52 | if (self != nil) 53 | { 54 | _controller = controller; 55 | _subviewControllers = [[NSMutableArray alloc] init]; 56 | } 57 | return self; 58 | } 59 | 60 | - (void)invalidate 61 | { 62 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 63 | 64 | for (PDViewController *controller in _subviewControllers) 65 | [controller invalidate]; 66 | } 67 | 68 | - (void)dealloc 69 | { 70 | [self invalidate]; 71 | } 72 | 73 | - (PDViewController *)viewControllerWithClass:(Class)cls 74 | { 75 | if ([self class] == cls) 76 | return self; 77 | 78 | for (PDViewController *obj in _subviewControllers) 79 | { 80 | PDViewController *tem = [obj viewControllerWithClass:cls]; 81 | if (tem != nil) 82 | return tem; 83 | } 84 | 85 | return nil; 86 | } 87 | 88 | - (NSArray *)subviewControllers 89 | { 90 | return _subviewControllers; 91 | } 92 | 93 | - (void)setSubviewControllers:(NSArray *)array 94 | { 95 | _subviewControllers = [array mutableCopy]; 96 | } 97 | 98 | - (void)addSubviewController:(PDViewController *)controller 99 | { 100 | [_subviewControllers addObject:controller]; 101 | } 102 | 103 | - (void)removeSubviewController:(PDViewController *)controller 104 | { 105 | NSInteger idx = [_subviewControllers indexOfObjectIdenticalTo:controller]; 106 | 107 | if (idx != NSNotFound) 108 | [_subviewControllers removeObjectAtIndex:idx]; 109 | } 110 | 111 | - (NSView *)initialFirstResponder 112 | { 113 | NSView *view = [self view]; 114 | return [view acceptsFirstResponder] ? view : nil; 115 | } 116 | 117 | - (void)viewDidLoad 118 | { 119 | [super viewDidLoad]; 120 | 121 | if (_progressIndicator != nil) 122 | { 123 | [[NSNotificationCenter defaultCenter] 124 | addObserver:self selector:@selector(_backgroundActivityDidChange:) 125 | name:PDBackgroundActivityDidChange object:[NSApp delegate]]; 126 | } 127 | } 128 | 129 | - (void)viewWillAppear 130 | { 131 | } 132 | 133 | - (void)viewDidAppear 134 | { 135 | } 136 | 137 | - (void)viewWillDisappear 138 | { 139 | } 140 | 141 | - (void)viewDidDisappear 142 | { 143 | } 144 | 145 | - (void)synchronize 146 | { 147 | for (PDViewController *controller in _subviewControllers) 148 | [controller synchronize]; 149 | } 150 | 151 | - (NSDictionary *)savedViewState 152 | { 153 | if (_subviewControllers.count == 0) 154 | return [NSDictionary dictionary]; 155 | 156 | NSMutableDictionary *controllers = [NSMutableDictionary dictionary]; 157 | 158 | for (PDViewController *controller in _subviewControllers) 159 | { 160 | NSDictionary *sub = [controller savedViewState]; 161 | if (sub.count != 0) 162 | controllers[controller.identifier] = sub; 163 | } 164 | 165 | return @{ 166 | @"PDViewControllers": controllers 167 | }; 168 | } 169 | 170 | - (void)applySavedViewState:(NSDictionary *)state 171 | { 172 | NSDictionary *dict = state[@"PDViewControllers"]; 173 | if (dict != nil) 174 | { 175 | for (PDViewController *controller in _subviewControllers) 176 | { 177 | NSDictionary *sub = dict[controller.identifier]; 178 | if (sub != nil) 179 | [controller applySavedViewState:sub]; 180 | } 181 | } 182 | } 183 | 184 | - (void)addToContainerView:(NSView *)superview 185 | { 186 | NSView *view = [self view]; 187 | assert([view superview] == nil); 188 | 189 | view.frame = superview.bounds; 190 | 191 | [self viewWillAppear]; 192 | 193 | [superview addSubview:view]; 194 | 195 | [self viewDidAppear]; 196 | } 197 | 198 | - (void)removeFromContainer 199 | { 200 | [self viewWillDisappear]; 201 | 202 | [self.view removeFromSuperview]; 203 | 204 | [self viewDidDisappear]; 205 | } 206 | 207 | - (void)_backgroundActivityDidChange:(NSNotification *)note 208 | { 209 | PDAppDelegate *delegate = (id)[NSApp delegate]; 210 | 211 | BOOL state = delegate.backgroundActivity; 212 | 213 | if (!state) 214 | { 215 | [_progressIndicator stopAnimation:self]; 216 | } 217 | else 218 | { 219 | if (!_pendingProgressUpdate) 220 | { 221 | dispatch_time_t t 222 | = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 4); 223 | 224 | dispatch_after(t, dispatch_get_main_queue(), ^ 225 | { 226 | _pendingProgressUpdate = NO; 227 | 228 | if (delegate.backgroundActivity) 229 | [_progressIndicator startAnimation:self]; 230 | else 231 | [_progressIndicator stopAnimation:self]; 232 | }); 233 | 234 | _pendingProgressUpdate = YES; 235 | } 236 | } 237 | } 238 | 239 | @end 240 | -------------------------------------------------------------------------------- /src/PXSourceList/LICENSE: -------------------------------------------------------------------------------- 1 | PXSourceList is licensed under the New BSD License, as detailed below 2 | (adapted from OSI http://www.opensource.org/licenses/bsd-license.php): 3 | 4 | Copyright (c) 2011, Alex Rozanski. All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are 8 | met: 9 | 10 | - Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 13 | - Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in the 15 | documentation and/or other materials provided with the distribution. 16 | 17 | - Neither the name of the author nor the names of its contributors may 18 | be used to endorse or promote products derived from this software 19 | without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 22 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | HOLDER 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 | -------------------------------------------------------------------------------- /src/PXSourceList/PXSourceList-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | uk.co.alexrozanski.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSHumanReadableCopyright 26 | Copyright © 2012 Alex Rozanski. All rights reserved. 27 | NSPrincipalClass 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/PXSourceList/PXSourceList.h: -------------------------------------------------------------------------------- 1 | // 2 | // PXSourceList.h 3 | // PXSourceList 4 | // 5 | // Created by Alex Rozanski on 05/09/2009. 6 | // Copyright 2009-10 Alex Rozanski http://perspx.com 7 | // 8 | 9 | #import 10 | 11 | #import "PXSourceListDelegate.h" 12 | #import "PXSourceListDataSource.h" 13 | 14 | #ifndef MAC_OS_X_VERSION_10_6 15 | @protocol NSOutlineViewDelegate @end 16 | @protocol NSOutlineViewDataSource @end 17 | #endif 18 | 19 | @interface PXSourceList: NSOutlineView 20 | { 21 | id _secondaryDelegate; //Used to store the publicly visible delegate 22 | id _secondaryDataSource; //Used to store the publicly visible data source 23 | 24 | NSSize _iconSize; //The size of icons in the Source List. Defaults to 16x16 25 | } 26 | 27 | @property (nonatomic, assign) NSSize iconSize; 28 | 29 | @property (nonatomic, assign) id dataSource; 30 | @property (nonatomic, assign) id delegate; 31 | 32 | - (NSUInteger)numberOfGroups; //Returns the number of groups in the Source List 33 | - (BOOL)isGroupItem:(id)item; //Returns whether `item` is a group 34 | - (BOOL)isGroupAlwaysExpanded:(id)group; //Returns whether `group` is displayed as always expanded 35 | 36 | - (BOOL)itemHasBadge:(id)item; //Returns whether `item` has a badge 37 | - (NSInteger)badgeValueForItem:(id)item; //Returns the badge value for `item` 38 | 39 | @end 40 | 41 | -------------------------------------------------------------------------------- /src/PXSourceList/PXSourceListDataSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // PXSourceListDataSource.h 3 | // PXViewKit 4 | // 5 | // Created by Alex Rozanski on 17/10/2009. 6 | // Copyright 2009-10 Alex Rozanski http://perspx.com 7 | // 8 | 9 | #import 10 | 11 | @class PXSourceList; 12 | 13 | @protocol PXSourceListDataSource 14 | 15 | @required 16 | - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item; 17 | - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item; 18 | - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item; 19 | - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item; 20 | 21 | @optional 22 | - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item; 23 | 24 | - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item; 25 | - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item; 26 | - (NSColor*)sourceList:(PXSourceList*)aSourceList badgeTextColorForItem:(id)item; 27 | - (NSColor*)sourceList:(PXSourceList*)aSourceList badgeBackgroundColorForItem:(id)item; 28 | 29 | - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item; 30 | - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item; 31 | 32 | //The rest of these methods are basically "wrappers" for the NSOutlineViewDataSource methods 33 | - (id)sourceList:(PXSourceList*)aSourceList itemForPersistentObject:(id)object; 34 | - (id)sourceList:(PXSourceList*)aSourceList persistentObjectForItem:(id)item; 35 | 36 | - (BOOL)sourceList:(PXSourceList*)aSourceList writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard; 37 | - (NSDragOperation)sourceList:(PXSourceList*)sourceList validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index; 38 | - (BOOL)sourceList:(PXSourceList*)aSourceList acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index; 39 | - (NSArray *)sourceList:(PXSourceList*)aSourceList namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination forDraggedItems:(NSArray *)items; 40 | 41 | //Drag and drop methods added for 10.7 42 | #ifdef MAC_OS_X_VERSION_10_7 43 | 44 | - (id )sourceList:(PXSourceList *)aSourceList pasteboardWriterForItem:(id)item; 45 | - (void)sourceList:(PXSourceList *)aSourceList draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint forItems:(NSArray *)draggedItems; 46 | - (void)sourceList:(PXSourceList *)aSourceList draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation; 47 | - (void)sourceList:(PXSourceList *)aSourceList updateDraggingItemsForDrag:(id )draggingInfo; 48 | 49 | #endif 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /src/PXSourceList/PXSourceListDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // PXSourceListDelegate.h 3 | // PXViewKit 4 | // 5 | // Created by Alex Rozanski on 17/10/2009. 6 | // Copyright 2009-10 Alex Rozanski http://perspx.com 7 | // 8 | 9 | #import 10 | 11 | @class PXSourceList; 12 | 13 | @protocol PXSourceListDelegate 14 | 15 | @optional 16 | //Extra methods 17 | - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group; 18 | - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item; 19 | 20 | //Basically NSOutlineViewDelegate wrapper methods 21 | - (BOOL)sourceList:(PXSourceList*)aSourceList shouldSelectItem:(id)item; 22 | - (NSIndexSet*)sourceList:(PXSourceList*)aSourceList selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes; 23 | 24 | - (BOOL)sourceList:(PXSourceList*)aSourceList shouldEditItem:(id)item; 25 | 26 | - (BOOL)sourceList:(PXSourceList*)aSourceList shouldTrackCell:(NSCell *)cell forItem:(id)item; 27 | 28 | - (BOOL)sourceList:(PXSourceList*)aSourceList shouldExpandItem:(id)item; 29 | - (BOOL)sourceList:(PXSourceList*)aSourceList shouldCollapseItem:(id)item; 30 | 31 | - (CGFloat)sourceList:(PXSourceList*)aSourceList heightOfRowByItem:(id)item; 32 | 33 | - (NSCell*)sourceList:(PXSourceList*)aSourceList willDisplayCell:(id)cell forItem:(id)item; 34 | - (NSCell*)sourceList:(PXSourceList*)aSourceList dataCellForItem:(id)item; 35 | 36 | @end 37 | 38 | @interface NSObject (PXSourceListNotifications) 39 | 40 | //Selection 41 | - (void)sourceListSelectionIsChanging:(NSNotification *)notification; 42 | - (void)sourceListSelectionDidChange:(NSNotification *)notification; 43 | 44 | //Item expanding/collapsing 45 | - (void)sourceListItemWillExpand:(NSNotification *)notification; 46 | - (void)sourceListItemDidExpand:(NSNotification *)notification; 47 | - (void)sourceListItemWillCollapse:(NSNotification *)notification; 48 | - (void)sourceListItemDidCollapse:(NSNotification *)notification; 49 | 50 | - (void)sourceListDeleteKeyPressedOnRows:(NSNotification *)notification; 51 | 52 | 53 | @end 54 | 55 | //PXSourceList delegate notifications 56 | extern NSString * const PXSLSelectionIsChangingNotification; 57 | extern NSString * const PXSLSelectionDidChangeNotification; 58 | extern NSString * const PXSLItemWillExpandNotification; 59 | extern NSString * const PXSLItemDidExpandNotification; 60 | extern NSString * const PXSLItemWillCollapseNotification; 61 | extern NSString * const PXSLItemDidCollapseNotification; 62 | extern NSString * const PXSLDeleteKeyPressedOnRowsNotification; 63 | 64 | -------------------------------------------------------------------------------- /src/Phod-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #endif 10 | -------------------------------------------------------------------------------- /src/main.m: -------------------------------------------------------------------------------- 1 | // -*- c-style: gnu -*- 2 | 3 | #import 4 | 5 | int 6 | main(int argc, const char **argv) 7 | { 8 | return NSApplicationMain(argc, argv); 9 | } 10 | --------------------------------------------------------------------------------