]> git.sesse.net Git - vlc/blobdiff - modules/gui/macosx/playlist.m
Cancel
[vlc] / modules / gui / macosx / playlist.m
index e36e3f8b269c4ccf132e73686183b2a5b5c015a4..31dfb8aa2f673bf72d2c291df25ad3ccce99e39f 100644 (file)
@@ -1,17 +1,12 @@
 /*****************************************************************************
- * playlist.m: MacOS X interface plugin
+ * playlist.m: MacOS X interface module
  *****************************************************************************
- * Copyright (C) 2002-2003 VideoLAN
- * $Id: playlist.m,v 1.15 2003/03/17 17:10:21 hartman Exp $
+ * Copyright (C) 2002-2004 VideoLAN
+ * $Id$
  *
  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
- *          Derk-Jan Hartman <thedj@users.sourceforge.net>
- * Thanks:  Andrew Stone for documenting the row reordering methods on the net
- *              http://www.omnigroup.com/mailman/archive/macosx-dev/
- *              2001-January/008195.html
- *          Apple Computer for documenting the Alternating row colors
- *             http://developer.apple.com/samplecode/Sample_Code/Cocoa/
- *              MP3_Player/MyTableView.m.htm
+ *          Derk-Jan Hartman <hartman at videolan dot org>
+ *          Benjamin Pracht <bigben at videolab dot org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <stdlib.h>                                      /* malloc(), free() */
 #include <sys/param.h>                                    /* for MAXPATHLEN */
 #include <string.h>
+#include <math.h>
+#include <sys/mount.h>
+#include <vlc_keys.h>
 
 #include "intf.h"
 #include "playlist.h"
-
-// RGB values for stripe color (light blue)
-#define STRIPE_RED   (237.0 / 255.0)
-#define STRIPE_GREEN (243.0 / 255.0)
-#define STRIPE_BLUE  (254.0 / 255.0)
-static NSColor *sStripeColor = nil;
+#include "controls.h"
+#include <OSD.h>
 
 /*****************************************************************************
  * VLCPlaylistView implementation 
@@ -57,7 +51,10 @@ static NSColor *sStripeColor = nil;
 - (void)keyDown:(NSEvent *)o_event
 {
     unichar key = 0;
-    int i_row;
+    int i, c, i_row;
+    NSMutableArray *o_to_delete;
+    NSNumber *o_number;
+    
     playlist_t * p_playlist;
     intf_thread_t * p_intf = [NSApp getIntf];
 
@@ -76,29 +73,24 @@ static NSColor *sStripeColor = nil;
     
     switch( key )
     {
-        case ' ':
-            vlc_mutex_lock( &p_playlist->object_lock );
-            if( p_playlist->p_input != NULL )
-            {
-                input_SetStatus( p_playlist->p_input, INPUT_STATUS_PAUSE );
-            }
-            vlc_mutex_unlock( &p_playlist->object_lock );
-            break;
-
         case NSDeleteCharacter:
         case NSDeleteFunctionKey:
         case NSDeleteCharFunctionKey:
         case NSBackspaceCharacter:
-            while( ( i_row = [self selectedRow] ) != -1 )
-            {
+            o_to_delete = [NSMutableArray arrayWithArray:[[self selectedRowEnumerator] allObjects]];
+            c = [o_to_delete count];
+            
+            for( i = 0; i < c; i++ ) {
+                o_number = [o_to_delete lastObject];
+                i_row = [o_number intValue];
+                
                 if( p_playlist->i_index == i_row && p_playlist->i_status )
                 {
                     playlist_Stop( p_playlist );
                 }
-        
-                playlist_Delete( p_playlist, i_row ); 
-        
+                [o_to_delete removeObject: o_number];
                 [self deselectRow: i_row];
+                playlist_Delete( p_playlist, i_row );
             }
             [self reloadData];
             break;
@@ -115,40 +107,6 @@ static NSColor *sStripeColor = nil;
 }
 
 
-/* This is called after the table background is filled in, but before the cell contents are drawn.
- * We override it so we can do our own light-blue row stripes a la iTunes.
- */
-- (void) highlightSelectionInClipRect:(NSRect)rect {
-    [self drawStripesInRect:rect];
-    [super highlightSelectionInClipRect:rect];
-}
-
-/* This routine does the actual blue stripe drawing, filling in every other row of the table
- * with a blue background so you can follow the rows easier with your eyes.
- */
-- (void) drawStripesInRect:(NSRect)clipRect {
-    NSRect stripeRect;
-    float fullRowHeight = [self rowHeight] + [self intercellSpacing].height;
-    float clipBottom = NSMaxY(clipRect);
-    int firstStripe = clipRect.origin.y / fullRowHeight;
-    if (firstStripe % 2 == 0)
-        firstStripe++;   // we're only interested in drawing the stripes
-                         // set up first rect
-    stripeRect.origin.x = clipRect.origin.x;
-    stripeRect.origin.y = firstStripe * fullRowHeight;
-    stripeRect.size.width = clipRect.size.width;
-    stripeRect.size.height = fullRowHeight;
-    // set the color
-    if (sStripeColor == nil)
-        sStripeColor = [[NSColor colorWithCalibratedRed:STRIPE_RED green:STRIPE_GREEN blue:STRIPE_BLUE alpha:1.0] retain];
-    [sStripeColor set];
-    // and draw the stripes
-    while (stripeRect.origin.y < clipBottom) {
-        NSRectFill(stripeRect);
-        stripeRect.origin.y += fullRowHeight * 2.0;
-    }
-}
-
 @end
 
 /*****************************************************************************
@@ -156,6 +114,16 @@ static NSColor *sStripeColor = nil;
  *****************************************************************************/
 @implementation VLCPlaylist
 
+- (id)init
+{
+    self = [super init];
+    if ( self !=nil )
+    {
+        i_moveRow = -1;
+    }
+    return self;
+}
+
 - (void)awakeFromNib
 {
     [o_table_view setTarget: self];
@@ -166,15 +134,123 @@ static NSColor *sStripeColor = nil;
 
     [o_table_view registerForDraggedTypes: 
         [NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
+    [o_table_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
+    [o_window setExcludedFromWindowsMenu: TRUE];
+
+//    [o_tbv_info setDataSource: [VLCInfoDataSource init]];
+
+/* We need to check whether _defaultTableHeaderSortImage exists, since it 
+belongs to an Apple hidden private API, and then can "disapear" at any time*/
+
+    if( [[NSTableView class] respondsToSelector:@selector(_defaultTableHeaderSortImage)] )
+    {
+        o_ascendingSortingImage = [[NSTableView class] _defaultTableHeaderSortImage];
+    }
+    else
+    {
+        o_ascendingSortingImage = nil;
+    }
+
+    if( [[NSTableView class] respondsToSelector:@selector(_defaultTableHeaderReverseSortImage)] )
+    {
+        o_descendingSortingImage = [[NSTableView class] _defaultTableHeaderReverseSortImage];
+    }
+    else
+    {
+        o_descendingSortingImage = nil;
+    }
+
+    [self initStrings];
+    [self playlistUpdated];
+}
 
+- (void)initStrings
+{
+    [o_window setTitle: _NS("Playlist")];
+    [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
     [o_mi_play setTitle: _NS("Play")];
     [o_mi_delete setTitle: _NS("Delete")];
     [o_mi_selectall setTitle: _NS("Select All")];
-    
-    [o_btn_add setToolTip: _NS("Add")];
-    [o_btn_remove setToolTip: _NS("Delete")];
+    [o_mi_info setTitle: _NS("Properties")];
+
+    [[o_tc_name headerCell] setStringValue:_NS("Name")];
+    [[o_tc_author headerCell] setStringValue:_NS("Author")];
+    [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
+    [o_random_ckb setTitle: _NS("Random")];
+    [o_search_button setTitle: _NS("Search")];
+    [o_btn_playlist setToolTip: _NS("Playlist")];
+    [[o_loop_popup itemAtIndex:0] setTitle: _NS("Standard Play")];
+    [[o_loop_popup itemAtIndex:1] setTitle: _NS("Repeat One")];
+    [[o_loop_popup itemAtIndex:2] setTitle: _NS("Repeat All")];
 }
 
+- (void) tableView:(NSTableView*)o_tv
+                  didClickTableColumn:(NSTableColumn *)o_tc
+{
+    intf_thread_t * p_intf = [NSApp getIntf];
+    playlist_t *p_playlist =
+        (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
+                                       FIND_ANYWHERE );
+
+    int max = [[o_table_view tableColumns] count];
+    int i;
+
+    if( p_playlist == NULL )
+    {
+        return;
+    }
+
+    if( o_tc_sortColumn == o_tc )
+    {
+        b_isSortDescending = !b_isSortDescending;
+    }
+    else if( o_tc == o_tc_name || o_tc == o_tc_author || 
+        o_tc == o_tc_id )
+    {
+        b_isSortDescending = VLC_FALSE;
+        [o_table_view setHighlightedTableColumn:o_tc];
+        o_tc_sortColumn = o_tc;
+        for( i=0 ; i<max ; i++ )
+        {
+            [o_table_view setIndicatorImage:nil inTableColumn:[[o_table_view tableColumns] objectAtIndex:i]];
+        }
+    }
+
+    if( o_tc_id == o_tc && !b_isSortDescending )
+    {    
+        playlist_SortID( p_playlist , ORDER_NORMAL );
+        [o_table_view setIndicatorImage:o_ascendingSortingImage inTableColumn:o_tc];    
+    }
+    else if( o_tc_name == o_tc && !b_isSortDescending )
+    {    
+        playlist_SortTitle( p_playlist , ORDER_NORMAL );
+        [o_table_view setIndicatorImage:o_ascendingSortingImage inTableColumn:o_tc];    
+    }
+    else if( o_tc_author == o_tc && !b_isSortDescending )
+    {
+        playlist_SortAuthor( p_playlist , ORDER_NORMAL );
+        [o_table_view setIndicatorImage:o_ascendingSortingImage inTableColumn:o_tc];
+    }
+    else if( o_tc_id == o_tc && b_isSortDescending )
+    {    
+        playlist_SortID( p_playlist , ORDER_REVERSE );
+        [o_table_view setIndicatorImage:o_ascendingSortingImage inTableColumn:o_tc];    
+    }
+    else if( o_tc_name == o_tc && b_isSortDescending )
+    {    
+        playlist_SortTitle( p_playlist , ORDER_REVERSE );
+        [o_table_view setIndicatorImage:o_descendingSortingImage inTableColumn:o_tc];
+    }
+    else if( o_tc_author == o_tc && b_isSortDescending )
+    {
+        playlist_SortAuthor( p_playlist , ORDER_REVERSE );
+        [o_table_view setIndicatorImage:o_descendingSortingImage inTableColumn:o_tc];
+    } 
+    vlc_object_release( p_playlist );
+    [self playlistUpdated];
+}
+
+
 - (BOOL)tableView:(NSTableView *)o_tv 
                   shouldEditTableColumn:(NSTableColumn *)o_tc
                   row:(int)i_row
@@ -197,29 +273,62 @@ static NSColor *sStripeColor = nil;
     [o_mi_play setEnabled: b_item_sel];
     [o_mi_delete setEnabled: b_item_sel];
     [o_mi_selectall setEnabled: b_rows];
+    [o_mi_info setEnabled: b_item_sel];
 
     return( o_ctx_menu );
 }
 
-- (IBAction)playItem:(id)sender
+- (IBAction)toggleWindow:(id)sender
+{
+    if( [o_window isVisible] )
+    {
+        [o_window orderOut:sender];
+        [o_btn_playlist setState:NSOffState];
+    }
+    else
+    {
+        [o_window makeKeyAndOrderFront:sender];
+        [o_btn_playlist setState:NSOnState];
+    }
+}
+
+- (IBAction)savePlaylist:(id)sender
 {
     intf_thread_t * p_intf = [NSApp getIntf];
     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                                        FIND_ANYWHERE );
+    
+    NSSavePanel *o_save_panel = [NSSavePanel savePanel];
+    NSString * o_name = [NSString stringWithFormat: @"%@.m3u", _NS("Untitled")];
+    [o_save_panel setTitle: _NS("Save Playlist")];
+    [o_save_panel setPrompt: _NS("Save")];
 
-    if( p_playlist == NULL )
+    if( [o_save_panel runModalForDirectory: nil
+            file: o_name] == NSOKButton )
     {
-        return;
+        playlist_Export( p_playlist, [[o_save_panel filename] fileSystemRepresentation], "export-m3u" );
     }
 
-    playlist_Goto( p_playlist, [o_table_view selectedRow] );
+}
 
-    vlc_object_release( p_playlist );
+- (IBAction)playItem:(id)sender
+{
+    intf_thread_t * p_intf = [NSApp getIntf];
+    playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
+                                                       FIND_ANYWHERE );
+
+    if( p_playlist != NULL )
+    {
+        playlist_Goto( p_playlist, [o_table_view selectedRow] );
+        vlc_object_release( p_playlist );
+    }
 }
 
 - (IBAction)deleteItems:(id)sender
 {
-    int i_row;
+    int i, c, i_row;
+    NSMutableArray *o_to_delete;
+    NSNumber *o_number;
 
     intf_thread_t * p_intf = [NSApp getIntf];
     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
@@ -229,17 +338,21 @@ static NSColor *sStripeColor = nil;
     {
         return;
     }
-
-    while( ( i_row = [o_table_view selectedRow] ) != -1 )
-    {
+    
+    o_to_delete = [NSMutableArray arrayWithArray:[[o_table_view selectedRowEnumerator] allObjects]];
+    c = (int)[o_to_delete count];
+    
+    for( i = 0; i < c; i++ ) {
+        o_number = [o_to_delete lastObject];
+        i_row = [o_number intValue];
+        
         if( p_playlist->i_index == i_row && p_playlist->i_status )
         {
             playlist_Stop( p_playlist );
         }
-
-        playlist_Delete( p_playlist, i_row ); 
-
+        [o_to_delete removeObject: o_number];
         [o_table_view deselectRow: i_row];
+        playlist_Delete( p_playlist, i_row );
     }
 
     vlc_object_release( p_playlist );
@@ -248,6 +361,7 @@ static NSColor *sStripeColor = nil;
      * when the playlist changes. we do this on purpose, because else there is a 
      * delay of .5 sec or so when we delete an item */
     [self playlistUpdated];
+    [self updateRowSelection];
 }
 
 - (IBAction)selectAll:(id)sender
@@ -255,11 +369,115 @@ static NSColor *sStripeColor = nil;
     [o_table_view selectAll: nil];
 }
 
-- (void)appendArray:(NSArray*)o_array atPos:(int)i_pos enqueue:(BOOL)b_enqueue
+
+- (IBAction)searchItem:(id)sender
+{
+    int i_current = -1;
+    NSString *o_current_name;
+    NSString *o_current_author;
+
+    intf_thread_t * p_intf = [NSApp getIntf];
+    playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
+                                               FIND_ANYWHERE );
+    
+    if( p_playlist == NULL )
+    {
+        return;
+    }
+    if( [o_table_view numberOfRows] < 1 )
+    {
+        return;
+    }
+
+    if( [o_table_view selectedRow] == [o_table_view numberOfRows]-1 )
+    {
+        i_current = -1;
+    }
+    else
+    {
+        i_current = [o_table_view selectedRow]; 
+    }
+
+    do
+    {
+        i_current++;
+
+        vlc_mutex_lock( &p_playlist->object_lock );
+        o_current_name = [NSString stringWithUTF8String: 
+            p_playlist->pp_items[i_current]->input.psz_name];
+        o_current_author = [NSString stringWithUTF8String: 
+            playlist_GetInfo(p_playlist, i_current ,_("General"),_("Author") )];
+        vlc_mutex_unlock( &p_playlist->object_lock );
+
+
+        if( [o_current_name rangeOfString:[o_search_keyword stringValue] options:NSCaseInsensitiveSearch ].length ||
+             [o_current_author rangeOfString:[o_search_keyword stringValue] options:NSCaseInsensitiveSearch ].length )
+        {
+             [o_table_view selectRow: i_current byExtendingSelection: NO];
+             [o_table_view scrollRowToVisible: i_current];
+             break;
+        }
+        if( i_current == [o_table_view numberOfRows] - 1 )
+        {
+             i_current = -1;
+        }
+    }
+    while (i_current != [o_table_view selectedRow]);
+    vlc_object_release( p_playlist );
+}
+
+
+- (IBAction)handlePopUp:(id)sender
+
 {
-    int i_items;
-    NSString * o_value;
-    NSEnumerator * o_enum;
+             intf_thread_t * p_intf = [NSApp getIntf];
+             vlc_value_t val1,val2;
+             playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
+                                                        FIND_ANYWHERE );
+             if( p_playlist == NULL )
+             {
+                 return;
+             }
+
+    switch ([o_loop_popup indexOfSelectedItem])
+    {
+        case 1:
+
+             val1.b_bool = 0;
+             var_Set( p_playlist, "loop", val1 );
+             val1.b_bool = 1;
+             var_Set( p_playlist, "repeat", val1 );
+             vout_OSDMessage( p_intf, _( "Repeat One" ) );
+        break;
+
+        case 2:
+             val1.b_bool = 0;
+             var_Set( p_playlist, "repeat", val1 );
+             val1.b_bool = 1;
+             var_Set( p_playlist, "loop", val1 );
+             vout_OSDMessage( p_intf, _( "Repeat All" ) );
+        break;
+
+        default:
+             var_Get( p_playlist, "repeat", &val1 );
+             var_Get( p_playlist, "loop", &val2 );
+             if (val1.b_bool || val2.b_bool)
+             {
+                  val1.b_bool = 0;
+                  var_Set( p_playlist, "repeat", val1 );
+                  var_Set( p_playlist, "loop", val1 );
+                  vout_OSDMessage( p_intf, _( "Repeat Off" ) );
+             }
+         break;
+     }
+     vlc_object_release( p_playlist );
+     [self playlistUpdated];
+}
+
+
+- (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
+{
+    int i_item;
     intf_thread_t * p_intf = [NSApp getIntf];
     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                                        FIND_ANYWHERE );
@@ -269,28 +487,80 @@ static NSColor *sStripeColor = nil;
         return;
     }
 
-    i_items = 0;
-    o_enum = [o_array objectEnumerator];
-    while( ( o_value = [o_enum nextObject] ) )
+    for ( i_item = 0; i_item < (int)[o_array count]; i_item++ )
     {
-        NSURL * o_url;
-
+        /* One item */
+        NSDictionary *o_one_item;
+        int j, i_total_options = 0, i_new_id = -1;
         int i_mode = PLAYLIST_INSERT;
+        BOOL b_rem = FALSE, b_dir = FALSE;
+        NSString *o_uri, *o_name;
+        NSArray *o_options;
+        NSURL *o_true_file;
+        char **ppsz_options = NULL;
+    
+        /* Get the item */
+        o_one_item = [o_array objectAtIndex: i_item];
+        o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
+        o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
+        o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
         
-        if (i_items == 0 && !b_enqueue)
-            i_mode |= PLAYLIST_GO;
+        /* If no name, then make a guess */
+        if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
+    
+        if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
+            [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
+                    isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem   )
+        {
+            /* All of this is to make sure CD's play when you D&D them on VLC */
+            /* Converts mountpoint to a /dev file */
+            struct statfs *buf;
+            char *psz_dev;
+            buf = (struct statfs *) malloc (sizeof(struct statfs));
+            statfs( [o_uri fileSystemRepresentation], buf );
+            psz_dev = strdup(buf->f_mntfromname);
+            o_uri = [NSString stringWithCString: psz_dev ];
+        }
 
-        playlist_Add( p_playlist, [o_value fileSystemRepresentation],
-            i_mode, i_pos == -1 ? PLAYLIST_END : i_pos + i_items );
+        if( o_options && [o_options count] > 0 )
+        {
+            /* Count the input options */
+            i_total_options = [o_options count];
+    
+            /* Allocate ppsz_options */
+            for( j = 0; j < i_total_options; j++ )
+            {
+                if( !ppsz_options )
+                    ppsz_options = (char **)malloc( sizeof(char *) * i_total_options );
+    
+                ppsz_options[j] = strdup([[o_options objectAtIndex:j] UTF8String]);
+            }
+        }
 
-        o_url = [NSURL fileURLWithPath: o_value];
-        if( o_url != nil )
+        /* Add the item */
+        i_new_id = playlist_AddExt( p_playlist, [o_uri fileSystemRepresentation], 
+                      [o_name UTF8String], i_mode, 
+                      i_position == -1 ? PLAYLIST_END : i_position + i_item,
+                      0, (ppsz_options != NULL ) ? (const char **)ppsz_options : 0, i_total_options );
+
+        /* clean up 
+        for( j = 0; j < i_total_options; j++ )
+            free( ppsz_options[j] );
+        if( ppsz_options ) free( ppsz_options ); */
+
+        /* Recent documents menu */
+        o_true_file = [NSURL fileURLWithPath: o_uri];
+        if( o_true_file != nil )
         { 
             [[NSDocumentController sharedDocumentController]
-                noteNewRecentDocumentURL: o_url]; 
+                noteNewRecentDocumentURL: o_true_file]; 
+        }
+        
+        if( i_item == 0 && !b_enqueue )
+        {
+            playlist_Goto( p_playlist, playlist_GetPositionById( p_playlist, i_new_id ) );
+            playlist_Play( p_playlist );
         }
-
-        i_items++;
     }
 
     vlc_object_release( p_playlist );
@@ -298,6 +568,31 @@ static NSColor *sStripeColor = nil;
 
 - (void)playlistUpdated
 {
+    vlc_value_t val1, val2;
+    intf_thread_t * p_intf = [NSApp getIntf];
+    playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
+                                                       FIND_ANYWHERE );
+    if( p_playlist != NULL )
+    {
+        var_Get( p_playlist, "random", &val1 );
+        [o_random_ckb setState: val1.b_bool];
+
+        var_Get( p_playlist, "repeat", &val1 );
+        var_Get( p_playlist, "loop", &val2 );
+        if(val1.b_bool)
+        {
+            [o_loop_popup selectItemAtIndex:1];
+        }
+        else if(val2.b_bool)
+        {
+            [o_loop_popup selectItemAtIndex:2];
+        }
+        else
+        {
+            [o_loop_popup selectItemAtIndex:0];
+        }
+        vlc_object_release( p_playlist );
+    }
     [o_table_view reloadData];
 }
 
@@ -314,15 +609,18 @@ static NSColor *sStripeColor = nil;
         return;
     }
 
-    vlc_mutex_lock( &p_playlist->object_lock );    
     i_row = p_playlist->i_index;
-    vlc_mutex_unlock( &p_playlist->object_lock );
     vlc_object_release( p_playlist );
 
     [o_table_view selectRow: i_row byExtendingSelection: NO];
     [o_table_view scrollRowToVisible: i_row];
 }
 
+- (int)selectedPlaylistItem
+{
+    return [o_table_view selectedRow];
+}
+
 @end
 
 @implementation VLCPlaylist (NSTableDataSource)
@@ -341,7 +639,7 @@ static NSColor *sStripeColor = nil;
         vlc_mutex_unlock( &p_playlist->object_lock );
         vlc_object_release( p_playlist );
     }
-
+    [o_status_field setStringValue: [NSString stringWithFormat:_NS("%i items in playlist"), i_count]];
     return( i_count );
 }
 
@@ -359,111 +657,206 @@ static NSColor *sStripeColor = nil;
         return( nil );
     }
 
-    vlc_mutex_lock( &p_playlist->object_lock );
-    o_value = [[NSString stringWithUTF8String: 
-        p_playlist->pp_items[i_row]->psz_name] lastPathComponent]; 
-    vlc_mutex_unlock( &p_playlist->object_lock ); 
+    if( [[o_tc identifier] isEqualToString:@"0"] )
+    {
+        o_value = [NSString stringWithFormat:@"%i", i_row + 1];
+    }
+    else if( [[o_tc identifier] isEqualToString:@"1"] )
+    {
+        vlc_mutex_lock( &p_playlist->object_lock );
+        o_value = [NSString stringWithUTF8String: 
+            p_playlist->pp_items[i_row]->input.psz_name];
+        if( o_value == NULL )
+            o_value = [NSString stringWithCString: 
+                p_playlist->pp_items[i_row]->input.psz_name];
+        vlc_mutex_unlock( &p_playlist->object_lock );
+    }
+    else if( [[o_tc identifier] isEqualToString:@"2"] )
+    {
+        vlc_mutex_lock( &p_playlist->object_lock );
+        o_value = [NSString stringWithUTF8String: 
+            playlist_GetInfo(p_playlist, i_row ,_("General"),_("Author") )];
+        if( o_value == NULL )
+            o_value = [NSString stringWithCString: 
+                playlist_GetInfo(p_playlist, i_row ,_("General"),_("Author") )];
+        vlc_mutex_unlock( &p_playlist->object_lock );
+    }
+    else if( [[o_tc identifier] isEqualToString:@"3"] )
+    {
+        char psz_duration[MSTRTIME_MAX_SIZE];
+        mtime_t dur = p_playlist->pp_items[i_row]->i_duration;
+        if( dur != -1 )
+        {
+            secstotimestr( psz_duration, dur/1000000 );
+            o_value = [NSString stringWithUTF8String: psz_duration];
+        }
+        else
+        {
+            o_value = @"-:--:--";
+        }
+    }
 
     vlc_object_release( p_playlist );
 
     return( o_value );
 }
 
-// NEW API for Dragging in TableView:
-// typedef enum { NSTableViewDropOn, NSTableViewDropAbove } NSTableViewDropOperation;
-// In drag and drop, used to specify a dropOperation. For example, given a table with N rows (numbered with row 0 at the top visually), a row of N-1 and operation of NSTableViewDropOn would specify a drop on the last row. To specify a drop below the last row, one would use a row of N and NSTableViewDropAbove for the operation.
+- (void)tableView:(NSTableView *)o_tv
+                   willDisplayCell:(id)o_cell
+                   forTableColumn:(NSTableColumn *)o_tc
+                   row:(int)o_rows
+{
+    intf_thread_t * p_intf = [NSApp getIntf];
+    playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
+                                               FIND_ANYWHERE );
+    if ((p_playlist->i_groups) > 1 )
+    {
+       [o_cell setDrawsBackground: VLC_TRUE];
+       switch ( p_playlist->pp_items[o_rows]->i_group % 8 )
+       {
+            case 1:
+              /*white*/
+              [o_cell setBackgroundColor: [NSColor colorWithDeviceRed:1.0 green:1.0 blue:1.0 alpha:1.0]];
+              break;
+
+            case 2:
+              /*red*/
+             [o_cell setBackgroundColor: [NSColor colorWithDeviceRed:1.0 green:0.76471 blue:0.76471 alpha:1.0]];
+           break;
+
+           case 3:
+              /*dark blue*/
+                  [o_cell setBackgroundColor: [NSColor colorWithDeviceRed:0.76471 green:0.76471 blue:1.0 alpha:1.0]];
+            break; 
+
+            case 4:
+               /*orange*/
+                   [o_cell setBackgroundColor: [NSColor colorWithDeviceRed:1.0 green:0.89804 blue:0.76471 alpha:1.0]];
+            break;
+
+            case 5:
+                /*purple*/
+                   [o_cell setBackgroundColor: [NSColor colorWithDeviceRed:1.0 green:0.76471 blue:1.0 alpha:1.0]];
+            break;
+            case 6:
+                /*green*/
+                   [o_cell setBackgroundColor: [NSColor colorWithDeviceRed:0.76471 green:1.0 blue:0.76471 alpha:1.0]];
+            break; 
+
+            case 7:
+               /*light blue*/
+                   [o_cell setBackgroundColor: [NSColor colorWithDeviceRed:0.76471 green:1.0 blue:1.0 alpha:1.0]];
+            break;
 
-static int _moveRow = -1;
+            case 0:
+               /*yellow*/
+                   [o_cell setBackgroundColor: [NSColor colorWithDeviceRed:1.0 green:1.0 blue:0.76471 alpha:1.0]];
+            break;
+       }
+     
+    }
+vlc_object_release( p_playlist );
+}
 
-- (BOOL)tableView:(NSTableView *)tv
-                    writeRows:(NSArray*)rows
-                    toPasteboard:(NSPasteboard*)pboard 
-// This method is called after it has been determined that a drag should begin, but before the drag has been started. To refuse the drag, return NO. To start a drag, return YES and place the drag data onto the pasteboard (data, owner, etc...). The drag image and other drag related information will be set up and provided by the table view once this call returns with YES. The rows array is the list of row numbers that will be participating in the drag.
+- (BOOL)tableView:(NSTableView *)o_tv
+                    writeRows:(NSArray*)o_rows
+                    toPasteboard:(NSPasteboard*)o_pasteboard 
 {
-    int rowCount = [rows count];
+    int i_rows = [o_rows count];
     NSArray *o_filenames = [NSArray array];
     
-    // we should allow group selection and copy between windows: PENDING
-    [pboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:self];
-    [pboard setPropertyList:o_filenames forType:NSFilenamesPboardType];
-    if (rowCount == 1)
+    [o_pasteboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:self];
+    [o_pasteboard setPropertyList:o_filenames forType:NSFilenamesPboardType];
+    if ( i_rows == 1 )
     {
-        _moveRow = [[rows objectAtIndex:0]intValue];
+        i_moveRow = [[o_rows objectAtIndex:0]intValue];
         return YES;
     }
     return NO;
 }
 
-- (NSDragOperation)tableView:(NSTableView*)tv
-                    validateDrop:(id <NSDraggingInfo>)info
-                    proposedRow:(int)row
-                    proposedDropOperation:(NSTableViewDropOperation)op 
-// This method is used by NSTableView to determine a valid drop target. Based on the mouse position, the table view will suggest a proposed drop location. This method must return a value that indicates which dragging operation the data source will perform. The data source may "re-target" a drop if desired by calling setDropRow:dropOperation: and returning something other than NSDragOperationNone. One may choose to re-target for various reasons (eg. for better visual feedback when inserting into a sorted position).
+- (NSDragOperation)tableView:(NSTableView*)o_tv
+                    validateDrop:(id <NSDraggingInfo>)o_info
+                    proposedRow:(int)i_row
+                    proposedDropOperation:(NSTableViewDropOperation)o_operation 
 {
-    if ( op == NSTableViewDropAbove )
+    if ( o_operation == NSTableViewDropAbove )
     {
-        if ( row != _moveRow && _moveRow >= 0 )
+        if ( i_moveRow >= 0 )
         {
-            return NSDragOperationMove;
+            if ( i_row != i_moveRow )
+            {
+                return NSDragOperationMove;
+            }
+            /* what if in the previous run, the row wasn't actually moved? 
+               then we can't drop new files on this location */
+            return NSDragOperationNone;
         }
-        return NSDragOperationLink;
+        return NSDragOperationGeneric;
     }
     return NSDragOperationNone;
 }
 
-- (BOOL)tableView:(NSTableView*)tv
-                    acceptDrop:(id <NSDraggingInfo>)info
-                    row:(int)i_row
-                    dropOperation:(NSTableViewDropOperation)op 
-// This method is called when the mouse is released over an outline view that previously decided to allow a drop via the validateDrop method. The data source should incorporate the data from the dragging pasteboard at this time. 
+- (BOOL)tableView:(NSTableView*)o_tv
+                    acceptDrop:(id <NSDraggingInfo>)o_info
+                    row:(int)i_proposed_row
+                    dropOperation:(NSTableViewDropOperation)o_operation 
 {
-    if (  _moveRow >= 0 )
+    if (  i_moveRow >= 0 )
     {
-        BOOL result = [self tableView:tv didDepositRow:_moveRow at:(int)i_row];
+        if (i_moveRow != -1 && i_proposed_row != -1)
+        {
+            intf_thread_t * p_intf = [NSApp getIntf];
+            playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
+                                                            FIND_ANYWHERE );
+        
+            if( p_playlist == NULL )
+            {
+                i_moveRow = -1;
+                return NO;
+            }
+    
+            playlist_Move( p_playlist, i_moveRow, i_proposed_row ); 
+        
+            vlc_object_release( p_playlist );
+        }
         [self playlistUpdated];
-        _moveRow = -1;
-        return result;
+        i_moveRow = -1;
+        return YES;
     }
     else
     {
-        NSArray * o_values;
         NSPasteboard * o_pasteboard;
-        
-        o_pasteboard = [info draggingPasteboard];
+        o_pasteboard = [o_info draggingPasteboard];
         
         if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
         {
-            o_values = [o_pasteboard propertyListForType: NSFilenamesPboardType];
-        
-            [self appendArray: o_values atPos: i_row enqueue:YES];
-        
-            return( YES );
+            int i;
+            NSArray *o_array = [NSArray array];
+            NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType]
+                        sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
+
+            for( i = 0; i < (int)[o_values count]; i++)
+            {
+                NSDictionary *o_dic;
+                o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
+                o_array = [o_array arrayByAddingObject: o_dic];
+            }
+            [self appendArray: o_array atPos: i_proposed_row enqueue:YES];
+            return YES;
         }
-        
-        return( NO );
+        return NO;
     }
+    [self updateRowSelection];
 }
 
--  (BOOL)tableView:(NSTableView *)tv didDepositRow:(int)i_row at:(int)i_newrow
+/* Delegate method of NSWindow */
+- (void)windowWillClose:(NSNotification *)aNotification
 {
-    if (i_row != -1 && i_newrow != -1)
-    {
-        intf_thread_t * p_intf = [NSApp getIntf];
-        playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
-                                                        FIND_ANYWHERE );
-    
-        if( p_playlist == NULL )
-        {
-            return NO;
-        }
-
-        playlist_Move( p_playlist, i_row, i_newrow ); 
-    
-        vlc_object_release( p_playlist );
-        return YES;
-    }
-    return NO;
+    [o_btn_playlist setState: NSOffState];
 }
 
 @end
 
+