From df5bba18e6c1ce6ad823ae8c2af088f6defd4e76 Mon Sep 17 00:00:00 2001 From: Patrick Wardle Date: Fri, 19 Jun 2015 20:43:59 -1000 Subject: [PATCH] fixed table issues (between outline an normal view) code cleanup task lookup by row.tag --- AppDelegate.h | 16 +- AppDelegate.m | 196 ++++----- Consts.h | 3 + ItemView.m | 7 + KKRow.h | 1 + TaskEnumerator.h | 8 + TaskEnumerator.m | 81 +++- .../xcdebugger/Breakpoints_v2.xcbkptlist | 326 ++++----------- TaskTableController.h | 7 + TaskTableController.m | 377 ++++++++---------- kkRowCell.h | 3 + kkRowCell.m | 2 + main.m | 2 + 13 files changed, 432 insertions(+), 597 deletions(-) diff --git a/AppDelegate.h b/AppDelegate.h index e015e38..2c5a6b4 100755 --- a/AppDelegate.h +++ b/AppDelegate.h @@ -32,7 +32,7 @@ } //@property (readonly) NSViewController *currentViewController; -@property(nonatomic, retain)NSViewController *currentViewController; +//@property(nonatomic, retain)NSViewController *currentViewController; //(current) bottom view controller @property(nonatomic, retain)TaskTableController *bottomViewController; @@ -125,20 +125,10 @@ // ->provide mouse over effects -(void)initTrackingAreas; -//create instances of all registered plugins -//-(NSMutableArray*)instantiatePlugins; - -//callback method, invoked by plugin(s) when item is found -// ->update the 'total' count and the item table (if active plugin is selected in UI) --(void)itemFound:(Task*)task; - //callback method, invoked by virus total when plugin's items have been processed // ->reload table if plugin matches active plugin //-(void)itemsProcessed:(PluginBase*)plugin; -//callback method, invoked by category table controller when user selects category -// ->save the selected plugin & reload the item table --(void)categorySelected:(NSUInteger)rowIndex; //callback when user has updated prefs // ->reload table, etc @@ -182,5 +172,9 @@ //begin task enumeration -(void)exploreTasks; +//sort tasks +// ->either name (flat view) or pid (tree view) +-(void)sortTasksForView:(OrderedDictionary*)tasks; + @end diff --git a/AppDelegate.m b/AppDelegate.m index 4baf3a5..276a9a4 100755 --- a/AppDelegate.m +++ b/AppDelegate.m @@ -40,7 +40,7 @@ @synthesize topPane; @synthesize taskEnumerator; //@synthesize treeViewController; -@synthesize currentViewController; +//@synthesize currentViewController; @synthesize viewSelector; @@ -317,10 +317,10 @@ -(void)changeViewController { //remove old view - if([self.currentViewController view] != nil) + if([self.taskTableController view] != nil) { //remove - [[self.currentViewController view] removeFromSuperview]; + [[self.taskTableController view] removeFromSuperview]; } switch(self.taskViewFormat) @@ -329,35 +329,39 @@ { //alloc/init taskTableController = [[TaskTableController alloc] initWithNibName:@"FlatView" bundle:nil]; - if(self.taskTableController != nil) + /*if(self.taskTableController != nil) { //update iVar self.currentViewController = self.taskTableController; - } + }*/ break; } case TREE_VIEW: { //alloc/init taskTableController = [[TaskTableController alloc] initWithNibName:@"TreeView" bundle:nil]; - if(self.taskTableController != nil) + + + + /*if(self.taskTableController != nil) { //update iVar self.currentViewController = self.taskTableController; - } + }*/ break; } } //add subview - [self.topPane addSubview:[self.currentViewController view]]; + [self.topPane addSubview:[self.taskTableController view]]; //set frame - [[self.currentViewController view] setFrame:[self.topPane bounds]]; + [[self.taskTableController view] setFrame:[self.topPane bounds]]; return; } +//TODO: add checks to make sure or handle switch to tree view!!!! //reload (to re-draw) a specific row in table -(void)reloadRow:(Task*)task item:(ItemBase*)item pane:(NSUInteger)pane { @@ -371,32 +375,56 @@ if(PANE_TOP == pane) { //get row that task is loaded in - tableView = [((id)self.currentViewController) itemView]; + tableView = [((id)self.taskTableController) itemView]; - //get index where task is - // TODO: doesn't account for filtering, etc!!! - row = [self.taskEnumerator.tasks indexOfKey:task.pid]; - if(NSNotFound == row) + //reloadItem + // ->flat view + if(YES != [tableView isKindOfClass:[NSOutlineView class]]) { - //bail - goto bail; + //get index where task is + // TODO: doesn't account for filtering, etc!!! + row = [self.taskEnumerator.tasks indexOfKey:task.pid]; + if(NSNotFound == row) + { + //bail + goto bail; + } + + //reload just the row + // ->on main thread + dispatch_async(dispatch_get_main_queue(), ^{ + + //begin updates + [tableView beginUpdates]; + + //reload row + [tableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:(row)] columnIndexes:[NSIndexSet indexSetWithIndex:0]]; + + //end updates + [tableView endUpdates]; + + }); } - - //dbg msg - //NSLog(@"updating row: %d", row); - - //reload just the row - // ->on main thread - dispatch_async(dispatch_get_main_queue(), ^{ + //reload item + // ->tree view + else + { + //begin updates + [tableView beginUpdates]; - //reload row - [tableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:(row)] columnIndexes:[NSIndexSet indexSetWithIndex:0]]; + //reload + [(NSOutlineView*)tableView reloadItem:task]; - }); + //end updates + [tableView endUpdates]; + } } + //TODO: bottom pane + + //bail bail: @@ -410,10 +438,11 @@ bail: //tag NSUInteger segmentTag = 0; + /* if(YES == [task.binary.name isEqualToString:@"launchd"]) { NSLog(@"asdf"); - } + }*/ //get segment tag segmentTag = [[self.bottomPaneBtn selectedCell] tagForSegment:[self.bottomPaneBtn selectedSegment]]; @@ -614,25 +643,35 @@ bail: return; } -//reload task table -// invoke custom refresh method on main thread --(void)reloadTaskTable +//TODO: don't think we need to sort by pid, since tree view, just uses kids!!! +//sort tasks +// ->either name (flat view) or pid (tree view) +-(void)sortTasksForView:(OrderedDictionary*)tasks; { //sort tasks // ->flat view, sort by name if(FLAT_VIEW == self.taskViewFormat) { //sort - [self.taskEnumerator.tasks sort:SORT_BY_NAME]; + [tasks sort:SORT_BY_NAME]; } //sort tasks // ->tree view, sort by pid else { //sort - [self.taskEnumerator.tasks sort:SORT_BY_PID]; + [tasks sort:SORT_BY_PID]; } + return; +} + +//reload task table +// invoke custom refresh method on main thread +-(void)reloadTaskTable +{ + //sort tasks + [self sortTasksForView:self.taskEnumerator.tasks]; //when exec'ing on background thread // ->exec on main thread @@ -643,7 +682,7 @@ bail: //refresh //[self.taskTableController refresh]; - [(id)self.currentViewController refresh]; + [(id)self.taskTableController refresh]; }); } @@ -655,86 +694,12 @@ bail: //TODO: currentViewCont?!? //refresh //[(id)self.taskTableController refresh]; - [(id)self.currentViewController refresh]; + [(id)self.taskTableController refresh]; } return; } -//callback method, invoked by plugin(s) when item is found -// ->update the 'total' count and the item table (if it's selected) --(void)itemFound:(Task*)task -{ - //active plugin object - //PluginBase* activePlugin = nil; - - //item - //ItemBase* uncoveredItem = nil; - - //item backing item table - // ->depending on flilter status, either all items, or just known ones - //NSArray* = nil; - - //grab active plugin object - //activePlugin = self.plugins[self.activePluginIndex]; - - //extract uncovered item - // ->either File obj, Command obj, or Extension obj - //uncoveredItem = [activePlugin.allItems lastObject]; - - //only show refresh table if - // a) filter is not enabled (e.g. show all) - // b) filtering is enable, but item is unknown - //if( (YES == self.prefsWindowController.showTrustedItems) || - // ((YES != self.prefsWindowController.showTrustedItems) && (YES != uncoveredItem.isTrusted)) ) - //{ - /* - //set table item array - // ->case: all - if(YES == self.prefsWindowController.showTrustedItems) - { - //set to all items - tableItems = activePlugin.allItems; - } - //set table item array - // ->case: unknown items - else - { - //set to unknown items - tableItems = activePlugin.unknownItems; - } - */ - //reload category table (on main thread) - // ->this will result in the 'total' being updated - dispatch_sync(dispatch_get_main_queue(), ^{ - - //begin updates - [[((id)self.currentViewController) itemView] beginUpdates]; - - //update category table row - // ->this will result in the 'total' being updated - //[self.categoryTableController.categoryTableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:self.activePluginIndex] columnIndexes:[NSIndexSet indexSetWithIndex:0]]; - - //if this plugin is currently the selected one (in the category table) - // ->update the item row - //if(self.selectedPlugin == activePlugin) - //{ - //first tell item table the # of items have changed - //[self.taskTableController.itemView noteNumberOfRowsChanged]; - [[((id)self.currentViewController) itemView] noteNumberOfRowsChanged]; - - //reload just the new row - // [self.itemTableController.itemTableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:(tableItems.count-1)] columnIndexes:[NSIndexSet indexSetWithIndex:0]]; - //} - - //end updates - //[self.taskTableController.itemView endUpdates]; - [[((id)self.currentViewController) itemView] beginUpdates]; - }); - //} - - return; -} /* //callback method, invoked by virus total when plugin's items have been processed @@ -813,21 +778,6 @@ bail: */ -//callback method, invoked by category table controller when OS/user clicks a row -// ->lookup/save the selected plugin & reload the item table --(void)categorySelected:(NSUInteger)rowIndex -{ - //save selected plugin - //self.selectedPlugin = self.plugins[rowIndex]; - - //scroll to top of item table - [self.taskTableController scrollToTop]; - - //reload item table - [self.taskTableController.itemView reloadData]; - - return; -} //callback when user has updated prefs // ->reload table, etc diff --git a/Consts.h b/Consts.h index 4d2488b..a11e27d 100644 --- a/Consts.h +++ b/Consts.h @@ -300,6 +300,9 @@ #define SORT_BY_NAME 0x1 +//delta for pid tag +#define PID_TAG_DELTA 1000 + diff --git a/ItemView.m b/ItemView.m index bf61b04..a97634f 100644 --- a/ItemView.m +++ b/ItemView.m @@ -10,6 +10,7 @@ #import "ItemView.h" #import "VTButton.h" #import "AppDelegate.h" +#import "kkRowCell.h" #import "3rdParty/OrderedDictionary.h" //create customize item view @@ -68,6 +69,10 @@ NSTableCellView* createItemView(NSTableView* tableView, id owner, id item) { //create & config view itemCell = createTaskView(tableView, owner, item); + + //set tag + // ->task pid (allows lookup later) + ((kkRowCell*)itemCell).tag = [((Task*)item).pid integerValue] + PID_TAG_DELTA; } //logic to create dylib view @@ -91,6 +96,8 @@ NSTableCellView* createItemView(NSTableView* tableView, id owner, id item) itemCell = createNetworkView(tableView, owner, item); } + + return itemCell; diff --git a/KKRow.h b/KKRow.h index e4c4c93..76c902e 100644 --- a/KKRow.h +++ b/KKRow.h @@ -10,4 +10,5 @@ @interface KKRow : NSTableRowView + @end diff --git a/TaskEnumerator.h b/TaskEnumerator.h index 5fec2a9..b956adb 100644 --- a/TaskEnumerator.h +++ b/TaskEnumerator.h @@ -61,5 +61,13 @@ // ->generally yes, unless the first enumeration (of all tasks) is not complete //-(BOOL)shouldEnumDylibs; +//remove a task +// ->contain extra logic to remove children, etc +-(void)removeTask:(Task*)task; + +//given a task +// ->get list of all child pids +-(void)getAllChildren:(Task*)parent children:(NSMutableArray*)children; + @end diff --git a/TaskEnumerator.m b/TaskEnumerator.m index 0516a57..cdd0446 100644 --- a/TaskEnumerator.m +++ b/TaskEnumerator.m @@ -130,14 +130,15 @@ continue; } - //enumerate task's dylibs - //[newTask enumerateDylibs:self.xpcConnection allDylibs:self.dylibs]; - //add new task [self.tasks setObject:newTask forKey:newTask.pid]; }//add new tasks + //sort tasks + // ->ensures that signing info etc w/ be generated for (top) visible tasks + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) sortTasksForView:newTasks]; + //reload task table [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadTaskTable]; @@ -294,7 +295,17 @@ bail: NSUInteger childIndex = 0; //init comparator - comparator = ^(id obj1, id obj2) { return NSOrderedSame; }; + // ->sort pids + comparator = ^(id obj1, id obj2) + { + if (obj1 < obj2) + return NSOrderedAscending; + + if (obj1 > obj2) + return NSOrderedDescending; + + return NSOrderedSame; + }; //interate over all task // ->insert task into parent's *ordered* child array @@ -327,4 +338,66 @@ bail: return; } +//remove a task +// ->contain extra logic to remove children, etc +-(void)removeTask:(Task*)task +{ + //parent + Task* parent = nil; + + //children + NSMutableArray* children = nil; + + //alloc array for children + children = [NSMutableArray array]; + + //get all children + [self getAllChildren:task children:children]; + + //remove all child tasks + for(NSNumber* childPid in children) + { + //remove + [self.tasks removeObjectForKey:childPid]; + } + + //remove task + [self.tasks removeObjectForKey:task.pid]; + + //get parent + parent = [self.tasks objectForKey:task.ppid]; + + //remove task from parent's list of children + [parent.children removeObject:task.pid]; + + return; + +} + +//given a task +// ->get list of all child pids +-(void)getAllChildren:(Task*)parent children:(NSMutableArray*)children +{ + //child task + Task* childTask = nil; + + //add parent's children + [children addObjectsFromArray:parent.children]; + + for(NSNumber* childPid in parent.children) + { + //get child task + childTask = self.tasks[childPid]; + + if(0 != [childTask.children count]) + { + //recurse + [self getAllChildren:childTask children:children]; + } + } + + return; +} + + @end diff --git a/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index b0bb648..ca56366 100644 --- a/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -19,29 +19,13 @@ shouldBeEnabled = "No" ignoreCount = "0" continueAfterRunningActions = "No" - filePath = "Task.m" - timestampString = "456217349.500536" + filePath = "TaskEnumerator.m" + timestampString = "456469787.454434" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "551" - endingLineNumber = "551" - landmarkName = "-enumerateNetworking:" - landmarkType = "5"> - - - - @@ -52,236 +36,12 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "456302892.352739" + timestampString = "456474906.073599" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "408" - endingLineNumber = "408" - landmarkName = "-reloadBottomPane:itemView:" - landmarkType = "5"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -292,28 +52,76 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "TaskTableController.m" - timestampString = "456305690.622084" + timestampString = "456474649.232255" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "40" - endingLineNumber = "40" - landmarkName = "-awakeFromNib" + startingLineNumber = "711" + endingLineNumber = "711" + landmarkName = "-refresh" landmarkType = "5"> + + + + + + + + + + + + diff --git a/TaskTableController.h b/TaskTableController.h index 432f354..8bb5bba 100644 --- a/TaskTableController.h +++ b/TaskTableController.h @@ -20,6 +20,9 @@ } +//flag for first time init's +@property BOOL didInit; + //tasks // ->updated by task enumerator //@property(nonatomic, retain)OrderedDictionary* tasks; @@ -77,5 +80,9 @@ // ->for now, just looks at 'tableItems' iVar -(BOOL)isTopPane; +//handle when user clicks row +// ->update bottom pane w/ task's dylibs/files/etc +-(void)handleRowSelection; + @end diff --git a/TaskTableController.m b/TaskTableController.m index 38e59c3..ddbda8b 100644 --- a/TaskTableController.m +++ b/TaskTableController.m @@ -25,6 +25,8 @@ #import +//TODO: need to do some sync or logic to handle swaps -otherwise crashes!!! + @implementation TaskTableController @synthesize itemView; @@ -34,13 +36,33 @@ @synthesize vtWindowController; @synthesize infoWindowController; - +@synthesize didInit; //@synthesize tasks; + +//TODO: called many timesss -(void)awakeFromNib { - self.selectedRow = -1; + if(YES != didInit) + { + self.selectedRow = -1; + + //for outline (tree) view + // ->expand + if(YES == [self.itemView isKindOfClass:[NSOutlineView class]]) + { + [(NSOutlineView*)self.itemView expandItem:nil expandChildren:YES]; + } + + //set flag + self.didInit = YES; + + } + + //self.selectedRow = -1; + + /* //for outline (tree) view // ->expand if(YES == [self.itemView isKindOfClass:[NSOutlineView class]]) @@ -50,9 +72,9 @@ //TODO: do this in IB? //[itemView setTarget:self]; } + */ - NSLog(@"item view: %@", self.itemView); - + /* NSString *title = @"[ all tasks ]"; NSTableColumn *yourColumn = self.itemView.tableColumns.lastObject; @@ -114,103 +136,9 @@ // ->only care about for top pane, trigger load bottom view -(void)tableViewSelectionDidChange:(NSNotification *)notification { - //tasks - __block OrderedDictionary* tasks = nil; + //handle selection + [self handleRowSelection]; - //task - Task* task = nil; - - //newly selected row (index) - __block NSInteger newlySelectedRow = -1; - - //selected row cell - NSTableCellView* selectedView = nil; - - //ignore events for bottom-pane - if(YES == self.isBottomPane) - { - //ignore - goto bail; - } - - //get index of newly selected row - newlySelectedRow = [self.itemView selectedRow]; - - //get row that's about to be selected - selectedView = [self.itemView viewAtColumn:0 row:newlySelectedRow makeIfNecessary:YES]; - - //grab task - task = [self taskForRow:nil]; - - //check if task is dead - if(YES != isAlive([task.pid intValue])) - { - //make it red - ((kkRowCell*)selectedView).color = [NSColor redColor]; - - //draw - [selectedView setNeedsDisplay:YES]; - - //make hide it after 1/4th second - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - - //reset color - ((kkRowCell*)selectedView).color = nil; - - //get tasks - tasks = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.tasks; - - //remove (now dead) task - [tasks removeObjectForKey:task.pid]; - - //reload table - [self.itemView reloadData]; - - //sanity check - // ->make sure removed task wasn't last - if(tasks.count <= newlySelectedRow) - { - //reset to (new) last - newlySelectedRow = tasks.count - 1; - } - //sanity check - // ->reset to first item on other errors - else if(-1 == newlySelectedRow) - { - //set to first - newlySelectedRow = 0; - } - - //re-select - [self.itemView selectRowIndexes:[NSIndexSet indexSetWithIndex:newlySelectedRow] byExtendingSelection:NO]; - - }); - - //bail - goto bail; - - } - - //ignore if row selection didn't change - if([self.itemView selectedRow] == self.selectedRow) - { - //ignore - goto bail; - } - - //save newly selected row index - self.selectedRow = [self.itemView selectedRow]; - - //save currently selected task - ((AppDelegate*)[[NSApplication sharedApplication] delegate]).currentTask = task; - - //reload bottom pane - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) selectBottomPaneContent:nil]; - - -//bail -bail: - return; } @@ -811,20 +739,31 @@ bail: // ->but only if task still exists (e.g. didn't exit) if(NSNotFound != taskIndex) { + //begin updates + [self.itemView beginUpdates]; + //(re)select [self.itemView selectRowIndexes:[NSIndexSet indexSetWithIndex:taskIndex] byExtendingSelection:NO]; + + //end updates + [self.itemView endUpdates]; } } return; } + +//TODO: make sure this works for outline view!!! //grab a task at a row -(Task*)taskForRow:(id)sender { //index of row NSInteger taskRow = 0; + //selected row cell + NSTableCellView* rowView = nil; + //tasks OrderedDictionary* tasks = nil; @@ -845,6 +784,7 @@ bail: { //grab row taskRow = [self.itemView selectedRow]; + } //sanity check(s) @@ -858,7 +798,14 @@ bail: //get task object // ->by index to get key, then by key - task = tasks[[tasks keyAtIndex:taskRow]]; + //task = tasks[[tasks keyAtIndex:taskRow]]; + + //get row that's about to be selected + rowView = [self.itemView viewAtColumn:0 row:taskRow makeIfNecessary:YES]; + + //extract task + // ->pid of task is view's id :) + task = tasks[[NSNumber numberWithInteger:(rowView.tag - PID_TAG_DELTA)]]; //bail bail: @@ -866,7 +813,7 @@ bail: return task; } - + //grab an item at a row -(ItemBase*)itemForRow:(id)sender { @@ -1122,7 +1069,7 @@ bail: //return !item ? YES : [[item children] count] != 0; } -//TODO: sort children!!! + //return child -(id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item @@ -1153,105 +1100,12 @@ bail: } -//TODO: combine logic - and make taskForRow method work for flat and tree views :) +//TODO: combine logic - +//TODO: when combine, use pid of task is view's id - to lookup task :) - (void)outlineViewSelectionDidChange:(NSNotification *)notification { - //tasks - __block OrderedDictionary* tasks = nil; - - //task - Task* task = nil; - - //newly selected row (index) - __block NSInteger newlySelectedRow = -1; - - //selected row cell - NSTableCellView* selectedView = nil; - - //ignore events for bottom-pane - if(YES == self.isBottomPane) - { - //ignore - goto bail; - } - - //get index of newly selected row - newlySelectedRow = [self.itemView selectedRow]; - - //get row that's about to be selected - selectedView = [self.itemView viewAtColumn:0 row:newlySelectedRow makeIfNecessary:YES]; - - //grab task - task = [self taskForRow:nil]; - - //check if task is dead - if(YES != isAlive([task.pid intValue])) - { - //make it red - ((kkRowCell*)selectedView).color = [NSColor redColor]; - - //draw - [selectedView setNeedsDisplay:YES]; - - //make hide it after 1/4th second - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - - //reset color - ((kkRowCell*)selectedView).color = nil; - - //get tasks - tasks = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.tasks; - - //remove (now dead) task - [tasks removeObjectForKey:task.pid]; - - //reload table - [self.itemView reloadData]; - - //sanity check - // ->make sure removed task wasn't last - if(tasks.count <= newlySelectedRow) - { - //reset to (new) last - newlySelectedRow = tasks.count - 1; - } - //sanity check - // ->reset to first item on other errors - else if(-1 == newlySelectedRow) - { - //set to first - newlySelectedRow = 0; - } - - //re-select - [self.itemView selectRowIndexes:[NSIndexSet indexSetWithIndex:newlySelectedRow] byExtendingSelection:NO]; - - }); - - //bail - goto bail; - - } - - //ignore if row selection didn't change - if([self.itemView selectedRow] == self.selectedRow) - { - //ignore - goto bail; - } - - //save newly selected row index - self.selectedRow = [self.itemView selectedRow]; - - //save currently selected task - ((AppDelegate*)[[NSApplication sharedApplication] delegate]).currentTask = task; - - //reload bottom pane - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) selectBottomPaneContent:nil]; - - - //bail -bail: + //handle selection + [self handleRowSelection]; return; } @@ -1290,4 +1144,127 @@ bail: return rowView; } +/* LOGIC FOR BOTH */ + +//handle when user clicks row +// ->update bottom pane w/ task's dylibs/files/etc +-(void)handleRowSelection +{ + //tasks + __block OrderedDictionary* tasks = nil; + + //task + Task* task = nil; + + //newly selected row (index) + __block NSInteger newlySelectedRow = -1; + + //selected row cell + NSTableCellView* selectedView = nil; + + //ignore events for bottom-pane + if(YES == self.isBottomPane) + { + //ignore + goto bail; + } + + //grab tasks + tasks = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.tasks; + + //get index of newly selected row + newlySelectedRow = [self.itemView selectedRow]; + + //sanity check + if( (-1 == newlySelectedRow) || + (newlySelectedRow >= tasks.count) ) + { + //bail + goto bail; + } + + //get row that's about to be selected + selectedView = [self.itemView viewAtColumn:0 row:newlySelectedRow makeIfNecessary:YES]; + + //extract task + // ->pid of task is view's id :) + task = tasks[[NSNumber numberWithInteger:(selectedView.tag - PID_TAG_DELTA)]]; + + //check if task is dead + if(YES != isAlive([task.pid intValue])) + { + //make it red + ((kkRowCell*)selectedView).color = [NSColor redColor]; + + //draw + [selectedView setNeedsDisplay:YES]; + + //make hide it after 1/4th second + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + + //reset color + ((kkRowCell*)selectedView).color = nil; + + //remove task + [((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator removeTask:task]; + + //reload table + [self.itemView reloadData]; + + //sanity check + // ->make sure removed task wasn't last + if(tasks.count <= newlySelectedRow) + { + //reset to (new) last + newlySelectedRow = tasks.count - 1; + } + //sanity check + // ->reset to first item on other errors + else if(-1 == newlySelectedRow) + { + //set to first + newlySelectedRow = 0; + } + + //begin updates + [self.itemView beginUpdates]; + + //re-select + [self.itemView selectRowIndexes:[NSIndexSet indexSetWithIndex:newlySelectedRow] byExtendingSelection:NO]; + + //end updates + [self.itemView endUpdates]; + }); + + //bail + goto bail; + + } + + + //ignore if row selection didn't change + if([self.itemView selectedRow] == self.selectedRow) + { + //ignore + goto bail; + } + + //save newly selected row index + self.selectedRow = [self.itemView selectedRow]; + + //save currently selected task + ((AppDelegate*)[[NSApplication sharedApplication] delegate]).currentTask = task; + + //reload bottom pane + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) selectBottomPaneContent:nil]; + + +//bail +bail: + + ; + +} + + @end diff --git a/kkRowCell.h b/kkRowCell.h index c6b01bf..067a6d1 100644 --- a/kkRowCell.h +++ b/kkRowCell.h @@ -15,6 +15,9 @@ /* PROPERTIES */ +//tag +@property NSInteger tag; + //color @property(nonatomic, retain)NSColor* color; diff --git a/kkRowCell.m b/kkRowCell.m index 639bba1..a3bbea1 100644 --- a/kkRowCell.m +++ b/kkRowCell.m @@ -10,6 +10,8 @@ @implementation kkRowCell +@synthesize tag; + //draw method - (void)drawRect:(NSRect)dirtyRect diff --git a/main.m b/main.m index 8e94c27..110a5cf 100755 --- a/main.m +++ b/main.m @@ -8,6 +8,8 @@ #import + +//TODO: add [tableView beginUpdates]; int main(int argc, char *argv[]) { return NSApplicationMain(argc, (const char **)argv);