VLCMediaListAspect.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*****************************************************************************
  2. * VLCMediaListAspect.m: VLCKit.framework VLCMediaListAspect implementation
  3. *****************************************************************************
  4. * Copyright (C) 2007 Pierre d'Herbemont
  5. * Copyright (C) 2007 the VideoLAN team
  6. * $Id$
  7. *
  8. * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 2 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, write to the Free Software
  22. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  23. *****************************************************************************/
  24. #import "VLCMediaListAspect.h"
  25. #import "VLCLibrary.h"
  26. #import "VLCEventManager.h"
  27. #import "VLCLibVLCBridging.h"
  28. #ifdef HAVE_CONFIG_H
  29. # include "config.h"
  30. #endif
  31. #include <vlc/vlc.h>
  32. #include <vlc/libvlc.h>
  33. // TODO: Documentation
  34. @interface VLCMediaListAspect (Private)
  35. /* Initializers */
  36. - (void)initInternalMediaListView;
  37. - (void)mediaListViewItemAdded:(NSArray *)args;
  38. - (void)mediaListViewItemRemoved:(NSNumber *)index;
  39. @end
  40. @implementation VLCMediaListAspectNode
  41. - (id)init
  42. {
  43. if(self = [super init])
  44. {
  45. media = nil;
  46. children = nil;
  47. }
  48. return self;
  49. }
  50. - (void)dealloc
  51. {
  52. [media release];
  53. [children release];
  54. [super dealloc];
  55. }
  56. @synthesize media;
  57. @synthesize children;
  58. - (BOOL)isLeaf
  59. {
  60. return self.children == nil;
  61. }
  62. @end
  63. @implementation VLCMediaListAspect (KeyValueCodingCompliance)
  64. /* For the @"media" key */
  65. - (int) countOfMedia
  66. {
  67. return [cachedNode count];
  68. }
  69. - (id) objectInMediaAtIndex:(int)i
  70. {
  71. return [[cachedNode objectAtIndex:i] media];
  72. }
  73. /* For the @"node" key */
  74. - (int) countOfNode
  75. {
  76. return [cachedNode count];
  77. }
  78. - (id) objectInNodeAtIndex:(int)i
  79. {
  80. return [cachedNode objectAtIndex:i];
  81. }
  82. @end
  83. /* libvlc event callback */
  84. static void HandleMediaListViewItemAdded(const libvlc_event_t * event, void * user_data)
  85. {
  86. NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  87. id self = user_data;
  88. [[VLCEventManager sharedManager] callOnMainThreadObject:self
  89. withMethod:@selector(mediaListViewItemAdded:)
  90. withArgumentAsObject:[NSArray arrayWithObject:[NSDictionary dictionaryWithObjectsAndKeys:
  91. [VLCMedia mediaWithLibVLCMediaDescriptor:event->u.media_list_item_added.item], @"media",
  92. [NSNumber numberWithInt:event->u.media_list_item_added.index], @"index",
  93. nil]]];
  94. [pool release];
  95. }
  96. static void HandleMediaListViewItemDeleted( const libvlc_event_t * event, void * user_data)
  97. {
  98. NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  99. id self = user_data;
  100. [[VLCEventManager sharedManager] callOnMainThreadObject:self
  101. withMethod:@selector(mediaListViewItemRemoved:)
  102. withArgumentAsObject:[NSNumber numberWithInt:event->u.media_list_item_deleted.index]];
  103. [pool release];
  104. }
  105. @implementation VLCMediaListAspect
  106. - (void)dealloc
  107. {
  108. // Release allocated memory
  109. libvlc_media_list_view_release(p_mlv);
  110. [cachedNode release];
  111. if( ownHisMediaList )
  112. [parentMediaList release];
  113. [super dealloc];
  114. }
  115. - (void)release
  116. {
  117. @synchronized(self)
  118. {
  119. if([self retainCount] <= 1)
  120. {
  121. /* We must make sure we won't receive new event after an upcoming dealloc
  122. * We also may receive a -retain in some event callback that may occcur
  123. * Before libvlc_event_detach. So this can't happen in dealloc */
  124. libvlc_event_manager_t * p_em = libvlc_media_list_view_event_manager(p_mlv);
  125. libvlc_event_detach(p_em, libvlc_MediaListViewItemDeleted, HandleMediaListViewItemDeleted, self, NULL);
  126. libvlc_event_detach(p_em, libvlc_MediaListViewItemAdded, HandleMediaListViewItemAdded, self, NULL);
  127. }
  128. [super release];
  129. }
  130. }
  131. - (NSString *)description
  132. {
  133. NSMutableString * content = [NSMutableString string];
  134. int i;
  135. for( i = 0; i < [self count]; i++)
  136. {
  137. [content appendFormat:@"%@\n", [self mediaAtIndex: i]];
  138. }
  139. return [NSString stringWithFormat:@"<%@ %p> {\n%@}", [self className], self, content];
  140. }
  141. - (VLCMedia *)mediaAtIndex:(int)index
  142. {
  143. libvlc_exception_t p_e;
  144. libvlc_exception_init( &p_e );
  145. libvlc_media_t * p_md = libvlc_media_list_view_item_at_index( p_mlv, index, &p_e );
  146. catch_exception( &p_e );
  147. // Returns local object for media descriptor, searchs for user data first. If not found it creates a
  148. // new cocoa object representation of the media descriptor.
  149. return [VLCMedia mediaWithLibVLCMediaDescriptor:p_md];
  150. }
  151. - (VLCMediaListAspect *)childrenAtIndex:(int)index
  152. {
  153. libvlc_exception_t p_e;
  154. libvlc_exception_init( &p_e );
  155. libvlc_media_list_view_t * p_sub_mlv = libvlc_media_list_view_children_at_index( p_mlv, index, &p_e );
  156. catch_exception( &p_e );
  157. if( !p_sub_mlv )
  158. return nil;
  159. // Returns local object for media descriptor, searchs for user data first. If not found it creates a
  160. // new cocoa object representation of the media descriptor.
  161. return [VLCMediaListAspect mediaListAspectWithLibVLCMediaListView:p_sub_mlv];
  162. }
  163. - (VLCMediaListAspectNode *)nodeAtIndex:(int)index
  164. {
  165. VLCMediaListAspectNode * node = [[[VLCMediaListAspectNode alloc] init] autorelease];
  166. [node setMedia:[self mediaAtIndex: index]];
  167. libvlc_media_list_view_t * p_sub_mlv = libvlc_media_list_view_children_for_item([self libVLCMediaListView], [node.media libVLCMediaDescriptor], NULL);
  168. if( p_sub_mlv )
  169. {
  170. [node setChildren:[VLCMediaListAspect mediaListAspectWithLibVLCMediaListView: p_sub_mlv]];
  171. libvlc_media_list_view_release(p_sub_mlv);
  172. }
  173. return node;
  174. }
  175. - (int)count
  176. {
  177. libvlc_exception_t p_e;
  178. libvlc_exception_init( &p_e );
  179. int result = libvlc_media_list_view_count( p_mlv, &p_e );
  180. catch_exception( &p_e );
  181. return result;
  182. }
  183. - (VLCMediaList *)parentMediaList
  184. {
  185. return parentMediaList;
  186. }
  187. @end
  188. @implementation VLCMediaListAspect (LibVLCBridging)
  189. + (id)mediaListAspectWithLibVLCMediaListView:(libvlc_media_list_view_t *)p_new_mlv
  190. {
  191. return [[[VLCMediaListAspect alloc] initWithLibVLCMediaListView:p_new_mlv andMediaList:nil] autorelease];
  192. }
  193. + (id)mediaListAspectWithLibVLCMediaListView:(libvlc_media_list_view_t *)p_new_mlv andMediaList:(VLCMediaList *)mediaList;
  194. {
  195. return [[[VLCMediaListAspect alloc] initWithLibVLCMediaListView:p_new_mlv andMediaList:mediaList] autorelease];
  196. }
  197. - (id)initWithLibVLCMediaListView:(libvlc_media_list_view_t *)p_new_mlv andMediaList:(VLCMediaList *)mediaList;
  198. {
  199. if( self = [super init] )
  200. {
  201. p_mlv = p_new_mlv;
  202. libvlc_media_list_view_retain(p_mlv);
  203. /* parentMediaList isn't retained, because we need a mediaList to exists, and not the contrary */
  204. parentMediaList = mediaList;
  205. ownHisMediaList = NO;
  206. if( !parentMediaList )
  207. {
  208. /* We have to create it then */
  209. libvlc_media_list_view_retain(p_mlv);
  210. libvlc_media_list_t * p_mlist = libvlc_media_list_view_parent_media_list(p_mlv, NULL);
  211. parentMediaList = [[VLCMediaList mediaListWithLibVLCMediaList: p_mlist] retain];
  212. libvlc_media_list_release( p_mlist );
  213. /* This is an exception, and we owns it here */
  214. ownHisMediaList = YES;
  215. }
  216. cachedNode = [[NSMutableArray alloc] initWithCapacity:libvlc_media_list_view_count(p_mlv, NULL)];
  217. libvlc_media_list_t * p_mlist;
  218. p_mlist = libvlc_media_list_view_parent_media_list( p_mlv, NULL );
  219. libvlc_media_list_lock( p_mlist );
  220. int i, count = libvlc_media_list_view_count(p_mlv, NULL);
  221. for( i = 0; i < count; i++ )
  222. {
  223. libvlc_media_t * p_md = libvlc_media_list_view_item_at_index(p_mlv, i, NULL);
  224. libvlc_media_list_view_t * p_sub_mlv = libvlc_media_list_view_children_at_index(p_mlv, i, NULL);
  225. VLCMediaListAspectNode * node = [[[VLCMediaListAspectNode alloc] init] autorelease];
  226. [node setMedia:[VLCMedia mediaWithLibVLCMediaDescriptor: p_md]];
  227. [node setChildren: p_sub_mlv ? [VLCMediaListAspect mediaListAspectWithLibVLCMediaListView: p_sub_mlv] : nil];
  228. if( p_sub_mlv ) NSAssert(![node isLeaf], @"Not leaf");
  229. [cachedNode addObject:node];
  230. libvlc_media_release(p_md);
  231. if( p_sub_mlv ) libvlc_media_list_view_release(p_sub_mlv);
  232. }
  233. [self initInternalMediaListView];
  234. libvlc_media_list_unlock( p_mlist );
  235. libvlc_media_list_release( p_mlist );
  236. }
  237. return self;
  238. }
  239. - (libvlc_media_list_view_t *)libVLCMediaListView
  240. {
  241. return (libvlc_media_list_view_t *)p_mlv;
  242. }
  243. @end
  244. @implementation VLCMediaListAspect (Private)
  245. - (void)initInternalMediaListView
  246. {
  247. libvlc_exception_t e;
  248. libvlc_exception_init( &e );
  249. libvlc_event_manager_t * p_em = libvlc_media_list_event_manager( p_mlv, &e );
  250. /* Add internal callback */
  251. libvlc_event_attach( p_em, libvlc_MediaListViewItemAdded, HandleMediaListViewItemAdded, self, &e );
  252. libvlc_event_attach( p_em, libvlc_MediaListViewItemDeleted, HandleMediaListViewItemDeleted, self, &e );
  253. catch_exception( &e );
  254. }
  255. - (void)mediaListViewItemAdded:(NSArray *)arrayOfArgs
  256. {
  257. NSAssert([NSThread isMainThread], @"We are not on main thread");
  258. /* We hope to receive index in a nide range, that could change one day */
  259. int start = [[[arrayOfArgs objectAtIndex: 0] objectForKey:@"index"] intValue];
  260. int end = [[[arrayOfArgs objectAtIndex: [arrayOfArgs count]-1] objectForKey:@"index"] intValue];
  261. NSRange range = NSMakeRange(start, end-start);
  262. [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] forKey:@"media"];
  263. [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] forKey:@"node"];
  264. for( NSDictionary * args in arrayOfArgs )
  265. {
  266. int index = [[args objectForKey:@"index"] intValue];
  267. VLCMedia * media = [args objectForKey:@"media"];
  268. VLCMediaListAspectNode * node = [[[VLCMediaListAspectNode alloc] init] autorelease];
  269. [node setMedia:media];
  270. /* Set the sub media list view we enventually have */
  271. libvlc_media_list_view_t * p_sub_mlv = libvlc_media_list_view_children_for_item([self libVLCMediaListView], [media libVLCMediaDescriptor], NULL);
  272. if( p_sub_mlv )
  273. {
  274. [node setChildren:[VLCMediaListAspect mediaListAspectWithLibVLCMediaListView: p_sub_mlv]];
  275. libvlc_media_list_view_release(p_sub_mlv);
  276. NSAssert(![node isLeaf], @"Not leaf");
  277. }
  278. /* Sanity check */
  279. if( index && index > [cachedNode count] )
  280. index = [cachedNode count];
  281. [cachedNode insertObject:node atIndex:index];
  282. }
  283. [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] forKey:@"node"];
  284. [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] forKey:@"media"];
  285. }
  286. - (void)mediaListViewItemRemoved:(NSNumber *)index
  287. {
  288. [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"media"];
  289. [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"node"];
  290. [cachedNode removeObjectAtIndex:[index intValue]];
  291. [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"node"];
  292. [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:[index intValue]] forKey:@"media"];
  293. }
  294. @end