Use garbage collection and add -[SocketWrapper close] to close down the socket and...
[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 * Called right before the window closes so that we can tell the socket to close down
62 */
63 - (void)windowWillClose:(NSNotification *)notif
64 {
65 [[connection socket] close];
66 }
67
68 /**
69 * Sets the status and clears any error message
70 */
71 - (void)setStatus:(NSString *)aStatus
72 {
73 [errormsg setHidden:YES];
74 [statusmsg setStringValue:aStatus];
75 [[self window] setTitle:[NSString stringWithFormat:@"GDBp @ %@:%d/%@", [connection remoteHost], [connection port], [connection session]]];
76
77 [stepInButton setEnabled:NO];
78 [stepOutButton setEnabled:NO];
79 [stepOverButton setEnabled:NO];
80 [runButton setEnabled:NO];
81 [reconnectButton setEnabled:NO];
82
83 if ([connection isConnected])
84 {
85 if ([aStatus isEqualToString:@"Starting"])
86 {
87 [stepInButton setEnabled:YES];
88 [runButton setEnabled:YES];
89 }
90 }
91 else
92 {
93 [reconnectButton setEnabled:YES];
94 }
95 }
96
97 /**
98 * Sets the status to be "Error" and then displays the error message
99 */
100 - (void)setError:(NSString *)anError
101 {
102 [errormsg setStringValue:anError];
103 [self setStatus:@"Error"];
104 [errormsg setHidden:NO];
105 }
106
107 /**
108 * Sets the root node element of the stacktrace
109 */
110 - (void)setStack:(NSArray *)node
111 {
112 stack = node;
113
114 if ([stack count] > 1)
115 {
116 [stepOutButton setEnabled:YES];
117 }
118 [stepInButton setEnabled:YES];
119 [stepOverButton setEnabled:YES];
120 [runButton setEnabled:YES];
121
122 [self updateSourceViewer];
123 }
124
125 /**
126 * Sets the stack root element so that the NSOutlineView can display it
127 */
128 - (void)setRegister:(NSXMLDocument *)elm
129 {
130 /*
131 [_registerController willChangeValueForKey:@"rootElement.children"];
132 [_registerController unbind:@"contentArray"];
133 [_registerController bind:@"contentArray" toObject:elm withKeyPath:@"rootElement.children" options:nil];
134 [_registerController didChangeValueForKey:@"rootElement.children"];
135 */
136 // XXX: Doing anything short of this will cause bindings to crash spectacularly for no reason whatsoever, and
137 // in seemingly arbitrary places. The class that crashes is _NSKeyValueObservationInfoCreateByRemoving.
138 // http://boredzo.org/blog/archives/2006-01-29/have-you-seen-this-crash says that this means nothing is
139 // being observed, but I doubt that he was using an NSOutlineView which seems to be one f!cking piece of
140 // sh!t when used with NSTreeController. http://www.cocoadev.com/index.pl?NSTreeControllerBugOrDeveloperError
141 // was the inspiration for this fix (below) but the author says that inserting does not work too well, but
142 // that's okay for us as we just need to replace the entire thing.
143 [registerController setContent:nil];
144 [registerController setContent:[[elm rootElement] children]];
145
146 /*for (NSTreeNode *node in expandedRegisters)
147 {
148 [registerView expandItem:node];
149 }*/
150 NSLog(@"expanded items = %@", expandedRegisters);
151 }
152
153 /**
154 * Forwards the message to run script execution to the connection
155 */
156 - (IBAction)run:(id)sender
157 {
158 [connection run];
159 }
160
161 /**
162 * Tells the connection to ask the server to reconnect
163 */
164 - (IBAction)reconnect:(id)sender
165 {
166
167 }
168
169 /**
170 * Forwards the message to "step in" to the connection
171 */
172 - (IBAction)stepIn:(id)sender
173 {
174 [connection stepIn];
175 }
176
177 /**
178 * Forwards the message to "step out" to the connection
179 */
180 - (IBAction)stepOut:(id)sender
181 {
182 [connection stepOut];
183 }
184
185 /**
186 * Forwards the message to "step over" to the connection
187 */
188 - (IBAction)stepOver:(id)sender
189 {
190 [connection stepOver];
191 }
192
193 /**
194 * NSTableView delegate method that informs the controller that the stack selection did change and that
195 * we should update the source viewer
196 */
197 - (void)tableViewSelectionDidChange:(NSNotification *)notif
198 {
199 [self updateSourceViewer];
200 }
201
202 /**
203 * Does the actual updating of the source viewer by reading in the file
204 */
205 - (void)updateSourceViewer
206 {
207 int selection = [stackController selectionIndex];
208 if (selection == NSNotFound)
209 {
210 [sourceViewer setString:@""];
211 return;
212 }
213
214 // get the filename and then set the text
215 NSString *filename = [[stack objectAtIndex:selection] valueForKey:@"filename"];
216 filename = [[NSURL URLWithString:filename] path];
217 NSString *text = [NSString stringWithContentsOfFile:filename];
218 [sourceViewer setString:text];
219
220 // go through the document until we find the NSRange for the line we want
221 int destination = [[[stack objectAtIndex:selection] valueForKey:@"lineno"] intValue];
222 int rangeIndex = 0;
223 for (int line = 0; line < destination; line++)
224 {
225 rangeIndex = NSMaxRange([text lineRangeForRange:NSMakeRange(rangeIndex, 0)]);
226 }
227
228 // now get the true start/end markers for it
229 unsigned lineStart, lineEnd;
230 [text getLineStart:&lineStart end:NULL contentsEnd:&lineEnd forRange:NSMakeRange(rangeIndex - 1, 0)];
231 NSRange lineRange = NSMakeRange(lineStart, lineEnd - lineStart);
232
233 // colorize it so the user knows which line we're on in the stack
234 [[sourceViewer textStorage] setAttributes:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSColor redColor], [NSColor yellowColor], nil]
235 forKeys:[NSArray arrayWithObjects:NSForegroundColorAttributeName, NSBackgroundColorAttributeName, nil]]
236 range:lineRange];
237 [sourceViewer scrollRangeToVisible:[text lineRangeForRange:NSMakeRange(lineStart, lineEnd - lineStart)]];
238
239 // make sure the font stays Monaco
240 [sourceViewer setFont:[NSFont fontWithName:@"Monaco" size:10.0]];
241 }
242
243 /**
244 * Called whenver an item is expanded. This allows us to determine if we need to fetch deeper
245 */
246 - (void)outlineViewItemDidExpand:(NSNotification *)notif
247 {
248 NSTreeNode *node = [[notif userInfo] objectForKey:@"NSObject"];
249
250 // we're not a leaf but have no children. this must be beyond our depth, so go make us deeper
251 if (![node isLeaf] && [[node childNodes] count] < 1)
252 {
253 [connection getProperty:[[[node representedObject] attributeForName:@"fullname"] stringValue] forNode:node];
254 }
255
256 [expandedRegisters addObject:[[node representedObject] variable]];
257 }
258
259 /**
260 * Called when an item was collapsed. This allows us to remove it from the list of expanded items
261 */
262 - (void)outlineViewItemDidCollapse:(NSNotification *)notif
263 {
264 [expandedRegisters removeObject:[[[[notif userInfo] objectForKey:@"NSObject"] representedObject] variable]];
265 }
266
267 /**
268 * Updates the register view by reinserting a given node back into the outline view
269 */
270 - (void)addChildren:(NSArray *)children toNode:(NSTreeNode *)node
271 {
272 NSIndexPath *masterPath = [node indexPath];
273 for (int i = 0; i < [children count]; i++)
274 {
275 [registerController insertObject:[children objectAtIndex:i] atArrangedObjectIndexPath:[masterPath indexPathByAddingIndex:i]];
276 }
277
278 [registerController rearrangeObjects];
279 }
280
281 @end