Adding a new class called BreakpointManager so we don't clutter up AppDelegate
[macgdbp.git] / Source / DebuggerWindowController.m
1 /*
2 * MacGDBp
3 * Copyright (c) 2007 - 2008, 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 #import "BreakpointManager.h"
22
23 @interface DebuggerWindowController (Private)
24
25 - (void)updateSourceViewer;
26
27 @end
28
29 @implementation DebuggerWindowController
30
31 @synthesize connection;
32
33 /**
34 * Initializes the window controller and sets the connection
35 */
36 - (id)initWithPort:(int)aPort session:(NSString *)aSession
37 {
38 if (self = [super initWithWindowNibName:@"Debugger"])
39 {
40 connection = [[DebuggerConnection alloc] initWithWindowController:self port:aPort session:aSession];
41 expandedRegisters = [[NSMutableSet alloc] init];
42 [[self window] makeKeyAndOrderFront:nil];
43 }
44 return self;
45 }
46
47 /**
48 * Before the display get's comfortable, set up the NSTextView to scroll horizontally
49 */
50 - (void)awakeFromNib
51 {
52 [self setStatus:@"Connecting"];
53 [sourceViewer setDelegate:self];
54 }
55
56 /**
57 * Called right before the window closes so that we can tell the socket to close down
58 */
59 - (void)windowWillClose:(NSNotification *)notif
60 {
61 [[connection socket] close];
62 }
63
64 /**
65 * Resets all the displays to be empty
66 */
67 - (void)resetDisplays
68 {
69 [registerController setContent:nil];
70 [stackController setContent:nil];
71 [[sourceViewer textView] setString:@""];
72 }
73
74 /**
75 * Sets the status and clears any error message
76 */
77 - (void)setStatus:(NSString *)aStatus
78 {
79 [errormsg setHidden:YES];
80 [statusmsg setStringValue:aStatus];
81 [[self window] setTitle:[NSString stringWithFormat:@"GDBp @ %@:%d/%@", [connection remoteHost], [connection port], [connection session]]];
82
83 [stepInButton setEnabled:NO];
84 [stepOutButton setEnabled:NO];
85 [stepOverButton setEnabled:NO];
86 [runButton setEnabled:NO];
87 [reconnectButton setEnabled:NO];
88
89 if ([connection isConnected])
90 {
91 if ([aStatus isEqualToString:@"Starting"])
92 {
93 [stepInButton setEnabled:YES];
94 [runButton setEnabled:YES];
95 }
96 }
97 else
98 {
99 [reconnectButton setEnabled:YES];
100 }
101 }
102
103 /**
104 * Sets the status to be "Error" and then displays the error message
105 */
106 - (void)setError:(NSString *)anError
107 {
108 [errormsg setStringValue:anError];
109 [self setStatus:@"Error"];
110 [errormsg setHidden:NO];
111 }
112
113 /**
114 * Sets the root node element of the stacktrace
115 */
116 - (void)setStack:(NSArray *)node
117 {
118 stack = node;
119
120 if ([stack count] > 1)
121 {
122 [stepOutButton setEnabled:YES];
123 }
124 [stepInButton setEnabled:YES];
125 [stepOverButton setEnabled:YES];
126 [runButton setEnabled:YES];
127
128 [self updateSourceViewer];
129 }
130
131 /**
132 * Sets the stack root element so that the NSOutlineView can display it
133 */
134 - (void)setRegister:(NSXMLDocument *)elm
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 (int i = 0; i < [registerView numberOfRows]; i++)
147 {
148 NSTreeNode *node = [registerView itemAtRow:i];
149 if ([expandedRegisters containsObject:[[node representedObject] fullname]])
150 {
151 [registerView expandItem:node];
152 }
153 }
154 }
155
156 /**
157 * Forwards the message to run script execution to the connection
158 */
159 - (IBAction)run:(id)sender
160 {
161 [connection run];
162 }
163
164 /**
165 * Tells the connection to ask the server to reconnect
166 */
167 - (IBAction)reconnect:(id)sender
168 {
169 [connection reconnect];
170 }
171
172 /**
173 * Forwards the message to "step in" to the connection
174 */
175 - (IBAction)stepIn:(id)sender
176 {
177 [connection stepIn];
178 }
179
180 /**
181 * Forwards the message to "step out" to the connection
182 */
183 - (IBAction)stepOut:(id)sender
184 {
185 [connection stepOut];
186 }
187
188 /**
189 * Forwards the message to "step over" to the connection
190 */
191 - (IBAction)stepOver:(id)sender
192 {
193 [connection stepOver];
194 }
195
196 /**
197 * NSTableView delegate method that informs the controller that the stack selection did change and that
198 * we should update the source viewer
199 */
200 - (void)tableViewSelectionDidChange:(NSNotification *)notif
201 {
202 [self updateSourceViewer];
203 }
204
205 /**
206 * Does the actual updating of the source viewer by reading in the file
207 */
208 - (void)updateSourceViewer
209 {
210 id selectedLevel = [[stackController selection] valueForKey:@"level"];
211 if (selectedLevel == NSNoSelectionMarker)
212 {
213 [[sourceViewer textView] setString:@""];
214 return;
215 }
216 int selection = [selectedLevel intValue];
217
218 // get the filename and then set the text
219 NSString *filename = [[stack objectAtIndex:selection] valueForKey:@"filename"];
220 filename = [[NSURL URLWithString:filename] path];
221 [sourceViewer setFile:filename];
222
223 int line = [[[stack objectAtIndex:selection] valueForKey:@"lineno"] intValue];
224 [sourceViewer setMarkedLine:line];
225 [sourceViewer scrollToLine:line];
226
227 // make sure the font stays Monaco
228 //[sourceViewer setFont:[NSFont fontWithName:@"Monaco" size:10.0]];
229 }
230
231 /**
232 * Called whenver an item is expanded. This allows us to determine if we need to fetch deeper
233 */
234 - (void)outlineViewItemDidExpand:(NSNotification *)notif
235 {
236 NSTreeNode *node = [[notif userInfo] objectForKey:@"NSObject"];
237
238 // we're not a leaf but have no children. this must be beyond our depth, so go make us deeper
239 if (![node isLeaf] && [[node childNodes] count] < 1)
240 {
241 [connection getProperty:[[node representedObject] fullname] forNode:node];
242 }
243
244 [expandedRegisters addObject:[[node representedObject] fullname]];
245 }
246
247 /**
248 * Called when an item was collapsed. This allows us to remove it from the list of expanded items
249 */
250 - (void)outlineViewItemDidCollapse:(NSNotification *)notif
251 {
252 [expandedRegisters removeObject:[[[[notif userInfo] objectForKey:@"NSObject"] representedObject] fullname]];
253 }
254
255 /**
256 * Updates the register view by reinserting a given node back into the outline view
257 */
258 - (void)addChildren:(NSArray *)children toNode:(NSTreeNode *)node
259 {
260 NSXMLElement *parent = [node representedObject];
261 for (NSXMLNode *child in children)
262 {
263 [parent addChild:child];
264 }
265
266 [registerController rearrangeObjects];
267 }
268
269 #pragma mark BSSourceView Delegate
270
271 /**
272 * The gutter was clicked, which indicates that a breakpoint needs to be changed
273 */
274 - (void)gutterClickedAtLine:(int)line forFile:(NSString *)file
275 {
276 [[BreakpointManager sharedManager] addBreakpoint:[[Breakpoint alloc] initWithLine:line inFile:file]];
277 [[sourceViewer numberView] setMarkers:[[BreakpointManager sharedManager] breakpointsForFile:file]];
278 [[sourceViewer numberView] setNeedsDisplay:YES];
279 }
280
281 @end