简体   繁体   English

在NSOutlineView中拖放的问题

[英]Problems with Drag and Drop in an NSOutlineView

The problem I have is that I have an NSOutlineView with Drag and Drop (see the bottom of post for code to make drag and drop work), it works, but when I drag one row into another row the dragged row becomes a child but it also stays where it was as a parent, and when I delete either row both of them get deleted. 我遇到的问题是我有一个带拖放功能的NSOutlineView(请参阅帖子底部的代码以进行拖放工作),它可以正常工作,但是当我将一行拖到另一行时,拖动的行会成为一个孩子,但它也保持它作为父母的位置,当我删除任一行时,它们都被删除。 To show you what I mean, I have recorded the problem, here is the link - http://dvlp.me/6kya9 为了告诉你我的意思,我已经记录了这个问题,这里是链接 - http://dvlp.me/6kya9

And here is the code which makes drag and drop work in the outline view. 这是在大纲视图中进行拖放工作的代码。

Header File. 头文件。

@interface _NSControllerTreeProxy : NSObject 
{
    // opaque
}
//
// Number of objects at the root level.
//
- (unsigned int)count;

- (id)nodeAtIndexPath:(id)fp8;
- (id)objectAtIndexPath:(id)fp8;
@end

@interface _NSArrayControllerTreeNode : NSObject
{
    // opaque
}
- (unsigned int)count;
- (id)observedObject;
- (id)parentNode;
- (id)nodeAtIndexPath:(id)fp8;
- (id)subnodeAtIndex:(unsigned int)fp8;
- (BOOL)isLeaf;
- (id)indexPath;
- (id)objectAtIndexPath:(id)fp8;
@end

// some more detailed reverse engineering is available here
// http://www.blueboxmoon.com/wiki/?page=Binding%20Tree


@interface DragController : NSObject
{
    IBOutlet NSTreeController *groupTreeControl;
    IBOutlet NSOutlineView *treeTable;

    NSArray* dragType;

    _NSArrayControllerTreeNode* draggedNode;


}

- (void) resortGroups:(NSManagedObjectContext*)objectContext forParent:(NSManagedObject*)parent;
- (NSArray* ) getSubGroups:(NSManagedObjectContext*)objectContext forParent:(NSManagedObject*)parent;
- (BOOL) category:(NSManagedObject* )cat isSubCategoryOf:(NSManagedObject* ) possibleSub;

@end

#define INTERVAL 10

Implemetation File. 实施文件。

@implementation DragController
- (void)awakeFromNib {  

    dragType = [NSArray arrayWithObjects:   @"factorialDragType", nil];

    [ dragType retain ]; 

    [ treeTable registerForDraggedTypes:dragType ];
    NSSortDescriptor* sortDesc = [[NSSortDescriptor alloc] initWithKey:@"position" ascending:YES];
    [groupTreeControl setSortDescriptors:[NSArray arrayWithObject: sortDesc]];
    [ sortDesc release ];
}   


//------------------------------------
#pragma mark NSOutlineView datasource methods -- see NSOutlineViewDataSource
//---------------------------------------------------------------------------   
- (BOOL) outlineView : (NSOutlineView *) outlineView 
          writeItems : (NSArray*) items 
        toPasteboard : (NSPasteboard*) pboard {

    [ pboard declareTypes:dragType owner:self ];        
    // items is an array of _NSArrayControllerTreeNode see http://theocacao.com/document.page/130 for more info
    draggedNode = [ items objectAtIndex:0 ];

    return YES; 
}




- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index {

    _NSArrayControllerTreeNode* parentNode = item;
    _NSArrayControllerTreeNode* siblingNode;
    _NSControllerTreeProxy* proxy = [ groupTreeControl arrangedObjects ];

    NSManagedObject* draggedGroup = [ draggedNode observedObject ];

    BOOL draggingDown = NO;
    BOOL isRootLevelDrag = NO;

    // ----------------------
    // Setup comparison paths
    // -------------------------
    NSIndexPath* draggedPath = [ draggedNode indexPath ];
    NSIndexPath* siblingPath =  [ NSIndexPath indexPathWithIndex:  index  ];
    if ( parentNode == NULL ) {     
        isRootLevelDrag = YES;
    } else {
        // A non-root drag - the index value is relative to this parent's children
        siblingPath = [ [ parentNode indexPath ] indexPathByAddingIndex: index ];
    }

    // ----------------------
    // Compare paths - modify sibling path for down drags, exit for redundant drags
    // -----------------------------------------------------------------------------    
    switch ( [ draggedPath compare:siblingPath] ) {
        case NSOrderedAscending:  // reset path for down dragging
            if ( isRootLevelDrag ) {
                siblingPath = [ NSIndexPath indexPathWithIndex: index  - 1];                             
            } else {
                siblingPath = [ [ parentNode indexPath ] indexPathByAddingIndex: index - 1 ]; 
            }
            draggingDown = YES;
            break;

        case NSOrderedSame:
            return NO;
            break;               
    }

    siblingNode = [ proxy nodeAtIndexPath:siblingPath ];    

    //  NSLog(@"returning early");
    //  return NO;  // TODO robustify


    // ------------------------------------------------------------ 
    // SPECIAL CASE: Dragging to the bottom
    // ------------------------------------------------------------
    // - K                               - K                            - C                              - C
    // - - U                             - - C     OR     - U                                - F
    // - - C     ====>     - - F                    - F                              - K
    // - - F               - U              - K                              - U
    // ------------------------------------------------------------ 
    if ( isRootLevelDrag  && siblingNode == NULL ) {        
        draggingDown = YES;
        siblingPath = [ NSIndexPath indexPathWithIndex: [ proxy count ] - 1 ];          
        siblingNode = [ proxy nodeAtIndexPath:siblingPath ] ;
    }

    // ------------------------------------------------------------ 
    // Give the dragged item a position relative to it's new sibling
    // ------------------------------------------------------------ 
    NSManagedObject* sibling = [ siblingNode observedObject ];   
    NSNumber* bystanderPosition = [ sibling valueForKey:@"position"];
    int newPos =   ( draggingDown ? [ bystanderPosition intValue ]  + 1 : [ bystanderPosition intValue ]  - 1 );
    [draggedGroup setValue:[ NSNumber numberWithInt:newPos ] forKey:@"position"];   

    // ----------------------------------------------------------------------------------------------
    // Set the new parent for the dragged item, resort the position attributes and refresh the tree
    // ----------------------------------------------------------------------------------------------    
    [ draggedGroup setValue:[ parentNode observedObject ] forKey:@"parent" ];
    [ self resortGroups:[draggedGroup managedObjectContext] forParent:[ parentNode observedObject ] ];          
    [ groupTreeControl rearrangeObjects ];  
    return YES;             
}






- (NSArray* ) getSubGroups:(NSManagedObjectContext*)objectContext forParent:(NSManagedObject*)parent {
    NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"projects" inManagedObjectContext:objectContext];

    [request setEntity:entity];
    NSSortDescriptor* aSortDesc = [[NSSortDescriptor alloc] initWithKey:@"position" ascending:YES];
    [request setSortDescriptors:[NSArray arrayWithObject: aSortDesc] ];
    [aSortDesc release];

    NSPredicate* validationPredicate = [NSPredicate predicateWithFormat:@"parent == %@", parent ];

    [ request setPredicate:validationPredicate ];

    NSError *error = nil;  // TODO - check the error bozo
    return [objectContext executeFetchRequest:request error:&error];    
}




- (void) resortGroups:(NSManagedObjectContext*)objectContext forParent:(NSManagedObject*)parent {

    NSArray *array = [ self getSubGroups:objectContext forParent:parent ];

    // Reset the indexes...
    NSEnumerator *enumerator = [array objectEnumerator];
    NSManagedObject* anObject;
    int index = 0;
    while (anObject = [enumerator nextObject]) {
        // Multiply index by 10 to make dragging code easier to implement ;) ....
        [anObject setValue:[ NSNumber numberWithInt:(index * INTERVAL ) ] forKey:@"position"];    
        index++;
    }   


}

- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index {

    _NSArrayControllerTreeNode* newParent = item;

    // drags to the root are always acceptable
    if ( newParent == NULL ) {  
        return  NSDragOperationGeneric; 
    }

    // Verify that we are not dragging a parent to one of it's ancestors
    // causes a parent loop where a group of nodes point to each other and disappear
    // from the control 
    NSManagedObject* dragged = [ draggedNode observedObject ];      
    NSManagedObject* newP = [ newParent observedObject ];

    if ( [ self category:dragged isSubCategoryOf:newP ] ) {
        return NO;
    }       

    return NSDragOperationGeneric;
}

- (BOOL) category:(NSManagedObject* )cat isSubCategoryOf:(NSManagedObject* ) possibleSub {

    // Depends on your interpretation of subCategory ....
    if ( cat == possibleSub ) { return YES; }

    NSManagedObject* possSubParent = [possibleSub valueForKey:@"parent"];   

    if ( possSubParent == NULL ) {  return NO; }

    while ( possSubParent != NULL ) {       
        if ( possSubParent == cat ) { return YES;   }

        // move up the tree
        possSubParent = [possSubParent valueForKey:@"parent"];          
    }   

    return NO;
}




// This method gets called by the framework but the values from bindings are used instead
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { 
    return NULL;
}

/* 
 The following are implemented as stubs because they are required when 
 implementing an NSOutlineViewDataSource. Because we use bindings on the
 table column these methods are never called. The NSLog statements have been
 included to prove that these methods are not called.
 */
- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
    NSLog(@"numberOfChildrenOfItem");
    return 1;
}

- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
    NSLog(@"isItemExpandable");
    return NO;
}

- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item {
    NSLog(@"child of Item");
    return NULL;
}



@end

Just did a Bit of searching around, and found how to fix it. 刚刚做了一些搜索,并找到了如何解决它。 In the Attributes for the NSTreeController set the fetch predicate as parent == nil . 在NSTreeController的Attributes中,将fetch谓词设置为parent == nil

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM