/**
EXPLAINS NSBox
*TODO : explains how is resized the rects (margins etc...)
*/ @implementation NSBox // // Class methods // + (void) initialize { if (self == [NSBox class]) { // Initial version [self setVersion: 1]; } } // // Instance methods // - (id) initWithFrame: (NSRect)frameRect { [super initWithFrame: frameRect]; _cell = [[NSCell alloc] initTextCell: @"Title"]; [_cell setAlignment: NSCenterTextAlignment]; [_cell setBordered: NO]; [_cell setEditable: NO]; _offsets.width = 5; _offsets.height = 5; _border_rect = _bounds; _border_type = NSGrooveBorder; _title_position = NSAtTop; _title_rect = NSZeroRect; [self setAutoresizesSubviews: NO]; _content_view = [NSView new]; [super addSubview: _content_view]; [_content_view setFrame: [self calcSizesAllowingNegative: NO]]; RELEASE(_content_view); return self; } - (void) dealloc { TEST_RELEASE(_cell); [super dealloc]; } /** *Returns the border retangle of the box
*/ - (NSRect) borderRect { return _border_rect; } /** *Returns the border type. See NSBorderType for more informations
*See Also: -setBorderType:
*/ - (NSBorderType) borderType { return _border_type; } /** *Sets the border type to aType, resizes the content view frame if needed, * and sends a -setNeedsDisplay: message. See NSBorderType for more * informations
*See Also: -borderType
*/ - (void) setBorderType: (NSBorderType)aType { if (_border_type != aType) { _border_type = aType; [_content_view setFrame: [self calcSizesAllowingNegative: NO]]; [self setNeedsDisplay: YES]; } } /** *Sets the title (cell) to aString, resizes the content view frame * and send a -setNeedsDisplay: message.
*Warning: This method does not implement the Cocoa behaviour
*See Also: -title
*/ // TODO: implement the macosx behaviour for setTitle: - (void) setTitle: (NSString *)aString { [_cell setStringValue: aString]; [_content_view setFrame: [self calcSizesAllowingNegative: NO]]; [self setNeedsDisplay: YES]; } - (void) setTitleWithMnemonic: (NSString *)aString { [_cell setTitleWithMnemonic: aString]; [_content_view setFrame: [self calcSizesAllowingNegative: NO]]; [self setNeedsDisplay: YES]; } /** *Sets the font of the title (cell) to fontObj, resizes * the content view frame if needed and sends a -setNeedsDisplay: message
*See Also: -titleFont
*/ - (void) setTitleFont: (NSFont *)fontObj { [_cell setFont: fontObj]; [_content_view setFrame: [self calcSizesAllowingNegative: NO]]; [self setNeedsDisplay: YES]; } /** *Sets the title (cell) position to aPosition, resizes the content *view frame if needed and sends a -setNeedsDisplay: message. *See NSTitlePosition for more information
*See Also: -titlePosition
* */ - (void) setTitlePosition: (NSTitlePosition)aPosition { if (_title_position != aPosition) { _title_position = aPosition; [_content_view setFrame: [self calcSizesAllowingNegative: NO]]; [self setNeedsDisplay: YES]; } } /** *Returns the title (cell) string
*See Also: -setTitle:
*/ - (NSString*) title { return [_cell stringValue]; } /** *Returns the box title cell
* */ - (id) titleCell { return _cell; } /** *Returns the the box title font
*See Also: -setTitleFont:
*/ - (NSFont*) titleFont { return [_cell font]; } /** *Returns the title position. See NSTitlePosition for more informations
*See Also: -setTitlePosition:
*/ - (NSTitlePosition) titlePosition { return _title_position; } /** *Returns the title rectangle
*/ - (NSRect) titleRect { return _title_rect; } /** *Returns the content view. The content view is resized ....TODO...
*See Also: -setContentView:
*/ - (id) contentView { return _content_view; } /** *Returns an NSSize containing the interior margins of the receiver. * An NSBox's content view margins are empty space that is subtracted * from the top, bottom, and sides as padding between the inside of the box * and the frame of its content view
*See Also: -setContentViewMargins:
*/ - (NSSize) contentViewMargins { return _offsets; } /** *Sets the content view to aView. The current content view is replaced * by -replaceSubview:with:. So you should -retain the current * view if you want to use it later. The contentView frame is resized if needed * See -contentView to know how the contentView frame is resized
*See Also: -contentView
*/ - (void) setContentView: (NSView*)aView { if (aView) { [super replaceSubview: _content_view with: aView]; _content_view = aView; [_content_view setFrame: [self calcSizesAllowingNegative: NO]]; } } /** *Sets the margins size of the content view to offsetSize, resized the *content view frame if needed and sends a -setNeedsDisplay message. * See -contentView for more informations to know how the contentView frame *is resized
*See Also: -contentViewMargins
*/ - (void) setContentViewMargins: (NSSize)offsetSize { NSAssert(offsetSize.width >= 0 && offsetSize.height >= 0, @"illegal margins supplied"); _offsets = offsetSize; [_content_view setFrame: [self calcSizesAllowingNegative: NO]]; [self setNeedsDisplay: YES]; } // // Resizing the Box // - (void) setFrame: (NSRect)frameRect { [super setFrame: frameRect]; [_content_view setFrame: [self calcSizesAllowingNegative: NO]]; } - (void) setFrameSize: (NSSize)newSize { [super setFrameSize: newSize]; [_content_view setFrame: [self calcSizesAllowingNegative: NO]]; } /** *TODO
*/ - (void) setFrameFromContentFrame: (NSRect)contentFrame { // First calc the sizes to see how much we are off by NSRect r = [self calcSizesAllowingNegative: YES]; NSRect f = _frame; NSAssert(contentFrame.size.width >= 0 && contentFrame.size.height >= 0, @"illegal content frame supplied"); if (_super_view) r = [_super_view convertRect: r fromView: self]; // Add the difference to the frame f.size.width = f.size.width + (contentFrame.size.width - r.size.width); f.size.height = f.size.height + (contentFrame.size.height - r.size.height); f.origin.x = f.origin.x + (contentFrame.origin.x - r.origin.x); f.origin.y = f.origin.y + (contentFrame.origin.y - r.origin.y); [self setFrame: f]; [_content_view setFrame: [self calcSizesAllowingNegative: NO]]; } -(NSSize) minimumSize { NSRect rect; NSSize borderSize = _sizeForBorderType (_border_type); if ([_content_view respondsToSelector: @selector(minimumSize)]) { rect.size = [_content_view minimumSize]; } else { NSArray *subviewArray = [_content_view subviews]; if ([subviewArray count]) { id subview; NSEnumerator *enumerator; enumerator = [subviewArray objectEnumerator]; rect = [[enumerator nextObject] frame]; // Loop through subviews and calculate rect // to encompass all while ((subview = [enumerator nextObject])) { rect = NSUnionRect(rect, [subview frame]); } } else // _content_view has no subviews { rect = NSZeroRect; } } rect.size = [self convertSize: rect.size fromView:_content_view]; rect.size.width += (2 * _offsets.width) + (2 * borderSize.width); rect.size.height += (2 * _offsets.height) + (2 * borderSize.height); return rect.size; } /** *TODO
*/ - (void) sizeToFit { NSRect f; if ([_content_view respondsToSelector: @selector(sizeToFit)]) { [_content_view sizeToFit]; } else // _content_view !respondsToSelector: sizeToFit { NSArray *subviewArray = [_content_view subviews]; if ([subviewArray count]) { id o, e = [subviewArray objectEnumerator]; NSRect r = [[e nextObject] frame]; // Loop through subviews and calculate rect to encompass all while ((o = [e nextObject])) { r = NSUnionRect(r, [o frame]); } [_content_view setBoundsOrigin: r.origin]; r.size = [self convertSize: r.size fromView: _content_view]; [_content_view setAutoresizesSubviews: NO]; [_content_view setFrameSize: r.size]; [_content_view setAutoresizesSubviews: YES]; } else // _content_view has no subviews { [_content_view setFrame: [self calcSizesAllowingNegative: NO]]; } } f = [_content_view frame]; // The box width should be enough to display the title if (_title_position != NSNoTitle) { NSSize titleSize = [_cell cellSize]; titleSize.width += 6; if (f.size.width < titleSize.width) f.size.width = titleSize.width; } if (_super_view != nil) [self setFrameFromContentFrame: [self convertRect: f toView: _super_view]]; else // _super_view == nil [self setFrameFromContentFrame: f]; } - (void) resizeWithOldSuperviewSize: (NSSize)oldSize { [super resizeWithOldSuperviewSize: oldSize]; [_content_view setFrame: [self calcSizesAllowingNegative: NO]]; } // // Managing the NSView Hierarchy // - (void) addSubview: (NSView*)aView { [_content_view addSubview: aView]; } - (void) addSubview: (NSView*)aView positioned: (NSWindowOrderingMode)place relativeTo: (NSView*)otherView { [_content_view addSubview: aView positioned: place relativeTo: otherView]; } - (void) replaceSubview: (NSView *)aView with: (NSView*) newView { [_content_view replaceSubview: aView with: newView]; } // // Displaying // - (void) drawRect: (NSRect)rect { NSColor *color = [_window backgroundColor]; rect = NSIntersectionRect(_bounds, rect); // Fill inside [color set]; NSRectFill(rect); // Draw border switch (_border_type) { case NSNoBorder: break; case NSLineBorder: [[NSColor controlDarkShadowColor] set]; NSFrameRect(_border_rect); break; case NSBezelBorder: [GSDrawFunctions drawDarkBezel: _border_rect : rect]; break; case NSGrooveBorder: [GSDrawFunctions drawGroove: _border_rect : rect]; break; } // Draw title if (_title_position != NSNoTitle) { // If the title is on the border, clip a hole in the later if ((_border_type != NSNoBorder) && ((_title_position == NSAtTop) || (_title_position == NSAtBottom))) { [color set]; NSRectFill(_title_rect); } [_cell drawWithFrame: _title_rect inView: self]; } } - (BOOL) isOpaque { return YES; } // // NSCoding protocol // - (void) encodeWithCoder: (NSCoder*)aCoder { [super encodeWithCoder: aCoder]; [aCoder encodeObject: _cell]; [aCoder encodeSize: _offsets]; [aCoder encodeValueOfObjCType: @encode(NSBorderType) at: &_border_type]; [aCoder encodeValueOfObjCType: @encode(NSTitlePosition) at: &_title_position]; // NB: the content view is our (only) subview, so it is already // encoded by NSView. } - (id) initWithCoder: (NSCoder*)aDecoder { self = [super initWithCoder: aDecoder]; if ([aDecoder allowsKeyedCoding]) { NSView *contentView = [aDecoder decodeObjectForKey: @"NSContentView"]; NSCell *titleCell = [aDecoder decodeObjectForKey: @"NSTitleCell"]; [self setContentView: contentView]; ASSIGN(_cell, titleCell); if ([aDecoder containsValueForKey: @"NSBoxType"]) { //int boxType = [aDecoder decodeIntForKey: @"NSBoxType"]; } if ([aDecoder containsValueForKey: @"NSBorderType"]) { NSBorderType borderType = [aDecoder decodeIntForKey: @"NSBorderType"]; [self setBorderType: borderType]; } if ([aDecoder containsValueForKey: @"NSTitlePosition"]) { NSTitlePosition titlePosition = [aDecoder decodeIntForKey: @"NSTitlePosition"]; [self setTitlePosition: titlePosition]; } if ([aDecoder containsValueForKey: @"NSTransparent"]) { //Bool transparent = [aDecoder decodeBoolForKey: @"NSTransparent"]; } if ([aDecoder containsValueForKey: @"NSOffsets"]) { [self setContentViewMargins: [aDecoder decodeSizeForKey: @"NSOffsets"]]; } } else { [aDecoder decodeValueOfObjCType: @encode(id) at: &_cell]; _offsets = [aDecoder decodeSize]; [aDecoder decodeValueOfObjCType: @encode(NSBorderType) at: &_border_type]; [aDecoder decodeValueOfObjCType: @encode(NSTitlePosition) at: &_title_position]; // The content view is our only sub_view if ([_sub_views count] == 0) { NSDebugLLog(@"NSBox", @"NSBox: decoding without content view\n"); // No content view _content_view = nil; [self calcSizesAllowingNegative: NO]; } else { if ([_sub_views count] != 1) { NSLog (@"Warning: Encoded NSBox with more than one content view!"); } _content_view = [_sub_views objectAtIndex: 0]; // The following also computes _title_rect and _border_rect. [_content_view setFrame: [self calcSizesAllowingNegative: NO]]; } } return self; } @end @implementation NSBox (Private) - (NSRect) calcSizesAllowingNegative: (BOOL)aFlag { NSRect r = NSZeroRect; switch (_title_position) { case NSNoTitle: { NSSize borderSize = _sizeForBorderType (_border_type); _border_rect = _bounds; _title_rect = NSZeroRect; // Add the offsets to the border rect r.origin.x = _offsets.width + borderSize.width; r.origin.y = _offsets.height + borderSize.height; r.size.width = _border_rect.size.width - (2 * _offsets.width) - (2 * borderSize.width); r.size.height = _border_rect.size.height - (2 * _offsets.height) - (2 * borderSize.height); break; } case NSAboveTop: { NSSize titleSize = [_cell cellSize]; NSSize borderSize = _sizeForBorderType (_border_type); float c; // Add spacer around title titleSize.width += 6; titleSize.height += 2; // Adjust border rect by title cell _border_rect = _bounds; _border_rect.size.height -= titleSize.height + borderSize.height; // Add the offsets to the border rect r.origin.x = _border_rect.origin.x + _offsets.width + borderSize.width; r.origin.y = _border_rect.origin.y + _offsets.height + borderSize.height; r.size.width = _border_rect.size.width - (2 * _offsets.width) - (2 * borderSize.width); r.size.height = _border_rect.size.height - (2 * _offsets.height) - (2 * borderSize.height); // center the title cell c = (_bounds.size.width - titleSize.width) / 2; if (c < 0) c = 0; _title_rect.origin.x = _bounds.origin.x + c; _title_rect.origin.y = _bounds.origin.y + _border_rect.size.height + borderSize.height; _title_rect.size = titleSize; break; } case NSBelowTop: { NSSize titleSize = [_cell cellSize]; NSSize borderSize = _sizeForBorderType (_border_type); float c; // Add spacer around title titleSize.width += 6; titleSize.height += 2; // Adjust border rect by title cell _border_rect = _bounds; // Add the offsets to the border rect r.origin.x = _border_rect.origin.x + _offsets.width + borderSize.width; r.origin.y = _border_rect.origin.y + _offsets.height + borderSize.height; r.size.width = _border_rect.size.width - (2 * _offsets.width) - (2 * borderSize.width); r.size.height = _border_rect.size.height - (2 * _offsets.height) - (2 * borderSize.height); // Adjust by the title size r.size.height -= titleSize.height + borderSize.height; // center the title cell c = (_border_rect.size.width - titleSize.width) / 2; if (c < 0) c = 0; _title_rect.origin.x = _border_rect.origin.x + c; _title_rect.origin.y = _border_rect.origin.y + _border_rect.size.height - titleSize.height - borderSize.height; _title_rect.size = titleSize; break; } case NSAtTop: { NSSize titleSize = [_cell cellSize]; NSSize borderSize = _sizeForBorderType (_border_type); float c; float topMargin; float topOffset; // Add spacer around title titleSize.width += 6; titleSize.height += 2; _border_rect = _bounds; topMargin = ceil(titleSize.height / 2); topOffset = titleSize.height - topMargin; // Adjust by the title size _border_rect.size.height -= topMargin; // Add the offsets to the border rect r.origin.x = _border_rect.origin.x + _offsets.width + borderSize.width; r.size.width = _border_rect.size.width - (2 * _offsets.width) - (2 * borderSize.width); if (topOffset > _offsets.height) { r.origin.y = _border_rect.origin.y + _offsets.height + borderSize.height; r.size.height = _border_rect.size.height - _offsets.height - (2 * borderSize.height) - topOffset; } else { r.origin.y = _border_rect.origin.y + _offsets.height + borderSize.height; r.size.height = _border_rect.size.height - (2 * _offsets.height) - (2 * borderSize.height); } // Adjust by the title size // r.size.height -= titleSize.height + borderSize.height; // center the title cell c = (_border_rect.size.width - titleSize.width) / 2; if (c < 0) c = 0; _title_rect.origin.x = _border_rect.origin.x + c; _title_rect.origin.y = _border_rect.origin.y + _border_rect.size.height - topMargin; _title_rect.size = titleSize; break; } case NSAtBottom: { NSSize titleSize = [_cell cellSize]; NSSize borderSize = _sizeForBorderType (_border_type); float c; float bottomMargin; float bottomOffset; // Add spacer around title titleSize.width += 6; titleSize.height += 2; _border_rect = _bounds; bottomMargin = ceil(titleSize.height / 2); bottomOffset = titleSize.height - bottomMargin; // Adjust by the title size _border_rect.origin.y += bottomMargin; _border_rect.size.height -= bottomMargin; // Add the offsets to the border rect r.origin.x = _border_rect.origin.x + _offsets.width + borderSize.width; r.size.width = _border_rect.size.width - (2 * _offsets.width) - (2 * borderSize.width); if (bottomOffset > _offsets.height) { r.origin.y = _border_rect.origin.y + bottomOffset + borderSize.height; r.size.height = _border_rect.size.height - _offsets.height - bottomOffset - (2 * borderSize.height); } else { r.origin.y = _border_rect.origin.y + _offsets.height + borderSize.height; r.size.height = _border_rect.size.height - (2 * _offsets.height) - (2 * borderSize.height); } // Adjust by the title size /* r.origin.y += (titleSize.height / 2) + borderSize.height; r.size.height -= (titleSize.height / 2) + borderSize.height; */ // center the title cell c = (_border_rect.size.width - titleSize.width) / 2; if (c < 0) c = 0; _title_rect.origin.x = c; _title_rect.origin.y = 0; _title_rect.size = titleSize; break; } case NSBelowBottom: { NSSize titleSize = [_cell cellSize]; NSSize borderSize = _sizeForBorderType (_border_type); float c; // Add spacer around title titleSize.width += 6; titleSize.height += 2; // Adjust by the title _border_rect = _bounds; _border_rect.origin.y += titleSize.height + borderSize.height; _border_rect.size.height -= titleSize.height + borderSize.height; // Add the offsets to the border rect r.origin.x = _border_rect.origin.x + _offsets.width + borderSize.width; r.origin.y = _border_rect.origin.y + _offsets.height + borderSize.height; r.size.width = _border_rect.size.width - (2 * _offsets.width) - (2 * borderSize.width); r.size.height = _border_rect.size.height - (2 * _offsets.height) - (2 * borderSize.height); // center the title cell c = (_border_rect.size.width - titleSize.width) / 2; if (c < 0) c = 0; _title_rect.origin.x = c; _title_rect.origin.y = 0; _title_rect.size = titleSize; break; } case NSAboveBottom: { NSSize titleSize = [_cell cellSize]; NSSize borderSize = _sizeForBorderType (_border_type); float c; // Add spacer around title titleSize.width += 6; titleSize.height += 2; _border_rect = _bounds; // Add the offsets to the border rect r.origin.x = _border_rect.origin.x + _offsets.width + borderSize.width; r.origin.y = _border_rect.origin.y + _offsets.height + borderSize.height; r.size.width = _border_rect.size.width - (2 * _offsets.width) - (2 * borderSize.width); r.size.height = _border_rect.size.height - (2 * _offsets.height) - (2 * borderSize.height); // Adjust by the title size r.origin.y += titleSize.height + borderSize.height; r.size.height -= titleSize.height + borderSize.height; // center the title cell c = (_border_rect.size.width - titleSize.width) / 2; if (c < 0) c = 0; _title_rect.origin.x = _border_rect.origin.x + c; _title_rect.origin.y = _border_rect.origin.y + borderSize.height; _title_rect.size = titleSize; break; } } if (!aFlag) { if (r.size.width < 0) { r.size.width = 0; } if (r.size.height < 0) { r.size.height = 0; } } return r; } @end