root/tags/2D14/AICustomTabCell.m

Revision 2558, 15.4 kB (checked in by eridius, 3 years ago)

Remove all trailing whitespace from lines
Remove indentation on blank lines

Line 
1 /*-------------------------------------------------------------------------------------------------------*\
2 | Adium, Copyright (C) 2001-2004, Adam Iser  (adamiser@mac.com | http://www.adiumx.com)                   |
3 \---------------------------------------------------------------------------------------------------------/
4  | This program is free software; you can redistribute it and/or modify it under the terms of the GNU
5  | General Public License as published by the Free Software Foundation; either version 2 of the License,
6  | or (at your option) any later version.
7  |
8  | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
9  | the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
10  | Public License for more details.
11  |
12  | You should have received a copy of the GNU General Public License along with this program; if not,
13  | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
14  \------------------------------------------------------------------------------------------------------ */
15
16 #import "AICustomTabCell.h"
17 #import "AICustomTabsView.h"
18
19 #define SHOW_CLOSE_BUTTON_FOR_SINGLE_TAB        YES             //Show close button when there is only one tab?
20
21 //Images (Shared between AICustomTabCell instances)
22 static NSImage          *tabFrontLeft = nil;
23 static NSImage          *tabFrontMiddle = nil;
24 static NSImage          *tabFrontRight = nil;
25 static NSImage          *tabCloseFront = nil;
26 static NSImage          *tabCloseBack = nil;
27 static NSImage          *tabCloseFrontPressed = nil;
28 static NSImage          *tabCloseFrontRollover = nil;
29 static NSSize           leftCapSize;
30 static NSSize           rightCapSize;
31
32 #define TAB_CLOSE_LEFTPAD               2               //Padding left of close button
33 #define TAB_CLOSE_RIGHTPAD              3               //Padding right of close button
34
35 #define TAB_CLOSE_Y_OFFSET              2       //Vertical offset of close button from center
36
37 #define TAB_RIGHT_PAD                   5       //Tab right edge padding
38 #define TAB_LABEL_Y_OFFSET              1       //Vertical offset of label text from center
39
40 #define TAB_MIN_WIDTH                   48      //(Could be used to) Enforce a mininum tab size safari style
41 #define TAB_SELECTED_HIGHER     NO      //Draw the selected tab higher?
42
43 @interface AICustomTabCell (PRIVATE)
44 - (id)initForTabViewItem:(NSTabViewItem<AICustomTabViewItem> *)inTabViewItem customTabsView:(AICustomTabsView *)inView;
45 - (NSRect)_closeButtonRect;
46 @end
47
48 @implementation AICustomTabCell
49
50 //Create a new custom tab
51 + (id)customTabForTabViewItem:(NSTabViewItem<AICustomTabViewItem> *)inTabViewItem customTabsView:(AICustomTabsView *)inView
52 {
53     return([[[self alloc] initForTabViewItem:inTabViewItem customTabsView:inView] autorelease]);
54 }
55
56 //init
57 - (id)initForTabViewItem:(NSTabViewItem<AICustomTabViewItem> *)inTabViewItem customTabsView:(AICustomTabsView *)inView
58 {
59     static BOOL haveLoadedImages = NO;
60
61     [super init];
62
63     //Share these images between all AICustomTabCell instances
64     if(!haveLoadedImages){
65                 tabFrontLeft = [[NSImage imageNamed:@"aquaTabLeft"] retain];
66                 tabFrontMiddle = [[NSImage imageNamed:@"aquaTabMiddle"] retain];
67                 tabFrontRight = [[NSImage imageNamed:@"aquaTabRight"] retain];
68
69                 tabCloseFront = [[NSImage imageNamed:@"aquaTabClose"] retain];
70                 tabCloseBack = [[NSImage imageNamed:@"aquaTabCloseBack"] retain];
71                 tabCloseFrontPressed = [[NSImage imageNamed:@"aquaTabClosePressed"] retain];
72                 tabCloseFrontRollover = [[NSImage imageNamed:@"aquaTabCloseRollover"] retain];
73
74                 leftCapSize = [tabFrontLeft size];
75                 rightCapSize = [tabFrontRight size];
76
77         haveLoadedImages = YES;
78     }
79
80     tabViewItem = [inTabViewItem retain];
81         view = inView;
82     allowsInactiveTabClosing = NO;
83         wasEnabled = YES;
84     trackingClose = NO;
85     hoveringClose = NO;
86     selected = NO;
87     trackingTag = 0;
88     closeTrackingTag = 0;
89         toolTipTag = 0;
90
91     return(self);
92 }
93
94 //dealloc
95 - (void)dealloc
96 {
97         [attributedLabel release];
98         [tabViewItem release];
99
100     [super dealloc];
101 }
102
103 //Return the desired size of this tab
104 - (NSSize)size
105 {
106         int width = leftCapSize.width + [[self attributedLabel] size].width + rightCapSize.width +
107         (TAB_CLOSE_LEFTPAD + [[tabViewItem icon] size].width + TAB_CLOSE_RIGHTPAD) + TAB_RIGHT_PAD;
108
109     return( NSMakeSize((width > TAB_MIN_WIDTH ? width : TAB_MIN_WIDTH), leftCapSize.height) );
110 }
111
112 //Compare the width of this tab to another
113 - (NSComparisonResult)compareWidth:(AICustomTabCell *)tab
114 {
115     int tabWidth = [tab size].width;
116     int ourWidth = [self size].width;
117
118     if(tabWidth > ourWidth){
119         return(NSOrderedAscending);
120
121     }else if(tabWidth < ourWidth){
122         return(NSOrderedDescending);
123
124     }else{
125         return(NSOrderedSame);
126
127     }
128 }
129
130 //Return the tab view item this tab is representing
131 - (NSTabViewItem *)tabViewItem
132 {
133     return(tabViewItem);
134 }
135
136 //Frame of our close button
137 - (NSRect)_closeButtonRect
138 {
139         NSSize  iconSize = [[tabViewItem icon] size];
140         NSSize  closeSize = [tabCloseFront size];
141     int         centerY = (frame.size.height - [tabCloseFront size].height) / 2.0;
142
143     return(NSMakeRect(frame.origin.x + leftCapSize.width + TAB_CLOSE_LEFTPAD + ((iconSize.width - closeSize.width) / 2.0),
144                                           frame.origin.y + centerY + TAB_CLOSE_Y_OFFSET + 1,
145                                           [tabCloseFront size].width,
146                                           [tabCloseFront size].height));
147 }
148
149 //Frame of our tab icon
150 - (NSRect)_tabIconRect
151 {
152         NSSize  imageSize = [[tabViewItem icon] size];
153         int             centerY = (frame.size.height - imageSize.height) / 2.0;
154
155         return(NSMakeRect(frame.origin.x + leftCapSize.width + TAB_CLOSE_LEFTPAD,
156                                           frame.origin.y + centerY + TAB_CLOSE_Y_OFFSET,
157                                           imageSize.width,
158                                           imageSize.height));
159 }
160
161
162 //Configure ------------------------------------------------------------------------------------------------------------
163 #pragma mark Configure
164 //Allow the user to close this tab even if it's not active
165 - (void)setAllowsInactiveTabClosing:(BOOL)inValue
166 {
167     allowsInactiveTabClosing = inValue;
168 }
169 - (BOOL)allowsInactiveTabClosing{
170         return(allowsInactiveTabClosing);
171 }
172
173 //The selected tab draws differently and has special close button behavior
174 - (void)setSelected:(BOOL)inSelected
175 {
176     selected = inSelected;
177 }
178 - (BOOL)isSelected{
179     return(selected);
180 }
181
182 //When a tab is hovered it should be highlighted.  Highlighted tabs draw differently.
183 - (void)setHighlighted:(BOOL)inHighlight
184 {
185         if(highlighted != inHighlight){
186                 highlighted = inHighlight;
187         [view setNeedsDisplayInRect:[self frame]];
188         }
189 }
190 - (BOOL)isHighlighted{
191     return(highlighted);
192 }
193
194 //Set whether the close button is currently hovered
195 - (void)setHoveringClose:(BOOL)hovering
196 {
197         if(hoveringClose != hovering){
198                 hoveringClose = hovering;
199         [view setNeedsDisplayInRect:NSUnionRect([self _tabIconRect],[self _closeButtonRect])];
200         }
201 }
202
203 //Frame determines where this tab cell will draw
204 - (void)setFrame:(NSRect)inFrame
205 {
206     frame = inFrame;
207 }
208 - (NSRect)frame{
209     return(frame);
210 }
211
212
213 //Drawing --------------------------------------------------------------------------------------------------------------
214 #pragma mark Drawing
215 //Normal draw routine
216 - (void)drawWithFrame:(NSRect)rect inView:(NSView *)controlView
217 {
218         [self drawWithFrame:rect inView:controlView ignoreSelection:NO];
219 }
220
221 //Draw.  Pass ignore selection to ignore whether this tab is selected or not when drawing
222 - (void)drawWithFrame:(NSRect)rect inView:(NSView *)controlView ignoreSelection:(BOOL)ignoreSelection
223 {
224     int         middleSourceWidth, middleRightEdge, middleLeftEdge;
225     NSRect      sourceRect, destRect;
226     NSSize      labelSize;
227         NSPoint destPoint;
228
229     //Pre-calc some dimensions
230     labelSize = [tabViewItem sizeOfLabel:NO];
231     middleSourceWidth = [tabFrontMiddle size].width;
232     middleRightEdge = (rect.origin.x + rect.size.width - rightCapSize.width);
233     middleLeftEdge = (rect.origin.x + leftCapSize.width);
234
235     //Background
236     if(selected && !ignoreSelection){
237         //Draw the left cap
238         [tabFrontLeft compositeToPoint:NSMakePoint(rect.origin.x, rect.origin.y) operation:NSCompositeSourceOver];
239
240         //Draw the middle
241         sourceRect = NSMakeRect(0, 0, [tabFrontMiddle size].width, [tabFrontMiddle size].height);
242         destRect = NSMakeRect(middleLeftEdge, rect.origin.y, sourceRect.size.width, sourceRect.size.height);
243
244         while(destRect.origin.x < middleRightEdge){
245             if((destRect.origin.x + destRect.size.width) > middleRightEdge){
246                 sourceRect.size.width -= (destRect.origin.x + destRect.size.width) - middleRightEdge;
247             }
248             [tabFrontMiddle compositeToPoint:destRect.origin fromRect:sourceRect operation:NSCompositeSourceOver];
249             destRect.origin.x += destRect.size.width;
250         }
251
252         //Draw the right cap
253         [tabFrontRight compositeToPoint:NSMakePoint(middleRightEdge, rect.origin.y) operation:NSCompositeSourceOver];
254
255     }else if(highlighted){
256         [[NSColor colorWithCalibratedWhite:0.0 alpha:0.1] set];
257         [NSBezierPath fillRect:NSMakeRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height)];
258     }
259
260         //We'll display our close icon if the user is hovering.  Otherwise, we display the tab specified icon
261         NSImage *leftIcon = [tabViewItem icon];
262         if((hoveringClose && (selected || allowsInactiveTabClosing || ( [[[NSApplication sharedApplication] currentEvent] modifierFlags] & NSCommandKeyMask ))) || !leftIcon){
263                 if(hoveringClose){
264                         leftIcon = (trackingClose ? tabCloseFrontPressed : tabCloseFrontRollover);
265                 }else{
266                         leftIcon = ((selected && !ignoreSelection) ? tabCloseFront : tabCloseBack);
267                 }
268                 destPoint = [self _closeButtonRect].origin;
269
270         }else{
271                 destPoint = [self _tabIconRect].origin;
272         }
273         [leftIcon compositeToPoint:destPoint operation:NSCompositeSourceOver fraction:( hoveringClose || [tabViewItem isEnabled] ? 1. : 0.5 )];
274
275         //Move over for label drawing.  We always move based on the tab icon and not on the close button.  This prevents
276         //tab text from jumping when hovered if the tab icons are a different size from the close button
277         int     offsetX = leftCapSize.width + TAB_CLOSE_LEFTPAD + [self _tabIconRect].size.width + TAB_CLOSE_RIGHTPAD;
278         rect.origin.x += offsetX;
279         rect.size.width -= offsetX + TAB_RIGHT_PAD;
280
281         //Draw our label
282         destRect = NSMakeRect(rect.origin.x,
283                                                   rect.origin.y + TAB_LABEL_Y_OFFSET,
284                                                   rect.size.width,
285                                                   rect.size.height - ((rect.size.height - labelSize.height) / 2.0));
286     if(TAB_SELECTED_HIGHER && !ignoreSelection && selected) destRect.origin.y += 1.0;
287         [[self attributedLabel] drawInRect:destRect];
288 }
289
290 //Returns the attributed form of our label for drawing (cached)
291 - (NSAttributedString *)attributedLabel
292 {
293         NSString        *label = [tabViewItem label];
294
295         if(![label isEqualToString:[attributedLabel string]] || wasEnabled != [tabViewItem isEnabled] ){
296                 wasEnabled = [tabViewItem isEnabled];
297                 //Paragraph Style (Turn off clipping by word)
298                 NSMutableParagraphStyle *paragraphStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease];
299                 [paragraphStyle setAlignment:NSCenterTextAlignment];
300                 [paragraphStyle setLineBreakMode:NSLineBreakByTruncatingTail];
301
302                 //Update the attributed string
303                 [attributedLabel release];
304                 attributedLabel = [[NSAttributedString alloc] initWithString:[tabViewItem label] attributes:
305                         [NSDictionary dictionaryWithObjectsAndKeys:
306                                 ( wasEnabled ? [NSColor controlTextColor] : [[NSColor controlTextColor] colorWithAlphaComponent:0.5] ), NSForegroundColorAttributeName,
307                                 [NSFont systemFontOfSize:11], NSFontAttributeName,
308                                 paragraphStyle, NSParagraphStyleAttributeName,
309                                 nil]];
310         }
311
312         return(attributedLabel);
313 }
314
315
316 //Cursor tracking ------------------------------------------------------------------------------------------------------
317 #pragma mark Cursor tracking
318 //Install tracking rects for our tab and its close button
319 - (void)addTrackingRectsWithFrame:(NSRect)trackRect cursorLocation:(NSPoint)cursorLocation
320 {
321     trackingTag = [view addTrackingRect:trackRect
322                                   owner:self
323                                userData:nil
324                            assumeInside:NSPointInRect(cursorLocation, trackRect)];
325     [self setHighlighted:NSPointInRect(cursorLocation, trackRect)];
326
327     closeTrackingTag = [view addTrackingRect:[self _closeButtonRect]
328                                        owner:self
329                                     userData:nil
330                                 assumeInside:NSPointInRect(cursorLocation, [self _closeButtonRect])];
331     [self setHoveringClose:NSPointInRect(cursorLocation, [self _closeButtonRect])];
332
333         toolTipTag = [view addToolTipRect:trackRect owner:view userData:NULL];
334 }
335
336 //Remove our tracking rects
337 - (void)removeTrackingRects
338 {
339     [view removeTrackingRect:trackingTag]; trackingTag = 0;
340     [view removeTrackingRect:closeTrackingTag]; closeTrackingTag = 0;
341         [view removeToolTip:toolTipTag]; toolTipTag = 0;
342 }
343
344 //Mouse entered our tabs (or close button)
345 - (void)mouseEntered:(NSEvent *)theEvent
346 {
347         //Scrubs the tab if option/alt is down. This is damn annoying!!
348 //      if(([theEvent modifierFlags] & NSAlternateKeyMask) && !selected){
349 //              [[tabViewItem tabView] selectTabViewItem:tabViewItem];
350 //      }
351
352     //Set ourself (or our close button) as hovered
353     if((allowsInactiveTabClosing || selected || ( [[[NSApplication sharedApplication] currentEvent] modifierFlags] & NSCommandKeyMask )) &&
354            ([theEvent trackingNumber] == closeTrackingTag)){
355                 [self setHoveringClose:YES];
356     }else{
357                 [self setHighlighted:YES];
358     }
359 }
360
361 //Mouse left one of our tabs - Set ourself (or our close button) as not hovered
362 - (void)mouseExited:(NSEvent *)theEvent
363 {
364     if([theEvent trackingNumber] == closeTrackingTag){
365                 [self setHoveringClose:NO];
366     }else{
367                 [self setHighlighted:NO];
368     }
369 }
370
371
372 //Clicking & Click tracking --------------------------------------------------------------------------------------------
373 #pragma mark Clicking & Click tracking
374 //Track click and hold on the close button
375 - (BOOL)willTrackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView
376 {
377     if((allowsInactiveTabClosing || selected || ( [[[NSApplication sharedApplication] currentEvent] modifierFlags] & NSCommandKeyMask )) &&
378            (SHOW_CLOSE_BUTTON_FOR_SINGLE_TAB || [[tabViewItem tabView] numberOfTabViewItems] != 1) &&
379            NSPointInRect([controlView convertPoint:[theEvent locationInWindow] fromView:nil], [self _closeButtonRect])){
380
381         [self trackMouse:theEvent inRect:[self _closeButtonRect] ofView:controlView untilMouseUp:YES];
382         return(YES);
383
384     }else{
385         return(NO);
386
387     }
388 }
389
390 //Start Tracking.  Redisplay the close button as pressed
391 - (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView
392 {
393     trackingClose = YES;
394     hoveringClose = YES;
395     [controlView setNeedsDisplayInRect:[self _closeButtonRect]];
396
397     return(YES);
398 }
399
400 //
401 - (BOOL)continueTracking:(NSPoint)lastPoint at:(NSPoint)currentPoint inView:(NSView *)controlView
402 {
403     BOOL        hovering = NSPointInRect(currentPoint, [self _closeButtonRect]);
404
405     if(hoveringClose != hovering){
406         hoveringClose = hovering;
407         [controlView setNeedsDisplayInRect:[self _closeButtonRect]];
408     }
409
410     return(YES);
411 }
412
413 //
414 - (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:(NSView *)controlView mouseIsUp:(BOOL)flag
415 {
416     BOOL        hovering = NSPointInRect(stopPoint, [self _closeButtonRect]);
417
418         //Closes all the other tabs in the current window if option is held down (And we have more than one tab)
419         if(hovering && ([[[controlView window] currentEvent] modifierFlags] & NSAlternateKeyMask) && [[tabViewItem tabView] numberOfTabViewItems] > 1){
420                 [(AICustomTabsView *)controlView closeAllTabsExceptFor:self];
421         }else if(hovering){ //If the mouse was released over the close button, close our tab
422         [(AICustomTabsView *)controlView closeTab:self];
423     }
424
425     hoveringClose = NO;
426     trackingClose = NO;
427     [controlView setNeedsDisplayInRect:[self _closeButtonRect]];
428 }
429
430 @end
Note: See TracBrowser for help on using the browser.