├── .gitignore ├── .gitmodules ├── LICENSE ├── Podfile ├── Podfile.lock ├── README.md ├── diumoo.xcodeproj └── project.pbxproj ├── diumoo ├── DMApp.h ├── DMApp.m ├── DMAppDelegate.h ├── DMAppDelegate.m ├── DMDocument │ ├── DMDocument.h │ ├── DMDocument.m │ ├── DMDocumentWindowController │ │ ├── DMDetailViewController │ │ │ ├── DMDetailViewController.h │ │ │ └── DMDetailViewController.m │ │ ├── DMDocumentWindowController.h │ │ ├── DMDocumentWindowController.m │ │ ├── en.lproj │ │ │ └── DMDocumentWindow.xib │ │ └── zh-Hans.lproj │ │ │ └── DMDocumentWindow.xib │ └── JUInspectorView │ │ ├── JUInspectorBaseView.h │ │ ├── JUInspectorBaseView.m │ │ ├── JUInspectorView.h │ │ ├── JUInspectorView.m │ │ ├── JUInspectorViewContainer.h │ │ ├── JUInspectorViewContainer.m │ │ ├── JUInspectorViewHeader.h │ │ └── JUInspectorViewHeader.m ├── core │ ├── DMAuthHelper.swift │ ├── DMControlCenter │ │ ├── DMControlCenter.h │ │ └── DMControlCenter.m │ ├── DMErrorLog │ │ ├── DMErrorLog.h │ │ └── DMErrorLog.m │ ├── DMNotificationCenter.swift │ ├── DMPlayRecordHandler │ │ ├── DMPlayRecordHandler.h │ │ ├── DMPlayRecordHandler.m │ │ └── DMPlayRecordModel.xcdatamodeld │ │ │ ├── .xccurrentversion │ │ │ └── DMPlayRecordModel.xcdatamodel │ │ │ └── contents │ ├── DMPlayableItem.swift │ ├── DMPlaylistFetcher.swift │ ├── DMService │ │ ├── DMService.h │ │ └── DMService.m │ ├── DMShortcutsHandler │ │ ├── DMShortcutsHandler.h │ │ └── DMShortcutsHandler.m │ └── DMUtils │ │ ├── Dictionary+urlEncoding.swift │ │ ├── NSData+AESCrypt.h │ │ ├── NSData+AESCrypt.m │ │ ├── NSData+Base64.h │ │ ├── NSData+Base64.m │ │ ├── NSDictionary+UrlEncoding.h │ │ ├── NSDictionary+UrlEncoding.m │ │ ├── NSImage+AsyncLoadImage.h │ │ └── NSImage+AsyncLoadImage.m ├── debugLog.h ├── diumoo-Bridging-Header.h ├── diumoo-Info.plist ├── diumoo-Prefix.pch ├── en.lproj │ ├── Credits.rtf │ ├── InfoPlist.strings │ ├── Localizable.strings │ └── MainMenu.xib ├── main.m ├── ui │ ├── DMPanel │ │ ├── DMBitrateControlLayer │ │ │ ├── DMBitrateControlLayer.h │ │ │ └── DMBitrateControlLayer.m │ │ ├── DMButton │ │ │ ├── DMButton.h │ │ │ └── DMButton.m │ │ ├── DMCoverControlView │ │ │ ├── DMCoverControlView.h │ │ │ └── DMCoverControlView.m │ │ ├── DMCoverSlide │ │ │ ├── DMCoverSlide.h │ │ │ └── DMCoverSlide.m │ │ ├── DMPanel.h │ │ ├── DMPanel.m │ │ ├── DMPanelWindowController.h │ │ ├── DMPanelWindowController.m │ │ ├── DMPopUpButtonCell │ │ │ ├── DMPopUpButtonCell.h │ │ │ ├── DMPopUpButtonCell.m │ │ │ ├── DMPopUpMenuController.h │ │ │ └── DMPopUpMenuController.m │ │ ├── DMVolumeSlideCell │ │ │ ├── DMVolumeSlideCell.h │ │ │ └── DMVolumeSlideCell.m │ │ ├── en.lproj │ │ │ └── DMPanelWindowController.xib │ │ └── zh-Hans.lproj │ │ │ └── DMPanelWindowController.xib │ ├── DMPrefsPanel │ │ ├── DMPrefsPanelDataProvider.h │ │ ├── DMPrefsPanelDataProvider.m │ │ ├── en.lproj │ │ │ └── DMPrefsPanel.xib │ │ └── zh-Hans.lproj │ │ │ └── DMPrefsPanel.xib │ ├── DMSearchPanel │ │ ├── DMSearchItemView.h │ │ ├── DMSearchItemView.m │ │ ├── DMSearchPanelController.h │ │ ├── DMSearchPanelController.m │ │ ├── en.lproj │ │ │ └── DMSearchPanelController.xib │ │ └── zh-Hans.lproj │ │ │ └── DMSearchPanelController.xib │ └── DockIcon │ │ └── DockImageProvider.swift └── zh-Hans.lproj │ ├── Credits.rtf │ ├── InfoPlist.strings │ ├── Localizable.strings │ └── MainMenu.xib ├── extern ├── PLTabPreference │ ├── PLTabPreferenceControl.h │ └── PLTabPreferenceControl.m ├── SSKeychain │ ├── LICENSE │ ├── SSKeychain.h │ └── SSKeychain.m └── StatusItem │ ├── MenubarController.h │ ├── MenubarController.m │ ├── StatusItemView.h │ └── StatusItemView.m ├── resources ├── 128kbps.png ├── 128kbps@2x.png ├── 192kbps.png ├── 192kbps@2x.png ├── 64kbps.png ├── 64kbps@2x.png ├── albumfail.png ├── albumfail@2x.png ├── ban.png ├── ban@2x.png ├── ban_mouseover.png ├── ban_mouseover@2x.png ├── channels.plist ├── chrome.png ├── chrome_2x.png ├── dark-status-icon-alt.png ├── dark-status-icon-alt@2x.png ├── dark-status-icon-mixed.png ├── dark-status-icon-mixed@2x.png ├── dark-status-icon.png ├── dark-status-icon@2x.png ├── detail_icon.png ├── detail_icon@2x.png ├── douban.png ├── douban@2x.png ├── exit.png ├── exit@2x.png ├── exit_mouseover.png ├── exit_mouseover@2x.png ├── eye_icon.png ├── eye_icon@2x.png ├── facebook.png ├── facebook@2x.png ├── fanfou.png ├── fanfou@2x.png ├── firefox.png ├── firefox_2x.png ├── icon.icns ├── info_icon.png ├── info_icon@2x.png ├── menu-heart-gray.png ├── menu-heart-gray@2x.png ├── menu-heart.png ├── menu-heart@2x.png ├── pause.png ├── pause@2x.png ├── pause_mouseover.png ├── pause_mouseover@2x.png ├── play.png ├── play@2x.png ├── play_mouseover.png ├── play_mouseover@2x.png ├── pro_icon.png ├── pro_icon@2x.png ├── rate.png ├── rate@2x.png ├── rate_mouseover.png ├── rate_mouseover@2x.png ├── rate_red.png ├── rate_red@2x.png ├── record.icns ├── safari.png ├── safari_2x.png ├── search.png ├── search@2x.png ├── search_mouseover.png ├── search_mouseover@2x.png ├── settings.png ├── settings@2x.png ├── settings_mouseover.png ├── settings_mouseover@2x.png ├── share.png ├── share@2x.png ├── sina.png ├── sina@2x.png ├── skip.png ├── skip@2x.png ├── skip_mouseover.png ├── skip_mouseover@2x.png ├── status-icon-alt.png ├── status-icon-alt@2x.png ├── status-icon-mixed.png ├── status-icon-mixed@2x.png ├── status-icon.png ├── status-icon@2x.png ├── time_machine.png ├── time_machine@2x.png ├── twitter.png ├── twitter@2x.png ├── view_icon.png ├── view_icon@2x.png ├── volume_back.png ├── volume_back@2x.png ├── volume_slider.png └── volume_slider@2x.png └── third-party ├── SMTabBar ├── InspectorTabBar │ ├── NSDictionary+SMKeyValueObserving.h │ ├── NSDictionary+SMKeyValueObserving.m │ ├── SMBar.h │ ├── SMBar.m │ ├── SMTabBar.h │ ├── SMTabBar.m │ ├── SMTabBarButtonCell.h │ ├── SMTabBarButtonCell.m │ ├── SMTabBarItem.h │ └── SMTabBarItem.m ├── README.md └── SMTabBar.podspec └── SPMediaKeyTap ├── README.md ├── SPInvocationGrabbing ├── NSObject+SPInvocationGrabbing.h └── NSObject+SPInvocationGrabbing.m ├── SPMediaKeyTap.h ├── SPMediaKeyTap.m └── SPMediaKeyTap.podspec /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | Growl.framework/* 19 | 20 | Pods/ 21 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "extern/MASShortcut"] 2 | path = extern/MASShortcut 3 | url = https://github.com/shpakovski/MASShortcut.git 4 | [submodule "extern/SPMediaKeyTap"] 5 | path = extern/SPMediaKeyTap 6 | url = https://github.com/nevyn/SPMediaKeyTap.git 7 | [submodule "extern/SMTabBar"] 8 | path = extern/SMTabBar 9 | url = https://github.com/smic/InspectorTabBar.git 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | diumoo - a Mac client of Douban.FM 2 | Copyright © 2016 Chase Zhang 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | 17 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/Cocoapods/Specs.git' 2 | 3 | platform :osx, '10.10' 4 | inhibit_all_warnings! 5 | 6 | target 'diumoo' do 7 | pod 'MASShortcut', '~> 2.3.6' 8 | pod 'SMTabBar', :path => './third-party/SMTabBar' 9 | pod 'SPMediaKeyTap', :path => './third-party/SPMediaKeyTap' 10 | end 11 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - MASShortcut (2.3.6) 3 | - SMTabBar (0.0.1) 4 | - SPMediaKeyTap (0.0.1) 5 | 6 | DEPENDENCIES: 7 | - MASShortcut (~> 2.3.6) 8 | - SMTabBar (from `./third-party/SMTabBar`) 9 | - SPMediaKeyTap (from `./third-party/SPMediaKeyTap`) 10 | 11 | EXTERNAL SOURCES: 12 | SMTabBar: 13 | :path: ./third-party/SMTabBar 14 | SPMediaKeyTap: 15 | :path: ./third-party/SPMediaKeyTap 16 | 17 | SPEC CHECKSUMS: 18 | MASShortcut: 9c215e8a8a78f3d01ce56da48e2730ab66b538fa 19 | SMTabBar: 11521459249d577ef278442319c0ac4d90628d2e 20 | SPMediaKeyTap: ddb3cf0f3485bf2ac5a31c1b5ce9fc396115be7f 21 | 22 | PODFILE CHECKSUM: b0edc7bd09b1788418d857997b326ea538e805c2 23 | 24 | COCOAPODS: 1.3.1 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Diumoo 2 | 3 | Third party client of [Douban.FM](http://douban.fm) for Mac. 4 | For more details, refer to our [Homepage](http://diumoo.net). 5 | 6 | ## Why? 7 | 8 | 1. NO Flash, NO web broswer 9 | 2. Native code, battery friendly 10 | 3. Silently rest in your status bar 11 | 4. More features (Time Machine, Search, etc.) 12 | 13 | ## Installation 14 | 15 | Download [precompiled version](https://github.com/shanzi/diumoo/releases) 16 | 17 | ## Develop 18 | 19 | To contribute or compile diumoo by yourself, follow instructions below: 20 | 21 | ``` 22 | git clone git@github.com:shanzi/diumoo.git 23 | 24 | git submodule init # Initialize dependencies 25 | 26 | ``` 27 | 28 | Then you can open `diumoo.xcodeproj` and modify the code or compile it. 29 | 30 | Pull requests are extremely welcomed. Fork and conforms to [GitHub workflow](https://guides.github.com/introduction/flow/index.html) 31 | before contribution :) 32 | 33 | **NOTICE**: We use `master` branch for developing and new pull requests will be merged into `master`. 34 | For latest stable version, pleace switch to `release` branch. 35 | 36 | Since 1.6 we will move to Swift 3+. Please make sure you compile with Xcode >= 8.1. 37 | 38 | ## TODO 39 | 40 | - [ ] migrate to CocoaPods instead of git submodules 41 | - [ ] migrate to swift (Anakin is working on this) 42 | 43 | ## Maintainers 44 | 45 | Currently, this project is mainly maintained by: 46 | 47 | 1. [Chase Zhang](github.com/shanzi) 48 | 2. [Anakin(Yancheng) Zheng](https://github.com/AnakinMac) 49 | 50 | More developers are wanted! 51 | 52 | # LICENSE 53 | 54 | Main code of this project is licensed under GPLv3. For more details refer to [LICENSE](./LICENSE). 55 | All codes under [extern](./extern) are external dependencies and are still licensed under their original licenses. 56 | -------------------------------------------------------------------------------- /diumoo/DMApp.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMApp.h 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-6-7. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface DMApp : NSApplication 13 | { 14 | NSString *openedURLString; 15 | } 16 | 17 | @property (copy) NSString *openedURLString; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /diumoo/DMApp.m: -------------------------------------------------------------------------------- 1 | // 2 | // DMApp.m 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-6-7. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "DMApp.h" 10 | #import "DMService.h" 11 | #import "SPMediaKeyTap.h" 12 | 13 | @implementation DMApp 14 | 15 | @synthesize openedURLString; 16 | 17 | -(id) init 18 | { 19 | self = [super init]; 20 | if(self) 21 | { 22 | NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier]; 23 | LSSetDefaultHandlerForURLScheme((CFStringRef)@"diumoo", (__bridge CFStringRef)bundleID); 24 | 25 | [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self 26 | andSelector:@selector(handleEvent:withReplyEvent:) 27 | forEventClass:kInternetEventClass 28 | andEventID:kAEGetURL]; 29 | } 30 | return self; 31 | } 32 | 33 | 34 | - (void)handleEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent 35 | { 36 | NSString* urlstring = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; 37 | openedURLString = [urlstring copy]; 38 | [DMService openDiumooLink:openedURLString]; 39 | } 40 | 41 | - (void)sendEvent:(NSEvent *)theEvent 42 | { 43 | // If event tap is not installed, handle events that reach the app instead 44 | BOOL shouldHandleMediaKeyEventLocally = ![SPMediaKeyTap usesGlobalMediaKeyTap]; 45 | 46 | if(shouldHandleMediaKeyEventLocally && [theEvent type] == NSSystemDefined && [theEvent subtype] == SPSystemDefinedEventMediaKeys) { 47 | [(id)[self delegate] mediaKeyTap:nil receivedMediaKeyEvent:theEvent]; 48 | } 49 | [super sendEvent:theEvent]; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /diumoo/DMAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMAppDelegate.h 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-6-7. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "DMControlCenter.h" 12 | #import "DMPanelWindowController.h" 13 | #import "DMShortcutsHandler.h" 14 | #import "PLTabPreferenceControl.h" 15 | #import "SPMediaKeyTap.h" 16 | 17 | @interface DMAppDelegate : NSObject { 18 | DMControlCenter* center; 19 | SPMediaKeyTap* mediakeyTap; 20 | } 21 | 22 | - (IBAction)showPreference:(id)sender; 23 | - (IBAction)importOrExport:(id)sender; 24 | - (IBAction)showHelp:(id)sender; 25 | - (void)keyShortcuts:(id)key; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /diumoo/DMDocument/DMDocument.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMDocument.h 3 | // documentTest 4 | // 5 | // Created by Shanzi on 12-6-25. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DMDocument : NSDocument { 12 | NSString* sid; 13 | NSString* ssid; 14 | NSString* aid; 15 | 16 | NSDictionary* baseSongInfo; 17 | } 18 | 19 | @property (readonly) NSDictionary* baseSongInfo; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /diumoo/DMDocument/DMDocument.m: -------------------------------------------------------------------------------- 1 | // 2 | // DMDocument.m 3 | // documentTest 4 | // 5 | // Created by Shanzi on 12-6-25. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "DMDocument.h" 10 | #import "DMDocumentWindowController.h" 11 | #import "DMPlayRecordHandler.h" 12 | #import "DMService.h" 13 | 14 | @implementation DMDocument 15 | @synthesize baseSongInfo; 16 | 17 | - (id)init 18 | { 19 | if (self = [super init]) { 20 | // Add your subclass-specific initialization here. 21 | } 22 | return self; 23 | } 24 | 25 | - (void)makeWindowControllers 26 | { 27 | if (sid == nil) 28 | return; 29 | if ([self.windowControllers count] == 0) { 30 | DMDocumentWindowController* windowController = [[DMDocumentWindowController allocWithZone:nil] init]; 31 | [self addWindowController:windowController]; 32 | } 33 | } 34 | 35 | - (void)revertDocumentToSaved:(id)sender 36 | { 37 | if ([self respondsToSelector:@selector(browseDocumentVersions:)]) { 38 | [super browseDocumentVersions:self]; 39 | } 40 | else { 41 | [super revertDocumentToSaved:self]; 42 | } 43 | } 44 | 45 | - (BOOL)revertToContentsOfURL:(NSURL*)url ofType:(NSString*)typeName error:(NSError**)outError 46 | { 47 | BOOL success = [super revertToContentsOfURL:url ofType:typeName error:outError]; 48 | if (success) { 49 | [[self windowControllers] makeObjectsPerformSelector:@selector(setupWindowForDocument:) withObject:self]; 50 | [[DMPlayRecordHandler sharedRecordHandler] removeCurrentVersion]; 51 | [[DMPlayRecordHandler sharedRecordHandler] playSongWith:sid andSsid:ssid]; 52 | } 53 | return YES; 54 | } 55 | 56 | + (BOOL)autosavesInPlace 57 | { 58 | return YES; 59 | } 60 | 61 | + (BOOL)preservesVersions 62 | { 63 | return YES; 64 | } 65 | 66 | - (BOOL)hasUnautosavedChanges 67 | { 68 | return NO; 69 | } 70 | 71 | - (NSData*)dataOfType:(NSString*)typeName error:(NSError**)outError 72 | { 73 | NSData* data = [[baseSongInfo descriptionWithLocale:nil] dataUsingEncoding:NSUTF8StringEncoding]; 74 | return data; 75 | } 76 | 77 | - (BOOL)readFromURL:(NSURL*)url ofType:(NSString*)type error:(NSError**)outError 78 | { 79 | if ([type isEqualToString:@"_private_record"]) { 80 | NSString* _sid = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil]; 81 | if (_sid) { 82 | DMPlayRecordHandler* sharedHandler = [DMPlayRecordHandler sharedRecordHandler]; 83 | NSManagedObject* object = [sharedHandler songWithSid:_sid]; 84 | if (object) { 85 | sid = [object valueForKey:@"sid"]; 86 | ssid = [object valueForKey:@"ssid"]; 87 | aid = [object valueForKey:@"aid"]; 88 | 89 | NSArray* keyarray = [[[object entity] attributesByName] allKeys]; 90 | NSDictionary* infodict = [object dictionaryWithValuesForKeys:keyarray]; 91 | baseSongInfo = infodict; 92 | return YES; 93 | } 94 | } 95 | } 96 | else if ([type isEqualToString:@"shortcut"]) { 97 | NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:url]; 98 | if (dict) { 99 | sid = [dict valueForKey:@"sid"]; 100 | ssid = [dict valueForKey:@"ssid"]; 101 | aid = [dict valueForKey:@"aid"]; 102 | baseSongInfo = dict; 103 | if (sid) { 104 | return YES; 105 | } 106 | } 107 | } 108 | else if ([type isEqualToString:@"Play Record"]) { 109 | if (NSAlertDefaultReturn 110 | == NSRunAlertPanel(NSLocalizedString(@"IMPORT_PLAY_RECORD", nil), 111 | NSLocalizedString(@"IMPORT_PLAY_RECORD_DETAIL", nil), 112 | //@"您刚刚打开的一个diumoo播放记录文件,是否导入这些记录?", 113 | NSLocalizedString(@"YES", nil), NSLocalizedString(@"NO", nil), nil)) { 114 | [DMService importRecordOperationWithFilePath:url]; 115 | } 116 | 117 | return YES; 118 | } 119 | 120 | if (outError != nil) { 121 | *outError = [NSError errorWithDomain:NSLocalizedString(@"OPEN_FILE_FAILED", nil) code:-1 userInfo:nil]; 122 | } 123 | 124 | return NO; 125 | } 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /diumoo/DMDocument/DMDocumentWindowController/DMDetailViewController/DMDetailViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMDetailViewController.h 3 | // documentTest 4 | // 5 | // Created by Shanzi on 12-7-2. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "JUInspectorViewContainer.h" 10 | #import 11 | 12 | @interface DMDetailViewController : JUInspectorViewContainer { 13 | NSDictionary* baseInfoDict; 14 | NSArray* baseInfoKeys; 15 | NSArray* songsArray; 16 | } 17 | 18 | @property (nonatomic, strong) NSTableView* baseInformationView; 19 | @property (nonatomic, strong) NSTextView* summaryView; 20 | @property (nonatomic, strong) NSTableView* songsView; 21 | 22 | - (id)initWithBaseInformation:(NSDictionary*)info summary:(NSString*)summary andSongs:(NSArray*)songs; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /diumoo/DMDocument/DMDocumentWindowController/DMDetailViewController/DMDetailViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // DMDetailViewController.m 3 | // documentTest 4 | // 5 | // Created by Shanzi on 12-7-2. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "DMDetailViewController.h" 10 | #import "JUInspectorView.h" 11 | 12 | #define kColumnKey @"k" 13 | #define kColumnValue @"v" 14 | 15 | @interface DMDetailViewController () 16 | 17 | @end 18 | 19 | @implementation DMDetailViewController 20 | @synthesize baseInformationView, summaryView, songsView; 21 | 22 | - (id)initWithBaseInformation:(NSDictionary*)info summary:(NSString*)summary andSongs:(NSArray*)songs 23 | { 24 | self = [super init]; 25 | if (self) { 26 | // Initialization code here 27 | 28 | baseInfoKeys = @[ 29 | NSLocalizedString(@"DETAIL_ALBUM", @"专辑名"), 30 | NSLocalizedString(@"DETAIL_SINGER", @"艺术家"), 31 | NSLocalizedString(@"DETAIL_DOUBAN_RATING", @"豆瓣评分"), 32 | NSLocalizedString(@"DETAIL_VERSION", @"版本特性"), 33 | NSLocalizedString(@"DETAIL_PUBLISHER", @"出版者"), 34 | NSLocalizedString(@"DETAIL_PUBDATE", @"发行时间"), 35 | NSLocalizedString(@"DETAIL_MEDIA", @"介质"), 36 | NSLocalizedString(@"DETAIL_DISCS", @"唱片数"), 37 | NSLocalizedString(@"DETAIL_EAN", @"条形码") 38 | ]; 39 | 40 | self.baseInformationView = [self buildTableView]; 41 | self.songsView = [self buildTableView]; 42 | 43 | self.summaryView = [[NSTextView alloc] 44 | initWithFrame:NSMakeRect(0, 0, 400, 200)]; 45 | [summaryView setEditable:NO]; 46 | [summaryView setAutoresizingMask:NSViewWidthSizable]; 47 | 48 | if (summary) { 49 | self.summaryView.string = summary; 50 | } 51 | else { 52 | self.summaryView.string = NSLocalizedString(@"DETAIL_UNKNOWN", nil); 53 | } 54 | 55 | baseInfoDict = info; 56 | songsArray = songs; 57 | 58 | JUInspectorView* infoInspector = [[JUInspectorView alloc] init]; 59 | infoInspector.name = NSLocalizedString(@"DETAIL_ALBUM_INFO", @"专辑信息"); 60 | infoInspector.body = baseInformationView; 61 | 62 | JUInspectorView* summaryInspector = [[JUInspectorView alloc] init]; 63 | NSScrollView* scrollView = [[NSScrollView alloc] 64 | initWithFrame:NSMakeRect(0, 0, 400, 200)]; 65 | 66 | [scrollView setDocumentView:summaryView]; 67 | [scrollView setHasVerticalScroller:YES]; 68 | [scrollView setAutohidesScrollers:YES]; 69 | 70 | summaryInspector.name = NSLocalizedString(@"DETAIL_ABSTRACT", @"简介"); 71 | summaryInspector.body = scrollView; 72 | 73 | JUInspectorView* songsInspector = [[JUInspectorView alloc] init]; 74 | songsInspector.name = NSLocalizedString(@"DETAIL_SONGS", @"曲目"); 75 | songsInspector.body = songsView; 76 | 77 | [self addInspectorView:infoInspector expanded:YES]; 78 | [self addInspectorView:summaryInspector expanded:NO]; 79 | [self addInspectorView:songsInspector expanded:YES]; 80 | } 81 | return self; 82 | } 83 | 84 | - (NSTableView*)buildTableView 85 | { 86 | NSTableView* tableview = [[NSTableView alloc] init]; 87 | NSTableColumn* column1 = [[NSTableColumn alloc] initWithIdentifier:kColumnKey]; 88 | NSTableColumn* column2 = [[NSTableColumn alloc] initWithIdentifier:kColumnValue]; 89 | [tableview addTableColumn:column1]; 90 | [tableview addTableColumn:column2]; 91 | [tableview setDelegate:self]; 92 | [tableview setDataSource:self]; 93 | 94 | return tableview; 95 | } 96 | 97 | - (BOOL)tableView:(NSTableView*)tableView shouldEditTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row 98 | { 99 | return NO; 100 | } 101 | 102 | - (BOOL)tableView:(NSTableView*)tableView shouldSelectRow:(NSInteger)row 103 | { 104 | return NO; 105 | } 106 | 107 | - (BOOL)tableView:(NSTableView*)tableView shouldSelectTableColumn:(NSTableColumn*)tableColumn 108 | { 109 | return NO; 110 | } 111 | 112 | - (BOOL)tabView:(NSTabView*)tableView shouldSelectTabViewItem:(NSTabViewItem*)tabViewItem 113 | { 114 | return YES; 115 | } 116 | 117 | - (NSInteger)numberOfRowsInTableView:(NSTableView*)tableView 118 | { 119 | if (tableView == songsView) { 120 | return [songsArray count]; 121 | } 122 | else { 123 | return [baseInfoKeys count]; 124 | ; 125 | } 126 | } 127 | 128 | - (id)tableView:(NSTableView*)tableView objectValueForTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row 129 | { 130 | if (tableView == songsView) { 131 | if ([tableColumn.identifier isEqualToString:kColumnKey]) { 132 | return [NSString stringWithFormat:@"%ld", row + 1]; 133 | } 134 | else { 135 | return songsArray[row]; 136 | } 137 | } 138 | else { 139 | if ([tableColumn.identifier isEqualToString:kColumnKey]) { 140 | 141 | return baseInfoKeys[row]; 142 | } 143 | else { 144 | 145 | NSString* string = [baseInfoDict valueForKey:baseInfoKeys[row]]; 146 | if (string == nil) { 147 | return NSLocalizedString(@"DETAIL_UNKNOWN", @"未知"); 148 | } 149 | else { 150 | return string; 151 | } 152 | } 153 | } 154 | } 155 | 156 | @end 157 | -------------------------------------------------------------------------------- /diumoo/DMDocument/DMDocumentWindowController/DMDocumentWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMDocumentWindowController.h 3 | // documentTest 4 | // 5 | // Created by Shanzi on 12-7-1. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | @class SMTabBar; 11 | 12 | @interface DMDocumentWindowController : NSWindowController { 13 | 14 | IBOutlet SMTabBar* tabBar; 15 | IBOutlet NSTabView* tabView; 16 | 17 | // -------------------------预览标签页-------------------------- 18 | IBOutlet NSButton* revertButton; 19 | IBOutlet NSButton* albumCoverButton; 20 | IBOutlet NSTextField* songTitle; 21 | IBOutlet NSTextField* artist; 22 | 23 | // ------------------------详细信息标签页------------------------ 24 | IBOutlet NSTextField* indicatorText; 25 | IBOutlet NSProgressIndicator* progressIndicator; 26 | 27 | // -----------------------其他--------------------------------- 28 | NSString* albumTitle; 29 | NSString* aid; 30 | NSString* albumLocation; 31 | 32 | NSLock* lock; 33 | } 34 | 35 | - (id)init; 36 | 37 | - (void)setupWindowForDocument:(NSDocument*)doc; 38 | 39 | - (IBAction)revert:(id)sender; 40 | - (IBAction)playAlbum:(id)sender; 41 | - (IBAction)openAlbumLocation:(id)sender; 42 | @end 43 | -------------------------------------------------------------------------------- /diumoo/DMDocument/JUInspectorView/JUInspectorBaseView.h: -------------------------------------------------------------------------------- 1 | // 2 | // JUInspectorBaseView.h 3 | // JUInspectorView 4 | // 5 | // Created by Jon Gilkison on 9/28/11. 6 | // Copyright 2011 Interfacelab LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface JUInspectorBaseView : NSView { 13 | } 14 | 15 | -(void)setupView; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /diumoo/DMDocument/JUInspectorView/JUInspectorBaseView.m: -------------------------------------------------------------------------------- 1 | // 2 | // JUInspectorBaseView.m 3 | // JUInspectorView 4 | // 5 | // Created by Jon Gilkison on 9/28/11. 6 | // Copyright 2011 Interfacelab LLC. All rights reserved. 7 | // 8 | 9 | #import "JUInspectorBaseView.h" 10 | 11 | 12 | @implementation JUInspectorBaseView 13 | 14 | #pragma mark - Init/Dealloc 15 | 16 | - (id)initWithFrame:(NSRect)frame 17 | { 18 | if((self = [super initWithFrame:frame])) 19 | { 20 | [self setupView]; 21 | } 22 | 23 | return self; 24 | } 25 | 26 | - (id)initWithCoder:(NSCoder *)decoder 27 | { 28 | if((self = [super initWithCoder:decoder])) 29 | { 30 | [self setupView]; 31 | } 32 | 33 | return self; 34 | } 35 | 36 | - (void)setupView 37 | { 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /diumoo/DMDocument/JUInspectorView/JUInspectorView.h: -------------------------------------------------------------------------------- 1 | // 2 | // JUInspectoView.h 3 | // JUInspectorView 4 | // 5 | // Copyright (c) 2011 by Sidney Just 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 7 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation 8 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 9 | // and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 13 | // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 14 | // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 15 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | // 17 | 18 | #import 19 | #import "JUInspectorBaseView.h" 20 | #import "JUInspectorViewHeader.h" 21 | #import "JUInspectorViewContainer.h" 22 | 23 | @interface JUInspectorView : JUInspectorBaseView 24 | { 25 | BOOL expanded; 26 | 27 | NSInteger index; 28 | NSView *body; 29 | 30 | JUInspectorViewHeader *header; 31 | JUInspectorViewContainer *container; 32 | } 33 | 34 | @property (assign, nonatomic) NSString *name; 35 | @property (assign, nonatomic) NSInteger index; 36 | 37 | @property (retain, nonatomic) IBOutlet NSView *body; 38 | @property (assign, nonatomic) BOOL expanded; 39 | 40 | @property (assign, nonatomic) JUInspectorViewContainer *container; 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /diumoo/DMDocument/JUInspectorView/JUInspectorView.m: -------------------------------------------------------------------------------- 1 | // 2 | // JUInspectoView.m 3 | // JUInspectorView 4 | // 5 | // Copyright (c) 2011 by Sidney Just 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 7 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation 8 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 9 | // and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 13 | // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 14 | // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 15 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | // 17 | 18 | #import "JUInspectorView.h" 19 | 20 | @implementation JUInspectorView 21 | 22 | @synthesize name, index, body, expanded, container; 23 | 24 | #pragma mark - Init/Dealloc 25 | 26 | - (void)setupView 27 | { 28 | header = [[JUInspectorViewHeader alloc] initWithFrame:NSZeroRect]; 29 | [header setAutoresizingMask:NSViewWidthSizable]; 30 | header.delegate=self; 31 | 32 | [self addSubview:header]; 33 | [self setExpanded:YES]; 34 | } 35 | 36 | - (void)dealloc 37 | { 38 | [header release]; 39 | [body release]; 40 | 41 | [super dealloc]; 42 | } 43 | 44 | #pragma mark - Properties 45 | 46 | - (void)setExpanded:(BOOL)value 47 | { 48 | if (expanded==value) 49 | return; 50 | 51 | expanded=value; 52 | 53 | NSRect frame; 54 | 55 | if (expanded) 56 | { 57 | frame = [body bounds]; 58 | frame.origin = [self frame].origin; 59 | frame.size.height += [header bounds].size.height; 60 | 61 | [body setHidden:NO]; 62 | [header setState:NSOnState]; 63 | [container arrangeViews]; 64 | } 65 | else 66 | { 67 | frame.origin = [self frame].origin; 68 | frame.size = [header frame].size; 69 | 70 | [body setHidden:YES]; 71 | [header setState:NSOffState]; 72 | } 73 | 74 | [self setFrame:frame]; 75 | [container arrangeViews]; 76 | } 77 | 78 | -(NSString *)name 79 | { 80 | return [header title]; 81 | } 82 | 83 | - (void)setName:(NSString *)value 84 | { 85 | [header setTitle:value]; 86 | } 87 | 88 | - (void)setBody:(NSView *)pbody 89 | { 90 | [body removeFromSuperview]; 91 | [body release]; 92 | 93 | body = [pbody retain]; 94 | 95 | if([body isFlipped]) 96 | { 97 | NSRect bodyFrame = [body bounds]; 98 | bodyFrame.origin.y = [header bounds].size.height + 1.0; 99 | 100 | [body setFrame:bodyFrame]; 101 | } 102 | else 103 | { 104 | NSRect bodyFrame = [body bounds]; 105 | bodyFrame.origin.y = -bodyFrame.size.height + [header bounds].size.height; 106 | 107 | [body setFrame:bodyFrame]; 108 | } 109 | 110 | [self addSubview:body]; 111 | 112 | self.expanded = !expanded; 113 | } 114 | 115 | #pragma mark - NSView Overrides 116 | 117 | - (void)setFrame:(NSRect)frameRect 118 | { 119 | [super setFrame:frameRect]; 120 | 121 | NSRect bodyRect = [body frame]; 122 | bodyRect.size.width = frameRect.size.width; 123 | [body setFrame:bodyRect]; 124 | } 125 | 126 | - (BOOL)isFlipped 127 | { 128 | return YES; 129 | } 130 | 131 | - (void)drawRect:(NSRect)dirtyRect 132 | { 133 | if(expanded) 134 | { 135 | [[header dashColor] set]; 136 | 137 | NSRect dashRect = [self bounds]; 138 | dashRect.origin.x -= 1.0; 139 | dashRect.size.width += 2.0; 140 | 141 | NSBezierPath *path = [NSBezierPath bezierPathWithRect:dashRect]; 142 | [path setLineWidth:1.0]; 143 | [path stroke]; 144 | } 145 | } 146 | 147 | - (NSString *)description 148 | { 149 | return [NSString stringWithFormat:@"JUCollectionView \"%@\", expanded: %@", self.name, expanded ? @"YES" : @"NO"]; 150 | } 151 | 152 | - (NSComparisonResult)compare:(JUInspectorView *)otherView 153 | { 154 | if(otherView.index > index) 155 | return NSOrderedDescending; 156 | 157 | if(otherView.index < index) 158 | return NSOrderedAscending; 159 | 160 | return NSOrderedSame; 161 | } 162 | 163 | #pragma mark - JUInspectorViewHeaderDelegate 164 | 165 | -(void)headerClicked:(JUInspectorViewHeader *)headerView 166 | { 167 | self.expanded=!expanded; 168 | } 169 | 170 | 171 | @end 172 | -------------------------------------------------------------------------------- /diumoo/DMDocument/JUInspectorView/JUInspectorViewContainer.h: -------------------------------------------------------------------------------- 1 | // 2 | // JUInspectorViewContainer.h 3 | // JUInspectorView 4 | // 5 | // Copyright (c) 2011 by Sidney Just 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 7 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation 8 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 9 | // and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 13 | // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 14 | // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 15 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | // 17 | 18 | #import 19 | #import "JUInspectorBaseView.h" 20 | 21 | @class JUInspectorView; 22 | 23 | @interface JUInspectorViewContainer : JUInspectorBaseView 24 | { 25 | NSMutableArray *inspectorViews; 26 | } 27 | 28 | - (void)arrangeViews; 29 | 30 | - (void)addInspectorView:(JUInspectorView *)view expanded:(BOOL)expanded; 31 | - (void)addInspectorView:(JUInspectorView *)view atIndex:(NSInteger)index expanded:(BOOL)expanded; 32 | - (void)removeInspectorView:(JUInspectorView *)view; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /diumoo/DMDocument/JUInspectorView/JUInspectorViewContainer.m: -------------------------------------------------------------------------------- 1 | // 2 | // JUInspectorViewContainer.m 3 | // JUInspectorView 4 | // 5 | // Copyright (c) 2011 by Sidney Just 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 7 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation 8 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 9 | // and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 13 | // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 14 | // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 15 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | // 17 | 18 | #import "JUInspectorViewContainer.h" 19 | #import "JUInspectorView.h" 20 | 21 | @interface JUInspectorView (JUInspectorViewPrivate) 22 | 23 | -(void)updateBounds; 24 | 25 | @end 26 | 27 | @implementation JUInspectorViewContainer 28 | 29 | #pragma mark - Init/Dealloc 30 | 31 | - (void)setupView 32 | { 33 | inspectorViews = [[NSMutableArray alloc] init]; 34 | } 35 | 36 | - (void)dealloc 37 | { 38 | [inspectorViews release]; 39 | [super dealloc]; 40 | } 41 | 42 | #pragma mark - NSView override 43 | 44 | - (void)setFrame:(NSRect)frameRect 45 | { 46 | [super setFrame:frameRect]; 47 | [self arrangeViews]; 48 | } 49 | 50 | - (BOOL)isFlipped 51 | { 52 | return YES; 53 | } 54 | 55 | 56 | #pragma mark - Private methods 57 | 58 | - (void)updateBounds 59 | { 60 | CGFloat height = 0.0; 61 | for(JUInspectorView *view in inspectorViews) 62 | { 63 | height += [view frame].size.height; 64 | } 65 | 66 | NSRect frame; 67 | frame.origin = [self frame].origin; 68 | frame.size.width = [self bounds].size.width; 69 | frame.size.height = height; 70 | 71 | NSClipView *clipView = [[self enclosingScrollView] contentView]; 72 | if(clipView) 73 | { 74 | frame.size.width = [clipView documentRect].size.width; 75 | } 76 | 77 | [super setFrame:frame]; 78 | } 79 | 80 | - (void)arrangeViews 81 | { 82 | NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES]; 83 | [inspectorViews sortUsingDescriptors:@[sortDescriptor]]; 84 | [self updateBounds]; 85 | 86 | 87 | BOOL collapsed = NO; 88 | NSRect frame = NSMakeRect(0.0, 0.0, [self bounds].size.width, 0.0); 89 | 90 | for(JUInspectorView *view in inspectorViews) 91 | { 92 | if(collapsed) 93 | frame.origin.y -= 1.0; 94 | 95 | frame.size.height = [view frame].size.height; 96 | 97 | [view setFrame:frame]; 98 | 99 | frame.origin.y += frame.size.height; 100 | collapsed = !view.expanded; 101 | } 102 | } 103 | 104 | 105 | #pragma mark - Add/Remove Inspectors 106 | 107 | - (void)addInspectorView:(JUInspectorView *)view expanded:(BOOL)expanded 108 | { 109 | if(![inspectorViews containsObject:view]) 110 | { 111 | [view setContainer:self]; 112 | 113 | [inspectorViews addObject:view]; 114 | [self addSubview:view]; 115 | [self arrangeViews]; 116 | 117 | view.expanded=expanded; 118 | } 119 | } 120 | 121 | - (void)addInspectorView:(JUInspectorView *)view atIndex:(NSInteger)index expanded:(BOOL)expanded 122 | { 123 | [view setIndex:index]; 124 | [self addInspectorView:view expanded:expanded]; 125 | } 126 | 127 | - (void)removeInspectorView:(JUInspectorView *)view 128 | { 129 | if([inspectorViews containsObject:view]) 130 | { 131 | [view setContainer:self]; 132 | 133 | [inspectorViews removeObject:view]; 134 | [view removeFromSuperview]; 135 | [self arrangeViews]; 136 | } 137 | } 138 | 139 | 140 | 141 | @end 142 | -------------------------------------------------------------------------------- /diumoo/DMDocument/JUInspectorView/JUInspectorViewHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // JUInspectorView.h 3 | // JUInspectorView 4 | // 5 | // Copyright (c) 2011 by Sidney Just 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 7 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation 8 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 9 | // and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 13 | // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 14 | // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 15 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | // 17 | 18 | #import 19 | #import "JUInspectorBaseView.h" 20 | 21 | @class JUInspectorViewHeader; 22 | 23 | @protocol JUInspectorViewHeaderDelegate 24 | 25 | -(void)headerClicked:(JUInspectorViewHeader *)headerView; 26 | 27 | @end 28 | 29 | @interface JUInspectorViewHeader : JUInspectorBaseView 30 | { 31 | id delegate; 32 | 33 | NSButton *disclosureTriangle; 34 | NSTextField *nameField; 35 | 36 | NSColor *dashColor; 37 | NSColor *gradientStartColor; 38 | NSColor *gradientEndColor; 39 | } 40 | 41 | @property (assign, nonatomic) id delegate; 42 | 43 | @property (readonly) NSButton *disclosureTriangle; 44 | 45 | @property (retain, nonatomic) NSColor *dashColor; 46 | @property (retain, nonatomic) NSColor *gradientStartColor; 47 | @property (retain, nonatomic) NSColor *gradientEndColor; 48 | 49 | @property (assign, nonatomic) NSInteger state; 50 | @property (assign, nonatomic) NSString *title; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /diumoo/DMDocument/JUInspectorView/JUInspectorViewHeader.m: -------------------------------------------------------------------------------- 1 | // 2 | // JUInspectorView.m 3 | // JUInspectorView 4 | // 5 | // Copyright (c) 2011 by Sidney Just 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 7 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation 8 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 9 | // and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 13 | // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 14 | // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 15 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | // 17 | 18 | #import "JUInspectorViewHeader.h" 19 | 20 | @interface JUInspectorViewHeader(Private) 21 | 22 | -(void)setupView; 23 | -(void)disclosureClicked:(id)sender; 24 | 25 | @end 26 | 27 | @implementation JUInspectorViewHeader 28 | 29 | @synthesize dashColor, gradientStartColor, gradientEndColor, disclosureTriangle, state, title, delegate; 30 | 31 | #pragma mark - Init/Dealloc 32 | 33 | - (id)initWithCoder:(NSCoder *)decoder 34 | { 35 | return [self initWithFrame:NSZeroRect]; 36 | } 37 | 38 | - (id)initWithFrame:(NSRect)frame 39 | { 40 | if((self = [super initWithFrame:NSMakeRect(0.0, 0.0, 222.0, 20.0)])) 41 | { 42 | } 43 | 44 | return self; 45 | } 46 | 47 | 48 | - (void)setupView 49 | { 50 | dashColor = [[NSColor colorWithCalibratedRed:0.502 green:0.502 blue:0.502 alpha:1.0] retain]; 51 | gradientStartColor = [[NSColor colorWithCalibratedRed:0.922 green:0.925 blue:0.976 alpha:1.0] retain]; 52 | gradientEndColor = [[NSColor colorWithCalibratedRed:0.741 green:0.749 blue:0.831 alpha:1.0] retain]; 53 | 54 | disclosureTriangle = [[NSButton alloc] initWithFrame:NSMakeRect(5.0, 4.0, 13.0, 13.0)]; 55 | [disclosureTriangle setBezelStyle:NSDisclosureBezelStyle]; 56 | [disclosureTriangle setButtonType: NSPushOnPushOffButton]; 57 | [disclosureTriangle setTitle:nil]; 58 | [disclosureTriangle highlight:NO]; 59 | [disclosureTriangle setTarget:self]; 60 | [disclosureTriangle setAction:@selector(disclosureClicked:)]; 61 | 62 | nameField = [[NSTextField alloc] initWithFrame:NSMakeRect(20.0, 4.0, [self bounds].size.width - 8.0, 15.0)]; 63 | [nameField setEditable:NO]; 64 | [nameField setBackgroundColor:[NSColor clearColor]]; 65 | [nameField setBezeled:NO]; 66 | [nameField setFont:[NSFont boldSystemFontOfSize:12.0]]; 67 | [nameField setTextColor:[NSColor colorWithCalibratedRed:0.220 green:0.224 blue:0.231 alpha:1.0]]; 68 | 69 | 70 | [self addSubview:disclosureTriangle]; 71 | [self addSubview:nameField]; 72 | } 73 | 74 | - (void)dealloc 75 | { 76 | [dashColor release]; 77 | [gradientEndColor release]; 78 | [gradientStartColor release]; 79 | [disclosureTriangle release]; 80 | [nameField release]; 81 | 82 | [super dealloc]; 83 | } 84 | 85 | #pragma mark - Properties 86 | 87 | -(NSInteger)state 88 | { 89 | return [disclosureTriangle state]; 90 | } 91 | 92 | - (void)setState:(NSInteger)value 93 | { 94 | [disclosureTriangle setState:value]; 95 | } 96 | 97 | -(NSString *)title 98 | { 99 | return [nameField stringValue]; 100 | } 101 | 102 | - (void)setTitle:(NSString *)value 103 | { 104 | [nameField setStringValue:value]; 105 | } 106 | 107 | #pragma mark - Drawing 108 | 109 | - (void)drawRect:(NSRect)dirtyRect 110 | { 111 | NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:gradientStartColor endingColor:gradientEndColor]; 112 | [gradient drawInRect:[self bounds] angle:-90.0]; 113 | [gradient autorelease]; 114 | 115 | [dashColor set]; 116 | 117 | NSRect dashRect = [self bounds]; 118 | dashRect.origin.x -= 1.0; 119 | dashRect.size.width += 2.0; 120 | 121 | NSBezierPath *path = [NSBezierPath bezierPathWithRect:dashRect]; 122 | [path setLineWidth:1.0]; 123 | [path stroke]; 124 | } 125 | 126 | #pragma mark - Event handling 127 | 128 | - (void)mouseUp:(NSEvent *)theEvent 129 | { 130 | [delegate headerClicked:self]; 131 | } 132 | 133 | 134 | -(void)disclosureClicked:(id)sender 135 | { 136 | [delegate headerClicked:self]; 137 | } 138 | 139 | @end 140 | -------------------------------------------------------------------------------- /diumoo/core/DMControlCenter/DMControlCenter.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMControlCenter.h 3 | // diumoo-core 4 | // 5 | // Created by Shanzi on 12-6-3. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #define kPauseOperationTypePass @"pass" 10 | #define kPauseOperationTypeSkip @"skip" 11 | #define kPauseOperationTypeFetchNewPlaylist @"newplaylist" 12 | #define kPauseOperationTypePlaySpecial @"special" 13 | 14 | #define kTimerPulseTypePlay @"kTimerPulseTypePlay" 15 | #define kTimerPulseTypePause @"kTimerPulseTypePause" 16 | #define KTimerPulseTypeVolumeChange @"kTimerPulseTypeVolumeChange" 17 | 18 | #import 19 | #import 20 | #import 21 | 22 | #import "diumoo-Swift.h" 23 | #import "DMPanelWindowController.h" 24 | #import "DMPlayRecordHandler.h" 25 | #import "NSDictionary+UrlEncoding.h" 26 | #import "DMService.h" 27 | #import "DMSearchPanelController.h" 28 | 29 | #define TIMER_INTERVAL 0.1 30 | 31 | 32 | typedef enum{ 33 | PAUSE_PASS = 0, 34 | PAUSE_PAUSE, 35 | PAUSE_SKIP, 36 | PAUSE_NEW_PLAYLIST, 37 | PAUSE_SPECIAL, 38 | PAUSE_EXIT, 39 | } PAUSE_OPERATION_TYPE; 40 | 41 | @interface DMControlCenter : NSObject 42 | { 43 | NSString *channel; 44 | 45 | DMPlayableItem *__strong playingItem; 46 | DMPlayableItem *__strong waitingItem; 47 | DMPlaylistFetcher *fetcher; 48 | 49 | DMNotificationCenter *notificationCenter; 50 | DMPanelWindowController *diumooPanel; 51 | DMPlayRecordHandler *recordHandler; 52 | 53 | BOOL canPlaySpecial; 54 | } 55 | 56 | @property (strong) DMPlayableItem *playingItem; 57 | @property (strong) DMPlayableItem *waitingItem; 58 | @property DMPanelWindowController *diumooPanel; 59 | 60 | //self methods 61 | -(void) fireToPlayDefault; 62 | -(void) stopForExit; 63 | -(void) qualityChanged; 64 | -(void) volumeChange:(float)volume; 65 | 66 | //methods in DMPlayableItemDelegate 67 | - (void)playableItem:(DMPlayableItem * )item logStateChanged:(NSInteger)logStateChanged; 68 | @end 69 | -------------------------------------------------------------------------------- /diumoo/core/DMErrorLog/DMErrorLog.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMErrorLog.h 3 | // diumoo 4 | // 5 | // Created by AnakinGWY on 12-8-10. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface DMErrorLog : NSObject 12 | +(id) sharedErrorLog; 13 | +(void) logErrorWith:(id)object method:(SEL)methodName andError:(NSError*)error; 14 | +(void) logStateWith:(id)object fromMethod:(SEL)methodName andString:(NSString*)aString; 15 | @end 16 | -------------------------------------------------------------------------------- /diumoo/core/DMErrorLog/DMErrorLog.m: -------------------------------------------------------------------------------- 1 | // 2 | // DMErrorLog.m 3 | // diumoo 4 | // 5 | // Created by AnakinGWY on 12-8-10. 6 | // 7 | // 8 | 9 | #import "DMErrorLog.h" 10 | 11 | static DMErrorLog *sharedErrorLogger; 12 | 13 | @implementation DMErrorLog 14 | 15 | +(id) sharedErrorLog 16 | { 17 | if ([[[NSUserDefaults standardUserDefaults] valueForKey:@"enableLog"] integerValue] == NSOnState) { 18 | sharedErrorLogger = [[DMErrorLog alloc] init]; 19 | } else 20 | sharedErrorLogger = nil; 21 | 22 | return sharedErrorLogger; 23 | } 24 | 25 | +(void) logErrorWith:(id)object method:(SEL)methodName andError:(NSError *)error 26 | { 27 | if (sharedErrorLogger != nil) { 28 | NSLog(@"ERROR Log: %@ %@ returns an Error = %@",object,NSStringFromSelector(methodName),error); 29 | } 30 | } 31 | 32 | +(void) logStateWith:(id)object fromMethod:(SEL)methodName andString:(NSString *)aString 33 | { 34 | if (sharedErrorLogger != nil) { 35 | NSLog(@"State Log: %@ %@ has a state = %@",object,NSStringFromSelector(methodName),aString); 36 | } 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /diumoo/core/DMNotificationCenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DMNotificationCenter.swift 3 | // diumoo 4 | // 5 | // Created by Yancheng Zheng on 6/18/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import AppKit 11 | 12 | public class DMNotificationCenter : NSObject, NSUserNotificationCenterDelegate { 13 | 14 | internal let pref = UserDefaults.standard 15 | internal let NCCenter = NSUserNotificationCenter.default 16 | internal let needToUpdateDock : Bool 17 | 18 | public func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent notification: NSUserNotification) -> Bool { 19 | return true 20 | } 21 | 22 | override init() { 23 | // Show Dock icon setting won't change without restart app, so it's safe to put it here 24 | // I'm not sure about how expensive this is 25 | needToUpdateDock = (Int(self.pref.value(forKey: "displayAlbumCoverOnDock") as! NSNumber) == NSOnState) 26 | super.init() 27 | NSUserNotificationCenter.default.delegate = self 28 | } 29 | 30 | deinit { 31 | NSUserNotificationCenter.default.delegate = nil 32 | } 33 | 34 | func isNotificationON() -> Bool { 35 | return Int(self.pref.value(forKey: "enableNotification") as! NSNumber) == NSOnState 36 | } 37 | 38 | public func notifyMusicPlayback(withItem item: DMPlayableItem) { 39 | if isNotificationON() { 40 | let detail = String("\(item.musicInfo["artist"]!) - <\(item.musicInfo["albumtitle"]!)>") 41 | let aNotification = NSUserNotification.init() 42 | aNotification.title = item.musicInfo["title"] as? String 43 | aNotification.informativeText = detail 44 | aNotification.contentImage = item.cover 45 | aNotification.soundName = nil 46 | self.NCCenter.deliver(aNotification) 47 | } 48 | 49 | if self.needToUpdateDock { 50 | let image = DockImageProvider.albumImage(from: item) 51 | NSApplication.shared().applicationIconImage = image 52 | } 53 | } 54 | 55 | public func notifyBitrate() { 56 | if isNotificationON() { 57 | let title = NSLocalizedString("BITRATE_CHANGED", comment: "Music bitrate changed.") 58 | var detail = NSLocalizedString("BITRATE_CHANGED_TO_VALUE", comment: "Music bitrate changed to.") 59 | let quality = pref.value(forKey: "musicQuality") as? Int 60 | detail.append(String("\(quality!) Kbps")) 61 | 62 | let aNotification = NSUserNotification.init() 63 | aNotification.title = title 64 | aNotification.informativeText = detail 65 | aNotification.soundName = nil 66 | 67 | self.NCCenter.deliver(aNotification) 68 | 69 | } 70 | } 71 | 72 | public func clearNotifications() { 73 | self.NCCenter.removeAllDeliveredNotifications() 74 | } 75 | 76 | public func copylinkNotification (URLStr: String) { 77 | if isNotificationON() { 78 | let title = NSLocalizedString("SHARE_LINK_TITLE", comment: "share link") 79 | 80 | let aNotification = NSUserNotification.init() 81 | 82 | aNotification.title = title 83 | aNotification.informativeText = URLStr 84 | aNotification.soundName = nil 85 | 86 | self.NCCenter.deliver(aNotification) 87 | } 88 | 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /diumoo/core/DMPlayRecordHandler/DMPlayRecordHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMPlayRecordHandler.h 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-7-2. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define kPlaySongNotificationName @"playsong" 12 | #define kPlayAlbumNotificationName @"playalbum" 13 | 14 | @protocol DMPlayRecordHandlerDelegate 15 | -(void) playSongWithSid:(NSString*)sid andSsid:(NSString*)ssid; 16 | @end 17 | 18 | @class DMPlayableItem; 19 | 20 | @interface DMPlayRecordHandler : NSObject 21 | { 22 | NSURL *recordFileURL; 23 | NSManagedObjectContext *context; 24 | id delegate; 25 | } 26 | @property(nonatomic,copy) NSURL* recordFileURL; 27 | @property(nonatomic) NSManagedObjectContext* context; 28 | @property(nonatomic) id delegate; 29 | 30 | +(DMPlayRecordHandler*) sharedRecordHandler; 31 | -(NSManagedObject*) songWithSid:(NSString*) sid; 32 | -(NSManagedObject*) addRecordWithItem:(DMPlayableItem*) item; 33 | -(BOOL) addRecordWithDict:(NSDictionary*) dict; 34 | -(void) open; 35 | -(void) save; 36 | -(void) removeCurrentVersion; 37 | -(void) playSongWith:(NSString*)sid andSsid:(NSString*) ssid; 38 | -(NSArray*) allSongs; 39 | -(void) removeVersionsToLimit; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /diumoo/core/DMPlayRecordHandler/DMPlayRecordHandler.m: -------------------------------------------------------------------------------- 1 | // 2 | // DMPlayRecordHandler.m 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-7-2. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "DMPlayRecordHandler.h" 10 | #import "diumoo-Swift.h" 11 | #import "DMService.h" 12 | #import "DMErrorLog.h" 13 | 14 | static DMPlayRecordHandler* recordHandler; 15 | 16 | @implementation DMPlayRecordHandler 17 | @synthesize recordFileURL,context,delegate; 18 | 19 | #pragma class methods 20 | +(DMPlayRecordHandler*) sharedRecordHandler 21 | { 22 | if (recordHandler==nil) { 23 | recordHandler = [[DMPlayRecordHandler alloc] init]; 24 | } 25 | return recordHandler; 26 | } 27 | 28 | #pragma --- 29 | 30 | #pragma init and dealloc 31 | -(id) init 32 | { 33 | if (self = [super init]) { 34 | NSString* pathToFolder = [DMService pathToDataFileFolder]; 35 | NSString* recordFilePath = [pathToFolder stringByAppendingPathComponent:@"record.dmsid"]; 36 | recordFileURL = [NSURL fileURLWithPath:recordFilePath]; 37 | context = [self makeContextWithPath:pathToFolder]; 38 | } 39 | return self; 40 | } 41 | 42 | #pragma --- 43 | 44 | #pragma private methods 45 | -(NSManagedObjectContext*) makeContextWithPath:(NSString*) datapath 46 | { 47 | datapath = [datapath stringByAppendingPathComponent:@"dmdata.db"]; 48 | 49 | NSManagedObjectModel* model = [NSManagedObjectModel mergedModelFromBundles:nil]; 50 | 51 | NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] 52 | initWithManagedObjectModel:model]; 53 | 54 | NSError *error; 55 | 56 | [coordinator addPersistentStoreWithType:NSSQLiteStoreType 57 | configuration:nil 58 | URL:[NSURL fileURLWithPath:datapath] 59 | options:nil 60 | error:&error]; 61 | if (error) { 62 | [DMErrorLog logErrorWith:self method:_cmd andError:error]; 63 | [[NSFileManager defaultManager] removeItemAtPath:datapath error:nil]; 64 | } 65 | 66 | context = [[NSManagedObjectContext alloc] initWithConcurrencyType: 67 | NSPrivateQueueConcurrencyType]; 68 | 69 | [context setPersistentStoreCoordinator:coordinator]; 70 | 71 | return context; 72 | } 73 | #pragma --- 74 | 75 | 76 | -(NSManagedObject*) songWithSid:(NSString*) sid 77 | { 78 | 79 | NSFetchRequest* fetchRequset = [NSFetchRequest fetchRequestWithEntityName:@"Song"]; 80 | [fetchRequset setPredicate:[NSPredicate predicateWithFormat:@"sid = %@",sid]]; 81 | 82 | NSError* fetchErr = nil; 83 | NSArray* results = [context executeFetchRequest:fetchRequset error:&fetchErr]; 84 | 85 | if ([results count]>0) { 86 | // 找到了之前的记录 87 | return results[0]; 88 | } 89 | return nil; 90 | } 91 | 92 | -(NSManagedObject*) addRecordWithItem:(DMPlayableItem *)item 93 | { 94 | if (item.musicInfo[@"ssid"] == nil) { 95 | return nil; 96 | } 97 | 98 | NSManagedObject* song = [self songWithSid:item.musicInfo[@"sid"]]; 99 | 100 | if (song) { 101 | [song setValue:[NSDate date] forKey:@"date"]; 102 | } 103 | else { 104 | // 没有找到之前的记录 105 | NSDate* date = [NSDate date]; 106 | 107 | song = [NSEntityDescription insertNewObjectForEntityForName:@"Song" 108 | inManagedObjectContext:context]; 109 | [song setValue:item.musicInfo[@"sid"] forKey:@"sid"]; 110 | [song setValue:item.musicInfo[@"ssid"] forKey:@"ssid"]; 111 | [song setValue:item.musicInfo[@"aid"] forKey:@"aid"]; 112 | [song setValue:item.musicInfo[@"title"] forKey:@"title"]; 113 | [song setValue:item.musicInfo[@"albumtitle"] forKey:@"albumtitle"]; 114 | [song setValue:item.musicInfo[@"artist"] forKey:@"artist"]; 115 | [song setValue:item.musicInfo[@"largePictureLocation"] forKey:@"picture"]; 116 | [song setValue:item.musicInfo[@"albumLocation"] forKey:@"url"]; 117 | [song setValue:@([item.musicInfo[@"rating_avg"] floatValue]) forKey:@"rating_avg"]; 118 | [song setValue:date forKey:@"date"]; 119 | 120 | [context save:nil]; 121 | } 122 | 123 | if ([[NSFileManager defaultManager] fileExistsAtPath:self.recordFileURL.path]) { 124 | 125 | [NSFileVersion addVersionOfItemAtURL: self.recordFileURL 126 | withContentsOfURL: self.recordFileURL 127 | options: 0 128 | error: nil]; 129 | } 130 | 131 | [item.musicInfo[@"sid"] writeToURL:self.recordFileURL atomically:YES encoding:NSASCIIStringEncoding error:nil]; 132 | 133 | return song; 134 | } 135 | 136 | -(BOOL) addRecordWithDict:(NSDictionary *)dict 137 | { 138 | NSString* sid = dict[@"sid"]; 139 | NSString* ssid = dict[@"ssid"]; 140 | NSString* aid = dict[@"aid"]; 141 | NSString* date = dict[@"date"]; 142 | NSString* picture = dict[@"picture"]; 143 | NSString* albumtitle = dict[@"albumtitle"]; 144 | NSString* rating_avg = dict[@"rating_avg"]; 145 | NSString* title = dict[@"title"]; 146 | NSString* url = dict[@"url"]; 147 | NSString* artist = dict[@"artist"]; 148 | 149 | NSManagedObject* theSong = [self songWithSid:sid]; 150 | if (theSong) { 151 | return YES; 152 | } 153 | else if (sid && ssid && aid && date && picture && albumtitle && rating_avg 154 | && title && url && artist 155 | ) 156 | { 157 | NSManagedObject* song = [NSEntityDescription 158 | insertNewObjectForEntityForName:@"Song" 159 | inManagedObjectContext:context]; 160 | [song setValue:sid forKey:@"sid"]; 161 | [song setValue:ssid forKey:@"ssid"]; 162 | [song setValue:aid forKey:@"aid"]; 163 | [song setValue:title forKey:@"title"]; 164 | [song setValue:albumtitle forKey:@"albumtitle"]; 165 | [song setValue:artist forKey:@"artist"]; 166 | [song setValue:picture forKey:@"picture"]; 167 | [song setValue:url forKey:@"url"]; 168 | [song setValue:rating_avg forKey:@"rating_avg"]; 169 | [song setValue:date forKey:@"date"]; 170 | return YES; 171 | } 172 | return NO; 173 | } 174 | 175 | -(void) open 176 | { 177 | NSDocumentController* controller =[NSDocumentController sharedDocumentController]; 178 | NSArray* documents = [controller documents]; 179 | if ([documents count]>0) { 180 | [documents makeObjectsPerformSelector:@selector(close)]; 181 | } 182 | 183 | [controller openDocumentWithContentsOfURL:self.recordFileURL 184 | display:YES 185 | error:nil]; 186 | } 187 | 188 | -(void) removeCurrentVersion 189 | { 190 | NSFileVersion* current = [NSFileVersion currentVersionOfItemAtURL:self.recordFileURL]; 191 | [current removeAndReturnError:nil]; 192 | } 193 | 194 | -(void) playSongWith:(NSString *)sid andSsid:(NSString *)ssid 195 | { 196 | [self.delegate playSongWithSid:sid andSsid:ssid]; 197 | } 198 | 199 | 200 | -(void) save 201 | { 202 | [context save:nil]; 203 | } 204 | 205 | -(NSArray*)allSongs 206 | { 207 | NSFetchRequest* fetchRequset = [NSFetchRequest fetchRequestWithEntityName:@"Song"]; 208 | return [context executeFetchRequest:fetchRequset error:nil]; 209 | } 210 | 211 | -(void) removeVersionsToLimit{ 212 | 213 | if ([[NSFileManager defaultManager] fileExistsAtPath:self.recordFileURL.path]) { 214 | NSInteger limit = [[[NSUserDefaults standardUserDefaults] valueForKey:@"versionsLimit"] integerValue]; 215 | NSArray* versions = [NSFileVersion otherVersionsOfItemAtURL:self.recordFileURL]; 216 | for (NSInteger i = ([versions count] - 1); i > limit; i--) { 217 | [versions[i] removeAndReturnError:nil]; 218 | } 219 | } 220 | [context save:nil]; 221 | } 222 | 223 | 224 | @end 225 | -------------------------------------------------------------------------------- /diumoo/core/DMPlayRecordHandler/DMPlayRecordModel.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | DMPlayRecordModel.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /diumoo/core/DMPlayRecordHandler/DMPlayRecordModel.xcdatamodeld/DMPlayRecordModel.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /diumoo/core/DMPlayableItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DMPlayableItem.swift 3 | // diumoo 4 | // 5 | // Created by Anakin(Yancheng) Zheng on 6/18/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import AVFoundation 11 | import AppKit 12 | 13 | @objc public enum ItemPlayState : Int { 14 | case waitToPlay, playing, playing_and_will_replay, replaying, replayed 15 | } 16 | 17 | @objc public protocol DMPlayableItemDelegate { 18 | func playableItem(_ item: DMPlayableItem, logStateChanged: Int) -> (Void) 19 | } 20 | 21 | public class DMPlayableItem: AVPlayerItem { 22 | // Interface variables 23 | public var cover : NSImage? 24 | public var isUsingDefaultFailedAlbumPhoto: Bool = false 25 | public var like : Bool 26 | private(set) public var musicInfo : [String: AnyObject]! 27 | public var playState: ItemPlayState 28 | public var delegate: DMPlayableItemDelegate? 29 | 30 | public var floatDuration: Float { 31 | get { 32 | return Float(CMTimeGetSeconds(self.asset.duration)) 33 | } 34 | } 35 | 36 | // Const variables 37 | static let douban_URL_prefix = "https://music.douban.com" 38 | 39 | init(WithDict aDict: Dictionary) { 40 | self.musicInfo = [ "subtype":aDict["subtype"]!, 41 | "title":aDict["title"]!, 42 | "artist":aDict["artist"]!, 43 | "albumtitle":aDict["albumtitle"]!, 44 | "musicLocation":aDict["url"]!, 45 | "pictureLocation":aDict["picture"]!] 46 | 47 | let pic = String(describing: aDict["picture"]!) 48 | self.musicInfo["largePictureLocation"] = pic.replacingOccurrences(of:"mpic", with:"lpic") as AnyObject? 49 | 50 | if aDict["aid"] != nil { 51 | self.musicInfo["aid"] = aDict["aid"]! 52 | self.musicInfo["sid"] = aDict["sid"]! 53 | self.musicInfo["ssid"] = aDict["ssid"]! 54 | self.musicInfo["length"] = Float(String(describing: aDict["length"]!))! * 1000 as AnyObject 55 | self.musicInfo["albumLocation"] = String("\(DMPlayableItem.douban_URL_prefix)\(String(describing: aDict["album"]!))") as AnyObject? 56 | } 57 | 58 | self.like = NSString(string: String(describing: aDict["like"]!)).boolValue 59 | self.playState = ItemPlayState.waitToPlay 60 | self.cover = nil 61 | 62 | let dictURL = String(describing: aDict["url"]!) 63 | let aURL = URL(string: dictURL) 64 | let aAsset = AVAsset(url: aURL!) 65 | super.init(asset: aAsset, automaticallyLoadedAssetKeys: nil) 66 | 67 | self.addObserver(self, forKeyPath:"status", options: NSKeyValueObservingOptions(rawValue: UInt(0)), context: nil) 68 | } 69 | 70 | public func invalidItem() { 71 | self.playState = ItemPlayState.waitToPlay 72 | self.removeObserver(self, forKeyPath:"status") 73 | } 74 | 75 | override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 76 | if keyPath == "status" { 77 | print("\(#function) :: \(musicInfo["title"]!) status changed to \(self.status)") 78 | self.delegate?.playableItem(self, logStateChanged: self.status.rawValue) 79 | } 80 | } 81 | 82 | public func shareAttributeWithChannel(_ channel: String) -> String? { 83 | if self.musicInfo["ssid"] != nil { 84 | return String(format:"%@g%@g%@",String(describing: self.musicInfo["sid"]), String(describing: self.musicInfo["ssid"]), channel) 85 | } else { 86 | return nil 87 | } 88 | } 89 | 90 | public func prepareCoverWithCallbackBlock(_ block: @escaping (NSImage?)->Void) { 91 | if self.cover != nil { 92 | block(self.cover!) 93 | return 94 | } 95 | 96 | let strURL = String(describing: musicInfo["largePictureLocation"]!) 97 | let aURL = URL(string: strURL) 98 | let request = URLRequest(url: aURL!, cachePolicy: .useProtocolCachePolicy , timeoutInterval: 5.0) 99 | 100 | let session = URLSession.shared.dataTask(with: request) { data, response, error in 101 | if error != nil || data == nil { 102 | print("\(#function) failed to get album image with reason \(error)") 103 | self.cover = #imageLiteral(resourceName: "albumfail") 104 | self.isUsingDefaultFailedAlbumPhoto = true 105 | } else { 106 | self.cover = NSImage(data: data!) 107 | self.isUsingDefaultFailedAlbumPhoto = false 108 | } 109 | block(self.cover) 110 | } 111 | session.resume() 112 | } 113 | 114 | deinit { 115 | self.delegate = nil 116 | self.invalidItem() 117 | } 118 | 119 | class func playableItem(WithDictionary aDict: Dictionary) -> DMPlayableItem { 120 | return DMPlayableItem.init(WithDict: aDict) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /diumoo/core/DMService/DMService.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMService.h 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-8-3. 6 | // 7 | // 8 | 9 | #import 10 | #import "NSData+AESCrypt.h" 11 | #import "NSData+Base64.h" 12 | #import "DMErrorLog.h" 13 | 14 | 15 | #define CURRENT_VERSION 1 16 | 17 | 18 | #define SERVICE_KEY @"-%ZxYx1@99D|ifwN{>*u'<.Rqw]6I:j'" 19 | #define REGISTER_SONG_SERVICE_URL @"http://127.0.0.1:8000/registersong/" 20 | 21 | @interface DMService : NSObject 22 | 23 | +(NSOperationQueue*) serviceQueue; 24 | 25 | +(void)performOnServiceQueue:(void(^)(void))block; 26 | +(void)performOnMainQueue:(void(^)(void))block; 27 | +(NSString*) cleanStartAttribute:(NSString*)start; 28 | +(BOOL) openDiumooLink:(NSString*)url; 29 | +(void) importRecordOperation; 30 | +(void) importRecordOperationWithFilePath:(NSURL*)fp; 31 | +(void) exportRecordOperation; 32 | +(void) showDMNotification; 33 | +(void) shareLinkWithDictionary:(NSDictionary*) dict callback:(void(^)(NSString* url)) block; 34 | +(NSString*) pathToDataFileFolder; 35 | @end 36 | -------------------------------------------------------------------------------- /diumoo/core/DMShortcutsHandler/DMShortcutsHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMShortcutsHandler.h 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-7-24. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define keyPlayShortcut @"playShortcut" 12 | #define keySkipShortcut @"skipShortcut" 13 | #define keyRateShortcut @"rateShortcut" 14 | #define keyBanShortcut @"banShortcut" 15 | #define keyShowPrefsPanel @"showPrefsPanel" 16 | #define keyTogglePanelShortcut @"togglePanelShortcut" 17 | 18 | 19 | @interface DMShortcutsHandler : NSObject 20 | 21 | +(void) registrationShortcuts; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /diumoo/core/DMShortcutsHandler/DMShortcutsHandler.m: -------------------------------------------------------------------------------- 1 | // 2 | // DMShortcutsHandler.m 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-7-24. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "DMShortcutsHandler.h" 10 | #import "Shortcut.h" 11 | #import "DMAppDelegate.h" 12 | 13 | @implementation DMShortcutsHandler 14 | 15 | +(void)registrationShortcuts 16 | { 17 | 18 | 19 | [[MASShortcutBinder sharedBinder] bindShortcutWithDefaultsKey:keyPlayShortcut 20 | toAction:^{ 21 | [[NSApp delegate] 22 | performSelector:@selector(keyShortcuts:) 23 | withObject:keyPlayShortcut]; 24 | }]; 25 | [[MASShortcutBinder sharedBinder] bindShortcutWithDefaultsKey:keySkipShortcut 26 | toAction:^{ 27 | [[NSApp delegate] 28 | performSelector:@selector(keyShortcuts:) 29 | withObject:keySkipShortcut]; 30 | }]; 31 | 32 | 33 | [[MASShortcutBinder sharedBinder] bindShortcutWithDefaultsKey:keyRateShortcut 34 | toAction:^{ 35 | [[NSApp delegate] 36 | performSelector:@selector(keyShortcuts:) 37 | withObject:keyRateShortcut]; 38 | }]; 39 | 40 | [[MASShortcutBinder sharedBinder] bindShortcutWithDefaultsKey:keyBanShortcut 41 | toAction:^{ 42 | [[NSApp delegate] 43 | performSelector:@selector(keyShortcuts:) 44 | withObject:keyBanShortcut]; 45 | }]; 46 | [[MASShortcutBinder sharedBinder] bindShortcutWithDefaultsKey:keyTogglePanelShortcut 47 | toAction:^{ 48 | [[NSApp delegate] 49 | performSelector:@selector(keyShortcuts:) 50 | withObject:keyTogglePanelShortcut]; 51 | }]; 52 | [[MASShortcutBinder sharedBinder] bindShortcutWithDefaultsKey:keyShowPrefsPanel 53 | toAction:^{ 54 | [[NSApp delegate] 55 | performSelector:@selector(keyShortcuts:) 56 | withObject:keyShowPrefsPanel]; 57 | }]; 58 | 59 | 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /diumoo/core/DMUtils/Dictionary+urlEncoding.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Dictionary+urlEncoding.swift 3 | // diumoo 4 | // 5 | // Created by Yancheng Zheng on 6/18/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension Dictionary { 12 | 13 | static func toString(_ object: Any) -> String { 14 | return String(describing: object); 15 | } 16 | 17 | static func urlEncode(_ object: Any) -> String { 18 | let inputString = toString(object); 19 | return inputString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)! 20 | } 21 | 22 | 23 | func urlEncodedString() -> String { 24 | var parts : Array = [] 25 | for (key, value) in self { 26 | let encodedkey = Dictionary.urlEncode(key) 27 | let encodedVal = Dictionary.urlEncode(value) 28 | let part = String("\(encodedkey)=\(encodedVal)") 29 | parts.append(part!) 30 | } 31 | return parts.joined(separator: "&") 32 | } 33 | 34 | func hString() -> String { 35 | var parts : Array = [] 36 | for (key, value) in self { 37 | let part = String("\(Dictionary.toString(key)):\(Dictionary.toString(value))") 38 | parts.append(part!) 39 | } 40 | return parts.joined(separator: "|") 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /diumoo/core/DMUtils/NSData+AESCrypt.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+AESCrypt.h 3 | // DMAESCryptTEST 4 | // 5 | // Created by Shanzi on 12-8-2. 6 | // Copyright (c) 2012年 Shanzi. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSData (AESCrypt) 12 | 13 | -(NSData *)AES256EncryptWithKey:(NSString *)key; 14 | -(NSData *)AES256DecryptWithKey:(NSString *)key; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /diumoo/core/DMUtils/NSData+AESCrypt.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "NSData+AESCrypt.h" 3 | 4 | @implementation NSData (AESCrypt) 5 | 6 | - (NSData *)AES256EncryptWithKey:(NSString *)key { 7 | // 'key' should be 32 bytes for AES256, will be null-padded otherwise 8 | char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused) 9 | bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding) 10 | 11 | // fetch key data 12 | [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; 13 | 14 | NSUInteger dataLength = [self length]; 15 | 16 | //See the doc: For block ciphers, the output size will always be less than or 17 | //equal to the input size plus the size of one block. 18 | //That's why we need to add the size of one block here 19 | size_t bufferSize = dataLength + kCCBlockSizeAES128; 20 | void *buffer = malloc(bufferSize); 21 | 22 | size_t numBytesEncrypted = 0; 23 | CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, 24 | keyPtr, kCCKeySizeAES256, 25 | "\x00 34 | 35 | @interface NSData (Base64) 36 | 37 | + (NSData *)dataWithBase64EncodedString:(NSString *)string; 38 | - (NSString *)base64EncodedStringWithWrapWidth:(NSUInteger)wrapWidth; 39 | - (NSString *)base64EncodedString; 40 | 41 | @end -------------------------------------------------------------------------------- /diumoo/core/DMUtils/NSData+Base64.m: -------------------------------------------------------------------------------- 1 | // 2 | // Base64.m 3 | // 4 | // Version 1.1 5 | // 6 | // Created by Nick Lockwood on 12/01/2012. 7 | // Copyright (C) 2012 Charcoal Design 8 | // 9 | // Distributed under the permissive zlib License 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/nicklockwood/Base64 13 | // 14 | // This software is provided 'as-is', without any express or implied 15 | // warranty. In no event will the authors be held liable for any damages 16 | // arising from the use of this software. 17 | // 18 | // Permission is granted to anyone to use this software for any purpose, 19 | // including commercial applications, and to alter it and redistribute it 20 | // freely, subject to the following restrictions: 21 | // 22 | // 1. The origin of this software must not be misrepresented; you must not 23 | // claim that you wrote the original software. If you use this software 24 | // in a product, an acknowledgment in the product documentation would be 25 | // appreciated but is not required. 26 | // 27 | // 2. Altered source versions must be plainly marked as such, and must not be 28 | // misrepresented as being the original software. 29 | // 30 | // 3. This notice may not be removed or altered from any source distribution. 31 | // 32 | 33 | #import "NSData+Base64.h" 34 | 35 | 36 | #import 37 | #if !__has_feature(objc_arc) 38 | #error This library requires automatic reference counting 39 | #endif 40 | 41 | 42 | @implementation NSData (Base64) 43 | 44 | + (NSData *)dataWithBase64EncodedString:(NSString *)string 45 | { 46 | const char lookup[] = 47 | { 48 | 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 49 | 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 50 | 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 62, 99, 99, 99, 63, 51 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 99, 99, 99, 99, 99, 99, 52 | 99, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 53 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 99, 99, 99, 99, 99, 54 | 99, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 55 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 99, 99, 99, 99, 99 56 | }; 57 | 58 | NSData *inputData = [string dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; 59 | long long inputLength = [inputData length]; 60 | const unsigned char *inputBytes = [inputData bytes]; 61 | 62 | long long maxOutputLength = (inputLength / 4 + 1) * 3; 63 | NSMutableData *outputData = [NSMutableData dataWithLength:maxOutputLength]; 64 | unsigned char *outputBytes = (unsigned char *)[outputData mutableBytes]; 65 | 66 | int accumulator = 0; 67 | long long outputLength = 0; 68 | unsigned char accumulated[] = {0, 0, 0, 0}; 69 | for (long long i = 0; i < inputLength; i++) 70 | { 71 | unsigned char decoded = lookup[inputBytes[i] & 0x7F]; 72 | if (decoded != 99) 73 | { 74 | accumulated[accumulator] = decoded; 75 | if (accumulator == 3) 76 | { 77 | outputBytes[outputLength++] = (accumulated[0] << 2) | (accumulated[1] >> 4); 78 | outputBytes[outputLength++] = (accumulated[1] << 4) | (accumulated[2] >> 2); 79 | outputBytes[outputLength++] = (accumulated[2] << 6) | accumulated[3]; 80 | } 81 | accumulator = (accumulator + 1) % 4; 82 | } 83 | } 84 | 85 | //handle left-over data 86 | if (accumulator > 0) outputBytes[outputLength] = (accumulated[0] << 2) | (accumulated[1] >> 4); 87 | if (accumulator > 1) outputBytes[++outputLength] = (accumulated[1] << 4) | (accumulated[2] >> 2); 88 | if (accumulator > 2) outputLength++; 89 | 90 | //truncate data to match actual output length 91 | outputData.length = outputLength; 92 | return outputLength? outputData: nil; 93 | } 94 | 95 | - (NSString *)base64EncodedStringWithWrapWidth:(NSUInteger)wrapWidth 96 | { 97 | //ensure wrapWidth is a multiple of 4 98 | wrapWidth = (wrapWidth / 4) * 4; 99 | 100 | const char lookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 101 | 102 | long long inputLength = [self length]; 103 | const unsigned char *inputBytes = [self bytes]; 104 | 105 | long long maxOutputLength = (inputLength / 3 + 1) * 4; 106 | maxOutputLength += wrapWidth? (maxOutputLength / wrapWidth) * 2: 0; 107 | unsigned char *outputBytes = (unsigned char *)malloc(maxOutputLength); 108 | 109 | long long i; 110 | long long outputLength = 0; 111 | for (i = 0; i < inputLength - 2; i += 3) 112 | { 113 | outputBytes[outputLength++] = lookup[(inputBytes[i] & 0xFC) >> 2]; 114 | outputBytes[outputLength++] = lookup[((inputBytes[i] & 0x03) << 4) | ((inputBytes[i + 1] & 0xF0) >> 4)]; 115 | outputBytes[outputLength++] = lookup[((inputBytes[i + 1] & 0x0F) << 2) | ((inputBytes[i + 2] & 0xC0) >> 6)]; 116 | outputBytes[outputLength++] = lookup[inputBytes[i + 2] & 0x3F]; 117 | 118 | //add line break 119 | if (wrapWidth && (outputLength + 2) % (wrapWidth + 2) == 0) 120 | { 121 | outputBytes[outputLength++] = '\r'; 122 | outputBytes[outputLength++] = '\n'; 123 | } 124 | } 125 | 126 | //handle left-over data 127 | if (i == inputLength - 2) 128 | { 129 | // = terminator 130 | outputBytes[outputLength++] = lookup[(inputBytes[i] & 0xFC) >> 2]; 131 | outputBytes[outputLength++] = lookup[((inputBytes[i] & 0x03) << 4) | ((inputBytes[i + 1] & 0xF0) >> 4)]; 132 | outputBytes[outputLength++] = lookup[(inputBytes[i + 1] & 0x0F) << 2]; 133 | outputBytes[outputLength++] = '='; 134 | } 135 | else if (i == inputLength - 1) 136 | { 137 | // == terminator 138 | outputBytes[outputLength++] = lookup[(inputBytes[i] & 0xFC) >> 2]; 139 | outputBytes[outputLength++] = lookup[(inputBytes[i] & 0x03) << 4]; 140 | outputBytes[outputLength++] = '='; 141 | outputBytes[outputLength++] = '='; 142 | } 143 | 144 | if (outputLength >= 4) 145 | { 146 | //truncate data to match actual output length 147 | outputBytes = realloc(outputBytes, outputLength); 148 | return [[NSString alloc] initWithBytesNoCopy:outputBytes 149 | length:outputLength 150 | encoding:NSASCIIStringEncoding 151 | freeWhenDone:YES]; 152 | } 153 | else if (outputBytes) 154 | { 155 | free(outputBytes); 156 | } 157 | return nil; 158 | } 159 | 160 | - (NSString *)base64EncodedString 161 | { 162 | return [self base64EncodedStringWithWrapWidth:0]; 163 | } 164 | 165 | @end 166 | 167 | 168 | @implementation NSString (Base64) 169 | 170 | + (NSString *)stringWithBase64EncodedString:(NSString *)string 171 | { 172 | NSData *data = [NSData dataWithBase64EncodedString:string]; 173 | if (data) 174 | { 175 | return [[self alloc] initWithData:data encoding:NSUTF8StringEncoding]; 176 | } 177 | return nil; 178 | } 179 | 180 | - (NSString *)base64EncodedStringWithWrapWidth:(NSUInteger)wrapWidth 181 | { 182 | NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES]; 183 | return [data base64EncodedStringWithWrapWidth:wrapWidth]; 184 | } 185 | 186 | - (NSString *)base64EncodedString 187 | { 188 | NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES]; 189 | return [data base64EncodedString]; 190 | } 191 | 192 | - (NSString *)base64DecodedString 193 | { 194 | return [NSString stringWithBase64EncodedString:self]; 195 | } 196 | 197 | - (NSData *)base64DecodedData 198 | { 199 | return [NSData dataWithBase64EncodedString:self]; 200 | } 201 | 202 | @end -------------------------------------------------------------------------------- /diumoo/core/DMUtils/NSDictionary+UrlEncoding.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+UrlEncoding.h 3 | // diumoo-core 4 | // 5 | // 代码来自: http://stackoverflow.com/questions/718429/creating-url-query-parameters-from-nsdictionary-objects-in-objectivec 6 | // 7 | // Created by Shanzi on 12-6-5. 8 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 9 | // 10 | 11 | #import 12 | 13 | @interface NSDictionary (UrlEncoding) 14 | -(NSString*) urlEncodedString; 15 | -(NSString*) hString; 16 | @end 17 | -------------------------------------------------------------------------------- /diumoo/core/DMUtils/NSDictionary+UrlEncoding.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+UrlEncoding.m 3 | // diumoo-core 4 | // 5 | // Created by Shanzi on 12-6-5. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "NSDictionary+UrlEncoding.h" 10 | 11 | static NSString* toString(id object) 12 | { 13 | return [NSString stringWithFormat:@"%@", object]; 14 | } 15 | 16 | static NSString* urlEncode(id object) 17 | { 18 | NSString* inputString = toString(object); 19 | NSString* encodedString = [inputString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; 20 | return encodedString; 21 | } 22 | 23 | @implementation NSDictionary (UrlEncoding) 24 | 25 | - (NSString*)urlEncodedString 26 | { 27 | NSMutableArray* parts = [NSMutableArray array]; 28 | for (id key in self) { 29 | id value = self[key]; 30 | NSString* encodedkey = urlEncode(key); 31 | NSString* encodedValue = urlEncode(value); 32 | NSString* part = [NSString stringWithFormat:@"%@=%@", encodedkey, encodedValue]; 33 | [parts addObject:part]; 34 | } 35 | return [parts componentsJoinedByString:@"&"]; 36 | } 37 | 38 | - (NSString*)hString 39 | { 40 | NSMutableArray* parts = [NSMutableArray array]; 41 | for (id key in self) { 42 | id value = self[key]; 43 | NSString* part = [NSString stringWithFormat:@"%@:%@", key, value]; 44 | [parts addObject:part]; 45 | } 46 | return [parts componentsJoinedByString:@"|"]; 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /diumoo/core/DMUtils/NSImage+AsyncLoadImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSImage+AsyncLoadImage.h 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-7-30. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface NSImage (AsyncLoadImage) 12 | +(void)AsyncLoadImageWithURLString:(NSString*) urlstring andCallBackBlock:(void(^)(NSImage*))block; 13 | @end 14 | -------------------------------------------------------------------------------- /diumoo/core/DMUtils/NSImage+AsyncLoadImage.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSImage+AsyncLoadImage.m 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-7-30. 6 | // 7 | // 8 | 9 | #import "NSImage+AsyncLoadImage.h" 10 | #import "DMErrorLog.h" 11 | 12 | @implementation NSImage (AsyncLoadImage) 13 | +(void)AsyncLoadImageWithURLString:(NSString*) urlstring andCallBackBlock:(void(^)(NSImage*))block 14 | { 15 | dispatch_queue_t imageLoadQueue = dispatch_queue_create("DMImageLoad", NULL); 16 | dispatch_queue_t high = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0); 17 | dispatch_set_target_queue(imageLoadQueue, high); 18 | 19 | dispatch_async(imageLoadQueue, ^{ 20 | 21 | NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlstring] 22 | cachePolicy: NSURLRequestUseProtocolCachePolicy 23 | timeoutInterval:3.0]; 24 | NSURLResponse *response; 25 | NSError *error; 26 | NSData *imageData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; 27 | 28 | if (error) { 29 | [DMErrorLog logErrorWith:self method:_cmd andError:error]; 30 | } 31 | 32 | if (block) { 33 | dispatch_sync(dispatch_get_main_queue(), ^{ 34 | block([[NSImage alloc] initWithData:imageData]); 35 | }); 36 | } 37 | }); 38 | } 39 | @end 40 | -------------------------------------------------------------------------------- /diumoo/debugLog.h: -------------------------------------------------------------------------------- 1 | // 2 | // debugLog.h 3 | // diumoo 4 | // 5 | // Created by Zheng Anakin on 12-6-9. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #ifdef DEBUG 10 | #define DMLog(format, ...) NSLog(format, ## __VA_ARGS__) 11 | #else 12 | #define DMLog(format, ...) 13 | #endif -------------------------------------------------------------------------------- /diumoo/diumoo-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // diumoo-Bridging-Header.h 3 | // diumoo 4 | // 5 | // Created by Yancheng Zheng on 6/18/16. 6 | // 7 | // 8 | 9 | #ifndef diumoo_Bridging_Header_h 10 | #define diumoo_Bridging_Header_h 11 | 12 | #import "debugLog.h" 13 | #import "HTMLParser.h" 14 | #import "NSDictionary+UrlEncoding.h" 15 | 16 | #endif /* diumoo_Bridging_Header_h */ 17 | -------------------------------------------------------------------------------- /diumoo/diumoo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | zh_CN 7 | CFBundleDisplayName 8 | diumoo 9 | CFBundleDocumentTypes 10 | 11 | 12 | CFBundleTypeExtensions 13 | 14 | dmplist 15 | 16 | CFBundleTypeIconFile 17 | 18 | CFBundleTypeName 19 | shortcut 20 | CFBundleTypeOSTypes 21 | 22 | ???? 23 | 24 | CFBundleTypeRole 25 | Viewer 26 | NSDocumentClass 27 | DMDocument 28 | 29 | 30 | CFBundleTypeExtensions 31 | 32 | dmsid 33 | 34 | CFBundleTypeName 35 | _private_record 36 | CFBundleTypeOSTypes 37 | 38 | ???? 39 | 40 | CFBundleTypeRole 41 | Viewer 42 | NSDocumentClass 43 | DMDocument 44 | 45 | 46 | CFBundleTypeExtensions 47 | 48 | dmrecord 49 | 50 | CFBundleTypeIconFile 51 | record.icns 52 | CFBundleTypeName 53 | Play Record 54 | CFBundleTypeOSTypes 55 | 56 | ???? 57 | 58 | CFBundleTypeRole 59 | Viewer 60 | NSDocumentClass 61 | DMDocument 62 | 63 | 64 | CFBundleExecutable 65 | ${EXECUTABLE_NAME} 66 | CFBundleIconFile 67 | icon.icns 68 | CFBundleIdentifier 69 | $(PRODUCT_BUNDLE_IDENTIFIER) 70 | CFBundleInfoDictionaryVersion 71 | 6.0 72 | CFBundleName 73 | ${PRODUCT_NAME} 74 | CFBundlePackageType 75 | APPL 76 | CFBundleShortVersionString 77 | 1.6 78 | CFBundleSignature 79 | ???? 80 | CFBundleURLTypes 81 | 82 | 83 | CFBundleTypeRole 84 | Viewer 85 | CFBundleURLName 86 | diumoo Share Link 87 | CFBundleURLSchemes 88 | 89 | diumoo 90 | 91 | 92 | 93 | CFBundleVersion 94 | 2626 95 | LSApplicationCategoryType 96 | public.app-category.music 97 | LSMinimumSystemVersion 98 | ${MACOSX_DEPLOYMENT_TARGET} 99 | LSUIElement 100 | 101 | NSAppTransportSecurity 102 | 103 | NSAllowsArbitraryLoads 104 | 105 | NSExceptionDomains 106 | 107 | douban.fm 108 | 109 | 110 | 111 | NSHumanReadableCopyright 112 | Open sourced with GPLv3 license 113 | NSMainNibFile 114 | MainMenu 115 | NSPrincipalClass 116 | NSApplication 117 | NSSupportsAutomaticGraphicsSwitching 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /diumoo/diumoo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'diumoo' target in the 'diumoo' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import "debugLog.h" 8 | #endif 9 | -------------------------------------------------------------------------------- /diumoo/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg936\cocoartf1138\cocoasubrtf470 2 | {\fonttbl\f0\fnil\fcharset134 STHeitiSC-Light;\f1\fswiss\fcharset0 Helvetica;\f2\fswiss\fcharset0 ArialMT; 3 | } 4 | {\colortbl;\red255\green255\blue255;\red63\green0\blue63;} 5 | \paperw11900\paperh16840\vieww9600\viewh8400\viewkind0 6 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720 7 | 8 | \f0\b\fs24 \cf0 Developed By 9 | \f1 : 10 | \b0 \ 11 | Shanzi, Anakin Zheng, Sean Lee\ 12 | \ 13 | 14 | \f0\b UI 15 | \f1 : 16 | \b0 \ 17 | 18 | \f2\fs28 \cf2 chumsdock 19 | \f0 , 20 | \f1\fs24 \cf0 Shanzi\ 21 | \ 22 | \ 23 | \ 24 | } -------------------------------------------------------------------------------- /diumoo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /diumoo/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* No comment provided by engineer. */ 2 | "PLAY_ALBUM_FAILED" = "Play Album Failed"; 3 | 4 | /* No comment provided by engineer. */ 5 | "PLAY_ALBUM_FAILED_DETAIL" = "The album you request to play is invalid."; 6 | 7 | /* No comment provided by engineer. */ 8 | "PRIVATE_MHZ" = "Private MHz"; 9 | 10 | /* No comment provided by engineer. */ 11 | "SOUNDTRACK_MHZ" = "Soundtrack"; 12 | 13 | /* No comment provided by engineer. */ 14 | "EXPORT" = "Export"; 15 | 16 | /* No comment provided by engineer. */ 17 | "EXPORT_PLAY_RECORD" = "Export Play Record"; 18 | 19 | /* No comment provided by engineer. */ 20 | "IMPORT" = "Import"; 21 | 22 | /* 导入失败 */ 23 | "IMPORT_FAILED" = "Import Failed"; 24 | 25 | /* 导入播放记录失败,您试图导入的文件类型不正确或者文件已损坏。 */ 26 | "IMPORT_FAILED_DETAIL" = "The play record file is broken or invalid."; 27 | 28 | /* 导入播放记录 */ 29 | "IMPORT_PLAY_RECORD" = "Import Play Record"; 30 | 31 | /* No comment provided by engineer. */ 32 | "OPEN_URL_FAILED" = "Open URL Failed"; 33 | 34 | /* 未能成功打开您指定的URL,可能是您试图播放的专辑、音乐家或电影原声不可用。 */ 35 | "OPEN_URL_FAILED_DETAIL" = "Failed to open URL. It seems that the album, songs of artist or soundtrack you request to play is invalid."; 36 | 37 | /* 查看详细 */ 38 | "SHOW_DETAIL" = "Show Details"; 39 | 40 | /* No comment provided by engineer. */ 41 | "SUCCESS_EXPORT" = "Export Success"; 42 | 43 | /* 成功导出 %ld 首歌曲的播放记录。 */ 44 | "SUCCESS_EXPORT_DETAIL" = "Success to export play record of %ld songs."; 45 | 46 | /* No comment provided by engineer. */ 47 | "SUCCESS_IMPORT" = "Import Success"; 48 | 49 | /* 成功导入了 %ld 个歌曲记录,失败 %ld 个。 */ 50 | "SUCCESS_IMPORT_DETAIL" = "Success to import play record of %ld songs with %ld failed."; 51 | 52 | /* No comment provided by engineer. */ 53 | "IMPORT_PLAY_RECORD" = "Import Play Record"; 54 | 55 | /* No comment provided by engineer. */ 56 | "IMPORT_PLAY_RECORD_DETAIL" = "You have just opened a diumoo play record file, import it now?"; 57 | /* No comment provided by engineer. */ 58 | "NO" = "No"; 59 | 60 | /* No comment provided by engineer. */ 61 | "OPEN_FILE_FAILED" = "Failed to open File"; 62 | 63 | /* No comment provided by engineer. */ 64 | "YES" = "Yes"; 65 | 66 | /* public */ 67 | "LOCALIZED_PUBLIC_LIST_NAME" = "en_public"; 68 | 69 | /* 发生网络错误,请尝试重启应用 */ 70 | "NETWORK_ERROR_INDICADTE_STRING"="Request Timeout"; 71 | 72 | /* 专辑:《%@》 */ 73 | "ALBUM_DOCUMENT_TITLE" = "Album : %@"; 74 | 75 | /* 专辑名 */ 76 | "DETAIL_ALBUM" = "Album Title"; 77 | 78 | /* 唱片数 */ 79 | "DETAIL_DISCS" = "Discs"; 80 | 81 | /* 豆瓣评分 */ 82 | "DETAIL_DOUBAN_RATING" = "Rating"; 83 | 84 | /* 条形码 */ 85 | "DETAIL_EAN" = "Ean"; 86 | 87 | /* 介质 */ 88 | "DETAIL_MEDIA" = "Media"; 89 | 90 | /* 发行时间 */ 91 | "DETAIL_PUBDATE" = "Pub-date"; 92 | 93 | /* 出版者 */ 94 | "DETAIL_PUBLISHER" = "Publisher"; 95 | 96 | /* %@ (%@ 人评价) */ 97 | "DETAIL_RATES" = "%@ (%@ ratings)"; 98 | 99 | /* 歌手 */ 100 | "DETAIL_SINGER" = "Artist"; 101 | 102 | /* 版本特性 */ 103 | "DETAIL_VERSION" = "Version"; 104 | 105 | /* 获取详细信息失败 */ 106 | "GET_DETAIL_FAILED" = "Get album detail failed."; 107 | 108 | /* 解析信息失败 */ 109 | "PARSE_DETAIL_FAILED" = "Parse album detail failed."; 110 | 111 | /* 解析信息成功! */ 112 | "PARSE_DETAIL_SUCCESS" = "Get detail success."; 113 | 114 | /* 简介 */ 115 | "DETAIL_ABSTRACT" = "Abstract"; 116 | 117 | /* 专辑信息 */ 118 | "DETAIL_ALBUM_INFO" = "Album Info"; 119 | 120 | /* 曲目 */ 121 | "DETAIL_SONGS" = "songs"; 122 | 123 | /* 未知 */ 124 | "DETAIL_UNKNOWN" = "unknown"; 125 | 126 | /* 需要验证码 */ 127 | "INVALID_CAPTCHA_CODE" = "Haven't fetch captcha yet"; 128 | 129 | /* 验证码不能为空 */ 130 | "CAPTCHA_MUST_NOT_BE_EMPTY" = "Captcha must not be empty"; 131 | 132 | /* 邮箱不能为空 */ 133 | "EMAIL_MUST_NOT_BE_EMPTY" = "Email must not be empty"; 134 | 135 | /* 获取失败,请重试 */ 136 | "FETCH_CAPTCHA_FAILED" = "Failed to fetch Captcha image"; 137 | 138 | /* 登陆失败! */ 139 | "LOGIN_FAILED" = "Failed to login"; 140 | 141 | /* 密码不能为空 */ 142 | "PASSWORD_MUST_NOT_BE_EMPTY" = "Password must not be empty"; 143 | 144 | /* 关于 */ 145 | "PREF_ABOUT" = "About"; 146 | 147 | /* 账户 */ 148 | "PREF_ACCOUNT" = "Account"; 149 | 150 | /* 通用 */ 151 | "PREF_GENERAL" = "General"; 152 | 153 | /* 快捷键 */ 154 | "PREF_SHORTCUTS" = "Shortcuts"; 155 | 156 | /* 详细信息 */ 157 | "DETAIL" = "Detail"; 158 | 159 | /* 预览 */ 160 | "PREVIEW" = "Preview"; 161 | 162 | /* 豆瓣FM PRO */ 163 | "PRO_NEED_BUY_TITLE" = "Douban FM Pro"; 164 | 165 | /* 豆瓣FM PRO 说明 */ 166 | "PRO_NEED_BUY" = "You can only play music of higher bitrate after paid for douban FM Pro. This service is provided by douban and has no relationship with diumoo.\n\nDo you want to know more?" ; 167 | 168 | /* 音乐比特率改变 */ 169 | "BITRATE_CHANGED" = "Bitrate of Music Changed"; 170 | 171 | /* 音乐比特率改变到 */ 172 | "BITRATE_CHANGED_TO_VALUE" = "Current bitrate of music is "; 173 | 174 | /* 分享链接已复制 */ 175 | "SHARE_LINK_TITLE" = "share link copied"; 176 | 177 | -------------------------------------------------------------------------------- /diumoo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-6-7. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "DMApp.h" 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | @autoreleasepool { 15 | [DMApp sharedApplication]; 16 | [[NSBundle mainBundle] loadNibNamed:@"MainMenu" owner:NSApp topLevelObjects:nil]; 17 | [NSApp run]; 18 | } 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /diumoo/ui/DMPanel/DMBitrateControlLayer/DMBitrateControlLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMBitrateControlLayer.h 3 | // DMBitrateSelectLayer 4 | // 5 | // Created by Shanzi on 13-1-18. 6 | // Copyright (c) 2013年 Shanzi. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DMBitrateControlLayer : CALayer 12 | { 13 | NSArray* LayerArray; 14 | CGColorRef black; 15 | CGColorRef focus; 16 | } 17 | 18 | -(BOOL) hitPostion:(NSPoint) point; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /diumoo/ui/DMPanel/DMBitrateControlLayer/DMBitrateControlLayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // DMBitrateControlLayer.m 3 | // DMBitrateSelectLayer 4 | // 5 | // Created by Shanzi on 13-1-18. 6 | // Copyright (c) 2013年 Shanzi. All rights reserved. 7 | // 8 | 9 | #import "DMBitrateControlLayer.h" 10 | 11 | @implementation DMBitrateControlLayer 12 | -(id)init 13 | { 14 | self = [super init]; 15 | if (self) { 16 | self.frame = CGRectMake(0, 0, 250, 40); 17 | self.anchorPoint = CGPointMake(0, 0); 18 | 19 | LayerArray = @[[CALayer new],[CALayer new],[CALayer new]]; 20 | 21 | int count=0; 22 | NSArray* imagenames=@[@"64kbps",@"128kbps",@"192kbps"]; 23 | black = CGColorCreateGenericGray(0, 0.6); 24 | focus = CGColorCreateGenericRGB(0.2, 0.5, 1.0, 1.0); 25 | CGRect frame = CGRectMake(0, 0, 70, 40); 26 | 27 | for (CALayer* layer in LayerArray) { 28 | layer.frame = frame; 29 | layer.anchorPoint = CGPointMake(0, 0); 30 | layer.backgroundColor = black; 31 | layer.position = CGPointMake(72*count +17 , 10); 32 | layer.contents = [NSImage imageNamed:imagenames[count]]; 33 | [self addSublayer:layer]; 34 | count ++; 35 | } 36 | 37 | [[NSUserDefaults standardUserDefaults] addObserver:self 38 | forKeyPath:@"musicQuality" 39 | options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) 40 | context:nil]; 41 | 42 | } 43 | return self; 44 | } 45 | 46 | -(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 47 | { 48 | 49 | NSInteger bitrate = [[NSUserDefaults standardUserDefaults] integerForKey:@"musicQuality"]; 50 | [self selectLayer:LayerArray[bitrate/64-1]]; 51 | } 52 | 53 | -(void) selectLayer:(CALayer*) layer 54 | { 55 | [CATransaction begin]; 56 | for (CALayer* l in LayerArray) { 57 | if (l==layer) { 58 | l.backgroundColor = focus; 59 | l.position = CGPointMake(l.position.x, 10); 60 | } 61 | else{ 62 | l.backgroundColor=black; 63 | l.position = CGPointMake(l.position.x, 0); 64 | } 65 | } 66 | [CATransaction commit]; 67 | } 68 | 69 | -(BOOL) hitPostion:(NSPoint)point 70 | { 71 | if (point.y>40) 72 | return NO; 73 | int bitrate = 0; 74 | if (point.x>17) 75 | { 76 | if (point.x<17+72) 77 | bitrate = 64; 78 | else if(point.x < 17+72*2) 79 | bitrate = 128; 80 | else if(point.x < 17+72*3) 81 | bitrate = 192; 82 | } 83 | if(bitrate) { 84 | NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 85 | if ([defaults boolForKey:@"isPro"]) { 86 | if ([defaults integerForKey:@"musicQuality"]!=bitrate) { 87 | [defaults setInteger:bitrate 88 | forKey:@"musicQuality"]; 89 | } 90 | } 91 | else{ 92 | NSInteger re = NSRunInformationalAlertPanel(NSLocalizedString(@"PRO_NEED_BUY_TITLE", nil), 93 | NSLocalizedString(@"PRO_NEED_BUY", nil), 94 | NSLocalizedString(@"YES", nil), 95 | NSLocalizedString(@"NO", nil), 96 | nil); 97 | if (re==NSAlertDefaultReturn) { 98 | [[NSWorkspace sharedWorkspace] openURL: 99 | [NSURL URLWithString:@"https://douban.fm/upgrade"]]; 100 | } 101 | } 102 | 103 | return YES; 104 | } 105 | return NO; 106 | } 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /diumoo/ui/DMPanel/DMButton/DMButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMControlButtonCell.h 3 | // diumoo 4 | // 5 | // Created by AnakinGWY on 12-8-15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface DMButton : NSButton { 12 | NSTrackingArea* trackingArea; 13 | } 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /diumoo/ui/DMPanel/DMButton/DMButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // DMControlButtonCell.m 3 | // diumoo 4 | // 5 | // Created by AnakinGWY on 12-8-15. 6 | // 7 | // 8 | 9 | #import "DMButton.h" 10 | 11 | @implementation DMButton 12 | 13 | - (id)init 14 | { 15 | if (self = [super init]) { 16 | } 17 | return self; 18 | } 19 | 20 | - (void)awakeFromNib 21 | { 22 | [[self window] setAcceptsMouseMovedEvents:YES]; 23 | [super awakeFromNib]; 24 | } 25 | 26 | - (void)mouseEntered:(NSEvent*)theEvent 27 | { 28 | if ([self isEnabled]) { 29 | NSString* name = [[self image] name]; 30 | NSImage* mouseoverImage = [NSImage imageNamed:[name stringByAppendingString:@"_mouseover"]]; 31 | if (mouseoverImage != nil) { 32 | [self setImage:mouseoverImage]; 33 | } 34 | } 35 | } 36 | - (void)mouseExited:(NSEvent*)theEvent 37 | { 38 | if ([self isEnabled]) { 39 | NSString* name = [[self image] name]; 40 | NSImage* normalImage = [NSImage imageNamed:[name stringByReplacingOccurrencesOfString:@"_mouseover" withString:@""]]; 41 | if (normalImage != nil) { 42 | [self setImage:normalImage]; 43 | } 44 | } 45 | } 46 | 47 | - (void)updateTrackingAreas 48 | { 49 | [self removeTrackingArea:trackingArea]; 50 | 51 | NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow; 52 | trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil]; 53 | 54 | [self addTrackingArea:trackingArea]; 55 | 56 | [super updateTrackingAreas]; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /diumoo/ui/DMPanel/DMCoverControlView/DMCoverControlView.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMCoverControlView.h 3 | // diumoo-main-ui 4 | // 5 | // Created by Shanzi on 12-5-31. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "DMCoverSlide.h" 11 | #import "DMBitrateControlLayer.h" 12 | 13 | @interface DMCoverControlView : NSButton 14 | { 15 | CALayer* mainLayer; 16 | DMCoverSlide* slide; 17 | DMBitrateControlLayer* bitratelayer; 18 | } 19 | @property(readonly)DMCoverSlide* slide; 20 | 21 | -(void) setPlayingInfo:(NSString*) musictitle 22 | :(NSString*) artist 23 | :(NSString*) albumTitle ; 24 | 25 | -(void) setAlbumImage:(NSImage*) albumImage; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /diumoo/ui/DMPanel/DMCoverControlView/DMCoverControlView.m: -------------------------------------------------------------------------------- 1 | // 2 | // DMCoverControlView.m 3 | // diumoo-main-ui 4 | // 5 | // Created by Shanzi on 12-5-31. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "DMCoverControlView.h" 10 | 11 | @implementation DMCoverControlView 12 | @synthesize slide; 13 | 14 | - (id)initWithFrame:(NSRect)frame 15 | { 16 | self = [super initWithFrame:frame]; 17 | if (self) { 18 | mainLayer = [[CALayer alloc] init]; 19 | 20 | mainLayer.frame = frame; 21 | mainLayer.masksToBounds = YES; 22 | 23 | slide = [[DMCoverSlide alloc] init]; 24 | slide.anchorPoint = CGPointMake(0, 0); 25 | slide.position =slide.anchorPoint; 26 | 27 | [mainLayer addSublayer:slide]; 28 | 29 | bitratelayer = [[DMBitrateControlLayer alloc] init]; 30 | 31 | bitratelayer.position = CGPointMake(0, frame.size.height); 32 | 33 | [mainLayer addSublayer:bitratelayer]; 34 | 35 | [self setWantsLayer:YES]; 36 | [self setLayer:mainLayer]; 37 | 38 | [self addTrackingRect:self.bounds 39 | owner:self 40 | userData:NULL assumeInside:NO]; 41 | 42 | } 43 | 44 | return self; 45 | } 46 | 47 | -(BOOL) acceptsFirstResponder 48 | { 49 | return YES; 50 | } 51 | 52 | -(void) mouseEntered:(NSEvent *)event 53 | { 54 | [slide setOpacity:0.6]; 55 | bitratelayer.position = CGPointMake(0, -20); 56 | } 57 | 58 | -(void) mouseExited:(NSEvent *)event 59 | { 60 | [slide setOpacity:1.0]; 61 | bitratelayer.position = CGPointMake(0, -50); 62 | } 63 | 64 | -(void) mouseDown:(NSEvent *)theEvent 65 | { 66 | NSPoint location = [self convertPoint:[theEvent locationInWindow] fromView:self.window.contentView]; 67 | 68 | if (![bitratelayer hitPostion:location]) { 69 | [NSApp sendAction:self.action to:self.target from:self]; 70 | } 71 | 72 | } 73 | 74 | 75 | -(void) setPlayingInfo:(NSString *)musictitle :(NSString *)artist :(NSString *)albumTitle 76 | { 77 | [self.slide setTitle:musictitle artist:artist andAlbum:albumTitle]; 78 | 79 | } 80 | 81 | -(void) setAlbumImage:(NSImage *)albumImage 82 | { 83 | [self.slide setFrontCoverImage:albumImage]; 84 | } 85 | 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /diumoo/ui/DMPanel/DMCoverSlide/DMCoverSlide.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMCoverSlide.h 3 | // diumoo-main-ui 4 | // 5 | // Created by Shanzi on 12-5-30. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #define BOUNDS CGRectMake(0,0,250,330) 10 | #define FRONT_BOUNDS CGRectMake(0,0,250,250) 11 | #define TITLE_BOUNDS CGRectMake(0,0,230,30) 12 | #define ARTIST_BOUNDS CGRectMake(0,0,230,20) 13 | #define ALBUM_BOUNDS CGRectMake(0,0,230,20) 14 | 15 | #import 16 | 17 | @interface DMCoverSlide : CALayer 18 | { 19 | CGPoint frontPosition; 20 | CGPoint titlePosition; 21 | CGPoint artistPosition; 22 | CGPoint albumPosition; 23 | 24 | CALayer* frontCover; 25 | CALayer* frontFadeTransitionLayer; 26 | 27 | CATextLayer* titleLayer; 28 | CATextLayer* artistLayer; 29 | CATextLayer* albumLayer; 30 | } 31 | @property(readonly) CALayer* frontCover; 32 | 33 | 34 | -(void) setTitle:(NSString*) title artist:(NSString*)artist andAlbum:(NSString*) album; 35 | 36 | -(void) setFrontCoverImage:(NSImage*) image; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /diumoo/ui/DMPanel/DMCoverSlide/DMCoverSlide.m: -------------------------------------------------------------------------------- 1 | // 2 | // DMCoverSlide.m 3 | // diumoo-main-ui 4 | // 5 | // Created by Shanzi on 12-5-30. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "DMCoverSlide.h" 10 | 11 | @implementation DMCoverSlide 12 | @synthesize frontCover; 13 | 14 | -(id) init 15 | { 16 | if (self = [super init]) { 17 | frontPosition = CGPointMake(0,0); 18 | titlePosition = CGPointMake(10,260); 19 | artistPosition = CGPointMake(13,283); 20 | albumPosition = CGPointMake(13,301); 21 | self.bounds = BOUNDS; 22 | self.masksToBounds = YES; 23 | 24 | CGFloat currentScale = [NSScreen mainScreen].backingScaleFactor; 25 | 26 | // init 27 | frontCover = [[CALayer alloc] init]; 28 | frontFadeTransitionLayer = [[CALayer alloc] init]; 29 | 30 | titleLayer = [[CATextLayer alloc] init]; 31 | artistLayer = [[CATextLayer alloc] init]; 32 | albumLayer = [[CATextLayer alloc] init]; 33 | 34 | frontCover.delegate = self; 35 | frontFadeTransitionLayer.delegate = self; 36 | 37 | titleLayer.delegate = self; 38 | artistLayer.delegate = self; 39 | albumLayer.delegate = self; 40 | 41 | frontCover.contentsScale = currentScale; 42 | frontFadeTransitionLayer.contentsScale = currentScale; 43 | titleLayer.contentsScale = currentScale; 44 | artistLayer.contentsScale = currentScale; 45 | albumLayer.contentsScale = currentScale; 46 | 47 | // anchor 48 | CGPoint anchor = CGPointMake(0, 0); 49 | 50 | frontCover.anchorPoint = anchor; 51 | frontFadeTransitionLayer.anchorPoint = anchor; 52 | 53 | titleLayer.anchorPoint = anchor; 54 | artistLayer.anchorPoint = anchor; 55 | albumLayer.anchorPoint = anchor; 56 | 57 | // position 58 | frontCover.position = frontPosition; 59 | frontFadeTransitionLayer.position = anchor; 60 | 61 | titleLayer.position = titlePosition; 62 | artistLayer.position = artistPosition; 63 | albumLayer.position = albumPosition; 64 | 65 | // bounds 66 | frontCover.bounds = FRONT_BOUNDS; 67 | frontFadeTransitionLayer.bounds = FRONT_BOUNDS; 68 | titleLayer.bounds = TITLE_BOUNDS; 69 | artistLayer.bounds = ARTIST_BOUNDS; 70 | albumLayer.bounds = ALBUM_BOUNDS; 71 | 72 | 73 | // gravity 74 | frontCover.contentsGravity = kCAGravityResizeAspectFill; 75 | frontFadeTransitionLayer.contentsGravity = kCAGravityResizeAspectFill; 76 | 77 | // text 78 | CGFontRef helveticaConsensedBold = CGFontCreateWithFontName((CFStringRef)@"Helvetica Neue Condensed Bold"); 79 | CGFontRef helveticaLight = CGFontCreateWithFontName((CFStringRef)@"Helvetica Neue Light"); 80 | CGColorRef titleColor = CGColorCreateGenericRGB(0.2, 0.5, 1.0, 1.0); 81 | CGColorRef lighterColor = CGColorCreateGenericGray(0.2, 1.0); 82 | 83 | titleLayer.font = helveticaConsensedBold; 84 | artistLayer.font = helveticaLight; 85 | albumLayer.font = helveticaLight; 86 | 87 | titleLayer.fontSize = 18; 88 | artistLayer.fontSize = 13; 89 | albumLayer.fontSize = 13; 90 | 91 | titleLayer.foregroundColor = titleColor; 92 | artistLayer.foregroundColor = lighterColor; 93 | albumLayer.foregroundColor = lighterColor; 94 | 95 | titleLayer.truncationMode = kCATruncationEnd; 96 | artistLayer.truncationMode = kCATruncationEnd; 97 | albumLayer.truncationMode = kCATruncationEnd; 98 | 99 | CGFontRelease(helveticaConsensedBold); 100 | CGFontRelease(helveticaLight); 101 | CGColorRelease(titleColor); 102 | CGColorRelease(lighterColor); 103 | 104 | // opacity 105 | frontFadeTransitionLayer.opacity = 0; 106 | 107 | // sublayer 108 | [frontCover setMasksToBounds:YES]; 109 | [frontCover addSublayer:frontFadeTransitionLayer]; 110 | [self addSublayer:frontCover]; 111 | [self addSublayer:titleLayer]; 112 | [self addSublayer:artistLayer]; 113 | [self addSublayer:albumLayer]; 114 | } 115 | return self; 116 | } 117 | 118 | 119 | -(void)setTitle:(NSString *)title artist:(NSString *)artist andAlbum :(NSString *)album 120 | { 121 | [CATransaction begin]; 122 | [CATransaction setCompletionBlock:^{ 123 | titleLayer.string = title; 124 | artistLayer.string = artist; 125 | albumLayer.string = album; 126 | 127 | titleLayer.opacity = 1; 128 | artistLayer.opacity = 1; 129 | albumLayer.opacity = 1; 130 | 131 | }]; 132 | titleLayer.opacity = 0; 133 | artistLayer.opacity = 0; 134 | albumLayer.opacity = 0; 135 | [CATransaction commit]; 136 | 137 | } 138 | 139 | -(void) setFrontCoverImage:(NSImage *)image 140 | { 141 | [CATransaction begin]; 142 | frontFadeTransitionLayer.contents = image; 143 | [CATransaction setAnimationDuration:1.0]; 144 | [CATransaction setAnimationTimingFunction: 145 | [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]]; 146 | [CATransaction setCompletionBlock:^{ 147 | [CATransaction begin]; 148 | [CATransaction setAnimationDuration:0.0]; 149 | frontCover.contents = image; 150 | frontFadeTransitionLayer.opacity = 0.0; 151 | [CATransaction commit]; 152 | }]; 153 | frontFadeTransitionLayer.opacity = 1.0; 154 | [CATransaction commit]; 155 | } 156 | 157 | - (BOOL)layer:(CALayer *)layer shouldInheritContentsScale:(CGFloat)newScale 158 | fromWindow:(NSWindow *)window 159 | { 160 | return YES; 161 | } 162 | 163 | @end 164 | -------------------------------------------------------------------------------- /diumoo/ui/DMPanel/DMPanel.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMPanel.h 3 | // diumoo 4 | // 5 | // Created by Anakin Zheng on 12-8-14. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface DMPanel : NSPanel 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /diumoo/ui/DMPanel/DMPanel.m: -------------------------------------------------------------------------------- 1 | // 2 | // DMPanel.m 3 | // diumoo 4 | // 5 | // Created by Anakin Zheng on 12-8-14. 6 | // 7 | // 8 | 9 | #import "DMPanel.h" 10 | #import "DMPanelWindowController.h" 11 | 12 | @implementation DMPanel 13 | 14 | - (BOOL)canBecomeKeyWindow; 15 | { 16 | return YES; // Allow Search field to become the first responder 17 | } 18 | 19 | - (void)scrollWheel:(NSEvent*)theEvent 20 | { 21 | [self.delegate performSelector:@selector(mouseScroll:) 22 | withObject:theEvent]; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /diumoo/ui/DMPanel/DMPanelWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMPannelWindowController.h 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-6-12. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "DMButton.h" 10 | #import "DMCoverControlView.h" 11 | #import "diumoo-Swift.h" 12 | #import "DMPopUpMenuController.h" 13 | #import "MenubarController.h" 14 | #import 15 | 16 | typedef enum { 17 | DOUBAN = 1, 18 | FANFOU = 2, 19 | SINA_WEIBO = 3, 20 | TWITTER = 4, 21 | FACEBOOK = 5, 22 | SYS_TWITTER = 6, 23 | SYS_FACEBOOK = 7, 24 | SYS_WEIBO = 8, 25 | COPY_LINK = 9 26 | 27 | } SNS_CODE; 28 | 29 | @protocol DMPanelWindowDelegate 30 | 31 | - (void)playOrPause; 32 | - (void)skip; 33 | - (void)rateOrUnrate; 34 | - (void)ban; 35 | - (void)volumeChange:(float)volume; 36 | - (BOOL)channelChangedTo:(NSString*)channel; 37 | - (void)exitedSpecialMode; 38 | - (void)share:(SNS_CODE)code; 39 | 40 | @end 41 | 42 | @interface DMPanelWindowController : NSWindowController { 43 | IBOutlet DMCoverControlView* coverView; 44 | IBOutlet DMPopUpMenuController* popupMenuController; 45 | 46 | IBOutlet DMButton* playPauseButton; 47 | IBOutlet DMButton* skipButton; 48 | IBOutlet DMButton* rateButton; 49 | IBOutlet DMButton* banButton; 50 | 51 | IBOutlet NSButton* userIconButton; 52 | IBOutlet NSTextField* usernameTextField; 53 | 54 | IBOutlet NSProgressIndicator* loadingIndicator; 55 | IBOutlet NSTextField* indicateString; 56 | 57 | BOOL _hasActivePanel; 58 | MenubarController* menubarController; 59 | } 60 | 61 | @property (nonatomic, strong) IBOutlet DMCoverControlView* coreView; 62 | 63 | @property (copy) NSString* openURL; 64 | @property (nonatomic) BOOL hasActivePanel; 65 | 66 | @property (strong) IBOutlet id delegate; 67 | 68 | + (DMPanelWindowController*)sharedWindowController; 69 | 70 | - (void)channelChangeActionWithSender:(id)sender; 71 | - (IBAction)controlAction:(id)sender; 72 | - (IBAction)showAlbumWindow:(id)sender; 73 | - (IBAction)specialAction:(id)sender; 74 | - (IBAction)shareAction:(id)sender; 75 | 76 | - (void)unlockUIWithError:(BOOL)has_err; 77 | - (void)setRated:(BOOL)rated; 78 | - (void)setPlaying:(BOOL)playing; 79 | - (void)setPlayingItem:(DMPlayableItem*)item; 80 | - (void)playDefaultChannel; 81 | 82 | - (NSString*)switchToDefaultChannel; 83 | - (void)invokeChannelWithCid:(NSInteger)cid andTitle:(NSString*)title andPlay:(BOOL)immediately; 84 | 85 | - (void)toggleSpecialWithDictionary:(NSDictionary*)info; 86 | - (IBAction)togglePanel:(id)sender; 87 | - (void)mouseScroll:(NSEvent*)event; 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /diumoo/ui/DMPanel/DMPopUpButtonCell/DMPopUpButtonCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMPopUpButtonCell.h 3 | // DMSubclassNSCellTest 4 | // 5 | // Created by Shanzi on 12-6-14. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DMPopUpButtonCell : NSButtonCell 12 | { 13 | NSDictionary* stringAttribute; 14 | NSDictionary* stringHighligtAttribute; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /diumoo/ui/DMPanel/DMPopUpButtonCell/DMPopUpButtonCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // DMPopUpButtonCell.m 3 | // DMSubclassNSCellTest 4 | // 5 | // Created by Shanzi on 12-6-14. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "DMPopUpButtonCell.h" 10 | 11 | #define HIGH_LIGHT_ATTRIBUTE [NSDictionary dictionaryWithObject:[NSColor whiteColor] forKey:NSForegroundColorAttributeName] 12 | 13 | @implementation DMPopUpButtonCell 14 | 15 | 16 | -(void) drawTitleWithFrame:(NSRect)cellFrame inView:(NSView *)controlView 17 | { 18 | NSString* label = self.title; 19 | NSAttributedString* string = [[NSAttributedString alloc] initWithString:label 20 | attributes:[self stringAttribute]]; 21 | NSSize stringSize = string.size; 22 | NSRect stringRect = NSMakeRect(cellFrame.origin.x + 10, 23 | NSMidY(cellFrame) - stringSize.height/2, 24 | cellFrame.size.width - 40, stringSize.height); 25 | [string drawInRect:stringRect]; 26 | 27 | } 28 | 29 | -(NSDictionary*) stringAttribute 30 | { 31 | if ([self isHighlighted]) { 32 | if (stringHighligtAttribute) { 33 | return stringHighligtAttribute; 34 | } 35 | else { 36 | NSMutableParagraphStyle* ps = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 37 | [ps setLineBreakMode:NSLineBreakByTruncatingTail]; 38 | 39 | stringHighligtAttribute = @{NSForegroundColorAttributeName: [NSColor selectedMenuItemTextColor], 40 | NSParagraphStyleAttributeName: ps, 41 | NSFontAttributeName: self.font}; 42 | return stringHighligtAttribute; 43 | } 44 | } 45 | else { 46 | if (stringAttribute) { 47 | return stringAttribute; 48 | } 49 | else { 50 | NSMutableParagraphStyle* ps = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 51 | [ps setLineBreakMode:NSLineBreakByTruncatingTail]; 52 | 53 | stringAttribute = @{NSForegroundColorAttributeName: [NSColor blackColor], 54 | NSParagraphStyleAttributeName: ps, 55 | NSFontAttributeName: self.font}; 56 | return stringAttribute; 57 | } 58 | } 59 | } 60 | 61 | -(void) drawBorderAndBackgroundWithFrame:(NSRect)cellFrame inView:(NSView *)controlView 62 | { 63 | NSBezierPath* rect = [NSBezierPath bezierPathWithRect:cellFrame]; 64 | [rect setLineWidth:1.0]; 65 | [[NSColor colorWithGenericGamma22White:0.6 alpha:1.0] setStroke]; 66 | 67 | NSBezierPath* tri = [[NSBezierPath alloc] init]; 68 | [tri moveToPoint:NSMakePoint(cellFrame.size.width - 20, NSMidY(cellFrame) - 5)]; 69 | [tri lineToPoint:NSMakePoint(cellFrame.size.width - 20, NSMidY(cellFrame) + 5)]; 70 | [tri lineToPoint:NSMakePoint(cellFrame.size.width + 5*sqrt(3) - 20, NSMidY(cellFrame))]; 71 | [tri closePath]; 72 | 73 | if([self isHighlighted]) 74 | { 75 | [[NSColor selectedMenuItemColor] setFill]; 76 | [rect fill]; 77 | [rect stroke]; 78 | 79 | [[NSColor whiteColor]setFill]; 80 | [tri fill]; 81 | } 82 | 83 | else 84 | { 85 | [[NSColor whiteColor] setFill]; 86 | [rect fill]; 87 | [rect stroke]; 88 | [tri stroke]; 89 | } 90 | } 91 | 92 | -(void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView 93 | { 94 | [self drawBorderAndBackgroundWithFrame:cellFrame inView:controlView]; 95 | [self drawTitleWithFrame:cellFrame inView:controlView]; 96 | } 97 | 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /diumoo/ui/DMPanel/DMPopUpButtonCell/DMPopUpMenuController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMPopUpMenuController.h 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-6-16. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface DMPopUpMenuController : NSObjectController 13 | { 14 | IBOutlet NSButton* mainButton; 15 | IBOutlet NSButton* longMainButton; 16 | IBOutlet NSButton* subButton; 17 | 18 | IBOutlet NSMenu * mainMenu; 19 | IBOutlet NSMenu * moreChannelMenu; 20 | IBOutlet NSMenu* exitSpecialMenu; 21 | 22 | IBOutlet NSMenu* shareMenu; 23 | 24 | } 25 | 26 | @property(strong) IBOutlet id delegate; 27 | 28 | @property(strong) NSMenu* publicMenu; 29 | @property(strong) NSMenu* suggestMenu; 30 | 31 | @property NSInteger currentChannelID; 32 | @property(strong) id currentChannelMenuItem; 33 | 34 | @property BOOL specialMode; 35 | 36 | -(IBAction)popUpMenu:(id)sender; 37 | -(IBAction)changeChannelAction:(id)sender; 38 | -(void) updateChannelMenuWithSender:(id)sender; 39 | -(void) updateChannelList; 40 | 41 | -(void) enterSpecialPlayingModeWithTitle:(NSString *)title artist:(NSString*)artist andTypeString:(NSString*) type; 42 | -(void) exitSepecialPlayingMode; 43 | -(void) setPrivateChannelEnabled:(BOOL) enable; 44 | -(void) unlockChannelMenuButton; 45 | 46 | -(void) invokeChannelWith:(NSInteger) cid andTitle:(NSString*) title andPlay:(BOOL)playImmediately; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /diumoo/ui/DMPanel/DMVolumeSlideCell/DMVolumeSlideCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMVolumeSlideCell.h 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-6-24. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DMVolumeSlideCell : NSSliderCell { 12 | NSImage* backImage; 13 | NSImage* sliderImage; 14 | NSRect sliderRect; 15 | NSRect sliderDrawingRect; 16 | NSRect backFrame; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /diumoo/ui/DMPanel/DMVolumeSlideCell/DMVolumeSlideCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // DMVolumeSlideCell.m 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-6-24. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "DMVolumeSlideCell.h" 10 | 11 | @implementation DMVolumeSlideCell 12 | 13 | - (void)awakeFromNib 14 | { 15 | backImage = [NSImage imageNamed:@"volume_back"]; 16 | sliderImage = [NSImage imageNamed:@"volume_slider"]; 17 | 18 | [backImage setFlipped:YES]; 19 | [sliderImage setFlipped:YES]; 20 | 21 | sliderRect = NSMakeRect(0, 0, 32, 21); 22 | sliderDrawingRect = NSMakeRect(0, 0, 32, 21); 23 | backFrame = NSMakeRect(0, 0, 250, 21); 24 | } 25 | - (NSRect)knobRectFlipped:(BOOL)flipped 26 | { 27 | sliderDrawingRect.origin.x = (250 - 32 * 2 - 10) * [self floatValue] + 21; 28 | return sliderDrawingRect; 29 | } 30 | 31 | - (void)drawBarInside:(NSRect)aRect flipped:(BOOL)flipped 32 | { 33 | [backImage drawInRect:backFrame 34 | fromRect:backFrame 35 | operation:NSCompositeSourceOver 36 | fraction:1.0]; 37 | } 38 | 39 | - (void)drawKnob:(NSRect)knobRect 40 | { 41 | CGFloat midX = NSMidX(knobRect); 42 | sliderDrawingRect.origin.x = midX - 16; 43 | [sliderImage drawInRect:knobRect 44 | fromRect:sliderRect 45 | operation:NSCompositeSourceOver 46 | fraction:1.0]; 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /diumoo/ui/DMPrefsPanel/DMPrefsPanelDataProvider.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMPrefsPanelDataProvider.h 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-6-16. 6 | // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "DMShortcutsHandler.h" 10 | #import "MASShortcutView.h" 11 | #import "PLTabPreferenceControl.h" 12 | #import 13 | 14 | #define PANEL_VIEW_COUNT 5 15 | 16 | #define GENERAL_PANEL_ID 0 17 | #define ACCOUNT_PANEL_ID 1 18 | #define KEYBINDINGS_PANNEL_ID 2 19 | #define SPACE_PANEL_ID 3 20 | #define INFO_PANEL_ID 4 21 | 22 | @interface DMPrefsPanelDataProvider : NSObject { 23 | // tab controller ( file owner ) 24 | IBOutlet PLTabPreferenceControl* tabcontroller; 25 | 26 | //-----------view outlet ----------- 27 | IBOutlet NSView* general; 28 | IBOutlet NSView* login; 29 | IBOutlet NSView* account; 30 | IBOutlet NSView* keybindings; 31 | IBOutlet NSView* info; 32 | IBOutlet NSView* diumoohelper; 33 | //---------------------------------- 34 | 35 | //------- General view outlet ------ 36 | IBOutlet NSButton* forceGrowl; 37 | IBOutlet NSPopUpButton* muiscQuality; 38 | 39 | //------- login view outlet -------- 40 | IBOutlet NSProgressIndicator* indicator; 41 | IBOutlet NSProgressIndicator* loginIndicator; 42 | IBOutlet NSButton* captchaButton; 43 | IBOutlet NSButton* submitButton; 44 | IBOutlet NSButton* resetButton; 45 | IBOutlet NSTextField* email; 46 | IBOutlet NSSecureTextField* password; 47 | IBOutlet NSTextField* captcha; 48 | IBOutlet NSTextField* errorLabel; 49 | //---------------------------------- 50 | 51 | //------ account view outlet ------- 52 | IBOutlet NSButton* userIconButton; 53 | IBOutlet NSTextField* usernameTextField; 54 | //---------------------------------- 55 | 56 | //------ key bindings outlet ------- 57 | IBOutlet MASShortcutView* playShortcut; 58 | IBOutlet MASShortcutView* skipShortcut; 59 | IBOutlet MASShortcutView* rateShortcut; 60 | IBOutlet MASShortcutView* banShortcut; 61 | IBOutlet MASShortcutView* togglePanelShortcut; 62 | IBOutlet MASShortcutView* showPrefsPanel; 63 | 64 | //------ info outlet --------------- 65 | IBOutlet NSTextField* displayName; 66 | IBOutlet NSTextField* version; 67 | IBOutlet NSTextField* developer; 68 | } 69 | 70 | @property (copy) NSString* captcha_code; 71 | 72 | - (IBAction)loginAction:(id)sender; 73 | - (IBAction)logoutAction:(id)sender; 74 | - (IBAction)donation:(id)sender; 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /diumoo/ui/DMSearchPanel/DMSearchItemView.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMSearchItemView.h 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-10-21. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface DMSearchCollectionView : NSCollectionView 12 | @property (nonatomic, unsafe_unretained) id target; 13 | - (NSManagedObject*)selectedItem; 14 | @end 15 | 16 | @interface DMSearchCollectionViewItem : NSCollectionViewItem 17 | 18 | @end 19 | 20 | @interface DMSearchItemView : NSView 21 | @property (readwrite) BOOL selected; 22 | @end 23 | -------------------------------------------------------------------------------- /diumoo/ui/DMSearchPanel/DMSearchItemView.m: -------------------------------------------------------------------------------- 1 | // 2 | // DMSearchItemView.m 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-10-21. 6 | // 7 | // 8 | 9 | #import "DMSearchItemView.h" 10 | 11 | @implementation DMSearchCollectionView 12 | @synthesize target; 13 | 14 | - (void)mouseDown:(NSEvent*)theEvent 15 | { 16 | [super mouseDown:theEvent]; 17 | 18 | if (theEvent.clickCount == 2) { 19 | NSManagedObject* songObject = [self selectedItem]; 20 | if (songObject) { 21 | 22 | NSString* sid = [songObject valueForKey:@"sid"]; 23 | NSString* ssid = [songObject valueForKey:@"ssid"]; 24 | NSString* start = [NSString stringWithFormat:@"%@g%@g0", sid, ssid]; 25 | 26 | NSDictionary* info = @{ 27 | @"type" : @"song", 28 | @"start" : start 29 | }; 30 | 31 | [[NSNotificationCenter defaultCenter] 32 | postNotificationName:@"playspecial" 33 | object:self 34 | userInfo:info]; 35 | } 36 | } 37 | } 38 | 39 | - (NSManagedObject*)selectedItem 40 | { 41 | if ([[self selectionIndexes] count]) { 42 | NSCollectionViewItem* selectedItem = [self itemAtIndex: 43 | [[self selectionIndexes] firstIndex]]; 44 | return selectedItem.representedObject; 45 | } 46 | else 47 | return nil; 48 | } 49 | 50 | @end 51 | 52 | @implementation DMSearchCollectionViewItem 53 | - (void)setSelected:(BOOL)selected 54 | { 55 | [super setSelected:selected]; 56 | [(DMSearchItemView*)self.view setSelected:selected]; 57 | [self.view setNeedsDisplay:YES]; 58 | } 59 | @end 60 | 61 | @implementation DMSearchItemView 62 | 63 | - (id)initWithFrame:(NSRect)frame 64 | { 65 | self = [super initWithFrame:frame]; 66 | if (self) { 67 | // Initialization code here. 68 | } 69 | 70 | return self; 71 | } 72 | 73 | - (void)drawRect:(NSRect)dirtyRect 74 | { 75 | if (self.selected) { 76 | [[NSColor colorWithCalibratedRed:0.75 green:1.0 blue:0.75 alpha:1.0] setFill]; 77 | NSRectFill([self bounds]); 78 | } 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /diumoo/ui/DMSearchPanel/DMSearchPanelController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMSearchPanelControllerWindowController.h 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-10-9. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface DMSearchPanelController : NSWindowController 12 | @property (readonly) NSManagedObjectContext* contextObject; 13 | 14 | + (DMSearchPanelController*)sharedSearchPanel; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /diumoo/ui/DMSearchPanel/DMSearchPanelController.m: -------------------------------------------------------------------------------- 1 | // 2 | // DMSearchPanelControllerWindowController.m 3 | // diumoo 4 | // 5 | // Created by Shanzi on 12-10-9. 6 | // 7 | // 8 | 9 | #import "DMPlayRecordHandler.h" 10 | #import "DMSearchItemView.h" 11 | #import "DMSearchPanelController.h" 12 | 13 | static DMSearchPanelController* sharedSearchPanel; 14 | 15 | @interface DMSearchPanelController () { 16 | IBOutlet DMSearchCollectionView* collectionview; 17 | IBOutlet NSArrayController* arrayController; 18 | } 19 | @end 20 | 21 | @implementation DMSearchPanelController 22 | 23 | - (id)init 24 | { 25 | self = [super initWithWindowNibName:@"DMSearchPanelController"]; 26 | if (self) { 27 | } 28 | 29 | return self; 30 | } 31 | 32 | - (void)windowDidLoad 33 | { 34 | [super windowDidLoad]; 35 | [self.window setLevel:NSModalPanelWindowLevel]; 36 | [arrayController setValue:@[ 37 | [NSSortDescriptor sortDescriptorWithKey:@"date" ascending:NO] 38 | ] 39 | forKey:@"sortDescriptors"]; 40 | } 41 | 42 | + (DMSearchPanelController*)sharedSearchPanel 43 | { 44 | if (sharedSearchPanel) 45 | return sharedSearchPanel; 46 | else { 47 | sharedSearchPanel = [[DMSearchPanelController alloc] init]; 48 | return sharedSearchPanel; 49 | } 50 | } 51 | 52 | - (NSManagedObjectContext*)contextObject 53 | { 54 | return [DMPlayRecordHandler sharedRecordHandler].context; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /diumoo/ui/DockIcon/DockImageProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DockImageProvider.swift 3 | // diumoo 4 | // 5 | // Created by leave on 22/12/2017. 6 | // 7 | 8 | import AppKit 9 | 10 | class DockImageProvider { 11 | 12 | static func albumImage(from playableItem: DMPlayableItem) -> NSImage? { 13 | if playableItem.isUsingDefaultFailedAlbumPhoto { 14 | return nil 15 | } 16 | return self.processedImage(playableItem.cover) 17 | } 18 | 19 | private static func processedImage(_ image: NSImage?) -> NSImage? { 20 | guard let image = image else { 21 | return nil 22 | } 23 | 24 | let edge = min(image.size.height, image.size.width, 100) 25 | let radius = 0.2 * edge 26 | let margin = 0.05 * edge 27 | return image.resizeImage(to: CGSize(width: edge, height: edge), 28 | radius: radius, 29 | margin: margin) 30 | } 31 | 32 | } 33 | 34 | extension NSImage { 35 | 36 | fileprivate func resizeImage(to size: CGSize, radius: CGFloat, margin: CGFloat) -> NSImage { 37 | let newImage = NSImage(size: size) 38 | newImage.lockFocus() 39 | 40 | var visiableRect = NSRect.zero 41 | let originalSize = self.size 42 | if originalSize.height > originalSize.width { 43 | visiableRect.origin.x = 0 44 | visiableRect.origin.y = (originalSize.height - originalSize.width) / 2 45 | visiableRect.size.width = originalSize.width 46 | visiableRect.size.height = originalSize.width 47 | } else { 48 | visiableRect.origin.y = 0 49 | visiableRect.origin.x = (originalSize.width - originalSize.height) / 2 50 | visiableRect.size.width = originalSize.height 51 | visiableRect.size.height = originalSize.height 52 | } 53 | 54 | let ctx = NSGraphicsContext.current() 55 | ctx?.imageInterpolation = .high 56 | let imageFrame = NSRect(x: margin, y: margin, width: size.width-margin*2, height: size.height-margin*2) 57 | 58 | let backShape = NSBezierPath(roundedRect: imageFrame.insetBy(dx: -margin*0.5, dy: -margin*0.5) 59 | , xRadius: radius*1.1, yRadius: radius*1.1) 60 | NSColor.white.set() 61 | backShape.fill() 62 | 63 | let clipPath = NSBezierPath(roundedRect: imageFrame, xRadius: radius, yRadius: radius) 64 | clipPath.windingRule = .evenOddWindingRule 65 | clipPath.addClip() 66 | 67 | 68 | self.draw(in: imageFrame, 69 | from: visiableRect, 70 | operation: .copy, fraction: 1) 71 | newImage.unlockFocus() 72 | return newImage 73 | } 74 | 75 | } 76 | 77 | -------------------------------------------------------------------------------- /diumoo/zh-Hans.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 2 | {\fonttbl\f0\fnil\fcharset134 STHeitiSC-Light;\f1\fswiss\fcharset0 Helvetica;\f2\fswiss\fcharset0 ArialMT; 3 | } 4 | {\colortbl;\red255\green255\blue255;\red63\green0\blue63;} 5 | \paperw11905\paperh16837\vieww9600\viewh8400\viewkind0 6 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\partightenfactor0 7 | 8 | \f0\b\fs24 \cf0 \'bf\'aa\'b7\'a2 9 | \f1 : 10 | \b0 \ 11 | ant_sz, Anakin(Yancheng Zheng)\ 12 | \ 13 | 14 | \f0\b \'bd\'e7\'c3\'e6\'c9\'e8\'bc\'c6 15 | \f1 : 16 | \b0 \ 17 | 18 | \f2\fs28 \cf2 chumsdock 19 | \f0 , 20 | \f1\fs24 \cf0 ant_sz\ 21 | \ 22 | \ 23 | \ 24 | } -------------------------------------------------------------------------------- /diumoo/zh-Hans.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /diumoo/zh-Hans.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* No comment provided by engineer. */ 2 | "PLAY_ALBUM_FAILED" = "播放专辑失败"; 3 | 4 | /* No comment provided by engineer. */ 5 | "PLAY_ALBUM_FAILED_DETAIL" = "没能成功播放您指定的专辑"; 6 | 7 | /* No comment provided by engineer. */ 8 | "PRIVATE_MHZ" = "私人兆赫"; 9 | 10 | /* No comment provided by engineer. */ 11 | "SOUNDTRACK_MHZ" = "电影原声"; 12 | 13 | /* No comment provided by engineer. */ 14 | "EXPORT" = "导出"; 15 | 16 | /* No comment provided by engineer. */ 17 | "EXPORT_PLAY_RECORD" = "导出播放历史"; 18 | 19 | /* No comment provided by engineer. */ 20 | "IMPORT" = "导入"; 21 | 22 | /* 导入失败 */ 23 | "IMPORT_FAILED" = "导入失败"; 24 | 25 | /* 导入播放记录失败,您试图导入的文件类型不正确或者文件已损坏。 */ 26 | "IMPORT_FAILED_DETAIL" = "导入播放历史失败,您试图导入的文件类型不正确或文件已损坏"; 27 | 28 | /* 导入播放记录 */ 29 | "IMPORT_PLAY_RECORD" = "导入播放记录"; 30 | 31 | /* No comment provided by engineer. */ 32 | "OPEN_URL_FAILED" = "打开 URL 失败"; 33 | 34 | /* 未能成功打开您指定的URL,可能是您试图播放的专辑、音乐家或电影原声不可用。 */ 35 | "OPEN_URL_FAILED_DETAIL" = "未能成功打开您指定的 URL ,可能是您试图播放的专辑、音乐家或电影原声不可用。"; 36 | 37 | /* 查看详细 */ 38 | "SHOW_DETAIL" = "查看详细"; 39 | 40 | /* No comment provided by engineer. */ 41 | "SUCCESS_EXPORT" = "成功导出"; 42 | 43 | /* 成功导出 %ld 首歌曲的播放记录。 */ 44 | "SUCCESS_EXPORT_DETAIL" = "成功导出 %ld 首歌曲的播放记录。"; 45 | 46 | /* No comment provided by engineer. */ 47 | "SUCCESS_IMPORT" = "成功导入"; 48 | 49 | /* 成功导入了 %ld 个歌曲记录,失败 %ld 个。 */ 50 | "SUCCESS_IMPORT_DETAIL" = "成功导入了 %ld 个歌曲记录,失败 %ld 个。"; 51 | 52 | /* No comment provided by engineer. */ 53 | "IMPORT_PLAY_RECORD" = "导入播放历史"; 54 | 55 | /* No comment provided by engineer. */ 56 | "IMPORT_PLAY_RECORD_DETAIL" = "您刚刚打开的一个diumoo播放记录文件,是否导入这些记录?"; 57 | 58 | /* No comment provided by engineer. */ 59 | "NO" = "否"; 60 | 61 | /* No comment provided by engineer. */ 62 | "OPEN_FILE_FAILED" = "打开文件失败"; 63 | 64 | /* No comment provided by engineer. */ 65 | "YES" = "是"; 66 | 67 | /* public */ 68 | "LOCALIZED_PUBLIC_LIST_NAME" = "public"; 69 | 70 | /* 发生网络错误,请尝试重启应用 */ 71 | "NETWORK_ERROR_INDICADTE_STRING"="连接超时,请尝试重启应用"; 72 | 73 | /* 专辑:《%@》 */ 74 | "ALBUM_DOCUMENT_TITLE" = "专辑:《%@》"; 75 | 76 | /* 专辑名 */ 77 | "DETAIL_ALBUM" = "专辑名"; 78 | 79 | /* 唱片数 */ 80 | "DETAIL_DISCS" = "唱片数"; 81 | 82 | /* 豆瓣评分 */ 83 | "DETAIL_DOUBAN_RATING" = "豆瓣评分"; 84 | 85 | /* 条形码 */ 86 | "DETAIL_EAN" = "条形码"; 87 | 88 | /* 介质 */ 89 | "DETAIL_MEDIA" = "介质"; 90 | 91 | /* 发行时间 */ 92 | "DETAIL_PUBDATE" = "发行时间"; 93 | 94 | /* 出版者 */ 95 | "DETAIL_PUBLISHER" = "出版者"; 96 | 97 | /* %@ (%@ 人评价) */ 98 | "DETAIL_RATES" = "%@ (%@ 人评分)"; 99 | 100 | /* 歌手 */ 101 | "DETAIL_SINGER" = "歌手"; 102 | 103 | /* 版本特性 */ 104 | "DETAIL_VERSION" = "版本特性"; 105 | 106 | /* 获取详细信息失败 */ 107 | "GET_DETAIL_FAILED" = "获取详细信息失败"; 108 | 109 | /* 解析信息失败 */ 110 | "PARSE_DETAIL_FAILED" = "解析信息失败"; 111 | 112 | /* 解析信息成功! */ 113 | "PARSE_DETAIL_SUCCESS" = "解析信息成功"; 114 | 115 | /* 简介 */ 116 | "DETAIL_ABSTRACT" = "简介"; 117 | 118 | /* 专辑信息 */ 119 | "DETAIL_ALBUM_INFO" = "专辑信息"; 120 | 121 | /* 曲目 */ 122 | "DETAIL_SONGS" = "曲目"; 123 | 124 | /* 未知 */ 125 | "DETAIL_UNKNOWN" = "未知"; 126 | 127 | /* 需要验证码 */ 128 | "INVALID_CAPTCHA_CODE" = "未获取验证码"; 129 | 130 | /* 验证码不能为空 */ 131 | "CAPTCHA_MUST_NOT_BE_EMPTY" = "验证码不能为空"; 132 | 133 | /* 邮箱不能为空 */ 134 | "EMAIL_MUST_NOT_BE_EMPTY" = "电子邮件不能为空"; 135 | 136 | /* 获取失败,请重试 */ 137 | "FETCH_CAPTCHA_FAILED" = "获取失败,请重试"; 138 | 139 | /* 登陆失败! */ 140 | "LOGIN_FAILED" = "登陆失败!"; 141 | 142 | /* 密码不能为空 */ 143 | "PASSWORD_MUST_NOT_BE_EMPTY" = "密码不能为空"; 144 | 145 | /* 关于 */ 146 | "PREF_ABOUT" = "关于"; 147 | 148 | /* 账户 */ 149 | "PREF_ACCOUNT" = "账号"; 150 | 151 | /* 通用 */ 152 | "PREF_GENERAL" = "通用"; 153 | 154 | /* 快捷键 */ 155 | "PREF_SHORTCUTS" = "快捷键"; 156 | 157 | /* 详细信息 */ 158 | "DETAIL" = "详细信息"; 159 | 160 | /* 预览 */ 161 | "PREVIEW" = "预览"; 162 | 163 | /* 豆瓣FM PRO */ 164 | "PRO_NEED_BUY_TITLE" = "豆瓣FM Pro"; 165 | 166 | /* 豆瓣FM PRO 说明 */ 167 | "PRO_NEED_BUY" = "您只有在豆瓣购买了豆瓣FM Pro 的用户才能收听更高音质的音乐。\n\n豆瓣FM Pro 服务由豆瓣官方提供,与 diumoo 无直接联系。\n\n您想要打开豆瓣FM Pro的购买页面么?"; 168 | 169 | /* 音乐比特率改变 */ 170 | "BITRATE_CHANGED" = "音乐比特率改变"; 171 | 172 | /* 音乐比特率改变到 */ 173 | "BITRATE_CHANGED_TO_VALUE" = "当前音乐的比特率为:"; 174 | 175 | /* 分享链接已复制 */ 176 | "SHARE_LINK_TITLE" = "分享链接已复制"; 177 | 178 | -------------------------------------------------------------------------------- /extern/PLTabPreference/PLTabPreferenceControl.h: -------------------------------------------------------------------------------- 1 | // 2 | // PLTabPreferenceControl.h 3 | // PLTabPreferencePanel 4 | // 5 | // Created by xhan on 3/23/11. 6 | // Copyright 2011 Baidu.com. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @protocol PLTabPreferenceDelegate; 13 | 14 | @interface PLTabPreferenceControl : NSWindowController 15 | { 16 | @private 17 | NSToolbar *toolbar; 18 | NSArray* prefIdentifyAry; 19 | id __strong delegate; 20 | } 21 | 22 | @property(nonatomic,strong) IBOutlet id delegate; 23 | 24 | +(PLTabPreferenceControl*) sharedPreferenceController; 25 | +(void) showPrefsAtIndex:(NSInteger) index; 26 | 27 | - (void)selectPanelAtIndex:(NSInteger)index; 28 | 29 | @end 30 | 31 | 32 | @protocol PLTabPreferenceDelegate 33 | 34 | @required 35 | 36 | - (int)countOfPreferencePanels; 37 | - (NSString*)titleForPreferencePanelAt:(NSInteger)index; 38 | - (NSImage*)imageForPreferencePanelAt:(NSInteger)index; 39 | - (NSView*)panelViewForPreferencePanelAt:(NSInteger)index; 40 | 41 | @optional 42 | - (NSString*)identifyForPreferencePanelAt:(NSInteger)index; 43 | - (void)prefViewWillAppear:(NSView*)view atIndex:(NSInteger)index; 44 | - (void)prefViewDidAppear:(NSView*)view atIndex:(NSInteger)index; 45 | @end -------------------------------------------------------------------------------- /extern/PLTabPreference/PLTabPreferenceControl.m: -------------------------------------------------------------------------------- 1 | // 2 | // PLTabPreferenceControl.m 3 | // PLTabPreferencePanel 4 | // 5 | // Created by xhan on 3/23/11. 6 | // Copyright 2011 Baidu.com. All rights reserved. 7 | // 8 | 9 | 10 | #import "PLTabPreferenceControl.h" 11 | 12 | static PLTabPreferenceControl* shared; 13 | 14 | @interface PLTabPreferenceControl() 15 | - (void)switchPanel:(id)sender; 16 | @end 17 | 18 | 19 | @implementation PLTabPreferenceControl 20 | @synthesize delegate; 21 | 22 | 23 | +(PLTabPreferenceControl*) sharedPreferenceController 24 | { 25 | if (shared) { 26 | return shared; 27 | } 28 | else { 29 | shared = [[PLTabPreferenceControl alloc] initWithWindowNibName:@"DMPrefsPanel"]; 30 | return shared; 31 | } 32 | } 33 | 34 | +(void)showPrefsAtIndex:(NSInteger)index 35 | { 36 | [PLTabPreferenceControl sharedPreferenceController]; 37 | [shared showWindow:nil]; 38 | [shared selectPanelAtIndex:index]; 39 | [NSApp activateIgnoringOtherApps:YES]; 40 | } 41 | 42 | 43 | - (id)initWithWindow:(NSWindow *)window 44 | { 45 | self = [super initWithWindow:window]; 46 | if (self) { 47 | // Initialization code here. 48 | } 49 | 50 | return self; 51 | } 52 | 53 | 54 | - (void)windowDidLoad 55 | { 56 | [super windowDidLoad]; 57 | } 58 | 59 | 60 | - (void)awakeFromNib{ 61 | 62 | NSWindow* theWin = [self window]; 63 | 64 | [theWin setShowsToolbarButton:NO]; 65 | // [theWin setShowsResizeIndicator:NO]; 66 | 67 | toolbar = [[NSToolbar alloc] initWithIdentifier:NSStringFromClass([self class])]; 68 | [toolbar setAllowsUserCustomization:NO]; 69 | [toolbar setShowsBaselineSeparator:YES]; 70 | [toolbar setDelegate:self]; 71 | [[self window] setToolbar:toolbar]; 72 | 73 | // [toolbar validateVisibleItems]; 74 | 75 | [self selectPanelAtIndex:0]; 76 | } 77 | 78 | ///////////////////////////////////////////// 79 | #pragma TabPreference methods 80 | - (void)switchPanel:(id)sender 81 | { 82 | NSView *viewToShow = [delegate panelViewForPreferencePanelAt:(NSInteger)[sender tag]]; 83 | NSWindow* theWin = [self window]; 84 | 85 | 86 | if (viewToShow && ([theWin contentView] != viewToShow.superview)) { 87 | 88 | for (NSView*v in [[theWin contentView] subviews]) { 89 | [v removeFromSuperview]; 90 | } 91 | 92 | viewToShow.alphaValue = 0; 93 | 94 | //will appear 95 | if ([delegate respondsToSelector:@selector(prefViewWillAppear:atIndex:)]) { 96 | [delegate prefViewWillAppear:viewToShow atIndex:[sender tag]]; 97 | } 98 | 99 | [toolbar setSelectedItemIdentifier:[sender itemIdentifier]]; 100 | 101 | NSRect newFrame = [theWin frameRectForContentRect:[viewToShow bounds]]; 102 | NSRect oldFrame = [theWin frame]; 103 | 104 | newFrame.origin = oldFrame.origin; 105 | newFrame.origin.y -= (newFrame.size.height - oldFrame.size.height); 106 | [[theWin contentView] addSubview:viewToShow]; 107 | 108 | 109 | 110 | 111 | [theWin setFrame:newFrame display:YES animate:YES]; 112 | [viewToShow.animator setAlphaValue:1.0]; 113 | 114 | [theWin setTitle:[sender label]]; 115 | 116 | 117 | if ([delegate respondsToSelector:@selector(prefViewDidAppear:atIndex:)]) { 118 | [delegate prefViewDidAppear:viewToShow atIndex:[sender tag]]; 119 | } 120 | 121 | } 122 | } 123 | 124 | - (void)selectPanelAtIndex:(NSInteger)index 125 | { 126 | NSToolbarItem* item = [toolbar items][index]; 127 | if (item) { 128 | [self switchPanel:item]; 129 | } 130 | } 131 | 132 | ///////////////////////////////////////////// 133 | #pragma toolbar delegates 134 | - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar 135 | { 136 | if (prefIdentifyAry) { 137 | return prefIdentifyAry; 138 | } 139 | if (self.delegate) { 140 | NSInteger count = [self.delegate countOfPreferencePanels]; 141 | NSMutableArray* array = [NSMutableArray arrayWithCapacity:count]; 142 | for (NSInteger i = 0; i< count; i++) { 143 | NSString* identify; 144 | if ([delegate respondsToSelector:@selector(identifyForPreferencePanelAt:)]) { 145 | identify = [delegate identifyForPreferencePanelAt:i]; 146 | }else{ 147 | identify = [NSString stringWithFormat:@"%ld",i]; 148 | } 149 | [array addObject:identify]; 150 | } 151 | prefIdentifyAry = [[NSArray alloc] initWithArray:array]; 152 | return prefIdentifyAry; 153 | } 154 | return nil; 155 | } 156 | 157 | - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)atoolbar 158 | { 159 | return [self toolbarAllowedItemIdentifiers:toolbar]; 160 | } 161 | - (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)atoolbar 162 | { 163 | return [self toolbarAllowedItemIdentifiers:toolbar]; 164 | } 165 | 166 | - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag 167 | { 168 | 169 | NSUInteger index = [prefIdentifyAry indexOfObject:itemIdentifier]; 170 | if (index == NSNotFound) { 171 | return nil; 172 | } 173 | NSToolbarItem *item = nil; 174 | 175 | item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier]; 176 | [item setLabel:[delegate titleForPreferencePanelAt:(NSInteger)index]]; 177 | [item setImage:[delegate imageForPreferencePanelAt:(NSInteger)index]]; 178 | [item setTag:index]; 179 | 180 | [item setTarget:self]; 181 | [item setAction:@selector(switchPanel:)]; 182 | [item setAutovalidates:NO]; 183 | 184 | return item; 185 | } 186 | 187 | 188 | @end 189 | -------------------------------------------------------------------------------- /extern/SSKeychain/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2011 Sam Soffes. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /extern/SSKeychain/SSKeychain.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSKeychain.m 3 | // SSToolkit 4 | // 5 | // Created by Sam Soffes on 5/19/10. 6 | // Copyright (c) 2009-2011 Sam Soffes. All rights reserved. 7 | // 8 | 9 | #import "SSKeychain.h" 10 | 11 | NSString *const kSSKeychainErrorDomain = @"com.samsoffes.sskeychain"; 12 | 13 | NSString *const kSSKeychainAccountKey = @"acct"; 14 | NSString *const kSSKeychainCreatedAtKey = @"cdat"; 15 | NSString *const kSSKeychainClassKey = @"labl"; 16 | NSString *const kSSKeychainDescriptionKey = @"desc"; 17 | NSString *const kSSKeychainLabelKey = @"labl"; 18 | NSString *const kSSKeychainLastModifiedKey = @"mdat"; 19 | NSString *const kSSKeychainWhereKey = @"svce"; 20 | 21 | #if __IPHONE_4_0 && TARGET_OS_IPHONE 22 | CFTypeRef SSKeychainAccessibilityType = NULL; 23 | #endif 24 | 25 | @interface SSKeychain () 26 | + (NSMutableDictionary *)_queryForService:(NSString *)service account:(NSString *)account; 27 | @end 28 | 29 | @implementation SSKeychain 30 | 31 | #pragma mark - Getting Accounts 32 | 33 | + (NSArray *)allAccounts { 34 | return [self accountsForService:nil error:nil]; 35 | } 36 | 37 | 38 | + (NSArray *)allAccounts:(NSError **)error { 39 | return [self accountsForService:nil error:error]; 40 | } 41 | 42 | 43 | + (NSArray *)accountsForService:(NSString *)service { 44 | return [self accountsForService:service error:nil]; 45 | } 46 | 47 | 48 | + (NSArray *)accountsForService:(NSString *)service error:(NSError **)error { 49 | OSStatus status = SSKeychainErrorBadArguments; 50 | NSMutableDictionary *query = [self _queryForService:service account:nil]; 51 | #if __has_feature(objc_arc) 52 | [query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes]; 53 | [query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit]; 54 | #else 55 | query[(id)kSecReturnAttributes] = (id)kCFBooleanTrue; 56 | query[(id)kSecMatchLimit] = (id)kSecMatchLimitAll; 57 | #endif 58 | 59 | CFTypeRef result = NULL; 60 | #if __has_feature(objc_arc) 61 | status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); 62 | #else 63 | status = SecItemCopyMatching((CFDictionaryRef)query, &result); 64 | #endif 65 | if (status != noErr && error != NULL) { 66 | *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil]; 67 | return nil; 68 | } 69 | 70 | #if __has_feature(objc_arc) 71 | return (__bridge_transfer NSArray *)result; 72 | #else 73 | return [(NSArray *)result autorelease]; 74 | #endif 75 | } 76 | 77 | 78 | #pragma mark - Getting Passwords 79 | 80 | + (NSString *)passwordForService:(NSString *)service account:(NSString *)account { 81 | return [self passwordForService:service account:account error:nil]; 82 | } 83 | 84 | 85 | + (NSString *)passwordForService:(NSString *)service account:(NSString *)account error:(NSError **)error { 86 | NSData *data = [self passwordDataForService:service account:account error:error]; 87 | if (data.length > 0) { 88 | NSString *string = [[NSString alloc] initWithData:(NSData *)data encoding:NSUTF8StringEncoding]; 89 | #if !__has_feature(objc_arc) 90 | [string autorelease]; 91 | #endif 92 | return string; 93 | } 94 | 95 | return nil; 96 | } 97 | 98 | 99 | + (NSData *)passwordDataForService:(NSString *)service account:(NSString *)account { 100 | return [self passwordDataForService:service account:account error:nil]; 101 | } 102 | 103 | 104 | + (NSData *)passwordDataForService:(NSString *)service account:(NSString *)account error:(NSError **)error { 105 | OSStatus status = SSKeychainErrorBadArguments; 106 | if (!service || !account) { 107 | if (error) { 108 | *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil]; 109 | } 110 | return nil; 111 | } 112 | 113 | CFTypeRef result = NULL; 114 | NSMutableDictionary *query = [self _queryForService:service account:account]; 115 | #if __has_feature(objc_arc) 116 | [query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData]; 117 | [query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit]; 118 | status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); 119 | #else 120 | query[(id)kSecReturnData] = (id)kCFBooleanTrue; 121 | query[(id)kSecMatchLimit] = (id)kSecMatchLimitOne; 122 | status = SecItemCopyMatching((CFDictionaryRef)query, &result); 123 | #endif 124 | 125 | if (status != noErr && error != NULL) { 126 | *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil]; 127 | return nil; 128 | } 129 | 130 | #if __has_feature(objc_arc) 131 | return (__bridge_transfer NSData *)result; 132 | #else 133 | return [(NSData *)result autorelease]; 134 | #endif 135 | } 136 | 137 | 138 | #pragma mark - Deleting Passwords 139 | 140 | + (BOOL)deletePasswordForService:(NSString *)service account:(NSString *)account { 141 | return [self deletePasswordForService:service account:account error:nil]; 142 | } 143 | 144 | 145 | + (BOOL)deletePasswordForService:(NSString *)service account:(NSString *)account error:(NSError **)error { 146 | OSStatus status = SSKeychainErrorBadArguments; 147 | if (service && account) { 148 | NSMutableDictionary *query = [self _queryForService:service account:account]; 149 | #if __has_feature(objc_arc) 150 | status = SecItemDelete((__bridge CFDictionaryRef)query); 151 | #else 152 | status = SecItemDelete((CFDictionaryRef)query); 153 | #endif 154 | } 155 | if (status != noErr && error != NULL) { 156 | *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil]; 157 | } 158 | return (status == noErr); 159 | 160 | } 161 | 162 | 163 | #pragma mark - Setting Passwords 164 | 165 | + (BOOL)setPassword:(NSString *)password forService:(NSString *)service account:(NSString *)account { 166 | return [self setPassword:password forService:service account:account error:nil]; 167 | } 168 | 169 | 170 | + (BOOL)setPassword:(NSString *)password forService:(NSString *)service account:(NSString *)account error:(NSError **)error { 171 | NSData *data = [password dataUsingEncoding:NSUTF8StringEncoding]; 172 | return [self setPasswordData:data forService:service account:account error:error]; 173 | } 174 | 175 | 176 | + (BOOL)setPasswordData:(NSData *)password forService:(NSString *)service account:(NSString *)account { 177 | return [self setPasswordData:password forService:service account:account error:nil]; 178 | } 179 | 180 | 181 | + (BOOL)setPasswordData:(NSData *)password forService:(NSString *)service account:(NSString *)account error:(NSError **)error { 182 | OSStatus status = SSKeychainErrorBadArguments; 183 | if (password && service && account) { 184 | [self deletePasswordForService:service account:account]; 185 | NSMutableDictionary *query = [self _queryForService:service account:account]; 186 | #if __has_feature(objc_arc) 187 | [query setObject:password forKey:(__bridge id)kSecValueData]; 188 | #else 189 | query[(id)kSecValueData] = password; 190 | #endif 191 | 192 | #if __IPHONE_4_0 && TARGET_OS_IPHONE 193 | if (SSKeychainAccessibilityType) { 194 | #if __has_feature(objc_arc) 195 | [query setObject:(id)[self accessibilityType] forKey:(__bridge id)kSecAttrAccessible]; 196 | #else 197 | [query setObject:(id)[self accessibilityType] forKey:(id)kSecAttrAccessible]; 198 | #endif 199 | } 200 | #endif 201 | 202 | #if __has_feature(objc_arc) 203 | status = SecItemAdd((__bridge CFDictionaryRef)query, NULL); 204 | #else 205 | status = SecItemAdd((CFDictionaryRef)query, NULL); 206 | #endif 207 | } 208 | if (status != noErr && error != NULL) { 209 | *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil]; 210 | } 211 | return (status == noErr); 212 | } 213 | 214 | 215 | #pragma mark - Configuration 216 | 217 | #if __IPHONE_4_0 && TARGET_OS_IPHONE 218 | + (CFTypeRef)accessibilityType { 219 | return SSKeychainAccessibilityType; 220 | } 221 | 222 | 223 | + (void)setAccessibilityType:(CFTypeRef)accessibilityType { 224 | CFRetain(accessibilityType); 225 | if (SSKeychainAccessibilityType) { 226 | CFRelease(SSKeychainAccessibilityType); 227 | } 228 | SSKeychainAccessibilityType = accessibilityType; 229 | } 230 | #endif 231 | 232 | 233 | #pragma mark - Private 234 | 235 | + (NSMutableDictionary *)_queryForService:(NSString *)service account:(NSString *)account { 236 | NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:3]; 237 | #if __has_feature(objc_arc) 238 | [dictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; 239 | #else 240 | dictionary[(id)kSecClass] = (id)kSecClassGenericPassword; 241 | #endif 242 | 243 | if (service) { 244 | #if __has_feature(objc_arc) 245 | [dictionary setObject:service forKey:(__bridge id)kSecAttrService]; 246 | #else 247 | dictionary[(id)kSecAttrService] = service; 248 | #endif 249 | } 250 | 251 | if (account) { 252 | #if __has_feature(objc_arc) 253 | [dictionary setObject:account forKey:(__bridge id)kSecAttrAccount]; 254 | #else 255 | dictionary[(id)kSecAttrAccount] = account; 256 | #endif 257 | } 258 | 259 | return dictionary; 260 | } 261 | 262 | @end 263 | -------------------------------------------------------------------------------- /extern/StatusItem/MenubarController.h: -------------------------------------------------------------------------------- 1 | #define STATUS_ITEM_VIEW_WIDTH 24.0 2 | 3 | #pragma mark - 4 | 5 | @class StatusItemView; 6 | 7 | @interface MenubarController : NSObject { 8 | @private 9 | StatusItemView *_statusItemView; 10 | } 11 | 12 | @property (nonatomic) BOOL hasActiveIcon; 13 | @property (nonatomic, readonly) StatusItemView *statusItemView; 14 | 15 | - (NSStatusItem *)statusItem; 16 | -(void) setMixed:(BOOL) mixed; 17 | -(void) setAction:(SEL) action withTarget:(id) target; 18 | @end 19 | -------------------------------------------------------------------------------- /extern/StatusItem/MenubarController.m: -------------------------------------------------------------------------------- 1 | #import "MenubarController.h" 2 | #import "StatusItemView.h" 3 | 4 | @implementation MenubarController 5 | 6 | @synthesize statusItemView = _statusItemView; 7 | 8 | #pragma mark - 9 | 10 | - (id)init 11 | { 12 | if (self = [super init]) 13 | { 14 | // Install status item into the menu bar 15 | NSStatusItem *statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:STATUS_ITEM_VIEW_WIDTH]; 16 | _statusItemView = [[StatusItemView alloc] initWithStatusItem:statusItem]; 17 | 18 | [self setStatusItemImage]; 19 | 20 | [[NSDistributedNotificationCenter defaultCenter] 21 | addObserver:self 22 | selector:@selector(setStatusItemImage) 23 | name:@"AppleInterfaceThemeChangedNotification" 24 | object:nil]; 25 | } 26 | return self; 27 | } 28 | 29 | - (bool) isDarkMode 30 | { 31 | NSString * interfaceStyle = [[NSUserDefaults standardUserDefaults] stringForKey: @"AppleInterfaceStyle"]; 32 | return (interfaceStyle != nil) && [interfaceStyle isEqualToString:@"Dark"]; 33 | } 34 | 35 | - (void) setStatusItemImage 36 | { 37 | if ([self isDarkMode]) { 38 | _statusItemView.image = [NSImage imageNamed:@"dark-status-icon"]; 39 | _statusItemView.mixedImage = [NSImage imageNamed:@"dark-status-icon-mixed"]; 40 | _statusItemView.alternateImage = [NSImage imageNamed:@"dark-status-icon-alt"]; 41 | } else { 42 | _statusItemView.image = [NSImage imageNamed:@"status-icon"]; 43 | _statusItemView.mixedImage = [NSImage imageNamed:@"status-icon-mixed"]; 44 | _statusItemView.alternateImage = [NSImage imageNamed:@"status-icon-alt"]; 45 | } 46 | } 47 | 48 | - (void)dealloc 49 | { 50 | [[NSStatusBar systemStatusBar] removeStatusItem:self.statusItem]; 51 | } 52 | 53 | #pragma mark - 54 | #pragma mark Public accessors 55 | 56 | - (NSStatusItem *)statusItem 57 | { 58 | return self.statusItemView.statusItem; 59 | } 60 | 61 | -(void) setMixed:(BOOL)mixed 62 | { 63 | [_statusItemView setMixed:mixed]; 64 | } 65 | 66 | -(void) setAction:(SEL)action withTarget:(id)target 67 | { 68 | _statusItemView.action = action; 69 | _statusItemView.target = target; 70 | } 71 | 72 | #pragma mark - 73 | 74 | - (BOOL)hasActiveIcon 75 | { 76 | return self.statusItemView.isHighlighted; 77 | } 78 | 79 | - (void)setHasActiveIcon:(BOOL)flag 80 | { 81 | self.statusItemView.isHighlighted = flag; 82 | } 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /extern/StatusItem/StatusItemView.h: -------------------------------------------------------------------------------- 1 | @interface StatusItemView : NSView { 2 | @private 3 | NSImage *_image; 4 | NSImage *_mixedImage; 5 | NSImage *_alternateImage; 6 | NSStatusItem *_statusItem; 7 | BOOL _isHighlighted; 8 | BOOL _isMixed; 9 | SEL _action; 10 | __unsafe_unretained id _target; 11 | } 12 | 13 | - (id)initWithStatusItem:(NSStatusItem *)statusItem; 14 | 15 | @property (nonatomic, strong, readonly) NSStatusItem *statusItem; 16 | @property (nonatomic, strong) NSImage *image; 17 | @property (nonatomic, strong) NSImage *mixedImage; 18 | @property (nonatomic, strong) NSImage *alternateImage; 19 | @property (nonatomic, setter = setHighlighted:) BOOL isHighlighted; 20 | @property (nonatomic, setter = setMixed:) BOOL isMixed; 21 | @property (nonatomic, readonly) NSRect globalRect; 22 | @property (nonatomic) SEL action; 23 | @property (nonatomic, unsafe_unretained) id target; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /extern/StatusItem/StatusItemView.m: -------------------------------------------------------------------------------- 1 | #import "StatusItemView.h" 2 | 3 | @implementation StatusItemView 4 | 5 | @synthesize statusItem = _statusItem; 6 | @synthesize image = _image; 7 | @synthesize mixedImage = _mixedImage; 8 | @synthesize alternateImage = _alternateImage; 9 | @synthesize isHighlighted = _isHighlighted; 10 | @synthesize isMixed = _isMixed; 11 | @synthesize action = _action; 12 | @synthesize target = _target; 13 | 14 | #pragma mark - 15 | 16 | - (id)initWithStatusItem:(NSStatusItem *)statusItem 17 | { 18 | CGFloat itemWidth = [statusItem length]; 19 | CGFloat itemHeight = [[NSStatusBar systemStatusBar] thickness]; 20 | NSRect itemRect = NSMakeRect(0.0, 0.0, itemWidth, itemHeight); 21 | self = [super initWithFrame:itemRect]; 22 | if (self != nil) { 23 | _statusItem = statusItem; 24 | _statusItem.view = self; 25 | } 26 | return self; 27 | } 28 | 29 | 30 | #pragma mark - 31 | 32 | - (void)drawRect:(NSRect)dirtyRect 33 | { 34 | [self.statusItem drawStatusBarBackgroundInRect:dirtyRect withHighlight:self.isHighlighted]; 35 | 36 | NSImage *icon = self.isHighlighted ? self.alternateImage : (self.isMixed ? self.mixedImage:self.image); 37 | NSSize iconSize = [icon size]; 38 | NSRect bounds = self.bounds; 39 | CGFloat iconX = roundf((NSWidth(bounds) - iconSize.width) / 2); 40 | CGFloat iconY = roundf((NSHeight(bounds) - iconSize.height) / 2); 41 | NSPoint iconPoint = NSMakePoint(iconX, iconY); 42 | [icon drawAtPoint:iconPoint fromRect:bounds operation:NSCompositeSourceOver fraction:1.0]; 43 | } 44 | 45 | #pragma mark - 46 | #pragma mark Mouse tracking 47 | 48 | - (void)mouseDown:(NSEvent *)theEvent 49 | { 50 | [NSApp sendAction:self.action to:self.target from:self]; 51 | } 52 | 53 | #pragma mark - 54 | #pragma mark Accessors 55 | 56 | - (void)setHighlighted:(BOOL)newFlag 57 | { 58 | if (_isHighlighted == newFlag) return; 59 | _isHighlighted = newFlag; 60 | [self setNeedsDisplay:YES]; 61 | } 62 | 63 | -(void) setMixed:(BOOL)isMixed 64 | { 65 | if (_isMixed == isMixed) { 66 | return; 67 | } 68 | else { 69 | _isMixed = isMixed; 70 | [self setNeedsDisplay:YES]; 71 | } 72 | } 73 | 74 | #pragma mark - 75 | 76 | - (void)setImage:(NSImage *)newImage 77 | { 78 | if (_image != newImage) { 79 | _image = newImage; 80 | [self setNeedsDisplay:YES]; 81 | } 82 | } 83 | 84 | - (void)setAlternateImage:(NSImage *)newImage 85 | { 86 | if (_alternateImage != newImage) { 87 | _alternateImage = newImage; 88 | if (self.isHighlighted) { 89 | [self setNeedsDisplay:YES]; 90 | } 91 | } 92 | } 93 | 94 | #pragma mark - 95 | 96 | - (NSRect)globalRect 97 | { 98 | return [self.window convertRectToScreen:[self frame]]; 99 | } 100 | 101 | @end 102 | -------------------------------------------------------------------------------- /resources/128kbps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/128kbps.png -------------------------------------------------------------------------------- /resources/128kbps@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/128kbps@2x.png -------------------------------------------------------------------------------- /resources/192kbps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/192kbps.png -------------------------------------------------------------------------------- /resources/192kbps@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/192kbps@2x.png -------------------------------------------------------------------------------- /resources/64kbps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/64kbps.png -------------------------------------------------------------------------------- /resources/64kbps@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/64kbps@2x.png -------------------------------------------------------------------------------- /resources/albumfail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/albumfail.png -------------------------------------------------------------------------------- /resources/albumfail@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/albumfail@2x.png -------------------------------------------------------------------------------- /resources/ban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/ban.png -------------------------------------------------------------------------------- /resources/ban@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/ban@2x.png -------------------------------------------------------------------------------- /resources/ban_mouseover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/ban_mouseover.png -------------------------------------------------------------------------------- /resources/ban_mouseover@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/ban_mouseover@2x.png -------------------------------------------------------------------------------- /resources/chrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/chrome.png -------------------------------------------------------------------------------- /resources/chrome_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/chrome_2x.png -------------------------------------------------------------------------------- /resources/dark-status-icon-alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/dark-status-icon-alt.png -------------------------------------------------------------------------------- /resources/dark-status-icon-alt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/dark-status-icon-alt@2x.png -------------------------------------------------------------------------------- /resources/dark-status-icon-mixed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/dark-status-icon-mixed.png -------------------------------------------------------------------------------- /resources/dark-status-icon-mixed@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/dark-status-icon-mixed@2x.png -------------------------------------------------------------------------------- /resources/dark-status-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/dark-status-icon.png -------------------------------------------------------------------------------- /resources/dark-status-icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/dark-status-icon@2x.png -------------------------------------------------------------------------------- /resources/detail_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/detail_icon.png -------------------------------------------------------------------------------- /resources/detail_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/detail_icon@2x.png -------------------------------------------------------------------------------- /resources/douban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/douban.png -------------------------------------------------------------------------------- /resources/douban@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/douban@2x.png -------------------------------------------------------------------------------- /resources/exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/exit.png -------------------------------------------------------------------------------- /resources/exit@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/exit@2x.png -------------------------------------------------------------------------------- /resources/exit_mouseover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/exit_mouseover.png -------------------------------------------------------------------------------- /resources/exit_mouseover@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/exit_mouseover@2x.png -------------------------------------------------------------------------------- /resources/eye_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/eye_icon.png -------------------------------------------------------------------------------- /resources/eye_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/eye_icon@2x.png -------------------------------------------------------------------------------- /resources/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/facebook.png -------------------------------------------------------------------------------- /resources/facebook@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/facebook@2x.png -------------------------------------------------------------------------------- /resources/fanfou.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/fanfou.png -------------------------------------------------------------------------------- /resources/fanfou@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/fanfou@2x.png -------------------------------------------------------------------------------- /resources/firefox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/firefox.png -------------------------------------------------------------------------------- /resources/firefox_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/firefox_2x.png -------------------------------------------------------------------------------- /resources/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/icon.icns -------------------------------------------------------------------------------- /resources/info_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/info_icon.png -------------------------------------------------------------------------------- /resources/info_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/info_icon@2x.png -------------------------------------------------------------------------------- /resources/menu-heart-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/menu-heart-gray.png -------------------------------------------------------------------------------- /resources/menu-heart-gray@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/menu-heart-gray@2x.png -------------------------------------------------------------------------------- /resources/menu-heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/menu-heart.png -------------------------------------------------------------------------------- /resources/menu-heart@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/menu-heart@2x.png -------------------------------------------------------------------------------- /resources/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/pause.png -------------------------------------------------------------------------------- /resources/pause@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/pause@2x.png -------------------------------------------------------------------------------- /resources/pause_mouseover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/pause_mouseover.png -------------------------------------------------------------------------------- /resources/pause_mouseover@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/pause_mouseover@2x.png -------------------------------------------------------------------------------- /resources/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/play.png -------------------------------------------------------------------------------- /resources/play@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/play@2x.png -------------------------------------------------------------------------------- /resources/play_mouseover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/play_mouseover.png -------------------------------------------------------------------------------- /resources/play_mouseover@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/play_mouseover@2x.png -------------------------------------------------------------------------------- /resources/pro_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/pro_icon.png -------------------------------------------------------------------------------- /resources/pro_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/pro_icon@2x.png -------------------------------------------------------------------------------- /resources/rate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/rate.png -------------------------------------------------------------------------------- /resources/rate@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/rate@2x.png -------------------------------------------------------------------------------- /resources/rate_mouseover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/rate_mouseover.png -------------------------------------------------------------------------------- /resources/rate_mouseover@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/rate_mouseover@2x.png -------------------------------------------------------------------------------- /resources/rate_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/rate_red.png -------------------------------------------------------------------------------- /resources/rate_red@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/rate_red@2x.png -------------------------------------------------------------------------------- /resources/record.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/record.icns -------------------------------------------------------------------------------- /resources/safari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/safari.png -------------------------------------------------------------------------------- /resources/safari_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/safari_2x.png -------------------------------------------------------------------------------- /resources/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/search.png -------------------------------------------------------------------------------- /resources/search@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/search@2x.png -------------------------------------------------------------------------------- /resources/search_mouseover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/search_mouseover.png -------------------------------------------------------------------------------- /resources/search_mouseover@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/search_mouseover@2x.png -------------------------------------------------------------------------------- /resources/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/settings.png -------------------------------------------------------------------------------- /resources/settings@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/settings@2x.png -------------------------------------------------------------------------------- /resources/settings_mouseover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/settings_mouseover.png -------------------------------------------------------------------------------- /resources/settings_mouseover@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/settings_mouseover@2x.png -------------------------------------------------------------------------------- /resources/share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/share.png -------------------------------------------------------------------------------- /resources/share@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/share@2x.png -------------------------------------------------------------------------------- /resources/sina.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/sina.png -------------------------------------------------------------------------------- /resources/sina@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/sina@2x.png -------------------------------------------------------------------------------- /resources/skip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/skip.png -------------------------------------------------------------------------------- /resources/skip@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/skip@2x.png -------------------------------------------------------------------------------- /resources/skip_mouseover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/skip_mouseover.png -------------------------------------------------------------------------------- /resources/skip_mouseover@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/skip_mouseover@2x.png -------------------------------------------------------------------------------- /resources/status-icon-alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/status-icon-alt.png -------------------------------------------------------------------------------- /resources/status-icon-alt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/status-icon-alt@2x.png -------------------------------------------------------------------------------- /resources/status-icon-mixed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/status-icon-mixed.png -------------------------------------------------------------------------------- /resources/status-icon-mixed@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/status-icon-mixed@2x.png -------------------------------------------------------------------------------- /resources/status-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/status-icon.png -------------------------------------------------------------------------------- /resources/status-icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/status-icon@2x.png -------------------------------------------------------------------------------- /resources/time_machine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/time_machine.png -------------------------------------------------------------------------------- /resources/time_machine@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/time_machine@2x.png -------------------------------------------------------------------------------- /resources/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/twitter.png -------------------------------------------------------------------------------- /resources/twitter@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/twitter@2x.png -------------------------------------------------------------------------------- /resources/view_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/view_icon.png -------------------------------------------------------------------------------- /resources/view_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/view_icon@2x.png -------------------------------------------------------------------------------- /resources/volume_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/volume_back.png -------------------------------------------------------------------------------- /resources/volume_back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/volume_back@2x.png -------------------------------------------------------------------------------- /resources/volume_slider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/volume_slider.png -------------------------------------------------------------------------------- /resources/volume_slider@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shanzi/diumoo/cefda3edf61ec2d8c9b5fe314cb0c23e630ea4d6/resources/volume_slider@2x.png -------------------------------------------------------------------------------- /third-party/SMTabBar/InspectorTabBar/NSDictionary+SMKeyValueObserving.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+SMKeyValueObserving.h 3 | // InspectorTabBar 4 | // 5 | // Created by Stephan Michels on 04.02.12. 6 | // Copyright (c) 2012 Stephan Michels Softwareentwicklung und Beratung. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface NSDictionary (SMKeyValueObserving) 13 | 14 | @property (readonly) NSUInteger keyValueChangeKind; 15 | @property (readonly) id keyValueChangeNew; 16 | @property (readonly) id keyValueChangeOld; 17 | @property (readonly) NSIndexSet *keyValueChangeIndexes; 18 | @property (readonly) BOOL keyValueChanged; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /third-party/SMTabBar/InspectorTabBar/NSDictionary+SMKeyValueObserving.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+SMKeyValueObserving.m 3 | // InspectorTabBar 4 | // 5 | // Created by Stephan Michels on 04.02.12. 6 | // Copyright (c) 2012 Stephan Michels Softwareentwicklung und Beratung. All rights reserved. 7 | // 8 | 9 | #import "NSDictionary+SMKeyValueObserving.h" 10 | 11 | 12 | @implementation NSDictionary (SMKeyValueObserving) 13 | 14 | - (NSUInteger)keyValueChangeKind { 15 | return [[self objectForKey:NSKeyValueChangeKindKey] unsignedIntegerValue]; 16 | } 17 | 18 | - (id)keyValueChangeNew { 19 | id value = [self objectForKey:NSKeyValueChangeNewKey]; 20 | if ([value isEqual:[NSNull null]]) { 21 | return nil; 22 | } 23 | return value; 24 | } 25 | 26 | - (id)keyValueChangeOld { 27 | id value = [self objectForKey:NSKeyValueChangeOldKey]; 28 | if ([value isEqual:[NSNull null]]) { 29 | return nil; 30 | } 31 | return value; 32 | } 33 | 34 | - (NSIndexSet *)keyValueChangeIndexes { 35 | return [self objectForKey:NSKeyValueChangeIndexesKey]; 36 | } 37 | 38 | - (BOOL)keyValueChanged { 39 | if ([self keyValueChangeOld] == nil) { 40 | return [self keyValueChangeNew] != nil; 41 | } 42 | return ![[self keyValueChangeNew] isEqual:[self keyValueChangeOld]]; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /third-party/SMTabBar/InspectorTabBar/SMBar.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMBar.h 3 | // InspectorTabBar 4 | // 5 | // Created by Stephan Michels on 12.02.12. 6 | // Copyright (c) 2012 Stephan Michels Softwareentwicklung und Beratung. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMBar : NSView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /third-party/SMTabBar/InspectorTabBar/SMBar.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMBar.m 3 | // InspectorTabBar 4 | // 5 | // Created by Stephan Michels on 12.02.12. 6 | // Copyright (c) 2012 Stephan Michels Softwareentwicklung und Beratung. All rights reserved. 7 | // 8 | 9 | #import "SMBar.h" 10 | 11 | @implementation SMBar 12 | 13 | // using app and window notifications to change gradient for inactive windows, see 14 | // http://code.google.com/p/tlanimatingoutlineview/source/browse/trunk/Classes/TLGradientView.m 15 | - (void)viewWillMoveToWindow:(NSWindow *)newWindow { 16 | [super viewWillMoveToWindow:newWindow]; 17 | 18 | NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 19 | 20 | NSWindow *oldWindow = self.window; 21 | 22 | if (!oldWindow && newWindow) { 23 | [center addObserver:self selector:@selector(windowDidChange:) name:NSApplicationDidBecomeActiveNotification object:NSApp]; 24 | [center addObserver:self selector:@selector(windowDidChange:) name:NSApplicationDidResignActiveNotification object:NSApp]; 25 | } else if (oldWindow && !newWindow) { 26 | [center removeObserver:self name:NSApplicationDidBecomeActiveNotification object:NSApp]; 27 | [center removeObserver:self name:NSApplicationDidResignActiveNotification object:NSApp]; 28 | } 29 | 30 | if (oldWindow) { 31 | [center removeObserver:self name:NSWindowDidResignKeyNotification object:oldWindow]; 32 | [center removeObserver:self name:NSWindowDidBecomeKeyNotification object:oldWindow]; 33 | } 34 | 35 | if (newWindow) { 36 | [center addObserver:self selector:@selector(windowDidChange:) name:NSWindowDidResignKeyNotification object:newWindow]; 37 | [center addObserver:self selector:@selector(windowDidChange:) name:NSWindowDidBecomeKeyNotification object:newWindow]; 38 | } 39 | } 40 | 41 | #pragma mark - Drawing 42 | 43 | - (void)drawRect:(CGRect)rect { 44 | 45 | if ([[self window] isKeyWindow]) { 46 | static NSGradient *gradient = nil; 47 | static NSColor *borderColor = nil; 48 | if (!gradient) { 49 | NSColor *color1 = [NSColor colorWithCalibratedWhite:0.851f alpha:1.0f]; 50 | NSColor *color2 = [NSColor colorWithCalibratedWhite:0.700f alpha:1.0f]; 51 | gradient = [[NSGradient alloc] initWithStartingColor:color1 52 | endingColor:color2]; 53 | borderColor = [NSColor colorWithCalibratedWhite:0.416 alpha:1]; 54 | } 55 | 56 | // Draw bar gradient 57 | [gradient drawInRect:self.bounds angle:90.0]; 58 | 59 | // add noise 60 | [self drawNoisePattern]; 61 | 62 | // Draw drak gray bottom border 63 | [borderColor setStroke]; 64 | [NSBezierPath strokeLineFromPoint:NSMakePoint(NSMinX(self.bounds), NSMaxY(self.bounds) - 0.5f) 65 | toPoint:NSMakePoint(NSMaxX(self.bounds), NSMaxY(self.bounds) - 0.5f)]; 66 | } else { 67 | static NSGradient *gradient = nil; 68 | static NSColor *borderColor = nil; 69 | if (!gradient) { 70 | NSColor *color1 = [NSColor colorWithCalibratedWhite:0.965 alpha:1]; 71 | NSColor *color2 = [NSColor colorWithCalibratedWhite:0.851 alpha:1]; 72 | gradient = [[NSGradient alloc] initWithStartingColor:color1 73 | endingColor:color2]; 74 | borderColor = [NSColor colorWithCalibratedWhite:0.651 alpha:1]; 75 | } 76 | 77 | // Draw bar gradient 78 | [gradient drawInRect:self.bounds angle:90.0]; 79 | 80 | // add noise 81 | [self drawNoisePattern]; 82 | 83 | // Draw drak gray bottom border 84 | [borderColor setStroke]; 85 | [NSBezierPath setDefaultLineWidth:0.0f]; 86 | [NSBezierPath strokeLineFromPoint:NSMakePoint(NSMinX(self.bounds), NSMaxY(self.bounds) - 0.5f) 87 | toPoint:NSMakePoint(NSMaxX(self.bounds), NSMaxY(self.bounds) - 0.5f)]; 88 | } 89 | } 90 | 91 | // add noise pattern, see http://stackoverflow.com/questions/8766239 92 | - (void)drawNoisePattern { 93 | static CGImageRef noisePattern = nil; 94 | if (noisePattern == nil) { 95 | noisePattern = SMNoiseImageCreate(128, 128, 0.015); 96 | } 97 | 98 | [NSGraphicsContext saveGraphicsState]; 99 | 100 | [[NSGraphicsContext currentContext] setCompositingOperation:NSCompositePlusLighter]; 101 | CGRect noisePatternRect = CGRectMake(0.0f, 0.0f, CGImageGetWidth(noisePattern), CGImageGetHeight(noisePattern)); 102 | CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; 103 | CGContextDrawTiledImage(context, noisePatternRect, noisePattern); 104 | 105 | [NSGraphicsContext restoreGraphicsState]; 106 | } 107 | 108 | static CGImageRef SMNoiseImageCreate(NSUInteger width, NSUInteger height, CGFloat factor) { 109 | NSUInteger size = width * height; 110 | char *rgba = (char *)malloc(size); srand(124); 111 | for(NSUInteger i = 0; i < size; i++) { 112 | rgba[i] = rand() % 256 * factor; 113 | } 114 | 115 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); 116 | CGContextRef bitmapContext = CGBitmapContextCreate(rgba, width, height, 8, width, colorSpace, kCGImageAlphaNone); 117 | CFRelease(colorSpace); 118 | free(rgba); 119 | 120 | CGImageRef image = CGBitmapContextCreateImage(bitmapContext); 121 | CFRelease(bitmapContext); 122 | 123 | return image; 124 | } 125 | 126 | - (BOOL)isFlipped { 127 | return YES; 128 | } 129 | 130 | #pragma mark - Notifications 131 | 132 | - (void)windowDidChange:(NSNotification *)notification { 133 | [self setNeedsDisplay:YES]; 134 | } 135 | 136 | @end 137 | -------------------------------------------------------------------------------- /third-party/SMTabBar/InspectorTabBar/SMTabBar.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMTabBar.h 3 | // InspectorTabBar 4 | // 5 | // Created by Stephan Michels on 30.01.12. 6 | // Copyright (c) 2012 Stephan Michels Softwareentwicklung und Beratung. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMBar.h" 11 | 12 | 13 | @class SMTabBarItem; 14 | @protocol SMTabBarDelegate; 15 | 16 | @interface SMTabBar : SMBar 17 | 18 | @property (nonatomic, copy) NSArray *items; 19 | @property (nonatomic, weak) SMTabBarItem *selectedItem; 20 | @property (nonatomic, weak) IBOutlet id delegate; 21 | 22 | @end 23 | 24 | 25 | @protocol SMTabBarDelegate 26 | 27 | @optional 28 | - (BOOL)tabBar:(SMTabBar *)tabBar shouldSelectItem:(SMTabBarItem *)item; 29 | - (void)tabBar:(SMTabBar *)tabBar willSelectItem:(SMTabBarItem *)item; 30 | - (void)tabBar:(SMTabBar *)tabBar didSelectItem:(SMTabBarItem *)item; 31 | 32 | @end -------------------------------------------------------------------------------- /third-party/SMTabBar/InspectorTabBar/SMTabBar.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMTabBar.m 3 | // InspectorTabBar 4 | // 5 | // Created by Stephan Michels on 30.01.12. 6 | // Copyright (c) 2012 Stephan Michels Softwareentwicklung und Beratung. All rights reserved. 7 | // 8 | 9 | #import "SMTabBar.h" 10 | #import "SMTabBarItem.h" 11 | #import "SMTabBarButtonCell.h" 12 | #import "NSDictionary+SMKeyValueObserving.h" 13 | 14 | 15 | #define SMTabBarButtonWidth 32.0f 16 | 17 | @interface SMTabBar () 18 | 19 | @property (nonatomic, copy) NSArray *barButtons; 20 | 21 | - (void)adjustSubviews; 22 | 23 | @end 24 | 25 | @implementation SMTabBar 26 | 27 | static char SMObservationContext; 28 | 29 | #pragma mark - Initialization / Deallocation 30 | 31 | - (id)initWithFrame:(NSRect)frame { 32 | self = [super initWithFrame:frame]; 33 | if (self) { 34 | // Initialization code 35 | 36 | // add observer for properties 37 | [self addObserver:self forKeyPath:@"items" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:&SMObservationContext]; 38 | [self addObserver:self forKeyPath:@"selectedItem" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:&SMObservationContext]; 39 | } 40 | return self; 41 | } 42 | 43 | - (void)dealloc { 44 | // remove observer for properties 45 | [self removeObserver:self forKeyPath:@"items" context:&SMObservationContext]; 46 | [self removeObserver:self forKeyPath:@"selectedItem" context:&SMObservationContext]; 47 | 48 | // unbind button 49 | for (NSButton *button in self.barButtons) { 50 | [button unbind:@"image"]; 51 | [button unbind:@"enabled"]; 52 | [button unbind:@"toolTip"]; 53 | [button unbind:@"keyEquivalent"]; 54 | [button unbind:@"keyEquivalentModifierMask"]; 55 | } 56 | } 57 | 58 | #pragma mark - Actions 59 | 60 | - (void)selectBarButton:(id)sender { 61 | // select a bar button 62 | 63 | NSUInteger itemIndex = [sender tag]; 64 | SMTabBarItem *tabBarItem = [self.items objectAtIndex:itemIndex]; 65 | if ([self.delegate respondsToSelector:@selector(tabBar:shouldSelectItem:)]) { 66 | BOOL shouldSelectItem = [self.delegate tabBar:self shouldSelectItem:tabBarItem]; 67 | if (!shouldSelectItem) { 68 | return; 69 | } 70 | } 71 | if ([self.delegate respondsToSelector:@selector(tabBar:willSelectItem:)]) { 72 | [self.delegate tabBar:self willSelectItem:tabBarItem]; 73 | } 74 | self.selectedItem = tabBarItem; 75 | if ([self.delegate respondsToSelector:@selector(tabBar:didSelectItem:)]) { 76 | [self.delegate tabBar:self didSelectItem:tabBarItem]; 77 | } 78 | } 79 | 80 | #pragma mark - Private methods 81 | 82 | - (void)updateButtons { 83 | // remove old buttons 84 | for (NSButton *button in self.barButtons) { 85 | [button removeFromSuperview]; 86 | 87 | [button unbind:@"image"]; 88 | [button unbind:@"enabled"]; 89 | [button unbind:@"toolTip"]; 90 | [button unbind:@"keyEquivalent"]; 91 | [button unbind:@"keyEquivalentModifierMask"]; 92 | } 93 | self.barButtons = nil; 94 | 95 | // add new buttons 96 | NSMutableArray *newBarButtons = [NSMutableArray arrayWithCapacity:[self.items count]]; 97 | NSUInteger selectedItemIndex = [self.items indexOfObject:self.selectedItem]; 98 | NSUInteger itemIndex = 0; 99 | for (SMTabBarItem *item in self.items) { 100 | NSButton *button = [[NSButton alloc] initWithFrame:NSMakeRect(0.0f, 0.0f, SMTabBarButtonWidth, NSHeight(self.bounds))]; 101 | 102 | // add special button cell for the selected state 103 | button.cell = [[SMTabBarButtonCell alloc] init]; 104 | 105 | // set properties of the button 106 | button.image = item.image; 107 | [button setEnabled:item.enabled]; 108 | button.state = itemIndex == selectedItemIndex ? NSOnState : NSOffState; 109 | button.tag = itemIndex; 110 | button.action = @selector(selectBarButton:); 111 | button.target = self; 112 | [button sendActionOn:NSLeftMouseDownMask]; 113 | [self addSubview:button]; 114 | 115 | // bind button properties to the item properties 116 | [button bind:@"image" toObject:item withKeyPath:@"image" options:nil]; 117 | [button bind:@"enabled" toObject:item withKeyPath:@"enabled" options:nil]; 118 | [button bind:@"toolTip" toObject:item withKeyPath:@"toolTip" options:nil]; 119 | [button bind:@"keyEquivalent" toObject:item withKeyPath:@"keyEquivalent" options:nil]; 120 | [button bind:@"keyEquivalentModifierMask" toObject:item withKeyPath:@"keyEquivalentModifierMask" options:nil]; 121 | 122 | [newBarButtons addObject:button]; 123 | 124 | itemIndex++; 125 | } 126 | self.barButtons = newBarButtons; 127 | 128 | // re-layout buttons 129 | [self adjustSubviews]; 130 | 131 | // pre-select first button 132 | if (![self.items containsObject:self.selectedItem]) { 133 | self.selectedItem = [self.items count] > 0 ? [self.items objectAtIndex:0] : nil; 134 | } 135 | } 136 | 137 | - (void)updateButtonState { 138 | // update button states if the corresponding item is selected 139 | NSUInteger selectedItemIndex = [self.items indexOfObject:self.selectedItem]; 140 | NSUInteger buttonIndex = 0; 141 | for (NSButton *button in self.barButtons) { 142 | button.state = buttonIndex == selectedItemIndex ? NSOnState : NSOffState; 143 | buttonIndex++; 144 | } 145 | } 146 | 147 | #pragma mark - Layout subviews 148 | 149 | - (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize { 150 | [super resizeSubviewsWithOldSize:oldBoundsSize]; 151 | [self adjustSubviews]; 152 | } 153 | 154 | - (void)adjustSubviews { 155 | // layout subviews 156 | NSUInteger numberOfButtons = [self.barButtons count]; 157 | CGFloat completeWidth = numberOfButtons * SMTabBarButtonWidth; 158 | CGFloat offset = floorf((NSWidth(self.bounds) - completeWidth) / 2.0f); 159 | for (NSButton *button in self.barButtons) { 160 | button.frame = NSMakeRect(offset, NSMinY(self.bounds), SMTabBarButtonWidth, NSHeight(self.bounds)); 161 | offset += SMTabBarButtonWidth; 162 | } 163 | } 164 | 165 | #pragma mark - KVO 166 | 167 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 168 | if (context != &SMObservationContext) { 169 | [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 170 | return; 171 | } 172 | 173 | if (![change keyValueChanged]) { 174 | return; 175 | } 176 | 177 | if ([keyPath isEqualToString:@"items"]) { 178 | [self updateButtons]; 179 | } else if ([keyPath isEqualToString:@"selectedItem"]) { 180 | [self updateButtonState]; 181 | } 182 | } 183 | 184 | @end 185 | -------------------------------------------------------------------------------- /third-party/SMTabBar/InspectorTabBar/SMTabBarButtonCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMTabBarButtonCell.h 3 | // InspectorTabBar 4 | // 5 | // Created by Stephan Michels on 04.02.12. 6 | // Copyright (c) 2012 Stephan Michels Softwareentwicklung und Beratung. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMTabBarButtonCell : NSButtonCell 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /third-party/SMTabBar/InspectorTabBar/SMTabBarButtonCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMTabBarButtonCell.m 3 | // InspectorTabBar 4 | // 5 | // Created by Stephan Michels on 04.02.12. 6 | // Copyright (c) 2012 Stephan Michels Softwareentwicklung und Beratung. All rights reserved. 7 | // 8 | 9 | #import "SMTabBarButtonCell.h" 10 | 11 | @implementation SMTabBarButtonCell 12 | 13 | - (id)init { 14 | self = [super init]; 15 | if (self) { 16 | self.bezelStyle = NSTexturedRoundedBezelStyle; 17 | } 18 | return self; 19 | } 20 | 21 | // prevent automatic state changes 22 | - (NSInteger)nextState { 23 | return self.state; 24 | } 25 | 26 | - (void)drawBezelWithFrame:(NSRect)frame inView:(NSView *)controlView { 27 | 28 | // Draw background only if the button is selected 29 | if (self.state == NSOnState) { 30 | [[NSGraphicsContext currentContext] saveGraphicsState]; 31 | 32 | // light vertical gradient 33 | static NSGradient *gradient = nil; 34 | if (!gradient) { 35 | NSColor *color1 = [NSColor colorWithCalibratedWhite:0.7 alpha:0.0]; 36 | NSColor *color2 = [NSColor colorWithCalibratedWhite:0.7 alpha:5.0]; 37 | CGFloat loactions[] = {0.0f, 0.5f, 1.0f}; 38 | gradient = [[NSGradient alloc] initWithColors:@[color1, color2, color1] atLocations:loactions colorSpace:[NSColorSpace genericGrayColorSpace]]; 39 | } 40 | [gradient drawInRect:frame angle:-90.0f]; 41 | 42 | 43 | // shadow on the left border 44 | NSShadow *shadow = [[NSShadow alloc] init]; 45 | shadow.shadowOffset = NSMakeSize(1.0f, 0.0f); 46 | shadow.shadowBlurRadius = 2.0f; 47 | shadow.shadowColor = [NSColor darkGrayColor]; 48 | [shadow set]; 49 | 50 | // not visible color 51 | [[NSColor redColor] set]; 52 | 53 | CGFloat radius = 50.0; 54 | 55 | NSPoint center = NSMakePoint(NSMinX(frame) - radius, NSMidY(frame)); 56 | NSBezierPath *path = [NSBezierPath bezierPath]; 57 | [path moveToPoint:center]; 58 | [path appendBezierPathWithArcWithCenter:center 59 | radius:radius 60 | startAngle:-90.0f 61 | endAngle:90.0f]; 62 | [path closePath]; 63 | [path fill]; 64 | 65 | // shadow of the right border 66 | shadow.shadowOffset = NSMakeSize(-1.0f, 0.0f); 67 | [shadow set]; 68 | 69 | center = NSMakePoint(NSMaxX(frame) + radius, NSMidY(frame)); 70 | path = [NSBezierPath bezierPath]; 71 | [path moveToPoint:center]; 72 | [path appendBezierPathWithArcWithCenter:center 73 | radius:radius 74 | startAngle:90.0f 75 | endAngle:270.0f]; 76 | [path closePath]; 77 | [path fill]; 78 | 79 | [[NSGraphicsContext currentContext] restoreGraphicsState]; 80 | } 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /third-party/SMTabBar/InspectorTabBar/SMTabBarItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMTabBarItem.h 3 | // InspectorTabBar 4 | // 5 | // Created by Stephan Michels on 03.02.12. 6 | // Copyright (c) 2012 Stephan Michels Softwareentwicklung und Beratung. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMTabBarItem : NSObject 12 | 13 | @property (nonatomic) BOOL enabled; 14 | @property (nonatomic, strong) NSImage *image; 15 | @property (nonatomic, copy) NSString *toolTip; 16 | @property (nonatomic, copy) NSString *keyEquivalent; 17 | @property (nonatomic) NSUInteger keyEquivalentModifierMask; 18 | @property (nonatomic) NSInteger tag; 19 | 20 | - (id)initWithImage:(NSImage *)image tag:(NSInteger)tag; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /third-party/SMTabBar/InspectorTabBar/SMTabBarItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMTabBarItem.m 3 | // InspectorTabBar 4 | // 5 | // Created by Stephan Michels on 03.02.12. 6 | // Copyright (c) 2012 Stephan Michels Softwareentwicklung und Beratung. All rights reserved. 7 | // 8 | 9 | #import "SMTabBarItem.h" 10 | 11 | @implementation SMTabBarItem 12 | 13 | - (id)initWithImage:(NSImage *)image tag:(NSInteger)tag { 14 | self = [super init]; 15 | if (self) { 16 | self.image = image; 17 | self.tag = tag; 18 | self.enabled = YES; 19 | } 20 | return self; 21 | } 22 | 23 | - (NSString *)description { 24 | return [NSString stringWithFormat:@"SMTabBarItem{tag=%li}", self.tag]; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /third-party/SMTabBar/README.md: -------------------------------------------------------------------------------- 1 | source: 2 | https://github.com/smic/InspectorTabBar 3 | -------------------------------------------------------------------------------- /third-party/SMTabBar/SMTabBar.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint SMTabBar.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | s.name = "SMTabBar" 12 | s.version = "0.0.1" 13 | s.summary = "Tab bar like in the Xcode inspector." 14 | s.homepage = "https://github.com/smic/InspectorTabBar" 15 | s.license = { :type => "MIT" } 16 | s.author = { "smic" => "stephan.michels@gmail.com" } 17 | s.source = { :git => "git@github.com:smic/InspectorTabBar.git" } 18 | s.source_files = "InspectorTabBar/**/*.{h,m}" 19 | s.platform = :osx, "10.10" 20 | # s.public_header_files = "Classes/**/*.h" 21 | # s.requires_arc = true 22 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 23 | # s.dependency "JSONKit", "~> 1.4" 24 | 25 | end 26 | -------------------------------------------------------------------------------- /third-party/SPMediaKeyTap/README.md: -------------------------------------------------------------------------------- 1 | source: 2 | https://github.com/nevyn/SPMediaKeyTap 3 | -------------------------------------------------------------------------------- /third-party/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SPInvocationGrabber : NSObject { 4 | id _object; 5 | NSInvocation *_invocation; 6 | int frameCount; 7 | char **frameStrings; 8 | BOOL backgroundAfterForward; 9 | BOOL onMainAfterForward; 10 | BOOL waitUntilDone; 11 | } 12 | -(id)initWithObject:(id)obj; 13 | -(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack; 14 | @property (readonly, retain, nonatomic) id object; 15 | @property (readonly, retain, nonatomic) NSInvocation *invocation; 16 | @property BOOL backgroundAfterForward; 17 | @property BOOL onMainAfterForward; 18 | @property BOOL waitUntilDone; 19 | -(void)invoke; // will release object and invocation 20 | -(void)printBacktrace; 21 | -(void)saveBacktrace; 22 | @end 23 | 24 | @interface NSObject (SPInvocationGrabbing) 25 | -(id)grab; 26 | -(id)invokeAfter:(NSTimeInterval)delta; 27 | -(id)nextRunloop; 28 | -(id)inBackground; 29 | -(id)onMainAsync:(BOOL)async; 30 | @end 31 | -------------------------------------------------------------------------------- /third-party/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m: -------------------------------------------------------------------------------- 1 | #import "NSObject+SPInvocationGrabbing.h" 2 | #import 3 | 4 | #pragma mark Invocation grabbing 5 | @interface SPInvocationGrabber () 6 | @property (readwrite, retain, nonatomic) id object; 7 | @property (readwrite, retain, nonatomic) NSInvocation *invocation; 8 | 9 | @end 10 | 11 | @implementation SPInvocationGrabber 12 | - (id)initWithObject:(id)obj; 13 | { 14 | return [self initWithObject:obj stacktraceSaving:YES]; 15 | } 16 | 17 | -(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack; 18 | { 19 | self.object = obj; 20 | 21 | if(saveStack) 22 | [self saveBacktrace]; 23 | 24 | return self; 25 | } 26 | -(void)dealloc; 27 | { 28 | free(frameStrings); 29 | self.object = nil; 30 | self.invocation = nil; 31 | [super dealloc]; 32 | } 33 | @synthesize invocation = _invocation, object = _object; 34 | 35 | @synthesize backgroundAfterForward, onMainAfterForward, waitUntilDone; 36 | - (void)runInBackground; 37 | { 38 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 39 | @try { 40 | [self invoke]; 41 | } 42 | @finally { 43 | [pool drain]; 44 | } 45 | } 46 | 47 | 48 | - (void)forwardInvocation:(NSInvocation *)anInvocation { 49 | [anInvocation retainArguments]; 50 | anInvocation.target = _object; 51 | self.invocation = anInvocation; 52 | 53 | if(backgroundAfterForward) 54 | [NSThread detachNewThreadSelector:@selector(runInBackground) toTarget:self withObject:nil]; 55 | else if(onMainAfterForward) 56 | [self performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:waitUntilDone]; 57 | } 58 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)inSelector { 59 | NSMethodSignature *signature = [super methodSignatureForSelector:inSelector]; 60 | if (signature == NULL) 61 | signature = [_object methodSignatureForSelector:inSelector]; 62 | 63 | return signature; 64 | } 65 | 66 | - (void)invoke; 67 | { 68 | 69 | @try { 70 | [_invocation invoke]; 71 | } 72 | @catch (NSException * e) { 73 | NSLog(@"SPInvocationGrabber's target raised %@:\n\t%@\nInvocation was originally scheduled at:", e.name, e); 74 | [self printBacktrace]; 75 | printf("\n"); 76 | [e raise]; 77 | } 78 | 79 | self.invocation = nil; 80 | self.object = nil; 81 | } 82 | 83 | -(void)saveBacktrace; 84 | { 85 | void *backtraceFrames[128]; 86 | frameCount = backtrace(&backtraceFrames[0], 128); 87 | frameStrings = backtrace_symbols(&backtraceFrames[0], frameCount); 88 | } 89 | -(void)printBacktrace; 90 | { 91 | for(int x = 3; x < frameCount; x++) { 92 | if(frameStrings[x] == NULL) { break; } 93 | printf("%s\n", frameStrings[x]); 94 | } 95 | } 96 | @end 97 | 98 | @implementation NSObject (SPInvocationGrabbing) 99 | -(id)grab; 100 | { 101 | return [[[SPInvocationGrabber alloc] initWithObject:self] autorelease]; 102 | } 103 | -(id)invokeAfter:(NSTimeInterval)delta; 104 | { 105 | id grabber = [self grab]; 106 | [NSTimer scheduledTimerWithTimeInterval:delta target:grabber selector:@selector(invoke) userInfo:nil repeats:NO]; 107 | return grabber; 108 | } 109 | - (id)nextRunloop; 110 | { 111 | return [self invokeAfter:0]; 112 | } 113 | -(id)inBackground; 114 | { 115 | SPInvocationGrabber *grabber = [self grab]; 116 | grabber.backgroundAfterForward = YES; 117 | return grabber; 118 | } 119 | -(id)onMainAsync:(BOOL)async; 120 | { 121 | SPInvocationGrabber *grabber = [self grab]; 122 | grabber.onMainAfterForward = YES; 123 | grabber.waitUntilDone = !async; 124 | return grabber; 125 | } 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /third-party/SPMediaKeyTap/SPMediaKeyTap.h: -------------------------------------------------------------------------------- 1 | #include 2 | #import 3 | #import 4 | 5 | // http://overooped.com/post/2593597587/mediakeys 6 | 7 | #define SPSystemDefinedEventMediaKeys 8 8 | 9 | @interface SPMediaKeyTap : NSObject { 10 | EventHandlerRef _app_switching_ref; 11 | EventHandlerRef _app_terminating_ref; 12 | CFMachPortRef _eventPort; 13 | CFRunLoopSourceRef _eventPortSource; 14 | CFRunLoopRef _tapThreadRL; 15 | BOOL _shouldInterceptMediaKeyEvents; 16 | id _delegate; 17 | // The app that is frontmost in this list owns media keys 18 | NSMutableArray *_mediaKeyAppList; 19 | } 20 | + (NSArray*)defaultMediaKeyUserBundleIdentifiers; 21 | 22 | -(id)initWithDelegate:(id)delegate; 23 | 24 | +(BOOL)usesGlobalMediaKeyTap; 25 | -(void)startWatchingMediaKeys; 26 | -(void)stopWatchingMediaKeys; 27 | -(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event; 28 | @end 29 | 30 | @interface NSObject (SPMediaKeyTapDelegate) 31 | -(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event; 32 | @end 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | extern NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey; 39 | extern NSString *kIgnoreMediaKeysDefaultsKey; 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif -------------------------------------------------------------------------------- /third-party/SPMediaKeyTap/SPMediaKeyTap.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint SPMediaKeyTap.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | s.name = "SPMediaKeyTap" 12 | s.version = "0.0.1" 13 | s.summary = "SPMediaKeyTap is a global event tap for the play/pause, prev and next keys on the keyboard." 14 | s.homepage = "https://github.com/nevyn/SPMediaKeyTap" 15 | s.license = { :type => "BSD 3-Clause"} 16 | s.author = { "nevyn" => "nevyn.jpg@gmail.com" } 17 | s.platform = :osx 18 | # s.osx.deployment_target = "10.7" 19 | s.source = { :git => "git://github.com/sonoramac/SPMediaKeyTap.git" } 20 | s.source_files = 'SPMediaKeyTap.{h,m}', 'SPInvocationGrabbing/NSObject+SPInvocationGrabbing.{h,m}' 21 | # s.public_header_files = "Classes/**/*.h" 22 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 23 | # s.frameworks = "SomeFramework", "AnotherFramework" 24 | # s.library = "iconv" 25 | # s.libraries = "iconv", "xml2" 26 | s.requires_arc = false 27 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 28 | # s.dependency "JSONKit", "~> 1.4" 29 | 30 | end 31 | --------------------------------------------------------------------------------