File:  [mozdev] / chimera / PreferencePanes / MVPreferencesMultipleIconView.m
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Tue Feb 26 11:49:02 2002 UTC (17 years, 8 months ago) by macserv
Branches: MAIN
CVS tags: HEAD
Checking in the underlying prefs structure, and a prefPane, also fir structural integrity.  Also including a couple categories to extend a couple Cocoa objects, and some project changes to support building with the bundled prefPanes.  Also, added a contrib directory to house user contributed artifacts.  To come soon: more prefPanes, and the colorized icon store, with pref-generated notifications to be picked up by toolbars.

    1: #import <Cocoa/Cocoa.h>
    2: #import "MVPreferencesMultipleIconView.h"
    3: #import "MVPreferencesController.h"
    4: #import "NSImageAdditions.h"
    5: 
    6: @interface MVPreferencesMultipleIconView (MVPreferencesMultipleIconViewPrivate)
    7: - (void) _focusFirst;
    8: - (void) _focusLast;
    9: - (unsigned int) _iconsWide;
   10: - (unsigned int) _numberOfIcons;
   11: - (unsigned int) _numberOfRows;
   12: - (BOOL) _isIconSelectedAtIndex:(unsigned int) index;
   13: - (BOOL) _column:(unsigned int *) column andRow:(unsigned int *) row forIndex:(unsigned int) index;
   14: - (NSRect) _boundsForIndex:(unsigned int) index;
   15: - (BOOL) _iconImage:(NSImage **) image andName:(NSString **) name forIndex:(unsigned int) index;
   16: - (BOOL) _iconImage:(NSImage **) image andName:(NSString **) name andIdentifier:(NSString **) identifier forIndex:(unsigned int) index;
   17: - (void) _drawIconAtIndex:(unsigned int) index drawRect:(NSRect) drawRect;
   18: - (void) _sizeToFit;
   19: - (BOOL) _dragIconIndex:(unsigned int) index event:(NSEvent *) event;
   20: - (BOOL) _dragIconImage:(NSImage *) iconImage andName:(NSString *) name event:(NSEvent *) event;
   21: - (BOOL) _dragIconImage:(NSImage *) iconImage andName:(NSString *) name andIdentifier:(NSString *) identifier event:(NSEvent *) event;
   22: @end
   23: 
   24: @interface MVPreferencesController (MVPreferencesControllerPrivate)
   25: - (IBAction) _selectPreferencePane:(id) sender;
   26: - (void) _resizeWindowForContentView:(NSView *) view;
   27: - (NSImage *) _imageForPaneBundle:(NSBundle *) bundle;
   28: - (NSString *) _paletteLabelForPaneBundle:(NSBundle *) bundle;
   29: - (NSString *) _labelForPaneBundle:(NSBundle *) bundle;
   30: @end
   31: 
   32: @implementation MVPreferencesMultipleIconView
   33: const NSSize buttonSize = { 81., 75. };
   34: const NSSize iconSize = { 32., 32. };
   35: const unsigned int titleBaseline = 15;
   36: const unsigned int iconBaseline = 30;
   37: const unsigned int bottomBorder = 15;
   38: 
   39: - (id) initWithFrame:(NSRect) rect {
   40: 	if( ( self = [super initWithFrame:rect] ) ) {
   41: 		pressedIconIndex = NSNotFound;
   42: 		focusedIndex = NSNotFound;
   43: 		selectedPane = nil;
   44: 	}
   45: 	return self;
   46: }
   47: 
   48: - (void) setPreferencesController:(MVPreferencesController *) newPreferencesController {
   49: 	[preferencesController autorelease];
   50: 	preferencesController = [newPreferencesController retain];
   51: 	[self setNeedsDisplay:YES];
   52: }
   53: 
   54: - (void) setPreferencePanes:(NSArray *) newPreferencePanes {
   55: 	[preferencePanes autorelease];
   56: 	preferencePanes = [newPreferencePanes retain];
   57: 	[self _sizeToFit];
   58: 	[self setNeedsDisplay:YES];
   59: }
   60: 
   61: - (NSArray *) preferencePanes {
   62: 	return preferencePanes;
   63: }
   64: 
   65: - (void) setSelectedPane:(NSBundle *) newSelectedPane {
   66: 	selectedPane = newSelectedPane;
   67: 	[self setNeedsDisplay:YES];
   68: }
   69: 
   70: - (void) mouseDown:(NSEvent *) event {
   71: 	NSPoint eventLocation;
   72: 	NSRect slopRect;
   73: 	const float dragSlop = 4.;
   74: 	unsigned int index;
   75: 	NSRect buttonRect;
   76: 	BOOL mouseInBounds = NO;
   77: 
   78: 	eventLocation = [self convertPoint:[event locationInWindow] fromView:nil];
   79: 	slopRect = NSInsetRect( NSMakeRect( eventLocation.x, eventLocation.y, 1., 1. ), -dragSlop, -dragSlop );
   80: 
   81: 	index = floor( eventLocation.x / buttonSize.width ) + ( floor( eventLocation.y / buttonSize.height ) * [self _iconsWide] );
   82: 	buttonRect = [self _boundsForIndex:index];
   83: 	if( index >= ( [self _iconsWide] * ( floor( eventLocation.y / buttonSize.height ) + 1 ) ) ) return;
   84: 	if( ! NSWidth( buttonRect ) ) return;
   85: 
   86: 	pressedIconIndex = index;
   87: 	[self setNeedsDisplayInRect:[self _boundsForIndex:index]];
   88: 
   89: 	while( 1 ) {
   90: 		NSEvent *nextEvent;
   91: 		NSPoint nextEventLocation;
   92: 		unsigned int newPressedIconIndex;
   93: 
   94: 		nextEvent = [NSApp nextEventMatchingMask:NSLeftMouseDraggedMask|NSLeftMouseUpMask untilDate:[NSDate distantFuture] inMode:NSEventTrackingRunLoopMode dequeue:YES];
   95: 
   96: 		nextEventLocation = [self convertPoint:[nextEvent locationInWindow] fromView:nil];
   97: 		mouseInBounds = NSMouseInRect(nextEventLocation, buttonRect, [self isFlipped]);
   98: 		newPressedIconIndex = ( mouseInBounds ? index : NSNotFound );
   99: 		if( newPressedIconIndex != pressedIconIndex ) {
  100: 			pressedIconIndex = newPressedIconIndex;
  101: 			[self setNeedsDisplayInRect:[self _boundsForIndex:pressedIconIndex]];
  102: 		}
  103: 
  104: 		if( [nextEvent type] == NSLeftMouseUp ) break;
  105: 		else if( ! NSMouseInRect( nextEventLocation, slopRect, NO ) ) {
  106: 			if( [self _dragIconIndex:index event:event] ) {
  107: 				mouseInBounds = NO;
  108: 				break;
  109: 			}
  110: 		}
  111: 	}
  112: 
  113: 	pressedIconIndex = NSNotFound;
  114: 	[self setNeedsDisplayInRect:[self _boundsForIndex:index]];
  115: 
  116: 	if( mouseInBounds )
  117: 		[preferencesController selectPreferencePaneByIdentifier:[[preferencePanes objectAtIndex:index] bundleIdentifier]];
  118: }
  119: 
  120: #define kTabCharCode 9
  121: #define kShiftTabCharCode 25
  122: #define kSpaceCharCode 32
  123: 
  124: - (void) keyDown:(NSEvent *) theEvent {
  125: 	NSView *nextView = nil;
  126: 	if( [[theEvent charactersIgnoringModifiers] characterAtIndex:0] == kTabCharCode ) {
  127: 		[self setKeyboardFocusRingNeedsDisplayInRect:[self _boundsForIndex:focusedIndex]];
  128: 		if( focusedIndex == NSNotFound && [self _numberOfIcons] ) focusedIndex = 0;
  129: 		else if( ! ( [theEvent modifierFlags] & NSShiftKeyMask ) ) focusedIndex++;
  130: 		if( focusedIndex >= [self _numberOfIcons] ) {
  131: 			if( ( nextView = [self nextValidKeyView] ) ) {
  132: 				[[self window] makeFirstResponder:nextView];
  133: 				focusedIndex = NSNotFound;
  134: 				if( [nextView isKindOfClass:[MVPreferencesMultipleIconView class]] )
  135: 					[(MVPreferencesMultipleIconView *)nextView _focusFirst];
  136: 			} else focusedIndex = 0;
  137: 		}
  138: 		if( ! [self _numberOfIcons] ) focusedIndex = NSNotFound;
  139: 		[self setNeedsDisplayInRect:[self _boundsForIndex:focusedIndex]];
  140: 	} else if( [[theEvent charactersIgnoringModifiers] characterAtIndex:0] == kShiftTabCharCode ) {
  141: 		[self setKeyboardFocusRingNeedsDisplayInRect:[self _boundsForIndex:focusedIndex]];
  142: 		if( focusedIndex == NSNotFound && [self _numberOfIcons] ) focusedIndex = [self _numberOfIcons] - 1;
  143: 		else if( [theEvent modifierFlags] & NSShiftKeyMask ) focusedIndex--;
  144: 		if( (signed) focusedIndex < 0 && [self _numberOfIcons] ) {
  145: 			if( ( nextView = [self previousValidKeyView] ) ) {
  146: 				[[self window] makeFirstResponder:nextView];
  147: 				focusedIndex = NSNotFound;
  148: 				if( [nextView isKindOfClass:[MVPreferencesMultipleIconView class]] )
  149: 					[(MVPreferencesMultipleIconView *)nextView _focusLast];
  150: 			} else focusedIndex = [self _numberOfIcons] - 1;
  151: 		}
  152: 		if( ! [self _numberOfIcons] ) focusedIndex = NSNotFound;
  153: 		[self setNeedsDisplayInRect:[self _boundsForIndex:focusedIndex]];
  154: 	} else if( [[theEvent charactersIgnoringModifiers] characterAtIndex:0] == kSpaceCharCode ) {
  155: 		if( focusedIndex != NSNotFound && focusedIndex < [self _numberOfIcons] && focusedIndex >= 0 ) {
  156: 			[preferencesController selectPreferencePaneByIdentifier:[[preferencePanes objectAtIndex:focusedIndex] bundleIdentifier]];
  157: 			focusedIndex = NSNotFound;
  158: 		}
  159: 	}
  160: }
  161: 
  162: - (BOOL) acceptsFirstResponder {
  163: 	return YES;
  164: }
  165: 
  166: - (BOOL) becomeFirstResponder {
  167: 	focusedIndex = NSNotFound;
  168: 	return YES;
  169: }
  170: 
  171: - (BOOL) resignFirstResponder {
  172: 	[self setKeyboardFocusRingNeedsDisplayInRect:[self _boundsForIndex:focusedIndex]];
  173: 	focusedIndex = NSNotFound;
  174: 	return YES;
  175: }
  176: 
  177: - (void) drawRect:(NSRect) rect {
  178: 	unsigned int paneIndex, paneCount;
  179: 
  180: 	paneCount = [self _numberOfIcons];
  181: 	for( paneIndex = 0; paneIndex < paneCount; paneIndex++ )
  182: 		[self _drawIconAtIndex:paneIndex drawRect:rect];
  183: }
  184: 
  185: - (BOOL) isFlipped {
  186: 	return YES;
  187: }
  188: 
  189: - (BOOL) isOpaque {
  190: 	return NO;
  191: }
  192: 
  193: - (int) tag {
  194: 	return tag;
  195: }
  196: 
  197: - (void) setTag:(int) newTag {
  198: 	tag = newTag;
  199: }
  200: 
  201: - (NSDragOperation) draggingSourceOperationMaskForLocal:(BOOL) flag {
  202: 	return NSDragOperationMove;
  203: }
  204: 
  205: - (void) draggedImage:(NSImage *)image endedAt:(NSPoint) screenPoint operation:(NSDragOperation) operation {
  206: }
  207: 
  208: - (BOOL) ignoreModifierKeysWhileDragging {
  209: 	return YES;
  210: }
  211: @end
  212: 
  213: @implementation MVPreferencesMultipleIconView (MVPreferencesMultipleIconViewPrivate)
  214: - (void) _focusFirst {
  215: 	focusedIndex = 0;
  216: 	[self setNeedsDisplayInRect:[self _boundsForIndex:focusedIndex]];
  217: }
  218: 
  219: - (void) _focusLast {
  220: 	focusedIndex = [self _numberOfIcons] - 1;
  221: 	[self setNeedsDisplayInRect:[self _boundsForIndex:focusedIndex]];
  222: }
  223: 
  224: - (unsigned int) _iconsWide {
  225: 	return NSWidth( [self bounds] ) / buttonSize.width;
  226: }
  227: 
  228: - (unsigned int) _numberOfIcons {
  229: 	return [[self preferencePanes] count];
  230: }
  231: 
  232: - (unsigned int) _numberOfRows {
  233: 	unsigned int row = 0, column = 0;
  234: 	if( ! [self _column:&column andRow:&row forIndex:[self _numberOfIcons] - 1] ) return 0;
  235: 	return row;
  236: }
  237: 
  238: - (BOOL) _isIconSelectedAtIndex:(unsigned int) index {
  239: 	return [[self preferencePanes] objectAtIndex:index] == selectedPane;
  240: }
  241: 
  242: - (BOOL) _column:(unsigned int *) column andRow:(unsigned int *) row forIndex:(unsigned int) index {
  243: 	if( index >= [self _numberOfIcons] ) return NO;
  244: 
  245: 	*row = index / [self _iconsWide];
  246: 	*column = index % [self _iconsWide];
  247: 
  248: 	return YES;
  249: }
  250: 
  251: - (NSRect) _boundsForIndex:(unsigned int) index {
  252: 	unsigned int row = 0, column = 0, leftEdge = 0;
  253: 
  254: 	if( ! [self _column:&column andRow:&row forIndex:index] ) return NSZeroRect;
  255: 
  256: 	leftEdge = ( NSWidth( _bounds ) - ( [self _iconsWide] * buttonSize.width ) ) / 2. ;
  257: 
  258: 	return NSMakeRect( ( column * buttonSize.width ) + leftEdge, row * buttonSize.height - (row * 6), buttonSize.width, buttonSize.height );
  259: }
  260: 
  261: - (BOOL) _iconImage:(NSImage **) image andName:(NSString **) name forIndex:(unsigned int) index {
  262: 	NSString *unused;
  263: 	return [self _iconImage:image andName:name andIdentifier:&unused forIndex:index];
  264: }
  265: 
  266: - (BOOL) _iconImage:(NSImage **) image andName:(NSString **) name andIdentifier:(NSString **) identifier forIndex:(unsigned int) index {
  267: 	NSDictionary *info = nil;
  268: 	NSBundle *pane = nil;
  269: 
  270: 	if( index >= [self _numberOfIcons] ) return NO;
  271: 
  272: 	pane = [[self preferencePanes] objectAtIndex:index];
  273: 	info = [pane infoDictionary];
  274: 	*image = [preferencesController _imageForPaneBundle:pane];
  275: 	*name = [preferencesController _paletteLabelForPaneBundle:pane];
  276: 	*identifier = [pane bundleIdentifier];
  277: 
  278: 	return YES;
  279: }
  280: 
  281: - (void) _drawIconAtIndex:(unsigned int) index drawRect:(NSRect) drawRect {
  282: 	NSImage *image;
  283: 	NSString *name;
  284: 	unsigned int row, column;
  285: 	NSPoint drawPoint;
  286: 	float nameHeight;
  287: 	NSRect buttonRect, destinationRect;
  288: 	NSDictionary *attributesDictionary;
  289: 	NSMutableParagraphStyle *paraStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
  290: 	CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
  291: 
  292: 	buttonRect = [self _boundsForIndex:index];
  293: 	if( ! NSIntersectsRect( buttonRect, drawRect ) ) return;
  294: 	if( ! [self _iconImage:&image andName:&name forIndex:index] ) return;
  295: 	if( ! [self _column:&column andRow:&row forIndex:index] ) return;
  296: 
  297: 	// Draw dark gray rectangle around currently selected icon (for MultipleIconView)
  298: 	if( [self _isIconSelectedAtIndex:index] ) {
  299: 		[[NSColor colorWithCalibratedWhite:0.8 alpha:0.75] set];
  300: 		NSRectFillUsingOperation(buttonRect, NSCompositeSourceOver);
  301: 	}
  302: 
  303: 	if( focusedIndex == index ) {
  304: 		CGContextSaveGState( context );
  305: 		NSSetFocusRingStyle( NSFocusRingAbove );
  306: 	}
  307: 
  308: 	// Draw icon, dark if it is currently being pressed
  309: 	destinationRect = NSIntegralRect( NSMakeRect( NSMidX( buttonRect) - iconSize.width / 2., NSMaxY( buttonRect ) - iconBaseline - iconSize.height, iconSize.width, iconSize.height ) );
  310: 	destinationRect.size = iconSize;
  311: 	if( index != pressedIconIndex && image ) [image drawFlippedInRect:destinationRect operation:NSCompositeSourceOver fraction:1.];
  312: 	else if( image ) {
  313: 		NSImage *darkImage;
  314: 		NSSize darkImageSize;
  315: 
  316: 		darkImage = [image copy];
  317: 		darkImageSize = [darkImage size];
  318: 		[darkImage lockFocus]; {
  319: 			[[NSColor blackColor] set];
  320: 			NSRectFillUsingOperation( NSMakeRect( 0., 0., darkImageSize.width, darkImageSize.height ), NSCompositeSourceIn );
  321: 			[darkImage unlockFocus];
  322: 		}
  323: 
  324: 		[darkImage drawFlippedInRect:destinationRect operation:NSCompositeSourceOver fraction:1.];
  325: 		[image drawFlippedInRect:destinationRect operation:NSCompositeSourceOver fraction:.6666667];
  326: 		[darkImage release];
  327: 	}
  328: 	if( focusedIndex == index ) CGContextRestoreGState( context );
  329: 
  330: 	// Draw text
  331: 	[paraStyle setAlignment:NSCenterTextAlignment];
  332: 	attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:[NSFont toolTipsFontOfSize:11.], NSFontAttributeName, paraStyle, NSParagraphStyleAttributeName, nil];
  333: 	nameHeight = [[NSFont toolTipsFontOfSize:11.] defaultLineHeightForFont];
  334: 	drawPoint = NSMakePoint( rint( buttonSize.width + 2 * NSMinX( buttonRect ) - NSWidth( _bounds ) - 1 ), rint( NSMaxY( buttonRect ) - titleBaseline - nameHeight ) );
  335: 	[name drawAtPoint:drawPoint withAttributes:attributesDictionary];
  336: }
  337: 
  338: - (void) _sizeToFit {
  339: 	if( ! [self preferencePanes] ) return;
  340: 	[self setFrameSize:NSMakeSize( NSWidth( _bounds ), NSMaxY( [self _boundsForIndex:[self _numberOfIcons] - 1] ) + bottomBorder ) ];
  341: }
  342: 
  343: - (BOOL) _dragIconIndex:(unsigned int) index event:(NSEvent *) event {
  344: 	NSImage *iconImage;
  345: 	NSString *name;
  346: 	NSString *identifier;
  347: 
  348: 	if( ! [self _iconImage:&iconImage andName:&name andIdentifier:&identifier forIndex:index] )
  349: 		return YES;
  350: 
  351: 	return [self _dragIconImage:iconImage andName:name andIdentifier:identifier event:event];
  352: }
  353: 
  354: - (BOOL) _dragIconImage:(NSImage *) iconImage andName:(NSString *) name event:(NSEvent *) event {
  355: 	return [self _dragIconImage:iconImage andName:name andIdentifier:name event:event];
  356: }
  357: 
  358: - (BOOL) _dragIconImage:(NSImage *) iconImage andName:(NSString *) name andIdentifier:(NSString *) identifier event:(NSEvent *) event {
  359: 	NSImage *dragImage;
  360: 	NSPasteboard *pasteboard;
  361: 	NSPoint dragPoint, startPoint;
  362: 	NSMutableParagraphStyle *paraStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
  363: 
  364: 	dragImage = [[NSImage alloc] initWithSize:buttonSize];
  365: 	[dragImage lockFocus]; {
  366: 		float nameHeight;
  367: 		NSSize nameSize;
  368: 		NSDictionary *attributesDictionary;
  369: 
  370: 		[iconImage drawInRect:NSMakeRect( buttonSize.width / 2. - iconSize.width / 2., iconBaseline, iconSize.width, iconSize.height ) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:.5];
  371: 
  372: 		[paraStyle setAlignment:NSCenterTextAlignment];
  373: 		attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:[NSFont toolTipsFontOfSize:11.], NSFontAttributeName, [[NSColor blackColor] colorWithAlphaComponent:.5], NSForegroundColorAttributeName, paraStyle, NSParagraphStyleAttributeName, nil];
  374: 		nameSize = [name sizeWithAttributes:attributesDictionary];
  375: 		nameHeight = [[NSFont toolTipsFontOfSize:11.] defaultLineHeightForFont];
  376: 		[name drawAtPoint:NSMakePoint( 0., nameHeight + titleBaseline - nameSize.height ) withAttributes:attributesDictionary];
  377: 		[dragImage unlockFocus];
  378: 	}
  379: 
  380: 	pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
  381: 	[pasteboard declareTypes:[NSArray arrayWithObject:@"NSToolbarIndividualItemDragType"] owner:nil];
  382: 	[pasteboard setString:identifier forType:@"NSToolbarItemIdentiferPboardType"];
  383: 
  384: 	dragPoint = [self convertPoint:[event locationInWindow] fromView:nil];
  385: 	startPoint = [self _boundsForIndex:[preferencePanes indexOfObject:[NSBundle bundleWithIdentifier:identifier]]].origin;
  386: 	startPoint.y += buttonSize.height;
  387: 	[self setNeedsDisplayInRect:[self _boundsForIndex:pressedIconIndex]];
  388: 	pressedIconIndex = NSNotFound;
  389: 	[self dragImage:dragImage at:startPoint offset:NSZeroSize event:event pasteboard:pasteboard source:self slideBack:YES];
  390: 
  391: 	return YES;
  392: }
  393: @end

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>