2 // PSMUnifiedTabStyle.m
3 // --------------------
5 // Created by Keith Blount on 30/04/2006.
6 // Copyright 2006 __MyCompanyName__. All rights reserved.
9 #import "PSMUnifiedTabStyle.h"
10 #import "PSMTabBarCell.h"
11 #import "PSMTabBarControl.h"
12 #import "NSBezierPath_AMShading.h"
14 #define kPSMUnifiedObjectCounterRadius 7.0
15 #define kPSMUnifiedCounterMinWidth 20
17 @interface PSMUnifiedTabStyle (Private)
18 - (void)drawInteriorWithTabCell:(PSMTabBarCell *)cell inView:(NSView*)controlView;
21 @implementation PSMUnifiedTabStyle
29 #pragma mark Creation/Destruction
33 if((self = [super init]))
35 unifiedCloseButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front"]];
36 unifiedCloseButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front_Pressed"]];
37 unifiedCloseButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front_Rollover"]];
39 _addTabButtonImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNew"]];
40 _addTabButtonPressedImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNewPressed"]];
41 _addTabButtonRolloverImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNewRollover"]];
50 [unifiedCloseButton release];
51 [unifiedCloseButtonDown release];
52 [unifiedCloseButtonOver release];
53 [_addTabButtonImage release];
54 [_addTabButtonPressedImage release];
55 [_addTabButtonRolloverImage release];
61 #pragma mark Control Specific
63 - (void)setLeftMarginForTabBarControl:(float)margin
68 - (float)leftMarginForTabBarControl
73 - (float)rightMarginForTabBarControl
79 #pragma mark Add Tab Button
81 - (NSImage *)addTabButtonImage
83 return _addTabButtonImage;
86 - (NSImage *)addTabButtonPressedImage
88 return _addTabButtonPressedImage;
91 - (NSImage *)addTabButtonRolloverImage
93 return _addTabButtonRolloverImage;
97 #pragma mark Cell Specific
99 - (NSRect) closeButtonRectForTabCell:(PSMTabBarCell *)cell
101 NSRect cellFrame = [cell frame];
103 if ([cell hasCloseButton] == NO) {
108 result.size = [unifiedCloseButton size];
109 result.origin.x = cellFrame.origin.x + MARGIN_X;
110 result.origin.y = cellFrame.origin.y + MARGIN_Y + 1.0;
115 - (NSRect)iconRectForTabCell:(PSMTabBarCell *)cell
117 NSRect cellFrame = [cell frame];
119 if ([cell hasIcon] == NO) {
124 result.size = NSMakeSize(kPSMTabBarIconWidth, kPSMTabBarIconWidth);
125 result.origin.x = cellFrame.origin.x + MARGIN_X;
126 result.origin.y = cellFrame.origin.y + MARGIN_Y + 1.0;
128 if([cell hasCloseButton] && ![cell isCloseButtonSuppressed])
129 result.origin.x += [unifiedCloseButton size].width + kPSMTabBarCellPadding;
134 - (NSRect)indicatorRectForTabCell:(PSMTabBarCell *)cell
136 NSRect cellFrame = [cell frame];
138 if ([[cell indicator] isHidden]) {
143 result.size = NSMakeSize(kPSMTabBarIndicatorWidth, kPSMTabBarIndicatorWidth);
144 result.origin.x = cellFrame.origin.x + cellFrame.size.width - MARGIN_X - kPSMTabBarIndicatorWidth;
145 result.origin.y = cellFrame.origin.y + MARGIN_Y - 1.0;
150 - (NSRect)objectCounterRectForTabCell:(PSMTabBarCell *)cell
152 NSRect cellFrame = [cell frame];
154 if ([cell count] == 0) {
158 float countWidth = [[self attributedObjectCountValueForTabCell:cell] size].width;
159 countWidth += (2 * kPSMUnifiedObjectCounterRadius - 6.0);
160 if(countWidth < kPSMUnifiedCounterMinWidth)
161 countWidth = kPSMUnifiedCounterMinWidth;
164 result.size = NSMakeSize(countWidth, 2 * kPSMUnifiedObjectCounterRadius); // temp
165 result.origin.x = cellFrame.origin.x + cellFrame.size.width - MARGIN_X - result.size.width;
166 result.origin.y = cellFrame.origin.y + MARGIN_Y + 1.0;
168 if(![[cell indicator] isHidden])
169 result.origin.x -= kPSMTabBarIndicatorWidth + kPSMTabBarCellPadding;
175 - (float)minimumWidthOfTabCell:(PSMTabBarCell *)cell
177 float resultWidth = 0.0;
180 resultWidth = MARGIN_X;
183 if([cell hasCloseButton] && ![cell isCloseButtonSuppressed])
184 resultWidth += [unifiedCloseButton size].width + kPSMTabBarCellPadding;
188 resultWidth += kPSMTabBarIconWidth + kPSMTabBarCellPadding;
191 resultWidth += kPSMMinimumTitleWidth;
195 resultWidth += [self objectCounterRectForTabCell:cell].size.width + kPSMTabBarCellPadding;
198 if ([[cell indicator] isHidden] == NO)
199 resultWidth += kPSMTabBarCellPadding + kPSMTabBarIndicatorWidth;
202 resultWidth += MARGIN_X;
204 return ceil(resultWidth);
207 - (float)desiredWidthOfTabCell:(PSMTabBarCell *)cell
209 float resultWidth = 0.0;
212 resultWidth = MARGIN_X;
215 if ([cell hasCloseButton] && ![cell isCloseButtonSuppressed])
216 resultWidth += [unifiedCloseButton size].width + kPSMTabBarCellPadding;
220 resultWidth += kPSMTabBarIconWidth + kPSMTabBarCellPadding;
223 resultWidth += [[cell attributedStringValue] size].width;
227 resultWidth += [self objectCounterRectForTabCell:cell].size.width + kPSMTabBarCellPadding;
230 if ([[cell indicator] isHidden] == NO)
231 resultWidth += kPSMTabBarCellPadding + kPSMTabBarIndicatorWidth;
234 resultWidth += MARGIN_X;
236 return ceil(resultWidth);
240 #pragma mark Cell Values
242 - (NSAttributedString *)attributedObjectCountValueForTabCell:(PSMTabBarCell *)cell
244 NSMutableAttributedString *attrStr;
245 NSFontManager *fm = [NSFontManager sharedFontManager];
246 NSNumberFormatter *nf = [[[NSNumberFormatter alloc] init] autorelease];
247 [nf setLocalizesFormat:YES];
249 [nf setHasThousandSeparators:YES];
250 NSString *contents = [nf stringFromNumber:[NSNumber numberWithInt:[cell count]]];
251 attrStr = [[[NSMutableAttributedString alloc] initWithString:contents] autorelease];
252 NSRange range = NSMakeRange(0, [contents length]);
254 // Add font attribute
255 [attrStr addAttribute:NSFontAttributeName value:[fm convertFont:[NSFont fontWithName:@"Helvetica" size:11.0] toHaveTrait:NSBoldFontMask] range:range];
256 [attrStr addAttribute:NSForegroundColorAttributeName value:[[NSColor whiteColor] colorWithAlphaComponent:0.85] range:range];
261 - (NSAttributedString *)attributedStringValueForTabCell:(PSMTabBarCell *)cell
263 NSMutableAttributedString *attrStr;
264 NSString * contents = [cell stringValue];
265 attrStr = [[[NSMutableAttributedString alloc] initWithString:contents] autorelease];
266 NSRange range = NSMakeRange(0, [contents length]);
268 [attrStr addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:11.0] range:range];
270 // Paragraph Style for Truncating Long Text
271 static NSMutableParagraphStyle *TruncatingTailParagraphStyle = nil;
272 if (!TruncatingTailParagraphStyle) {
273 TruncatingTailParagraphStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain];
274 [TruncatingTailParagraphStyle setLineBreakMode:NSLineBreakByTruncatingTail];
276 [attrStr addAttribute:NSParagraphStyleAttributeName value:TruncatingTailParagraphStyle range:range];
282 #pragma mark ---- drawing ----
284 - (void)drawTabCell:(PSMTabBarCell *)cell
286 NSRect cellFrame = [cell frame];
287 NSColor * lineColor = nil;
288 NSBezierPath* bezier = [NSBezierPath bezierPath];
289 lineColor = [NSColor colorWithCalibratedWhite:0.576 alpha:1.0];
291 if ([cell state] == NSOnState)
294 NSRect aRect = NSMakeRect(cellFrame.origin.x+0.5, cellFrame.origin.y-0.5, cellFrame.size.width-1.0, cellFrame.size.height);
295 aRect.size.height -= 0.5;
297 aRect.size.height+=0.5;
300 float radius = MIN(6.0, 0.5f * MIN(NSWidth(aRect), NSHeight(aRect)));
301 NSRect rect = NSInsetRect(aRect, radius, radius);
303 [bezier appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMinY(rect)) radius:radius startAngle:180.0 endAngle:270.0];
305 [bezier appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMinY(rect)) radius:radius startAngle:270.0 endAngle:360.0];
307 NSPoint cornerPoint = NSMakePoint(NSMaxX(aRect), NSMaxY(aRect));
308 [bezier appendBezierPathWithPoints:&cornerPoint count:1];
310 cornerPoint = NSMakePoint(NSMinX(aRect), NSMaxY(aRect));
311 [bezier appendBezierPathWithPoints:&cornerPoint count:1];
315 //[[NSColor windowBackgroundColor] set];
317 [bezier linearGradientFillWithStartColor:[NSColor colorWithCalibratedWhite:0.99 alpha:1.0]
318 endColor:[NSColor colorWithCalibratedWhite:0.941 alpha:1.0]];
326 NSRect aRect = NSMakeRect(cellFrame.origin.x, cellFrame.origin.y, cellFrame.size.width, cellFrame.size.height);
327 aRect.origin.y += 0.5;
328 aRect.origin.x += 1.5;
329 aRect.size.width -= 1;
332 aRect.size.width += 1;
335 if ([cell isHighlighted])
337 [[NSColor colorWithCalibratedWhite:0.0 alpha:0.1] set];
338 NSRectFillUsingOperation(aRect, NSCompositeSourceAtop);
344 [bezier moveToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y-0.5)];
345 if(!([cell tabState] & PSMTab_RightIsSelectedMask)){
346 [bezier lineToPoint:NSMakePoint(NSMaxX(aRect), NSMaxY(aRect))];
351 // Create a thin lighter line next to the dividing line for a bezel effect
352 if(!([cell tabState] & PSMTab_RightIsSelectedMask)){
353 [[[NSColor whiteColor] colorWithAlphaComponent:0.5] set];
354 [NSBezierPath strokeLineFromPoint:NSMakePoint(NSMaxX(aRect)+1.0, aRect.origin.y-0.5)
355 toPoint:NSMakePoint(NSMaxX(aRect)+1.0, NSMaxY(aRect)-2.5)];
358 // If this is the leftmost tab, we want to draw a line on the left, too
359 if ([cell tabState] & PSMTab_PositionLeftMask)
362 [NSBezierPath strokeLineFromPoint:NSMakePoint(aRect.origin.x,aRect.origin.y-0.5)
363 toPoint:NSMakePoint(aRect.origin.x,NSMaxY(aRect)-2.5)];
364 [[[NSColor whiteColor] colorWithAlphaComponent:0.5] set];
365 [NSBezierPath strokeLineFromPoint:NSMakePoint(aRect.origin.x+1.0,aRect.origin.y-0.5)
366 toPoint:NSMakePoint(aRect.origin.x+1.0,NSMaxY(aRect)-2.5)];
370 [self drawInteriorWithTabCell:cell inView:[cell controlView]];
375 - (void)drawInteriorWithTabCell:(PSMTabBarCell *)cell inView:(NSView*)controlView
377 NSRect cellFrame = [cell frame];
378 float labelPosition = cellFrame.origin.x + MARGIN_X;
381 if ([cell hasCloseButton] && ![cell isCloseButtonSuppressed]) {
382 NSSize closeButtonSize = NSZeroSize;
383 NSRect closeButtonRect = [cell closeButtonRectForFrame:cellFrame];
384 NSImage * closeButton = nil;
386 closeButton = unifiedCloseButton;
387 if ([cell closeButtonOver]) closeButton = unifiedCloseButtonOver;
388 if ([cell closeButtonPressed]) closeButton = unifiedCloseButtonDown;
390 closeButtonSize = [closeButton size];
391 if ([controlView isFlipped]) {
392 closeButtonRect.origin.y += closeButtonRect.size.height;
395 [closeButton compositeToPoint:closeButtonRect.origin operation:NSCompositeSourceOver fraction:1.0];
398 labelPosition += closeButtonSize.width + kPSMTabBarCellPadding;
403 NSRect iconRect = [self iconRectForTabCell:cell];
404 NSImage *icon = [[[[cell representedObject] identifier] content] icon];
405 if ([controlView isFlipped]) {
406 iconRect.origin.y = cellFrame.size.height - iconRect.origin.y;
408 [icon compositeToPoint:iconRect.origin operation:NSCompositeSourceOver fraction:1.0];
411 labelPosition += iconRect.size.width + kPSMTabBarCellPadding;
415 if([cell count] > 0){
416 [[NSColor colorWithCalibratedWhite:0.3 alpha:0.6] set];
417 NSBezierPath *path = [NSBezierPath bezierPath];
418 NSRect myRect = [self objectCounterRectForTabCell:cell];
419 myRect.origin.y -= 1.0;
420 [path moveToPoint:NSMakePoint(myRect.origin.x + kPSMUnifiedObjectCounterRadius, myRect.origin.y)];
421 [path lineToPoint:NSMakePoint(myRect.origin.x + myRect.size.width - kPSMUnifiedObjectCounterRadius, myRect.origin.y)];
422 [path appendBezierPathWithArcWithCenter:NSMakePoint(myRect.origin.x + myRect.size.width - kPSMUnifiedObjectCounterRadius, myRect.origin.y + kPSMUnifiedObjectCounterRadius) radius:kPSMUnifiedObjectCounterRadius startAngle:270.0 endAngle:90.0];
423 [path lineToPoint:NSMakePoint(myRect.origin.x + kPSMUnifiedObjectCounterRadius, myRect.origin.y + myRect.size.height)];
424 [path appendBezierPathWithArcWithCenter:NSMakePoint(myRect.origin.x + kPSMUnifiedObjectCounterRadius, myRect.origin.y + kPSMUnifiedObjectCounterRadius) radius:kPSMUnifiedObjectCounterRadius startAngle:90.0 endAngle:270.0];
427 // draw attributed string centered in area
428 NSRect counterStringRect;
429 NSAttributedString *counterString = [self attributedObjectCountValueForTabCell:cell];
430 counterStringRect.size = [counterString size];
431 counterStringRect.origin.x = myRect.origin.x + ((myRect.size.width - counterStringRect.size.width) / 2.0) + 0.25;
432 counterStringRect.origin.y = myRect.origin.y + ((myRect.size.height - counterStringRect.size.height) / 2.0) + 0.5;
433 [counterString drawInRect:counterStringRect];
438 labelRect.origin.x = labelPosition;
439 labelRect.size.width = cellFrame.size.width - (labelRect.origin.x - cellFrame.origin.x) - kPSMTabBarCellPadding;
440 NSSize s = [[cell attributedStringValue] size];
441 labelRect.origin.y = cellFrame.origin.y + (cellFrame.size.height-s.height)/2.0 - 1.0;
442 labelRect.size.height = s.height;
444 if(![[cell indicator] isHidden])
445 labelRect.size.width -= (kPSMTabBarIndicatorWidth + kPSMTabBarCellPadding);
448 labelRect.size.width -= ([self objectCounterRectForTabCell:cell].size.width + kPSMTabBarCellPadding);
451 [[cell attributedStringValue] drawInRect:labelRect];
454 - (void)drawTabBar:(PSMTabBarControl *)bar inRect:(NSRect)rect
456 NSRect gradientRect = rect;
457 gradientRect.size.height -= 1.0;
458 NSBezierPath *path = [NSBezierPath bezierPathWithRect:gradientRect];
459 [path linearGradientFillWithStartColor:[NSColor colorWithCalibratedWhite:0.918 alpha:1.0]
460 endColor:[NSColor colorWithCalibratedWhite:0.843 alpha:1.0]];
461 [[NSColor colorWithCalibratedWhite:0.576 alpha:1.0] set];
462 [NSBezierPath strokeLineFromPoint:NSMakePoint(rect.origin.x,NSMaxY(rect)-0.5)
463 toPoint:NSMakePoint(NSMaxX(rect),NSMaxY(rect)-0.5)];
465 // no tab view == not connected
467 NSRect labelRect = rect;
468 labelRect.size.height -= 4.0;
469 labelRect.origin.y += 4.0;
470 NSMutableAttributedString *attrStr;
471 NSString *contents = @"PSMTabBarControl";
472 attrStr = [[[NSMutableAttributedString alloc] initWithString:contents] autorelease];
473 NSRange range = NSMakeRange(0, [contents length]);
474 [attrStr addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:11.0] range:range];
475 NSMutableParagraphStyle *centeredParagraphStyle = nil;
476 if (!centeredParagraphStyle) {
477 centeredParagraphStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain];
478 [centeredParagraphStyle setAlignment:NSCenterTextAlignment];
480 [attrStr addAttribute:NSParagraphStyleAttributeName value:centeredParagraphStyle range:range];
481 [attrStr drawInRect:labelRect];
486 NSEnumerator *e = [[bar cells] objectEnumerator];
488 while(cell = [e nextObject]){
489 if(![cell isInOverflowMenu]){
490 [cell drawWithFrame:[cell frame] inView:bar];
496 #pragma mark Archiving
498 - (void)encodeWithCoder:(NSCoder *)aCoder
500 //[super encodeWithCoder:aCoder];
501 if ([aCoder allowsKeyedCoding]) {
502 [aCoder encodeObject:unifiedCloseButton forKey:@"unifiedCloseButton"];
503 [aCoder encodeObject:unifiedCloseButtonDown forKey:@"unifiedCloseButtonDown"];
504 [aCoder encodeObject:unifiedCloseButtonOver forKey:@"unifiedCloseButtonOver"];
505 [aCoder encodeObject:_addTabButtonImage forKey:@"addTabButtonImage"];
506 [aCoder encodeObject:_addTabButtonPressedImage forKey:@"addTabButtonPressedImage"];
507 [aCoder encodeObject:_addTabButtonRolloverImage forKey:@"addTabButtonRolloverImage"];
511 - (id)initWithCoder:(NSCoder *)aDecoder
513 // self = [super initWithCoder:aDecoder];
515 if ([aDecoder allowsKeyedCoding]) {
516 unifiedCloseButton = [[aDecoder decodeObjectForKey:@"unifiedCloseButton"] retain];
517 unifiedCloseButtonDown = [[aDecoder decodeObjectForKey:@"unifiedCloseButtonDown"] retain];
518 unifiedCloseButtonOver = [[aDecoder decodeObjectForKey:@"unifiedCloseButtonOver"] retain];
519 _addTabButtonImage = [[aDecoder decodeObjectForKey:@"addTabButtonImage"] retain];
520 _addTabButtonPressedImage = [[aDecoder decodeObjectForKey:@"addTabButtonPressedImage"] retain];
521 _addTabButtonRolloverImage = [[aDecoder decodeObjectForKey:@"addTabButtonRolloverImage"] retain];