Frameworks/AIUtilities Framework/Source/AITableViewAdditions.m
author Evan Schoenberg
Thu, 14 Nov 2019 16:24:17 -0500
changeset 5999 91f7da2a2338
parent 4897 5ea3bd51010d
permissions -rw-r--r--
Fixed an error compiling on Xcode 11.2.1 (and perhaps elsewhere). method_invoke needs to be cast to the signature of the original method.
 * Adium is the legal property of its developers, whose names are listed in the copyright file included
 * with this source distribution.
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

#import "AITableViewAdditions.h"
#import "AIApplicationAdditions.h"
#import <objc/objc-class.h>

@implementation NSTableView (AITableViewAdditions)

//Return an array of items which are currently selected. SourceArray should be an array from which to pull the items;
//its count must be the same as the number of rows
- (NSArray *)selectedItemsFromArray:(NSArray *)sourceArray
	NSParameterAssert([sourceArray count] >= [self numberOfRows]);

	NSMutableArray 	*itemArray = [NSMutableArray array];
	id 				item;

	//Apple wants us to do some pretty crazy stuff for selections in 10.3
	NSIndexSet *indices = [self selectedRowIndexes];
	NSUInteger bufSize = [indices count];
	NSUInteger *buf = malloc(bufSize * sizeof(NSUInteger));
	NSUInteger i;

	NSRange range = NSMakeRange([indices firstIndex], ([indices lastIndex]-[indices firstIndex]) + 1);
	[indices getIndexes:buf maxCount:bufSize inIndexRange:&range];
	for (i = 0; i != bufSize; i++) {
		if ((item = [sourceArray objectAtIndex:buf[i]])) {
			[itemArray addObject:item];


	return itemArray;

- (void)selectItemsInArray:(NSArray *)selectedItems usingSourceArray:(NSArray *)sourceArray
	if ([sourceArray count] != [self numberOfRows]) {
		NSLog(@"SourceArray is %lu; rows is %ld",(unsigned long)[sourceArray count], (long)[self numberOfRows]);

	NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet];
	NSEnumerator *enumerator = [selectedItems objectEnumerator];
	id	item;
	while ((item = [enumerator nextObject])) {
		NSUInteger i = [sourceArray indexOfObject:item];
		if (i != NSNotFound) {
			[indexes addIndex:i];
	[self selectRowIndexes:indexes byExtendingSelection:NO];


@interface AITableView : NSTableView {}

@implementation AITableView

 * @brief Load
 * Install ourself to intercept keyDown: calls so we can stick our delete handling in, and menuForEvent: calls so we can ask our delegate
+ (void)load
	//Anything you can do, I can do better...
	method_exchangeImplementations(class_getInstanceMethod([NSTableView class], @selector(keyDown:)), class_getInstanceMethod(self, @selector(keyDown:)));
	method_exchangeImplementations(class_getInstanceMethod([NSTableView class], @selector(menuForEvent:)), class_getInstanceMethod(self, @selector(menuForEvent:)));

//Filter keydowns looking for the delete key (to delete the current selection)
- (void)keyDown:(NSEvent *)theEvent
	NSString	*charString = [theEvent charactersIgnoringModifiers];
	unichar		pressedChar = 0;

	//Get the pressed character
	if ([charString length] == 1) pressedChar = [charString characterAtIndex:0];

	//Check if 'delete' was pressed
	if (pressedChar == NSDeleteFunctionKey || pressedChar == NSBackspaceCharacter || pressedChar == NSDeleteCharacter) { //Delete
		if ([[self delegate] respondsToSelector:@selector(tableViewDeleteSelectedRows:)])
			[(id <AITableViewDelegate>)[self delegate] tableViewDeleteSelectedRows:self]; //Delete the selection
	} else {
		//Pass the key event on to the unswizzled impl
        static void (*_key_down_method_invoke)(id, Method, NSEvent *) = (void (*)(id, Method, NSEvent *)) method_invoke;
		_key_down_method_invoke(self, class_getInstanceMethod([AITableView class], @selector(keyDown:)), theEvent);

//Allow our delegate to specify context menus
- (NSMenu *)menuForEvent:(NSEvent *)theEvent
	if ([[self delegate] respondsToSelector:@selector(tableView:menuForEvent:)])
		return [(id<AITableViewDelegate>)[self delegate] tableView:self menuForEvent:theEvent];
    static NSMenu * (*_menu_for_event_method_invoke)(id, Method, NSEvent *) = (NSMenu * (*)(id, Method, NSEvent *)) method_invoke;
	return _menu_for_event_method_invoke(self, class_getInstanceMethod([AITableView class], @selector(menuForEvent:)), theEvent);