]> git.sesse.net Git - vlc/blob - projects/macosx/vlc_app/Sources/VLCMediaListLayer.m
Rework of vout_OSDEpg.
[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             while( index < selectedIndex  )
219                 [self changeSelectedLayerToPreviousIndex];
220             return;
221         }
222         [self changeSelectedLayerToPreviousIndex];
223         [self changeSelectedLayerToPreviousIndex];
224         selectedIndex = index;
225         [self resetLayers];
226     }
227     else
228     {
229         if( index - selectedIndex < 5  )
230         {
231             while( index > selectedIndex  )
232                 [self changeSelectedLayerToNextIndex];
233             return;
234         }
235         [self changeSelectedLayerToNextIndex];
236         [self changeSelectedLayerToNextIndex];
237         selectedIndex = index;
238         [self resetLayers];
239     }
240 }
241
242 - (void)resetLayers
243 {
244     VLCMediaLayer * layer;
245     [CATransaction begin];
246     layer = [self previousLayer];
247     if( previousLayer != layer )
248     {
249         if( previousLayer ) [self replaceSublayer:previousLayer with:layer];
250         else [self addSublayer:layer];
251         [self setPreviousLayer:layer];
252     }
253     layer = [self selectedLayer];
254     if( selectedLayer != layer )
255     {
256         if( selectedLayer ) [self replaceSublayer:selectedLayer with:layer];
257         else [self addSublayer:layer];
258         [self setSelectedLayer:layer];
259     }
260     layer = [self nextLayer];
261     if( nextLayer != layer )
262     {
263         if( nextLayer ) [self replaceSublayer:nextLayer with:layer];
264         else [self addSublayer:layer];
265         [self setNextLayer:layer];
266     }
267     [CATransaction commit];
268 }
269
270
271 - (void)setSelectedLayer:(VLCMediaLayer *)layer
272 {
273     [selectedLayer autorelease];
274     if( !layer )
275     {
276         selectedLayer = nil;
277         return;
278     }
279     selectedLayer = [layer retain];
280     selectedLayer.frame = [self bounds];
281     [selectedLayer setAutoresizingMask:kCALayerWidthSizable|kCALayerHeightSizable];
282 }
283
284 - (void)setPreviousLayer:(VLCMediaLayer *)layer
285 {
286     [previousLayer autorelease];
287     if( !layer )
288     {
289         previousLayer = nil;
290         return;
291     }
292     previousLayer = [layer retain];
293     CGRect frame = [self bounds];
294     frame.origin.x -= frame.size.width;
295     previousLayer.frame = frame;
296     [previousLayer setAutoresizingMask:kCALayerMaxXMargin|kCALayerHeightSizable];
297 }
298
299 - (void)setNextLayer:(VLCMediaLayer *)layer
300 {
301     [nextLayer autorelease];
302     if( !layer )
303     {
304         nextLayer = nil;
305         return;
306     }
307     nextLayer = [layer retain];
308     CGRect frame = [self bounds];
309     frame.origin.x += frame.size.width;
310     nextLayer.frame = frame;
311     [nextLayer setAutoresizingMask:kCALayerMinXMargin|kCALayerHeightSizable];
312 }
313
314 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
315 {
316     if([keyPath isEqualToString:@"selectionIndex"])
317     {
318         if ( selectedIndex == NSNotFound || [object selectionIndex] == NSNotFound )
319         {
320             selectedIndex = [object selectionIndex];
321             if(selectedIndex == NSNotFound  && [content count])
322             {
323                 selectedIndex = 0;
324             }
325             [self resetLayers];
326             return;
327         }
328
329         [self gotoIndex: [object selectionIndex]];
330         return;
331     }
332     if([keyPath isEqualToString:@"arrangedObjects"] || [keyPath isEqualToString:@"contentMediaList"])
333     {
334         selectedIndex = [object selectionIndex];
335         if(selectedIndex == NSNotFound  && [[object arrangedObjects] count])
336         {
337             selectedIndex = 0;
338         }
339         [content release];
340         content = [[object arrangedObjects] retain];
341         [self resetLayers];
342         return;
343     }
344     [self observeValueForKeyPath:keyPath ofObject:object change:change context:context];
345 }
346
347 @end