forked from mmower/spike
-
Notifications
You must be signed in to change notification settings - Fork 0
/
LogDocument.m
257 lines (200 loc) · 8.59 KB
/
LogDocument.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
//
// LogDocument.m
// Spike
//
// Created by Matt Mower on 18/02/2009.
// Copyright 2009 LucidMac Software. All rights reserved.
//
#import "LogDocument.h"
#import "AppController.h"
#import "LogParser.h"
#import "RailsRequest.h"
static NSString *ReloadToolbarItemIdentifier = @"spike.toolbar.reload";
static NSString *FocusToolbarItemIdentifier = @"spike.toolbar.focus";
static NSString *RemoveToolbarItemIdentifier = @"spike.toolbar.remove";
static NSString *SearchToolbarItemIdentifier = @"spike.toolbar.search";
float adjusted_brightness( float brightness ) {
brightness *= 1.5;
if( brightness > 1.0 ) {
brightness = 1.0;
}
return brightness;
}
static NSMutableDictionary *HTTPMethodColors;
@interface LogDocument (PrivateMethods)
+ (NSColor *)colorForHTTPMethod:(NSString *)method brighten:(BOOL)brighten;
@end
@implementation LogDocument
/*
* Returns an NSColor associated with the specified HTTP method and, optionally,
* brightens the colour first.
*/
+ (NSColor *)colorForHTTPMethod:(NSString *)method brighten:(BOOL)brighten {
NSColor *color = [HTTPMethodColors objectForKey:[method uppercaseString]];
if( color ) {
if( brighten ) {
color = [NSColor colorWithDeviceHue:[color hueComponent] saturation:[color saturationComponent] brightness:adjusted_brightness([color brightnessComponent]) alpha:1.0];
}
return color;
} else {
return [NSColor controlTextColor];
}
}
+ (void)initialize {
if( !HTTPMethodColors ) {
HTTPMethodColors = [NSMutableDictionary dictionaryWithCapacity:4];
[HTTPMethodColors setObject:[NSColor colorWithDeviceRed:(20.0/255) green:(128.0/255) blue:(65.0/255) alpha:1.0] forKey:@"GET"];
[HTTPMethodColors setObject:[NSColor blueColor] forKey:@"PUT"];
[HTTPMethodColors setObject:[NSColor blueColor] forKey:@"POST"];
[HTTPMethodColors setObject:[NSColor redColor] forKey:@"DELETE"];
}
}
@synthesize compressedLog;
@synthesize requests;
@synthesize requestsController;
#pragma mark NSDocument implementation
- (void)makeWindowControllers {
[self addWindowController:[[NSWindowController alloc] initWithWindowNibName:@"LogDocument" owner:self]];
detailsController = [[NSWindowController alloc] initWithWindowNibName:@"RequestDetails" owner:self];
[self addWindowController:detailsController];
}
- (NSString *)displayName {
return [[self fileURL] path];
}
#pragma mark NSWindow delegate implementation
/*
* Trigger a read of the logfile from a background thread. The thread will update
* the user interface when the data has been parsed. Detects whether the document
* corresponds to a gzip'd log and triggers decompression where necessary.
*/
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError {
if( [typeName isEqualToString:@"Compressed Log File"] ) {
[self setCompressedLog:YES];
} else {
[self setCompressedLog:NO];
}
logParser = [[LogParser alloc] initWithDocument:self];
// The parser handles the log data on a background thread. When it is done
// it will call back on the main thread to set the requests property.
[NSThread detachNewThreadSelector:@selector(parseLogData:)
toTarget:logParser
withObject:data];
// Always return YES. Not quite sure what we should do if there is ever an error
// in the legitimate load process.
return YES;
}
/*
* Close document when main window closes.
*/
- (void)windowWillClose:(NSNotification *)notification {
[self close];
}
#pragma mark NSToolbar delegate implementations
- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)aToolbar {
return [NSArray arrayWithObjects:ReloadToolbarItemIdentifier,FocusToolbarItemIdentifier,RemoveToolbarItemIdentifier,NSToolbarFlexibleSpaceItemIdentifier,SearchToolbarItemIdentifier,nil];
}
- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)aToolbar {
return [NSArray arrayWithObjects:ReloadToolbarItemIdentifier,FocusToolbarItemIdentifier,RemoveToolbarItemIdentifier,NSToolbarFlexibleSpaceItemIdentifier,SearchToolbarItemIdentifier,nil];
}
- (NSToolbarItem *)toolbar:(NSToolbar *)aToolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag {
NSToolbarItem *toolbarItem = nil;
if( [itemIdentifier isEqualTo:ReloadToolbarItemIdentifier] ) {
toolbarItem = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
[toolbarItem setLabel:@"Reload"];
[toolbarItem setImage:[NSImage imageNamed:@"refresh_32.gif"]];
[toolbarItem setAction:@selector(reloadChangedLog:)];
} else if( [itemIdentifier isEqualTo:FocusToolbarItemIdentifier] ) {
toolbarItem = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
[toolbarItem setLabel:@"Focus"];
[toolbarItem setImage:[NSImage imageNamed:@"search_32.gif"]];
[toolbarItem setAction:@selector(focusOnRequest:)];
focusToolbarItem = toolbarItem;
} else if( [itemIdentifier isEqualTo:RemoveToolbarItemIdentifier] ) {
toolbarItem = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
[toolbarItem setLabel:@"Remove"];
[toolbarItem setImage:[NSImage imageNamed:@"close_32.gif"]];
[toolbarItem setAction:@selector(removeSimilarRequests:)];
} else if( [itemIdentifier isEqualTo:SearchToolbarItemIdentifier] ) {
toolbarItem = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
[toolbarItem setView:searchField];
[toolbarItem setMinSize:[searchField frame].size];
[toolbarItem setMaxSize:[searchField frame].size];
}
return toolbarItem;
}
#pragma mark NSTableView delegate implementations
/*
* For the HTTP Method column, color the text appropriately. All other text columns
* get the default color. The selected row gets a brighter color to compensate for
* the dark background of that row.
*/
- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(int)rowIndex {
if( [[tableColumn identifier] isEqualToString:@"method"] ) {
[cell setTextColor:[[self class] colorForHTTPMethod:[cell stringValue]
brighten:(rowIndex == [tableView selectedRow])]];
} else if( [cell respondsToSelector:@selector(setTextColor:)]) {
[cell setTextColor:[NSColor controlTextColor]];
}
}
#pragma mark Actions
/*
* Destructively remove all requests from the table that match the currently
* selected controller & action.
*/
- (IBAction)removeSimilarRequests:(id)sender {
RailsRequest *request = [[requestsController selectedObjects] objectAtIndex:0];
if( request ) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"!( controller == %@ AND action == %@ )",[request controller],[request action]];
[self setRequests:[requests filteredArrayUsingPredicate:predicate]];
}
}
/*
* Remove any user supplied filter predicate and replace with a predicate to
* select only those rows matching the currently selected rows controller and
* action.
*/
- (IBAction)focusOnRequest:(id)sender {
NSMenuItem *focusMenuItem = [[NSApp delegate] focusMenuItem];
if( [focusMenuItem state] == NSOffState ) {
RailsRequest *request = [[requestsController selectedObjects] objectAtIndex:0];
if( request ) {
[searchField setStringValue:@""];
[requestsController setFilterPredicate:[NSPredicate predicateWithFormat:@"controller == %@ AND action == %@",[request controller],[request action]]];
}
[focusToolbarItem setLabel:@"Unfocus"];
[focusMenuItem setState:NSOnState];
} else {
[requestsController setFilterPredicate:nil];
[focusToolbarItem setLabel:@"Focus"];
[focusMenuItem setState:NSOffState];
}
}
/*
* Take the session from the currently selected request and filter on it.
*/
- (IBAction)followSession:(id)sender {
RailsRequest *request = [[requestsController selectedObjects] objectAtIndex:0];
if( request ) {
[searchField setStringValue:@""];
[requestsController setFilterPredicate:[NSPredicate predicateWithFormat:@"session == %@",[request session]]];
}
}
/*
* Reloads the log data but only reparses what is new since the last parse.
*/
- (IBAction)reloadChangedLog:(id)sender {
NSLog( @"reloadChangedLog:" );
// The parser handles the log data on a background thread. When it is done
// it will call back on the main thread to set the requests property.
[NSThread detachNewThreadSelector:@selector(parseLogData:)
toTarget:logParser
withObject:[NSData dataWithContentsOfURL:[self fileURL]]];
}
/*
* Reshow detail window if it got closed
*/
- (IBAction)showDetails:(id)sender {
NSLog( @"Show details");
[detailsController showWindow:sender];
}
@end