Deprecated: Assigning the return value of new by reference is deprecated in /home/bluestat/public_html/source/index.php on line 477
/*
* MacGDBp
* Copyright (c) 2007 - 2009, Blue Static
*
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if not,
* write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#import "DebuggerController.h"
#import "GDBpConnection.h"
#import "NSXMLElementAdditions.h"
#import "AppDelegate.h"
#import "BreakpointManager.h"
@interface DebuggerController (Private)
- (void)updateSourceViewer;
- (void)updateStackViewer;
- (void)expandVariables;
- (void)reloadStack;
@end
@implementation DebuggerController
@synthesize connection, sourceViewer, inspector;
/**
* Initializes the window controller and sets the connection using preference
* values
*/
- (id)init
{
if (self = [super initWithWindowNibName:@"Debugger"])
{
stackController = [[StackController alloc] init];
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
connection = [[GDBpConnection alloc] initWithPort:[defaults integerForKey:@"Port"] session:[defaults stringForKey:@"IDEKey"]];
expandedVariables = [[NSMutableSet alloc] init];
[[self window] makeKeyAndOrderFront:nil];
[[self window] setDelegate:self];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(handleConnectionError:)
name:kErrorOccurredNotif
object:connection
];
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"InspectorWindowVisible"])
[inspector orderFront:self];
}
return self;
}
/**
* Dealloc
*/
- (void)dealloc
{
[connection release];
[expandedVariables release];
[stackController release];
[super dealloc];
}
/**
* Before the display get's comfortable, set up the NSTextView to scroll horizontally
*/
- (void)awakeFromNib
{
[[self window] setExcludedFromWindowsMenu:YES];
[[self window] setTitle:[NSString stringWithFormat:@"GDBp @ %@:%d/%@", [connection remoteHost], [connection port], [connection session]]];
[sourceViewer setDelegate:self];
[stackArrayController setSortDescriptors:[NSArray arrayWithObject:[[[NSSortDescriptor alloc] initWithKey:@"index" ascending:YES] autorelease]]];
}
/**
* Called right before the window closes so that we can tell the socket to close down
*/
- (void)windowWillClose:(NSNotification*)notif
{
[[connection socket] close];
}
/**
* Validates the menu items for the "Debugger" menu
*/
- (BOOL)validateUserInterfaceItem:(id )anItem
{
SEL action = [anItem action];
if (action == @selector(stepOut:))
return ([connection isConnected] && [stackController.stack count] > 1);
else if (action == @selector(stepIn:) || action == @selector(stepOver:) || action == @selector(run:))
return [connection isConnected];
else if (action == @selector(reconnect:))
return ![connection isConnected];
return [[self window] validateUserInterfaceItem:anItem];
}
/**
* Shows the inspector window
*/
- (IBAction)showInspectorWindow:(id)sender
{
if (![inspector isVisible])
[inspector makeKeyAndOrderFront:sender];
else
[inspector orderOut:sender];
}
/**
* Resets all the displays to be empty
*/
- (void)resetDisplays
{
[variablesTreeController setContent:nil];
[stackController.stack removeAllObjects];
[stackArrayController rearrangeObjects];
[[sourceViewer textView] setString:@""];
sourceViewer.file = nil;
}
/**
* Sets the status to be "Error" and then displays the error message
*/
- (void)setError:(NSString*)anError
{
[errormsg setStringValue:anError];
[errormsg setHidden:NO];
}
/**
* Handles a GDBpConnection error
*/
- (void)handleConnectionError:(NSNotification*)notif
{
[self setError:[[notif userInfo] valueForKey:@"NSString"]];
}
/**
* Called once the socket accepts and MacGDBp is connected to the debugger
*/
- (void)startDebugger
{
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"BreakOnFirstLine"])
[self stepIn:self];
}
/**
* Forwards the message to run script execution to the connection
*/
- (IBAction)run:(id)sender
{
[connection run];
if ([connection isConnected])
[self reloadStack];
}
/**
* Tells the connection to ask the server to reconnect
*/
- (IBAction)reconnect:(id)sender
{
[connection reconnect];
[self resetDisplays];
}
/**
* Forwards the message to "step in" to the connection
*/
- (IBAction)stepIn:(id)sender
{
if ([[variablesTreeController selectedObjects] count] > 0)
selectedVariable = [[variablesTreeController selectedObjects] objectAtIndex:0];
[connection stepIn];
if ([connection isConnected])
[self reloadStack];
}
/**
* Forwards the message to "step out" to the connection
*/
- (IBAction)stepOut:(id)sender
{
if ([[variablesTreeController selectedObjects] count] > 0)
selectedVariable = [[variablesTreeController selectedObjects] objectAtIndex:0];
[connection stepOut];
if ([connection isConnected])
[self reloadStack];
}
/**
* Forwards the message to "step over" to the connection
*/
- (IBAction)stepOver:(id)sender
{
if ([[variablesTreeController selectedObjects] count] > 0)
selectedVariable = [[variablesTreeController selectedObjects] objectAtIndex:0];
[connection stepOver];
if ([connection isConnected])
[self reloadStack];
}
/**
* NSTableView delegate method that informs the controller that the stack selection did change and that
* we should update the source viewer
*/
- (void)tableViewSelectionDidChange:(NSNotification*)notif
{
[self updateSourceViewer];
[self expandVariables];
}
/**
* Called whenver an item is expanded. This allows us to determine if we need to fetch deeper
*/
- (void)outlineViewItemDidExpand:(NSNotification*)notif
{
NSTreeNode* node = [[notif userInfo] objectForKey:@"NSObject"];
[expandedVariables addObject:[[node representedObject] fullname]];
}
/**
* Called when an item was collapsed. This allows us to remove it from the list of expanded items
*/
- (void)outlineViewItemDidCollapse:(NSNotification*)notif
{
[expandedVariables removeObject:[[[[notif userInfo] objectForKey:@"NSObject"] representedObject] fullname]];
}
#pragma mark Private
/**
* Does the actual updating of the source viewer by reading in the file
*/
- (void)updateSourceViewer
{
id selection = [stackArrayController selection];
if ([selection valueForKey:@"filename"] == NSNoSelectionMarker)
return;
// get the filename
NSString* filename = [selection valueForKey:@"filename"];
filename = [[NSURL URLWithString:filename] path];
if ([filename isEqualToString:@""])
return;
// replace the source if necessary
if (![sourceViewer.file isEqualToString:filename])
{
NSString* source = [selection valueForKey:@"source"];
[sourceViewer setString:source asFile:filename];
NSSet* breakpoints = [NSSet setWithArray:[[BreakpointManager sharedManager] breakpointsForFile:filename]];
[[sourceViewer numberView] setMarkers:breakpoints];
}
int line = [[selection valueForKey:@"lineNumber"] intValue];
[sourceViewer setMarkedLine:line];
[sourceViewer scrollToLine:line];
[[sourceViewer textView] display];
}
/**
* Does some house keeping to the stack viewer
*/
- (void)updateStackViewer
{
[stackArrayController rearrangeObjects];
[stackArrayController setSelectionIndex:0];
[self expandVariables];
}
/**
* Expands the variables based on the stored set
*/
- (void)expandVariables
{
NSString* selection = [selectedVariable fullname];
for (int i = 0; i < [variablesOutlineView numberOfRows]; i++)
{
NSTreeNode* node = [variablesOutlineView itemAtRow:i];
NSString* fullname = [[node representedObject] fullname];
// see if it needs expanding
if ([expandedVariables containsObject:fullname])
[variablesOutlineView expandItem:node];
// select it if we had it selected before
if ([fullname isEqualToString:selection])
[variablesTreeController setSelectionIndexPath:[node indexPath]];
}
}
/**
* This updates the entire stack. Xdebug is queried to get the stack, non-shifted
* frames are reused and new ones are fetched.
*/
- (void)reloadStack
{
NSArray* stack = [connection getCurrentStack];
if (stack == nil)
return;
[stackController.stack removeAllObjects];
[stackController.stack addObjectsFromArray:stack];
[self updateStackViewer];
[self updateSourceViewer];
}
#pragma mark BSSourceView Delegate
/**
* The gutter was clicked, which indicates that a breakpoint needs to be changed
*/
- (void)gutterClickedAtLine:(int)line forFile:(NSString*)file
{
BreakpointManager* mngr = [BreakpointManager sharedManager];
if ([mngr hasBreakpointAt:line inFile:file])
{
[mngr removeBreakpointAt:line inFile:file];
}
else
{
Breakpoint* bp = [[Breakpoint alloc] initWithLine:line inFile:file];
[mngr addBreakpoint:bp];
[bp release];
}
[[sourceViewer numberView] setMarkers:[NSSet setWithArray:[mngr breakpointsForFile:file]]];
[[sourceViewer numberView] setNeedsDisplay:YES];
}
@end