]> git.sesse.net Git - vlc/blob - projects/macosx/vlc_app/Sources/VLCMediaListLayer.m
vlc_app: New media layer view. Enabled by default. Set bindings from resources. ...
[vlc] / projects / macosx / vlc_app / Sources / VLCMediaListLayer.m
1 //
2 //  VLCMediaListLayer.m
3 //  VLC
4 //
5 //  Created by Pierre d'Herbemont on 1/14/08.
6 //  Copyright 2008 __MyCompanyName__. All rights reserved.
7 //
8
9 #import "VLCMediaListLayer.h"
10
11 /*****************************************************************************
12  * @implementation VLCMediaListLayer
13  */
14
15 @interface VLCMediaListLayer (Private)
16 - (VLCMediaLayer *)selectedLayer;
17 - (VLCMediaLayer *)previousLayer;
18 - (VLCMediaLayer *)nextLayer;
19 - (void)changeSelectedLayerToNextIndex;
20 - (void)changeSelectedLayerToPreviousIndex;
21 - (void)resetLayers;
22
23
24 - (void)setSelectedLayer:(VLCMediaLayer *)layer;
25 - (void)setPreviousLayer:(VLCMediaLayer *)layer;
26 - (void)setNextLayer:(VLCMediaLayer *)layer;
27 @end
28
29 /*****************************************************************************
30  * @implementation VLCMediaListLayer
31  */
32
33 @implementation VLCMediaListLayer
34 @synthesize selectedIndex;
35 @synthesize content;
36 @synthesize controller;
37
38 + (id)layer
39 {
40     VLCMediaListLayer * me = [super layer];
41
42     me.layoutManager = [CAConstraintLayoutManager layoutManager];
43
44     [CATransaction commit];
45
46     me->selectedIndex = NSNotFound;
47     return me;
48 }
49
50 + (id)layerWithMediaArrayController:(VLCMediaArrayController *)aController
51 {
52     VLCMediaListLayer * me = [VLCMediaListLayer layer];
53     me.controller = aController;
54     
55     /* The following will trigger -observeValueForKeyPath: ofObject: change: context: */
56     [me.controller addObserver:me forKeyPath:@"arrangedObjects" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
57     [me.controller addObserver:me forKeyPath:@"selectionIndex" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
58     [me.controller addObserver:me forKeyPath:@"contentMediaList" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
59
60     return me;
61 }
62
63 - (void)dealloc
64 {
65     /* Previously registered in +layerWithMediaArrayController: */
66     [self.controller removeObserver:self forKeyPath:@"arrangedObjects"];
67     [self.controller removeObserver:self forKeyPath:@"contentMediaList"];
68     [self.controller removeObserver:self forKeyPath:@"selectionIndex"];
69     [super dealloc];
70 }
71 @end
72
73 /*****************************************************************************
74  * @implementation VLCMediaListLayer (Private)
75  */
76
77 @implementation VLCMediaListLayer (Private)
78 + (NSSet *)keyPathsForValuesAffectingSelectedLayer
79 {
80     return [NSSet setWithObjects:@"selectedLayer", @"content", nil];
81 }
82
83 - (VLCMediaLayer *)selectedLayer
84 {
85     VLCMedia * media = (self.selectedIndex != NSNotFound) ? [self.content objectAtIndex:self.selectedIndex ] : nil;
86     if( !media )
87     {
88         CATextLayer * layer = [CATextLayer layer];
89         CALayer * container = [CALayer layer];
90         container.layoutManager = [CAConstraintLayoutManager layoutManager];
91         if([self.controller.contentMediaList isReadOnly])
92             layer.string = @"Empty";
93         else if ([self.content count])
94             layer.string = @"Empty search.";
95         else
96             layer.string = @"Drag and Drop a movie or a music here.";
97         layer.alignmentMode = kCAAlignmentCenter;
98         layer.wrapped = YES;
99         [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY relativeTo:@"superlayer" attribute:kCAConstraintMidY]];
100         [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidX relativeTo:@"superlayer" attribute:kCAConstraintMidX]];
101         [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintWidth relativeTo:@"superlayer" attribute:kCAConstraintWidth]];
102         [container addSublayer:layer];
103         return (VLCMediaLayer *)container;
104     }
105
106     if( [selectedLayer isKindOfClass:[VLCMediaLayer class]] && [media compare:[selectedLayer media]] == NSOrderedSame )
107         return [[selectedLayer retain] autorelease];
108
109     return [VLCMediaLayer layerWithMedia:[self.content objectAtIndex:self.selectedIndex]];
110 }
111
112 - (VLCMediaLayer *)previousLayer
113 {
114     if( self.selectedIndex == NSNotFound )
115         return nil;
116     VLCMedia * media = self.selectedIndex > 0 ? [self.content objectAtIndex:self.selectedIndex - 1] : nil;
117     if( !media )
118         return nil;
119
120     if( [previousLayer isKindOfClass:[VLCMediaLayer class]] && [media compare:[previousLayer media]] == NSOrderedSame )
121         return [[previousLayer retain] autorelease];
122     
123     return [VLCMediaLayer layerWithMedia: media ];
124 }
125
126 - (VLCMediaLayer *)nextLayer
127 {
128     if( self.selectedIndex == NSNotFound )
129         return nil;
130     VLCMedia * media = self.selectedIndex + 1 < [content count] ? [self.content objectAtIndex:self.selectedIndex + 1] : nil;
131     if( !media )
132         return nil;
133
134     if( [nextLayer isKindOfClass:[VLCMediaLayer class]] && [media compare:[nextLayer media]] == NSOrderedSame )
135         return [[nextLayer retain] autorelease];
136     
137     return [VLCMediaLayer layerWithMedia: media ];
138 }
139
140 - (void)changeSelectedLayerToNextIndex
141 {
142     if(!nextLayer)
143     {
144         /* Can't do anything */
145         return;
146     }
147     selectedIndex++;
148
149     /* Remove offscreen layer. Without actions */
150     if( previousLayer )
151     {
152         [CATransaction begin];
153         [CATransaction setValue:[NSNumber numberWithBool:YES] forKey:kCATransactionDisableActions];
154             [previousLayer removeFromSuperlayer];
155         [CATransaction commit];
156     }
157
158     [CATransaction begin];
159         if ( [[NSApp currentEvent] modifierFlags] & NSShiftKeyMask )
160             [CATransaction setValue:[NSNumber numberWithFloat:1.5] forKey:kCATransactionAnimationDuration];
161
162         [self setPreviousLayer: selectedLayer];        
163         [self setSelectedLayer: nextLayer];
164         [self setNextLayer: [self nextLayer]];
165     [CATransaction commit];
166
167     /* Move the new nextLayer layer on screen. Without Actions */
168     [CATransaction begin];
169     [CATransaction setValue:[NSNumber numberWithBool:YES] forKey:kCATransactionDisableActions];
170         [self addSublayer: nextLayer];
171     [CATransaction commit];
172 }
173
174 - (void)changeSelectedLayerToPreviousIndex
175 {
176     if(!previousLayer)
177     {
178         /* Can't do anything */
179         return;
180     }
181     selectedIndex--;
182
183     /* Remove offscreen layer. Without actions */
184     if( nextLayer )
185     {
186         [CATransaction begin];
187         [CATransaction setValue:[NSNumber numberWithBool:YES] forKey:kCATransactionDisableActions];
188             [nextLayer removeFromSuperlayer];
189         [CATransaction commit];
190     }
191
192     [CATransaction begin];
193         if ( [[NSApp currentEvent] modifierFlags] & NSShiftKeyMask )
194             [CATransaction setValue:[NSNumber numberWithFloat:1.5] forKey:kCATransactionAnimationDuration];
195
196         [self setNextLayer: selectedLayer];        
197         [self setSelectedLayer: previousLayer];
198         [self setPreviousLayer: [self previousLayer]];
199     [CATransaction commit];
200
201     /* Move the new previous layer on screen. Without Actions */
202     [CATransaction begin];
203     [CATransaction setValue:[NSNumber numberWithBool:YES] forKey:kCATransactionDisableActions];
204         [self addSublayer:previousLayer];
205     [CATransaction commit];
206 }
207
208 - (void)gotoIndex:(NSUInteger)index;
209 {
210     if( selectedIndex == index )
211         return;
212
213     if( selectedIndex > index )
214     {
215         /* It is ok to scroll five layers */
216         if( selectedIndex - index < 5  )
217         {
218             [self changeSelectedLayerToPreviousIndex];
219             return;
220         }
221         [self changeSelectedLayerToPreviousIndex];
222         [self changeSelectedLayerToPreviousIndex];
223         selectedIndex = index;
224         [self resetLayers];
225     }
226     else
227     {
228         if( index - selectedIndex < 5  )
229         {
230             [self changeSelectedLayerToNextIndex];
231             return;
232         }
233         [self changeSelectedLayerToNextIndex];
234         [self changeSelectedLayerToNextIndex];
235         selectedIndex = index;
236         [self resetLayers];
237     }
238 }
239
240 - (void)resetLayers
241 {
242     VLCMediaLayer * layer;
243     [CATransaction begin];
244     layer = [self previousLayer];
245     if( previousLayer != layer )
246     {
247         if( previousLayer ) [self replaceSublayer:previousLayer with:layer];
248         else [self addSublayer:layer];
249         [self setPreviousLayer:layer];
250     }
251     layer = [self selectedLayer];
252     if( selectedLayer != layer )
253     {
254         if( selectedLayer ) [self replaceSublayer:selectedLayer with:layer];
255         else [self addSublayer:layer];
256         [self setSelectedLayer:layer];
257     }
258     layer = [self nextLayer];
259     if( nextLayer != layer )
260     {
261         if( nextLayer ) [self replaceSublayer:nextLayer with:layer];
262         else [self addSublayer:layer];
263         [self setNextLayer:layer];
264     }
265     [CATransaction commit];
266 }
267
268
269 - (void)setSelectedLayer:(VLCMediaLayer *)layer
270 {
271     [selectedLayer autorelease];
272     if( !layer )
273     {
274         selectedLayer = nil;
275         return;
276     }
277     selectedLayer = [layer retain];
278     selectedLayer.frame = [self bounds];
279     [selectedLayer setAutoresizingMask:kCALayerWidthSizable|kCALayerHeightSizable];
280 }
281
282 - (void)setPreviousLayer:(VLCMediaLayer *)layer
283 {
284     [previousLayer autorelease];
285     if( !layer )
286     {
287         previousLayer = nil;
288         return;
289     }
290     previousLayer = [layer retain];
291     CGRect frame = [self bounds];
292     frame.origin.x -= frame.size.width;
293     previousLayer.frame = frame;
294     [previousLayer setAutoresizingMask:kCALayerMaxXMargin|kCALayerHeightSizable];
295 }
296
297 - (void)setNextLayer:(VLCMediaLayer *)layer
298 {
299     [nextLayer autorelease];
300     if( !layer )
301     {
302         nextLayer = nil;
303         return;
304     }
305     nextLayer = [layer retain];
306     CGRect frame = [self bounds];
307     frame.origin.x += frame.size.width;
308     nextLayer.frame = frame;
309     [nextLayer setAutoresizingMask:kCALayerMinXMargin|kCALayerHeightSizable];
310 }
311
312 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
313 {
314     if([keyPath isEqualToString:@"selectionIndex"])
315     {
316         if ( selectedIndex == NSNotFound || [object selectionIndex] == NSNotFound )
317         {
318             selectedIndex = [object selectionIndex];
319             if(selectedIndex == NSNotFound  && [content count])
320             {
321                 selectedIndex = 0;
322             }
323             [self resetLayers];
324             return;
325         }
326
327         [self gotoIndex: [object selectionIndex]];
328         return;
329     }
330     if([keyPath isEqualToString:@"arrangedObjects"] || [keyPath isEqualToString:@"contentMediaList"])
331     {
332         selectedIndex = [object selectionIndex];
333         if(selectedIndex == NSNotFound  && [[object arrangedObjects] count])
334         {
335             selectedIndex = 0;
336         }
337         [content release];
338         content = [[object arrangedObjects] retain];
339         [self resetLayers];
340         return;
341     }
342     [self observeValueForKeyPath:keyPath ofObject:object change:change context:context];
343 }
344
345 @end