// // BPIcon.m // Attempt2 // // Created by Bea on 13/05/05. // Copyright 2005 __MyCompanyName__. All rights reserved. // #import "BPIcon.h" #import "BPIconGroup.h" #import "BPUtil.h" @implementation BPIcon + (int)defaultHorizShiftForMarking { return 30; } - (id)initWithImage:(NSImage *)iconImage opacity:(float)iconOpacity represents:(NSObject *)representedObject { [super init]; representedObj = [representedObject retain]; iconAlphaRecord = NULL; [self setImage:iconImage]; opacity = iconOpacity; iconGroup = nil; highlighted = NO; isMarked = NO; horizShiftForMarking = [BPIcon defaultHorizShiftForMarking]; hasMovedOutOfGroupBefore = NO; // for the mouse actions delegate mouseActionsDelegate = nil; mouseEnteredIconSel = @selector(mouseEnteredIcon:); mouseExitedIconSel = @selector(mouseExitedIcon:); return self; } - (id)representedObject { return representedObj; } - (void)setMouseActionsDelegate:(id)delegate { [delegate retain]; [mouseActionsDelegate release]; mouseActionsDelegate = delegate; } - (void)resetIconAlphaData { // srcImageRep is the NSBitmapImageRep of the source image NSBitmapImageRep *srcImageRep = [NSBitmapImageRep imageRepWithData:[image TIFFRepresentation]]; int n = [srcImageRep bitsPerPixel] / 8; // Bytes per pixel int w = [srcImageRep pixelsWide]; int h = [srcImageRep pixelsHigh]; int rowBytes = [srcImageRep bytesPerRow]; int i; unsigned char *srcData = [srcImageRep bitmapData]; unsigned char r,g,b,alpha; int alphaCount = 0; int imageWidth = [image size].width; int imageHeight = [image size].height; //BOOL *newIconAlphaRecord = malloc(112*49*sizeof(BOOL)); BOOL *newIconAlphaRecord = (BOOL *)malloc(sizeof(BOOL[imageWidth*imageHeight])); if (newIconAlphaRecord == NULL) { NSLog(@"resetIconAlphaData Error: no memory available to malloc newIconAlphaRecord"); return; } int count = 0; //for ( i = 0; i < rowBytes * h; i++ ) { for ( i = 0; i < imageWidth * imageHeight * 4; i+=4 ) { // should it be 4 not 3???? r = *(srcData + i); g = *(srcData + i + 1); b = *(srcData + i + 2); alpha = *(srcData + i + 3); //if (alpha == 0) alphaCount++; //printf("%d %d %d\t", r, g, b); // note current point if ( r == 0 && b == 0 && g == 0 && alpha == 0) { //*newIconAlphaRecord = 1; // this point has alpha newIconAlphaRecord[count] = 1; } else { //*newIconAlphaRecord = 0; newIconAlphaRecord[count] = 0; } //if (alpha == 0) // NSLog(@"%d %d %d", r, g, b); // move the pointer along count++; //newIconAlphaRecord++; } // free old iconAlphaRecord array if (iconAlphaRecord != NULL) { free(iconAlphaRecord); iconAlphaRecord = NULL; } // record new iconAlphaRecord array iconAlphaRecord = newIconAlphaRecord; /* int j; for (j=0; j<112; j++) printf("%d ", newIconAlphaRecord[j]); printf("\n"); */ //NSLog(@"alphaCount %d", alphaCount); } // generates the "inverted" image to show when this icon is highlighted // (i.e. selected) - (NSImage *)createInverseImage { // code from 'Cocoa in a Nutshell' chapter 4 for inverting the colour data // in an image. // srcImageRep is the NSBitmapImageRep of the source image NSBitmapImageRep *srcImageRep = [NSBitmapImageRep imageRepWithData:[image TIFFRepresentation]]; int n = [srcImageRep bitsPerPixel] / 8; // Bytes per pixel int w = [srcImageRep pixelsWide]; int h = [srcImageRep pixelsHigh]; int rowBytes = [srcImageRep bytesPerRow]; int i; NSBitmapImageRep *destImageRep = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:w pixelsHigh:h bitsPerSample:8 samplesPerPixel:n hasAlpha:[srcImageRep hasAlpha] isPlanar:NO colorSpaceName:[srcImageRep colorSpaceName] bytesPerRow:rowBytes bitsPerPixel:NULL] autorelease]; unsigned char *srcData = [srcImageRep bitmapData]; unsigned char *destData = [destImageRep bitmapData]; // inverse data when copying it into destData //for ( i = 0; i < rowBytes * h; i++ ) // *(destData + i) = 255 - *(srcData + i); unsigned char r,g,b,alpha; int j=0; int alphaCount = 0; //for ( i = 0; i < rowBytes * h; i++ ) { for ( i = 0; i < 112 * 49 * 4; i+=4 ) { // should it be 4 not 3???? r = *(srcData + i); g = *(srcData + i + 1); b = *(srcData + i + 2); alpha = *(srcData + i + 3); if (alpha == 0 ) alphaCount++; if (alpha == 0) { //if(r == 255 && g == 255 && b == 255){ *(destData + j + 0) = 255; *(destData + j + 1) = 0; *(destData + j + 2) = 0; *(destData + j + 3) = 255; // 255 is full, 0 is transparent } else { *(destData + j) = r; *(destData + j + 1) = g; *(destData + j + 2) = b; *(destData + j + 3) = 255; } j+=4; } NSLog(@"alphaCount %d", alphaCount); /* int row, col, x, y, cell_width, cell_height; int alpha; cell_width = ([image size].width / 16); cell_height = ([image size].height / 3); x = (col * cell_width); y = (row * cell_height); int j=0; for (i = 0; i < cell_width * cell_height * 3; i += 3) { unsigned char r, g, b; r = *(srcData + i); g = *(srcData + i + 1); b = *(srcData + i + 2); alpha = *(destData + i + 3); if(r == 255 && g == 255 && b == 255){ *(destData + j + 0) = 0; *(destData + j + 1) = 0; *(destData + j + 2) = 0; *(destData + j + 3) = 0; } else { *(destData + j) = r; *(destData + j + 1) = g; *(destData + j + 2) = b; *(destData + j + 3) = 255; } j += 4; } */ // place destData into dest image NSImage *inverseImage = [[[NSImage alloc] initWithSize:NSMakeSize(w, h)] autorelease]; [inverseImage addRepresentation:destImageRep]; return inverseImage; } - (NSImage *)createGrayBackgroundImage { NSImage *grayBackgImage = [[image copy] autorelease]; NSImage *shadeImage = [[NSImage alloc] initWithSize:[image size]]; // shade the shaded image [shadeImage lockFocus]; [[NSColor darkGrayColor] set]; NSRectFill(NSMakeRect(0.0,0.0,[image size].width,[image size].height)); [shadeImage unlockFocus]; // composite the shaded image onto the original image [grayBackgImage lockFocus]; [shadeImage compositeToPoint:NSZeroPoint operation:NSCompositeDestinationOver fraction:0.3]; [grayBackgImage unlockFocus]; // garbage collect [shadeImage release]; return grayBackgImage; } - (NSImage *)createShadedImageWithColour:(NSColor *)shadeColour shadeOpacity:(float)shadeOpacity { // set up source & dest images // can't just do [image copy] for imageToShade, that doesn't copy any // images composited on top NSImage *imageToShade = [[[NSImage alloc] initWithData:[image TIFFRepresentation]] autorelease]; NSImage *blackShade = [[NSImage alloc] initWithSize:[image size]]; // shade the shaded image [blackShade lockFocus]; [shadeColour set]; NSRectFill(NSMakeRect(0.0,0.0,[image size].width,[image size].height)); [blackShade unlockFocus]; // composite the shaded image onto the original image [imageToShade lockFocus]; [blackShade compositeToPoint:NSZeroPoint operation:NSCompositeSourceAtop // composites source image only where dest image is opaque fraction:shadeOpacity]; [imageToShade unlockFocus]; // garbage collect [blackShade release]; return imageToShade; } - (void)generateHighlightImage { NSImage *newHighlightImage = [self createShadedImageWithColour:[NSColor blackColor] shadeOpacity:0.4]; [newHighlightImage retain]; // release the old highlight image [highlightImage release]; highlightImage = newHighlightImage; } - (void)generateBrowsedImage { NSData *colourAsData = [[NSUserDefaults standardUserDefaults] objectForKey:@"MailStacker_highlightBrowsedItemColour"]; NSColor *shadeColour = [NSKeyedUnarchiver unarchiveObjectWithData:colourAsData]; NSImage *newDarkShadedImage = [[self createShadedImageWithColour:shadeColour shadeOpacity:0.2] retain]; [darkShadedImage release]; darkShadedImage = newDarkShadedImage; } - (void)generatePaintedImage { if (paintColour != nil) { NSImage *newPaintedImage = [[self createShadedImageWithColour:paintColour shadeOpacity:0.3] retain]; [paintedImage release]; paintedImage = newPaintedImage; } } - (void)generateAlternateImages { [self generateHighlightImage]; [self generateBrowsedImage]; //[self generateDragFormImage]; [self generatePaintedImage]; } - (void)setImage:(NSImage *)iconImage { [iconImage retain]; [image release]; image = iconImage; [self resetIconAlphaData]; [image setBackgroundColor:[NSColor clearColor]]; [self generateAlternateImages]; } - (NSImage *)image { return image; } - (void)setOpacity:(float)iconOpacity { opacity = iconOpacity; } - (float)opacity { return opacity; } - (void)setIconGroup:(BPIconGroup *)group { iconGroup = group; } - (BPIconGroup *)iconGroup { return iconGroup; } - (int)xOffsetToDefaultPosn { NSPoint defaultPosn = [iconGroup defaultPositionForIcon:self]; int xOffsetToDefaultPosn = position->x - defaultPosn.x; return xOffsetToDefaultPosn; } - (void)setPosition:(NSPoint)iconPosition { // do nothing if position isn't changing if (NSEqualPoints(iconPosition, *position)) return; // super implementation records the position [super setPosition:iconPosition]; if ([self xOffsetToDefaultPosn] == 0) { [self setMarked:NO]; } else { horizShiftForMarking = [self xOffsetToDefaultPosn]; [self setMarked:YES]; } // post notification NSDictionary *dict = [NSDictionary dictionaryWithObject:self forKey:@"Icon"]; NSNotification *notif = [NSNotification notificationWithName:@"Icon_PositionChanged" object:nil userInfo:dict]; [[NSNotificationCenter defaultCenter] postNotification:notif]; } - (void)resetPosition { if (iconGroup == nil) { NSLog(@"setPosition Error: icon %@ has no group, using NSZeroPoint as position", self); [self setPosition:NSZeroPoint]; } NSPoint posn = [iconGroup defaultPositionForIcon:self]; if (isMarked) posn.x += horizShiftForMarking; [self setPosition:posn]; } - (void)resetToDefaultPosition { if (iconGroup == nil) { NSLog(@"setPosition Error: icon %@ has no group, using NSZeroPoint as position", self); [self setPosition:NSZeroPoint]; } [self setPosition:[iconGroup defaultPositionForIcon:self]]; } - (void)setHasMovedOutOfGroupBefore:(BOOL)hasMovedOut { hasMovedOutOfGroupBefore = hasMovedOut; } - (BOOL)hasMovedOutOfGroupBefore { return hasMovedOutOfGroupBefore; } - (NSRect)trackingRectBounds { return [self bounds]; } - (NSRect)bounds { NSRect rect; rect.size = [image size]; rect.origin = *position; return rect; } - (int)depthCompare:(BPIcon *)otherIcon { // sort first by group's z value, then by position within the group if ([iconGroup z] > [[otherIcon iconGroup] z]) { // this icon's group is in front of the other icon's group return NSOrderedAscending; } else if ([iconGroup z] < [[otherIcon iconGroup] z]) { // this icon's group is behind the other icon's group return NSOrderedDescending; } else { // both icons in same group if (iconGroup != [otherIcon iconGroup]) { NSLog(@"depthCompare Error: 2 icons groups have same position" "Group 1 posn: %@ \nGroup 2 posn: %@", NSStringFromPoint([iconGroup position]), NSStringFromPoint([[otherIcon iconGroup] position]) ); return NSOrderedAscending; } int thisIconIndex = [iconGroup indexOfIcon:self]; int otherIconIndex = [iconGroup indexOfIcon:otherIcon]; if (thisIconIndex > otherIconIndex) { // this icon is higher in the pile return NSOrderedDescending; } else if (thisIconIndex < otherIconIndex) { // this icon is lower in the pile return NSOrderedAscending; } else { NSLog(@"BPIcon depthCompare error: %@ and %@ have same position in pile???", self, otherIcon); return NSOrderedAscending; } } } - (void)setHighlighted:(BOOL)highlight { highlighted = highlight; } - (BOOL)isHighlighted { return highlighted; } - (void)setMarked:(BOOL)mark { isMarked = mark; } - (BOOL)isMarked { return isMarked; } - (void)toggleShift { NSPoint point = *position; if ([self xOffsetToDefaultPosn] == 0) { // move out by horiz shift amount point.x += horizShiftForMarking; [self setMarked:YES]; } else { // revert to default X posn point.x = [iconGroup defaultPositionForIcon:self].x; [self setMarked:NO]; } [self setPosition:point]; } - (void)setMarkAmount:(int)amount { if (amount != 0) horizShiftForMarking = amount; } - (int)markAmount { return horizShiftForMarking; } - (void)paintWithColour:(NSColor *)colour { if ([colour isEqual:paintColour]) return; if (colour == nil) { [paintedImage release]; paintedImage = nil; [paintColour release]; paintColour = nil; } else { [colour retain]; [paintColour release]; paintColour = colour; [self generatePaintedImage]; } // post notification NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys: self, @"Icon", NULL]; if (paintColour != nil) [dict setObject:paintColour forKey:@"Colour"]; NSNotification *notif = [NSNotification notificationWithName:@"Icon_ColourChanged" object:nil userInfo:dict]; [[NSNotificationCenter defaultCenter] postNotification:notif]; } - (void)setBeingBrowsed:(BOOL)isBeingBrowsed; { beingBrowsed = isBeingBrowsed; } - (BOOL)isBeingBrowsed { return beingBrowsed; } #pragma mark - #pragma mark drawing - (void)drawSelf { NSImage *imageToDraw; if (beingBrowsed) { imageToDraw = darkShadedImage; } else if (highlighted) { imageToDraw = highlightImage; } else { if (paintColour != nil && paintedImage != nil) imageToDraw = paintedImage; else imageToDraw = image; } NSRect drawRect; drawRect.origin = NSZeroPoint; drawRect.size = [imageToDraw size]; [imageToDraw drawAtPoint:*position fromRect:drawRect operation:NSCompositeSourceOver fraction:opacity]; } - (void)drawDragForm { NSRect drawRect; drawRect.origin = NSZeroPoint; drawRect.size = [image size]; [image drawAtPoint:*dragFormPosition fromRect:drawRect operation:NSCompositeSourceOver fraction:0.4]; } - (BOOL)hitTest:(NSPoint)aPoint { // use icon alpha array cos had issues with using NSReadPixel if (iconAlphaRecord == NULL) { NSLog(@"hitTest Error: iconAlphaRecord is NULL, returning YES"); return YES; } aPoint.x -= position->x; aPoint.y -= position->y; int columnHeight = [image size].height - aPoint.y; int arrayIndex = ([image size].width * columnHeight) + aPoint.x -1; // -1 because 0-based array index BOOL pointHasAlpha = iconAlphaRecord[arrayIndex]; if (pointHasAlpha) return NO; else return YES; } - (BOOL)isMouseOver:(NSPoint)point { //NSLog(@"%@ in %@", NSStringFromPoint(point), NSStringFromRect([self bounds])); if (! [BPUtil point:point overRect:[self bounds]] ) { return NO; } //return YES; return [self hitTest:point]; } - (void)mouseEnteredTrackingRect { if ([mouseActionsDelegate respondsToSelector:mouseEnteredIconSel]) [mouseActionsDelegate performSelector:mouseEnteredIconSel withObject:self]; } - (void)mouseExitedTrackingRect { if ([mouseActionsDelegate respondsToSelector:mouseExitedIconSel]) [mouseActionsDelegate performSelector:mouseExitedIconSel withObject:self]; } // ---------- description - (NSString *)description { return [NSString stringWithFormat:@"BPIcon no.%d in pile %@", [iconGroup indexOfIcon:self], iconGroup]; } // ----------- dealloc - (void)dealloc { NSLog(@"dealloc %@", self); if (iconAlphaRecord != NULL) free(iconAlphaRecord); [representedObj release]; [image release]; [originalImage release]; [dragFormImage release]; [highlightImage release]; [darkShadedImage release]; [paintColour release]; [paintedImage release]; [super dealloc]; } @end