DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Mozilla has posted 1 posts at DZone. View Full User Profile

How To Display Indeterminate NSProgressIndicator In The NSOutlineView

08.10.2009
| 6825 views |
  • submit to reddit
        ImageTextFieldCell.h:
@interface ImageTextFieldCell : NSTextFieldCell {
  
  // An image to display before text label
  NSImage *image;
  
  // An array for caching all used indicators
  NSMutableArray *indicators;
  
  // Flag of displaying progress indicator
  BOOL inProgress;
  
  // Every connected item should have a property called progressIndicator
  id item;
}
@property (retain) NSImage *image;
@property (retain) id item;
@property BOOL inProgress;
@end

ImageTextFieldCell.m:
@interface ImageTextFieldCell(Private)
// Creates and auto-releases new progress indicator
- (NSProgressIndicator *)autoreleasedIndicator;

// Utility methods
- (void)drawImageInFrame:(NSRect *)cellFrame view:(NSView *)controlView;
- (void)placeIndicatorInFrame:(NSRect *)cellFrame view:(NSView *)controlView;
@end

@implementation ImageTextFieldCell

@synthesize image;
@synthesize inProgress;
@synthesize item;

// This method is implemented because text cells are defined through the NIB-file
- (id)initWithCoder:(NSCoder *)aDecoder {
  if (self = [super initWithCoder:aDecoder]) {
    indicators = [NSMutableArray new];
  }
  return self;
}

- (id)copyWithZone:(NSZone *)zone {
  ImageTextFieldCell *cell = [super copyWithZone:zone];
  cell->image = [image retain];
  cell->inProgress = inProgress;
  cell->indicators = [NSMutableArray new];
  cell->item = [item retain];
  return cell;
}

- (void) dealloc {
  [self setImage:nil];
  [self setItem:nil];
  [indicators release], indicators = nil;
  [super dealloc];
}

CGFloat const kSpaceBetweenImageAndText = 2.0f;

- (void)editWithFrame:(NSRect)aRect
               inView:(NSView *)controlView
               editor:(NSText *)textObj
             delegate:(id)anObject
                event:(NSEvent *)theEvent {
  NSRect imageFrame, textFrame;
  NSDivideRect (aRect, &imageFrame, &textFrame,
                [image size].width + kSpaceBetweenImageAndText * 2, NSMinXEdge);
  [super editWithFrame:textFrame
                inView:controlView
                editor:textObj
              delegate:anObject
                 event:theEvent];
}

- (void)selectWithFrame:(NSRect)aRect
                 inView:(NSView *)controlView
                 editor:(NSText *)textObj
               delegate:(id)anObject
                  start:(int)selStart
                 length:(int)selLength {
  NSRect imageFrame, textFrame;
  NSDivideRect(aRect, &imageFrame, &textFrame,
               [image size].width + kSpaceBetweenImageAndText * 2, NSMinXEdge);
  [super selectWithFrame:textFrame
                  inView:controlView
                  editor:textObj
                delegate:anObject
                   start:selStart
                  length:selLength];
}

- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
  [self drawImageInFrame:&cellFrame view:controlView];
  [self placeIndicatorInFrame:&cellFrame view:controlView];
  
  [super drawWithFrame:cellFrame inView:controlView];
}

#pragma mark -
#pragma mark Helper methods

CGFloat const kWidthOfProgressIndicator = 16.0f;

- (NSProgressIndicator *)autoreleasedIndicator {
  NSRect frame = NSMakeRect(0.0f, 0.0f, kWidthOfProgressIndicator, kWidthOfProgressIndicator);
  NSProgressIndicator *indicator = [[NSProgressIndicator alloc] initWithFrame:frame];
  [indicator setControlSize:NSSmallControlSize];
  [indicator setStyle:NSProgressIndicatorSpinningStyle];
  [indicator setUsesThreadedAnimation:YES];
  return [indicator autorelease];
}

-(void)drawImageInFrame:(NSRect *)cellFrame view:(NSView *)controlView {
  if (!image) return;
  
  NSSize imageSize = [image size];
  NSRect imageFrame;
  NSDivideRect(*cellFrame, &imageFrame, cellFrame,
               imageSize.width + kSpaceBetweenImageAndText * 2, NSMinXEdge);
  
  if ([self drawsBackground]) {
    [[self backgroundColor] set];
    NSRectFill(imageFrame);
  }
  imageFrame.size = imageSize;
  imageFrame.origin.x += kSpaceBetweenImageAndText;
  
  if ([controlView isFlipped])
    imageFrame.origin.y += ceil(((*cellFrame).size.height + imageFrame.size.height) / 2);
  else
    imageFrame.origin.y += ceil(((*cellFrame).size.height - imageFrame.size.height) / 2);
  
  [image compositeToPoint:imageFrame.origin operation:NSCompositeSourceOver];
}

- (void)placeIndicatorInFrame:(NSRect *)cellFrame view:(NSView *)controlView {
  NSProgressIndicator *indicator = [item valueForKey:@"progressIndicator"];
  
  if (inProgress) {
    
    if (!indicator) {
      indicator = [self autoreleasedIndicator];
      [indicator startAnimation:nil];
      [indicators addObject:indicator];
      [item setValue:indicator forKey:@"progressIndicator"];
    }
    
    NSRect indicatorFrame;
    NSDivideRect(*cellFrame, &indicatorFrame, cellFrame,
                 kWidthOfProgressIndicator + kSpaceBetweenImageAndText * 2, NSMaxXEdge);
    indicatorFrame.size = [indicator frame].size;
    indicatorFrame.origin.x += kSpaceBetweenImageAndText;
    indicatorFrame.origin.y += ceil(((*cellFrame).size.height - kWidthOfProgressIndicator) / 2);
    
    [indicator setFrame:indicatorFrame];
    [controlView addSubview:indicator];
  }
  else if (indicator) {
    [indicator stopAnimation:nil];
    [[indicator superview] setNeedsDisplayInRect:[indicator frame]];
    [indicator removeFromSuperviewWithoutNeedingDisplay];
    [indicators removeObject:indicator];
    [item setValue:nil forKey:@"progressIndicator"];
  }
}

@end

SidebarViewController.m:
…
- (void)outlineView:(NSOutlineView *)outlineView
    willDisplayCell:(id)cell
     forTableColumn:(NSTableColumn *)tableColumn
               item:(id)item {
  id sidebarItem = [item representedObject];
  [cell setItem:sidebarItem];
  [cell setInProgress:[sidebarItem isHandling]];
  if (sidebarItem == searchItem) {
    [cell setImage:searchImage];
  }
  else if (sidebarItem == uploadsItem) {
    [cell setImage:uploadsImage];
  }
  else {
    [cell setImage:nil];
  }
}
…