├── Design └── Feedback.sketch ├── Resources ├── DFWarning.pdf ├── zh_CN.lproj │ └── DFLocalizable.strings ├── zh_TW.lproj │ ├── DFLocalizable.strings │ └── DFCrashReportWindow.xib ├── ja.lproj │ └── DFLocalizable.strings ├── ru.lproj │ └── DFLocalizable.strings ├── uk.lproj │ └── DFLocalizable.strings ├── sv.lproj │ └── DFLocalizable.strings ├── tr.lproj │ └── DFLocalizable.strings ├── pl.lproj │ └── DFLocalizable.strings ├── it.lproj │ └── DFLocalizable.strings ├── es.lproj │ └── DFLocalizable.strings ├── de.lproj │ └── DFLocalizable.strings ├── pt.lproj │ └── DFLocalizable.strings ├── fr.lproj │ └── DFLocalizable.strings └── en.lproj │ └── DFLocalizable.strings ├── Demo ├── Demo.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcuserdata │ │ └── oleg.xcuserdatad │ │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── Demo.xcscheme ├── English.lproj │ └── InfoPlist.strings ├── DemoAppDelegate.h ├── main.m ├── Demo-Info.plist └── DemoAppDelegate.m ├── Helpers ├── LiteralHelpers.h ├── StringAnonymizer.h ├── EmailValidation.h ├── NSURLRequest+Extension.h ├── DFFeedbackSenderDelegate.h ├── DFSystemProfileFetcherDelegate.h ├── ApplicationSandboxInfo.h ├── GenericAnimation.h ├── DFFeedbackSender.h ├── OSXVersion.h ├── DFSystemProfileFetcher.h ├── GenericAnimation.m ├── EmailValidation.m ├── SoftwareVersion.h ├── StringAnonymizer.m ├── LiteralHelpers.m ├── NSURLRequest+Extension.m ├── DFFeedbackSender.m ├── OSXVersion.m ├── ApplicationSandboxInfo.m ├── SoftwareVersion.m └── DFSystemProfileFetcher.m ├── .gitignore ├── Views ├── DFKeyTabView.h ├── DFLinkLabelDelegate.h ├── DFComboBoxCell.h ├── DFKeyTabView.m ├── DFPlaceholderTextView.h ├── DFLinkLabel.h ├── DFBounceIconView.h ├── DFComboBoxCell.m ├── DFPlaceholderTextView.m ├── DFBounceIconView.m └── DFLinkLabel.m ├── DFApplication.h ├── DFRelaunch.sh ├── MIT License.txt ├── DFStyleSheet.h ├── DFFeedbackWindowController.h ├── DFCrashReportWindowController.h ├── DFStyleSheet.m ├── Google Toolbox ├── GTMStackTrace.h ├── GTMStackTrace.m └── GTMDefines.h ├── DFSystemProfileDataType.h ├── ReadMe.markdown ├── DFApplication.m └── DFCrashReportWindowController.m /Design/Feedback.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AJet/DFeedback/HEAD/Design/Feedback.sketch -------------------------------------------------------------------------------- /Resources/DFWarning.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AJet/DFeedback/HEAD/Resources/DFWarning.pdf -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/English.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------------------------------- 2 | Copyright (c) 2008 DaisyDisk Team: 3 | Some rights reserved: 4 | -------------------------------------------------------------------------------------------------*/ 5 | 6 | -------------------------------------------------------------------------------- /Helpers/LiteralHelpers.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Software Ambience Corp. All rights reserved. 3 | // 4 | 5 | #import 6 | 7 | //------------------------------------------------------------------------------------------------- 8 | // Creates NSDictionary much like a literal but allows nil values 9 | extern NS_REQUIRES_NIL_TERMINATION NSDictionary* NSDictionaryWithKeysAndValues(id firstKey, ...); 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Demo/build/* 3 | *.pch 4 | *.pbxuser 5 | *.mode1v3 6 | 7 | Demo/Demo.xcodeproj/xcuserdata/* 8 | Demo/Demo.xcodeproj/project.xcworkspace/xcuserdata/* 9 | 10 | Demo/Demo.xcodeproj/project.xcworkspace/xcuserdata/oleg.xcuserdatad/UserInterfaceState.xcuserstate 11 | Demo/Demo.xcodeproj/xcuserdata/oleg.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist 12 | /Demo/Demo.xcodeproj/project.xcworkspace/xcshareddata/Demo.xccheckout -------------------------------------------------------------------------------- /Demo/DemoAppDelegate.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | // Application delegate 10 | @interface DemoAppDelegate : NSObject 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /Resources/zh_CN.lproj/DFLocalizable.strings: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2008 DaisyDisk Team: */ 3 | /* Some rights reserved: */ 4 | /*-------------------------------------------------------------------------------------------------*/ 5 | 6 | "DFeedback_Alert_SendFailed_Title" = "发送反馈失败"; 7 | "DFeedback_Alert_SendFailed_Message" = "发生错误:%@"; 8 | "DFeedback_Alert_SendFailed_Button_Dismiss" = "离开"; 9 | 10 | "DFeedback_PlaceholderText" = "请用英文撰写和提交反馈信息,我们看不懂中文。"; 11 | 12 | -------------------------------------------------------------------------------- /Views/DFKeyTabView.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | // An tab view subclass that can be key and accept first responder 10 | @interface DFKeyTabView : NSTabView 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /Resources/zh_TW.lproj/DFLocalizable.strings: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2008 DaisyDisk Team: */ 3 | /* Some rights reserved: */ 4 | /*-------------------------------------------------------------------------------------------------*/ 5 | 6 | "DFeedback_Alert_SendFailed_Title" = "發送回饋失敗"; 7 | "DFeedback_Alert_SendFailed_Message" = "發生錯誤:%@"; 8 | "DFeedback_Alert_SendFailed_Button_Dismiss" = "離開"; 9 | 10 | "DFeedback_PlaceholderText" = "請以英文撰寫和提交回饋,我們看不懂中文,請見諒。"; 11 | 12 | 13 | -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/xcuserdata/oleg.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Demo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 8D1107260486CEB800E47090 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Resources/ja.lproj/DFLocalizable.strings: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2008 DaisyDisk Team: */ 3 | /* Some rights reserved: */ 4 | /*-------------------------------------------------------------------------------------------------*/ 5 | 6 | "DFeedback_Alert_SendFailed_Title" = "フィードバックの送信に失敗しました。"; 7 | "DFeedback_Alert_SendFailed_Message" = "次のエラーが発生しました:%@"; 8 | "DFeedback_Alert_SendFailed_Button_Dismiss" = "閉じる"; 9 | 10 | "DFeedback_PlaceholderText" = "可能な限り英語でメッセージをお願いします"; 11 | 12 | -------------------------------------------------------------------------------- /Resources/ru.lproj/DFLocalizable.strings: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2008 DaisyDisk Team: */ 3 | /* Some rights reserved: */ 4 | /*-------------------------------------------------------------------------------------------------*/ 5 | 6 | "DFeedback_Alert_SendFailed_Title" = "Не удалось отправить сообщение"; 7 | "DFeedback_Alert_SendFailed_Message" = "Причина неудачи: %@"; 8 | "DFeedback_Alert_SendFailed_Button_Dismiss" = "Скрыть"; 9 | 10 | "DFeedback_PlaceholderText" = "Можно писать по-русски, мы понимаем! 😀"; 11 | 12 | -------------------------------------------------------------------------------- /Resources/uk.lproj/DFLocalizable.strings: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2008 DaisyDisk Team: */ 3 | /* Some rights reserved: */ 4 | /*-------------------------------------------------------------------------------------------------*/ 5 | 6 | "DFeedback_Alert_SendFailed_Title" = "Не вдалося відправити повідомлення"; 7 | "DFeedback_Alert_SendFailed_Message" = "Причина невдачі: %@"; 8 | "DFeedback_Alert_SendFailed_Button_Dismiss" = "Закрити"; 9 | 10 | "DFeedback_PlaceholderText" = "Можна писати українською, ми розуміємо! 😀🇺🇦"; 11 | 12 | -------------------------------------------------------------------------------- /Resources/sv.lproj/DFLocalizable.strings: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2008 DaisyDisk Team: */ 3 | /* Some rights reserved: */ 4 | /*-------------------------------------------------------------------------------------------------*/ 5 | 6 | "DFeedback_Alert_SendFailed_Title" = "Kunde inte skicka feedback"; 7 | "DFeedback_Alert_SendFailed_Message" = "Följande fel har inträffat: %@"; 8 | "DFeedback_Alert_SendFailed_Button_Dismiss" = "Avfärda"; 9 | 10 | "DFeedback_PlaceholderText" = "Skriv din feedback på engelska, eftersom vi inte förstår svenska."; 11 | 12 | -------------------------------------------------------------------------------- /Resources/tr.lproj/DFLocalizable.strings: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2008 DaisyDisk Team: */ 3 | /* Some rights reserved: */ 4 | /*-------------------------------------------------------------------------------------------------*/ 5 | 6 | "DFeedback_Alert_SendFailed_Title" = "Geri bildirim gönderilemedi"; 7 | "DFeedback_Alert_SendFailed_Message" = "Aşağıdaki hata oluştu: %@"; 8 | "DFeedback_Alert_SendFailed_Button_Dismiss" = "Umursama"; 9 | 10 | "DFeedback_PlaceholderText" = "Türkçe anlayamadığımız için lütfen geri bildiriminizi İngilizce yazın."; 11 | 12 | -------------------------------------------------------------------------------- /Helpers/StringAnonymizer.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | // Helpers for anonymizing strings 10 | 11 | // Strips string from all mentions of the user full or short name, replacing it with a dummy 12 | extern NSString* AnonymizeString(NSString* inputString); 13 | -------------------------------------------------------------------------------- /Resources/pl.lproj/DFLocalizable.strings: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2008 DaisyDisk Team: */ 3 | /* Some rights reserved: */ 4 | /*-------------------------------------------------------------------------------------------------*/ 5 | 6 | "DFeedback_Alert_SendFailed_Title" = "Nie udało się wysłać opinii"; 7 | "DFeedback_Alert_SendFailed_Message" = "Wystąpił następujący błąd: %@"; 8 | "DFeedback_Alert_SendFailed_Button_Dismiss" = "Zamknij"; 9 | 10 | "DFeedback_PlaceholderText" = "Proszę napisz swojej zgłoszenie po angielsku, gdyż nie rozumiemy polskiego."; 11 | 12 | -------------------------------------------------------------------------------- /Demo/main.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | // Entry point 10 | //------------------------------------------------------------------------------------------------- 11 | int main(int argc, char *argv[]) 12 | { 13 | return NSApplicationMain(argc, (const char **) argv); 14 | } 15 | -------------------------------------------------------------------------------- /Helpers/EmailValidation.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | // Helpers for e-mail validation 10 | 11 | // Empty message test 12 | extern BOOL IsEmptyMessage(NSString* string); 13 | 14 | // Address validation 15 | extern BOOL IsValidEmailAddress(NSString* emailAddress); 16 | -------------------------------------------------------------------------------- /Resources/it.lproj/DFLocalizable.strings: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2008 DaisyDisk Team: */ 3 | /* Some rights reserved: */ 4 | /*-------------------------------------------------------------------------------------------------*/ 5 | 6 | "DFeedback_Alert_SendFailed_Title" = "Invio del feedback fallito"; 7 | "DFeedback_Alert_SendFailed_Message" = "Si è verificato il seguente errore: %@"; 8 | "DFeedback_Alert_SendFailed_Button_Dismiss" = "Ignora"; 9 | 10 | "DFeedback_PlaceholderText" = "Scrivi i tuoi consigli in inglese, poiché non parliamo l’italiano. Grazie!"; 11 | 12 | -------------------------------------------------------------------------------- /Resources/es.lproj/DFLocalizable.strings: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2008 DaisyDisk Team: */ 3 | /* Some rights reserved: */ 4 | /*-------------------------------------------------------------------------------------------------*/ 5 | 6 | "DFeedback_Alert_SendFailed_Title" = "Falló al enviar la retroalimentación"; 7 | "DFeedback_Alert_SendFailed_Message" = "Ha ocurrido el siguiente error: %@"; 8 | "DFeedback_Alert_SendFailed_Button_Dismiss" = "Rechazar"; 9 | 10 | "DFeedback_PlaceholderText" = "Por favor, escribe tus sugerencias en inglés ya que no hablamos español."; 11 | 12 | -------------------------------------------------------------------------------- /Views/DFLinkLabelDelegate.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | @class DFLinkLabel; 9 | 10 | //------------------------------------------------------------------------------------------------- 11 | // Clickable link delegate 12 | @protocol DFLinkLabelDelegate 13 | 14 | // Link clicked 15 | - (void)linkLabel:(DFLinkLabel*)sender didClickLinkNo:(NSUInteger)linkIndex; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Resources/de.lproj/DFLocalizable.strings: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2008 DaisyDisk Team: */ 3 | /* Some rights reserved: */ 4 | /*-------------------------------------------------------------------------------------------------*/ 5 | 6 | "DFeedback_Alert_SendFailed_Title" = "Senden des Feedbacks war nicht möglich"; 7 | "DFeedback_Alert_SendFailed_Message" = "Folgender Fehler ist aufgetreten: %@"; 8 | "DFeedback_Alert_SendFailed_Button_Dismiss" = "Schließen"; 9 | 10 | "DFeedback_PlaceholderText" = "Bitte verfasse dein Feedback auf Englisch, da wir kein Deutsch verstehen."; 11 | 12 | -------------------------------------------------------------------------------- /Resources/pt.lproj/DFLocalizable.strings: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2008 DaisyDisk Team: */ 3 | /* Some rights reserved: */ 4 | /*-------------------------------------------------------------------------------------------------*/ 5 | 6 | "DFeedback_Alert_SendFailed_Title" = "Não foi possível enviar o feedback"; 7 | "DFeedback_Alert_SendFailed_Message" = "O seguinte erro aconteceu: %@"; 8 | "DFeedback_Alert_SendFailed_Button_Dismiss" = "Dispensar"; 9 | 10 | "DFeedback_PlaceholderText" = "Por favor, escreva seus comentários em inglês, pois não podemos entender português."; 11 | 12 | -------------------------------------------------------------------------------- /Views/DFComboBoxCell.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | // Extension of combo box cell with the right margin that works nicely with bouncing icon view 10 | @interface DFComboBoxCell : NSComboBoxCell 11 | 12 | // Right margin 13 | @property (nonatomic) CGFloat rightMargin; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Resources/fr.lproj/DFLocalizable.strings: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2008 DaisyDisk Team: */ 3 | /* Some rights reserved: */ 4 | /*-------------------------------------------------------------------------------------------------*/ 5 | 6 | "DFeedback_Alert_SendFailed_Title" = "Erreur lors de l’envoi du commentaire"; 7 | "DFeedback_Alert_SendFailed_Message" = "L’erreur suivante est survenue : %@"; 8 | "DFeedback_Alert_SendFailed_Button_Dismiss" = "Fermer"; 9 | 10 | "DFeedback_PlaceholderText" = "Merci d’écrire votre commentaire en anglais car que nous ne comprenons pas le français."; 11 | 12 | -------------------------------------------------------------------------------- /Helpers/NSURLRequest+Extension.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | // Extension to NSURLRequest with utilities 10 | @interface NSURLRequest (Extension) 11 | 12 | // Create request with HTTP POST form 13 | + (NSURLRequest*)requestWithUrl:(NSURL*)url 14 | postForm:(NSDictionary*)values; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Helpers/DFFeedbackSenderDelegate.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | @class DFFeedbackSender; 9 | 10 | //------------------------------------------------------------------------------------------------- 11 | // Feedback sender delegate 12 | @protocol DFFeedbackSenderDelegate 13 | 14 | // Completion, with or without error 15 | - (void)feedbackSender:(DFFeedbackSender*)sender didFinishWithError:(NSError*)error; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Helpers/DFSystemProfileFetcherDelegate.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | @class DFSystemProfileFetcher; 9 | 10 | //------------------------------------------------------------------------------------------------- 11 | // System profile fetcher delegate 12 | @protocol DFSystemProfileFetcherDelegate 13 | 14 | // Completion, with or without error 15 | - (void)systemProfileFetcherDidFinish:(DFSystemProfileFetcher*)sender; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Resources/en.lproj/DFLocalizable.strings: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2008 DaisyDisk Team: */ 3 | /* Some rights reserved: */ 4 | /*-------------------------------------------------------------------------------------------------*/ 5 | 6 | "DFeedback_Alert_SendFailed_Title" = "Failed to send the feedback"; 7 | "DFeedback_Alert_SendFailed_Message" = "The following error has occurred: %@"; 8 | "DFeedback_Alert_SendFailed_Button_Dismiss" = "Dismiss"; 9 | 10 | "DFeedback_PlaceholderText" = ""; /* we do understand English, for other languages should be, translated: Please write your feedback in English, as we can't understand . */ 11 | -------------------------------------------------------------------------------- /Helpers/ApplicationSandboxInfo.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | // Provides sandbox information 10 | @interface ApplicationSandboxInfo : NSObject 11 | 12 | // Checks if the bundle is sandboxed 13 | + (BOOL)isSandboxed; 14 | 15 | // Checks if the bundle has a given entitlement 16 | + (BOOL)hasAddressBookDataEntitlement; 17 | 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Views/DFKeyTabView.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import "DFKeyTabView.h" 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | @implementation DFKeyTabView 10 | 11 | //------------------------------------------------------------------------------------------------- 12 | - (BOOL)canBecomeKeyView 13 | { 14 | return YES; 15 | } 16 | 17 | //------------------------------------------------------------------------------------------------- 18 | - (BOOL)acceptsFirstResponder 19 | { 20 | return YES; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /DFApplication.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | // Application 10 | @interface DFApplication : NSApplication 11 | 12 | // Relaunches the app 13 | - (void)relaunch; 14 | 15 | // Flag indicating that an unhandled exception has occured and the app is in process of terminating 16 | @property (nonatomic, readonly) BOOL isPostmortem; 17 | 18 | // You can ignore certain exceptions whose stack traces contain the following strings 19 | + (void)ignoreExceptionsWhoseStackTraceContains:(NSArray*)strings; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Views/DFPlaceholderTextView.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | // A text view subclass that displays a placeholder text when it's empty and not key 10 | @interface DFPlaceholderTextView : NSTextView 11 | 12 | // Placeholder text 13 | // NOTE: this property is intentionally named differently than placeholderString, to avoid clashing 14 | // with undocumented NSTextView's method that seems to do the same thing and causes the text to double 15 | @property (nonatomic, retain) NSString* placeholderText; 16 | 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Helpers/GenericAnimation.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | @class GenericAnimation; 9 | 10 | //------------------------------------------------------------------------------------------------- 11 | // Generic animation delegate 12 | @protocol GenericAnimationDelegate 13 | 14 | // Progress 15 | - (void)animation:(GenericAnimation*)animation didProgress:(NSAnimationProgress)progress; 16 | 17 | @end 18 | 19 | //------------------------------------------------------------------------------------------------- 20 | // Animation that calls delegate on each progress advance 21 | @interface GenericAnimation : NSAnimation 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Views/DFLinkLabel.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | @protocol DFLinkLabelDelegate; 9 | 10 | //------------------------------------------------------------------------------------------------- 11 | // A text label that can contain clickable hyperlink 12 | @interface DFLinkLabel : NSView 13 | 14 | // Initializer from existing text field, to take its text, font and frame 15 | - (id)initWithTextField:(NSTextField*)textField; 16 | 17 | // String value, hyperlinks enclosed in [square brackets] 18 | @property (nonatomic, retain) NSString* stringValue; 19 | 20 | // Delegate 21 | @property (nonatomic, assign) id delegate; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Helpers/DFFeedbackSender.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | @protocol DFFeedbackSenderDelegate; 9 | 10 | //------------------------------------------------------------------------------------------------- 11 | // Encapsulates asynchronous feedback sending 12 | @interface DFFeedbackSender : NSObject 13 | 14 | // Delegate 15 | @property (nonatomic, assign) id delegate; 16 | 17 | // Begins sending feedback, completes asynchronously 18 | - (void)sendFeedbackToUrl:(NSString*)url 19 | feedbackText:(NSString*)feedbackText 20 | feedbackType:(NSString*)feedbackType 21 | systemProfile:(NSString*)systemProfile 22 | userEmail:(NSString*)userEmail; 23 | 24 | // Cancels pending request 25 | - (void)cancel; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /DFRelaunch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #------------------------------------------------------------------------------------------------- 4 | # Copyright (c) 2008 DaisyDisk Team: 5 | # Some rights reserved: 6 | #------------------------------------------------------------------------------------------------- 7 | 8 | #------------------------------------------------------------------------------------------------- 9 | # Bash script to relaunch the app 10 | 11 | processPath=$1 12 | processID=$2 13 | processBundleID=$3 14 | 15 | # waiting cycle 16 | i="10" 17 | while [ $i -gt 0 ] 18 | do 19 | # wait one second 20 | sleep 0.5 21 | 22 | # check if running 23 | instanceCount=$(ps -p $processID | grep $processPath | wc -l) 24 | 25 | if [ $instanceCount -gt 0 ] 26 | then 27 | # wait again 28 | echo Still running $instanceCount instances of application, $i attempts left; 29 | i=$(($i - 1)) 30 | 31 | else 32 | # exit cycle 33 | i="0" 34 | fi 35 | done 36 | 37 | # relaunch the app 38 | echo Relaunching application 39 | open -n -b $processBundleID 40 | 41 | -------------------------------------------------------------------------------- /MIT License.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008 DaisyDisk Team: 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -------------------------------------------------------------------------------- /Demo/Demo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.yourcompany.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleSignature 20 | ???? 21 | CFBundleShortVersionString 22 | 1.0 23 | LSMinimumSystemVersion 24 | ${MACOSX_DEPLOYMENT_TARGET} 25 | CFBundleVersion 26 | 1 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | DFApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /Views/DFBounceIconView.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | #import "GenericAnimation.h" 8 | 9 | //------------------------------------------------------------------------------------------------- 10 | // A custom view that displays a bouncing icon to visually attract user's attention to an input error 11 | // NOTE: The view needs to be slightly larger than its icon, to allow room for bouncing 12 | @interface DFBounceIconView : NSView 13 | 14 | // The icon 15 | @property (nonatomic, retain) NSImage* icon; 16 | 17 | // Shows/hides with/without fade and bounce animations 18 | - (void)showWithAnimation:(BOOL)withAnimation; 19 | 20 | - (void)hideWithAnimation:(BOOL)withAnimation; 21 | 22 | 23 | // Currently showing flag 24 | @property (nonatomic, readonly) BOOL isShowing; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Helpers/OSXVersion.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | @class SoftwareVersion; 9 | 10 | //------------------------------------------------------------------------------------------------- 11 | typedef enum OSXGeneration : NSUInteger 12 | { 13 | OSXGeneration_Unknown = 0, 14 | OSXGeneration_Leopard = 0x0A05, 15 | OSXGeneration_SnowLeopard = 0x0A06, 16 | OSXGeneration_Lion = 0x0A07, 17 | OSXGeneration_MountainLion = 0x0A08, 18 | OSXGeneration_Mavericks = 0x0A09, 19 | OSXGeneration_Yosemite = 0x0A0A, 20 | OSXGeneration_ElCapitan = 0x0A0B, 21 | OSXGeneration_Sierra = 0x0A0C, 22 | } OSXGeneration; 23 | 24 | //------------------------------------------------------------------------------------------------- 25 | // OSX version 26 | @interface OSXVersion : NSObject 27 | 28 | // Gets OSX generation 29 | + (OSXGeneration)generation; 30 | 31 | // Gets full OSX version 32 | + (SoftwareVersion*)version; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /Helpers/DFSystemProfileFetcher.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | #import "DFSystemProfileDataType.h" 8 | 9 | @protocol DFSystemProfileFetcherDelegate; 10 | 11 | //------------------------------------------------------------------------------------------------- 12 | // Encapsulates asynchronous fetching of system profile 13 | @interface DFSystemProfileFetcher : NSObject 14 | 15 | // Delegate 16 | @property (nonatomic, assign) id delegate; 17 | 18 | // Begins fetching system profile, completes asynchronously 19 | - (void)fetchDataTypes:(DFSystemProfileDataType)dataTypes; 20 | 21 | // Cancels a pending request 22 | - (void)cancel; 23 | 24 | // Resulting profile 25 | @property (nonatomic, readonly) NSString* profile; 26 | 27 | // Is done fetching flag 28 | @property (nonatomic, readonly) BOOL isDoneFetching; 29 | 30 | // Checks if system profile can be fetched on this OS version and sandbox mode 31 | + (BOOL)canFetch; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /DFStyleSheet.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | // Customizable style constants that define some appearance and behavior properties of all elements 10 | 11 | // Bounce icon 12 | extern NSTimeInterval DFBounceIcon_animationDuration; 13 | 14 | // Placeholder text view 15 | extern NSDictionary* DFPlaceholderTextView_placeholderTextAttributes; 16 | 17 | // Feedback window 18 | extern CGFloat DFFeedbackWindow_bottomBarHeight; 19 | extern NSImage* DFFeedbackWindow_emailWarningImage; 20 | extern CGFloat DFFeedbackWindow_emailWarningImageZoomIncrement; 21 | 22 | // Crash report window 23 | extern CGFloat DFCrashReportWindow_bottomBarHeight; 24 | 25 | // Link label 26 | extern NSFont* DFLinkLabel_font; 27 | extern NSColor* DFLinkLabel_normalColor; 28 | extern NSColor* DFLinkLabel_linkColor; 29 | extern BOOL DFLinkLabel_linkUnderlined; 30 | 31 | // Initialization 32 | extern void InitializeDFStyles(void); 33 | -------------------------------------------------------------------------------- /Helpers/GenericAnimation.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import "GenericAnimation.h" 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | @implementation GenericAnimation 10 | 11 | //------------------------------------------------------------------------------------------------- 12 | - (void)setCurrentProgress:(NSAnimationProgress)progress 13 | { 14 | [super setCurrentProgress:progress]; 15 | 16 | if ([self.delegate conformsToProtocol:@protocol(GenericAnimationDelegate)]) 17 | { 18 | // it was found that sometimes the auto-release of objects during animation is postponed 19 | // which may cause build-up of significant amount of used memory 20 | // so auto-release explicitly 21 | @autoreleasepool 22 | { 23 | [(id)self.delegate animation:self 24 | didProgress:progress]; 25 | } 26 | } 27 | } 28 | 29 | @end 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /DFFeedbackWindowController.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | #import "DFSystemProfileDataType.h" 8 | #import "DFFeedbackSenderDelegate.h" 9 | #import "DFSystemProfileFetcherDelegate.h" 10 | 11 | //------------------------------------------------------------------------------------------------- 12 | // Feedback window controller 13 | @interface DFFeedbackWindowController : NSWindowController 14 | 15 | // Initialization, call before first use 16 | + (void)initializeWithFeedbackUrl:(NSString*)feedbackUrl 17 | systemProfileDataTypes:(DFSystemProfileDataType)systemProfileDataTypes; 18 | 19 | // Singleton 20 | + (DFFeedbackWindowController*)singleton; 21 | 22 | @property (nonatomic, retain) NSString* defaultUserEmail; 23 | 24 | @property (nonatomic, retain) NSString* message; 25 | 26 | // Shows the feedback window on the specified tab 27 | - (void)showGeneralQuestion; 28 | 29 | - (void)showBugReport; 30 | 31 | - (void)showFeatureRequest; 32 | 33 | // default first page 34 | - (void)show; 35 | 36 | 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Helpers/EmailValidation.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import "EmailValidation.h" 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | BOOL IsEmptyMessage(NSString* string) 10 | { 11 | BOOL result = YES; 12 | if ([string length] > 0) 13 | { 14 | NSString* regEx = @".*[^ \t\r\n]+.*"; 15 | NSPredicate* test = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regEx]; 16 | if([test evaluateWithObject:string]) 17 | { 18 | result = NO; 19 | } 20 | } 21 | return result; 22 | } 23 | 24 | //------------------------------------------------------------------------------------------------- 25 | BOOL IsValidEmailAddress(NSString* emailAddress) 26 | { 27 | if (emailAddress != nil && emailAddress.length > 0) 28 | { 29 | NSString* emailRegEx = @"^([-a-z0-9._+])+@([a-z0-9])(([a-z0-9._-])*([a-z0-9]))*(\\.([a-z0-9])([a-z0-9_-])*([a-z0-9]))+$"; 30 | NSPredicate* emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES[cd] %@", emailRegEx]; 31 | if([emailTest evaluateWithObject:emailAddress]) 32 | { 33 | return YES; 34 | } 35 | } 36 | return NO; 37 | } 38 | -------------------------------------------------------------------------------- /DFCrashReportWindowController.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | #import "DFLinkLabelDelegate.h" 8 | #import "DFSystemProfileDataType.h" 9 | #import "DFFeedbackSenderDelegate.h" 10 | #import "DFSystemProfileFetcherDelegate.h" 11 | 12 | //------------------------------------------------------------------------------------------------- 13 | // Crash report window controller 14 | @interface DFCrashReportWindowController : NSWindowController 15 | 16 | // Initialization, call before first use 17 | + (void)initializeWithFeedbackUrl:(NSString*)feedbackUrl 18 | updateUrl:(NSString*)updateUrl 19 | icon:(NSImage*)icon 20 | systemProfileDataTypes:(DFSystemProfileDataType)systemProfileDataTypes; 21 | 22 | // Singleton 23 | + (DFCrashReportWindowController*)singleton; 24 | 25 | // Shows the crash report window for the specified exception 26 | - (void)showReportForException:(NSException*)exception 27 | exceptionStackTrace:(NSString*)exceptionStackTrace; 28 | 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Helpers/SoftwareVersion.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | // Encapsulates software version parsing and comparison 10 | @interface SoftwareVersion : NSObject 11 | 12 | // Parses string and creates version 13 | + (SoftwareVersion*)makeVersionFromString:(NSString*)versionString; 14 | 15 | // Creates version from an array of numbers 16 | + (SoftwareVersion*)makeVersionFromNumbers:(const NSUInteger*)numbers 17 | count:(NSUInteger)count; 18 | 19 | // Round version to the nearest lower number by cutting off unneeded parts 20 | - (void)roundDownToParts:(NSUInteger)numberOfParts; 21 | 22 | // Comparator 23 | - (NSComparisonResult)compare:(SoftwareVersion*)other; 24 | 25 | // Display name saved 26 | @property (nonatomic, retain) NSString* displayName; 27 | 28 | // Display name generated 29 | - (void)makeDisplayName; 30 | 31 | - (NSString*)displayNameUpToMinor; 32 | 33 | // Access to particular numbers 34 | @property (nonatomic, readonly) NSUInteger majorNumber; 35 | @property (nonatomic, readonly) NSUInteger minorNumber; 36 | @property (nonatomic, readonly) NSUInteger buildNumber; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Views/DFComboBoxCell.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import "DFComboBoxCell.h" 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | @implementation DFComboBoxCell 10 | 11 | //------------------------------------------------------------------------------------------------- 12 | - (void)setRightMargin:(CGFloat)value 13 | { 14 | if (value != _rightMargin) 15 | { 16 | _rightMargin = value; 17 | NSControl* control = (NSControl*)self.controlView; 18 | control.needsDisplay = YES; 19 | // NOTE: this will not update field editor if it's already editing, so you can't change the margin dynamically 20 | // to be able to do that, you need to check if the field is the first responder, get the current field editor 21 | // and call selectWithFrame:..., however it's not supposed to be used like that and is buggy 22 | // particularly, the last text change may be lost on calling selectWithFrame:... 23 | } 24 | } 25 | 26 | //------------------------------------------------------------------------------------------------- 27 | - (NSRect)drawingRectForBounds:(NSRect)bounds 28 | { 29 | NSRect superBounds = [super drawingRectForBounds:bounds]; 30 | superBounds.size.width -= self.rightMargin; 31 | return superBounds; 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /Helpers/StringAnonymizer.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import "StringAnonymizer.h" 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | static NSString* const kObscuredUserName = @""; 10 | static NSString* const kObscuredUserFullName = @""; 11 | 12 | //------------------------------------------------------------------------------------------------- 13 | NSString* AnonymizeString(NSString* inputString) 14 | { 15 | NSString* result = inputString; 16 | if (inputString != nil) 17 | { 18 | // remove full name 19 | NSString* userFullName = NSFullUserName(); 20 | result = [result stringByReplacingOccurrencesOfString:userFullName 21 | withString:kObscuredUserFullName 22 | options:NSCaseInsensitiveSearch 23 | range:NSMakeRange(0, result.length)]; 24 | // remove short name 25 | NSString* userName = NSUserName(); 26 | result = [result stringByReplacingOccurrencesOfString:userName 27 | withString:kObscuredUserName 28 | options:NSCaseInsensitiveSearch 29 | range:NSMakeRange(0, result.length)]; 30 | } 31 | return result; 32 | } 33 | -------------------------------------------------------------------------------- /Views/DFPlaceholderTextView.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import "DFPlaceholderTextView.h" 7 | #import "DFStyleSheet.h" 8 | 9 | //------------------------------------------------------------------------------------------------- 10 | @implementation DFPlaceholderTextView 11 | { 12 | NSString* _placeholderText; 13 | BOOL _shouldInvalidateOnChange; 14 | } 15 | 16 | //------------------------------------------------------------------------------------------------- 17 | - (id)initWithFrame:(NSRect)frame 18 | { 19 | self = [super initWithFrame:frame]; 20 | if (self != nil) 21 | { 22 | // do nothing 23 | } 24 | return self; 25 | } 26 | 27 | //------------------------------------------------------------------------------------------------- 28 | - (void)dealloc 29 | { 30 | [_placeholderText release]; 31 | [super dealloc]; 32 | } 33 | 34 | //------------------------------------------------------------------------------------------------- 35 | - (void)setPlaceholderText:(NSString*)value 36 | { 37 | [value retain]; 38 | [_placeholderText release]; 39 | _placeholderText = value; 40 | 41 | self.needsDisplay = YES; 42 | } 43 | 44 | //------------------------------------------------------------------------------------------------- 45 | - (void)drawRect:(NSRect)rect 46 | { 47 | // draw super 48 | [super drawRect:rect]; 49 | 50 | [NSGraphicsContext saveGraphicsState]; 51 | 52 | // draw placeholder 53 | if ((self.string == nil || [self.string isEqualToString:@""]) && self != self.window.firstResponder) 54 | { 55 | NSRect textRect = NSInsetRect(self.bounds, self.textContainer.lineFragmentPadding, 0.0); 56 | [self.placeholderText drawInRect:textRect withAttributes:DFPlaceholderTextView_placeholderTextAttributes]; 57 | } 58 | 59 | [NSGraphicsContext restoreGraphicsState]; 60 | } 61 | 62 | //------------------------------------------------------------------------------------------------- 63 | - (void)didChangeText 64 | { 65 | [super didChangeText]; 66 | // clear explicitly, or otherwise the placeholder text will sometimes leave some dirty pixels 67 | if (_shouldInvalidateOnChange) 68 | { 69 | self.needsDisplay = YES; 70 | } 71 | _shouldInvalidateOnChange = (self.string == nil || [self.string isEqualToString:@""]); 72 | } 73 | 74 | //------------------------------------------------------------------------------------------------- 75 | - (BOOL)becomeFirstResponder 76 | { 77 | self.needsDisplay = YES; 78 | return [super becomeFirstResponder]; 79 | } 80 | 81 | //------------------------------------------------------------------------------------------------- 82 | - (BOOL)resignFirstResponder 83 | { 84 | self.needsDisplay = YES; 85 | return [super resignFirstResponder]; 86 | } 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /Helpers/LiteralHelpers.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Software Ambience Corp. All rights reserved. 3 | // 4 | 5 | #import "LiteralHelpers.h" 6 | 7 | //------------------------------------------------------------------------------------------------- 8 | static NSUInteger const kStaticCapacity = 16; 9 | static NSUInteger const kDynamicCapacityIncrement = 16; 10 | 11 | //------------------------------------------------------------------------------------------------- 12 | NSDictionary* NSDictionaryWithKeysAndValues(id firstKey, ...) 13 | { 14 | va_list keyValueList; 15 | va_start(keyValueList, firstKey); 16 | 17 | __unsafe_unretained id staticKeys[kStaticCapacity]; 18 | __unsafe_unretained id staticValues[kStaticCapacity]; 19 | 20 | __unsafe_unretained id* dynamicKeys = NULL; 21 | __unsafe_unretained id* dynamicValues = NULL; 22 | 23 | // begin with static capacity at first 24 | NSUInteger resultCount = 0; 25 | NSUInteger currentCapacity = kStaticCapacity; 26 | BOOL isStaticCapacity = YES; 27 | __unsafe_unretained id* resultKeys = (__unsafe_unretained id*)staticKeys; 28 | __unsafe_unretained id* resultValues = (__unsafe_unretained id*)staticValues; 29 | 30 | for (id key = firstKey; key != nil; key = va_arg(keyValueList, id)) 31 | { 32 | id value = va_arg(keyValueList, id); 33 | if (value != nil) 34 | { 35 | // add capacity if necessary 36 | if (resultCount == currentCapacity) 37 | { 38 | currentCapacity += kDynamicCapacityIncrement; 39 | // switch from static to dynamic allocation 40 | if (isStaticCapacity) 41 | { 42 | dynamicKeys = (__unsafe_unretained id *)malloc(currentCapacity * sizeof(id)); 43 | dynamicValues = (__unsafe_unretained id *)malloc(currentCapacity * sizeof(id)); 44 | memcpy(dynamicKeys, staticKeys, kStaticCapacity * sizeof(id)); 45 | memcpy(dynamicValues, staticValues, kStaticCapacity * sizeof(id)); 46 | isStaticCapacity = NO; 47 | } 48 | // dynamic reallocation 49 | else 50 | { 51 | dynamicKeys = (__unsafe_unretained id *)realloc(dynamicKeys, currentCapacity * sizeof(id)); 52 | dynamicValues = (__unsafe_unretained id *)realloc(dynamicValues, currentCapacity * sizeof(id)); 53 | } 54 | // refresh resulting pointers 55 | resultKeys = dynamicKeys; 56 | resultValues = dynamicValues; 57 | } 58 | 59 | resultKeys[resultCount] = key; 60 | resultValues[resultCount] = value; 61 | ++resultCount; 62 | } 63 | } 64 | 65 | va_end(keyValueList); 66 | 67 | NSDictionary* result = [NSDictionary dictionaryWithObjects:resultValues forKeys:resultKeys count:resultCount]; 68 | if (dynamicKeys != NULL) 69 | { 70 | free(dynamicKeys); 71 | } 72 | if (dynamicValues != NULL) 73 | { 74 | free(dynamicValues); 75 | } 76 | return result; 77 | } 78 | -------------------------------------------------------------------------------- /Helpers/NSURLRequest+Extension.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import "NSURLRequest+Extension.h" 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | @implementation NSURLRequest(Extension) 10 | 11 | //------------------------------------------------------------------------------------------------- 12 | + (void)appendString:(NSString*)string toData:(NSMutableData*)data 13 | { 14 | [data appendData:[string dataUsingEncoding:NSUTF8StringEncoding]]; 15 | } 16 | 17 | //------------------------------------------------------------------------------------------------- 18 | + (void)appendFormat:(NSString*)format arg:(NSString*)arg toData:(NSMutableData*)data 19 | { 20 | NSString* string = [NSString stringWithFormat:format, arg]; 21 | [self appendString:string toData:data]; 22 | } 23 | 24 | //------------------------------------------------------------------------------------------------- 25 | + (NSURLRequest*)requestWithUrl:(NSURL*)url postForm:(NSDictionary*)values 26 | { 27 | // create the mime multipart boundary 28 | CFUUIDRef uuidRef = CFUUIDCreate(NULL); 29 | CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef); 30 | CFRelease(uuidRef); 31 | NSString* uuid = [(NSString*)uuidStringRef autorelease]; 32 | NSString* boundary = [NSString stringWithFormat:@"x-mime-boundary://%@", uuid]; 33 | 34 | // create the form 35 | NSMutableData* formData = [NSMutableData data]; 36 | for (NSString* key in values.allKeys) 37 | { 38 | [NSURLRequest appendFormat:@"\r\n--%@\r\n" arg:boundary toData:formData]; 39 | [NSURLRequest appendFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n" arg:key toData:formData]; 40 | id value = values[key]; 41 | if ([value isKindOfClass:[NSData class]]) 42 | { 43 | [NSURLRequest appendString:@"\r\n" toData:formData]; 44 | [formData appendData:value]; 45 | } 46 | else if ([value isKindOfClass:[NSString class]]) 47 | { 48 | [NSURLRequest appendFormat:@"Content-Type: text/plain; charset=utf-8\r\n\r\n" arg:nil toData:formData]; 49 | [NSURLRequest appendString:value toData:formData]; 50 | } 51 | else 52 | { 53 | NSAssert(NO, @"unknown value class: %@", [value className]); 54 | } 55 | } 56 | [NSURLRequest appendFormat:@"\r\n--%@--\r\n" arg:boundary toData:formData]; 57 | 58 | // create request 59 | NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url]; 60 | request.HTTPMethod = @"POST"; 61 | [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] forHTTPHeaderField:@"Content-Type"]; 62 | [request setValue:[NSString stringWithFormat:@"%lu", (unsigned long)[formData length]] forHTTPHeaderField:@"Content-Length"]; 63 | request.HTTPBody = formData; 64 | 65 | return request; 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /DFStyleSheet.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import "DFStyleSheet.h" 7 | #import "LiteralHelpers.h" 8 | 9 | //------------------------------------------------------------------------------------------------- 10 | static BOOL _isInitialized = NO; 11 | 12 | //------------------------------------------------------------------------------------------------- 13 | // Bounce icon 14 | NSTimeInterval DFBounceIcon_animationDuration = .25; 15 | static void InitializeDFBounceIconStyles(void) 16 | { 17 | // do nothing 18 | } 19 | 20 | //------------------------------------------------------------------------------------------------- 21 | // Placeholder text view 22 | NSDictionary* DFPlaceholderTextView_placeholderTextAttributes = nil; 23 | static void InitializeDFPlaceholderTextViewStyles(void) 24 | { 25 | DFPlaceholderTextView_placeholderTextAttributes = [NSDictionaryWithKeysAndValues(NSForegroundColorAttributeName, [NSColor colorWithDeviceRed:128./255. green:128./255. blue:145./255. alpha:1.], 26 | NSFontAttributeName, [NSFont userFontOfSize:12], 27 | nil) retain]; 28 | } 29 | 30 | //------------------------------------------------------------------------------------------------- 31 | // Feedback window 32 | CGFloat DFFeedbackWindow_bottomBarHeight = 45.; 33 | NSImage* DFFeedbackWindow_emailWarningImage = nil; 34 | CGFloat DFFeedbackWindow_emailWarningImageZoomIncrement = 20.; 35 | static void InitializeDFFeedbackWindow(void) 36 | { 37 | DFFeedbackWindow_emailWarningImage = [[NSImage imageNamed:@"DFWarning"] retain]; 38 | } 39 | 40 | //------------------------------------------------------------------------------------------------- 41 | // Crash report window 42 | CGFloat DFCrashReportWindow_bottomBarHeight = 45.; 43 | static void InitializeDFCrashReportWindow(void) 44 | { 45 | // do nothing 46 | } 47 | 48 | //------------------------------------------------------------------------------------------------- 49 | // Link label 50 | NSFont* DFLinkLabel_font = nil; 51 | NSColor* DFLinkLabel_normalColor = nil; 52 | NSColor* DFLinkLabel_linkColor = nil; 53 | BOOL DFLinkLabel_linkUnderlined = YES; 54 | static void InitializeDFLinkLabel(void) 55 | { 56 | DFLinkLabel_font = [[NSFont systemFontOfSize:12.] retain]; 57 | DFLinkLabel_normalColor = [[NSColor textColor] retain]; 58 | DFLinkLabel_linkColor = [[NSColor linkColor] retain]; 59 | } 60 | 61 | 62 | //------------------------------------------------------------------------------------------------- 63 | void InitializeDFStyles(void) 64 | { 65 | if (!_isInitialized) 66 | { 67 | InitializeDFBounceIconStyles(); 68 | 69 | InitializeDFPlaceholderTextViewStyles(); 70 | 71 | InitializeDFFeedbackWindow(); 72 | 73 | InitializeDFCrashReportWindow(); 74 | 75 | InitializeDFLinkLabel(); 76 | 77 | _isInitialized = YES; 78 | } 79 | } 80 | 81 | 82 | -------------------------------------------------------------------------------- /Google Toolbox/GTMStackTrace.h: -------------------------------------------------------------------------------- 1 | // 2 | // GTMStackTrace.h 3 | // 4 | // Copyright 2007-2008 Google Inc. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 7 | // use this file except in compliance with the License. You may obtain a copy 8 | // of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | // License for the specific language governing permissions and limitations under 16 | // the License. 17 | // 18 | 19 | #import 20 | #import "GTMDefines.h" 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | struct GTMAddressDescriptor { 27 | const void *address; // address 28 | const char *symbol; // nearest symbol to address 29 | const char *class_name; // if it is an obj-c method, the method's class 30 | BOOL is_class_method; // if it is an obj-c method, type of method 31 | const char *filename; // file that the method came from. 32 | }; 33 | 34 | // Returns a string containing a nicely formatted stack trace. 35 | // 36 | // This function gets the stack trace for the current thread. It will 37 | // be from the caller of GTMStackTrace upwards to the top the calling stack. 38 | // Typically this function will be used along with some logging, 39 | // as in the following: 40 | // 41 | // MyAppLogger(@"Should never get here:\n%@", GTMStackTrace()); 42 | // 43 | // Here is a sample stack trace returned from this function: 44 | // 45 | // #0 0x00002d92 D () [/Users/me/./StackLog] 46 | // #1 0x00002e45 C () [/Users/me/./StackLog] 47 | // #2 0x00002e53 B () [/Users/me/./StackLog] 48 | // #3 0x00002e61 A () [/Users/me/./StackLog] 49 | // #4 0x00002e6f main () [/Users/me/./StackLog] 50 | // #5 0x00002692 tart () [/Users/me/./StackLog] 51 | // #6 0x000025b9 tart () [/Users/me/./StackLog] 52 | // 53 | 54 | NSString *GTMStackTrace(void); 55 | 56 | // Returns a string containing a nicely formatted stack trace from the 57 | // exception. Only available on 10.5 or later, uses 58 | // -[NSException callStackReturnAddresses]. 59 | // 60 | NSString *GTMStackTraceFromException(NSException *e); 61 | 62 | // Returns an array of GTMAddressDescriptors from the current thread's stack. 63 | // *** You should probably use GTMStackTrace() instead of this function *** 64 | // However, if you actually want all the PCs with symbols, this is the way 65 | // to get them. There is no memory allocations done, so no clean up is required 66 | // except for the caller to free outDescs if they allocated it themselves. 67 | // This will include PCs of GTMStaceTrace and its inner utility functions that 68 | // you may want to strip out. 69 | // 70 | // Args: 71 | // outDescs - an array of "struct GTMAddressDescriptor" pointers corresponding 72 | // to the program counters found on the current thread's stack. 73 | // count - the number of entries in the outDescs array 74 | // 75 | // Returns: 76 | // The number of program counters actually added to outPcs. 77 | // 78 | NSUInteger GTMGetStackAddressDescriptors(struct GTMAddressDescriptor outDescs[], 79 | NSUInteger count); 80 | 81 | #ifdef __cplusplus 82 | } 83 | #endif 84 | -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/xcuserdata/oleg.xcuserdatad/xcschemes/Demo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /Helpers/DFFeedbackSender.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import "DFFeedbackSender.h" 7 | #import "DFFeedbackSenderDelegate.h" 8 | #import "NSURLRequest+Extension.h" 9 | #import "LiteralHelpers.h" 10 | 11 | //------------------------------------------------------------------------------------------------- 12 | @implementation DFFeedbackSender 13 | { 14 | BOOL _isCanceled; 15 | NSURLConnection* _connection; 16 | } 17 | 18 | //------------------------------------------------------------------------------------------------- 19 | - (id)init 20 | { 21 | self = [super init]; 22 | if (self != nil) 23 | { 24 | } 25 | return self; 26 | } 27 | 28 | //------------------------------------------------------------------------------------------------- 29 | - (void)dealloc 30 | { 31 | [_connection release]; 32 | [super dealloc]; 33 | } 34 | 35 | //------------------------------------------------------------------------------------------------- 36 | - (void)sendFeedbackToUrl:(NSString*)url 37 | feedbackText:(NSString*)feedbackText 38 | feedbackType:(NSString*)feedbackType 39 | systemProfile:(NSString*)systemProfile 40 | userEmail:(NSString*)userEmail 41 | 42 | { 43 | // create dictionary of fields to be transmitted using http POST 44 | NSDictionary* form = NSDictionaryWithKeysAndValues(@"feedbackType", feedbackType, 45 | @"feedback", feedbackText, 46 | @"email", userEmail != nil ? userEmail : @"", 47 | @"appName", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"], 48 | @"bundleID", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"], 49 | @"version", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"], 50 | @"systemProfile", systemProfile != nil ? systemProfile : @"", 51 | nil); 52 | // create request 53 | NSURLRequest* request = [NSURLRequest requestWithUrl:[NSURL URLWithString:url] postForm:form]; 54 | // begin sending the data 55 | _connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; 56 | [_connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 57 | [_connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSModalPanelRunLoopMode]; 58 | [_connection start]; 59 | } 60 | 61 | //------------------------------------------------------------------------------------------------- 62 | - (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error 63 | { 64 | if (!_isCanceled) 65 | { 66 | [self.delegate feedbackSender:self didFinishWithError:error]; 67 | } 68 | } 69 | 70 | //------------------------------------------------------------------------------------------------- 71 | - (void)connectionDidFinishLoading:(NSURLConnection*)connection 72 | { 73 | if (!_isCanceled) 74 | { 75 | [self.delegate feedbackSender:self didFinishWithError:nil]; 76 | } 77 | } 78 | 79 | //------------------------------------------------------------------------------------------------- 80 | - (void)cancel 81 | { 82 | _isCanceled = YES; 83 | } 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /Demo/DemoAppDelegate.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import "DemoAppDelegate.h" 7 | #import "DFFeedbackWindowController.h" 8 | #import "DFCrashReportWindowController.h" 9 | 10 | //------------------------------------------------------------------------------------------------- 11 | @interface DemoAppDelegate() 12 | 13 | @property (assign) IBOutlet NSWindow* window; 14 | 15 | @end 16 | 17 | //------------------------------------------------------------------------------------------------- 18 | @implementation DemoAppDelegate 19 | 20 | //------------------------------------------------------------------------------------------------- 21 | - (void)applicationDidFinishLaunching:(NSNotification*)notification 22 | { 23 | // TODO: insert your feedback URL here 24 | NSString* feedbackUrl = @""; 25 | NSString* updateUrl = @""; 26 | [DFFeedbackWindowController initializeWithFeedbackUrl:feedbackUrl 27 | systemProfileDataTypes:DFSystemProfileData_All]; 28 | // TODO: insert your icon here 29 | [DFCrashReportWindowController initializeWithFeedbackUrl:feedbackUrl 30 | updateUrl:updateUrl 31 | icon:[NSApp applicationIconImage] 32 | systemProfileDataTypes:DFSystemProfileData_All]; 33 | } 34 | 35 | //------------------------------------------------------------------------------------------------- 36 | - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)application 37 | { 38 | return YES; 39 | } 40 | 41 | //------------------------------------------------------------------------------------------------- 42 | - (IBAction)sendGeneralQuestion:(id)sender 43 | { 44 | [[DFFeedbackWindowController singleton] showGeneralQuestion]; 45 | } 46 | 47 | //------------------------------------------------------------------------------------------------- 48 | - (IBAction)sendBugReport:(id)sender 49 | { 50 | [[DFFeedbackWindowController singleton] showBugReport]; 51 | } 52 | 53 | //------------------------------------------------------------------------------------------------- 54 | - (IBAction)sendFeatureRequest:(id)sender 55 | { 56 | [[DFFeedbackWindowController singleton] showFeatureRequest]; 57 | } 58 | 59 | //------------------------------------------------------------------------------------------------- 60 | - (void)crashThread:(BOOL)isGCD 61 | { 62 | NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 63 | 64 | NSAssert(false, @"Test crash in a separate %@ thread", isGCD ? @"GCD" : @"NS"); 65 | 66 | [pool release]; 67 | 68 | [NSThread exit]; 69 | } 70 | 71 | //------------------------------------------------------------------------------------------------- 72 | - (IBAction)testCrashInNSThread:(id)sender 73 | { 74 | [NSThread detachNewThreadSelector:@selector(crashThread:) toTarget:self withObject:(id)NO]; 75 | } 76 | 77 | //------------------------------------------------------------------------------------------------- 78 | - (IBAction)testCrashInGCDThread:(id)sender 79 | { 80 | dispatch_queue_t dispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 81 | dispatch_async(dispatchQueue, 82 | ^{ 83 | [self crashThread:YES]; 84 | }); 85 | } 86 | 87 | //------------------------------------------------------------------------------------------------- 88 | - (IBAction)testCrash:(id)sender 89 | { 90 | NSAssert(false, @"Test Crash"); 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /DFSystemProfileDataType.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | // Kinds of data to retrieve from system_profiler tool 10 | // Note: there can be more data types depending on the macOS version, use system_profiler -listDataTypes to list all 11 | typedef enum : UInt64 12 | { 13 | DFSystemProfileData_All = 0, 14 | DFSystemProfileData_AirPort = 1L << 0, // SPAirPortDataType 15 | DFSystemProfileData_Applications = 1L << 1, // SPApplicationsDataType 16 | DFSystemProfileData_Audio = 1L << 2, // SPAudioDataType 17 | DFSystemProfileData_Bluetooth = 1L << 3, // SPBluetoothDataType 18 | DFSystemProfileData_Camera = 1L << 4, // SPCameraDataType 19 | DFSystemProfileData_CardReader = 1L << 5, // SPCardReaderDataType 20 | DFSystemProfileData_Component = 1L << 6, // SPComponentDataType 21 | DFSystemProfileData_ConfigurationProfile = 1L << 7, // SPConfigurationProfileDataType 22 | DFSystemProfileData_DeveloperTools = 1L << 8, // SPDeveloperToolsDataType 23 | DFSystemProfileData_Diagnostics = 1L << 9, // SPDiagnosticsDataType 24 | DFSystemProfileData_DisabledSoftware = 1L << 10, // SPDisabledSoftwareDataType 25 | DFSystemProfileData_DiscBurning = 1L << 11, // SPDiscBurningDataType 26 | DFSystemProfileData_EthernetCards = 1L << 12, // SPEthernetDataType 27 | DFSystemProfileData_Extensions = 1L << 13, // SPExtensionsDataType 28 | DFSystemProfileData_Firewall = 1L << 14, // SPFirewallDataType 29 | DFSystemProfileData_FireWire = 1L << 15, // SPFireWireDataType 30 | DFSystemProfileData_FibreChannel = 1L << 16, // SPFibreChannelDataType 31 | DFSystemProfileData_Fonts = 1L << 17, // SPFontsDataType 32 | DFSystemProfileData_Frameworks = 1L << 18, // SPFrameworksDataType 33 | DFSystemProfileData_Displays = 1L << 19, // SPDisplaysDataType 34 | DFSystemProfileData_Hardware = 1L << 20, // SPHardwareDataType 35 | DFSystemProfileData_HardwareRAID = 1L << 21, // SPHardwareRAIDDataType 36 | DFSystemProfileData_InstallHistory = 1L << 22, // SPInstallHistoryDataType 37 | DFSystemProfileData_Logs = 1L << 23, // SPLogsDataType 38 | DFSystemProfileData_ManagedClient = 1L << 24, // SPManagedClientDataType 39 | DFSystemProfileData_Memory = 1L << 25, // SPMemoryDataType 40 | DFSystemProfileData_Network = 1L << 26, // SPNetworkDataType 41 | DFSystemProfileData_NetworkLocations = 1L << 27, // SPNetworkLocationDataType 42 | DFSystemProfileData_NetworkVolume = 1L << 28, // SPNetworkVolumeDataType 43 | DFSystemProfileData_NVMe = 1L << 29, // SPNVMeDataType 44 | DFSystemProfileData_ParallelATA = 1L << 30, // SPParallelATADataType 45 | DFSystemProfileData_ParallelSCSI = 1L << 31, // SPParallelSCSIDataType 46 | DFSystemProfileData_PCI = 1L << 32, // SPPCIDataType 47 | DFSystemProfileData_Power = 1L << 33, // SPPowerDataType 48 | DFSystemProfileData_PrefPane = 1L << 34, // SPPrefPaneDataType 49 | DFSystemProfileData_PrinterSoftware = 1L << 35, // SPPrintersSoftwareDataType 50 | DFSystemProfileData_Printers = 1L << 36, // SPPrintersDataType 51 | DFSystemProfileData_SAS = 1L << 37, // SPSASDataType 52 | DFSystemProfileData_SerialATA = 1L << 38, // SPSerialATADataType 53 | DFSystemProfileData_Software = 1L << 39, // SPSoftwareDataType 54 | DFSystemProfileData_SPI = 1L << 40, // SPSPIDataType 55 | DFSystemProfileData_StartupItem = 1L << 41, // SPStartupItemDataType 56 | DFSystemProfileData_Storage = 1L << 42, // SPStorageDataType 57 | DFSystemProfileData_SyncServices = 1L << 43, // SPSyncServicesDataType 58 | DFSystemProfileData_Thunderbolt = 1L << 44, // SPThunderboltDataType 59 | DFSystemProfileData_UniversalAccess = 1L << 45, // SPUniversalAccessDataType 60 | DFSystemProfileData_USB = 1L << 46, // SPUSBDataType 61 | DFSystemProfileData_WWAN = 1L << 47, // SPWWANDataType 62 | } DFSystemProfileDataType; 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /ReadMe.markdown: -------------------------------------------------------------------------------- 1 | # What's the buzz? 2 | 3 | DFeedback (DaisyDisk Feedback) is a two-in-one component for providing user feedback: 4 | 5 | It allows the user to send feedback and request support from within your app. Along with the message, the component can optionally send the user's e-mail and system configuration profile. 6 | ![Screenshot](http://cl.ly/image/071u0O021t2J/Feedback.png) 7 | 8 | It automatically catches all unhandled exceptions and shows a crash report window suggesting the user to send an anonymous crash report, along with the stack trace, system configuration profile and optional user comments. 9 | ![Screenshot](http://f.cl.ly/items/2G1k2Y353B2h2R3i2J3a/crash.png) 10 | 11 | # Usage 12 | 13 | DFeedback is a package of source code files (Cocoa, Obj-C) that you need to add to your project. 14 | 15 | ## The feedback window 16 | First, call `DFFeedbackWindowController initializeWithFeedbackUrl:systemProfileDataTypes:` once to initialize the component and provide a URL for sending the feedback. (For example, in your `NSApplicationDelegate applicationWillFinishLaunching:`) 17 | Call one of `DFFeedbackWindowController show*` methods to display the feedback dialog. 18 | 19 | The `systemProfileDataTypes` parameter allows to filter the system profile string, to remove long unnecessary parts, such as Printer Software. There is a hidden feature: if you hold OPT key when calling `DFFeedbackWindowController showBugReport` or `DFFeedbackWindowController showFeatureRequest`, or switching to Bug Report or Feature Request tabs, in other words, whenever the system profile begins fetching, you can gather the entire unfiltered profile. You can tell the user to do so if you happen to need the full profile instead of the filtered one. 20 | 21 | ## The crash reporter 22 | First, call `DFCrashReporterWindowController initializeWithFeedbackUrl:updateUrl:icon:systemProfileDataTypes:` once to initialize the component and provide a URL for sending the feedback. (For example, in your -`NSApplicationDelegate applicationWillFinishLaunching:`) 23 | Specify `DFApplication` class as the principle class in your application's Info.plist file. Or, if you already use your own application principal class subclassed from `NSApplication`, subclass it from the `DFApplication` class instead. 24 | 25 | Use `DFApplication isPortmortem` flag: 26 | In your `NSApplicationDelegate applicationShouldTerminateAfterLastWindowClosed:`, if your app should normally quit on last window closed, but not so after a crash, when the crash reporter will forcedly close all windows and show the report window, like this: 27 | 28 | (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)application 29 | { 30 | // don't quit at this point and allow the crash reporter to show first 31 | return ![(DFApplication*)NSApp isPostmortem]; 32 | } 33 | 34 | In your application delegate's `NSUserInterfaceValidations validateUserInterfaceItem:`, in order to disable all (or some) menu items while the app is being terminated due to an uncaught exception (no need to do the same in window delegates, because all windows will be forcedly closed by the crash reporter), like this: 35 | 36 | (BOOL)validateUserInterfaceItem:(id)item 37 | { 38 | // disable all menu items while showing the crash reporter 39 | if ([(DFApplication*)NSApp isPostmortem]) 40 | { 41 | return NO; 42 | } 43 | … 44 | } 45 | 46 | Note the `+[DFApplication ignoreExceptionsWhoseStackTraceContains:]` method. You can use it to absorb certain exceptions in case if their stack trace contains either of the specified strings. This is useful to avoid crashing on exceptions in third-party components. 47 | 48 | The two above components are independent on each other, you can use any or both of them. 49 | 50 | If you want to do some fine-tuning, there is DFStyleSheet.m file containing some appearance/behavior constants. 51 | 52 | Don't forget to link to the following frameworks: 53 | - QuatzCore 54 | - AddressBook 55 | 56 | # Origin, credits and legal stuff 57 | 58 | DFeedback was inspired by [JRFeedbackProvider](https://github.com/rentzsch/jrfeedbackprovider) and [FeedbackReporter](https://github.com/tcurdt/feedbackreporter), but totally rewritten from scratch (except maybe some tiny portions of code) to provide better experience. 59 | 60 | The component is available under the terms of non-restrictive [MIT license](http://en.wikipedia.org/wiki/MIT_License). 61 | 62 | The stack trace for the crash reporter is obtained using the [Google Toolbox for Mac](http://code.google.com/p/google-toolbox-for-mac/). 63 | 64 | Portions of the software may contain third party code distributed under other non-restrictive licenses. 65 | 66 | Love DFeedback and use it in your own projects? Feel free to [send](http://www.daisydiskapp.com/support.php) us a link/screenshot. 67 | -------------------------------------------------------------------------------- /Helpers/OSXVersion.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import "OSXVersion.h" 7 | #import "SoftwareVersion.h" 8 | 9 | //------------------------------------------------------------------------------------------------- 10 | static OSXGeneration _generation = OSXGeneration_Unknown; 11 | static SoftwareVersion* _version = nil; 12 | 13 | //------------------------------------------------------------------------------------------------- 14 | @implementation OSXVersion 15 | 16 | //------------------------------------------------------------------------------------------------- 17 | + (OSXGeneration)generation; 18 | { 19 | if (_generation == OSXGeneration_Unknown) 20 | { 21 | OSXGeneration result = _generation; 22 | 23 | SInt32 majorVersion = 0; 24 | SInt32 minorVersion = 0; 25 | BOOL hasVersion = NO; 26 | 27 | // pre-yosemite deprecated way 28 | #if __MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10 29 | if (Gestalt(gestaltSystemVersionMajor, &majorVersion) == noErr) 30 | { 31 | if (Gestalt(gestaltSystemVersionMinor, &minorVersion) == noErr) 32 | { 33 | hasVersion = YES; 34 | } 35 | } 36 | #else 37 | NSOperatingSystemVersion version = NSProcessInfo.processInfo.operatingSystemVersion; 38 | majorVersion = (SInt32)version.majorVersion; 39 | minorVersion = (SInt32)version.minorVersion; 40 | hasVersion = YES; 41 | 42 | #endif 43 | if (hasVersion) 44 | { 45 | if (majorVersion == 10) 46 | { 47 | switch (minorVersion) 48 | { 49 | case 12: 50 | result = OSXGeneration_Sierra; 51 | break; 52 | case 11: 53 | result = OSXGeneration_ElCapitan; 54 | break; 55 | case 10: 56 | result = OSXGeneration_Yosemite; 57 | break; 58 | case 9: 59 | result = OSXGeneration_Mavericks; 60 | break; 61 | case 8: 62 | result = OSXGeneration_MountainLion; 63 | break; 64 | case 7: 65 | result = OSXGeneration_Lion; 66 | break; 67 | case 6: 68 | result = OSXGeneration_SnowLeopard; 69 | break; 70 | case 5: 71 | result = OSXGeneration_Leopard; 72 | break; 73 | 74 | default: 75 | break; 76 | } 77 | } 78 | 79 | if (result == OSXGeneration_Unknown) 80 | { 81 | // an attempt to provide future compatibility 82 | if (majorVersion >= 0 && majorVersion <= 0xFF && minorVersion >= 0 && minorVersion <= 0xFF) 83 | { 84 | result = (OSXGeneration)((majorVersion << 8) | minorVersion); 85 | } 86 | } 87 | } 88 | 89 | _generation = result; 90 | } 91 | return _generation; 92 | } 93 | 94 | //------------------------------------------------------------------------------------------------- 95 | + (SoftwareVersion*)version 96 | { 97 | if (_version == nil) 98 | { 99 | SInt32 majorVersion = 0; 100 | SInt32 minorVersion = 0; 101 | SInt32 buildVersion = 0; 102 | BOOL hasVersion = NO; 103 | 104 | #if __MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10 105 | if (Gestalt(gestaltSystemVersionMajor, &majorVersion) == noErr) 106 | { 107 | if (Gestalt(gestaltSystemVersionMinor, &minorVersion) == noErr) 108 | { 109 | if (Gestalt(gestaltSystemVersionBugFix, &buildVersion) == noErr) 110 | { 111 | hasVersion = YES; 112 | } 113 | } 114 | } 115 | #else 116 | NSOperatingSystemVersion version = NSProcessInfo.processInfo.operatingSystemVersion; 117 | majorVersion = (SInt32)version.majorVersion; 118 | minorVersion = (SInt32)version.minorVersion; 119 | buildVersion = (SInt32)version.patchVersion; 120 | hasVersion = YES; 121 | 122 | #endif 123 | 124 | if (hasVersion) 125 | { 126 | NSUInteger numbers[3] = {(NSUInteger)majorVersion, (NSUInteger)minorVersion, (NSUInteger)buildVersion}; 127 | _version = [[SoftwareVersion makeVersionFromNumbers:numbers count:3] retain]; 128 | [_version makeDisplayName]; 129 | } 130 | } 131 | 132 | return _version; 133 | } 134 | 135 | 136 | @end 137 | -------------------------------------------------------------------------------- /Helpers/ApplicationSandboxInfo.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import 7 | #import 8 | #import "ApplicationSandboxInfo.h" 9 | 10 | //------------------------------------------------------------------------------------------------- 11 | static NSString* const kEntitlementSandbox = @"com.apple.security.app-sandbox"; 12 | static NSString* const kEntitlementAddressBookData = @"com.apple.security.personal-information.addressbook"; 13 | 14 | //------------------------------------------------------------------------------------------------- 15 | static ApplicationSandboxInfo* _singleton = nil; 16 | 17 | //------------------------------------------------------------------------------------------------- 18 | @implementation ApplicationSandboxInfo 19 | { 20 | NSURL* _bundleUrl; 21 | NSMutableDictionary* _queriedEntitlements; 22 | } 23 | 24 | //------------------------------------------------------------------------------------------------- 25 | + (ApplicationSandboxInfo*)singleton 26 | { 27 | if (_singleton == nil) 28 | { 29 | _singleton = [[ApplicationSandboxInfo alloc] initWithBundleUrl:[NSBundle mainBundle].bundleURL]; 30 | } 31 | return _singleton; 32 | } 33 | 34 | //------------------------------------------------------------------------------------------------- 35 | - (id)initWithBundleUrl:(NSURL*)bundleUrl 36 | { 37 | self = [super init]; 38 | if (self != nil) 39 | { 40 | _bundleUrl = [bundleUrl retain]; 41 | _queriedEntitlements = [[NSMutableDictionary alloc] initWithCapacity:1]; 42 | } 43 | return self; 44 | } 45 | 46 | //------------------------------------------------------------------------------------------------- 47 | - (void)dealloc 48 | { 49 | [_bundleUrl release]; 50 | [_queriedEntitlements release]; 51 | [super dealloc]; 52 | } 53 | 54 | //------------------------------------------------------------------------------------------------- 55 | - (BOOL)hasEntitlements:(NSArray*)entitlementCodes 56 | { 57 | BOOL result = YES; 58 | 59 | // filter those codes that have already been queried 60 | NSMutableArray* newEntitlementCodes = [NSMutableArray arrayWithCapacity:entitlementCodes.count]; 61 | for (NSString* entitlementCode in entitlementCodes) 62 | { 63 | NSNumber* entitlementValue = _queriedEntitlements[entitlementCode]; 64 | if (entitlementValue == nil) 65 | { 66 | [newEntitlementCodes addObject:entitlementCode]; 67 | } 68 | else 69 | { 70 | // if at least one entitlement failed, the entire result failed 71 | if (!entitlementValue.boolValue) 72 | { 73 | result = NO; 74 | break; 75 | } 76 | } 77 | } 78 | 79 | if (result) 80 | { 81 | // query new codes 82 | for (NSString* entitlementCode in newEntitlementCodes) 83 | { 84 | BOOL entitlementResult = NO; 85 | // create static code object for the bundle 86 | SecStaticCodeRef bundleStaticCode = NULL; 87 | SecStaticCodeCreateWithPath((CFURLRef)_bundleUrl, kSecCSDefaultFlags, &bundleStaticCode); 88 | if (bundleStaticCode != NULL) 89 | { 90 | // create requirement 91 | NSString* requirementString = [NSString stringWithFormat:@"entitlement[\"%@\"] exists", entitlementCode]; 92 | SecRequirementRef entitlementRequirement = NULL; 93 | SecRequirementCreateWithString((CFStringRef)requirementString, kSecCSDefaultFlags, &entitlementRequirement); 94 | if (entitlementRequirement != NULL) 95 | { 96 | // validate code signature and at the same time check requirement 97 | OSStatus signatureValidationResult = SecStaticCodeCheckValidityWithErrors(bundleStaticCode, (SecCSFlags)kSecCSBasicValidateOnly, entitlementRequirement, NULL); 98 | if (signatureValidationResult == errSecSuccess) 99 | { 100 | entitlementResult = YES; 101 | } 102 | CFRelease(entitlementRequirement); 103 | } 104 | CFRelease(bundleStaticCode); 105 | } 106 | // save result 107 | _queriedEntitlements[entitlementCode] = @(entitlementResult); 108 | 109 | // if at least one entitlement failed, the entire result failed 110 | if (!entitlementResult) 111 | { 112 | result = NO; 113 | break; 114 | } 115 | } 116 | } 117 | 118 | return result; 119 | } 120 | 121 | //------------------------------------------------------------------------------------------------- 122 | + (BOOL)hasAddressBookDataEntitlement 123 | { 124 | return [self.singleton hasEntitlements:@[kEntitlementSandbox, kEntitlementAddressBookData]]; 125 | } 126 | 127 | //------------------------------------------------------------------------------------------------- 128 | + (BOOL)isSandboxed 129 | { 130 | return [self.singleton hasEntitlements:@[kEntitlementSandbox]]; 131 | } 132 | 133 | 134 | @end 135 | -------------------------------------------------------------------------------- /Helpers/SoftwareVersion.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import "SoftwareVersion.h" 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | typedef enum : NSUInteger 10 | { 11 | VersionPart_Major = 0, 12 | VersionPart_Minor = 1, 13 | VersionPart_Build = 2, 14 | VersionPart_Count 15 | } VersionPartIndex; 16 | 17 | typedef struct 18 | { 19 | BOOL isSet; 20 | NSUInteger number; 21 | NSString* string; 22 | } VersionPart; 23 | 24 | //------------------------------------------------------------------------------------------------- 25 | @implementation SoftwareVersion 26 | { 27 | VersionPart _parts[VersionPart_Count]; 28 | } 29 | 30 | //------------------------------------------------------------------------------------------------- 31 | - (id)init 32 | { 33 | self = [super init]; 34 | if (self != nil) 35 | { 36 | bzero(_parts, sizeof(_parts)); 37 | } 38 | return self; 39 | } 40 | 41 | //------------------------------------------------------------------------------------------------- 42 | - (void)dealloc 43 | { 44 | [_displayName release]; 45 | for (VersionPartIndex i = 0; i < VersionPart_Count; ++i) 46 | { 47 | [_parts[i].string release]; 48 | } 49 | [super dealloc]; 50 | } 51 | 52 | //------------------------------------------------------------------------------------------------- 53 | + (SoftwareVersion*)makeVersionFromString:(NSString*)versionString 54 | { 55 | SoftwareVersion* result = [[[SoftwareVersion alloc] init] autorelease]; 56 | NSArray* partStrings = [versionString componentsSeparatedByString:@"."]; 57 | VersionPartIndex partCount = MIN(partStrings.count, VersionPart_Count); 58 | for (VersionPartIndex i = 0; i < partCount; ++i) 59 | { 60 | NSString* partString = partStrings[i]; 61 | NSScanner* scanner = [NSScanner scannerWithString:partString]; 62 | VersionPart* part = result.parts + i; 63 | NSInteger number = 0; 64 | part->isSet = [scanner scanInteger:&number] && number >= 0; 65 | if (part->isSet) 66 | { 67 | part->number = (NSUInteger)number; 68 | if (!scanner.isAtEnd) 69 | { 70 | part->string = [[partString substringFromIndex:scanner.scanLocation] retain]; 71 | } 72 | } 73 | else 74 | { 75 | // invalid format, do not continue 76 | break; 77 | } 78 | } 79 | // must be at least one valid part 80 | if (!result.parts[0].isSet) 81 | { 82 | result = nil; 83 | } 84 | 85 | return result; 86 | } 87 | 88 | //------------------------------------------------------------------------------------------------- 89 | + (SoftwareVersion*)makeVersionFromNumbers:(const NSUInteger*)numbers 90 | count:(NSUInteger)count 91 | { 92 | SoftwareVersion* result = nil; 93 | if (count > 0) 94 | { 95 | result = [[[SoftwareVersion alloc] init] autorelease]; 96 | VersionPartIndex partCount = MIN(count, VersionPart_Count); 97 | for (VersionPartIndex i = 0; i < partCount; ++i) 98 | { 99 | VersionPart* part = result.parts + i; 100 | part->isSet = YES; 101 | part->number = numbers[i]; 102 | } 103 | } 104 | return result; 105 | } 106 | 107 | //------------------------------------------------------------------------------------------------- 108 | - (void)roundDownToParts:(NSUInteger)numberOfParts 109 | { 110 | for (VersionPartIndex i = numberOfParts; i < VersionPart_Count; ++i) 111 | { 112 | VersionPart* part = _parts + i; 113 | part->number = 0; 114 | [part->string release]; 115 | part->string = nil; 116 | part->isSet = NO; 117 | } 118 | } 119 | 120 | //------------------------------------------------------------------------------------------------- 121 | - (NSComparisonResult)compare:(SoftwareVersion*)other 122 | { 123 | if (other == nil) 124 | { 125 | return NSOrderedDescending; 126 | } 127 | for (VersionPartIndex i = 0; i < VersionPart_Count; ++i) 128 | { 129 | VersionPart* selfPart = _parts + i; 130 | VersionPart* otherPart = other.parts + i; 131 | // compare presense 132 | if (!selfPart->isSet && otherPart->isSet) 133 | { 134 | return NSOrderedAscending; 135 | } 136 | if (selfPart->isSet && !otherPart->isSet) 137 | { 138 | return NSOrderedDescending; 139 | } 140 | if (selfPart->isSet && otherPart->isSet) 141 | { 142 | // compare numbers 143 | if (selfPart->number < otherPart->number) 144 | { 145 | return NSOrderedAscending; 146 | } 147 | if (selfPart->number > otherPart->number) 148 | { 149 | return NSOrderedDescending; 150 | } 151 | // compare strings 152 | // string (like b, rc) is earlier than without string 153 | if (selfPart->string.length == 0 && otherPart->string.length > 0) 154 | { 155 | return NSOrderedDescending; 156 | } 157 | if (selfPart->string.length > 0 && otherPart->string.length == 0) 158 | { 159 | return NSOrderedAscending; 160 | } 161 | if (selfPart->string.length > 0 && otherPart->string.length > 0) 162 | { 163 | NSComparisonResult stringResult = [selfPart->string compare:otherPart->string]; 164 | // continue if strings are the same 165 | if (stringResult != NSOrderedSame) 166 | { 167 | return stringResult; 168 | } 169 | } 170 | } 171 | } 172 | return NSOrderedSame; 173 | } 174 | 175 | //------------------------------------------------------------------------------------------------- 176 | - (NSString*)displayNameUpToPart:(VersionPartIndex)partIndex 177 | { 178 | NSString* displayName = nil; 179 | for (VersionPartIndex i = 0; i <= partIndex; ++i) 180 | { 181 | VersionPart* part = _parts + i; 182 | if (part->isSet) 183 | { 184 | if (i == 0) 185 | { 186 | displayName = @""; 187 | } 188 | else 189 | { 190 | displayName = [displayName stringByAppendingString:@"."]; 191 | } 192 | displayName = [displayName stringByAppendingFormat:@"%lu", part->number]; 193 | if (part->string != nil) 194 | { 195 | displayName = [displayName stringByAppendingFormat:@"%@", part->string]; 196 | } 197 | } 198 | else 199 | { 200 | break; 201 | } 202 | } 203 | return displayName; 204 | } 205 | 206 | //------------------------------------------------------------------------------------------------- 207 | - (NSString*)displayNameUpToMinor 208 | { 209 | return [self displayNameUpToPart:VersionPart_Minor]; 210 | } 211 | 212 | //------------------------------------------------------------------------------------------------- 213 | - (void)makeDisplayName 214 | { 215 | NSString* displayName = [self displayNameUpToPart:VersionPart_Count - 1]; 216 | self.displayName = displayName; 217 | } 218 | 219 | 220 | //------------------------------------------------------------------------------------------------- 221 | - (VersionPart*)parts 222 | { 223 | return _parts; 224 | } 225 | 226 | //------------------------------------------------------------------------------------------------- 227 | - (NSUInteger)majorNumber 228 | { 229 | return _parts[VersionPart_Major].number; 230 | } 231 | 232 | //------------------------------------------------------------------------------------------------- 233 | - (NSUInteger)minorNumber 234 | { 235 | return _parts[VersionPart_Minor].number; 236 | } 237 | 238 | //------------------------------------------------------------------------------------------------- 239 | - (NSUInteger)buildNumber 240 | { 241 | return _parts[VersionPart_Build].number; 242 | } 243 | 244 | @end 245 | -------------------------------------------------------------------------------- /DFApplication.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import "DFApplication.h" 7 | #import "DFCrashReportWindowController.h" 8 | #import "GTMStackTrace.h" 9 | #import "OSXVersion.h" 10 | 11 | //------------------------------------------------------------------------------------------------- 12 | static NSString* const kUserDefaultCrashSequenceCount = @"DFApplication_crashSequenceCount"; 13 | static NSUInteger const kCrashSequenceCountMax = 3; 14 | 15 | //------------------------------------------------------------------------------------------------- 16 | static NSMutableArray* _ignoredExceptionStrings = nil; 17 | static BOOL _hasHardcodedExceptions = NO; 18 | 19 | //------------------------------------------------------------------------------------------------- 20 | @implementation DFApplication 21 | { 22 | BOOL _isRelaunching; 23 | BOOL _isPostmortem; 24 | } 25 | 26 | //------------------------------------------------------------------------------------------------- 27 | + (void)ignoreExceptionsWhoseStackTraceContains:(NSArray*)strings 28 | { 29 | if (_ignoredExceptionStrings == nil) 30 | { 31 | _ignoredExceptionStrings = [[NSMutableArray alloc] init]; 32 | } 33 | 34 | if (strings != nil) 35 | { 36 | [_ignoredExceptionStrings addObjectsFromArray:strings]; 37 | } 38 | } 39 | 40 | //------------------------------------------------------------------------------------------------- 41 | - (id)init 42 | { 43 | self = [super init]; 44 | if (self != nil) 45 | { 46 | if (!_hasHardcodedExceptions) 47 | { 48 | [self.class ignoreExceptionsWhoseStackTraceContains: @[@"Versions/A/Sparkle", 49 | @"SIMBL", 50 | @"MTTextTools"]]; 51 | _hasHardcodedExceptions = YES; 52 | } 53 | } 54 | return self; 55 | } 56 | 57 | //------------------------------------------------------------------------------------------------- 58 | - (void)dealloc 59 | { 60 | [super dealloc]; 61 | } 62 | 63 | //------------------------------------------------------------------------------------------------- 64 | - (void)launchAnotherInstanceAndWaitForTermination 65 | { 66 | _isRelaunching = YES; 67 | 68 | // NOTE: it seems to sometimes not work, at least it fails sometimes on 10.8 in sandboxed mode 69 | 70 | // launch a script that waits for the app to exit and then relaunches it 71 | NSString* scriptPath = [[NSBundle mainBundle] pathForResource:@"DFRelaunch" ofType:@"sh"]; 72 | NSString* bundlePath = [NSString stringWithFormat:@"%s", [NSBundle mainBundle].bundlePath.fileSystemRepresentation]; 73 | NSString* processID = [NSString stringWithFormat:@"%d", [NSProcessInfo processInfo].processIdentifier]; 74 | NSString* bundleID = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]; 75 | NSArray* arguments = @[scriptPath, 76 | bundlePath, 77 | processID, 78 | bundleID]; 79 | NSTask* task = [[[NSTask alloc] init] autorelease]; 80 | task.launchPath = @"/bin/bash"; 81 | task.arguments = arguments; 82 | [task launch]; 83 | } 84 | 85 | //------------------------------------------------------------------------------------------------- 86 | - (void)relaunch 87 | { 88 | // prevent endless loop of relaunch and crash 89 | NSUInteger crashSequenceCount = (NSUInteger)[[NSUserDefaults standardUserDefaults] integerForKey:kUserDefaultCrashSequenceCount]; 90 | if (crashSequenceCount < kCrashSequenceCountMax - 1) 91 | { 92 | [self launchAnotherInstanceAndWaitForTermination]; 93 | } 94 | [self terminate:self]; 95 | } 96 | 97 | //------------------------------------------------------------------------------------------------- 98 | - (void)terminate:(id)sender 99 | { 100 | // abnormal termination 101 | if (_isPostmortem && _isRelaunching) 102 | { 103 | // save sequential crash count 104 | NSUInteger crashSequenceCount = (NSUInteger)[[NSUserDefaults standardUserDefaults] integerForKey:kUserDefaultCrashSequenceCount]; 105 | ++crashSequenceCount; 106 | [[NSUserDefaults standardUserDefaults] setInteger:(NSInteger)crashSequenceCount 107 | forKey:kUserDefaultCrashSequenceCount]; 108 | [[NSUserDefaults standardUserDefaults] synchronize]; 109 | 110 | } 111 | // normal termination 112 | else 113 | { 114 | // reset crash counter 115 | [[NSUserDefaults standardUserDefaults] removeObjectForKey:kUserDefaultCrashSequenceCount]; 116 | [[NSUserDefaults standardUserDefaults] synchronize]; 117 | } 118 | // do terminate 119 | [super terminate:self]; 120 | } 121 | 122 | //------------------------------------------------------------------------------------------------- 123 | - (BOOL)shouldIgnoreException:(NSException*)exception 124 | exceptionStackTrace:(NSString*)exceptionStackTrace 125 | { 126 | BOOL result = NO; 127 | NSString* exceptionName = exception.name; 128 | 129 | // accessibility exceptions are a normal mode of operation, should be ignored 130 | BOOL isAccessibilityException = [exceptionName isEqualToString:NSAccessibilityException]; 131 | if (isAccessibilityException) 132 | { 133 | result = YES; 134 | } 135 | else 136 | { 137 | for (NSString* ignoredString in _ignoredExceptionStrings) 138 | { 139 | BOOL isMatch = [exceptionStackTrace rangeOfString:ignoredString].location != NSNotFound; 140 | if (isMatch) 141 | { 142 | result = YES; 143 | break; 144 | } 145 | } 146 | } 147 | 148 | return result; 149 | } 150 | 151 | //------------------------------------------------------------------------------------------------- 152 | - (void)reportExceptionInMainThread:(NSException*)exception 153 | exceptionStackTrace:(NSString*)exceptionStackTrace 154 | { 155 | @try 156 | { 157 | if (!_isPostmortem) 158 | { 159 | // prevent endless loop 160 | _isPostmortem = YES; 161 | 162 | NSLog(@"Reporting exception: %@", exception.reason); 163 | 164 | // hide all windows 165 | for (NSWindow* window in [NSApp windows]) 166 | { 167 | [window orderOut:self]; 168 | } 169 | 170 | // show problem report window 171 | [[DFCrashReportWindowController singleton] showReportForException:exception 172 | exceptionStackTrace:exceptionStackTrace]; 173 | } 174 | } 175 | @catch (NSException* fatalException) 176 | { 177 | // the exception occurred during exception handling - considered fatal 178 | NSLog(@"Fatal error:%@\nwhile processing exception: %@", fatalException.reason, exception.reason); 179 | @try 180 | { 181 | [(DFApplication*)NSApp relaunch]; 182 | } 183 | @catch (NSException* exception) 184 | { 185 | // absorb silently 186 | } 187 | } 188 | } 189 | 190 | //------------------------------------------------------------------------------------------------- 191 | - (void)reportException:(NSException*)exception 192 | { 193 | if (OSXVersion.generation < OSXGeneration_Sierra) 194 | { 195 | // starting from Sierra, this causes another dialog about the crash to display 196 | [super reportException:exception]; 197 | } 198 | 199 | NSString* exceptionStackTrace = GTMStackTraceFromException(exception); 200 | 201 | if (![self shouldIgnoreException:exception 202 | exceptionStackTrace:exceptionStackTrace]) 203 | { 204 | // main thread 205 | if ([NSThread currentThread] == [NSThread mainThread]) 206 | { 207 | [self reportExceptionInMainThread:exception 208 | exceptionStackTrace:exceptionStackTrace]; 209 | } 210 | // not main thread 211 | else 212 | { 213 | // handle on main thread 214 | dispatch_async(dispatch_get_main_queue(), 215 | ^{ 216 | [self reportExceptionInMainThread:exception 217 | exceptionStackTrace:exceptionStackTrace]; 218 | }); 219 | // exit immediately, or will crash the app 220 | // TODO: this causes a crash with signal if the thread is from GCD, not a pthread 221 | [NSThread exit]; 222 | } 223 | } 224 | } 225 | 226 | @end 227 | -------------------------------------------------------------------------------- /Views/DFBounceIconView.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import "DFBounceIconView.h" 7 | #import "DFStyleSheet.h" 8 | #import "GenericAnimation.h" 9 | 10 | //------------------------------------------------------------------------------------------------- 11 | @implementation DFBounceIconView 12 | { 13 | NSImage* _icon; 14 | BOOL _isShowing; 15 | NSAnimation* _animation; 16 | BOOL _isAnimatingForward; 17 | CGFloat _fromOpacity; 18 | CGFloat _fromSizeFactor; 19 | BOOL _suppressRefreshDuringAnimation; 20 | } 21 | 22 | //------------------------------------------------------------------------------------------------- 23 | - (id)initWithFrame:(NSRect)frame 24 | { 25 | self = [super initWithFrame:frame]; 26 | if (self != nil) 27 | { 28 | _animation = [[GenericAnimation alloc] initWithDuration:DFBounceIcon_animationDuration 29 | animationCurve:NSAnimationLinear]; 30 | _animation.animationBlockingMode = NSAnimationNonblocking; 31 | _animation.delegate = self; 32 | } 33 | return self; 34 | } 35 | 36 | //------------------------------------------------------------------------------------------------- 37 | - (void)dealloc 38 | { 39 | [_icon release]; 40 | _animation.delegate = nil; 41 | [_animation release]; 42 | [super dealloc]; 43 | } 44 | 45 | //------------------------------------------------------------------------------------------------- 46 | - (BOOL)isOpaque 47 | { 48 | return NO; 49 | } 50 | 51 | //------------------------------------------------------------------------------------------------- 52 | - (BOOL)isFlipped 53 | { 54 | return NO; 55 | } 56 | 57 | //------------------------------------------------------------------------------------------------- 58 | - (void)setIcon:(NSImage*)icon 59 | { 60 | if (icon != _icon) 61 | { 62 | [icon retain]; 63 | [_icon release]; 64 | _icon = icon; 65 | 66 | self.needsDisplay = YES; 67 | } 68 | } 69 | 70 | //------------------------------------------------------------------------------------------------- 71 | - (void)showWithAnimation:(BOOL)withAnimation 72 | { 73 | _isAnimatingForward = YES; 74 | _isShowing = YES; 75 | 76 | if (withAnimation) 77 | { 78 | _fromOpacity = [self calculateCurrentOpacity]; 79 | _fromSizeFactor = [self calculateCurrentSizeFactor]; 80 | [self restartAnimation]; 81 | } 82 | else 83 | { 84 | [_animation stopAnimation]; 85 | [self animationDidComplete:YES]; 86 | } 87 | } 88 | 89 | //------------------------------------------------------------------------------------------------- 90 | - (void)hideWithAnimation:(BOOL)withAnimation 91 | { 92 | // only hide when not already hidden 93 | if (_isShowing) 94 | { 95 | _isAnimatingForward = NO; 96 | if (withAnimation) 97 | { 98 | _fromOpacity = [self calculateCurrentOpacity]; 99 | _fromSizeFactor = [self calculateCurrentSizeFactor]; 100 | [self restartAnimation]; 101 | } 102 | else 103 | { 104 | [_animation stopAnimation]; 105 | [self animationDidComplete:YES]; 106 | } 107 | } 108 | } 109 | 110 | //------------------------------------------------------------------------------------------------- 111 | - (void)restartAnimation 112 | { 113 | [_animation stopAnimation]; 114 | _animation.duration = DFBounceIcon_animationDuration; 115 | _suppressRefreshDuringAnimation = YES; 116 | _animation.currentProgress = 0.; 117 | _suppressRefreshDuringAnimation = NO; 118 | [_animation startAnimation]; 119 | } 120 | 121 | //------------------------------------------------------------------------------------------------- 122 | - (void)animation:(GenericAnimation*)animation didProgress:(NSAnimationProgress)progress 123 | { 124 | if (animation == _animation) 125 | { 126 | if (!_suppressRefreshDuringAnimation) 127 | { 128 | self.needsDisplay = YES; 129 | [self displayIfNeeded]; 130 | } 131 | } 132 | } 133 | 134 | //------------------------------------------------------------------------------------------------- 135 | - (void)animationDidStop:(NSAnimation*)animation 136 | { 137 | if (animation == _animation) 138 | { 139 | [self animationDidComplete:NO]; 140 | } 141 | } 142 | 143 | //------------------------------------------------------------------------------------------------- 144 | - (void)animationDidEnd:(NSAnimation*)animation 145 | { 146 | if (animation == _animation) 147 | { 148 | [self animationDidComplete:YES]; 149 | } 150 | } 151 | 152 | //------------------------------------------------------------------------------------------------- 153 | - (void)animationDidComplete:(BOOL)isFinished 154 | { 155 | if (isFinished) 156 | { 157 | if (!_isAnimatingForward) 158 | { 159 | _isShowing = NO; 160 | } 161 | self.needsDisplay = YES; 162 | } 163 | } 164 | 165 | 166 | //------------------------------------------------------------------------------------------------- 167 | - (CGFloat)calculateCurrentOpacity 168 | { 169 | CGFloat result = 0.; 170 | if (_isShowing) 171 | { 172 | result = 1.; 173 | if (_animation.isAnimating) 174 | { 175 | CGFloat toOpacity = _isAnimatingForward ? 1. : 0.; 176 | CGFloat animationValue = _animation.currentValue; 177 | result = animationValue * (toOpacity - _fromOpacity) + _fromOpacity; 178 | } 179 | } 180 | return result; 181 | } 182 | 183 | //------------------------------------------------------------------------------------------------- 184 | - (CGFloat)calculateCurrentSizeFactor 185 | { 186 | CGFloat result = 1.; 187 | if (_isShowing) 188 | { 189 | if (_animation.isAnimating) 190 | { 191 | CGFloat animationValue = _animation.currentValue; 192 | CGFloat animationPhaseValue = animationValue; 193 | CGFloat fromSizeFactor = _fromSizeFactor; 194 | CGFloat toSizeFactor = 1.; 195 | if (_isAnimatingForward) 196 | { 197 | // bounce in 198 | if (animationValue <= 0.5) 199 | { 200 | toSizeFactor = [self calculateMaxSizeFactor]; 201 | animationPhaseValue = animationValue * 2.; 202 | } 203 | // then bounce out 204 | else 205 | { 206 | fromSizeFactor = [self calculateMaxSizeFactor]; 207 | toSizeFactor = 1.; 208 | animationPhaseValue = (animationValue - 0.5) * 2.; 209 | } 210 | } 211 | // just bouncing out 212 | else 213 | { 214 | toSizeFactor = 1.; 215 | } 216 | result = animationPhaseValue * (toSizeFactor - fromSizeFactor) + fromSizeFactor; 217 | } 218 | } 219 | return result; 220 | } 221 | 222 | //------------------------------------------------------------------------------------------------- 223 | - (CGFloat)calculateMaxSizeFactor 224 | { 225 | CGFloat result = 1.; 226 | NSSize iconSize = _icon.size; 227 | if (_icon != nil && iconSize.width > 0 && iconSize.height > 0) 228 | { 229 | NSSize boundsSize = self.bounds.size; 230 | result = fmin(boundsSize.width / iconSize.width, boundsSize.height / iconSize.height); 231 | } 232 | return result; 233 | } 234 | 235 | //------------------------------------------------------------------------------------------------- 236 | - (void)drawRect:(NSRect)dirtyRect 237 | { 238 | if (_icon != nil && _isShowing) 239 | { 240 | NSRect bounds = self.bounds; 241 | NSSize iconSize = _icon.size; 242 | 243 | // calculate center 244 | NSPoint center = NSMakePoint(bounds.origin.x + 0.5 * bounds.size.width, 245 | bounds.origin.y + 0.5 * bounds.size.height); 246 | // make sure we have integer pixels when not animating 247 | center.x = round(center.x); 248 | center.y = round(center.y); 249 | 250 | CGFloat currOpacity = [self calculateCurrentOpacity]; 251 | CGFloat currSizeFactor = [self calculateCurrentSizeFactor]; 252 | 253 | iconSize.width *= currSizeFactor; 254 | iconSize.height *= currSizeFactor; 255 | NSRect iconFrame = NSMakeRect(center.x - 0.5 * iconSize.width, 256 | center.y - 0.5 * iconSize.height, 257 | iconSize.width, 258 | iconSize.height); 259 | [_icon drawInRect:iconFrame 260 | fromRect:NSZeroRect 261 | operation:NSCompositingOperationSourceOver 262 | fraction:currOpacity 263 | respectFlipped:YES 264 | hints:nil]; 265 | } 266 | } 267 | 268 | 269 | 270 | @end 271 | -------------------------------------------------------------------------------- /Helpers/DFSystemProfileFetcher.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import "DFSystemProfileFetcher.h" 7 | #import "DFSystemProfileFetcherDelegate.h" 8 | #import "OSXVersion.h" 9 | #import "ApplicationSandboxInfo.h" 10 | 11 | //------------------------------------------------------------------------------------------------- 12 | static NSString* kDataTypeIDs[] = 13 | { 14 | @"SPAirPortDataType", 15 | @"SPApplicationsDataType", 16 | @"SPAudioDataType", 17 | @"SPBluetoothDataType", 18 | @"SPCameraDataType", 19 | @"SPCardReaderDataType", 20 | @"SPComponentDataType", 21 | @"SPConfigurationProfileDataType", 22 | @"SPDeveloperToolsDataType", 23 | @"SPDiagnosticsDataType", 24 | @"SPDisabledSoftwareDataType", 25 | @"SPDiscBurningDataType", 26 | @"SPEthernetDataType", 27 | @"SPExtensionsDataType", 28 | @"SPFirewallDataType", 29 | @"SPFireWireDataType", 30 | @"SPFibreChannelDataType", 31 | @"SPFontsDataType", 32 | @"SPFrameworksDataType", 33 | @"SPDisplaysDataType", 34 | @"SPHardwareDataType", 35 | @"SPHardwareRAIDDataType", 36 | @"SPInstallHistoryDataType", 37 | @"SPLogsDataType", 38 | @"SPManagedClientDataType", 39 | @"SPMemoryDataType", 40 | @"SPNetworkDataType", 41 | @"SPNetworkLocationDataType", 42 | @"SPNetworkVolumeDataType", 43 | @"SPNVMeDataType", 44 | @"SPParallelATADataType", 45 | @"SPParallelSCSIDataType", 46 | @"SPPCIDataType", 47 | @"SPPowerDataType", 48 | @"SPPrefPaneDataType", 49 | @"SPPrintersSoftwareDataType", 50 | @"SPPrintersDataType", 51 | @"SPSASDataType", 52 | @"SPSerialATADataType", 53 | @"SPSoftwareDataType", 54 | @"SPSPIDataType", 55 | @"SPStartupItemDataType", 56 | @"SPStorageDataType", 57 | @"SPSyncServicesDataType", 58 | @"SPThunderboltDataType", 59 | @"SPUniversalAccessDataType", 60 | @"SPUSBDataType", 61 | @"SPWWANDataType" 62 | }; 63 | 64 | static const DFSystemProfileDataType kDataTypes[] = 65 | { 66 | DFSystemProfileData_AirPort, 67 | DFSystemProfileData_Applications, 68 | DFSystemProfileData_Audio, 69 | DFSystemProfileData_Bluetooth, 70 | DFSystemProfileData_Camera, 71 | DFSystemProfileData_CardReader, 72 | DFSystemProfileData_Component, 73 | DFSystemProfileData_ConfigurationProfile, 74 | DFSystemProfileData_DeveloperTools, 75 | DFSystemProfileData_Diagnostics, 76 | DFSystemProfileData_DisabledSoftware, 77 | DFSystemProfileData_DiscBurning, 78 | DFSystemProfileData_EthernetCards, 79 | DFSystemProfileData_Extensions, 80 | DFSystemProfileData_Firewall, 81 | DFSystemProfileData_FireWire, 82 | DFSystemProfileData_FibreChannel, 83 | DFSystemProfileData_Fonts, 84 | DFSystemProfileData_Frameworks, 85 | DFSystemProfileData_Displays, 86 | DFSystemProfileData_Hardware, 87 | DFSystemProfileData_HardwareRAID, 88 | DFSystemProfileData_InstallHistory, 89 | DFSystemProfileData_Logs, 90 | DFSystemProfileData_ManagedClient, 91 | DFSystemProfileData_Memory, 92 | DFSystemProfileData_Network, 93 | DFSystemProfileData_NetworkLocations, 94 | DFSystemProfileData_NetworkVolume, 95 | DFSystemProfileData_NVMe, 96 | DFSystemProfileData_ParallelATA, 97 | DFSystemProfileData_ParallelSCSI, 98 | DFSystemProfileData_PCI, 99 | DFSystemProfileData_Power, 100 | DFSystemProfileData_PrefPane, 101 | DFSystemProfileData_PrinterSoftware, 102 | DFSystemProfileData_Printers, 103 | DFSystemProfileData_SAS, 104 | DFSystemProfileData_SerialATA, 105 | DFSystemProfileData_Software, 106 | DFSystemProfileData_SPI, 107 | DFSystemProfileData_StartupItem, 108 | DFSystemProfileData_Storage, 109 | DFSystemProfileData_SyncServices, 110 | DFSystemProfileData_Thunderbolt, 111 | DFSystemProfileData_UniversalAccess, 112 | DFSystemProfileData_USB, 113 | DFSystemProfileData_WWAN 114 | }; 115 | 116 | //------------------------------------------------------------------------------------------------- 117 | @implementation DFSystemProfileFetcher 118 | { 119 | NSTask* _scriptTask; 120 | NSPipe* _scriptPipe; 121 | NSString* _profile; 122 | BOOL _isDoneFetching; 123 | DFSystemProfileDataType _dataTypes; 124 | } 125 | 126 | //------------------------------------------------------------------------------------------------- 127 | + (void)initialize 128 | { 129 | NSAssert(sizeof(kDataTypeIDs) / sizeof(kDataTypeIDs[0]) == sizeof(kDataTypes) / sizeof(kDataTypes[0]), @"The number of data types doesn't match the number of data type ids."); 130 | } 131 | 132 | //------------------------------------------------------------------------------------------------- 133 | - (id)init 134 | { 135 | self = [super init]; 136 | if (self != nil) 137 | { 138 | } 139 | return self; 140 | } 141 | 142 | //------------------------------------------------------------------------------------------------- 143 | - (void)dealloc 144 | { 145 | [[NSNotificationCenter defaultCenter] removeObserver:self 146 | name:NSFileHandleReadToEndOfFileCompletionNotification 147 | object:_scriptPipe.fileHandleForReading]; 148 | [_scriptTask release]; 149 | [_scriptPipe release]; 150 | [_profile release]; 151 | [super dealloc]; 152 | } 153 | 154 | //------------------------------------------------------------------------------------------------- 155 | - (void)scriptPipeDidComplete:(NSNotification*)notification 156 | { 157 | if (notification != nil) 158 | { 159 | NSData* data = notification.userInfo[NSFileHandleNotificationDataItem]; 160 | if (data != nil) 161 | { 162 | [_profile release]; 163 | _profile = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 164 | } 165 | } 166 | _isDoneFetching = YES; 167 | 168 | [self.delegate systemProfileFetcherDidFinish:self]; 169 | } 170 | 171 | //------------------------------------------------------------------------------------------------- 172 | - (void)fetchDataTypes:(DFSystemProfileDataType)dataTypes 173 | { 174 | BOOL success = NO; 175 | _dataTypes = dataTypes; 176 | _isDoneFetching = NO; 177 | NSString* failureReason = nil; 178 | if (!self.class.canFetch) 179 | { 180 | failureReason = @"Cannot fetch system profile on OSX 10.7.x in sandboxed mode"; 181 | } 182 | else 183 | { 184 | _scriptPipe = [[NSPipe pipe] retain]; 185 | [[NSNotificationCenter defaultCenter] addObserver:self 186 | selector:@selector(scriptPipeDidComplete:) 187 | name:NSFileHandleReadToEndOfFileCompletionNotification 188 | object:_scriptPipe.fileHandleForReading]; 189 | 190 | _scriptTask = [[NSTask alloc] init]; 191 | _scriptTask.launchPath = @"/usr/sbin/system_profiler"; 192 | 193 | NSMutableArray* arguments = [NSMutableArray array]; 194 | if (dataTypes != DFSystemProfileData_All) 195 | { 196 | for (NSUInteger i = 0; i < sizeof(kDataTypeIDs) / sizeof(kDataTypeIDs[0]); ++i) 197 | { 198 | DFSystemProfileDataType currDataType = kDataTypes[i]; 199 | if ((currDataType & dataTypes) != 0) 200 | { 201 | [arguments addObject:kDataTypeIDs[i]]; 202 | } 203 | } 204 | } 205 | [arguments addObject:@"-detailLevel"]; 206 | [arguments addObject:@"mini"]; 207 | 208 | _scriptTask.arguments = arguments; 209 | _scriptTask.standardOutput = _scriptPipe; 210 | @try 211 | { 212 | [_scriptTask launch]; 213 | NSFileHandle* handle = _scriptPipe.fileHandleForReading; 214 | if (handle == nil) 215 | { 216 | failureReason = @"Invalid file handle"; 217 | } 218 | else 219 | { 220 | [handle readToEndOfFileInBackgroundAndNotifyForModes:@[NSDefaultRunLoopMode, 221 | NSModalPanelRunLoopMode]]; 222 | success = YES; 223 | } 224 | } 225 | @catch (NSException* exception) 226 | { 227 | failureReason = exception.reason; 228 | } 229 | } 230 | 231 | if (!success) 232 | { 233 | NSLog(@"Failed to fetch system profile: %@", failureReason); 234 | // emulate async completion 235 | [self scriptPipeDidComplete:nil]; 236 | } 237 | } 238 | 239 | //------------------------------------------------------------------------------------------------- 240 | - (void)cancel 241 | { 242 | [_scriptTask terminate]; 243 | } 244 | 245 | //------------------------------------------------------------------------------------------------- 246 | + (BOOL)canFetch 247 | { 248 | BOOL result = YES; 249 | // on 10.8, system profile seems to work somehow, even in sandbox, but not on 10.7 in sandbox 250 | if ([OSXVersion generation] < OSXGeneration_MountainLion) 251 | { 252 | if ([ApplicationSandboxInfo isSandboxed]) 253 | { 254 | // currently, this would require a temporary exception entitlement, don't rely on it 255 | // maybe later implement it using xpc then check the corresponding entitlement here 256 | result = NO; 257 | } 258 | } 259 | return result; 260 | } 261 | 262 | @end 263 | 264 | -------------------------------------------------------------------------------- /Google Toolbox/GTMStackTrace.m: -------------------------------------------------------------------------------- 1 | // 2 | // GTMStackTrace.m 3 | // 4 | // Copyright 2007-2008 Google Inc. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 7 | // use this file except in compliance with the License. You may obtain a copy 8 | // of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | // License for the specific language governing permissions and limitations under 16 | // the License. 17 | // 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "GTMStackTrace.h" 25 | 26 | struct GTMClassDescription { 27 | const char *class_name; 28 | Method *class_methods; 29 | unsigned int class_method_count; 30 | Method *instance_methods; 31 | unsigned int instance_method_count; 32 | }; 33 | 34 | #pragma mark Private utility functions 35 | 36 | static struct GTMClassDescription *GTMClassDescriptions(NSUInteger *total_count) { 37 | int class_count = objc_getClassList(nil, 0); 38 | struct GTMClassDescription *class_descs 39 | = calloc((NSUInteger)class_count, sizeof(struct GTMClassDescription)); 40 | if (class_descs) { 41 | Class *classes = calloc((NSUInteger)class_count, sizeof(Class)); 42 | if (classes) { 43 | objc_getClassList(classes, class_count); 44 | for (int i = 0; i < class_count; ++i) { 45 | class_descs[i].class_methods 46 | = class_copyMethodList(object_getClass(classes[i]), 47 | &class_descs[i].class_method_count); 48 | class_descs[i].instance_methods 49 | = class_copyMethodList(classes[i], 50 | &class_descs[i].instance_method_count); 51 | class_descs[i].class_name = class_getName(classes[i]); 52 | } 53 | free(classes); 54 | } else { 55 | // COV_NF_START - Don't know how to force this in a unittest 56 | free(class_descs); 57 | class_descs = NULL; 58 | class_count = 0; 59 | // COV_NF_END 60 | } 61 | } 62 | if (total_count) { 63 | *total_count = (NSUInteger)class_count; 64 | } 65 | return class_descs; 66 | } 67 | 68 | static void GTMFreeClassDescriptions(struct GTMClassDescription *class_descs, 69 | NSUInteger count) { 70 | if (!class_descs) return; 71 | for (NSUInteger i = 0; i < count; ++i) { 72 | if (class_descs[i].instance_methods) { 73 | free(class_descs[i].instance_methods); 74 | } 75 | if (class_descs[i].class_methods) { 76 | free(class_descs[i].class_methods); 77 | } 78 | } 79 | free(class_descs); 80 | } 81 | 82 | static NSUInteger GTMGetStackAddressDescriptorsForAddresses(void *pcs[], 83 | struct GTMAddressDescriptor outDescs[], 84 | NSUInteger count) { 85 | if (count < 1 || !pcs || !outDescs) return 0; 86 | 87 | NSUInteger class_desc_count; 88 | 89 | // Get our obj-c class descriptions. This is expensive, so we do it once 90 | // at the top. We go through this because dladdr doesn't work with 91 | // obj methods. 92 | struct GTMClassDescription *class_descs 93 | = GTMClassDescriptions(&class_desc_count); 94 | if (class_descs == NULL) { 95 | class_desc_count = 0; 96 | } 97 | 98 | // Iterate through the stack. 99 | for (NSUInteger i = 0; i < count; ++i) { 100 | const char *class_name = NULL; 101 | BOOL is_class_method = NO; 102 | size_t smallest_diff = SIZE_MAX; 103 | struct GTMAddressDescriptor *currDesc = &outDescs[i]; 104 | currDesc->address = pcs[i]; 105 | Method best_method = NULL; 106 | // Iterate through all the classes we know of. 107 | for (NSUInteger j = 0; j < class_desc_count; ++j) { 108 | // First check the class methods. 109 | for (NSUInteger k = 0; k < class_descs[j].class_method_count; ++k) { 110 | void *imp = (void *)method_getImplementation(class_descs[j].class_methods[k]); 111 | if (imp <= currDesc->address) { 112 | size_t diff = (size_t)currDesc->address - (size_t)imp; 113 | if (diff < smallest_diff) { 114 | best_method = class_descs[j].class_methods[k]; 115 | class_name = class_descs[j].class_name; 116 | is_class_method = YES; 117 | smallest_diff = diff; 118 | } 119 | } 120 | } 121 | // Then check the instance methods. 122 | for (NSUInteger k = 0; k < class_descs[j].instance_method_count; ++k) { 123 | void *imp = (void *)method_getImplementation(class_descs[j].instance_methods[k]); 124 | if (imp <= currDesc->address) { 125 | size_t diff = (size_t)currDesc->address - (size_t)imp; 126 | if (diff < smallest_diff) { 127 | best_method = class_descs[j].instance_methods[k]; 128 | class_name = class_descs[j].class_name; 129 | is_class_method = NO; 130 | smallest_diff = diff; 131 | } 132 | } 133 | } 134 | } 135 | 136 | // If we have one, store it off. 137 | if (best_method) { 138 | currDesc->symbol = sel_getName(method_getName(best_method)); 139 | currDesc->is_class_method = is_class_method; 140 | currDesc->class_name = class_name; 141 | } 142 | Dl_info info = { NULL, NULL, NULL, NULL }; 143 | 144 | // Check to see if the one returned by dladdr is better. 145 | dladdr(currDesc->address, &info); 146 | if ((size_t)currDesc->address - (size_t)info.dli_saddr < smallest_diff) { 147 | currDesc->symbol = info.dli_sname; 148 | currDesc->is_class_method = NO; 149 | currDesc->class_name = NULL; 150 | } 151 | currDesc->filename = info.dli_fname; 152 | if (!currDesc->symbol) { 153 | currDesc->symbol = "???"; 154 | currDesc->is_class_method = NO; 155 | currDesc->class_name = NULL; 156 | } 157 | } 158 | GTMFreeClassDescriptions(class_descs, class_desc_count); 159 | return count; 160 | } 161 | 162 | static NSString *GTMStackTraceFromAddressDescriptors(struct GTMAddressDescriptor descs[], 163 | NSUInteger count) { 164 | NSMutableString *trace = [NSMutableString string]; 165 | 166 | for (NSUInteger i = 0; i < count; i++) { 167 | // Newline between all the lines 168 | if (i) { 169 | [trace appendString:@"\n"]; 170 | } 171 | NSString *fileName = nil; 172 | if (descs[i].filename) { 173 | fileName = [NSString stringWithCString:descs[i].filename 174 | encoding:NSUTF8StringEncoding]; 175 | fileName = [fileName lastPathComponent]; 176 | } else { 177 | fileName = @"??"; 178 | } 179 | if (descs[i].class_name) { 180 | [trace appendFormat:@"#%-2lu %-35s %#0*lX %s[%s %s]", 181 | (unsigned long)i, 182 | [fileName UTF8String], 183 | // sizeof(void*) * 2 is the length of the hex address (32 vs 64) and + 2 184 | // for the 0x prefix 185 | (int)(sizeof(void *) * 2 + 2), 186 | (unsigned long)descs[i].address, 187 | (descs[i].is_class_method ? "+" : "-"), 188 | descs[i].class_name, 189 | (descs[i].symbol ? descs[i].symbol : "??")]; 190 | } else { 191 | [trace appendFormat:@"#%-2lu %-35s %#0*lX %s()", 192 | (unsigned long)i, 193 | [fileName UTF8String], 194 | // sizeof(void*) * 2 is the length of the hex address (32 vs 64) and + 2 195 | // for the 0x prefix 196 | (int)(sizeof(void *) * 2 + 2), 197 | (unsigned long)descs[i].address, 198 | (descs[i].symbol ? descs[i].symbol : "??")]; 199 | } 200 | } 201 | return trace; 202 | } 203 | 204 | #pragma mark Public functions 205 | 206 | NSUInteger GTMGetStackAddressDescriptors(struct GTMAddressDescriptor outDescs[], 207 | NSUInteger count) { 208 | if (count < 1 || !outDescs) return 0; 209 | NSUInteger result = 0; 210 | NSArray *addresses = [NSThread callStackReturnAddresses]; 211 | NSUInteger addrCount = [addresses count]; 212 | if (addrCount) { 213 | void **pcs = calloc(addrCount, sizeof(void*)); 214 | if (pcs) { 215 | void **pcsScanner = pcs; 216 | for (NSNumber *address in addresses) { 217 | NSUInteger addr = [address unsignedIntegerValue]; 218 | *pcsScanner = (void *)addr; 219 | ++pcsScanner; 220 | } 221 | if (count < addrCount) { 222 | addrCount = count; 223 | } 224 | // Fill in the desc structures 225 | result = GTMGetStackAddressDescriptorsForAddresses(pcs, outDescs, addrCount); 226 | } 227 | free(pcs); 228 | } 229 | 230 | return result; 231 | } 232 | 233 | NSString *GTMStackTrace(void) { 234 | // If we don't have enough frames, return an empty string 235 | NSString *result = @""; 236 | NSArray *addresses = [NSThread callStackReturnAddresses]; 237 | NSUInteger count = [addresses count]; 238 | if (count) { 239 | void **pcs = calloc(count, sizeof(void*)); 240 | struct GTMAddressDescriptor *descs 241 | = calloc(count, sizeof(struct GTMAddressDescriptor)); 242 | if (pcs && descs) { 243 | void **pcsScanner = pcs; 244 | for (NSNumber *address in addresses) { 245 | NSUInteger addr = [address unsignedIntegerValue]; 246 | *pcsScanner = (void *)addr; 247 | ++pcsScanner; 248 | } 249 | // Fill in the desc structures 250 | count = GTMGetStackAddressDescriptorsForAddresses(pcs, descs, count); 251 | // Build the trace 252 | // We skip 1 frame because the +[NSThread callStackReturnAddresses] will 253 | // start w/ this frame. 254 | const size_t kTracesToStrip = 1; 255 | if (count > kTracesToStrip) { 256 | result = GTMStackTraceFromAddressDescriptors(&descs[kTracesToStrip], 257 | (count - kTracesToStrip)); 258 | } 259 | } 260 | free(pcs); 261 | free(descs); 262 | } 263 | 264 | return result; 265 | } 266 | 267 | 268 | NSString *GTMStackTraceFromException(NSException *e) { 269 | NSString *trace = @""; 270 | 271 | // collect the addresses 272 | NSArray *addresses = [e callStackReturnAddresses]; 273 | NSUInteger count = [addresses count]; 274 | if (count) { 275 | void **pcs = calloc(count, sizeof(void*)); 276 | struct GTMAddressDescriptor *descs 277 | = calloc(count, sizeof(struct GTMAddressDescriptor)); 278 | if (pcs && descs) { 279 | void **pcsScanner = pcs; 280 | for (NSNumber *address in addresses) { 281 | NSUInteger addr = [address unsignedIntegerValue]; 282 | *pcsScanner = (void *)addr; 283 | ++pcsScanner; 284 | } 285 | // Fill in the desc structures 286 | count = GTMGetStackAddressDescriptorsForAddresses(pcs, descs, count); 287 | // Build the trace 288 | trace = GTMStackTraceFromAddressDescriptors(descs, count); 289 | } 290 | free(pcs); 291 | free(descs); 292 | } 293 | 294 | return trace; 295 | } 296 | 297 | -------------------------------------------------------------------------------- /Views/DFLinkLabel.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import "DFLinkLabel.h" 7 | #import "DFLinkLabelDelegate.h" 8 | #import "DFStyleSheet.h" 9 | #import "LiteralHelpers.h" 10 | 11 | //------------------------------------------------------------------------------------------------- 12 | @implementation DFLinkLabel 13 | { 14 | NSFont* _font; 15 | NSColor* _textColor; 16 | NSTextStorage* _text; 17 | NSTextContainer* _textContainer; 18 | NSLayoutManager* _textLayoutManager; 19 | NSTrackingArea* _trackingArea; 20 | NSMutableArray* _linkRanges; 21 | NSMutableArray* _linkRectArrays; 22 | BOOL _isCursorSet; 23 | } 24 | 25 | //------------------------------------------------------------------------------------------------- 26 | - (id)initWithFrame:(NSRect)frameRect 27 | { 28 | self = [super initWithFrame:frameRect]; 29 | if (self != nil) 30 | { 31 | _text = [[NSTextStorage alloc] init]; 32 | _textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(frameRect.size.width, FLT_MAX)]; 33 | _textLayoutManager = [[NSLayoutManager alloc] init]; 34 | [_textLayoutManager addTextContainer:_textContainer]; 35 | [_text addLayoutManager:_textLayoutManager]; 36 | _textContainer.lineFragmentPadding = 0.0; 37 | // this is necessary to match layout of the standard text field 38 | _textLayoutManager.typesetterBehavior = NSTypesetterBehavior_10_2_WithCompatibility; 39 | _linkRanges = [[NSMutableArray alloc] init]; 40 | _linkRectArrays = [[NSMutableArray alloc] init]; 41 | } 42 | return self; 43 | } 44 | 45 | //------------------------------------------------------------------------------------------------- 46 | - (id)initWithTextField:(NSTextField*)textField 47 | { 48 | self = [self initWithFrame:textField.frame]; 49 | if (self != nil) 50 | { 51 | // save font and color 52 | _font = [textField.font retain]; 53 | _textColor = [textField.textColor retain]; 54 | // initialize value 55 | [self setStringValue:textField.stringValue]; 56 | } 57 | return self; 58 | } 59 | 60 | //------------------------------------------------------------------------------------------------- 61 | - (void)dealloc 62 | { 63 | [_text release]; 64 | [_textContainer release]; 65 | [_textLayoutManager release]; 66 | [_linkRanges release]; 67 | [_linkRectArrays release]; 68 | [_font release]; 69 | [_textColor release]; 70 | [_trackingArea release]; 71 | [_stringValue release]; 72 | [super dealloc]; 73 | } 74 | 75 | //------------------------------------------------------------------------------------------------- 76 | - (BOOL)isFlipped 77 | { 78 | return YES; 79 | } 80 | 81 | //------------------------------------------------------------------------------------------------- 82 | - (void)appendTextWithPart:(NSString*)newPart 83 | attributes:(NSDictionary*)attrs 84 | { 85 | NSAttributedString* attributedNewPart = [[[NSAttributedString alloc] initWithString:newPart attributes:attrs] autorelease]; 86 | [_text appendAttributedString:attributedNewPart]; 87 | } 88 | 89 | 90 | //------------------------------------------------------------------------------------------------- 91 | - (void)setStringValue:(NSString*)value 92 | { 93 | // reset 94 | _text.attributedString = [[[NSAttributedString alloc] init] autorelease]; 95 | [_linkRanges removeAllObjects]; 96 | [_linkRectArrays removeAllObjects]; 97 | 98 | if (value != nil) 99 | { 100 | NSDictionary* normalTextAttrs = NSDictionaryWithKeysAndValues(NSFontAttributeName, (_font != nil ? _font : DFLinkLabel_font), 101 | NSForegroundColorAttributeName, (_textColor != nil ? _textColor : DFLinkLabel_normalColor), 102 | nil); 103 | 104 | NSDictionary* linkTextAttrs = NSDictionaryWithKeysAndValues(NSFontAttributeName, (_font != nil ? _font : DFLinkLabel_font), 105 | NSForegroundColorAttributeName, DFLinkLabel_linkColor, 106 | NSUnderlineStyleAttributeName, (DFLinkLabel_linkUnderlined ? @(NSUnderlinePatternSolid | NSUnderlineStyleSingle) : @(NSUnderlineStyleNone)), 107 | nil); 108 | 109 | // split into attributed ranges 110 | NSUInteger openBracketLocation = NSNotFound; 111 | NSUInteger searchLocation = 0; 112 | NSUInteger valueLength = value.length; 113 | while (searchLocation < valueLength) 114 | { 115 | openBracketLocation = [value rangeOfString:@"[" 116 | options:(NSStringCompareOptions)0 117 | range:NSMakeRange(searchLocation, valueLength - searchLocation)].location; 118 | 119 | // string before open bracket 120 | NSString* newPiece = openBracketLocation == NSNotFound ? 121 | [value substringFromIndex:searchLocation] : 122 | [value substringWithRange:NSMakeRange(searchLocation, openBracketLocation - searchLocation)]; 123 | [self appendTextWithPart:newPiece attributes:normalTextAttrs]; 124 | 125 | if (openBracketLocation == NSNotFound) 126 | { 127 | break; 128 | } 129 | else 130 | { 131 | // link range 132 | searchLocation = openBracketLocation + 1; 133 | NSUInteger closeBracketLocation = [value rangeOfString:@"]" 134 | options:(NSStringCompareOptions)0 135 | range:NSMakeRange(searchLocation, valueLength - searchLocation)].location; 136 | NSAssert(closeBracketLocation != NSNotFound, @"Link label error: open bracket unmatched with close bracket"); 137 | 138 | NSString* linkPiece = [value substringWithRange:NSMakeRange(openBracketLocation + 1, closeBracketLocation - openBracketLocation - 1)]; 139 | [_linkRanges addObject:[NSValue valueWithRange:NSMakeRange(_text.length, linkPiece.length)]]; 140 | [self appendTextWithPart:linkPiece attributes:linkTextAttrs]; 141 | 142 | 143 | searchLocation = closeBracketLocation + 1; 144 | } 145 | } 146 | } 147 | // redraw 148 | self.needsDisplay = YES; 149 | } 150 | 151 | //------------------------------------------------------------------------------------------------- 152 | - (void)drawRect:(NSRect)dirtyRect 153 | { 154 | NSRange glyphRange = [_textLayoutManager glyphRangeForTextContainer:_textContainer]; 155 | [_textLayoutManager drawGlyphsForGlyphRange:glyphRange 156 | atPoint:NSMakePoint(self.bounds.origin.x, self.bounds.origin.y)]; 157 | } 158 | 159 | //------------------------------------------------------------------------------------------------- 160 | - (void)setFrameSize:(NSSize)value 161 | { 162 | super.frameSize = value; 163 | // update container size 164 | _textContainer.containerSize = NSMakeSize(self.bounds.size.width, FLT_MAX); 165 | self.needsDisplay = YES; 166 | } 167 | 168 | //------------------------------------------------------------------------------------------------- 169 | - (NSPoint)locationFromEvent:(NSEvent*)event 170 | { 171 | NSPoint windowPoint = event.locationInWindow; 172 | NSPoint point = [self convertPoint:windowPoint fromView:nil]; 173 | return point; 174 | } 175 | 176 | //------------------------------------------------------------------------------------------------- 177 | - (void)mouseUp:(NSEvent*)event 178 | { 179 | // respond to link click 180 | NSPoint location = [self locationFromEvent:event]; 181 | NSUInteger linkIndex = [self hitTestLinkAtPoint:location]; 182 | if (linkIndex != NSNotFound) 183 | { 184 | [self.delegate linkLabel:self didClickLinkNo:linkIndex]; 185 | } 186 | } 187 | 188 | //------------------------------------------------------------------------------------------------- 189 | - (void)updateTrackingAreas 190 | { 191 | if (_trackingArea != nil) 192 | { 193 | [self removeTrackingArea:_trackingArea]; 194 | [_trackingArea release]; 195 | } 196 | _trackingArea = [[NSTrackingArea alloc] 197 | initWithRect:self.bounds 198 | options:(NSTrackingAreaOptions)(NSTrackingActiveInKeyWindow | NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited) 199 | owner:self 200 | userInfo:nil]; 201 | [self addTrackingArea:_trackingArea]; 202 | } 203 | 204 | //------------------------------------------------------------------------------------------------- 205 | - (void)resetCursorRects 206 | { 207 | // rebuild cursor links 208 | for (NSUInteger i = 0; i < _linkRanges.count; ++i) 209 | { 210 | // get character range of the link 211 | NSRange linkRange = ((NSValue*)_linkRanges[i]).rangeValue; 212 | NSUInteger rectCount = 0; 213 | // measure set of bounding rects for the specified character range 214 | NSRectArray rects = [_textLayoutManager rectArrayForCharacterRange:linkRange 215 | withinSelectedCharacterRange:NSMakeRange(NSNotFound, 0) 216 | inTextContainer:_textContainer 217 | rectCount:&rectCount]; 218 | // save the rects for hit testing and create cursor rects 219 | NSMutableArray* linkRects = [NSMutableArray arrayWithCapacity:rectCount]; 220 | for (NSUInteger j = 0; j < rectCount; ++j) 221 | { 222 | NSRect rect = rects[j]; 223 | [self addCursorRect:rect cursor:[NSCursor pointingHandCursor]]; 224 | [linkRects addObject:[NSValue valueWithRect:rect]]; 225 | } 226 | [_linkRectArrays addObject:linkRects]; 227 | } 228 | } 229 | 230 | //------------------------------------------------------------------------------------------------- 231 | - (NSUInteger)hitTestLinkAtPoint:(NSPoint)point 232 | { 233 | // cycle of each link's rect array 234 | for (NSUInteger i = 0; i < _linkRectArrays.count; ++i) 235 | { 236 | NSArray* linkRects = _linkRectArrays[i]; 237 | // cycle of rects within single link 238 | for (NSUInteger j = 0; j < linkRects.count; ++j) 239 | { 240 | NSRect linkRect = ((NSValue*)linkRects[j]).rectValue; 241 | // hit test 242 | if (NSPointInRect(point, linkRect)) 243 | { 244 | // link found 245 | return i; 246 | } 247 | } 248 | } 249 | // link not found 250 | return NSNotFound; 251 | } 252 | 253 | 254 | @end 255 | -------------------------------------------------------------------------------- /Google Toolbox/GTMDefines.h: -------------------------------------------------------------------------------- 1 | // 2 | // GTMDefines.h 3 | // 4 | // Copyright 2008 Google Inc. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 7 | // use this file except in compliance with the License. You may obtain a copy 8 | // of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | // License for the specific language governing permissions and limitations under 16 | // the License. 17 | // 18 | 19 | // ============================================================================ 20 | 21 | #include 22 | #include 23 | 24 | #ifdef __OBJC__ 25 | #include 26 | #endif // __OBJC__ 27 | 28 | #if TARGET_OS_IPHONE 29 | #include 30 | #endif // TARGET_OS_IPHONE 31 | 32 | // ---------------------------------------------------------------------------- 33 | // CPP symbols that can be overridden in a prefix to control how the toolbox 34 | // is compiled. 35 | // ---------------------------------------------------------------------------- 36 | 37 | 38 | // By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and 39 | // GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens 40 | // when a validation fails. If you implement your own validators, you may want 41 | // to control their internals using the same macros for consistency. 42 | #ifndef GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 43 | #define GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 0 44 | #endif 45 | 46 | // Ensure __has_feature and __has_extension are safe to use. 47 | // See http://clang-analyzer.llvm.org/annotations.html 48 | #ifndef __has_feature // Optional. 49 | #define __has_feature(x) 0 // Compatibility with non-clang compilers. 50 | #endif 51 | 52 | #ifndef __has_extension 53 | #define __has_extension __has_feature // Compatibility with pre-3.0 compilers. 54 | #endif 55 | 56 | // Give ourselves a consistent way to do inlines. Apple's macros even use 57 | // a few different actual definitions, so we're based off of the foundation 58 | // one. 59 | #if !defined(GTM_INLINE) 60 | #if (defined (__GNUC__) && (__GNUC__ == 4)) || defined (__clang__) 61 | #define GTM_INLINE static __inline__ __attribute__((always_inline)) 62 | #else 63 | #define GTM_INLINE static __inline__ 64 | #endif 65 | #endif 66 | 67 | // Give ourselves a consistent way of doing externs that links up nicely 68 | // when mixing objc and objc++ 69 | #if !defined (GTM_EXTERN) 70 | #if defined __cplusplus 71 | #define GTM_EXTERN extern "C" 72 | #define GTM_EXTERN_C_BEGIN extern "C" { 73 | #define GTM_EXTERN_C_END } 74 | #else 75 | #define GTM_EXTERN extern 76 | #define GTM_EXTERN_C_BEGIN 77 | #define GTM_EXTERN_C_END 78 | #endif 79 | #endif 80 | 81 | // Give ourselves a consistent way of exporting things if we have visibility 82 | // set to hidden. 83 | #if !defined (GTM_EXPORT) 84 | #define GTM_EXPORT __attribute__((visibility("default"))) 85 | #endif 86 | 87 | // Give ourselves a consistent way of declaring something as unused. This 88 | // doesn't use __unused because that is only supported in gcc 4.2 and greater. 89 | #if !defined (GTM_UNUSED) 90 | #define GTM_UNUSED(x) ((void)(x)) 91 | #endif 92 | 93 | // _GTMDevLog & _GTMDevAssert 94 | // 95 | // _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for 96 | // developer level errors. This implementation simply macros to NSLog/NSAssert. 97 | // It is not intended to be a general logging/reporting system. 98 | // 99 | // Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert 100 | // for a little more background on the usage of these macros. 101 | // 102 | // _GTMDevLog log some error/problem in debug builds 103 | // _GTMDevAssert assert if condition isn't met w/in a method/function 104 | // in all builds. 105 | // 106 | // To replace this system, just provide different macro definitions in your 107 | // prefix header. Remember, any implementation you provide *must* be thread 108 | // safe since this could be called by anything in what ever situation it has 109 | // been placed in. 110 | // 111 | 112 | // Ignore the "Macro name is a reserved identifier" warning in this section 113 | #pragma clang diagnostic push 114 | #pragma clang diagnostic ignored "-Wreserved-id-macro" 115 | 116 | // We only define the simple macros if nothing else has defined this. 117 | #ifndef _GTMDevLog 118 | 119 | #ifdef DEBUG 120 | #define _GTMDevLog(...) NSLog(__VA_ARGS__) 121 | #else 122 | #define _GTMDevLog(...) do { } while (0) 123 | #endif 124 | 125 | #endif // _GTMDevLog 126 | 127 | #ifndef _GTMDevAssert 128 | // we directly invoke the NSAssert handler so we can pass on the varargs 129 | // (NSAssert doesn't have a macro we can use that takes varargs) 130 | #if !defined(NS_BLOCK_ASSERTIONS) 131 | #define _GTMDevAssert(condition, ...) \ 132 | do { \ 133 | if (__builtin_expect(!(condition), 0)) { \ 134 | NSString *__assert_func_name__ = \ 135 | [NSString stringWithUTF8String:__PRETTY_FUNCTION__]; \ 136 | __assert_func_name__ = __assert_func_name__ ?: @""; \ 137 | NSString *__assert_file_name__ = \ 138 | [NSString stringWithUTF8String:__FILE__]; \ 139 | __assert_file_name__ = __assert_file_name__ ?: @""; \ 140 | [[NSAssertionHandler currentHandler] \ 141 | handleFailureInFunction:__assert_func_name__ \ 142 | file:__assert_file_name__ \ 143 | lineNumber:__LINE__ \ 144 | description:__VA_ARGS__]; \ 145 | } \ 146 | } while(0) 147 | #else // !defined(NS_BLOCK_ASSERTIONS) 148 | #define _GTMDevAssert(condition, ...) do { } while (0) 149 | #endif // !defined(NS_BLOCK_ASSERTIONS) 150 | 151 | #endif // _GTMDevAssert 152 | 153 | // _GTMCompileAssert 154 | // 155 | // Note: Software for current compilers should just use _Static_assert directly 156 | // instead of this macro. 157 | // 158 | // _GTMCompileAssert is an assert that is meant to fire at compile time if you 159 | // want to check things at compile instead of runtime. For example if you 160 | // want to check that a wchar is 4 bytes instead of 2 you would use 161 | // _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X) 162 | // Note that the second "arg" is not in quotes, and must be a valid processor 163 | // symbol in it's own right (no spaces, punctuation etc). 164 | 165 | // Wrapping this in an #ifndef allows external groups to define their own 166 | // compile time assert scheme. 167 | #ifndef _GTMCompileAssert 168 | #if __has_feature(c_static_assert) || __has_extension(c_static_assert) 169 | #define _GTMCompileAssert(test, msg) _Static_assert((test), #msg) 170 | #else 171 | // Pre-Xcode 7 support. 172 | // 173 | // We got this technique from here: 174 | // http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html 175 | #define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg 176 | #define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg) 177 | #define _GTMCompileAssert(test, msg) \ 178 | typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ] 179 | #endif // __has_feature(c_static_assert) || __has_extension(c_static_assert) 180 | #endif // _GTMCompileAssert 181 | 182 | #pragma clang diagnostic pop 183 | 184 | // ---------------------------------------------------------------------------- 185 | // CPP symbols defined based on the project settings so the GTM code has 186 | // simple things to test against w/o scattering the knowledge of project 187 | // setting through all the code. 188 | // ---------------------------------------------------------------------------- 189 | 190 | // Provide a single constant CPP symbol that all of GTM uses for ifdefing 191 | // iPhone code. 192 | #if TARGET_OS_IPHONE // iPhone SDK 193 | // For iPhone specific stuff 194 | #define GTM_IPHONE_SDK 1 195 | #if TARGET_IPHONE_SIMULATOR 196 | #define GTM_IPHONE_DEVICE 0 197 | #define GTM_IPHONE_SIMULATOR 1 198 | #else 199 | #define GTM_IPHONE_DEVICE 1 200 | #define GTM_IPHONE_SIMULATOR 0 201 | #endif // TARGET_IPHONE_SIMULATOR 202 | // By default, GTM has provided it's own unittesting support, define this 203 | // to use the support provided by Xcode, especially for the Xcode4 support 204 | // for unittesting. 205 | #ifndef GTM_USING_XCTEST 206 | #define GTM_USING_XCTEST 0 207 | #endif 208 | #define GTM_MACOS_SDK 0 209 | #else 210 | // For MacOS specific stuff 211 | #define GTM_MACOS_SDK 1 212 | #define GTM_IPHONE_SDK 0 213 | #define GTM_IPHONE_SIMULATOR 0 214 | #define GTM_IPHONE_DEVICE 0 215 | #ifndef GTM_USING_XCTEST 216 | #define GTM_USING_XCTEST 0 217 | #endif 218 | #endif 219 | 220 | // Some of our own availability macros 221 | #if GTM_MACOS_SDK 222 | #define GTM_AVAILABLE_ONLY_ON_IPHONE UNAVAILABLE_ATTRIBUTE 223 | #define GTM_AVAILABLE_ONLY_ON_MACOS 224 | #else 225 | #define GTM_AVAILABLE_ONLY_ON_IPHONE 226 | #define GTM_AVAILABLE_ONLY_ON_MACOS UNAVAILABLE_ATTRIBUTE 227 | #endif 228 | 229 | // GC was dropped by Apple, define the old constant incase anyone still keys 230 | // off of it. 231 | #ifndef GTM_SUPPORT_GC 232 | #define GTM_SUPPORT_GC 0 233 | #endif 234 | 235 | // Some support for advanced clang static analysis functionality 236 | #ifndef NS_RETURNS_RETAINED 237 | #if __has_feature(attribute_ns_returns_retained) 238 | #define NS_RETURNS_RETAINED __attribute__((ns_returns_retained)) 239 | #else 240 | #define NS_RETURNS_RETAINED 241 | #endif 242 | #endif 243 | 244 | #ifndef NS_RETURNS_NOT_RETAINED 245 | #if __has_feature(attribute_ns_returns_not_retained) 246 | #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) 247 | #else 248 | #define NS_RETURNS_NOT_RETAINED 249 | #endif 250 | #endif 251 | 252 | #ifndef CF_RETURNS_RETAINED 253 | #if __has_feature(attribute_cf_returns_retained) 254 | #define CF_RETURNS_RETAINED __attribute__((cf_returns_retained)) 255 | #else 256 | #define CF_RETURNS_RETAINED 257 | #endif 258 | #endif 259 | 260 | #ifndef CF_RETURNS_NOT_RETAINED 261 | #if __has_feature(attribute_cf_returns_not_retained) 262 | #define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained)) 263 | #else 264 | #define CF_RETURNS_NOT_RETAINED 265 | #endif 266 | #endif 267 | 268 | #ifndef NS_CONSUMED 269 | #if __has_feature(attribute_ns_consumed) 270 | #define NS_CONSUMED __attribute__((ns_consumed)) 271 | #else 272 | #define NS_CONSUMED 273 | #endif 274 | #endif 275 | 276 | #ifndef CF_CONSUMED 277 | #if __has_feature(attribute_cf_consumed) 278 | #define CF_CONSUMED __attribute__((cf_consumed)) 279 | #else 280 | #define CF_CONSUMED 281 | #endif 282 | #endif 283 | 284 | #ifndef NS_CONSUMES_SELF 285 | #if __has_feature(attribute_ns_consumes_self) 286 | #define NS_CONSUMES_SELF __attribute__((ns_consumes_self)) 287 | #else 288 | #define NS_CONSUMES_SELF 289 | #endif 290 | #endif 291 | 292 | #ifndef GTM_NONNULL 293 | #if defined(__has_attribute) 294 | #if __has_attribute(nonnull) 295 | #define GTM_NONNULL(x) __attribute__((nonnull x)) 296 | #else 297 | #define GTM_NONNULL(x) 298 | #endif 299 | #else 300 | #define GTM_NONNULL(x) 301 | #endif 302 | #endif 303 | 304 | // Invalidates the initializer from which it's called. 305 | #ifndef GTMInvalidateInitializer 306 | #if __has_feature(objc_arc) 307 | #define GTMInvalidateInitializer() \ 308 | do { \ 309 | [self class]; /* Avoid warning of dead store to |self|. */ \ 310 | _GTMDevAssert(NO, @"Invalid initializer."); \ 311 | return nil; \ 312 | } while (0) 313 | #else 314 | #define GTMInvalidateInitializer() \ 315 | do { \ 316 | [self release]; \ 317 | _GTMDevAssert(NO, @"Invalid initializer."); \ 318 | return nil; \ 319 | } while (0) 320 | #endif 321 | #endif 322 | 323 | #ifndef GTMCFAutorelease 324 | // GTMCFAutorelease returns an id. In contrast, Apple's CFAutorelease returns 325 | // a CFTypeRef. 326 | #if __has_feature(objc_arc) 327 | #define GTMCFAutorelease(x) CFBridgingRelease(x) 328 | #else 329 | #define GTMCFAutorelease(x) ([(id)x autorelease]) 330 | #endif 331 | #endif 332 | 333 | #ifdef __OBJC__ 334 | 335 | 336 | // Macro to allow you to create NSStrings out of other macros. 337 | // #define FOO foo 338 | // NSString *fooString = GTM_NSSTRINGIFY(FOO); 339 | #if !defined (GTM_NSSTRINGIFY) 340 | #define GTM_NSSTRINGIFY_INNER(x) @#x 341 | #define GTM_NSSTRINGIFY(x) GTM_NSSTRINGIFY_INNER(x) 342 | #endif 343 | 344 | // ============================================================================ 345 | 346 | // GTM_SEL_STRING is for specifying selector (usually property) names to KVC 347 | // or KVO methods. 348 | // In debug it will generate warnings for undeclared selectors if 349 | // -Wunknown-selector is turned on. 350 | // In release it will have no runtime overhead. 351 | #ifndef GTM_SEL_STRING 352 | #ifdef DEBUG 353 | #define GTM_SEL_STRING(selName) NSStringFromSelector(@selector(selName)) 354 | #else 355 | #define GTM_SEL_STRING(selName) @#selName 356 | #endif // DEBUG 357 | #endif // GTM_SEL_STRING 358 | 359 | #ifndef GTM_WEAK 360 | #if __has_feature(objc_arc_weak) 361 | // With ARC enabled, __weak means a reference that isn't implicitly 362 | // retained. __weak objects are accessed through runtime functions, so 363 | // they are zeroed out, but this requires OS X 10.7+. 364 | // At clang r251041+, ARC-style zeroing weak references even work in 365 | // non-ARC mode. 366 | #define GTM_WEAK __weak 367 | #elif __has_feature(objc_arc) 368 | // ARC, but targeting 10.6 or older, where zeroing weak references don't 369 | // exist. 370 | #define GTM_WEAK __unsafe_unretained 371 | #else 372 | // With manual reference counting, __weak used to be silently ignored. 373 | // clang r251041 gives it the ARC semantics instead. This means they 374 | // now require a deployment target of 10.7, while some clients of GTM 375 | // still target 10.6. In these cases, expand to __unsafe_unretained instead 376 | #define GTM_WEAK 377 | #endif 378 | #endif 379 | 380 | #endif // __OBJC__ 381 | -------------------------------------------------------------------------------- /DFCrashReportWindowController.m: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------- 2 | // Copyright (c) 2008 DaisyDisk Team: 3 | // Some rights reserved: 4 | //------------------------------------------------------------------------------------------------- 5 | 6 | #import "DFCrashReportWindowController.h" 7 | #import "DFSystemProfileFetcher.h" 8 | #import "DFFeedbackSenderDelegate.h" 9 | #import "DFFeedbackSender.h" 10 | #import "DFPlaceholderTextView.h" 11 | #import "DFLinkLabel.h" 12 | #import "DFStyleSheet.h" 13 | #import "DFApplication.h" 14 | #import "StringAnonymizer.h" 15 | #import "LiteralHelpers.h" 16 | 17 | //------------------------------------------------------------------------------------------------- 18 | static NSString* const kNibName = @"DFCrashReportWindow"; 19 | 20 | //------------------------------------------------------------------------------------------------- 21 | static DFCrashReportWindowController* _singleton = nil; 22 | static NSImage* _icon = nil; 23 | static NSString* _feedbackUrl = nil; 24 | static NSString* _updateUrl = nil; 25 | static DFSystemProfileDataType _systemProfileDataTypes = DFSystemProfileData_All; 26 | 27 | //------------------------------------------------------------------------------------------------- 28 | @interface DFCrashReportWindowController() 29 | // icon 30 | @property (nonatomic, assign) IBOutlet NSImageView* iconImageView; 31 | 32 | // comments controls 33 | @property (nonatomic, assign) IBOutlet NSView* commentsBoxView; 34 | @property (nonatomic, assign) IBOutlet NSButton* commentsDisclosureButton; 35 | @property (nonatomic, assign) IBOutlet NSScrollView* commentsScrollView; 36 | @property (nonatomic, assign) IBOutlet DFPlaceholderTextView* commentsTextView; 37 | 38 | // details controls 39 | @property (nonatomic, assign) IBOutlet NSView* detailsBoxView; 40 | @property (nonatomic, assign) IBOutlet NSButton* detailsDisclosureButton; 41 | @property (nonatomic, assign) IBOutlet NSScrollView* detailsScrollView; 42 | @property (nonatomic, assign) IBOutlet NSTextView* detailsTextView; 43 | 44 | // update link label 45 | @property (nonatomic, assign) IBOutlet NSTextField* updateLabel; 46 | 47 | // progress controls 48 | @property (nonatomic, assign) IBOutlet NSProgressIndicator* progressIndicator; 49 | @property (nonatomic, assign) IBOutlet NSTextField* fetchingSystemProfileProgressLabel; 50 | @property (nonatomic, assign) IBOutlet NSTextField* sendingReportProgressLabel; 51 | @property (nonatomic, assign) IBOutlet NSTextField* anonymousLabel; 52 | 53 | // footer controls 54 | @property (nonatomic, assign) IBOutlet NSButton* sendButton; 55 | 56 | // exception data 57 | @property (nonatomic, retain) NSString* exceptionMessage; 58 | @property (nonatomic, retain) NSString* exceptionStackTrace; 59 | @property (nonatomic, retain) NSString* systemProfile; 60 | 61 | @end 62 | 63 | //------------------------------------------------------------------------------------------------- 64 | @implementation DFCrashReportWindowController 65 | { 66 | // runtime controls 67 | DFLinkLabel* _updateLinkLabel; 68 | 69 | // workers 70 | DFSystemProfileFetcher* _systemProfileFetcher; 71 | DFFeedbackSender* _feedbackSender; 72 | 73 | // stored info and flags 74 | BOOL _sendButtonWasClicked; 75 | BOOL _cancelButtonWasClicked; 76 | 77 | // animations 78 | NSViewAnimation* _windowAnimation; 79 | 80 | // saved sizes 81 | CGFloat _detailsTextViewDefaultHeight; 82 | CGFloat _commentsTextViewDefaultHeight; 83 | } 84 | 85 | //------------------------------------------------------------------------------------------------- 86 | + (void)initializeWithFeedbackUrl:(NSString*)feedbackUrl 87 | updateUrl:(NSString*)updateUrl 88 | icon:(NSImage*)icon 89 | systemProfileDataTypes:(DFSystemProfileDataType)systemProfileDataTypes; 90 | { 91 | [icon retain]; 92 | [_icon release]; 93 | _icon = icon; 94 | 95 | [feedbackUrl retain]; 96 | [_feedbackUrl release]; 97 | _feedbackUrl = feedbackUrl; 98 | 99 | [updateUrl retain]; 100 | [_updateUrl release]; 101 | _updateUrl = updateUrl; 102 | 103 | _systemProfileDataTypes = systemProfileDataTypes; 104 | } 105 | 106 | //------------------------------------------------------------------------------------------------- 107 | - (id)init 108 | { 109 | self = [super initWithWindowNibName:kNibName]; 110 | if (self != nil) 111 | { 112 | _systemProfileFetcher = [[DFSystemProfileFetcher alloc] init]; 113 | _systemProfileFetcher.delegate = self; 114 | } 115 | return self; 116 | } 117 | 118 | //------------------------------------------------------------------------------------------------- 119 | - (void)dealloc 120 | { 121 | [self cancelPendingStuff]; 122 | [_exceptionMessage release]; 123 | [_exceptionStackTrace release]; 124 | [_systemProfile release]; 125 | [_updateLinkLabel release]; 126 | _windowAnimation.delegate = nil; 127 | [_windowAnimation release]; 128 | [super dealloc]; 129 | } 130 | 131 | //------------------------------------------------------------------------------------------------- 132 | + (DFCrashReportWindowController*)singleton 133 | { 134 | if (_singleton == nil) 135 | { 136 | InitializeDFStyles(); 137 | 138 | _singleton = [[DFCrashReportWindowController alloc] init]; 139 | } 140 | return _singleton; 141 | } 142 | 143 | 144 | //------------------------------------------------------------------------------------------------- 145 | - (void)initializeControls 146 | { 147 | _sendButtonWasClicked = NO; 148 | _cancelButtonWasClicked = NO; 149 | 150 | _sendButton.enabled = YES; 151 | 152 | NSString* details = [NSString stringWithFormat:@"MESSAGE:\n%@\n\nSTACK TRACE:\n%@", _exceptionMessage, _exceptionStackTrace]; 153 | _detailsTextView.textStorage.attributedString = [[[NSAttributedString alloc] initWithString:details 154 | attributes:@{NSForegroundColorAttributeName : NSColor.controlTextColor}] autorelease]; 155 | 156 | _detailsDisclosureButton.state = NSOffState; 157 | 158 | [self expandOrCollapseBox:_detailsBoxView 159 | textView:_detailsScrollView 160 | textViewDefaultHeight:_detailsTextViewDefaultHeight 161 | alternateBox:_commentsBoxView 162 | shouldCollapseOrExpand:NO 163 | isLowerOrUpper:NO 164 | withAnimation:NO]; 165 | 166 | [self beginFetchingSystemProfile]; 167 | } 168 | 169 | //------------------------------------------------------------------------------------------------- 170 | - (void)awakeFromNib 171 | { 172 | [self.window setContentBorderThickness:DFCrashReportWindow_bottomBarHeight forEdge: NSMinYEdge]; 173 | 174 | NSString* windowTitle = self.window.title; 175 | windowTitle = [NSString stringWithFormat:windowTitle, [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]]; 176 | self.window.title = windowTitle; 177 | 178 | _iconImageView.image = _icon; 179 | 180 | _commentsTextView.placeholderText = NSLocalizedStringFromTable(@"DFeedback_PlaceholderText", @"DFLocalizable", nil); 181 | 182 | // replace update label with link label 183 | if (_updateLabel != nil) 184 | { 185 | if (_updateUrl != nil) 186 | { 187 | _updateLinkLabel = [[DFLinkLabel alloc] initWithTextField:_updateLabel]; 188 | _updateLinkLabel.delegate = self; 189 | [_updateLabel.superview replaceSubview:_updateLabel with:_updateLinkLabel]; 190 | } 191 | else 192 | { 193 | // no update url, simply remove 194 | [_updateLabel removeFromSuperview]; 195 | } 196 | } 197 | 198 | // save some sizes 199 | _detailsTextViewDefaultHeight = _detailsTextView.frame.size.height; 200 | _commentsTextViewDefaultHeight = _commentsTextView.frame.size.height; 201 | 202 | 203 | // make sure the key view loop is correct after all manipulations 204 | [self.window recalculateKeyViewLoop]; 205 | } 206 | 207 | //------------------------------------------------------------------------------------------------- 208 | - (void)showReportForException:(NSException*)exception 209 | exceptionStackTrace:(NSString*)exceptionStackTrace 210 | { 211 | // save exception 212 | self.exceptionMessage = exception.reason; 213 | self.exceptionStackTrace = AnonymizeString(exceptionStackTrace); 214 | 215 | // center the window 216 | NSWindow* window = self.window; 217 | if (!window.isVisible) 218 | { 219 | [window center]; 220 | } 221 | 222 | // show window 223 | [self initializeControls]; 224 | [NSApp runModalForWindow:window]; 225 | } 226 | 227 | //------------------------------------------------------------------------------------------------- 228 | - (void)beginFetchingSystemProfile 229 | { 230 | self.systemProfile = nil; 231 | _fetchingSystemProfileProgressLabel.hidden = NO; 232 | [_progressIndicator startAnimation:nil]; 233 | [_systemProfileFetcher fetchDataTypes:_systemProfileDataTypes]; 234 | } 235 | 236 | //------------------------------------------------------------------------------------------------- 237 | - (void)beginSendingFeedback 238 | { 239 | // update controls 240 | _anonymousLabel.hidden = YES; 241 | [_progressIndicator startAnimation:nil]; 242 | _progressIndicator.hidden = NO; 243 | _sendingReportProgressLabel.hidden = NO; 244 | _sendButton.enabled = NO; 245 | 246 | // begin sending feedback 247 | NSString* feedbackText = [NSString stringWithFormat:@"MESSAGE:\n%@\n\nCOMMENTS:\n%@\n\nSTACK TRACE:\n%@", 248 | _exceptionMessage, 249 | _commentsTextView.textStorage.string, 250 | _exceptionStackTrace]; 251 | [_feedbackSender cancel]; 252 | _feedbackSender.delegate = nil; 253 | [_feedbackSender release]; 254 | _feedbackSender = nil; 255 | 256 | _feedbackSender = [[DFFeedbackSender alloc] init]; 257 | _feedbackSender.delegate = self; 258 | [_feedbackSender sendFeedbackToUrl:_feedbackUrl 259 | feedbackText:feedbackText 260 | feedbackType:@"Crash" 261 | systemProfile:_systemProfile 262 | userEmail:nil]; 263 | } 264 | 265 | //------------------------------------------------------------------------------------------------- 266 | - (void)cancelPendingStuff 267 | { 268 | [_systemProfileFetcher cancel]; 269 | _systemProfileFetcher.delegate = nil; 270 | [_systemProfileFetcher release]; 271 | _systemProfileFetcher = nil; 272 | [_feedbackSender cancel]; 273 | _feedbackSender.delegate = nil; 274 | [_feedbackSender release]; 275 | _feedbackSender = nil; 276 | } 277 | 278 | //------------------------------------------------------------------------------------------------- 279 | - (IBAction)sendReport:(id)sender 280 | { 281 | // fetching system profile should be complete at the moment 282 | if (_systemProfileFetcher.isDoneFetching) 283 | { 284 | [self beginSendingFeedback]; 285 | } 286 | else 287 | { 288 | _sendButton.enabled = NO; 289 | _sendButtonWasClicked = YES; 290 | } 291 | } 292 | 293 | //------------------------------------------------------------------------------------------------- 294 | - (void)feedbackSender:(DFFeedbackSender*)sender didFinishWithError:(NSError*)error 295 | { 296 | if (sender == _feedbackSender) 297 | { 298 | [_feedbackSender release]; 299 | _feedbackSender = nil; 300 | [self dismiss]; 301 | [(DFApplication*)NSApp relaunch]; 302 | } 303 | } 304 | 305 | //------------------------------------------------------------------------------------------------- 306 | - (void)systemProfileFetcherDidFinish:(DFSystemProfileFetcher*)sender 307 | { 308 | if (sender == _systemProfileFetcher) 309 | { 310 | [_progressIndicator stopAnimation:nil]; 311 | _progressIndicator.hidden = YES; 312 | _fetchingSystemProfileProgressLabel.hidden = YES; 313 | _anonymousLabel.hidden = NO; 314 | if (_systemProfileFetcher.profile != nil) 315 | { 316 | self.systemProfile = AnonymizeString(_systemProfileFetcher.profile); 317 | NSString* profileString = [NSString stringWithFormat:@"\n\nSYSTEM PROFILE:\n\n%@", _systemProfile]; 318 | [_detailsTextView.textStorage appendAttributedString:[[[NSAttributedString alloc] initWithString:profileString 319 | attributes:@{NSForegroundColorAttributeName : NSColor.controlTextColor}] autorelease]]; 320 | } 321 | if (_sendButtonWasClicked) 322 | { 323 | [self beginSendingFeedback]; 324 | } 325 | } 326 | } 327 | 328 | //------------------------------------------------------------------------------------------------- 329 | - (void)dismiss 330 | { 331 | [self cancelPendingStuff]; 332 | [self.window orderOut:self]; 333 | } 334 | 335 | //------------------------------------------------------------------------------------------------- 336 | - (void)windowWillClose:(NSNotification*)notification 337 | { 338 | if (!_sendButtonWasClicked && !_cancelButtonWasClicked) 339 | { 340 | [self dismiss]; 341 | // on close just terminate without relaunching and sending the report 342 | [NSApp terminate:self]; 343 | } 344 | } 345 | 346 | //------------------------------------------------------------------------------------------------- 347 | - (IBAction)cancel:(id)sender 348 | { 349 | // relaunch without sending report 350 | _cancelButtonWasClicked = YES; 351 | [self dismiss]; 352 | [(DFApplication*)NSApp relaunch]; 353 | } 354 | 355 | //------------------------------------------------------------------------------------------------- 356 | - (IBAction)close:(id)sender 357 | { 358 | [self cancel:sender]; 359 | } 360 | 361 | //------------------------------------------------------------------------------------------------- 362 | - (IBAction)expandCollapseCommentsBox:(id)sender 363 | { 364 | BOOL shouldCollapseOrExpand = (_commentsDisclosureButton.state == NSOnState); 365 | [self expandOrCollapseBox:_commentsBoxView 366 | textView:_commentsScrollView 367 | textViewDefaultHeight:_commentsTextViewDefaultHeight 368 | alternateBox:_detailsBoxView 369 | shouldCollapseOrExpand:shouldCollapseOrExpand 370 | isLowerOrUpper:YES 371 | withAnimation:YES]; 372 | } 373 | 374 | //------------------------------------------------------------------------------------------------- 375 | - (IBAction)expandCollapseDetailsBox:(id)sender 376 | { 377 | BOOL shouldCollapseOrExpand = (_detailsDisclosureButton.state == NSOnState); 378 | [self expandOrCollapseBox:_detailsBoxView 379 | textView:_detailsScrollView 380 | textViewDefaultHeight:_detailsTextViewDefaultHeight 381 | alternateBox:_commentsBoxView 382 | shouldCollapseOrExpand:shouldCollapseOrExpand 383 | isLowerOrUpper:NO 384 | withAnimation:YES]; 385 | } 386 | 387 | //------------------------------------------------------------------------------------------------- 388 | - (void)expandOrCollapseBox:(NSView*)box 389 | textView:(NSView*)textView 390 | textViewDefaultHeight:(CGFloat)textViewDefaultHeight 391 | alternateBox:(NSView*)alternateBox 392 | shouldCollapseOrExpand:(BOOL)shouldCollapseOrExpand 393 | isLowerOrUpper:(BOOL)isLowerOrUpper 394 | withAnimation:(BOOL)withAnimation 395 | { 396 | // reset previous animation if any 397 | if (_windowAnimation != nil) 398 | { 399 | NSDictionary* viewAnimation = _windowAnimation.viewAnimations[0]; 400 | NSRect endWindowFrame = ((NSValue*)viewAnimation[NSViewAnimationEndFrameKey]).rectValue; 401 | [self.window setFrame:endWindowFrame 402 | display:YES 403 | animate:NO]; 404 | [_windowAnimation stopAnimation]; 405 | _windowAnimation.delegate = nil; 406 | [_windowAnimation release]; 407 | _windowAnimation = nil; 408 | } 409 | 410 | // window frames 411 | NSRect sourceWindowFrame = self.window.frame; 412 | NSRect targetWindowFrame = sourceWindowFrame; 413 | 414 | // expand 415 | if (shouldCollapseOrExpand) 416 | { 417 | box.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; 418 | alternateBox.autoresizingMask = NSViewWidthSizable | (isLowerOrUpper ? NSViewMaxYMargin : NSViewMinYMargin); 419 | CGFloat heightDiff = textViewDefaultHeight; 420 | targetWindowFrame.size.height += heightDiff; 421 | targetWindowFrame.origin.y -= heightDiff; 422 | } 423 | // collapse 424 | else 425 | { 426 | textView.hidden = YES; 427 | box.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; 428 | alternateBox.autoresizingMask = NSViewWidthSizable | (isLowerOrUpper ? NSViewMaxYMargin : NSViewMinYMargin); 429 | CGFloat heightDiff = textViewDefaultHeight; 430 | targetWindowFrame.size.height -= heightDiff; 431 | targetWindowFrame.origin.y += heightDiff; 432 | } 433 | 434 | // animate 435 | if (withAnimation) 436 | { 437 | // with animation, need non-blocking mode or disclosure button animation will appear jerky 438 | _windowAnimation = [[NSViewAnimation alloc] init]; 439 | _windowAnimation.animationBlockingMode = NSAnimationNonblocking; 440 | _windowAnimation.animationCurve = NSAnimationEaseInOut; 441 | _windowAnimation.duration = [self.window animationResizeTime:targetWindowFrame]; 442 | _windowAnimation.delegate = self; 443 | NSDictionary* windowAnimDict = NSDictionaryWithKeysAndValues(NSViewAnimationTargetKey, self.window, 444 | NSViewAnimationStartFrameKey, [NSValue valueWithRect:sourceWindowFrame], 445 | NSViewAnimationEndFrameKey, [NSValue valueWithRect:targetWindowFrame], 446 | nil); 447 | _windowAnimation.viewAnimations = @[windowAnimDict]; 448 | [_windowAnimation startAnimation]; 449 | } 450 | else 451 | { 452 | // without animation 453 | [self.window setFrame:targetWindowFrame 454 | display:YES 455 | animate:NO]; 456 | // emulate animation ended event 457 | [self animationDidEnd:_windowAnimation]; 458 | } 459 | } 460 | 461 | //------------------------------------------------------------------------------------------------- 462 | - (void)animationDidEnd:(NSAnimation*)animation 463 | { 464 | if (animation == _windowAnimation) 465 | { 466 | // update visibility of controls after animation edns 467 | if (_commentsDisclosureButton.state == NSOnState) 468 | { 469 | _commentsScrollView.hidden = NO; 470 | } 471 | if (_detailsDisclosureButton.state == NSOnState) 472 | { 473 | _detailsScrollView.hidden = NO; 474 | } 475 | _windowAnimation.delegate = nil; 476 | [_windowAnimation release]; 477 | _windowAnimation = nil; 478 | } 479 | } 480 | 481 | //------------------------------------------------------------------------------------------------- 482 | - (void)linkLabel:(DFLinkLabel*)sender didClickLinkNo:(NSUInteger)linkIndex 483 | { 484 | if (_updateUrl != nil) 485 | { 486 | if (sender == _updateLinkLabel) 487 | { 488 | if (linkIndex == 0) 489 | { 490 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:_updateUrl]]; 491 | } 492 | } 493 | } 494 | } 495 | 496 | @end 497 | -------------------------------------------------------------------------------- /Resources/zh_TW.lproj/DFCrashReportWindow.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 54 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 134 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 167 | 171 | 172 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | --------------------------------------------------------------------------------