2 * Author: Andreas Linde <mail@andreaslinde.de>
5 * Copyright (c) 2011 Andreas Linde & Kent Sutherland.
8 * Permission is hereby granted, free of charge, to any person
9 * obtaining a copy of this software and associated documentation
10 * files (the "Software"), to deal in the Software without
11 * restriction, including without limitation the rights to use,
12 * copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
31 #import "BWQuincyUI.h"
32 #import "BWQuincyManager.h"
33 #import <sys/sysctl.h>
35 #define CRASHREPORTSENDER_MAX_CONSOLE_SIZE 50000
37 @interface BWQuincyUI(private)
38 - (void) askCrashReportDetails;
39 - (void) endCrashReporter;
42 const CGFloat kCommentsHeight = 105;
43 const CGFloat kDetailsHeight = 285;
45 @implementation BWQuincyUI
47 - (id)initWithManager:(BWQuincyManager *)quincyManager crashFile:(NSString *)crashFile companyName:(NSString *)companyName applicationName:(NSString *)applicationName {
49 self = [super initWithWindowNibName: @"BWQuincyMain"];
53 _quincyManager = quincyManager;
54 _crashFile = crashFile;
55 _companyName = companyName;
56 _applicationName = applicationName;
57 [self setShowComments: YES];
58 [self setShowDetails: NO];
60 NSRect windowFrame = [[self window] frame];
61 windowFrame.size = NSMakeSize(windowFrame.size.width, windowFrame.size.height - kDetailsHeight);
62 windowFrame.origin.y -= kDetailsHeight;
63 [[self window] setFrame: windowFrame
74 crashLogTextView.editable = NO;
75 crashLogTextView.selectable = NO;
76 crashLogTextView.automaticSpellingCorrectionEnabled = NO;
78 [showButton setTitle:_NS("Show Details")];
79 [hideButton setTitle:_NS("Hide Details")];
80 [cancelButton setTitle:_NS("Cancel")];
81 [submitButton setTitle:_NS("Send")];
82 [titleText setStringValue:[NSString stringWithFormat:_NS("%@ unexpectedly quit the last time it was run. Would you like to send a crash report to %@?"), _applicationName, _companyName]];
83 [commentsText setStringValue:_NS("Comments")];
84 [detailsText setStringValue:_NS("Problem details and system configuration")];
88 - (void) endCrashReporter {
93 - (IBAction) showComments: (id) sender {
94 NSRect windowFrame = [[self window] frame];
96 if ([sender intValue]) {
97 [self setShowComments: NO];
99 windowFrame.size = NSMakeSize(windowFrame.size.width, windowFrame.size.height + kCommentsHeight);
100 windowFrame.origin.y -= kCommentsHeight;
101 [[self window] setFrame: windowFrame
105 [self setShowComments: YES];
107 [self setShowComments: NO];
109 windowFrame.size = NSMakeSize(windowFrame.size.width, windowFrame.size.height - kCommentsHeight);
110 windowFrame.origin.y += kCommentsHeight;
111 [[self window] setFrame: windowFrame
118 - (IBAction) showDetails:(id)sender {
119 NSRect windowFrame = [[self window] frame];
121 windowFrame.size = NSMakeSize(windowFrame.size.width, windowFrame.size.height + kDetailsHeight);
122 windowFrame.origin.y -= kDetailsHeight;
123 [[self window] setFrame: windowFrame
127 [self setShowDetails:YES];
132 - (IBAction) hideDetails:(id)sender {
133 NSRect windowFrame = [[self window] frame];
135 [self setShowDetails:NO];
137 windowFrame.size = NSMakeSize(windowFrame.size.width, windowFrame.size.height - kDetailsHeight);
138 windowFrame.origin.y += kDetailsHeight;
139 [[self window] setFrame: windowFrame
145 - (IBAction) cancelReport:(id)sender {
146 [self endCrashReporter];
149 [_quincyManager cancelReport];
152 - (void) _sendReportAfterDelay {
153 NSString *notes = [NSString stringWithFormat:@"Comments:\n%@\n\nConsole:\n%@", [descriptionTextField stringValue], _consoleContent];
155 [_quincyManager sendReportCrash:_crashLogContent description:notes];
156 [_crashLogContent release];
157 _crashLogContent = nil;
160 - (IBAction) submitReport:(id)sender {
161 [submitButton setEnabled:NO];
163 [[self window] makeFirstResponder: nil];
165 [self performSelector:@selector(_sendReportAfterDelay) withObject:nil afterDelay:0.01];
167 [self endCrashReporter];
172 - (void) askCrashReportDetails {
175 [[self window] setTitle:[NSString stringWithFormat:_NS("Problem Report for %@"), _applicationName]];
177 [[descriptionTextField cell] setPlaceholderString:_NS("Please describe any steps needed to trigger the problem")];
178 [noteText setStringValue:_NS("No personal information will be sent with this report.")];
181 NSString *crashLogs = [NSString stringWithContentsOfFile:_crashFile encoding:NSUTF8StringEncoding error:&error];
182 NSString *lastCrash = [[crashLogs componentsSeparatedByString: @"**********\n\n"] lastObject];
184 _crashLogContent = [lastCrash retain];
186 // get the console log
187 NSEnumerator *theEnum = [[[NSString stringWithContentsOfFile:@"/private/var/log/system.log" encoding:NSUTF8StringEncoding error:&error] componentsSeparatedByString: @"\n"] objectEnumerator];
188 NSString* currentObject;
189 NSMutableArray* applicationStrings = [NSMutableArray array];
191 NSString* searchString = [_applicationName stringByAppendingString:@"["];
192 while ( (currentObject = [theEnum nextObject]) ) {
193 if ([currentObject rangeOfString:searchString].location != NSNotFound)
194 [applicationStrings addObject: currentObject];
197 _consoleContent = [[NSMutableString alloc] initWithString:@""];
200 for(i = ((NSInteger)[applicationStrings count])-1; (i>=0 && i>((NSInteger)[applicationStrings count])-100); i--) {
201 [_consoleContent appendString:[applicationStrings objectAtIndex:i]];
202 [_consoleContent appendString:@"\n"];
205 // Now limit the content to CRASHREPORTSENDER_MAX_CONSOLE_SIZE (default: 50kByte)
206 if ([_consoleContent length] > CRASHREPORTSENDER_MAX_CONSOLE_SIZE) {
207 _consoleContent = (NSMutableString *)[_consoleContent substringWithRange:NSMakeRange([_consoleContent length]-CRASHREPORTSENDER_MAX_CONSOLE_SIZE-1, CRASHREPORTSENDER_MAX_CONSOLE_SIZE)];
210 [crashLogTextView setString:[NSString stringWithFormat:@"%@\n\n%@", _crashLogContent, _consoleContent]];
212 [NSApp runModalForWindow:[self window]];
217 [_consoleContent release]; _consoleContent = nil;
219 _quincyManager = nil;
225 - (BOOL)showComments {
230 - (void)setShowComments:(BOOL)value {
231 showComments = value;
235 - (BOOL)showDetails {
240 - (void)setShowDetails:(BOOL)value {
244 #pragma mark NSTextField Delegate
246 - (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
247 BOOL commandHandled = NO;
249 if (commandSelector == @selector(insertNewline:)) {
250 [textView insertNewlineIgnoringFieldEditor:self];
251 commandHandled = YES;
254 return commandHandled;