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