Merge branch 'refactoring' into leopard
[macgdbp.git] / Source / DebuggerWindowController.m
1 /*
2 * MacGDBp
3 * Copyright (c) 2002 - 2007, Blue Static <http://www.bluestatic.org>
4 *
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17 #import "DebuggerWindowController.h"
18 #import "DebuggerConnection.h"
19 #import "NSXMLElementAdditions.h"
20 #import "AppDelegate.h"
21
22 @interface DebuggerWindowController (Private)
23
24 - (void)updateSourceViewer;
25
26 @end
27
28 @implementation DebuggerWindowController
29
30 /**
31 * Initializes the window controller and sets the connection
32 */
33 - (id)initWithPort:(int)aPort session:(NSString *)aSession
34 {
35 if (self = [super initWithWindowNibName:@"Debugger"])
36 {
37 connection = [[DebuggerConnection alloc] initWithWindowController:self port:aPort session:aSession];
38 expandedRegisters = [[NSMutableArray alloc] init];
39 [[self window] makeKeyAndOrderFront:nil];
40 }
41 return self;
42 }
43
44 /**
45 * Before the display get's comfortable, set up the NSTextView to scroll horizontally
46 */
47 - (void)awakeFromNib
48 {
49 // set up the scroller for the source viewer
50 [sourceViewer setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
51 [[sourceViewer textContainer] setContainerSize:NSMakeSize(FLT_MAX, FLT_MAX)];
52 [[sourceViewer textContainer] setWidthTracksTextView:NO];
53 [sourceViewer setHorizontallyResizable:YES];
54 [sourceViewerScroller setHasHorizontalScroller:YES];
55 [sourceViewerScroller display];
56
57 [self setStatus:@"Connecting"];
58 }
59
60 /**
61 * Release object members
62 */
63 - (void)dealloc
64 {
65 [expandedRegisters release];
66
67 [super dealloc];
68 }
69
70 /**
71 * Sets the status and clears any error message
72 */
73 - (void)setStatus:(NSString *)aStatus
74 {
75 [errormsg setHidden:YES];
76 [statusmsg setStringValue:aStatus];
77 [[self window] setTitle:[NSString stringWithFormat:@"GDBp @ %@:%d/%@", [connection remoteHost], [connection port], [connection session]]];
78
79 [stepInButton setEnabled:NO];
80 [stepOutButton setEnabled:NO];
81 [stepOverButton setEnabled:NO];
82 [runButton setEnabled:NO];
83 [reconnectButton setEnabled:NO];
84
85 if ([connection isConnected])
86 {
87 if ([aStatus isEqualToString:@"Starting"])
88 {
89 [stepInButton setEnabled:YES];
90 [runButton setEnabled:YES];
91 }
92 }
93 else
94 {
95 [reconnectButton setEnabled:YES];
96 }
97 }
98
99 /**
100 * Sets the status to be "Error" and then displays the error message
101 */
102 - (void)setError:(NSString *)anError
103 {
104 [errormsg setStringValue:anError];
105 [self setStatus:@"Error"];
106 [errormsg setHidden:NO];
107 }
108
109 /**
110 * Sets the root node element of the stacktrace
111 */
112 - (void)setStack:(NSArray *)node
113 {
114 if (stack != nil)
115 {
116 [stack release];
117 }
118
119 stack = node;
120 [stack retain];
121
122 if ([stack count] > 1)
123 {
124 [stepOutButton setEnabled:YES];
125 }
126 [stepInButton setEnabled:YES];
127 [stepOverButton setEnabled:YES];
128 [runButton setEnabled:YES];
129
130 [self updateSourceViewer];
131 }
132
133 /**
134 * Sets the stack root element so that the NSOutlineView can display it
135 */
136 - (void)setRegister:(NSXMLDocument *)elm
137 {
138 /*
139 [_registerController willChangeValueForKey:@"rootElement.children"];
140 [_registerController unbind:@"contentArray"];
141 [_registerController bind:@"contentArray" toObject:elm withKeyPath:@"rootElement.children" options:nil];
142 [_registerController didChangeValueForKey:@"rootElement.children"];
143 */
144 // XXX: Doing anything short of this will cause bindings to crash spectacularly for no reason whatsoever, and
145 // in seemingly arbitrary places. The class that crashes is _NSKeyValueObservationInfoCreateByRemoving.
146 // http://boredzo.org/blog/archives/2006-01-29/have-you-seen-this-crash says that this means nothing is
147 // being observed, but I doubt that he was using an NSOutlineView which seems to be one f!cking piece of
148 // sh!t when used with NSTreeController. http://www.cocoadev.com/index.pl?NSTreeControllerBugOrDeveloperError
149 // was the inspiration for this fix (below) but the author says that inserting does not work too well, but
150 // that's okay for us as we just need to replace the entire thing.
151 [registerController setContent:nil];
152 [registerController setContent:[[elm rootElement] children]];
153
154 /*for (NSTreeNode *node in expandedRegisters)
155 {
156 [registerView expandItem:node];
157 }*/
158 NSLog(@"expanded items = %@", expandedRegisters);
159 }
160
161 /**
162 * Forwards the message to run script execution to the connection
163 */
164 - (IBAction)run:(id)sender
165 {
166 [connection run];
167 }
168
169 /**
170 * Tells the connection to ask the server to reconnect
171 */
172 - (IBAction)reconnect:(id)sender
173 {
174
175 }
176
177 /**
178 * Forwards the message to "step in" to the connection
179 */
180 - (IBAction)stepIn:(id)sender
181 {
182 [connection stepIn];
183 }
184
185 /**
186 * Forwards the message to "step out" to the connection
187 */
188 - (IBAction)stepOut:(id)sender
189 {
190 [connection stepOut];
191 }
192
193 /**
194 * Forwards the message to "step over" to the connection
195 */
196 - (IBAction)stepOver:(id)sender
197 {
198 [connection stepOver];
199 }
200
201 /**
202 * NSTableView delegate method that informs the controller that the stack selection did change and that
203 * we should update the source viewer
204 */
205 - (void)tableViewSelectionDidChange:(NSNotification *)notif
206 {
207 [self updateSourceViewer];
208 }
209
210 /**
211 * Does the actual updating of the source viewer by reading in the file
212 */
213 - (void)updateSourceViewer
214 {
215 int selection = [stackController selectionIndex];
216 if (selection == NSNotFound)
217 {
218 [sourceViewer setString:@""];
219 return;
220 }
221
222 // get the filename and then set the text
223 NSString *filename = [[stack objectAtIndex:selection] valueForKey:@"filename"];
224 filename = [[NSURL URLWithString:filename] path];
225 NSString *text = [NSString stringWithContentsOfFile:filename];
226 [sourceViewer setString:text];
227
228 // go through the document until we find the NSRange for the line we want
229 int destination = [[[stack objectAtIndex:selection] valueForKey:@"lineno"] intValue];
230 int rangeIndex = 0;
231 for (int line = 0; line < destination; line++)
232 {
233 rangeIndex = NSMaxRange([text lineRangeForRange:NSMakeRange(rangeIndex, 0)]);
234 }
235
236 // now get the true start/end markers for it
237 unsigned lineStart, lineEnd;
238 [text getLineStart:&lineStart end:NULL contentsEnd:&lineEnd forRange:NSMakeRange(rangeIndex - 1, 0)];
239 NSRange lineRange = NSMakeRange(lineStart, lineEnd - lineStart);
240
241 // colorize it so the user knows which line we're on in the stack
242 [[sourceViewer textStorage] setAttributes:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSColor redColor], [NSColor yellowColor], nil]
243 forKeys:[NSArray arrayWithObjects:NSForegroundColorAttributeName, NSBackgroundColorAttributeName, nil]]
244 range:lineRange];
245 [sourceViewer scrollRangeToVisible:[text lineRangeForRange:NSMakeRange(lineStart, lineEnd - lineStart)]];
246
247 // make sure the font stays Monaco
248 [sourceViewer setFont:[NSFont fontWithName:@"Monaco" size:10.0]];
249 }
250
251 /**
252 * Called whenver an item is expanded. This allows us to determine if we need to fetch deeper
253 */
254 - (void)outlineViewItemDidExpand:(NSNotification *)notif
255 {
256 NSTreeNode *node = [[notif userInfo] objectForKey:@"NSObject"];
257
258 // we're not a leaf but have no children. this must be beyond our depth, so go make us deeper
259 if (![node isLeaf] && [[node childNodes] count] < 1)
260 {
261 [connection getProperty:[[[node representedObject] attributeForName:@"fullname"] stringValue] forNode:node];
262 }
263
264 [expandedRegisters addObject:[[node representedObject] variable]];
265 }
266
267 /**
268 * Called when an item was collapsed. This allows us to remove it from the list of expanded items
269 */
270 - (void)outlineViewItemDidCollapse:(NSNotification *)notif
271 {
272 [expandedRegisters removeObject:[[[[notif userInfo] objectForKey:@"NSObject"] representedObject] variable]];
273 }
274
275 /**
276 * Updates the register view by reinserting a given node back into the outline view
277 */
278 - (void)addChildren:(NSArray *)children toNode:(NSTreeNode *)node
279 {
280 NSIndexPath *masterPath = [node indexPath];
281 for (int i = 0; i < [children count]; i++)
282 {
283 [registerController insertObject:[children objectAtIndex:i] atArrangedObjectIndexPath:[masterPath indexPathByAddingIndex:i]];
284 }
285
286 [registerController rearrangeObjects];
287 }
288
289 @end