extra/MacOSX/VLC_app: Initial import of a wanna-be VLC.app application that use the...
[vlc.git] / extras / MacOSX / VLC_app / VLCCategoryCell.m
1 /*****************************************************************************
2  * VLCCategoryCell.h: VLC.app custom cell
3  * Most of the code here from Colloquy (GPL v2)
4  *****************************************************************************
5  * Copyright (C) 2007 Pierre d'Herbemont
6  * Copyright (C) 2007 the VideoLAN team
7  * $Id$
8  *
9  * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25 #import "VLCCategoryCell.h"
26
27 @implementation VLCCategoryCell
28 - (id) init {
29         if( ( self = [super init] ) ) {
30                 _altImage = nil;
31                 _statusImage = nil;
32                 _mainText = nil;
33                 _infoText = nil;
34         _boldAndWhiteOnHighlight = YES;
35         _selectable = YES;
36
37                 [self setImageAlignment:NSImageAlignLeft];
38                 [self setImageScaling:NSScaleProportionally];
39                 [self setImageFrameStyle:NSImageFrameNone];
40                 [self setLineBreakMode:NSLineBreakByTruncatingTail];
41         }
42
43         return self;
44 }
45
46 - (id) copyWithZone:(NSZone *) zone {
47         VLCCategoryCell *cell = (VLCCategoryCell *)[super copyWithZone:zone];
48         cell -> _statusImage = [_statusImage retain];
49         cell -> _altImage = [_altImage retain];
50         cell -> _mainText = [_mainText copyWithZone:zone];
51         cell -> _infoText = [_infoText copyWithZone:zone];
52         cell -> _lineBreakMode = _lineBreakMode;
53         return cell;
54 }
55
56 - (void) dealloc {
57         [_altImage release];
58         [_statusImage release];
59         [_mainText release];
60         [_infoText release];
61
62         _altImage = nil;
63         _statusImage = nil;
64         _mainText = nil;
65         _infoText = nil;
66
67         [super dealloc];
68 }
69
70 #pragma mark -
71
72 - (void) setStatusImage:(NSImage *) image {
73         [_statusImage autorelease];
74         _statusImage = [image retain];
75 }
76
77 - (NSImage *) statusImage {
78         return _statusImage;
79 }
80
81 #pragma mark -
82
83 - (void) setStatusNumber:(unsigned) number {
84         _statusNumber = number;
85 }
86
87 - (unsigned) statusNumber {
88         return _statusNumber;
89 }
90
91 #pragma mark -
92
93 - (void) setImportantStatusNumber:(unsigned) number {
94         _importantStatusNumber = number;
95 }
96
97 - (unsigned) importantStatusNumber {
98         return _importantStatusNumber;
99 }
100
101 #pragma mark -
102
103 - (void) setHighlightedImage:(NSImage *) image {
104         [_altImage autorelease];
105         _altImage = [image retain];
106 }
107
108 - (NSImage *) highlightedImage {
109         return _altImage;
110 }
111
112 #pragma mark -
113 - (void) setSelectable:(BOOL) selectable {
114         _selectable = selectable;
115 }
116
117 - (BOOL) selectable {
118         return _selectable;
119 }
120
121 #pragma mark -
122
123 - (void) setMainText:(NSString *) text {
124         [_mainText autorelease];
125         _mainText = [text copy];
126 }
127
128 - (NSString *) mainText {
129         return _mainText;
130 }
131
132 #pragma mark -
133
134 - (void) setInformationText:(NSString *) text {
135         [_infoText autorelease];
136         _infoText = [text copy];
137 }
138
139 - (NSString *) informationText {
140         return _infoText;
141 }
142
143 #pragma mark -
144
145 - (void) setBoldAndWhiteOnHighlight:(BOOL) boldAndWhite {
146         _boldAndWhiteOnHighlight = boldAndWhite;
147 }
148
149 - (BOOL) boldAndWhiteOnHighlight {
150         return _boldAndWhiteOnHighlight;
151 }
152
153 #pragma mark -
154
155 - (void) drawWithFrame:(NSRect) cellFrame inView:(NSView *) controlView {
156         float imageWidth = 0.;
157         BOOL highlighted = ( [self isHighlighted] && [[controlView window] firstResponder] == controlView && [[controlView window] isKeyWindow] && [[NSApplication sharedApplication] isActive] );
158
159         NSMutableParagraphStyle *paraStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease];
160         [paraStyle setLineBreakMode:_lineBreakMode];
161         [paraStyle setAlignment:[self alignment]];
162
163         NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithObjectsAndKeys:[self font], NSFontAttributeName, paraStyle, NSParagraphStyleAttributeName, ( [self isEnabled] ? ( highlighted ? [NSColor alternateSelectedControlTextColor] : [NSColor controlTextColor] ) : ( highlighted ? [NSColor alternateSelectedControlTextColor] : [[NSColor controlTextColor] colorWithAlphaComponent:0.50] ) ), NSForegroundColorAttributeName, nil];
164         NSMutableDictionary *subAttributes = [NSMutableDictionary dictionaryWithObjectsAndKeys:[NSFont toolTipsFontOfSize:9.], NSFontAttributeName, paraStyle, NSParagraphStyleAttributeName, ( [self isEnabled] ? ( highlighted ? [NSColor alternateSelectedControlTextColor] : [[NSColor controlTextColor] colorWithAlphaComponent:0.75] ) : ( highlighted ? [NSColor alternateSelectedControlTextColor] : [[NSColor controlTextColor] colorWithAlphaComponent:0.40] ) ), NSForegroundColorAttributeName, nil];
165         NSImage *mainImage = nil, *curImage = nil;
166         NSSize mainStringSize = [_mainText sizeWithAttributes:attributes];
167         NSSize subStringSize = [_infoText sizeWithAttributes:subAttributes];
168
169         if( _boldAndWhiteOnHighlight && [self isHighlighted] ) {
170                 NSFont *boldFont = [[NSFontManager sharedFontManager] fontWithFamily:@"Lucida Grande" traits:0 weight:15 size:11.];
171                 NSShadow *shadow = [[NSShadow allocWithZone:nil] init];
172                 NSColor *whiteColor = [NSColor whiteColor];
173                 if( ! [self isEnabled] ) whiteColor = [whiteColor colorWithAlphaComponent:0.5];
174
175         [shadow setShadowOffset:NSMakeSize( 0, -1 )];
176                 [shadow setShadowBlurRadius:0.1];
177                 [shadow setShadowColor:[[NSColor shadowColor] colorWithAlphaComponent:0.2]];
178
179                 [attributes setObject:boldFont forKey:NSFontAttributeName];
180                 [attributes setObject:whiteColor forKey:NSForegroundColorAttributeName];
181                 [attributes setObject:shadow forKey:NSShadowAttributeName];
182
183                 boldFont = [[NSFontManager sharedFontManager] fontWithFamily:@"Lucida Grande" traits:0 weight:15 size:9.];
184                 [subAttributes setObject:boldFont forKey:NSFontAttributeName];
185                 [subAttributes setObject:whiteColor forKey:NSForegroundColorAttributeName];
186                 [subAttributes setObject:shadow forKey:NSShadowAttributeName];
187                 [shadow release];
188         }
189     else /*if( [self selectable] )
190     {
191         [self setMainText: [_mainText uppercaseString]];
192                 [attributes setObject:[NSColor grayColor] forKey:NSForegroundColorAttributeName];
193     }
194     else*/
195     {
196         NSFont *font = [[NSFontManager sharedFontManager] fontWithFamily:@"Lucida Grande" traits:0 weight:1 size:11.];
197                 [attributes setObject:font forKey:NSFontAttributeName];
198     }
199
200         if( highlighted && _altImage ) {
201                 mainImage = [[self image] retain];
202                 [self setImage:_altImage];
203         }
204
205         if( ! [self isEnabled] && [self image] ) {
206                 NSImage *fadedImage = [[[NSImage alloc] initWithSize:[[self image] size]] autorelease];
207                 [fadedImage lockFocus];
208                 [[self image] dissolveToPoint:NSMakePoint( 0., 0. ) fraction:0.5];
209                 [fadedImage unlockFocus];
210                 curImage = [[self image] retain];
211                 [self setImage:fadedImage];
212         }
213
214         cellFrame = NSMakeRect( cellFrame.origin.x + 1., cellFrame.origin.y, cellFrame.size.width - 1., cellFrame.size.height );
215         [super drawWithFrame:cellFrame inView:controlView];
216
217         if( ! [self isEnabled] ) {
218                 [self setImage:curImage];
219                 [curImage autorelease];
220         }
221
222         if( highlighted && mainImage ) {
223                 [self setImage:mainImage];
224                 [mainImage autorelease];
225         }
226
227         if( [self image] ) {
228                 switch( [self imageScaling] ) {
229                 case NSScaleProportionally:
230                         if( NSHeight( cellFrame ) < [[self image] size].height )
231                                 imageWidth = ( NSHeight( cellFrame ) / [[self image] size].height ) * [[self image] size].width;
232                         else imageWidth = [[self image] size].width;
233                         break;
234                 default:
235                 case NSScaleNone:
236                         imageWidth = [[self image] size].width;
237                         break;
238                 case NSScaleToFit:
239                         imageWidth = [[self image] size].width;
240                         break;
241                 }
242         }
243
244 #define JVDetailCellLabelPadding 3.
245 #define JVDetailCellImageLabelPadding 5.
246 #define JVDetailCellTextLeading 3.
247 #define JVDetailCellStatusImageLeftPadding 2.
248 #define JVDetailCellStatusImageRightPadding JVDetailCellStatusImageLeftPadding
249
250         float statusWidth = ( _statusImage ? [_statusImage size].width + JVDetailCellStatusImageRightPadding : 0. );
251         if( ! _statusImage && ( _statusNumber || _importantStatusNumber ) ) {
252                 NSColor *textColor = [NSColor whiteColor];
253                 NSColor *backgroundColor = [NSColor colorWithCalibratedRed:0.6 green:0.6705882352941176 blue:0.7725490196078431 alpha:1.];
254                 NSColor *importantColor = [NSColor colorWithCalibratedRed:0.831372549019608 green:0.572549019607843 blue:0.541176470588235 alpha:1.];
255
256                 if( ! _statusNumber && _importantStatusNumber )
257                         backgroundColor = importantColor;
258
259                 if( [self isHighlighted] ) {
260                         textColor = [backgroundColor shadowWithLevel:0.2];
261                         backgroundColor = [backgroundColor highlightWithLevel:0.7];
262                 }
263
264                 NSFont *font = [[NSFontManager sharedFontManager] fontWithFamily:@"Lucida Grande" traits:NSBoldFontMask weight:9 size:11.];
265                 NSMutableParagraphStyle *numberParaStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease];
266                 [numberParaStyle setAlignment:NSCenterTextAlignment];
267
268                 NSDictionary *statusNumberAttributes = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, numberParaStyle, NSParagraphStyleAttributeName, textColor, NSForegroundColorAttributeName, [NSNumber numberWithFloat:1.0], NSKernAttributeName, nil];
269
270                 NSString *statusText = [NSString stringWithFormat:@"%d", ( _statusNumber ? _statusNumber : _importantStatusNumber )];
271                 NSSize numberSize = [statusText sizeWithAttributes:statusNumberAttributes];
272                 statusWidth = numberSize.width + 12.;
273
274                 if( imageWidth + ( imageWidth ? JVDetailCellImageLabelPadding : JVDetailCellLabelPadding ) + statusWidth < NSWidth( cellFrame ) ) {
275                         float radius = ( _importantStatusNumber ? 8. : 7. );
276                         NSRect mainRect = NSMakeRect( NSMinX( cellFrame ) + NSWidth( cellFrame ) - statusWidth - 2., NSMinY( cellFrame ) + ( ( NSHeight( cellFrame ) / 2 ) - radius ), statusWidth, radius * 2 );
277                         NSRect pathRect = NSInsetRect( mainRect, radius, radius );
278
279                         NSBezierPath *mainPath = [NSBezierPath bezierPath];
280                         [mainPath appendBezierPathWithArcWithCenter:NSMakePoint( NSMinX( pathRect ), NSMinY( pathRect ) ) radius:radius startAngle:180. endAngle:270.];
281                         [mainPath appendBezierPathWithArcWithCenter:NSMakePoint( NSMaxX( pathRect ), NSMinY( pathRect ) ) radius:radius startAngle:270. endAngle:360.];
282                         [mainPath appendBezierPathWithArcWithCenter:NSMakePoint( NSMaxX( pathRect ), NSMaxY( pathRect ) ) radius:radius startAngle:0. endAngle:90.];
283                         [mainPath appendBezierPathWithArcWithCenter:NSMakePoint( NSMinX( pathRect ), NSMaxY( pathRect ) ) radius:radius startAngle:90. endAngle:180.];
284                         [mainPath closePath];
285
286                         if( _importantStatusNumber ) {
287                                 NSString *importantStatusText = [NSString stringWithFormat:@"%d", _importantStatusNumber];
288                                 numberSize = [importantStatusText sizeWithAttributes:statusNumberAttributes];
289                                 float mainStatusWidth = statusWidth;
290                                 statusWidth += numberSize.width + 10.;
291                                 radius = 7.;
292
293                                 NSRect rect = NSMakeRect( NSMinX( cellFrame ) + NSWidth( cellFrame ) - statusWidth - 2., NSMinY( cellFrame ) + ( ( NSHeight( cellFrame ) / 2 ) - radius ), statusWidth - mainStatusWidth + 10., radius * 2 );
294                                 pathRect = NSInsetRect( rect, radius, radius );
295
296                                 NSBezierPath *path = [NSBezierPath bezierPath];
297                                 [path appendBezierPathWithArcWithCenter:NSMakePoint( NSMinX( pathRect ), NSMinY( pathRect ) ) radius:radius startAngle:180. endAngle:270.];
298                                 [path appendBezierPathWithArcWithCenter:NSMakePoint( NSMaxX( pathRect ), NSMinY( pathRect ) ) radius:radius startAngle:270. endAngle:360.];
299                                 [path appendBezierPathWithArcWithCenter:NSMakePoint( NSMaxX( pathRect ), NSMaxY( pathRect ) ) radius:radius startAngle:0. endAngle:90.];
300                                 [path appendBezierPathWithArcWithCenter:NSMakePoint( NSMinX( pathRect ), NSMaxY( pathRect ) ) radius:radius startAngle:90. endAngle:180.];
301                                 [path closePath];
302
303                                 if( [self isHighlighted] ) [[NSColor whiteColor] set];
304                                 else [[NSColor colorWithCalibratedRed:0.92156862745098 green:0.231372549019608 blue:0.243137254901961 alpha:0.85] set];
305                                 [path fill];
306
307                                 rect.origin.x -= 3.;
308                                 [importantStatusText drawInRect:rect withAttributes:statusNumberAttributes];
309                         }
310
311                         [backgroundColor set];
312                         [mainPath fill];
313
314                         if( _importantStatusNumber ) {
315                                 if( [self isHighlighted] ) [[NSColor colorWithCalibratedRed:0.5803921568627451 green:0.6705882352941176 blue:0.7882352941176471 alpha:1.] set];
316                                 else [[NSColor whiteColor] set];
317
318                                 [mainPath setLineWidth:1.25];
319                                 [mainPath stroke];
320                         }
321
322                         if( _importantStatusNumber ) mainRect.origin.y += 1.;
323                         [statusText drawInRect:mainRect withAttributes:statusNumberAttributes];
324
325                         statusWidth += JVDetailCellStatusImageRightPadding + 3.;
326
327                 } else statusWidth = 0.;
328         }
329
330         if( ( ! [_infoText length] && [_mainText length] ) || ( ( subStringSize.height + mainStringSize.height ) >= NSHeight( cellFrame ) - 2. ) ) {
331                 float mainYLocation = 0.;
332
333                 if( NSHeight( cellFrame ) >= mainStringSize.height ) {
334                         mainYLocation = NSMinY( cellFrame ) + ( NSHeight( cellFrame ) / 2 ) - ( mainStringSize.height / 2 );
335                         [_mainText drawInRect:NSMakeRect( NSMinX( cellFrame ) + imageWidth + ( imageWidth ? JVDetailCellImageLabelPadding : JVDetailCellLabelPadding ), mainYLocation, NSWidth( cellFrame ) - imageWidth - ( JVDetailCellImageLabelPadding * 1. ) - statusWidth, [_mainText sizeWithAttributes:attributes].height ) withAttributes:attributes];
336                 }
337         } else if( [_infoText length] && [_mainText length] ) {
338                 float mainYLocation = 0., subYLocation = 0.;
339
340                 if( NSHeight( cellFrame ) >= mainStringSize.height ) {
341                         mainYLocation = NSMinY( cellFrame ) + ( NSHeight( cellFrame ) / 2 ) - mainStringSize.height + ( JVDetailCellTextLeading / 2. );
342                         [_mainText drawInRect:NSMakeRect( cellFrame.origin.x + imageWidth + ( imageWidth ? JVDetailCellImageLabelPadding : JVDetailCellLabelPadding ), mainYLocation, NSWidth( cellFrame ) - imageWidth - ( JVDetailCellImageLabelPadding * 1. ) - statusWidth, [_mainText sizeWithAttributes:attributes].height ) withAttributes:attributes];
343
344                         subYLocation = NSMinY( cellFrame ) + ( NSHeight( cellFrame ) / 2 ) + subStringSize.height - mainStringSize.height + ( JVDetailCellTextLeading / 2. );
345                         [_infoText drawInRect:NSMakeRect( NSMinX( cellFrame ) + imageWidth + ( imageWidth ? JVDetailCellImageLabelPadding : JVDetailCellLabelPadding ), subYLocation, NSWidth( cellFrame ) - imageWidth - ( JVDetailCellImageLabelPadding * 1. ) - statusWidth, [_infoText sizeWithAttributes:subAttributes].height ) withAttributes:subAttributes];
346                 }
347         }
348
349         if( _statusImage && NSHeight( cellFrame ) >= [_statusImage size].height ) {
350                 [_statusImage compositeToPoint:NSMakePoint( NSMinX( cellFrame ) + NSWidth( cellFrame ) - statusWidth, NSMaxY( cellFrame ) - ( ( NSHeight( cellFrame ) / 2 ) - ( [_statusImage size].height / 2 ) ) ) operation:NSCompositeSourceAtop fraction:( [self isEnabled] ? 1. : 0.5)];
351         }
352 }
353
354 #pragma mark -
355
356 - (void) setImageScaling:(NSImageScaling) newScaling {
357         [super setImageScaling:( newScaling == NSScaleProportionally || newScaling == NSScaleNone ? newScaling : NSScaleProportionally )];
358 }
359
360 - (void) setImageAlignment:(NSImageAlignment) newAlign {
361         [super setImageAlignment:NSImageAlignLeft];
362 }
363
364 - (void) setLineBreakMode:(NSLineBreakMode) mode {
365         _lineBreakMode = mode;
366 }
367
368 - (NSLineBreakMode) lineBreakMode {
369         return _lineBreakMode;
370 }
371
372 - (void) setStringValue:(NSString *) string {
373         [self setMainText:string];
374 }
375
376 - (void) setObjectValue:(id <NSCopying>) obj {
377         if( ! obj || [(NSObject *)obj isKindOfClass:[NSImage class]] ) {
378                 [super setObjectValue:obj];
379         } else if( [(NSObject *)obj isKindOfClass:[NSString class]] ) {
380                 [self setMainText:(NSString *)obj];
381         }
382 }
383
384 - (NSString *) stringValue {
385         return _mainText;
386 }
387 @end