├── ScreenShots
├── s0.png
├── s1.png
├── s2.png
├── s3.png
├── sorting.png
└── load-bundle.png
├── .travis.yml
├── RTTinyPNGWebAPIPlugin.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
├── xcshareddata
│ └── xcschemes
│ │ └── RTTinyPNGWebAPIPlugin.xcscheme
└── project.pbxproj
├── RTTinyPNGWebAPIPlugin
├── RTImageController.h
├── RTWorkspace.h
├── NSObject_Extension.h
├── RTTinyPNGWebAPIPlugin.h
├── NSObject_Extension.m
├── RTWorkspace.m
├── RTImageItem.h
├── Info.plist
├── RTTinyPNGWebAPIPlugin.m
├── RTImageItem.m
├── XcodeIDE.h
├── RTImageController.m
└── RTImageController.xib
├── LICENSE
├── .gitignore
└── README.md
/ScreenShots/s0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rickytan/RTTinyPNGWebAPIPlugin/HEAD/ScreenShots/s0.png
--------------------------------------------------------------------------------
/ScreenShots/s1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rickytan/RTTinyPNGWebAPIPlugin/HEAD/ScreenShots/s1.png
--------------------------------------------------------------------------------
/ScreenShots/s2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rickytan/RTTinyPNGWebAPIPlugin/HEAD/ScreenShots/s2.png
--------------------------------------------------------------------------------
/ScreenShots/s3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rickytan/RTTinyPNGWebAPIPlugin/HEAD/ScreenShots/s3.png
--------------------------------------------------------------------------------
/ScreenShots/sorting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rickytan/RTTinyPNGWebAPIPlugin/HEAD/ScreenShots/sorting.png
--------------------------------------------------------------------------------
/ScreenShots/load-bundle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rickytan/RTTinyPNGWebAPIPlugin/HEAD/ScreenShots/load-bundle.png
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 | script: xctool -project RTTinyPNGWebAPIPlugin.xcodeproj -scheme RTTinyPNGWebAPIPlugin
3 |
--------------------------------------------------------------------------------
/RTTinyPNGWebAPIPlugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/RTTinyPNGWebAPIPlugin/RTImageController.h:
--------------------------------------------------------------------------------
1 | //
2 | // RTImageController.h
3 | // RTTinyPNGWebAPIPlugin
4 | //
5 | // Created by benfen on 16/2/4.
6 | // Copyright © 2016年 Shiqu. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RTImageController : NSWindowController
12 | - (void)clearImageItems;
13 | @end
14 |
--------------------------------------------------------------------------------
/RTTinyPNGWebAPIPlugin/RTWorkspace.h:
--------------------------------------------------------------------------------
1 | //
2 | // IAWorkspace.h
3 | // RTImageAssets
4 | //
5 | // Created by ricky on 14-12-10.
6 | // Copyright (c) 2014年 rickytan. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RTWorkspace : NSObject
12 |
13 | + (NSString *)currentWorkspacePath;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/RTTinyPNGWebAPIPlugin/NSObject_Extension.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSObject_Extension.h
3 | // RTTinyPNGWebAPIPlugin
4 | //
5 | // Created by benfen on 16/2/4.
6 | // Copyright © 2016年 Shiqu. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface NSObject (Xcode_Plugin_Template_Extension)
12 |
13 | + (void)pluginDidLoad:(NSBundle *)plugin;
14 |
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/RTTinyPNGWebAPIPlugin/RTTinyPNGWebAPIPlugin.h:
--------------------------------------------------------------------------------
1 | //
2 | // RTTinyPNGWebAPIPlugin.h
3 | // RTTinyPNGWebAPIPlugin
4 | //
5 | // Created by benfen on 16/2/4.
6 | // Copyright © 2016年 Shiqu. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class RTTinyPNGWebAPIPlugin;
12 |
13 | static RTTinyPNGWebAPIPlugin *sharedPlugin;
14 |
15 | @interface RTTinyPNGWebAPIPlugin : NSObject
16 |
17 | + (instancetype)sharedPlugin;
18 | - (id)initWithBundle:(NSBundle *)plugin;
19 |
20 | @property (nonatomic, strong, readonly) NSBundle* bundle;
21 | @end
--------------------------------------------------------------------------------
/RTTinyPNGWebAPIPlugin/NSObject_Extension.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSObject_Extension.m
3 | // RTTinyPNGWebAPIPlugin
4 | //
5 | // Created by benfen on 16/2/4.
6 | // Copyright © 2016年 Shiqu. All rights reserved.
7 | //
8 |
9 |
10 | #import "NSObject_Extension.h"
11 | #import "RTTinyPNGWebAPIPlugin.h"
12 |
13 | @implementation NSObject (Xcode_Plugin_Template_Extension)
14 |
15 | + (void)pluginDidLoad:(NSBundle *)plugin
16 | {
17 | static dispatch_once_t onceToken;
18 | NSString *currentApplicationName = [[NSBundle mainBundle] infoDictionary][@"CFBundleName"];
19 | if ([currentApplicationName isEqual:@"Xcode"]) {
20 | dispatch_once(&onceToken, ^{
21 | sharedPlugin = [[RTTinyPNGWebAPIPlugin alloc] initWithBundle:plugin];
22 | });
23 | }
24 | }
25 | @end
26 |
--------------------------------------------------------------------------------
/RTTinyPNGWebAPIPlugin/RTWorkspace.m:
--------------------------------------------------------------------------------
1 | //
2 | // IAWorkspace.m
3 | // RTImageAssets
4 | //
5 | // Created by ricky on 14-12-10.
6 | // Copyright (c) 2014年 rickytan. All rights reserved.
7 | //
8 |
9 | #import "XcodeIDE.h"
10 | #import "RTWorkspace.h"
11 |
12 | @implementation RTWorkspace
13 |
14 | + (IDEWorkspaceWindowController *)keyWindowController
15 | {
16 | NSArray *workspaceWindowControllers = [NSClassFromString(@"IDEWorkspaceWindowController") valueForKey:@"workspaceWindowControllers"];
17 | for (IDEWorkspaceWindowController *controller in workspaceWindowControllers) {
18 | if (controller.window.isKeyWindow) {
19 | return controller;
20 | }
21 | }
22 | return workspaceWindowControllers.firstObject;
23 | }
24 |
25 | + (IDEWorkspace *)workspaceForKeyWindow
26 | {
27 | return [[self keyWindowController] valueForKey:@"_workspace"];
28 | }
29 |
30 | + (NSString *)currentWorkspacePath
31 | {
32 | return [self workspaceForKeyWindow].representingFilePath.pathString;
33 | }
34 |
35 | @end
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Ricky Tan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/RTTinyPNGWebAPIPlugin/RTImageItem.h:
--------------------------------------------------------------------------------
1 | //
2 | // RTImageItem.h
3 | // RTTinyPNGWebAPIPlugin
4 | //
5 | // Created by benfen on 16/2/4.
6 | // Copyright © 2016年 Shiqu. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | typedef NS_ENUM(NSInteger, RTImageOptimizeState) {
12 | RTImageOptimizeStateNormal,
13 | RTImageOptimizeStatePending,
14 | RTImageOptimizeStateOptimized,
15 | RTImageOptimizeStateFailed
16 | };
17 |
18 | @interface RTImageItem : NSObject
19 | @property (nonatomic, strong) NSString *imageName;
20 | @property (nonatomic, strong) NSString *imagePath;
21 | @property (nonatomic, readonly) NSImage *imageIcon;
22 | @property (nonatomic, assign) NSSize size;
23 |
24 | /**
25 | * @author Ricky, 16-02-04 19:02:56
26 | *
27 | * File size in bytes
28 | */
29 | @property (nonatomic, assign) NSInteger imageSize;
30 |
31 | /**
32 | * @author Ricky, 16-02-04 19:02:06
33 | *
34 | * File size after optimization
35 | */
36 | @property (nonatomic, assign) NSInteger imageSizeOptimized;
37 |
38 | @property (nonatomic, strong) NSDate *lastUpdateDate;
39 | @property (nonatomic, assign, getter=isSelected) BOOL selected;
40 | @property (nonatomic, assign, getter=hasOptimized, readonly) BOOL optimized;
41 | @property (nonatomic, assign) RTImageOptimizeState state;
42 |
43 | + (instancetype)itemWithPath:(NSString *)path;
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/RTTinyPNGWebAPIPlugin/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | BNDL
19 | CFBundleShortVersionString
20 | 0.5.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | DVTPlugInCompatibilityUUIDs
26 |
27 | C4A681B0-4A26-480E-93EC-1218098B9AA0
28 | F41BD31E-2683-44B8-AE7F-5F09E919790E
29 | AD68E85B-441B-4301-B564-A45E4919A6AD
30 | A16FF353-8441-459E-A50C-B071F53F51B7
31 | 9F75337B-21B4-4ADC-B558-F9CADF7073A7
32 | E969541F-E6F9-4D25-8158-72DC3545A6C6
33 | 8DC44374-2B35-4C57-A6FE-2AD66A36AAD9
34 | AABB7188-E14E-4433-AD3B-5CD791EAD9A3
35 | 8DC44374-2B35-4C57-A6FE-2AD66A36AAD9
36 | AABB7188-E14E-4433-AD3B-5CD791EAD9A3
37 | 7FDF5C7A-131F-4ABB-9EDC-8C5F8F0B8A90
38 | 0420B86A-AA43-4792-9ED0-6FE0F2B16A13
39 | 7265231C-39B4-402C-89E1-16167C4CC990
40 |
41 | LSMinimumSystemVersion
42 | $(MACOSX_DEPLOYMENT_TARGET)
43 | NSPrincipalClass
44 | RTTinyPNGWebAPIPlugin
45 | XC4Compatible
46 |
47 | XCPluginHasUI
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ### OSX ###
6 | .DS_Store
7 | .AppleDouble
8 | .LSOverride
9 |
10 | # Thumbnails
11 | ._*
12 |
13 | # Files that might appear on external disk
14 | .Spotlight-V100
15 | .Trashes
16 |
17 | ### AppCode ###
18 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
19 |
20 | *.iml
21 |
22 | ## Directory-based project format:
23 | .idea/
24 | # if you remove the above rule, at least ignore the following:
25 |
26 | # User-specific stuff:
27 | # .idea/workspace.xml
28 | # .idea/tasks.xml
29 | # .idea/dictionaries
30 |
31 | # Sensitive or high-churn files:
32 | # .idea/dataSources.ids
33 | # .idea/dataSources.xml
34 | # .idea/sqlDataSources.xml
35 | # .idea/dynamic.xml
36 | # .idea/uiDesigner.xml
37 |
38 | # Gradle:
39 | # .idea/gradle.xml
40 | # .idea/libraries
41 |
42 | # Mongo Explorer plugin:
43 | # .idea/mongoSettings.xml
44 |
45 | ## File-based project format:
46 | *.ipr
47 | *.iws
48 |
49 | ## Build generated
50 | build/
51 | DerivedData
52 |
53 | ## Various settings
54 | *.pbxuser
55 | !default.pbxuser
56 | *.mode1v3
57 | !default.mode1v3
58 | *.mode2v3
59 | !default.mode2v3
60 | *.perspectivev3
61 | !default.perspectivev3
62 | xcuserdata
63 |
64 | ## Other
65 | *.xccheckout
66 | *.moved-aside
67 | *.xcuserstate
68 | *.xcscmblueprint
69 |
70 | ## Obj-C/Swift specific
71 | *.hmap
72 | *.ipa
73 |
74 | # CocoaPods
75 | #
76 | # We recommend against adding the Pods directory to your .gitignore. However
77 | # you should judge for yourself, the pros and cons are mentioned at:
78 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
79 | #
80 | # Pods/
81 |
82 | # Carthage
83 | #
84 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
85 | # Carthage/Checkouts
86 |
87 | Carthage/Build
88 |
89 | # fastlane
90 | #
91 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
92 | # screenshots whenever they are needed.
93 | # For more information about the recommended setup visit:
94 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
95 |
96 | fastlane/report.xml
97 | fastlane/screenshots
98 |
--------------------------------------------------------------------------------
/RTTinyPNGWebAPIPlugin.xcodeproj/xcshareddata/xcschemes/RTTinyPNGWebAPIPlugin.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
46 |
47 |
48 |
49 |
61 |
65 |
66 |
67 |
73 |
74 |
75 |
76 |
77 |
78 |
84 |
85 |
87 |
88 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RTTinyPNGWebAPIPlugin [](https://travis-ci.org/rickytan/RTTinyPNGWebAPIPlugin) [](./LICENSE)
2 |
3 | [](http://rickytan.cn/donate/ "Donate me a cup of coffee")
4 |
5 | ## Introduction
6 |
7 | This is a simple **Xcode Plugin** to help **iOS** developers to minimize app assets, it calls api to compress your `.png`, `.jpg` image resources. So before you using this, please get your api key [HERE](https://tinypng.com/developers).
8 |
9 | ## Usage
10 |
11 | ### State
12 |
13 | A image source has 4 states, whith are:
14 |
15 | - *Normal*, not optimized
16 |
17 | 
18 |
19 | - *Pending*, waiting for optimization
20 |
21 | 
22 |
23 | - *Optimized*,
24 |
25 | 
26 |
27 | - *Error*, a network error occurred
28 |
29 | ### Selection
30 |
31 | When **Start** button pressed, the selected(with a check mark on the left) images are pushed into a processing queue, waiting for compressing. Your can click the checkbox on the table header to toggle select/deselect all, or click and drag to multi-select, then use the context menu to select/deselect multiple items.
32 |
33 | 
34 |
35 | ### Sorting
36 |
37 | The **Image Name**, **Image Path**, **File Size**, **Optimized** columns support sorting, use them well :smile:.
38 |
39 | 
40 |
41 | ## How to install
42 |
43 | * Just clone or download the Repo, and build with your **Xcode**, then restart **Xcode**.
44 | * Download the binary [HERE](https://github.com/rickytan/RTTinyPNGWebAPIPlugin/releases), decompress and move it to `~/Library/Application Support/Developer/Shared/Xcode/Plug-ins`
45 | * Open [Package Manager](http://alcatraz.io/) and search `RTTinyPNGWebAPIPlugin`
46 |
47 | > **Note**: If Xcode prompt a dialog, please click **Load Bundle**.
48 | >
49 | > 
50 |
51 | After you successfully installed the plugin, you can open the plugin window in **File** menu, or press `Shift Control t` for short.
52 |
53 | ## Issues
54 |
55 | If you find any bug(s), please let me know:
56 |
57 | ## License
58 |
59 | **MIT**
60 |
61 | ```
62 | The MIT License (MIT)
63 |
64 | Copyright (c) 2016 Ricky Tan
65 |
66 | Permission is hereby granted, free of charge, to any person obtaining a copy
67 | of this software and associated documentation files (the "Software"), to deal
68 | in the Software without restriction, including without limitation the rights
69 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
70 | copies of the Software, and to permit persons to whom the Software is
71 | furnished to do so, subject to the following conditions:
72 |
73 | The above copyright notice and this permission notice shall be included in all
74 | copies or substantial portions of the Software.
75 |
76 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
77 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
78 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
79 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
80 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
81 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
82 | SOFTWARE.
83 | ```
84 |
--------------------------------------------------------------------------------
/RTTinyPNGWebAPIPlugin/RTTinyPNGWebAPIPlugin.m:
--------------------------------------------------------------------------------
1 | //
2 | // RTTinyPNGWebAPIPlugin.m
3 | // RTTinyPNGWebAPIPlugin
4 | //
5 | // Created by benfen on 16/2/4.
6 | // Copyright © 2016年 Shiqu. All rights reserved.
7 | //
8 |
9 | #import "RTTinyPNGWebAPIPlugin.h"
10 |
11 | #import "RTImageController.h"
12 |
13 | @interface RTTinyPNGWebAPIPlugin()
14 | @property (nonatomic, strong, readwrite) NSBundle *bundle;
15 |
16 | @property (nonatomic, strong) RTImageController *imageController;
17 | @property (nonatomic, strong) NSMenuItem *actionMenuItem;
18 | @end
19 |
20 | @implementation RTTinyPNGWebAPIPlugin
21 |
22 | + (instancetype)sharedPlugin
23 | {
24 | return sharedPlugin;
25 | }
26 |
27 | - (id)initWithBundle:(NSBundle *)plugin
28 | {
29 | if (self = [super init]) {
30 | // reference to plugin's bundle, for resource access
31 | self.bundle = plugin;
32 |
33 | [[NSNotificationCenter defaultCenter] addObserver:self
34 | selector:@selector(onLaunch:)
35 | name:NSApplicationDidFinishLaunchingNotification
36 | object:nil];
37 |
38 | [[NSNotificationCenter defaultCenter] addObserver:self
39 | selector:@selector(onProjectChanged:)
40 | name:@"PBXProjectDidChangeNotification"
41 | object:nil];
42 |
43 | [[NSNotificationCenter defaultCenter] addObserver:self
44 | selector:@selector(onProjectOpen:)
45 | name:@"PBXProjectDidOpenNotification"
46 | object:nil];
47 |
48 | [[NSNotificationCenter defaultCenter] addObserver:self
49 | selector:@selector(onProjectClose:)
50 | name:@"PBXProjectWillCloseNotification"
51 | object:nil];
52 | }
53 | return self;
54 | }
55 |
56 | - (RTImageController *)imageController
57 | {
58 | if (!_imageController) {
59 | _imageController = [[RTImageController alloc] initWithWindowNibName:NSStringFromClass([RTImageController class])];
60 | }
61 | return _imageController;
62 | }
63 |
64 | - (NSMenuItem *)actionMenuItem
65 | {
66 | if (!_actionMenuItem) {
67 | NSMenuItem *menuItem = [[NSApp mainMenu] itemWithTitle:@"File"];
68 | if (menuItem) {
69 | [[menuItem submenu] addItem:[NSMenuItem separatorItem]];
70 | NSMenuItem *actionMenuItem = [[NSMenuItem alloc] initWithTitle:@"Tiny PNG"
71 | action:@selector(showWindow:)
72 | keyEquivalent:@"T"];
73 | [actionMenuItem setKeyEquivalentModifierMask:NSAlphaShiftKeyMask | NSControlKeyMask];
74 | [actionMenuItem setTarget:self.imageController];
75 | [[menuItem submenu] addItem:actionMenuItem];
76 |
77 | _actionMenuItem = actionMenuItem;
78 | }
79 | }
80 | return _actionMenuItem;
81 | }
82 |
83 | - (void)onLaunch:(NSNotification*)notif
84 | {
85 | //removeObserver
86 | [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationDidFinishLaunchingNotification object:nil];
87 |
88 | self.actionMenuItem.enabled = NO;
89 | }
90 |
91 | - (void)onProjectOpen:(NSNotification *)notif
92 | {
93 | self.actionMenuItem.enabled = YES;
94 | }
95 |
96 | - (void)onProjectChanged:(NSNotification *)notif
97 | {
98 | [self.imageController clearImageItems];
99 | [self.imageController close];
100 |
101 | self.actionMenuItem.enabled = YES;
102 | }
103 |
104 | - (void)onProjectClose:(NSNotification *)notif
105 | {
106 | self.actionMenuItem.enabled = NO;
107 |
108 | [self.imageController clearImageItems];
109 | [self.imageController close];
110 | }
111 |
112 | - (void)dealloc
113 | {
114 | [[NSNotificationCenter defaultCenter] removeObserver:self];
115 | }
116 |
117 | @end
118 |
--------------------------------------------------------------------------------
/RTTinyPNGWebAPIPlugin/RTImageItem.m:
--------------------------------------------------------------------------------
1 | //
2 | // RTImageItem.m
3 | // RTTinyPNGWebAPIPlugin
4 | //
5 | // Created by benfen on 16/2/4.
6 | // Copyright © 2016年 Shiqu. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | #import "RTImageItem.h"
13 |
14 | static const char *RT_IMAGE_OPTIMIZED_KEY = "com.tinypng.hasOptimized";
15 | static const char *RT_IMAGE_ORIGIN_SIZE_KEY = "com.tinypng.originSize";
16 |
17 | @implementation RTImageItem
18 | @synthesize optimized = _optimized;
19 |
20 | + (instancetype)itemWithPath:(NSString *)path
21 | {
22 | RTImageItem *item = [[RTImageItem alloc] initWithPath:path];
23 |
24 | return item;
25 | }
26 |
27 | - (instancetype)initWithPath:(NSString *)filePath
28 | {
29 | self = [super init];
30 | if (self) {
31 | self.imagePath = filePath;
32 | NSDictionary *attr = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath
33 | error:NULL];
34 | if (attr) {
35 | self.lastUpdateDate = [attr fileModificationDate];
36 |
37 | if (self.hasOptimized) {
38 | NSInteger size = 0;
39 | getxattr(self.imagePath.UTF8String, RT_IMAGE_ORIGIN_SIZE_KEY, &size, sizeof(size), 0, 0);
40 | self.imageSize = size;
41 | self.imageSizeOptimized = [attr fileSize];
42 |
43 | // If fail to get origin size, use optimized size instead
44 | if (!size)
45 | self.imageSize = self.imageSizeOptimized;
46 | _state = RTImageOptimizeStateOptimized;
47 | }
48 | else {
49 | self.imageSize = [attr fileSize];
50 | _state = RTImageOptimizeStateNormal;
51 | }
52 | }
53 | NSImage *image = [[NSImage alloc] initWithContentsOfFile:filePath];
54 | NSImageRep *rep = image.representations.firstObject;
55 | self.size = NSMakeSize(rep.pixelsWide, rep.pixelsHigh);
56 | }
57 | return self;
58 | }
59 |
60 | - (NSImage *)imageIcon
61 | {
62 | return [[NSWorkspace sharedWorkspace] iconForFile:self.imagePath];
63 | }
64 |
65 | - (BOOL)hasOptimized
66 | {
67 | if (self.imagePath) {
68 | BOOL optimized = NO;
69 | getxattr(self.imagePath.UTF8String, RT_IMAGE_OPTIMIZED_KEY, &optimized, sizeof(optimized), 0, 0);
70 | _optimized = optimized;
71 | }
72 | return _optimized;
73 | }
74 |
75 | - (void)refreshState
76 | {
77 | if (self.imagePath) {
78 | BOOL optimized = NO;
79 | setxattr(self.imagePath.UTF8String, RT_IMAGE_OPTIMIZED_KEY, &optimized, sizeof(optimized), 0, 0);
80 |
81 | NSDictionary *attr = [[NSFileManager defaultManager] attributesOfItemAtPath:self.imagePath
82 | error:NULL];
83 | if (attr) {
84 | self.lastUpdateDate = [attr fileModificationDate];
85 | self.imageSize = [attr fileSize];
86 | }
87 | }
88 | }
89 |
90 | - (void)setState:(RTImageOptimizeState)state
91 | {
92 | if (_state == state)
93 | return;
94 |
95 | _state = state;
96 | switch (_state) {
97 | case RTImageOptimizeStateOptimized:
98 | {
99 | if (self.imagePath) {
100 | BOOL optimized = YES;
101 | setxattr(self.imagePath.UTF8String, RT_IMAGE_OPTIMIZED_KEY, &optimized, sizeof(optimized), 0, 0);
102 | NSInteger size = self.imageSize;
103 | setxattr(self.imagePath.UTF8String, RT_IMAGE_ORIGIN_SIZE_KEY, &size, sizeof(size), 0, 0);
104 |
105 | NSDictionary *attr = [[NSFileManager defaultManager] attributesOfItemAtPath:self.imagePath
106 | error:NULL];
107 | if (attr) {
108 | self.lastUpdateDate = [attr fileModificationDate];
109 | self.imageSizeOptimized = [attr fileSize];
110 | }
111 | }
112 | }
113 | break;
114 |
115 | default:
116 | break;
117 | }
118 | }
119 |
120 | - (void)setImagePath:(NSString *)imagePath
121 | {
122 | _imagePath = imagePath;
123 | if (!_imageName) {
124 | self.imageName = imagePath.pathComponents.lastObject;
125 | }
126 | }
127 |
128 | @end
129 |
--------------------------------------------------------------------------------
/RTTinyPNGWebAPIPlugin/XcodeIDE.h:
--------------------------------------------------------------------------------
1 | //
2 | // XcodeIDE.h
3 | // RTImageAssets
4 | //
5 | // Created by ricky on 14-12-10.
6 | // Copyright (c) 2014年 rickytan. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class IDEWorkspace;
12 | @class IDEWorkspaceSettings;
13 | @class IDEWorkspaceSharedSettings;
14 | @class IDEWorkspaceUserSettings;
15 |
16 | @interface DVTFilePath : NSObject
17 | @property(readonly) DVTFilePath *symbolicLinkDestinationFilePath;
18 | @property(readonly) NSURL *fileReferenceURL;
19 | @property(readonly) NSDictionary *fileSystemAttributes;
20 | @property(readonly) NSDictionary *fileAttributes;
21 | @property(readonly) NSString *fileTypeAttribute;
22 | @property(readonly) NSArray *sortedDirectoryContents;
23 | @property(readonly) NSArray *directoryContents;
24 | @property(readonly) NSDate *modificationDate;
25 | @property(readonly) BOOL isExcludedFromBackup;
26 | @property(readonly) BOOL isExecutable;
27 | @property(readonly) BOOL isDeletable;
28 | @property(readonly) BOOL isWritable;
29 | @property(readonly) BOOL isReadable;
30 | @property(readonly) BOOL existsInFileSystem;
31 | @property(readonly) NSString *fileName;
32 | @property(readonly) NSURL *fileURL;
33 | @property(readonly) NSString *pathString;
34 | @property(readonly) DVTFilePath *volumeFilePath;
35 | @property(readonly) DVTFilePath *parentFilePath;
36 | @end
37 |
38 | @interface IDEWorkspaceWindowController : NSWindowController
39 | @end
40 |
41 | @interface IDEWorkspaceArena : NSObject
42 | @property(readonly) IDEWorkspace *workspace;
43 | @property(readonly) DVTFilePath *testResultsFolderPath;
44 | @property(readonly) DVTFilePath *logFolderPath;
45 | @property(readonly) DVTFilePath *indexPrecompiledHeadersFolderPath;
46 | @property(readonly) DVTFilePath *indexFolderPath;
47 | @property(readonly) DVTFilePath *precompiledHeadersFolderPath;
48 | @property(readonly) DVTFilePath *installingBuildFolderPath;
49 | @property(readonly) DVTFilePath *archivingBuildFolderPath;
50 | @property(readonly) DVTFilePath *buildIntermediatesFolderPath;
51 | @end
52 |
53 |
54 |
55 | @interface IDEWorkspace : NSObject
56 | @property BOOL isCleaningBuildFolder;
57 | @property(nonatomic) BOOL finishedLoading;
58 | @property(nonatomic) BOOL pendingFileReferencesAndContainers;
59 | @property BOOL initialContainerScanComplete;
60 | @property(retain, nonatomic) IDEWorkspaceArena *workspaceArena;
61 | @property(readonly) DVTFilePath *wrappedXcode3ProjectPath;
62 | @property(readonly) NSString *representingTitle;
63 | @property(readonly) DVTFilePath *representingFilePath;
64 | @property(retain, nonatomic) IDEWorkspaceSharedSettings *sharedSettings;
65 | @property(retain, nonatomic) IDEWorkspaceUserSettings *userSettings;
66 | @end
67 |
68 | @interface DVTLayoutView_ML : NSView
69 | @end
70 |
71 | @protocol IBViewDragDelegate
72 | - (BOOL)view:(id)arg1 performDragOperation:(id)arg2;
73 | - (BOOL)view:(id)arg1 prepareForDragOperation:(id)arg2;
74 | - (unsigned long long)view:(id)arg1 draggingEntered:(id)arg2;
75 | - (id)dragTypesForView:(id)arg1;
76 |
77 | @optional
78 | - (void)view:(id)arg1 draggingEnded:(id)arg2;
79 | - (void)view:(id)arg1 draggingExited:(id)arg2;
80 | - (void)view:(id)arg1 concludeDragOperation:(id)arg2;
81 | - (unsigned long long)view:(id)arg1 draggingUpdated:(id)arg2;
82 | @end
83 |
84 | @protocol IBICMultipartImageViewDelegate
85 | - (id)multipartImageView:(id)arg1 imageForImageRepIdentifier:(id)arg2;
86 | - (id)multipartImageView:(id)arg1 titleForImageRepIdentifier:(id)arg2;
87 | - (void)multipartImageViewWillLayout:(id)arg1;
88 | - (BOOL)multipartImageView:(id)arg1 interceptMouseUp:(id)arg2;
89 | - (BOOL)multipartImageView:(id)arg1 interceptMouseDragged:(id)arg2 withOriginalMouseDown:(id)arg3;
90 | - (BOOL)multipartImageView:(id)arg1 interceptMouseDown:(id)arg2;
91 | - (void)multipartImageView:(id)arg1 userDidEditTitle:(id)arg2;
92 | - (void)multipartImageView:(id)arg1 performDelete:(id)arg2;
93 | @end
94 |
95 | @interface IBICMultipartImageView : DVTLayoutView_ML
96 | @property(nonatomic) __weak id delegate;
97 | - (id)effectiveOuterBorderColor;
98 | - (id)effectiveTitleColor;
99 | @end
100 |
101 | @protocol IDEApplicationEventDelegate
102 | @optional
103 | - (BOOL)application:(id)arg1 shouldSendEvent:(id)arg2;
104 | @end
105 |
106 |
107 | @interface DVTApplication : NSApplication
108 | - (id)addActionMonitorWithHandlerBlock:(id)arg1;
109 | @end
110 |
111 |
112 | @interface IDEApplication : DVTApplication
113 | @property(retain) id eventDelegate; // @synthesize
114 | @end
115 |
116 |
117 | @interface IBICAbstractCatalogItem : NSObject
118 | @property(readonly, nonatomic) NSSet *children; // @synthesize children=_children;
119 | @property(readonly, nonatomic) IBICAbstractCatalogItem *parent; // @synthesize parent=_parent;
120 | @property(readonly, nonatomic) NSDate *manifestModificationDate; // @synthesize manifestModificationDate=_manifestModificationDate;
121 | @property(readonly, nonatomic) NSDate *modificatoinDate; // @synthesize modificatoinDate=_modificatoinDate;
122 | @property(copy, nonatomic) NSURL *absoluteFileURL; // @synthesize absoluteFileURL=_absoluteFileURL;
123 | @property(copy, nonatomic) NSString *explicitContainingDirectory; // @synthesize explicitContainingDirectory=_explicitContainingDirectory;
124 | @property(copy, nonatomic) NSString *fileName; // @synthesize fileName=_fileName;
125 | @property long long changeCount; // @synthesize changeCount=_changeCount;
126 | @property(readonly, nonatomic) NSArray *displayOrderedChildren;
127 | @property(readonly) NSString *absoluteManifestFilePath;
128 | @property(readonly) NSData *manifestFileData;
129 | @property(readonly) NSString *manifestFileName;
130 | @property(readonly, nonatomic) NSString *relativeFilePathFromRoot;
131 | @property(readonly, nonatomic) NSString *relativeIdentifierPath;
132 | @property(copy, nonatomic) NSString *absoluteFilePath;
133 | @property(readonly, nonatomic) BOOL canBeEmbeddedInFolder;
134 | @property(readonly, nonatomic) BOOL canHaveChildren;
135 | @property(readonly, nonatomic) NSString *identifier;
136 | @property(readonly, nonatomic) NSString *displayName;
137 | @end
138 |
139 | @interface IBICFolder : IBICAbstractCatalogItem
140 | - (BOOL)fileStructureSnapshotChildWouldMapToModelChild:(id)arg1;
141 | - (void)replaceChildrenFromFileSystemSnapshot:(id)arg1 results:(id)arg2;
142 | - (Class)itemClassForDirectoryExtension:(id)arg1;
143 | - (id)imageSetWithFileName:(id)arg1;
144 | - (id)imageSetWithName:(id)arg1;
145 | - (id)folderForFileName:(id)arg1;
146 | - (id)childWithFileName:(id)arg1;
147 | - (id)validatedFileNameForProposedDisplayName:(id)arg1;
148 | - (id)enclosingFolderIncludingReceiver;
149 | - (BOOL)canBeEmbeddedInFolder;
150 | - (BOOL)canHaveChildren;
151 | - (id)identifier;
152 | - (id)displayName;
153 | - (id)descriptionShortClassName;
154 |
155 | @end
156 |
157 | @interface IBICCatalog : IBICFolder
158 | - (void)replaceChildrenWithDiskContent:(id)arg1;
159 | - (BOOL)canBeEmbeddedInFolder;
160 | - (BOOL)canHaveChildren;
161 | - (id)displayName;
162 | - (id)allIconSets;
163 | - (id)allImageSets;
164 | - (id)catalog;
165 | - (id)descriptionShortClassName;
166 |
167 | @end
168 |
169 |
170 | @interface IBICMultipartImage : IBICAbstractCatalogItem
171 | {
172 | }
173 |
174 | + (Class)repIdentifierClass;
175 | + (Class)slotClass;
176 | + (id)pluralTypeNameForIssues;
177 | + (id)typeNameForIssues;
178 | + (id)keysThatImpactImageName;
179 | + (id)fileNameForImageSetName:(id)arg1;
180 | + (Class)imageRepClass;
181 | + (id)fileExtension;
182 | + (id)defaultInstanceForPlatforms:(id)arg1;
183 | + (id)defaultImageName;
184 | + (Class)classForDirectoryExtension:(id)arg1;
185 | + (id)multipartImageClassesInImportOrder;
186 | + (id)multipartImageClasses;
187 | + (id)allocWithZone:(struct _NSZone *)arg1;
188 | - (void)populateMutatorsToAddRequiredChildCounterparts:(id)arg1;
189 | - (id)suggestedFileNameForImageRepInSlot:(id)arg1;
190 | - (void)populateIssues:(id)arg1 context:(id)arg2;
191 | - (void)assertChildIsLegalToAdd:(id)arg1;
192 | - (id)pluralTypeNameForIssues;
193 | - (id)typeNameForIssues;
194 | - (id)descriptionShortClassName;
195 | - (id)imageRepForImageRepIdentifier:(id)arg1;
196 | - (id)imageRepForSlot:(id)arg1;
197 | - (id)imageRepForIdentifier:(id)arg1;
198 | - (id)childForIdentifier:(id)arg1;
199 | - (BOOL)canBeEmbeddedInFolder;
200 | - (BOOL)canHaveChildren;
201 | - (id)identifier;
202 | - (id)displayName;
203 | @property(copy, nonatomic) NSString *imageName;
204 | - (id)validatedFileNameForProposedImageName:(id)arg1;
205 | - (id)enclosingMultipartImageIncludingReceiver;
206 |
207 | @end
208 |
209 |
210 | @interface IBICMappedMultipartImage : IBICMultipartImage
211 | {
212 | }
213 |
214 | + (id)orderedSlotComponentClasses;
215 | + (double)currentContentsJSONVersionNumber;
216 | + (double)latestUnderstoodContentsJSONVersionNumber;
217 | + (double)earliestUnderstoodContentsJSONVersionNumber;
218 | - (id)contentsDictionary;
219 | - (void)populateContentsJSONImageEntry:(id)arg1 forImageRep:(id)arg2;
220 | - (BOOL)shouldIncludeImageRepInContentsJSON:(id)arg1;
221 | - (void)replaceChildrenFromFileSystemSnapshot:(id)arg1 results:(id)arg2;
222 | - (id)imageRepsByMergingLooseFilesContentFromSnapshot:(id)arg1 withJSONReferencedContent:(id)arg2 results:(id)arg3;
223 | - (id)imageRepsFromContentsJSONImageEntries:(id)arg1 results:(id)arg2;
224 | - (id)imageRepFromImageEntry:(id)arg1 results:(id)arg2;
225 | - (id)validatedContentsJSONImageEntriesFromSnapshot:(id)arg1 results:(id)arg2;
226 | - (id)readContentsJSONFromSnapshot:(id)arg1 results:(id)arg2;
227 | - (BOOL)fileStructureSnapshotChildWouldMapToModelChild:(id)arg1;
228 | - (id)manifestFileData;
229 | - (id)manifestFileName;
230 | - (id)orderedSlotComponentClasses;
231 |
232 | @end
233 |
234 |
235 |
236 | @interface IBICAppIconSet : IBICMappedMultipartImage
237 | {
238 | BOOL _preRendered;
239 | }
240 |
241 | + (id)pluralTypeNameForIssues;
242 | + (id)typeNameForIssues;
243 | + (id)fileExtension;
244 | + (id)defaultInstanceForPlatforms:(id)arg1;
245 | + (id)defaultImageName;
246 | + (double)currentContentsJSONVersionNumber;
247 | + (double)latestUnderstoodContentsJSONVersionNumber;
248 | + (double)earliestUnderstoodContentsJSONVersionNumber;
249 | + (Class)imageRepClass;
250 | @property(nonatomic, getter=isPreRendered) BOOL preRendered; // @synthesize preRendered=_preRendered;
251 | - (id)descriptionShortClassName;
252 | - (id)contentsDictionary;
253 | - (id)readContentsJSONFromSnapshot:(id)arg1 results:(id)arg2;
254 | - (id)suggestedFileNameForImageRepInSlot:(id)arg1;
255 | - (id)childForIdentifier:(id)arg1;
256 | - (id)imageRepForIdentifier:(id)arg1;
257 | - (id)imageRepForImageRepIdentifier:(id)arg1;
258 | - (id)imageRepForSlot:(id)arg1;
259 |
260 | @end
261 |
262 | @interface IBICLaunchImageSet : IBICMappedMultipartImage
263 | {
264 | }
265 |
266 | + (id)pluralTypeNameForIssues;
267 | + (id)typeNameForIssues;
268 | + (id)fileExtension;
269 | + (id)defaultInstanceForPlatforms:(id)arg1;
270 | + (id)defaultImageName;
271 | + (double)currentContentsJSONVersionNumber;
272 | + (double)latestUnderstoodContentsJSONVersionNumber;
273 | + (double)earliestUnderstoodContentsJSONVersionNumber;
274 | + (Class)imageRepClass;
275 | - (id)descriptionShortClassName;
276 | - (id)validatedContentsJSONImageEntriesFromSnapshot:(id)arg1 results:(id)arg2;
277 | - (id)suggestedFileNameForImageRepInSlot:(id)arg1;
278 | - (id)childForIdentifier:(id)arg1;
279 | - (id)imageRepForIdentifier:(id)arg1;
280 | - (id)imageRepForImageRepIdentifier:(id)arg1;
281 | - (id)imageRepForSlot:(id)arg1;
282 |
283 | @end
284 |
285 |
286 | @interface IBICMultipartImageRepSlot : NSObject
287 | {
288 | NSDictionary *_componentsByClass;
289 | }
290 |
291 | + (id)orderedComponentClasses;
292 | + (id)defaultSlot;
293 | + (id)emptySlot;
294 | + (id)allocWithZone:(struct _NSZone *)arg1;
295 | + (id)slotWithComponents:(id)arg1;
296 | + (id)slotWithComponents:(id *)arg1 count:(unsigned long long)arg2;
297 | - (id)requiredPointSize;
298 | - (id)requiredPixelSize;
299 | - (id)suggestedRepNameForMultipartImageSetName:(id)arg1;
300 | - (id)detailAreaKey;
301 | - (id)requiredFileName;
302 | - (id)displayName;
303 | - (id)description;
304 | - (id)stringRepresentation;
305 | - (id)shortDisplayNameDefiningItem;
306 | - (unsigned long long)hash;
307 | - (BOOL)isEqual:(id)arg1;
308 | - (BOOL)isEqualToMultipartImageRepSlot:(id)arg1;
309 | - (long long)compareDisplayOrder:(id)arg1;
310 | - (id)slotComponentsForClasses:(id)arg1;
311 | - (id)slotComponentForClass:(Class)arg1;
312 | - (void)enumerateOrderedSlotComponentsAndValues:(id)arg1;
313 | - (id)initWithComponents:(id)arg1;
314 | - (void)captureComponents;
315 |
316 | @end
317 |
318 |
319 |
320 | @interface IBICMultipartImageRep : IBICAbstractCatalogItem
321 | + (id)keysThatImpactDisplayOrder;
322 | + (id)validSourceImageExtensions;
323 | + (id)imageRepWithRepIdentifier:(id)arg1;
324 | + (id)imageRepWithSlot:(id)arg1 fileName:(id)arg2 andUnassigned:(BOOL)arg3;
325 | + (Class)slotClass;
326 | + (Class)multiplartImageClass;
327 | + (Class)repIdentifierClass;
328 | + (id)allocWithZone:(struct _NSZone *)arg1;
329 | @property(copy, nonatomic) NSData *imageData; // @synthesize imageData=_imageData;
330 | @property(copy, nonatomic) IBICMultipartImageRepSlot *slot; // @synthesize slot=_slot;
331 | @property(nonatomic, getter=isUnassigned) BOOL unassigned; // @synthesize unassigned=_unassigned;
332 | - (id)suggestedFileName;
333 | @property(readonly) NSValue *imageDataPixelSize;
334 | @property(readonly) NSValue *requiredPointSize;
335 | @property(readonly) NSValue *requiredPixelSize;
336 | - (void)populateIssues:(id)arg1 context:(id)arg2;
337 | - (BOOL)updateModificationDatesWithMutationResult:(id)arg1;
338 | - (void)setImageDataFromPath:(id)arg1;
339 | - (BOOL)fileStructureSnapshotChildWouldMapToModelChild:(id)arg1;
340 | - (long long)compareDisplayOrder:(id)arg1;
341 | - (void)replaceChildrenFromFileSystemSnapshot:(id)arg1 results:(id)arg2;
342 | - (id)fileWrapperRepresentationWithOptions:(unsigned long long)arg1;
343 | - (id)parent;
344 | - (void)enumerateDescriptionAttributeComponents:(id)arg1;
345 | - (BOOL)isBrokenFileReference;
346 | - (id)identifier;
347 | - (id)structuredIdentifier;
348 | - (BOOL)canBeEmbeddedInFolder;
349 | - (BOOL)canHaveChildren;
350 | - (id)descriptionShortClassName;
351 | - (BOOL)isMinimallyFitForCompiling;
352 | @property(readonly) NSString *shortDisplayName;
353 | - (id)displayName;
354 | - (id)initWithSlot:(id)arg1;
355 |
356 | @end
357 |
358 |
359 | @interface IBICIconSetRep : IBICMultipartImageRep
360 | {
361 | }
362 |
363 | + (id)outputImageExtension;
364 | + (Class)multiplartImageClass;
365 | + (Class)repIdentifierClass;
366 | + (id)imageRepWithSlot:(id)arg1 fileName:(id)arg2 andUnassigned:(BOOL)arg3;
367 | + (id)imageRepWithRepIdentifier:(id)arg1;
368 | - (BOOL)isMinimallyFitForCompiling;
369 | - (BOOL)isImageDataSizedProperly;
370 | - (id)descriptionShortClassName;
371 | - (void)setSlot:(id)arg1;
372 | - (id)slot;
373 | - (id)structuredIdentifier;
374 | - (id)parent;
375 | - (id)initWithSlot:(id)arg1;
376 |
377 | @end
378 |
379 |
380 | @interface IBICCatalogSynchronizer : NSObject
381 | + (id)synchronizerForCatalogAtPath:(NSString *)path;
382 | - (void)preventSynchronizationDuring:(id)arg1;
383 | - (BOOL)isSynchronizationEnabled;
384 | - (void)enableSynchronization;
385 | - (void)disableSynchronization;
386 | - (void)applyMutationToModelAndScheduleForDisk:(id)arg1;
387 | - (id)replaceCatalogWithContentsOfPathWhileItIsKnowThatSyncOperationsAreNotInflightAndAreDisabled:(id)arg1;
388 | - (id)replaceCatalogWithContentsOfPath:(id)arg1;
389 | - (void)validateBatchedChanges:(id)arg1;
390 | - (void)validateChangesToDiskIfNeeded;
391 | - (void)validateChangesFromDiskIfNeeded;
392 | - (void)resetContentFromDisk;
393 | @property(readonly) IBICCatalog *catalog;
394 | - (void)primitiveInvalidate;
395 | - (id)init;
396 | - (id)initByTakingOwnershipsOfCatalog:(id)arg1;
397 |
398 | // Remaining properties
399 | @property(readonly, nonatomic, getter=isValid) BOOL valid;
400 | @end
401 |
402 | @interface DTAssetiLifeDelegate : NSObject
403 | @end
404 |
--------------------------------------------------------------------------------
/RTTinyPNGWebAPIPlugin.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 6D30B96A1C633158002B419A /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6D30B9691C633158002B419A /* AppKit.framework */; };
11 | 6D30B96C1C633158002B419A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6D30B96B1C633158002B419A /* Foundation.framework */; };
12 | 6D30B9701C633158002B419A /* RTTinyPNGWebAPIPlugin.xcscheme in Resources */ = {isa = PBXBuildFile; fileRef = 6D30B96F1C633158002B419A /* RTTinyPNGWebAPIPlugin.xcscheme */; };
13 | 6D30B9731C633158002B419A /* RTTinyPNGWebAPIPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D30B9721C633158002B419A /* RTTinyPNGWebAPIPlugin.m */; };
14 | 6D30B9761C633158002B419A /* NSObject_Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D30B9751C633158002B419A /* NSObject_Extension.m */; };
15 | 6D30B9801C633240002B419A /* RTImageController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D30B97E1C633240002B419A /* RTImageController.m */; };
16 | 6D30B9811C633240002B419A /* RTImageController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D30B97F1C633240002B419A /* RTImageController.xib */; };
17 | 6D30B9851C633F8F002B419A /* RTWorkspace.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D30B9831C633F8F002B419A /* RTWorkspace.m */; };
18 | 6D30B9881C63426A002B419A /* RTImageItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D30B9871C63426A002B419A /* RTImageItem.m */; };
19 | 6DF5F7C61C6447680095CB95 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6DF5F7C51C6447680095CB95 /* QuartzCore.framework */; };
20 | /* End PBXBuildFile section */
21 |
22 | /* Begin PBXFileReference section */
23 | 6D30B9661C633158002B419A /* RTTinyPNGWebAPIPlugin.xcplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RTTinyPNGWebAPIPlugin.xcplugin; sourceTree = BUILT_PRODUCTS_DIR; };
24 | 6D30B9691C633158002B419A /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; };
25 | 6D30B96B1C633158002B419A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; };
26 | 6D30B96F1C633158002B419A /* RTTinyPNGWebAPIPlugin.xcscheme */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = RTTinyPNGWebAPIPlugin.xcscheme; path = RTTinyPNGWebAPIPlugin.xcodeproj/xcshareddata/xcschemes/RTTinyPNGWebAPIPlugin.xcscheme; sourceTree = SOURCE_ROOT; };
27 | 6D30B9711C633158002B419A /* RTTinyPNGWebAPIPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RTTinyPNGWebAPIPlugin.h; sourceTree = ""; };
28 | 6D30B9721C633158002B419A /* RTTinyPNGWebAPIPlugin.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RTTinyPNGWebAPIPlugin.m; sourceTree = ""; };
29 | 6D30B9741C633158002B419A /* NSObject_Extension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSObject_Extension.h; sourceTree = ""; };
30 | 6D30B9751C633158002B419A /* NSObject_Extension.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSObject_Extension.m; sourceTree = ""; };
31 | 6D30B9771C633158002B419A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
32 | 6D30B97D1C633240002B419A /* RTImageController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RTImageController.h; sourceTree = ""; };
33 | 6D30B97E1C633240002B419A /* RTImageController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RTImageController.m; sourceTree = ""; };
34 | 6D30B97F1C633240002B419A /* RTImageController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RTImageController.xib; sourceTree = ""; };
35 | 6D30B9821C633F8F002B419A /* RTWorkspace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RTWorkspace.h; sourceTree = ""; };
36 | 6D30B9831C633F8F002B419A /* RTWorkspace.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RTWorkspace.m; sourceTree = ""; };
37 | 6D30B9841C633F8F002B419A /* XcodeIDE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XcodeIDE.h; sourceTree = ""; };
38 | 6D30B9861C63426A002B419A /* RTImageItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RTImageItem.h; sourceTree = ""; };
39 | 6D30B9871C63426A002B419A /* RTImageItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RTImageItem.m; sourceTree = ""; };
40 | 6DF5F7C51C6447680095CB95 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
41 | /* End PBXFileReference section */
42 |
43 | /* Begin PBXFrameworksBuildPhase section */
44 | 6D30B9641C633158002B419A /* Frameworks */ = {
45 | isa = PBXFrameworksBuildPhase;
46 | buildActionMask = 2147483647;
47 | files = (
48 | 6DF5F7C61C6447680095CB95 /* QuartzCore.framework in Frameworks */,
49 | 6D30B96A1C633158002B419A /* AppKit.framework in Frameworks */,
50 | 6D30B96C1C633158002B419A /* Foundation.framework in Frameworks */,
51 | );
52 | runOnlyForDeploymentPostprocessing = 0;
53 | };
54 | /* End PBXFrameworksBuildPhase section */
55 |
56 | /* Begin PBXGroup section */
57 | 6D30B95D1C633158002B419A = {
58 | isa = PBXGroup;
59 | children = (
60 | 6D30B96D1C633158002B419A /* RTTinyPNGWebAPIPlugin */,
61 | 6D30B9681C633158002B419A /* Frameworks */,
62 | 6D30B9671C633158002B419A /* Products */,
63 | );
64 | sourceTree = "";
65 | };
66 | 6D30B9671C633158002B419A /* Products */ = {
67 | isa = PBXGroup;
68 | children = (
69 | 6D30B9661C633158002B419A /* RTTinyPNGWebAPIPlugin.xcplugin */,
70 | );
71 | name = Products;
72 | sourceTree = "";
73 | };
74 | 6D30B9681C633158002B419A /* Frameworks */ = {
75 | isa = PBXGroup;
76 | children = (
77 | 6DF5F7C51C6447680095CB95 /* QuartzCore.framework */,
78 | 6D30B9691C633158002B419A /* AppKit.framework */,
79 | 6D30B96B1C633158002B419A /* Foundation.framework */,
80 | );
81 | name = Frameworks;
82 | sourceTree = "";
83 | };
84 | 6D30B96D1C633158002B419A /* RTTinyPNGWebAPIPlugin */ = {
85 | isa = PBXGroup;
86 | children = (
87 | 6D30B9741C633158002B419A /* NSObject_Extension.h */,
88 | 6D30B9751C633158002B419A /* NSObject_Extension.m */,
89 | 6D30B97D1C633240002B419A /* RTImageController.h */,
90 | 6D30B97E1C633240002B419A /* RTImageController.m */,
91 | 6D30B97F1C633240002B419A /* RTImageController.xib */,
92 | 6D30B9861C63426A002B419A /* RTImageItem.h */,
93 | 6D30B9871C63426A002B419A /* RTImageItem.m */,
94 | 6D30B9711C633158002B419A /* RTTinyPNGWebAPIPlugin.h */,
95 | 6D30B9721C633158002B419A /* RTTinyPNGWebAPIPlugin.m */,
96 | 6D30B9821C633F8F002B419A /* RTWorkspace.h */,
97 | 6D30B9831C633F8F002B419A /* RTWorkspace.m */,
98 | 6D30B96E1C633158002B419A /* Supporting Files */,
99 | 6D30B9841C633F8F002B419A /* XcodeIDE.h */,
100 | );
101 | path = RTTinyPNGWebAPIPlugin;
102 | sourceTree = "";
103 | };
104 | 6D30B96E1C633158002B419A /* Supporting Files */ = {
105 | isa = PBXGroup;
106 | children = (
107 | 6D30B9771C633158002B419A /* Info.plist */,
108 | 6D30B96F1C633158002B419A /* RTTinyPNGWebAPIPlugin.xcscheme */,
109 | );
110 | name = "Supporting Files";
111 | sourceTree = "";
112 | };
113 | /* End PBXGroup section */
114 |
115 | /* Begin PBXNativeTarget section */
116 | 6D30B9651C633158002B419A /* RTTinyPNGWebAPIPlugin */ = {
117 | isa = PBXNativeTarget;
118 | buildConfigurationList = 6D30B97A1C633158002B419A /* Build configuration list for PBXNativeTarget "RTTinyPNGWebAPIPlugin" */;
119 | buildPhases = (
120 | 6D30B9621C633158002B419A /* Sources */,
121 | 6D30B9631C633158002B419A /* Resources */,
122 | 6D30B9641C633158002B419A /* Frameworks */,
123 | );
124 | buildRules = (
125 | );
126 | dependencies = (
127 | );
128 | name = RTTinyPNGWebAPIPlugin;
129 | productName = RTTinyPNGWebAPIPlugin;
130 | productReference = 6D30B9661C633158002B419A /* RTTinyPNGWebAPIPlugin.xcplugin */;
131 | productType = "com.apple.product-type.bundle";
132 | };
133 | /* End PBXNativeTarget section */
134 |
135 | /* Begin PBXProject section */
136 | 6D30B95E1C633158002B419A /* Project object */ = {
137 | isa = PBXProject;
138 | attributes = {
139 | LastUpgradeCheck = 0720;
140 | ORGANIZATIONNAME = Shiqu;
141 | TargetAttributes = {
142 | 6D30B9651C633158002B419A = {
143 | CreatedOnToolsVersion = 7.2;
144 | };
145 | };
146 | };
147 | buildConfigurationList = 6D30B9611C633158002B419A /* Build configuration list for PBXProject "RTTinyPNGWebAPIPlugin" */;
148 | compatibilityVersion = "Xcode 3.2";
149 | developmentRegion = English;
150 | hasScannedForEncodings = 0;
151 | knownRegions = (
152 | en,
153 | );
154 | mainGroup = 6D30B95D1C633158002B419A;
155 | productRefGroup = 6D30B9671C633158002B419A /* Products */;
156 | projectDirPath = "";
157 | projectRoot = "";
158 | targets = (
159 | 6D30B9651C633158002B419A /* RTTinyPNGWebAPIPlugin */,
160 | );
161 | };
162 | /* End PBXProject section */
163 |
164 | /* Begin PBXResourcesBuildPhase section */
165 | 6D30B9631C633158002B419A /* Resources */ = {
166 | isa = PBXResourcesBuildPhase;
167 | buildActionMask = 2147483647;
168 | files = (
169 | 6D30B9701C633158002B419A /* RTTinyPNGWebAPIPlugin.xcscheme in Resources */,
170 | 6D30B9811C633240002B419A /* RTImageController.xib in Resources */,
171 | );
172 | runOnlyForDeploymentPostprocessing = 0;
173 | };
174 | /* End PBXResourcesBuildPhase section */
175 |
176 | /* Begin PBXSourcesBuildPhase section */
177 | 6D30B9621C633158002B419A /* Sources */ = {
178 | isa = PBXSourcesBuildPhase;
179 | buildActionMask = 2147483647;
180 | files = (
181 | 6D30B9731C633158002B419A /* RTTinyPNGWebAPIPlugin.m in Sources */,
182 | 6D30B9851C633F8F002B419A /* RTWorkspace.m in Sources */,
183 | 6D30B9881C63426A002B419A /* RTImageItem.m in Sources */,
184 | 6D30B9801C633240002B419A /* RTImageController.m in Sources */,
185 | 6D30B9761C633158002B419A /* NSObject_Extension.m in Sources */,
186 | );
187 | runOnlyForDeploymentPostprocessing = 0;
188 | };
189 | /* End PBXSourcesBuildPhase section */
190 |
191 | /* Begin XCBuildConfiguration section */
192 | 6D30B9781C633158002B419A /* Debug */ = {
193 | isa = XCBuildConfiguration;
194 | buildSettings = {
195 | ALWAYS_SEARCH_USER_PATHS = NO;
196 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
197 | CLANG_CXX_LIBRARY = "libc++";
198 | CLANG_ENABLE_MODULES = YES;
199 | CLANG_ENABLE_OBJC_ARC = YES;
200 | CLANG_WARN_BOOL_CONVERSION = YES;
201 | CLANG_WARN_CONSTANT_CONVERSION = YES;
202 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
203 | CLANG_WARN_EMPTY_BODY = YES;
204 | CLANG_WARN_ENUM_CONVERSION = YES;
205 | CLANG_WARN_INT_CONVERSION = YES;
206 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
207 | CLANG_WARN_UNREACHABLE_CODE = YES;
208 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
209 | COPY_PHASE_STRIP = NO;
210 | DEBUG_INFORMATION_FORMAT = dwarf;
211 | ENABLE_STRICT_OBJC_MSGSEND = YES;
212 | ENABLE_TESTABILITY = YES;
213 | GCC_C_LANGUAGE_STANDARD = gnu99;
214 | GCC_DYNAMIC_NO_PIC = NO;
215 | GCC_NO_COMMON_BLOCKS = YES;
216 | GCC_OPTIMIZATION_LEVEL = 0;
217 | GCC_PREPROCESSOR_DEFINITIONS = (
218 | "DEBUG=1",
219 | "$(inherited)",
220 | );
221 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
222 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
223 | GCC_WARN_UNDECLARED_SELECTOR = YES;
224 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
225 | GCC_WARN_UNUSED_FUNCTION = YES;
226 | GCC_WARN_UNUSED_VARIABLE = YES;
227 | MTL_ENABLE_DEBUG_INFO = YES;
228 | ONLY_ACTIVE_ARCH = YES;
229 | };
230 | name = Debug;
231 | };
232 | 6D30B9791C633158002B419A /* Release */ = {
233 | isa = XCBuildConfiguration;
234 | buildSettings = {
235 | ALWAYS_SEARCH_USER_PATHS = NO;
236 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
237 | CLANG_CXX_LIBRARY = "libc++";
238 | CLANG_ENABLE_MODULES = YES;
239 | CLANG_ENABLE_OBJC_ARC = YES;
240 | CLANG_WARN_BOOL_CONVERSION = YES;
241 | CLANG_WARN_CONSTANT_CONVERSION = YES;
242 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
243 | CLANG_WARN_EMPTY_BODY = YES;
244 | CLANG_WARN_ENUM_CONVERSION = YES;
245 | CLANG_WARN_INT_CONVERSION = YES;
246 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
247 | CLANG_WARN_UNREACHABLE_CODE = YES;
248 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
249 | COPY_PHASE_STRIP = NO;
250 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
251 | ENABLE_NS_ASSERTIONS = NO;
252 | ENABLE_STRICT_OBJC_MSGSEND = YES;
253 | GCC_C_LANGUAGE_STANDARD = gnu99;
254 | GCC_NO_COMMON_BLOCKS = YES;
255 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
256 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
257 | GCC_WARN_UNDECLARED_SELECTOR = YES;
258 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
259 | GCC_WARN_UNUSED_FUNCTION = YES;
260 | GCC_WARN_UNUSED_VARIABLE = YES;
261 | MTL_ENABLE_DEBUG_INFO = NO;
262 | };
263 | name = Release;
264 | };
265 | 6D30B97B1C633158002B419A /* Debug */ = {
266 | isa = XCBuildConfiguration;
267 | buildSettings = {
268 | COMBINE_HIDPI_IMAGES = YES;
269 | DEPLOYMENT_LOCATION = YES;
270 | DSTROOT = "$(HOME)";
271 | INFOPLIST_FILE = RTTinyPNGWebAPIPlugin/Info.plist;
272 | INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins";
273 | MACOSX_DEPLOYMENT_TARGET = 10.9;
274 | PRODUCT_BUNDLE_IDENTIFIER = cn.rickytan.RTTinyPNGWebAPIPlugin;
275 | PRODUCT_NAME = "$(TARGET_NAME)";
276 | WRAPPER_EXTENSION = xcplugin;
277 | };
278 | name = Debug;
279 | };
280 | 6D30B97C1C633158002B419A /* Release */ = {
281 | isa = XCBuildConfiguration;
282 | buildSettings = {
283 | COMBINE_HIDPI_IMAGES = YES;
284 | DEPLOYMENT_LOCATION = YES;
285 | DSTROOT = "$(HOME)";
286 | INFOPLIST_FILE = RTTinyPNGWebAPIPlugin/Info.plist;
287 | INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins";
288 | MACOSX_DEPLOYMENT_TARGET = 10.9;
289 | PRODUCT_BUNDLE_IDENTIFIER = cn.rickytan.RTTinyPNGWebAPIPlugin;
290 | PRODUCT_NAME = "$(TARGET_NAME)";
291 | WRAPPER_EXTENSION = xcplugin;
292 | };
293 | name = Release;
294 | };
295 | /* End XCBuildConfiguration section */
296 |
297 | /* Begin XCConfigurationList section */
298 | 6D30B9611C633158002B419A /* Build configuration list for PBXProject "RTTinyPNGWebAPIPlugin" */ = {
299 | isa = XCConfigurationList;
300 | buildConfigurations = (
301 | 6D30B9781C633158002B419A /* Debug */,
302 | 6D30B9791C633158002B419A /* Release */,
303 | );
304 | defaultConfigurationIsVisible = 0;
305 | defaultConfigurationName = Release;
306 | };
307 | 6D30B97A1C633158002B419A /* Build configuration list for PBXNativeTarget "RTTinyPNGWebAPIPlugin" */ = {
308 | isa = XCConfigurationList;
309 | buildConfigurations = (
310 | 6D30B97B1C633158002B419A /* Debug */,
311 | 6D30B97C1C633158002B419A /* Release */,
312 | );
313 | defaultConfigurationIsVisible = 0;
314 | defaultConfigurationName = Release;
315 | };
316 | /* End XCConfigurationList section */
317 | };
318 | rootObject = 6D30B95E1C633158002B419A /* Project object */;
319 | }
320 |
--------------------------------------------------------------------------------
/RTTinyPNGWebAPIPlugin/RTImageController.m:
--------------------------------------------------------------------------------
1 | //
2 | // RTImageController.m
3 | // RTTinyPNGWebAPIPlugin
4 | //
5 | // Created by benfen on 16/2/4.
6 | // Copyright © 2016年 Shiqu. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import "RTImageController.h"
12 |
13 | #import "RTWorkspace.h"
14 | #import "RTImageItem.h"
15 |
16 | @interface RTHeaderCell : NSTableHeaderCell
17 | @property (strong) NSButtonCell *checkBox;
18 | @end
19 |
20 | @implementation RTHeaderCell
21 |
22 | - (instancetype)init
23 | {
24 | self = [super init];
25 | if (self) {
26 | self.checkBox = [[NSButtonCell alloc] init];
27 | self.checkBox.title = @"";
28 | self.checkBox.controlSize = NSRegularControlSize;
29 | self.checkBox.buttonType = NSSwitchButton;
30 | }
31 | return self;
32 | }
33 |
34 | - (void)drawInteriorWithFrame:(NSRect)cellFrame
35 | inView:(NSView *)controlView
36 | {
37 | NSSize size = [self.checkBox cellSize];
38 |
39 | NSRect rect = NSMakeRect(cellFrame.origin.x + (cellFrame.size.width - size.width) / 2,
40 | cellFrame.origin.y + (cellFrame.size.height - size.height) / 2,
41 | size.width, size.height);
42 | [self.checkBox drawInteriorWithFrame:rect
43 | inView:controlView];
44 | }
45 |
46 | @end
47 |
48 | @interface RTImageController ()
49 | @property (weak) IBOutlet NSTableView *tableView;
50 | @property (weak) IBOutlet NSTextField *apiKeyField;
51 | @property (weak) IBOutlet NSProgressIndicator *progressIndicator;
52 | @property (weak) IBOutlet NSButton *startButton;
53 | @property (weak) IBOutlet NSButton *cancelButton;
54 | @property (weak) IBOutlet NSPopUpButton *concurrencyButton;
55 | @property (weak) IBOutlet NSTextField *statsLabel;
56 |
57 | @property (strong) RTHeaderCell *selectAllCell;
58 |
59 | @property (nonatomic, strong) NSMutableArray *imageItems;
60 | @property (assign) NSUInteger totalBytes, optimizedTotalBytes;
61 | @property (nonatomic, assign, getter=isLoading) BOOL loading;
62 | @property (assign, getter=isProcessing) BOOL processing;
63 | @end
64 |
65 | @implementation RTImageController
66 | @synthesize processing = _processing;
67 |
68 | static NSString *const TINY_PNG_HOST = @"https://api.tinify.com/shrink";
69 | static void *observingContext = &observingContext;
70 | static NSOperationQueue *RTImageCompressingQueue() {
71 | static NSOperationQueue * _theQueue = nil;
72 | static dispatch_once_t onceToken;
73 | dispatch_once(&onceToken, ^{
74 | _theQueue = [[NSOperationQueue alloc] init];
75 | _theQueue.maxConcurrentOperationCount = 2;
76 | });
77 | return _theQueue;
78 | }
79 |
80 | - (void)windowDidLoad {
81 | [super windowDidLoad];
82 |
83 | // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
84 |
85 | {
86 | RTHeaderCell *cell = [[RTHeaderCell alloc] init];
87 | self.selectAllCell = cell;
88 | ((NSTableColumn *)self.tableView.tableColumns.firstObject).headerCell = cell;
89 | }
90 |
91 | [self reloadImages];
92 | }
93 |
94 | - (void)showWindow:(id)sender {
95 | [super showWindow:sender];
96 |
97 | if (self.isWindowLoaded)
98 | [self reloadImages];
99 | }
100 |
101 | - (void)observeValueForKeyPath:(NSString *)keyPath
102 | ofObject:(id)object
103 | change:(NSDictionary *)change
104 | context:(void *)context
105 | {
106 | if (context == observingContext) {
107 | if ([@"operationCount" isEqualToString:keyPath]) {
108 | if (RTImageCompressingQueue().operationCount == 0) {
109 | self.processing = NO;
110 | }
111 | }
112 | }
113 | }
114 |
115 | #pragma mark - Actions
116 |
117 | - (void)onToggleSelectionAll:(id)sender
118 | {
119 | if (self.selectAllCell.checkBox.state == NSOffState) {
120 | [self.imageItems enumerateObjectsUsingBlock:^(RTImageItem *item, NSUInteger idx, BOOL *stop) {
121 | item.selected = YES;
122 | }];
123 | self.selectAllCell.checkBox.state = NSOnState;
124 | }
125 | else if (self.selectAllCell.checkBox.state == NSOnState) {
126 | [self.imageItems enumerateObjectsUsingBlock:^(RTImageItem *item, NSUInteger idx, BOOL *stop) {
127 | item.selected = NO;
128 | }];
129 | self.selectAllCell.checkBox.state = NSOffState;
130 | }
131 | // Redraw the cell
132 | [self.tableView.headerView setNeedsDisplayInRect:[self.tableView.headerView headerRectOfColumn:0]];
133 |
134 | [self.tableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.imageItems.count)]
135 | columnIndexes:[NSIndexSet indexSetWithIndex:[self.tableView columnWithIdentifier:@"Selection"]]];
136 |
137 | }
138 |
139 | - (IBAction)onMarkSelected:(id)sender
140 | {
141 | [self.tableView.selectedRowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
142 | ((RTImageItem *)self.imageItems[idx]).selected = YES;
143 | }];
144 | [self.tableView reloadDataForRowIndexes:self.tableView.selectedRowIndexes
145 | columnIndexes:[NSIndexSet indexSetWithIndex:[self.tableView columnWithIdentifier:@"Selection"]]];
146 | [self.tableView deselectAll:sender];
147 | }
148 |
149 | - (IBAction)onMarkDeselected:(id)sender
150 | {
151 | [self.tableView.selectedRowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
152 | ((RTImageItem *)self.imageItems[idx]).selected = NO;
153 | }];
154 | [self.tableView reloadDataForRowIndexes:self.tableView.selectedRowIndexes
155 | columnIndexes:[NSIndexSet indexSetWithIndex:[self.tableView columnWithIdentifier:@"Selection"]]];
156 | [self.tableView deselectAll:sender];
157 | }
158 |
159 | - (IBAction)onViewInFinder:(id)sender
160 | {
161 | NSInteger row = [self.tableView rowForView:sender];
162 | if (row >= 0) {
163 | RTImageItem *item = self.imageItems[row];
164 | [[NSWorkspace sharedWorkspace] selectFile:item.imagePath
165 | inFileViewerRootedAtPath:item.imagePath.stringByDeletingLastPathComponent];
166 | }
167 | }
168 |
169 | - (IBAction)onClickHeader:(NSTableView *)tableView {
170 | NSInteger rol = tableView.clickedRow;
171 | NSInteger column = tableView.clickedColumn;
172 | if (rol < 0 && column == 0) {
173 | [self onToggleSelectionAll:self];
174 | }
175 | }
176 |
177 | - (IBAction)onDoubleClickRow:(NSTableView *)tableView {
178 | if ([tableView selectedRow] >= 0) {
179 | RTImageItem *item = self.imageItems[tableView.selectedRow];
180 | item.selected = !item.isSelected;
181 | [self.tableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:tableView.selectedRow]
182 | columnIndexes:[NSIndexSet indexSetWithIndex:0]];
183 | }
184 | }
185 |
186 | - (IBAction)onConcurrencyChanged:(id)sender {
187 | NSInteger value = [self.concurrencyButton.selectedItem.title integerValue];
188 | RTImageCompressingQueue().maxConcurrentOperationCount = value;
189 | }
190 |
191 | - (IBAction)onSelect:(NSButton *)checkBox {
192 | NSInteger row = [self.tableView rowForView:checkBox];
193 | if (row >= 0) {
194 | RTImageItem *item = self.imageItems[row];
195 | item.selected = !item.isSelected;
196 | }
197 | }
198 |
199 | - (IBAction)onShowHelp:(id)sender
200 | {
201 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://tinypng.com/developers"]];
202 | }
203 |
204 | - (IBAction)onStart:(id)sender {
205 | NSString *apiKey = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] stringForKey:@"TINY_PNG_APIKEY"];
206 | if (apiKey.length <= 0) {
207 | NSBeginAlertSheet(@"API key required!", @"OK", @"Show me where to find", nil, self.window, self, @selector(sheetDidEnd:returnCode:contextInfo:), NULL, NULL, @"Please input your Api key and hit ENTER");
208 | return;
209 | }
210 |
211 | NSString *base64encodedKey = [[[NSString stringWithFormat:@"api:%@", apiKey] dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn];
212 | NSString *auth = [NSString stringWithFormat:@"Basic %@", base64encodedKey];
213 |
214 | self.processing = YES;
215 |
216 | [RTImageCompressingQueue() addObserver:self
217 | forKeyPath:@"operationCount"
218 | options:NSKeyValueObservingOptionNew
219 | context:observingContext];
220 |
221 | [self.imageItems enumerateObjectsUsingBlock:^(RTImageItem *obj, NSUInteger idx, BOOL *stop) {
222 | if (obj.selected) {
223 | [RTImageCompressingQueue() addOperationWithBlock:^{
224 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:TINY_PNG_HOST]
225 | cachePolicy:NSURLRequestReloadIgnoringCacheData
226 | timeoutInterval:8.f];
227 | request.timeoutInterval = 10.f;
228 | request.HTTPMethod = @"POST";
229 | [request setValue:auth
230 | forHTTPHeaderField:@"Authorization"];
231 | request.HTTPBodyStream = [NSInputStream inputStreamWithFileAtPath:obj.imagePath];
232 |
233 | // Call web api synchronizely
234 |
235 | NSHTTPURLResponse *response = nil;
236 | NSError *error = nil;
237 |
238 | NSData *data = [NSURLConnection sendSynchronousRequest:request
239 | returningResponse:&response
240 | error:&error];
241 | id json = nil;
242 | if (data) {
243 | json = [NSJSONSerialization JSONObjectWithData:data
244 | options:0
245 | error:NULL];
246 | }
247 |
248 |
249 | if (response.statusCode == 201 && json) {
250 | obj.imageSizeOptimized = [json[@"output"][@"size"] integerValue];
251 | NSURL *compressURL = [NSURL URLWithString:json[@"output"][@"url"]];
252 | if (compressURL && [[NSData dataWithContentsOfURL:compressURL] writeToFile:obj.imagePath
253 | atomically:YES]) {
254 | obj.state = RTImageOptimizeStateOptimized;
255 | obj.selected = NO;
256 | self.optimizedTotalBytes -= obj.imageSize - obj.imageSizeOptimized;
257 | }
258 | }
259 | else {
260 | obj.state = RTImageOptimizeStateFailed;
261 |
262 | if (error) {
263 | // Api error, we should stop
264 | if (json) {
265 | [RTImageCompressingQueue() cancelAllOperations];
266 | dispatch_async(dispatch_get_main_queue(), ^{
267 | NSBeginAlertSheet(json[@"error"] ?: error.localizedFailureReason, @"OK", nil, nil, self.window, nil, NULL, NULL, NULL, @"%@", json[@"message"] ?: error.localizedDescription);
268 | });
269 | }
270 | // Network error, skip
271 | else {
272 | NSLog(@"%@", error);
273 | }
274 | }
275 | // API call limit exceeded, we should stop
276 | else if (response.statusCode == 429) {
277 | [RTImageCompressingQueue() cancelAllOperations];
278 | dispatch_async(dispatch_get_main_queue(), ^{
279 | NSBeginAlertSheet(json[@"error"], @"OK", nil, nil, self.window, nil, NULL, NULL, NULL, @"%@", json[@"message"]);
280 | });
281 | }
282 | else {
283 | NSLog(@"%@", response);
284 | }
285 | }
286 | dispatch_async(dispatch_get_main_queue(), ^{
287 | [self updateStats];
288 | [self.tableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:idx]
289 | columnIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.tableView.tableColumns.count)]];
290 | });
291 | }];
292 |
293 | obj.state = RTImageOptimizeStatePending;
294 | [self.tableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:idx]
295 | columnIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.tableView.tableColumns.count)]];
296 | }
297 | }];
298 | }
299 |
300 | - (IBAction)onCancel:(id)sender {
301 | self.processing = NO;
302 | }
303 |
304 | #pragma mark - Methods
305 |
306 |
307 | - (void)sheetDidEnd:(NSWindow *)sheet
308 | returnCode:(NSInteger)returnCode
309 | contextInfo:(void *)contextInfo
310 | {
311 | if (returnCode == NSAlertAlternateReturn) {
312 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://tinypng.com/developers"]];
313 | }
314 | }
315 |
316 | - (void)setProcessing:(BOOL)processing
317 | {
318 | @synchronized(self) {
319 | _processing = processing;
320 | if (_processing) {
321 | self.startButton.enabled = NO;
322 | self.concurrencyButton.enabled = NO;
323 | self.tableView.enabled = NO;
324 | self.progressIndicator.hidden = NO;
325 | [self.progressIndicator startAnimation:self];
326 | }
327 | else {
328 | self.startButton.enabled = YES;
329 | self.concurrencyButton.enabled = YES;
330 | self.tableView.enabled = YES;
331 | self.progressIndicator.hidden = YES;
332 | [self.progressIndicator stopAnimation:self];
333 | }
334 | }
335 | }
336 |
337 | - (BOOL)isProcessing
338 | {
339 | @synchronized(self) {
340 | return _processing;
341 | }
342 | }
343 |
344 | - (NSMutableArray *)imageItems
345 | {
346 | if (!_imageItems) {
347 | _imageItems = [NSMutableArray array];
348 | }
349 | return _imageItems;
350 | }
351 |
352 | - (void)setLoading:(BOOL)loading
353 | {
354 | _loading = loading;
355 | if (_loading) {
356 | self.startButton.enabled = NO;
357 | self.progressIndicator.hidden = NO;
358 | [self.progressIndicator startAnimation:self];
359 | }
360 | else {
361 | self.startButton.enabled = YES;
362 | [self.progressIndicator stopAnimation:self];
363 | self.progressIndicator.hidden = YES;
364 | }
365 | }
366 |
367 | - (void)updateStats
368 | {
369 | self.statsLabel.stringValue = [NSString stringWithFormat:@"%lu/%lu, Compress ratio:%.2f", (unsigned long)self.optimizedTotalBytes, (unsigned long)self.totalBytes, (float)1.f * self.optimizedTotalBytes / self.totalBytes];
370 | }
371 |
372 | - (void)reloadImages
373 | {
374 | NSString *path = [RTWorkspace currentWorkspacePath].stringByDeletingLastPathComponent;
375 | if (!path || self.isLoading || self.isProcessing)
376 | return;
377 |
378 | self.loading = YES;
379 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
380 |
381 | // Find all png/jpg/jpeg image files
382 | NSArray *allowedImageTypes = @[@"png", @"jpg", @"jpeg"];
383 |
384 | NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtPath:path];
385 | NSString *relaventPath = nil;
386 |
387 | [self.imageItems removeAllObjects];
388 | self.totalBytes = self.optimizedTotalBytes = 0;
389 |
390 | BOOL allSelected = YES;
391 | while (relaventPath = [enumerator nextObject]) {
392 | if ([allowedImageTypes containsObject:relaventPath.pathExtension.lowercaseString]) {
393 | RTImageItem *item = [RTImageItem itemWithPath:[path stringByAppendingPathComponent:relaventPath]];
394 | item.selected = !item.hasOptimized;
395 |
396 | self.totalBytes += item.imageSize;
397 | if (item.imageSizeOptimized)
398 | self.optimizedTotalBytes += item.imageSizeOptimized;
399 | else
400 | self.optimizedTotalBytes += item.imageSize;
401 |
402 | if (!item.isSelected) {
403 | allSelected = NO;
404 | }
405 | [self.imageItems addObject:item];
406 | }
407 | }
408 | [self.imageItems sortUsingDescriptors:self.tableView.sortDescriptors];
409 |
410 | dispatch_async(dispatch_get_main_queue(), ^{
411 | self.loading = NO;
412 | self.selectAllCell.state = NSOffState;
413 | if (allSelected)
414 | [self onToggleSelectionAll:self];
415 |
416 | [self updateStats];
417 | [self.tableView reloadData];
418 | });
419 | });
420 | }
421 |
422 | - (NSImage *)makeImageForState:(RTImageOptimizeState)state
423 | {
424 | NSString *imageNames[] = {
425 | @"NSStatusNone",
426 | @"NSStatusPartiallyAvailable",
427 | @"NSStatusAvailable",
428 | @"NSStatusUnavailable",
429 | };
430 | return [NSImage imageNamed:imageNames[state]];
431 | }
432 |
433 | - (void)clearImageItems
434 | {
435 | [self.imageItems removeAllObjects];
436 | [self.tableView reloadData];
437 | }
438 |
439 | #pragma mark - NSTableView
440 |
441 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
442 | {
443 | return self.imageItems.count;
444 | }
445 |
446 | - (NSView *)tableView:(NSTableView *)tableView
447 | viewForTableColumn:(NSTableColumn *)tableColumn
448 | row:(NSInteger)row
449 | {
450 | NSInteger col = [tableView.tableColumns indexOfObject:tableColumn];
451 | RTImageItem *item = self.imageItems[row];
452 | NSTableCellView *cell = [tableView makeViewWithIdentifier:tableColumn.identifier
453 | owner:self];
454 | switch (col) {
455 | case 0:
456 | {
457 | ((NSButton *)cell).state = item.isSelected ? NSOnState : NSOffState;
458 | }
459 | break;
460 | case 1:
461 | cell.imageView.image = item.imageIcon;
462 | cell.textField.stringValue = item.imageName;
463 | cell.toolTip = item.imageName;
464 | break;
465 | case 2:
466 | cell.textField.stringValue = item.imagePath;
467 | cell.toolTip = item.imagePath;
468 | break;
469 | case 3:
470 | cell.textField.objectValue = @(item.imageSize);
471 | break;
472 | case 4:
473 | cell.textField.objectValue = @(item.imageSizeOptimized);
474 | break;
475 | case 5:
476 | cell.textField.stringValue = [NSString stringWithFormat:@"%dx%d", (int)item.size.width, (int)item.size.height];
477 | break;
478 | case 6:
479 | {
480 | // cell.textField.stringValue = item.hasOptimized ? @"✔︎" : @"✘";
481 | cell.imageView.image = [self makeImageForState:item.state];
482 | }
483 | break;
484 | default:
485 | break;
486 | }
487 |
488 | return cell;
489 | }
490 |
491 | - (void)tableView:(NSTableView *)tableView
492 | sortDescriptorsDidChange:(NSArray *)oldDescriptors
493 | {
494 | [self.imageItems sortUsingDescriptors:tableView.sortDescriptors];
495 | [tableView reloadData];
496 | }
497 |
498 | @end
499 |
--------------------------------------------------------------------------------
/RTTinyPNGWebAPIPlugin/RTImageController.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 |
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 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
394 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
--------------------------------------------------------------------------------