1#import "FavoritesDrawerController.h" 2#import "CelestiaFavorite.h" 3#import "CelestiaFavorites.h" 4#define SAFENODE(node) ((MyTree*)((node == nil) ? [CelestiaFavorites sharedFavorites] : node)) 5#define DragDropSimplePboardType @"CelestiaFavoriteOutlineViewPboardType" 6/* 7@implementation NSMenuItem(DebuggingAPI) 8-(NSString*)description 9{ 10 return [NSString stringWithFormat:@"<MenuItem: 0x%x %@ enabled:%@ target:%@ representedObject:%@>",self,[self title],[self isEnabled]?@"YES":@"NO",[self target],[self representedObject]]; 11} 12@end 13*/ 14@implementation CelestiaFavorite(ViewAPI) 15-(NSMenuItem*)favoriteMenuItem 16{ 17 NSMenuItem* menuItem = [[[NSMenuItem alloc] initWithTitle:[self name] action:([self isFolder] ? nil : @selector(activate)) keyEquivalent:@""] autorelease]; 18 return [self setupFavoriteMenuItem:menuItem]; 19} 20-(NSMenuItem*)setupFavoriteMenuItem:(NSMenuItem*)menuItem 21{ 22 [menuItem setTarget:self]; 23 [menuItem setAction:([self isFolder] ? nil : @selector(activate))]; 24 [menuItem setTitle:[self name]]; 25 [menuItem setRepresentedObject:self]; 26 [menuItem setKeyEquivalent:@""]; 27 [menuItem setEnabled:YES]; 28 return menuItem; 29} 30@end 31 32@implementation CelestiaFavorites(ViewAPI) 33-(NSArray*)favoriteMenuItems 34{ 35 NSEnumerator *enumerator = [[self children] objectEnumerator]; 36 MyTree* node = nil; 37 NSMutableArray* menuItems = [NSMutableArray arrayWithCapacity:[[self children] count]]; 38 //NSLog(@"[CelestiaFavorites favoriteMenuItems]"); 39 while ((node = [enumerator nextObject]) != nil) 40 [menuItems addObject:[node favoriteMenuItem]]; 41 return (NSArray*)menuItems; 42} 43@end 44@implementation MyTree(ViewAPI) 45-(void)activate 46{ 47 [(CelestiaFavorite*)[self nodeValue] activate]; 48} 49-(NSMenuItem*)favoriteMenuItem 50{ 51 NSEnumerator* enumerator = nil; 52 NSMenu* subMenu = nil; 53 NSMenuItem* menuItem = [[self nodeValue] favoriteMenuItem]; 54 MyTree* node = nil; 55 //NSLog(@"[MyTree favoriteMenuItem]"); 56 if ([self isLeaf]) 57 return menuItem; 58 enumerator = [[self children] objectEnumerator]; 59 subMenu = [[[NSMenu alloc] initWithTitle:[[self nodeValue] name]] autorelease]; 60 while ((node = [enumerator nextObject]) != nil) 61 [subMenu addItem:[node favoriteMenuItem]]; 62 [menuItem setSubmenu:subMenu]; 63 [menuItem setTarget:menuItem]; 64 [menuItem setAction:@selector(submenuAction:)]; 65 return menuItem; 66} 67@end 68 69@implementation FavoritesDrawerController 70-(NSArray*)selectedNodes 71{ 72 return [outlineView allSelectedItems]; 73} 74-(NSArray*)draggedNodes 75{ 76 return draggedNodes; 77} 78-(void)activateFavorite:(CelestiaFavorite*)fav 79{ 80 id menuItem = fav; 81 if ([menuItem isKindOfClass:[NSMenuItem class]]) 82 fav = [(NSMenuItem*)menuItem representedObject]; 83 [fav activate]; 84 [outlineView deselectAll:self]; 85} 86-(void)awakeFromNib 87{ 88 draggedNodes = nil; 89 [outlineView setVerticalMotionCanBeginDrag: YES]; 90 [outlineView setTarget:self]; 91 [outlineView setDoubleAction:@selector(doubleClick:)]; 92 [outlineView registerForDraggedTypes:[NSArray arrayWithObjects:DragDropSimplePboardType, nil]]; 93 [favoritesMenu setAutoenablesItems:NO]; 94} 95-(IBAction)addNewFavorite:(id)sender 96{ 97 CelestiaFavorites* favs = [CelestiaFavorites sharedFavorites]; 98 MyTree* node = nil; 99 //NSLog(@"[FavoritesDrawerController addNewFavorite:%@]",sender); 100 node = [favs addNewFavorite:nil]; 101 102 // kludge to fix name 103 { 104 CelestiaAppCore* appCore = [CelestiaAppCore sharedAppCore]; 105 NSString* newName = [ [ [appCore simulation] selection ] briefName ]; 106 [((CelestiaFavorite*)[node nodeValue]) setName: newName]; 107 } 108 109 [[CelestiaFavorites sharedFavorites] synchronize]; 110// [self outlineView:outlineView editItem:node]; 111} 112-(IBAction)addNewFolder:(id)sender 113{ 114 CelestiaFavorites* favs = [CelestiaFavorites sharedFavorites]; 115 MyTree* node = nil; 116 //NSLog(@"[FavoritesDrawerController addNewFavorite:%@]",sender); 117 node = [favs addNewFolder:NSLocalizedString(@"untitled folder",@"")]; 118 [[CelestiaFavorites sharedFavorites] synchronize]; 119 [self outlineView:outlineView editItem:node]; 120} 121-(IBAction)doubleClick:(id)sender 122{ 123 //NSLog(@"[FavoritesDrawerController doubleClick:%@]",sender); 124 if ([outlineView numberOfSelectedRows]==1) 125 { 126 // Make sure item is not a folder 127 id theItem = [outlineView itemAtRow:[outlineView selectedRow]]; 128 if (![outlineView isExpandable: theItem]) 129 [self activateFavorite:[theItem nodeValue]]; 130 } 131} 132-(void)synchronizeFavoritesMenu 133{ 134 NSEnumerator *enumerator = [[[CelestiaFavorites sharedFavorites] favoriteMenuItems] objectEnumerator]; 135 NSMenuItem *menuItem = nil; 136 // remove old menu items 137 while ([favoritesMenu numberOfItems]>3) 138 [favoritesMenu removeItemAtIndex:[favoritesMenu numberOfItems]-1]; 139 while ((menuItem = [enumerator nextObject]) != nil) 140 [favoritesMenu addItem:menuItem]; 141 [outlineView reloadData]; 142} 143-(id)outlineView:(NSOutlineView*)olv child:(int)index ofItem:(id)item 144{ 145 id rval; 146 //NSLog(@"[FavoritesDrawerController outlineview:%@ child:%d ofItem:%@]",olv,index,item); 147 rval = [SAFENODE(item) childAtIndex:index]; 148 //NSLog(@"rval = %@",rval); 149 return rval; 150} 151-(BOOL)outlineView:(NSOutlineView*)olv isItemExpandable:(id)item 152{ 153 //NSLog(@"[FavoritesDrawerController outlineview:%@ itemIsExpandable:%@]",olv,item); 154 return ![SAFENODE(item) isLeaf]; 155} 156-(int)outlineView:(NSOutlineView *)olv numberOfChildrenOfItem:(id)item 157{ 158 int rval; 159 //NSLog(@"[FavoritesDrawerController outlineview:%@ numberOfChildrenOfItem:%@]",olv,item); 160 rval = [SAFENODE(item) numberOfChildren]; 161 //NSLog(@"rval = %d",rval); 162 return rval; 163} 164-(id)outlineView:(NSOutlineView *)olv objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item 165{ 166 id objectValue = nil; 167 //NSLog(@"[FavoritesDrawerController outlineview:%@ objectValueForTableColumn:%@ byItem:%@]",olv,tableColumn,item); 168 if([[tableColumn identifier] isEqualToString: @"NAME"]) { 169 objectValue = [(CelestiaFavorite*)[SAFENODE(item) nodeValue] name]; 170 } else if([[tableColumn identifier] isEqualToString: @"SELECTION"] && [SAFENODE(item) isLeaf]) { 171 objectValue = [(CelestiaFavorite*)[SAFENODE(item) nodeValue] selectionName]; 172 } 173 //NSLog(@"rval = %@",objectValue); 174 return objectValue; 175} 176// Optional method: needed to allow editing. 177-(void)outlineView:(NSOutlineView *)olv setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item 178{ 179 //NSLog(@"[FavoritesDrawerController outlineview:%@ setObjectValue:%@ forTableColumn:%@ byItem:%@]",olv,object,tableColumn,item); 180 if([[tableColumn identifier] isEqualToString: @"NAME"]) { 181 [(CelestiaFavorite*)[SAFENODE(item) nodeValue] setName: object]; 182 [[CelestiaFavorites sharedFavorites] synchronize]; 183 [(FavoriteInfoWindowController*)favoriteInfoWindowController updateFavorite:(CelestiaFavorite*)[SAFENODE(item) nodeValue]]; 184 185 } 186} 187 188// ================================================================ 189// NSOutlineView delegate methods. 190// ================================================================ 191- (NSMenu *)outlineView:(NSOutlineView *)olv 192contextMenuForItem:(id)item 193{ 194 MyTree* node = SAFENODE(item); 195 NSMenu* contextMenu = [favoritesContextMenu copy]; 196 NSInvocation* editInv = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:@selector(outlineView:editItem:)]]; 197 NSInvocation* delInv = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:@selector(outlineView:deleteItem:)]]; 198 NSMenuItem* editItem = [contextMenu itemAtIndex:3]; 199 NSMenuItem* delItem = [contextMenu itemAtIndex:4]; 200 NSMutableArray* arr = nil; 201 202 BOOL multipleItems = NO; 203 //NSLog(@"[FavoritesDrawerController outlineView:%@ contextMenuForItem:%@]",olv,item); 204 //NSLog(@"row = %d",[olv rowForItem:item]); 205 //[olv selectRow:[olv rowForItem:item] byExtendingSelection:NO]; 206 if ([olv numberOfSelectedRows]>1 || [olv rowForItem:item]!=[olv selectedRow]) { 207 id nextItem = nil; 208 NSEnumerator* rowEnum = [olv selectedRowEnumerator]; 209 arr = [NSMutableArray arrayWithCapacity:[olv numberOfSelectedRows]]; 210 while ((nextItem = [rowEnum nextObject]) != nil) { 211 nextItem = [olv itemAtRow:[nextItem intValue]]; 212 if (nextItem == nil) 213 continue; 214 if ([item isEqual:nextItem]) 215 multipleItems = YES; 216 [arr addObject:nextItem]; 217 } 218 } 219 if (multipleItems) 220 item = arr; 221 else 222 [olv selectRow:[olv rowForItem:item] byExtendingSelection:NO]; 223 if (![node isLeaf] || multipleItems) { 224 [contextMenu removeItemAtIndex:0]; 225 [contextMenu removeItemAtIndex:0]; 226 [contextMenu removeItemAtIndex:0]; 227 } else { 228 NSMenuItem* navItem = [contextMenu itemAtIndex:0]; 229 NSString* title = [navItem title]; 230 NSMenuItem* showItem = [contextMenu itemAtIndex:1]; 231 [showItem setTarget:favoriteInfoWindowController]; 232 [showItem setAction:@selector(showWindow:)]; 233 [(CelestiaFavorite*)[node nodeValue] setupFavoriteMenuItem:navItem]; 234 [navItem setTarget:self]; 235 [navItem setTitle:title]; 236 [navItem setAction:@selector(activateFavorite:)]; 237 } 238 if (multipleItems) { 239 [contextMenu removeItemAtIndex:0]; 240 } else { 241 [editInv setTarget:self]; 242 [editInv setSelector:@selector(outlineView:editItem:)]; 243 [editInv setArgument:&olv atIndex:2]; 244 [editInv setArgument:&item atIndex:3]; 245 [editItem setTarget:editInv]; 246 [editItem setAction:@selector(invoke)]; 247 } 248 [delInv setTarget:self]; 249 [delInv setSelector:@selector(outlineView:deleteItem:)]; 250 [delInv setArgument:&olv atIndex:2]; 251 [delInv setArgument:&item atIndex:3]; 252 [delItem setTarget:delInv]; 253 [delItem setAction:@selector(invoke)]; 254 return contextMenu; 255} 256- (void)outlineView:(NSOutlineView*)olv editItem:(id)item 257{ 258 int row = [olv rowForItem:item]; 259 //NSLog(@"[FavoritesDrawerController outlineView:%@ editItem:%@]",olv,item); 260 //NSLog(@"row = %d",row); // -1 ?? 261 if (row<0) { 262 NSLog(@"row is -1"); 263 return; 264 } 265 [olv selectRow:row byExtendingSelection:NO]; 266 [olv editColumn:[olv columnWithIdentifier:@"NAME"] row:row withEvent:nil select:YES]; 267} 268 269- (void)outlineView:(NSOutlineView*)olv deleteItem:(id)item 270{ 271 //NSLog(@"[FavoritesDrawerController outlineView:%@ deleteItem:%@]",olv,item); 272 if ([item isKindOfClass:[MyTree class]]) 273 [item removeFromParent]; 274 else 275 [(NSArray*)item makeObjectsPerformSelector:@selector(removeFromParent)]; 276 [[CelestiaFavorites sharedFavorites] synchronize]; 277 [olv deselectAll:self]; 278 [(FavoriteInfoWindowController*)favoriteInfoWindowController updateFavorite:nil]; 279} 280 281-(void)outlineViewSelectionDidChange:(NSNotification*)notification 282{ 283 NSOutlineView* olv = [notification object]; 284 if ([olv numberOfSelectedRows]==1) { 285 [(FavoriteInfoWindowController*)favoriteInfoWindowController updateFavorite:[[olv itemAtRow:[olv selectedRow]] nodeValue]]; 286 } else { 287 [(FavoriteInfoWindowController*)favoriteInfoWindowController updateFavorite:nil]; 288 } 289} 290/* 291- (BOOL)outlineView:(NSOutlineView *)olv shouldExpandItem:(id)item 292{ 293 BOOL rval; 294 NSLog(@"[FavoritesDrawerController outlineview:%@ shouldExpandItem:%@]",olv,item); 295 rval = ![SAFENODE(item) isLeaf]; 296 NSLog(@"rval = %@",((rval)?@"YES":@"NO")); 297 return rval; 298} 299*/ 300/* 301- (void)outlineView:(NSOutlineView *)olv willDisplayCell:(NSCell *)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item 302{ 303 // NSLog(@"[FavoritesDrawerController outlineview:%@ willDisplayCell:%@ forTableColumn:%@ item:0x%X]",olv,cell,tableColumn,item); 304 if ([[tableColumn identifier] isEqualToString: @"NAME"]) { 305 [cell setStringValue: [(CelestiaFavorite*)item name]]; 306 } else if ([[tableColumn identifier] isEqualToString: @"SELECTION"]) { 307 if ([(CelestiaFavorite*)item isFolder]) 308 [cell setStringValue:@""]; 309 else 310 [cell setStringValue:[(CelestiaFavorite*)item selectionName]]; 311 } 312} 313*/ 314 315// ================================================================ 316// NSOutlineView data source methods. (dragging related) 317// ================================================================ 318 319- (BOOL)outlineView:(NSOutlineView *)olv writeItems:(NSArray*)items toPasteboard:(NSPasteboard*)pboard 320{ 321 //NSLog(@"[FavoritesDrawerController outlineView:%@ writeItems:%@ toPasteboard:%@]",olv,items,pboard); 322 draggedNodes = items; // Don't retain since this is just holding temporaral drag information, and it is only used during a drag! We could put this in the pboard actually. 323 324 // Provide data for our custom type, and simple NSStrings. 325 [pboard declareTypes:[NSArray arrayWithObjects: DragDropSimplePboardType, NSStringPboardType, nil] owner:self]; 326 327 // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!. 328 [pboard setData:[NSData data] forType:DragDropSimplePboardType]; 329 330 // Put string data on the pboard... notice you candrag into TextEdit! 331 [pboard setString: [draggedNodes description] forType: NSStringPboardType]; 332 333 return YES; 334} 335 336- (unsigned int)outlineView:(NSOutlineView*)olv validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)childIndex { 337 // This method validates whether or not the proposal is a valid one. Returns NO if the drop should not be allowed. 338 MyTree *targetNode = item; 339 BOOL targetNodeIsValid = YES; 340 341 BOOL isOnDropTypeProposal = childIndex==NSOutlineViewDropOnItemIndex; 342 //NSLog(@"[FavoritesDrawerController outlineView:%@ validateDrop:%@ proposedItem:%@ proposedChildIndex:%d]",olv,info,item,childIndex); 343 // Refuse if: dropping "on" the view itself unless we have no data in the view. 344 /* 345 if (targetNode==nil && isOnDropTypeProposal==YES && [self outlineView:olv numberOfChildrenOfItem:nil]!=0) 346 targetNodeIsValid = NO; 347 */ 348 if (targetNode==nil && isOnDropTypeProposal==YES)// && [self allowOnDropOnLeaf]==NO) 349 targetNodeIsValid = NO; 350 351 if (targetNodeIsValid && [targetNode isLeaf]) 352 targetNodeIsValid = NO; 353 354 if (isOnDropTypeProposal) 355 targetNodeIsValid = NO; 356 357 // Check to make sure we don't allow a node to be inserted into one of its descendants! 358 if (targetNodeIsValid && ([info draggingSource]==outlineView) && [[info draggingPasteboard] availableTypeFromArray:[NSArray arrayWithObject: DragDropSimplePboardType]] != nil) { 359 NSArray* _draggedNodes = [[[info draggingSource] dataSource] draggedNodes]; 360 //NSEnumerator* enumerator = [_draggedNodes objectEnumerator]; 361 //MyTree* node = nil; 362 targetNodeIsValid = ![targetNode isDescendantOfNodeInArray: _draggedNodes]; 363 /* 364 while ((node = [enumerator nextObject]) != nil) 365 if ([[node parent] isEqualTo:SAFENODE(item)] && !isOnDropTypeProposal && childIndex >= [SAFENODE(item) numberOfChildren]) { 366 targetNodeIsValid = NO; 367 break; 368 } 369 */ 370 } 371 372 373 // Set the item and child index in case we computed a retargeted one. 374 [outlineView setDropItem:targetNode dropChildIndex:childIndex]; 375 376 return targetNodeIsValid ? NSDragOperationGeneric : NSDragOperationNone; 377} 378 379- (void)_performDropOperation:(id <NSDraggingInfo>)info onNode:(id)pnode atIndex:(int)childIndex { 380 // Helper method to insert dropped data into the model. 381 NSPasteboard* pboard = [info draggingPasteboard]; 382 NSMutableArray* itemsToSelect = nil; 383 MyTree* parentNode = SAFENODE(pnode); 384 //NSLog(@"[FavoritesDrawerController _performDropOperation:%@ onNode:%@ atIndex:%d]",info,pnode,childIndex); 385 //NSLog(@"parentNode = %@",parentNode); 386 if ([pboard availableTypeFromArray:[NSArray arrayWithObjects:DragDropSimplePboardType, nil]] != nil) { 387 FavoritesDrawerController *dragDataSource = [[info draggingSource] dataSource]; 388 NSArray *_draggedNodes = [MyTree minimumNodeCoverFromNodesInArray: [dragDataSource draggedNodes]]; 389 NSEnumerator *draggedNodesEnum = [_draggedNodes objectEnumerator]; 390 MyTree *_draggedNode = nil, *_draggedNodeParent = nil; 391 //MyTree* favs = [CelestiaFavorites sharedFavorites]; 392 itemsToSelect = [NSMutableArray arrayWithArray:[self selectedNodes]]; 393 /* 394 NSLog(@"dragDataSource = %@",dragDataSource); 395 NSLog(@"_draggedNodes = %@",_draggedNodes); 396 NSLog(@"itemsToSelect = %@",itemsToSelect); 397 */ 398 while ((_draggedNode = [draggedNodesEnum nextObject])) { 399 _draggedNodeParent = [_draggedNode parent]; 400 if (parentNode==_draggedNodeParent && [parentNode indexOfChild: _draggedNode]<childIndex) childIndex--; 401 [_draggedNodeParent removeChild: _draggedNode]; 402 } 403 [parentNode insertChildren: _draggedNodes atIndex: childIndex]; 404 } 405 406 [[CelestiaFavorites sharedFavorites] synchronize]; 407 [outlineView selectItems: itemsToSelect byExtendingSelection: NO]; 408} 409 410- (BOOL)outlineView:(NSOutlineView*)olv acceptDrop:(id <NSDraggingInfo>)info item:(id)targetItem childIndex:(int)childIndex { 411 MyTree * parentNode = nil; 412 //NSLog(@"[FavoritesDrawerController outlineView:%@ acceptDrop:%@ item:%@ childIndex:%d]",olv,info,targetItem,childIndex); 413 // Determine the parent to insert into and the child index to insert at. 414 parentNode = (MyTree*)(targetItem ? targetItem : [CelestiaFavorites sharedFavorites]);//SAFENODE(targetItem); 415 childIndex = (childIndex==NSOutlineViewDropOnItemIndex ? 0 : childIndex); 416 [self _performDropOperation:info onNode:parentNode atIndex:childIndex]; 417 return YES; 418} 419 420@end 421