├── 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 | 
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 | 
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 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
--------------------------------------------------------------------------------