Logo Search packages:      
Sourcecode: adun.app version File versions  Download package

ULOutlineViewDelegate.m

/*
   Project: UL

   Copyright (C) 2006 Michael Johnston & Jordi Villa-Freixa

   Author: Michael Johnston

   This application 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 application 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
   Library General Public License for more details.

   You should have received a copy of the GNU General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/

#include "ULOutlineViewDelegate.h"

/*********

Base class for the object used to display
an Adun XML options file in an Outline view.

This should be replaced (at least in part) by
an ULOptionMenu class ?

********/

@class ULSelectionWrapper;
@class ULNodeWrapper;
@class ULOptionsLeaf;

@interface ULOutlineViewWrapper: NSObject
{
      int noChildren;
      NSMutableArray* children;
      id parent;
      id identifier;
      id wrappedObject;
      BOOL isExpandable;
      id value;
}

- (id) initWithObject: (id) object parent: (id) parent identifier: (id) aName;
- (int) numberChildren;
- (id) parent;
- (id) identifier;
- (id) childAtIndex: (int) index;
- (id) value;
- (id) wrappedObject;
- (void) childWasSelected: (id) child;

@end


@implementation ULOutlineViewWrapper

+ (id) wrapperForObject: (id) object parent: (id) par identifier: (id) aName
{
      NSDebugLLog(@"ULOptionsViewController", @"Wrapper for object %@ with id %@", object, aName);

      if([object isKindOfClass: [NSDictionary class]])
      {
            if([object objectForKey: @"Selection"] != nil)
            {
                  return [[[ULSelectionWrapper alloc] 
                        initWithObject: object
                        parent: par
                        identifier: aName] autorelease];
            }
            else
            {
                   return [[[ULNodeWrapper alloc] 
                        initWithObject: object 
                        parent: par
                        identifier: aName] autorelease];
            }
      }
      else
            return [[[ULOptionsLeaf alloc] 
                        initWithObject: object 
                        parent: par
                        identifier: aName] autorelease];
}

- (id) initWithObject: (id) object parent: (id) parent identifier: (id) aName;
{
      return self;
}

- (int) numberChildren { return noChildren; }

- (id) parent { return parent; }

- (id) identifier { return identifier; }

- (id) childAtIndex: (int) index { return [children objectAtIndex: index]; }

- (BOOL) isExpandable { return isExpandable; }

- (id) value { return value; }

- (void) setValue: (id) aValue
{
      [value release];
      value = aValue;
      [value retain];
}

- (id) wrappedObject { return wrappedObject; }

- (void) childWasSelected: (id) child
{
      //does nothing
}

- (void) dealloc
{
      [children release];
      [parent release];
      [identifier release];
      [value release];
}

@end

/**
ULNodeWrapper wraps  non-selection NSDictionaries
of an Adun options file
*/

00138 @interface ULNodeWrapper: ULOutlineViewWrapper
- (void) setValue: (id) aValue forChild: (id) child;
- (NSString*) childForIdentifier: (NSString*)anId;
@end

@implementation ULNodeWrapper

- (void) _setChildren
{
      id anEnum, key;
      
      noChildren = [wrappedObject count];
      anEnum = [wrappedObject keyEnumerator];
      while(key = [anEnum nextObject])
      {
            [children addObject: 
                  [ULOutlineViewWrapper wrapperForObject: [wrappedObject valueForKey: key] 
                        parent: self
                        identifier: key]];
      }

}

- (id) initWithObject: (id) object parent: (id) par identifier: (id) aName
{
      children = [[NSMutableArray arrayWithCapacity: 1] retain];
      isExpandable = YES;
      value = @"";
      wrappedObject = object;
      parent = [par retain];
      identifier = [aName retain];
      [value retain];
      [self _setChildren];    

      return self;
}

- (void) setValue: (id) aValue forChild: (id) child
{
      NSString* key;

      if(![child isKindOfClass: [NSDictionary class]])
      {
            key = [child identifier];
            [wrappedObject setValue: aValue forKey: key];
            [child setValue: aValue];
      }
}

- (NSString*) childForIdentifier: (NSString*) anId
{
      NSEnumerator* childEnum;
      id child;

      childEnum = [children objectEnumerator];
      while(child = [childEnum nextObject])
            if([[child identifier] isEqual: anId])
                  return child;

      return nil;
}

- (void) dealloc
{
      [super dealloc];
}

@end

/**
ULSelectionWrapper wraps  selection NSDictionaries
of an Adun options file. These are dictionaries that contain
the key @"Selection" and @"Type"
*/

00213 @interface ULSelectionWrapper: ULNodeWrapper
{
      BOOL isMultiple;
}
- (void) setValue: (id) aValue forChild: (id) child;
@end

@implementation ULSelectionWrapper

- (id) _setChildrenForArray
{
      int i;
      id anEnum, key;
      id object, selectedValue, choiceArray;
      NSString* defaultSelection;
      NSEnumerator* selectionEnum;

      defaultSelection = nil;
      
      //the keys are not in a defined order
      //therefore find the choice array first
      //then deal with the default selection

      anEnum = [wrappedObject keyEnumerator];
      while(key = [anEnum nextObject])
            if(![key isEqual: @"Selection"] && ![key isEqual: @"Type"])
                  choiceArray = [wrappedObject valueForKey: key];
                  for(i=0; i<[choiceArray count]; i++)
                        [children addObject: 
                              [ULOutlineViewWrapper wrapperForObject: @""
                                    parent: self
                                    identifier: [choiceArray objectAtIndex: i]]];
                  
      
      anEnum = [wrappedObject keyEnumerator];
      while(key = [anEnum nextObject])
      {
            if([key isEqual: @"Selection"])
            {
                  object = [wrappedObject valueForKey: @"Selection"];
                  NSDebugLLog(@"ULOptionsViewController", @"Selection value is %@", object);
                  if(![object isKindOfClass: [NSArray class]])
                        [NSException raise: NSInternalInconsistencyException
                              format: @"Invalid structure for options selection object. Must be an array"];
                  if(![object count] == 0)
                  {
                        //check the selection(s) is/are in the array 
                        
                        selectionEnum = [object objectEnumerator];
                        while(selectedValue = [selectionEnum nextObject])
                        {
                              if(![choiceArray containsObject: selectedValue])
                              {
                                    [NSException raise: NSInternalInconsistencyException
                                          format: [NSString stringWithFormat:
                                           @"Selected value %@ is not in object", selectedValue]];
                              }
                              else
                              {           
                                    //FIXME: no support for multiple selections!
                                    defaultSelection = [[object objectAtIndex: 0] retain];
                              }
                        }     
                  }
            }
            else if([key isEqual: @"Type"])
                  if([[wrappedObject valueForKey: @"Type"] isEqual: @"Single"])
                        isMultiple = NO;
      }

      return defaultSelection;
}

- (id) _setChildrenForDictionary
{
      id anEnum, key;
      id object, selectedValue;
      NSString* defaultSelection;
      NSEnumerator* selectionEnum;

      defaultSelection = nil;
      anEnum = [wrappedObject keyEnumerator];
      while(key = [anEnum nextObject])
      {
            if(![key isEqual: @"Selection"] && ![key isEqual: @"Type"])
            {
                  object = [wrappedObject valueForKey: key];
                  [children addObject: 
                        [ULOutlineViewWrapper wrapperForObject: object 
                              parent: self
                              identifier: key]];
            }
            else if([key isEqual: @"Selection"])
            {
                  object = [wrappedObject valueForKey: @"Selection"];
                  NSDebugLLog(@"ULOptionsViewController", @"Selection value is %@", object);
                  if(![object isKindOfClass: [NSArray class]])
                        [NSException raise: NSInternalInconsistencyException
                              format: @"Invalid structure for options selection object. Must be an array"];
                  if(![object count] == 0)
                  {
                        //check the selection(s) is/are in the dictionary
                        
                        selectionEnum = [object objectEnumerator];
                        while(selectedValue = [selectionEnum nextObject])
                              if([wrappedObject objectForKey: selectedValue] == nil)
                              {
                                    [NSException raise: NSInternalInconsistencyException
                                          format: [NSString stringWithFormat:
                                           @"Selected value %@ is not in object", selectedValue]];
                              }
                              else
                              {           
                                    //FIXME: no support for multiple selections!
                                    defaultSelection = [[object objectAtIndex: 0] retain];
                              }
                  }
            }
            else if([key isEqual: @"Type"])
                  if([[wrappedObject valueForKey: @"Type"] isEqual: @"Single"])
                        isMultiple = NO;
      }
      
      return defaultSelection;
}

- (void) _setChildren
{
      id anEnum, key;
      id object, optionsType;
      id defaultSelection;

      //first check is it an array or dictionary based selection
      //array based options can only contain 3 object (Type, Selection, and the array)

      if([wrappedObject count] <= 3)
      {
            anEnum = [wrappedObject keyEnumerator];
            while(key = [anEnum nextObject])
                  if(![key isEqual: @"Selection"] && ![key isEqual: @"Type"])
                  {
                        object = [wrappedObject valueForKey: key];
                        if([object isKindOfClass: [NSArray class]])
                              optionsType = @"Array";
                        else
                              optionsType = @"Dict";
                  }
      }
      else
            optionsType = @"Dict";

      NSDebugLLog(@"ULOptionsViewController", @"Selection type is %@", optionsType);

      if([optionsType isEqual:  @"Dict"])
            defaultSelection = [self _setChildrenForDictionary];
      else
            defaultSelection = [self _setChildrenForArray];
      
      noChildren = [children count];
      if(defaultSelection != nil)
      {
            anEnum = [children objectEnumerator];
            while(object = [anEnum nextObject])
                  if([[object identifier] isEqual: defaultSelection])
                        [object setValue: @"Selected"];
            [defaultSelection release];
      }
}

- (id) initWithObject: (id) object parent: (id) par identifier: (id) aName
{
      children = [[NSMutableArray arrayWithCapacity: 1] retain];
      isExpandable = YES;
      value = @"";
      wrappedObject = object;
      parent = [par retain];
      identifier = [aName retain];
      [value retain];
      isMultiple = YES;
      [self _setChildren];

      return self;
}

- (void) childWasSelected: (id) child
{
      id key;
      id previousSelection;

      NSDebugLLog(@"ULOptionsViewController", @"Child %@ with value %@ was selected", child, [child value]);

      key = [child identifier];

      if([[child value] isEqual: @"Selected"])
      {
            NSDebugLLog(@"ULOptionsViewController", 
                  @"Child is Yes - Changing to no and removing from %@",
                         [[wrappedObject valueForKey: @"Selection"] description]);
            [child setValue: @""];
            [[wrappedObject valueForKey:@"Selection"] removeObject: key];
            NSDebugLLog(@"ULOptionsViewControlle", @"Selection is now %@",
                         [[wrappedObject valueForKey: @"Selection"] description]);
      }
      else
      {
            NSDebugLLog(@"ULOptionsViewController", 
                  @"Child is NO - Changing to Yes and adding to selection %@", 
                        [[wrappedObject valueForKey: @"Selection"] description]);
            [child setValue: @"Selected"];
            if(!isMultiple)   
            {     
                  //catch when nothing has been selected
                  NS_DURING   
                  {
                        previousSelection = [self childForIdentifier: 
                                          [[wrappedObject valueForKey:@"Selection"] objectAtIndex: 0]];
                        [previousSelection setValue: @""];
                        [[wrappedObject valueForKey:@"Selection"] removeAllObjects];
                  }
                  NS_HANDLER
                  {
                        if(![[localException name] isEqual: NSRangeException])
                              [localException raise];
                  }
                  NS_ENDHANDLER
            }
            [[wrappedObject valueForKey:@"Selection"] addObject: [child identifier]];
            NSDebugLLog(@"ULOptionsViewController", @"Selection is now %@", 
                        [[wrappedObject valueForKey: @"Selection"] description]);
      }           
}

- (void) setValue: (id) aValue forChild: (id) child
{

}

- (void) dealloc
{
      [super dealloc];
}

@end

/**
ULOptionsLeaf wraps the final key:value option
pairs in an Adun options file i.e. where value
is a NSString. They are also used to display
the selection state of the elements of an ULSelectionWrapper
(YES, NO)
*/

00465 @interface ULOptionsLeaf: ULOutlineViewWrapper
@end

@implementation ULOptionsLeaf

- (id) initWithObject: (id) object parent: (id) par identifier: (id) aName
{
      noChildren = 0;
      value = object;
      parent = [par retain];
      identifier = [aName retain];
      [value retain];
      return self;
}

- (void) dealloc
{
      [super dealloc];
}

@end

/******

The Main Class

*******/

@implementation ULOutlineViewDelegate

- (id) initWithProperties: (id) properties allowEditing: (BOOL) value
{
      if(self == [super init])
      {
            wrappedOptions = [ULOutlineViewWrapper 
                              wrapperForObject: properties
                              parent: nil
                              identifier: @"root"];
            valueEditing = value;
            isProperties = YES;
            [wrappedOptions retain];
      }

      return self;
}

- (id) initWithOptions: (id) options
{
      if(self == [super init])
      {
            wrappedOptions = [ULOutlineViewWrapper 
                              wrapperForObject: options
                              parent: nil
                              identifier: @"root"];
            valueEditing = YES;
            isProperties = NO;
            [wrappedOptions retain];
      }
      
      return self;
}

- (void) dealloc
{
      [wrappedOptions release];
      [super dealloc];
}

/*************

OutlineView Data Source Methods

*************/

- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
      return (item == nil) ? [wrappedOptions numberChildren] : [item numberChildren];
}

- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
{
      return (item == nil) ? [wrappedOptions childAtIndex: index] : [item childAtIndex: index];
}

- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
      return (item == nil) ? YES :  [item isExpandable]; 
}

- (id)outlineView:(NSOutlineView *)outlineView 
      objectValueForTableColumn:(NSTableColumn *)tableColumn 
      byItem:(id)item
{
      if([[tableColumn identifier] isEqual:@"Options"])
      {
            return (item == nil) ? [wrappedOptions identifier] : [item identifier];
      }
      else
            return [item value];
}

- (void)outlineView:(NSOutlineView *)outlineView 
            setObjectValue:(id)object 
            forTableColumn:(NSTableColumn *)tableColumn 
            byItem:(id)item
{
      NSDebugLLog(@"ULOptionsViewController", @"Object is %@. Item is %@", object, item);

      if(![item isExpandable])
            [[item parent] setValue: object forChild: item];
}

/***********

OutlineView Delegate Methods

************/

- (void) outlineViewSelectionDidChange: (NSNotification*) aNotification
{
      id item, parent;
      id outlineView;

      outlineView = [aNotification object];
      item = [outlineView itemAtRow: [outlineView selectedRow]];
      parent = [item parent];

      NSDebugLLog(@"ULOptionsViewController", @"Selection changed to %@ (%@). Parent %@", 
                  item, [item identifier], parent);

      [parent childWasSelected: item];
      [outlineView reloadItem: item];
}

- (BOOL) outlineView: (NSOutlineView*) outlineView shouldExpandItem: (id) item
{
      return YES;
}

- (BOOL) outlineView: (NSOutlineView*) outlineView shouldEditTableColumn: (NSTableColumn*) tableColumn item: (id) item
{
      //dont allow editing of the outline column

      if([[tableColumn identifier] isEqual: @"Options"])
            return NO;

      if(![item isExpandable] && isProperties)
      {
            if(valueEditing && [[[item parent] identifier] isEqual: @"Metadata"])
                  return YES;
            else
                  return NO;
      }     
      else if(![item isExpandable] && valueEditing)
            return YES;
      else
            return NO;
}

@end

Generated by  Doxygen 1.6.0   Back to index