5 // Created by John Pannell on 2/17/06.
6 // Copyright 2006 Positive Spin Media. All rights reserved.
9 #import "PSMMetalTabStyle.h"
10 #import "PSMTabBarCell.h"
11 #import "PSMTabBarControl.h"
13 #define kPSMMetalObjectCounterRadius 7.0
14 #define kPSMMetalCounterMinWidth 20
16 // NSDrawWindowBackground() is broken for borderless windows, see
17 // http://lists.apple.com/archives/cocoa-dev/2006/Feb/msg00130.html
18 void MyNSDrawWindowBackground(NSRect rect)
20 [[NSColor windowBackgroundColor] set];
24 @implementation PSMMetalTabStyle
32 #pragma mark Creation/Destruction
36 //NSLog(@"PSMMetalTabStyle init");
38 if((self = [super init]))
41 metalCloseButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabClose_Front"]];
42 //NSLog(@"metalCloseButton=%@ path=%@", metalCloseButton,
43 // [[PSMTabBarControl bundle] pathForImageResource:@"TabClose_Front"]);
44 metalCloseButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabClose_Front_Pressed"]];
45 metalCloseButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabClose_Front_Rollover"]];
47 _addTabButtonImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabNewMetal"]];
48 _addTabButtonPressedImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabNewMetalPressed"]];
49 _addTabButtonRolloverImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabNewMetalRollover"]];
56 [metalCloseButton release];
57 [metalCloseButtonDown release];
58 [metalCloseButtonOver release];
59 [_addTabButtonImage release];
60 [_addTabButtonPressedImage release];
61 [_addTabButtonRolloverImage release];
67 #pragma mark Control Specific
69 - (float)leftMarginForTabBarControl
74 - (float)rightMarginForTabBarControl
80 #pragma mark Add Tab Button
82 - (NSImage *)addTabButtonImage
84 return _addTabButtonImage;
87 - (NSImage *)addTabButtonPressedImage
89 return _addTabButtonPressedImage;
92 - (NSImage *)addTabButtonRolloverImage
94 return _addTabButtonRolloverImage;
98 #pragma mark Cell Specific
100 - (NSRect) closeButtonRectForTabCell:(PSMTabBarCell *)cell
102 NSRect cellFrame = [cell frame];
104 if ([cell hasCloseButton] == NO) {
109 result.size = [metalCloseButton size];
110 result.origin.x = cellFrame.origin.x + MARGIN_X;
111 result.origin.y = cellFrame.origin.y + MARGIN_Y + 2.0;
113 if([cell state] == NSOnState){
114 result.origin.y -= 1;
120 - (NSRect)iconRectForTabCell:(PSMTabBarCell *)cell
122 NSRect cellFrame = [cell frame];
124 if ([cell hasIcon] == NO) {
129 result.size = NSMakeSize(kPSMTabBarIconWidth, kPSMTabBarIconWidth);
130 result.origin.x = cellFrame.origin.x + MARGIN_X;
131 result.origin.y = cellFrame.origin.y + MARGIN_Y;
133 if([cell hasCloseButton] && ![cell isCloseButtonSuppressed])
134 result.origin.x += [metalCloseButton size].width + kPSMTabBarCellPadding;
136 if([cell state] == NSOnState){
137 result.origin.y += 1;
143 - (NSRect)indicatorRectForTabCell:(PSMTabBarCell *)cell
145 NSRect cellFrame = [cell frame];
147 if ([[cell indicator] isHidden]) {
152 result.size = NSMakeSize(kPSMTabBarIndicatorWidth, kPSMTabBarIndicatorWidth);
153 result.origin.x = cellFrame.origin.x + cellFrame.size.width - MARGIN_X - kPSMTabBarIndicatorWidth;
154 result.origin.y = cellFrame.origin.y + MARGIN_Y;
156 if([cell state] == NSOnState){
157 result.origin.y -= 1;
163 - (NSRect)objectCounterRectForTabCell:(PSMTabBarCell *)cell
165 NSRect cellFrame = [cell frame];
167 if ([cell count] == 0) {
171 float countWidth = [[self attributedObjectCountValueForTabCell:cell] size].width;
172 countWidth += (2 * kPSMMetalObjectCounterRadius - 6.0);
173 if(countWidth < kPSMMetalCounterMinWidth)
174 countWidth = kPSMMetalCounterMinWidth;
177 result.size = NSMakeSize(countWidth, 2 * kPSMMetalObjectCounterRadius); // temp
178 result.origin.x = cellFrame.origin.x + cellFrame.size.width - MARGIN_X - result.size.width;
179 result.origin.y = cellFrame.origin.y + MARGIN_Y + 1.0;
181 if(![[cell indicator] isHidden])
182 result.origin.x -= kPSMTabBarIndicatorWidth + kPSMTabBarCellPadding;
188 - (float)minimumWidthOfTabCell:(PSMTabBarCell *)cell
190 float resultWidth = 0.0;
193 resultWidth = MARGIN_X;
196 if([cell hasCloseButton] && ![cell isCloseButtonSuppressed])
197 resultWidth += [metalCloseButton size].width + kPSMTabBarCellPadding;
201 resultWidth += kPSMTabBarIconWidth + kPSMTabBarCellPadding;
204 resultWidth += kPSMMinimumTitleWidth;
208 resultWidth += [self objectCounterRectForTabCell:cell].size.width + kPSMTabBarCellPadding;
211 if ([[cell indicator] isHidden] == NO)
212 resultWidth += kPSMTabBarCellPadding + kPSMTabBarIndicatorWidth;
215 resultWidth += MARGIN_X;
217 return ceil(resultWidth);
220 - (float)desiredWidthOfTabCell:(PSMTabBarCell *)cell
222 float resultWidth = 0.0;
225 resultWidth = MARGIN_X;
228 if ([cell hasCloseButton] && ![cell isCloseButtonSuppressed])
229 resultWidth += [metalCloseButton size].width + kPSMTabBarCellPadding;
233 resultWidth += kPSMTabBarIconWidth + kPSMTabBarCellPadding;
236 resultWidth += [[cell attributedStringValue] size].width;
240 resultWidth += [self objectCounterRectForTabCell:cell].size.width + kPSMTabBarCellPadding;
243 if ([[cell indicator] isHidden] == NO)
244 resultWidth += kPSMTabBarCellPadding + kPSMTabBarIndicatorWidth;
247 resultWidth += MARGIN_X;
249 return ceil(resultWidth);
253 #pragma mark Cell Values
255 - (NSAttributedString *)attributedObjectCountValueForTabCell:(PSMTabBarCell *)cell
257 NSMutableAttributedString *attrStr;
258 NSFontManager *fm = [NSFontManager sharedFontManager];
259 NSNumberFormatter *nf = [[[NSNumberFormatter alloc] init] autorelease];
260 [nf setLocalizesFormat:YES];
262 [nf setHasThousandSeparators:YES];
263 NSString *contents = [nf stringFromNumber:[NSNumber numberWithInt:[cell count]]];
264 attrStr = [[[NSMutableAttributedString alloc] initWithString:contents] autorelease];
265 NSRange range = NSMakeRange(0, [contents length]);
267 // Add font attribute
268 [attrStr addAttribute:NSFontAttributeName value:[fm convertFont:[NSFont fontWithName:@"Helvetica" size:11.0] toHaveTrait:NSBoldFontMask] range:range];
269 [attrStr addAttribute:NSForegroundColorAttributeName value:[[NSColor whiteColor] colorWithAlphaComponent:0.85] range:range];
274 - (NSAttributedString *)attributedStringValueForTabCell:(PSMTabBarCell *)cell
276 NSMutableAttributedString *attrStr;
277 NSString *contents = [cell stringValue];
278 attrStr = [[[NSMutableAttributedString alloc] initWithString:contents] autorelease];
279 NSRange range = NSMakeRange(0, [contents length]);
281 // Add font attribute
282 [attrStr addAttribute:NSFontAttributeName value:[NSFont boldSystemFontOfSize:11.0] range:range];
283 [attrStr addAttribute:NSForegroundColorAttributeName value:[[NSColor textColor] colorWithAlphaComponent:0.75] range:range];
285 // Add shadow attribute
287 shadow = [[[NSShadow alloc] init] autorelease];
289 if(([cell state] == NSOnState) || [cell isHighlighted]){
294 [shadow setShadowColor:[NSColor colorWithCalibratedWhite:1.0 alpha:shadowAlpha]];
295 [shadow setShadowOffset:NSMakeSize(0, -1)];
296 [shadow setShadowBlurRadius:1.0];
297 [attrStr addAttribute:NSShadowAttributeName value:shadow range:range];
299 // Paragraph Style for Truncating Long Text
300 static NSMutableParagraphStyle *TruncatingTailParagraphStyle = nil;
301 if (!TruncatingTailParagraphStyle) {
302 TruncatingTailParagraphStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain];
303 [TruncatingTailParagraphStyle setLineBreakMode:NSLineBreakByTruncatingTail];
304 [TruncatingTailParagraphStyle setAlignment:NSCenterTextAlignment];
306 [attrStr addAttribute:NSParagraphStyleAttributeName value:TruncatingTailParagraphStyle range:range];
312 #pragma mark ---- drawing ----
314 - (void)drawTabCell:(PSMTabBarCell *)cell
316 NSRect cellFrame = [cell frame];
317 NSColor * lineColor = nil;
318 NSBezierPath* bezier = [NSBezierPath bezierPath];
319 lineColor = [NSColor darkGrayColor];
321 if ([cell state] == NSOnState) {
323 NSRect aRect = NSMakeRect(cellFrame.origin.x, cellFrame.origin.y, cellFrame.size.width, cellFrame.size.height-2.5);
324 aRect.size.height -= 0.5;
327 MyNSDrawWindowBackground(aRect);
329 aRect.size.height+=0.5;
332 aRect.origin.x += 0.5;
334 [bezier moveToPoint:NSMakePoint(aRect.origin.x, aRect.origin.y)];
335 [bezier lineToPoint:NSMakePoint(aRect.origin.x, aRect.origin.y+aRect.size.height-1.5)];
336 [bezier lineToPoint:NSMakePoint(aRect.origin.x+1.5, aRect.origin.y+aRect.size.height)];
337 [bezier lineToPoint:NSMakePoint(aRect.origin.x+aRect.size.width-1.5, aRect.origin.y+aRect.size.height)];
338 [bezier lineToPoint:NSMakePoint(aRect.origin.x+aRect.size.width, aRect.origin.y+aRect.size.height-1.5)];
339 [bezier lineToPoint:NSMakePoint(aRect.origin.x+aRect.size.width, aRect.origin.y)];
340 if([[cell controlView] frame].size.height < 2){
341 // special case of hidden control; need line across top of cell
342 [bezier moveToPoint:NSMakePoint(aRect.origin.x, aRect.origin.y+0.5)];
343 [bezier lineToPoint:NSMakePoint(aRect.origin.x+aRect.size.width, aRect.origin.y+0.5)];
349 NSRect aRect = NSMakeRect(cellFrame.origin.x, cellFrame.origin.y, cellFrame.size.width, cellFrame.size.height);
350 aRect.origin.y += 0.5;
351 aRect.origin.x += 1.5;
352 aRect.size.width -= 1;
355 if ([cell isHighlighted]) {
356 [[NSColor colorWithCalibratedWhite:0.0 alpha:0.1] set];
357 NSRectFillUsingOperation(aRect, NSCompositeSourceAtop);
361 aRect.size.width += 1;
365 [bezier moveToPoint:NSMakePoint(aRect.origin.x, aRect.origin.y)];
366 [bezier lineToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y)];
367 if(!([cell tabState] & PSMTab_RightIsSelectedMask)){
368 [bezier lineToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y + aRect.size.height)];
373 [self drawInteriorWithTabCell:cell inView:[cell controlView]];
378 - (void)drawInteriorWithTabCell:(PSMTabBarCell *)cell inView:(NSView*)controlView
380 NSRect cellFrame = [cell frame];
381 float labelPosition = cellFrame.origin.x + MARGIN_X;
384 if ([cell hasCloseButton] && ![cell isCloseButtonSuppressed]) {
385 NSSize closeButtonSize = NSZeroSize;
386 NSRect closeButtonRect = [cell closeButtonRectForFrame:cellFrame];
387 NSImage * closeButton = nil;
389 closeButton = metalCloseButton;
390 if ([cell closeButtonOver]) closeButton = metalCloseButtonOver;
391 if ([cell closeButtonPressed]) closeButton = metalCloseButtonDown;
393 closeButtonSize = [closeButton size];
394 if ([controlView isFlipped]) {
395 closeButtonRect.origin.y += closeButtonRect.size.height;
398 [closeButton compositeToPoint:closeButtonRect.origin operation:NSCompositeSourceOver fraction:1.0];
401 labelPosition += closeButtonSize.width + kPSMTabBarCellPadding;
406 NSRect iconRect = [self iconRectForTabCell:cell];
407 NSImage *icon = [[[[cell representedObject] identifier] content] icon];
408 if ([controlView isFlipped]) {
409 iconRect.origin.y = cellFrame.size.height - iconRect.origin.y;
411 [icon compositeToPoint:iconRect.origin operation:NSCompositeSourceOver fraction:1.0];
414 labelPosition += iconRect.size.width + kPSMTabBarCellPadding;
418 if([cell count] > 0){
419 [[NSColor colorWithCalibratedWhite:0.3 alpha:0.6] set];
420 NSBezierPath *path = [NSBezierPath bezierPath];
421 NSRect myRect = [self objectCounterRectForTabCell:cell];
422 if([cell state] == NSOnState)
423 myRect.origin.y -= 1.0;
424 [path moveToPoint:NSMakePoint(myRect.origin.x + kPSMMetalObjectCounterRadius, myRect.origin.y)];
425 [path lineToPoint:NSMakePoint(myRect.origin.x + myRect.size.width - kPSMMetalObjectCounterRadius, myRect.origin.y)];
426 [path appendBezierPathWithArcWithCenter:NSMakePoint(myRect.origin.x + myRect.size.width - kPSMMetalObjectCounterRadius, myRect.origin.y + kPSMMetalObjectCounterRadius) radius:kPSMMetalObjectCounterRadius startAngle:270.0 endAngle:90.0];
427 [path lineToPoint:NSMakePoint(myRect.origin.x + kPSMMetalObjectCounterRadius, myRect.origin.y + myRect.size.height)];
428 [path appendBezierPathWithArcWithCenter:NSMakePoint(myRect.origin.x + kPSMMetalObjectCounterRadius, myRect.origin.y + kPSMMetalObjectCounterRadius) radius:kPSMMetalObjectCounterRadius startAngle:90.0 endAngle:270.0];
431 // draw attributed string centered in area
432 NSRect counterStringRect;
433 NSAttributedString *counterString = [self attributedObjectCountValueForTabCell:cell];
434 counterStringRect.size = [counterString size];
435 counterStringRect.origin.x = myRect.origin.x + ((myRect.size.width - counterStringRect.size.width) / 2.0) + 0.25;
436 counterStringRect.origin.y = myRect.origin.y + ((myRect.size.height - counterStringRect.size.height) / 2.0) + 0.5;
437 [counterString drawInRect:counterStringRect];
442 labelRect.origin.x = labelPosition;
443 labelRect.size.width = cellFrame.size.width - (labelRect.origin.x - cellFrame.origin.x) - kPSMTabBarCellPadding;
444 labelRect.size.height = cellFrame.size.height;
445 labelRect.origin.y = cellFrame.origin.y + MARGIN_Y + 1.0;
447 if([cell state] == NSOnState){
448 labelRect.origin.y -= 1;
451 if(![[cell indicator] isHidden])
452 labelRect.size.width -= (kPSMTabBarIndicatorWidth + kPSMTabBarCellPadding);
455 labelRect.size.width -= ([self objectCounterRectForTabCell:cell].size.width + kPSMTabBarCellPadding);
458 [[cell attributedStringValue] drawInRect:labelRect];
461 - (void)drawTabBar:(PSMTabBarControl *)bar inRect:(NSRect)rect
463 MyNSDrawWindowBackground(rect);
464 [[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set];
465 NSRectFillUsingOperation(rect, NSCompositeSourceAtop);
466 [[NSColor darkGrayColor] set];
467 [NSBezierPath strokeLineFromPoint:NSMakePoint(rect.origin.x,rect.origin.y+0.5) toPoint:NSMakePoint(rect.origin.x+rect.size.width,rect.origin.y+0.5)];
468 [NSBezierPath strokeLineFromPoint:NSMakePoint(rect.origin.x,rect.origin.y+rect.size.height-0.5) toPoint:NSMakePoint(rect.origin.x+rect.size.width,rect.origin.y+rect.size.height-0.5)];
470 // no tab view == not connected
472 NSRect labelRect = rect;
473 labelRect.size.height -= 4.0;
474 labelRect.origin.y += 4.0;
475 NSMutableAttributedString *attrStr;
476 NSString *contents = @"PSMTabBarControl";
477 attrStr = [[[NSMutableAttributedString alloc] initWithString:contents] autorelease];
478 NSRange range = NSMakeRange(0, [contents length]);
479 [attrStr addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:11.0] range:range];
480 NSMutableParagraphStyle *centeredParagraphStyle = nil;
481 if (!centeredParagraphStyle) {
482 centeredParagraphStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain];
483 [centeredParagraphStyle setAlignment:NSCenterTextAlignment];
485 [attrStr addAttribute:NSParagraphStyleAttributeName value:centeredParagraphStyle range:range];
486 [attrStr drawInRect:labelRect];
491 NSEnumerator *e = [[bar cells] objectEnumerator];
493 while(cell = [e nextObject]){
494 if(![cell isInOverflowMenu]){
495 [cell drawWithFrame:[cell frame] inView:bar];
501 #pragma mark Archiving
503 - (void)encodeWithCoder:(NSCoder *)aCoder
505 //[super encodeWithCoder:aCoder];
506 if ([aCoder allowsKeyedCoding]) {
507 [aCoder encodeObject:metalCloseButton forKey:@"metalCloseButton"];
508 [aCoder encodeObject:metalCloseButtonDown forKey:@"metalCloseButtonDown"];
509 [aCoder encodeObject:metalCloseButtonOver forKey:@"metalCloseButtonOver"];
510 [aCoder encodeObject:_addTabButtonImage forKey:@"addTabButtonImage"];
511 [aCoder encodeObject:_addTabButtonPressedImage forKey:@"addTabButtonPressedImage"];
512 [aCoder encodeObject:_addTabButtonRolloverImage forKey:@"addTabButtonRolloverImage"];
516 - (id)initWithCoder:(NSCoder *)aDecoder
518 // self = [super initWithCoder:aDecoder];
520 if ([aDecoder allowsKeyedCoding]) {
521 metalCloseButton = [[aDecoder decodeObjectForKey:@"metalCloseButton"] retain];
522 metalCloseButtonDown = [[aDecoder decodeObjectForKey:@"metalCloseButtonDown"] retain];
523 metalCloseButtonOver = [[aDecoder decodeObjectForKey:@"metalCloseButtonOver"] retain];
524 _addTabButtonImage = [[aDecoder decodeObjectForKey:@"addTabButtonImage"] retain];
525 _addTabButtonPressedImage = [[aDecoder decodeObjectForKey:@"addTabButtonPressedImage"] retain];
526 _addTabButtonRolloverImage = [[aDecoder decodeObjectForKey:@"addTabButtonRolloverImage"] retain];