Logo Search packages:      
Sourcecode: parole version File versions  Download package

parole-medialist.c

/*
 * * Copyright (C) 2009 Ali <aliov@xfce.org>
 *
 * Licensed under the GNU General Public License Version 2
 *
 * 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
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <gio/gio.h>

#include <libxfce4util/libxfce4util.h>
#include <libxfcegui4/libxfcegui4.h>

#include <parole/parole-file.h>

#include "interfaces/playlist_ui.h"
#include "interfaces/save-playlist_ui.h"

#include "parole-builder.h"
#include "parole-medialist.h"
#include "parole-mediachooser.h"
#include "parole-open-location.h"

#include "parole-filters.h"
#include "parole-pl-parser.h"
#include "parole-utils.h"
#include "parole-rc-utils.h"
#include "parole-dbus.h"

#include "common/parole-common.h"

#define PAROLE_AUTO_SAVED_PLAYLIST  "xfce4/parole/auto-saved-playlist.m3u"

typedef struct
{
    GtkWidget *chooser;
    GtkTreeSelection *sel;
    ParoleMediaList *list;
    
} ParolePlaylistSave;

static struct
{
    ParolePlFormat format;
    gchar * ext;
} playlist_format_map [] = {
  { PAROLE_PL_FORMAT_UNKNOWN, ""      },
  { PAROLE_PL_FORMAT_M3U,     ".m3u"  },
  { PAROLE_PL_FORMAT_PLS,     ".pls"  },
  { PAROLE_PL_FORMAT_ASX,     ".asx"  },
  { PAROLE_PL_FORMAT_XSPF,    ".xspf" }
};

static GtkTargetEntry target_entry[] =
{
    { "STRING",        0, 0 },
    { "text/uri-list", 0, 1 },
};

static void       parole_media_list_dbus_class_init (ParoleMediaListClass *klass);
static void       parole_media_list_dbus_init     (ParoleMediaList      *list);

static GtkTreeRowReference * 
    parole_media_list_get_row_reference_from_iter (ParoleMediaList *list, 
                                       GtkTreeIter *iter, 
                                       gboolean select_path);

static void       parole_media_list_select_path         (ParoleMediaList *list, 
                                       GtkTreePath *path);

static void parole_media_list_clear_list    (ParoleMediaList *list);

/*
 * Callbacks for GtkBuilder
 */
void        parole_media_list_media_up_clicked_cb     (GtkButton *button, 
                                           ParoleMediaList *list);
                                           
void        parole_media_list_media_down_clicked_cb (GtkButton *button, 
                                           ParoleMediaList *list);

void        parole_media_list_save_cb           (GtkButton *button,
                                             ParoleMediaList *list);
                                           
void        parole_media_list_add_clicked_cb    (GtkButton *button, 
                                           ParoleMediaList *list);
                                           
void        parole_media_list_remove_clicked_cb       (GtkButton *button, 
                                           ParoleMediaList *list);

void        parole_media_list_row_activated_cb  (GtkTreeView *view, 
                                           GtkTreePath *path,
                                           GtkTreeViewColumn *col,
                                           ParoleMediaList *list);

gboolean    parole_media_list_button_release_event  (GtkWidget *widget, 
                                           GdkEventButton *ev, 
                                           ParoleMediaList *list);
                                           
void        parole_media_list_drag_data_received_cb (GtkWidget *widget,
                                           GdkDragContext *drag_context,
                                           gint x,
                                           gint y,
                                           GtkSelectionData *data,
                                           guint info,
                                           guint drag_time,
                                           ParoleMediaList *list);

gboolean    parole_media_list_key_press         (GtkWidget *widget, 
                                           GdkEventKey *ev, 
                                           ParoleMediaList *list);

void        parole_media_list_format_cursor_changed_cb (GtkTreeView *view,
                                              ParolePlaylistSave *data);
                                              
void        parole_media_list_close_save_dialog_cb (GtkButton *button,
                                            ParolePlaylistSave *data);
                                        
void        parole_media_list_save_playlist_cb     (GtkButton *button,
                                            ParolePlaylistSave *data);
/*
 * End of GtkBuilder callbacks
 */

#define PAROLE_MEDIA_LIST_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), PAROLE_TYPE_MEDIA_LIST, ParoleMediaListPrivate))

struct ParoleMediaListPrivate
{
    DBusGConnection     *bus;
    GtkWidget           *view;
    GtkWidget           *box;
    GtkListStore  *store;
    GtkTreeSelection    *sel;
    
    GtkWidget           *remove;
    GtkWidget           *up;
    GtkWidget           *down;
    GtkWidget           *save;
};

enum
{
    MEDIA_ACTIVATED,
    MEDIA_CURSOR_CHANGED,
    URI_OPENED,
    LAST_SIGNAL
};

static guint signals [LAST_SIGNAL] = { 0 };

G_DEFINE_TYPE (ParoleMediaList, parole_media_list, GTK_TYPE_VBOX)

static void
parole_media_list_set_widget_sensitive (ParoleMediaList *list, gboolean sensitive)
{
    gtk_widget_set_sensitive (GTK_WIDGET (list->priv->up), sensitive);
    gtk_widget_set_sensitive (GTK_WIDGET (list->priv->remove), sensitive);
    gtk_widget_set_sensitive (GTK_WIDGET (list->priv->down), sensitive);
    gtk_widget_set_sensitive (GTK_WIDGET (list->priv->save), sensitive);
}

static void
parole_media_list_add (ParoleMediaList *list, ParoleFile *file, gboolean emit, gboolean select_row)
{
    GtkListStore *list_store;
    GtkTreePath *path;
    GtkTreeRowReference *row;
    GtkTreeIter iter;
    gint nch;
    
    list_store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (list->priv->view)));
    
    gtk_list_store_append (list_store, &iter);
    
    gtk_list_store_set (list_store, 
                  &iter, 
                  NAME_COL, parole_file_get_display_name (file),
                  DATA_COL, file,
                  -1);
    
    if ( emit )
    {
      path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_store), &iter);
      row = gtk_tree_row_reference_new (GTK_TREE_MODEL (list_store), path);
      if ( select_row )
          parole_media_list_select_path (list, path);
      gtk_tree_path_free (path);
      g_signal_emit (G_OBJECT (list), signals [MEDIA_ACTIVATED], 0, row);
      gtk_tree_row_reference_free (row);
    }
  
    /*
     * Unref it as the list store will have
     * a reference anyway.
     */
    g_object_unref (file);
    
    nch = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (list->priv->store), NULL); 
    
    if ( nch == 1 )
    {
      gtk_widget_set_sensitive (list->priv->up, FALSE);
      gtk_widget_set_sensitive (list->priv->down, FALSE);
      gtk_widget_set_sensitive (list->priv->save, TRUE);
      gtk_widget_set_sensitive (list->priv->remove, TRUE);
    }
    else
      parole_media_list_set_widget_sensitive (list, TRUE);
      
}

static void
parole_media_list_files_open (ParoleMediaList *list, GSList *files, 
                        gboolean replace, gboolean emit)
{
    ParoleFile *file;
    guint len;
    guint i;
    
    len = g_slist_length (files);
    TRACE ("Adding files");
    
    if ( len != 0 )
    {
      if ( replace )
          parole_media_list_clear_list (list);
      file = g_slist_nth_data (files, 0);
      parole_media_list_add (list, file, emit, TRUE);
    }
    
    for ( i = 1; i < len; i++)
    {
      file = g_slist_nth_data (files, i);
      parole_media_list_add (list, file, FALSE, FALSE);
    }
}

static void
parole_media_list_files_opened_cb (ParoleMediaChooser *chooser, 
                           gboolean play,
                           gboolean replace,
                           GSList *files, 
                           ParoleMediaList *list)
{
    parole_media_list_files_open (list, files, replace, play);
}

static void
parole_media_list_location_opened_cb (ParoleOpenLocation *obj, const gchar *location, ParoleMediaList *list)
{
    ParoleFile *file;
    
    if ( parole_is_uri_disc (location) )
    {
      g_signal_emit (G_OBJECT (list), signals [URI_OPENED], 0, location);
    }
    else
    {
      file = parole_file_new (location);
      parole_media_list_add (list, file, TRUE, TRUE);
    }
}

static void
parole_media_list_open_internal (ParoleMediaList *list)
{
    GtkWidget *chooser;
    
    chooser = parole_media_chooser_open_local (gtk_widget_get_toplevel (GTK_WIDGET (list)));
                                     
    g_signal_connect (G_OBJECT (chooser), "media_files_opened",
                  G_CALLBACK (parole_media_list_files_opened_cb), list);
    
    gtk_widget_show_all (GTK_WIDGET (chooser));
}

static void
parole_media_list_open_location_internal (ParoleMediaList *list)
{
    GtkWidget *location;
    
    location = parole_open_location (gtk_widget_get_toplevel (GTK_WIDGET (list)));
                                     
    g_signal_connect (G_OBJECT (location), "location-opened",
                      G_CALLBACK (parole_media_list_location_opened_cb), list);
    
    gtk_widget_show_all (GTK_WIDGET (location));
}

static GSList *
parole_media_list_get_files (ParoleMediaList *list)
{
    ParoleFile *file;
    GtkTreeIter iter;
    gboolean valid;
    GSList *files_list = NULL;
    
    for ( valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list->priv->store), &iter);
        valid;
        valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (list->priv->store), &iter))
    {
      gtk_tree_model_get (GTK_TREE_MODEL (list->priv->store), &iter,
                      DATA_COL, &file,
                      -1);
      
      files_list = g_slist_append (files_list, file);
    }
    
    return files_list;
}

void  parole_media_list_drag_data_received_cb (GtkWidget *widget,
                                     GdkDragContext *drag_context,
                                     gint x,
                                     gint y,
                                     GtkSelectionData *data,
                                     guint info,
                                     guint drag_time,
                                     ParoleMediaList *list)
{
    gchar **uri_list;
    gchar *path;
    guint i;
    guint added = 0;
    
    parole_window_busy_cursor (GTK_WIDGET (list)->window);
    
    uri_list = g_uri_list_extract_uris ((const gchar *)data->data);
    
    for ( i = 0; uri_list[i] != NULL; i++)
    {
      path = g_filename_from_uri (uri_list[i], NULL, NULL);
      added += parole_media_list_add_by_path (list, path, i == 0 ? TRUE : FALSE);

      g_free (path);
    }

    g_strfreev (uri_list);

    gdk_window_set_cursor (GTK_WIDGET (list)->window, NULL);
    gtk_drag_finish (drag_context, added == i ? TRUE : FALSE, FALSE, drag_time);
}

gboolean parole_media_list_key_press (GtkWidget *widget, GdkEventKey *ev, ParoleMediaList *list)
{
    if ( ev->keyval == GDK_Delete )
    {
      parole_media_list_remove_clicked_cb (NULL, list);
      return TRUE;
    }
    return FALSE;
}

void
parole_media_list_add_clicked_cb (GtkButton *button, ParoleMediaList *list)
{
    parole_media_list_open_internal (list);
}

void parole_media_list_close_save_dialog_cb (GtkButton *button, ParolePlaylistSave *data)
{
    gtk_widget_destroy (GTK_WIDGET (data->chooser));
    g_free (data);
}

static GtkTreeRowReference *
parole_media_list_get_first_selected_row (ParoleMediaList *list)
{
    GtkTreeModel *model;
    GtkTreeRowReference *row = NULL;
    GtkTreeIter iter;
    GList *path_list = NULL;
    
    path_list = gtk_tree_selection_get_selected_rows (list->priv->sel, &model);
    
    if ( g_list_length (path_list) > 0 )
    {
      GtkTreePath *path;
      path = g_list_nth_data (path_list, 0);
      
      if ( G_LIKELY (gtk_tree_model_get_iter (model, &iter, path) == TRUE ))
      {
          row = parole_media_list_get_row_reference_from_iter (list, &iter, FALSE);
      }
    }
    
    g_list_foreach (path_list, (GFunc)gtk_tree_path_free, NULL);
    g_list_free (path_list);
    
    return row;
}

void parole_media_list_save_playlist_cb (GtkButton *button, ParolePlaylistSave *data)
{
    ParolePlFormat format = PAROLE_PL_FORMAT_UNKNOWN;
    GSList *list = NULL;
    GtkTreeModel *model;
    GtkTreeIter iter;
    gchar *filename;
    gchar *dirname;
    
    filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (data->chooser));
    dirname = g_path_get_dirname (filename);
    
    if ( gtk_tree_selection_get_selected (data->sel, &model, &iter ) )
    {
      gtk_tree_model_get (model, &iter, 2, &format, -1);
    }
    
    if ( g_access (dirname, W_OK) == -1 )
    {
      xfce_err ("%s %s %s", _("Error saving playlist file"), dirname, _("Permission denied"));
      goto out;
    }
    
    if ( format == PAROLE_PL_FORMAT_UNKNOWN )
    {
      format = parole_pl_parser_guess_format_from_extension (filename);
      if ( format == PAROLE_PL_FORMAT_UNKNOWN )
      {
          xfce_info ("%s", _("Unknown playlist format, Please select a support playlist format"));
          goto out;
      }
    }
    
    list = parole_media_list_get_files (data->list);
    
    parole_media_list_close_save_dialog_cb (NULL, data);
    
    parole_pl_parser_save_from_files (list, filename, format);
    g_slist_free (list);
out:
    g_free (filename);
    g_free (dirname);
}

void parole_media_list_format_cursor_changed_cb (GtkTreeView *view, ParolePlaylistSave *data)
{
    GtkTreeIter iter;
    GtkTreeModel *model;
    ParolePlFormat format;
    gchar *filename;
    gchar *fbasename;
    
    filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (data->chooser));
    fbasename = g_path_get_basename (filename);
    
    g_free (filename);
    
    if ( gtk_tree_selection_get_selected (data->sel, &model, &iter ) )
    {
      gtk_tree_model_get (model, &iter, 2, &format, -1);
      if ( format != PAROLE_PL_FORMAT_UNKNOWN )
      {
          gchar *name, *new_name;
          name = parole_get_name_without_extension (fbasename);
          new_name = g_strdup_printf ("%s%s", name, playlist_format_map[format].ext);
          gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (data->chooser), new_name);
          g_free (new_name);
          g_free (name);
      }
    }
    g_free (fbasename);
    
}

void parole_media_list_save_cb (GtkButton *button, ParoleMediaList *list)
{
    ParolePlaylistSave *data;
    GtkWidget *chooser;
    GtkWidget *view;
    GtkListStore *store;
    GtkBuilder *builder;
    gchar *filename;
    GtkTreeIter iter;
    
    data = g_new0 (ParolePlaylistSave, 1);
    
    builder = parole_builder_new_from_string (save_playlist_ui, save_playlist_ui_length);
    
    chooser = GTK_WIDGET (gtk_builder_get_object (builder, "filechooserdialog"));
    store = GTK_LIST_STORE (gtk_builder_get_object (builder, "liststore"));

    gtk_window_set_transient_for (GTK_WINDOW (chooser), 
                          GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (list))));
                          
    gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (chooser), TRUE);
      
    filename = g_strconcat (_("Playlist"), ".m3u", NULL);
    gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (chooser), filename);
    g_free (filename);
    
    gtk_list_store_append (store, &iter);
    gtk_list_store_set (store, 
                  &iter, 
                  0, _("M3U Playlists"),
                  1, "m3u",
                  2, PAROLE_PL_FORMAT_M3U,
                  -1);
                  
    gtk_list_store_append (store, &iter);
    gtk_list_store_set (store, 
                  &iter, 
                  0, _("PLS Playlists"),
                  1, "pls",
                  2, PAROLE_PL_FORMAT_PLS,
                  -1);
                  
    gtk_list_store_append (store, &iter);
    gtk_list_store_set (store, 
                  &iter, 
                  0, _("Advanced Stream Redirector"),
                  1, "asx",
                  2, PAROLE_PL_FORMAT_ASX,
                  -1);
                  
    gtk_list_store_append (store, &iter);
    gtk_list_store_set (store, 
                  &iter, 
                  0, _("Shareable Playlist"),
                  1, "xspf",
                  2, PAROLE_PL_FORMAT_XSPF,
                  -1);

    view = GTK_WIDGET (gtk_builder_get_object (builder, "treeview"));
    
    data->chooser = chooser;
    data->sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
    data->list = list;

    gtk_builder_connect_signals (builder, data);
    gtk_widget_show_all (chooser);
    g_object_unref (builder);
}

static GList *
parole_media_list_path_to_row_list (GList *path_list, GtkTreeModel *model)
{
    GList *row_list = NULL;
    guint len, i;
    
    len = g_list_length (path_list);
    
    for ( i = 0; i < len; i++)
    {
      GtkTreePath *path;
      GtkTreeRowReference *row;
      
      path = g_list_nth_data (path_list, i);
      
      row = gtk_tree_row_reference_new (model, path);
      path = gtk_tree_row_reference_get_path (row);
      row_list = g_list_append (row_list, row);
    }
    
    return row_list;
}

void
parole_media_list_remove_clicked_cb (GtkButton *button, ParoleMediaList *list)
{
    GtkTreeModel *model;
    GList *path_list = NULL;
    GList *row_list = NULL;
    GtkTreeIter iter;
    gint nch;
    guint len, i;
    
    path_list = gtk_tree_selection_get_selected_rows (list->priv->sel, &model);
      
    row_list = parole_media_list_path_to_row_list (path_list, model);
    
    g_list_foreach (path_list, (GFunc) gtk_tree_path_free, NULL);
    g_list_free (path_list);
    
    len = g_list_length (row_list);
    
    for ( i = 0; i < len; i++)
    {
      GtkTreePath *path;
      GtkTreeRowReference *row;
      row = g_list_nth_data (row_list, i);
      path = gtk_tree_row_reference_get_path (row);
    
      if ( G_LIKELY (gtk_tree_model_get_iter (model, &iter, path) == TRUE ) )
      {
          gtk_list_store_remove (GTK_LIST_STORE (model),
                           &iter);
      }
    }
    
    g_list_foreach (row_list, (GFunc) gtk_tree_row_reference_free, NULL);
    g_list_free (row_list);
    
    /*
     * Returns the number of children that iter has. 
     * As a special case, if iter is NULL, 
     * then the number of toplevel nodes is returned. Gtk API doc.
     */
    nch = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (list->priv->store), NULL); 
    
    if ( nch == 0)
    {
      parole_media_list_set_widget_sensitive (list, FALSE);
      /*
       * Will emit the signal media_cursor_changed with FALSE because there is no any 
       * row remaining, so the player can disable click on the play button.
       */
      g_signal_emit (G_OBJECT (list), signals [MEDIA_CURSOR_CHANGED], 0, FALSE);
    }
    else if ( nch == 1 )
    {
      gtk_widget_set_sensitive (list->priv->up, FALSE);
      gtk_widget_set_sensitive (list->priv->down, FALSE);
    }
}

static void
parole_media_list_move_one_down (GtkListStore *store, GtkTreeIter *iter)
{
    GtkTreeIter *pos_iter;
    
    /* Save the selected iter to the selected row */
    pos_iter = gtk_tree_iter_copy (iter);

    /* We are on the last node in the list!*/
    if ( !gtk_tree_model_iter_next (GTK_TREE_MODEL (store), iter) )
    {
      /* Return false if tree is empty*/
      if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), iter))
      {
          gtk_list_store_move_before (GTK_LIST_STORE (store), pos_iter, iter);
      }
    }
    else
    {
      gtk_list_store_move_after (GTK_LIST_STORE (store), pos_iter, iter);
    }
    
    gtk_tree_iter_free (pos_iter);
}

static void
parole_media_list_move_many_down (GList *path_list, GtkTreeModel *model)
{
    GList *row_list = NULL;
    GtkTreeIter iter;
    guint len;
    guint i;
    
    row_list = parole_media_list_path_to_row_list (path_list, model);
    
    len = g_list_length (row_list);
    
    for ( i = 0; i < len; i++)
    {
      GtkTreeRowReference *row;
      GtkTreePath *path;
      
      row = g_list_nth_data (row_list, i);
      
      path = gtk_tree_row_reference_get_path (row);
      
      if ( G_LIKELY (gtk_tree_model_get_iter (model, &iter, path) ) )
      {
          parole_media_list_move_one_down (GTK_LIST_STORE (model), &iter);
      }
    }
    
    g_list_foreach (row_list, (GFunc) gtk_tree_row_reference_free, NULL);
    g_list_free (row_list);
}

void
parole_media_list_media_down_clicked_cb (GtkButton *button, ParoleMediaList *list)
{
    GtkTreeIter iter;
    GList *path_list = NULL;
    GtkTreeModel *model;
    guint len;
    
    path_list = gtk_tree_selection_get_selected_rows (list->priv->sel, &model);

    len = g_list_length (path_list);

    if ( len == 1 )
    {
      GtkTreePath *path;
      path = g_list_nth_data (path_list, 0);
      if ( G_LIKELY (gtk_tree_model_get_iter (model, &iter, path)) )
          parole_media_list_move_one_down (list->priv->store, &iter);
    }
    else
    {
      parole_media_list_move_many_down (path_list, model);
    }
    
    g_list_foreach (path_list, (GFunc) gtk_tree_path_free, NULL);
    g_list_free (path_list);
}

static void
parole_media_list_move_one_up (GtkListStore *store, GtkTreeIter *iter)
{
    GtkTreePath *path;
    GtkTreeIter *pos_iter;
    
    /* Save the selected iter to the selected row */
    pos_iter = gtk_tree_iter_copy (iter);
    
    path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), iter);
    
    /* We are on the top of the list */
    if ( !gtk_tree_path_prev (path) )
    {
      /* Passing NULL as the last argument will cause this call to move 
       * the iter to the end of the list, Gtk API doc*/
      gtk_list_store_move_before (GTK_LIST_STORE (store), iter, NULL);
    }
    else
    {
      if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), iter, path))
          gtk_list_store_move_before (GTK_LIST_STORE (store), pos_iter, iter);
    }
    
    gtk_tree_path_free (path);
    gtk_tree_iter_free (pos_iter);
}

static void
parole_media_list_move_many_up (GList *path_list, GtkTreeModel *model)
{
    GList *row_list = NULL;
    GtkTreeIter iter;
    guint len;
    guint i;
    
    row_list = parole_media_list_path_to_row_list (path_list, model);
    
    len = g_list_length (row_list);
    
    for ( i = 0; i < len; i++)
    {
      GtkTreeRowReference *row;
      GtkTreePath *path;
      
      row = g_list_nth_data (row_list, i);
      
      path = gtk_tree_row_reference_get_path (row);
      
      if ( G_LIKELY (gtk_tree_model_get_iter (model, &iter, path) ) )
      {
          parole_media_list_move_one_up (GTK_LIST_STORE (model), &iter);
      }
    }
    
    g_list_foreach (row_list, (GFunc) gtk_tree_row_reference_free, NULL);
    g_list_free (row_list);
}

void
parole_media_list_media_up_clicked_cb (GtkButton *button, ParoleMediaList *list)
{
    GtkTreeIter iter;
    GList *path_list = NULL;
    GtkTreeModel *model;
    guint len;
    
    path_list = gtk_tree_selection_get_selected_rows (list->priv->sel, &model);

    len = g_list_length (path_list);

    if ( len == 1 )
    {
      GtkTreePath *path;
      path = g_list_nth_data (path_list, 0);
      if ( G_LIKELY (gtk_tree_model_get_iter (model, &iter, path)) )
          parole_media_list_move_one_up (list->priv->store, &iter);
    }
    else
    {
      parole_media_list_move_many_up (path_list, model);
    }
    
    g_list_foreach (path_list, (GFunc) gtk_tree_path_free, NULL);
    g_list_free (path_list);
}

void
parole_media_list_row_activated_cb (GtkTreeView *view, GtkTreePath *path, 
                            GtkTreeViewColumn *col, ParoleMediaList *list)
{
    GtkTreeModel *model;
    GtkTreeRowReference *row;
    
    model = gtk_tree_view_get_model (GTK_TREE_VIEW (list->priv->view));

    row = gtk_tree_row_reference_new (gtk_tree_view_get_model (GTK_TREE_VIEW (list->priv->view)), 
                              path);
                              
    g_signal_emit (G_OBJECT (list), signals [MEDIA_ACTIVATED], 0, row);
}

static void
parole_media_list_selection_changed_cb (GtkTreeSelection *sel, ParoleMediaList *list)
{
    g_signal_emit (G_OBJECT (list), signals [MEDIA_CURSOR_CHANGED], 0,
               gtk_tree_selection_count_selected_rows (sel) > 0); 
}

static void
parole_media_list_clear_list (ParoleMediaList *list)
{
    gtk_list_store_clear (GTK_LIST_STORE (list->priv->store));
    parole_media_list_set_widget_sensitive (list, FALSE);
}

static void
save_list_activated_cb (GtkWidget *mi)
{
    gboolean active;
    
    active = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mi));
    
    parole_rc_write_entry_bool ("SAVE_LIST_ON_EXIT", PAROLE_RC_GROUP_GENERAL, active);
}

static void
parole_media_list_show_menu (ParoleMediaList *list, guint button, guint activate_time)
{
    GtkWidget *menu, *mi;

    menu = gtk_menu_new ();
    
    mi = gtk_check_menu_item_new_with_label (_("Remember playlist"));
    gtk_widget_set_sensitive (mi, TRUE);
    gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi),
                            parole_rc_read_entry_bool ("SAVE_LIST_ON_EXIT", 
                                                  PAROLE_RC_GROUP_GENERAL, 
                                                FALSE));
    g_signal_connect (mi, "activate",
                      G_CALLBACK (save_list_activated_cb), NULL);
                        
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
    
    gtk_widget_show (mi);
    
    mi = gtk_separator_menu_item_new ();
    gtk_widget_show (mi);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
    
    
    /* Clear */
    mi = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLEAR, NULL);
    gtk_widget_set_sensitive (mi, TRUE);
    gtk_widget_show (mi);
    g_signal_connect_swapped (mi, "activate",
                              G_CALLBACK (parole_media_list_clear_list), list);
                        
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
    
    g_signal_connect_swapped (menu, "selection-done",
                              G_CALLBACK (gtk_widget_destroy), menu);
    
    gtk_menu_popup (GTK_MENU (menu), 
                    NULL, NULL,
                    NULL, NULL,
                    button, activate_time);
}

gboolean
parole_media_list_button_release_event (GtkWidget *widget, GdkEventButton *ev, ParoleMediaList *list)
{
    if ( ev->button == 3 )
    {
      parole_media_list_show_menu (list, ev->button, ev->time);
      return TRUE;
    }
    
    return FALSE;
}

static void
parole_media_list_select_path (ParoleMediaList *list, GtkTreePath *path)
{
    gtk_tree_selection_select_path (list->priv->sel, path);
    gtk_tree_view_set_cursor (GTK_TREE_VIEW (list->priv->view), path, NULL, FALSE);
}

static GtkTreeRowReference *
parole_media_list_get_row_reference_from_iter (ParoleMediaList *list, GtkTreeIter *iter, gboolean select_path)
{
    GtkTreePath *path;
    GtkTreeRowReference *row;
    
    path = gtk_tree_model_get_path (GTK_TREE_MODEL (list->priv->store), iter);
    row = gtk_tree_row_reference_new (GTK_TREE_MODEL (list->priv->store), path);
    
    if ( select_path)
      parole_media_list_select_path (list, path);
    
    gtk_tree_path_free (path);
    
    return row;
}

static void
parole_media_list_finalize (GObject *object)
{
    ParoleMediaList *list;

    list = PAROLE_MEDIA_LIST (object);
    
    dbus_g_connection_unref (list->priv->bus);

    G_OBJECT_CLASS (parole_media_list_parent_class)->finalize (object);
}

static void
parole_media_list_class_init (ParoleMediaListClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS (klass);

    object_class->finalize = parole_media_list_finalize;
    
    signals[MEDIA_ACTIVATED] = 
        g_signal_new ("media-activated",
                      PAROLE_TYPE_MEDIA_LIST,
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET (ParoleMediaListClass, media_activated),
                      NULL, NULL,
                      g_cclosure_marshal_VOID__POINTER,
                      G_TYPE_NONE, 1, G_TYPE_POINTER);

    signals[MEDIA_CURSOR_CHANGED] = 
        g_signal_new ("media-cursor-changed",
                      PAROLE_TYPE_MEDIA_LIST,
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET (ParoleMediaListClass, media_cursor_changed),
                      NULL, NULL,
                      g_cclosure_marshal_VOID__BOOLEAN,
                      G_TYPE_NONE, 1, G_TYPE_BOOLEAN);

    signals[URI_OPENED] = 
        g_signal_new ("uri-opened",
                      PAROLE_TYPE_MEDIA_LIST,
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET (ParoleMediaListClass, uri_opened),
                      NULL, NULL,
                      g_cclosure_marshal_VOID__STRING,
                      G_TYPE_NONE, 1, G_TYPE_STRING);

    g_type_class_add_private (klass, sizeof (ParoleMediaListPrivate));
    
    parole_media_list_dbus_class_init (klass);
}

static void
parole_media_list_setup_view (ParoleMediaList *list)
{
    GtkTreeSelection *sel;
    GtkListStore *list_store;
    GtkTreeViewColumn *col;
    GtkCellRenderer *renderer;

    list_store = gtk_list_store_new (COL_NUMBERS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_OBJECT);

    gtk_tree_view_set_model (GTK_TREE_VIEW (list->priv->view), GTK_TREE_MODEL(list_store));
    
    gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (list->priv->view), TRUE);
    col = gtk_tree_view_column_new ();

    renderer = gtk_cell_renderer_pixbuf_new ();
    
    gtk_tree_view_column_pack_start(col, renderer, FALSE);
    gtk_tree_view_column_set_attributes(col, renderer, "pixbuf", PIXBUF_COL, NULL);

    renderer = gtk_cell_renderer_text_new();
    
    gtk_tree_view_column_pack_start (col, renderer, TRUE);
    gtk_tree_view_column_set_attributes (col, renderer, "text", NAME_COL, NULL);
    g_object_set (renderer, 
              "ellipsize", PANGO_ELLIPSIZE_END, 
              NULL);
    
    gtk_tree_view_append_column (GTK_TREE_VIEW (list->priv->view), col);
    gtk_tree_view_column_set_title (col, _("Media list"));

    gtk_drag_dest_set (list->priv->view, GTK_DEST_DEFAULT_ALL, target_entry, G_N_ELEMENTS (target_entry),
                       GDK_ACTION_COPY | GDK_ACTION_MOVE);
    
    list->priv->sel = sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list->priv->view));
    gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE);
    
    g_signal_connect (sel, "changed",
                  G_CALLBACK (parole_media_list_selection_changed_cb), list);
    
    list->priv->store = list_store;
}

static void
parole_media_list_init (ParoleMediaList *list)
{
    GtkBuilder *builder;
    GtkWidget  *box;
    
    list->priv = PAROLE_MEDIA_LIST_GET_PRIVATE (list);
    
    list->priv->bus = parole_g_session_bus_get ();
    
    builder = parole_builder_new_from_string (playlist_ui, playlist_ui_length);
    
    list->priv->view = GTK_WIDGET (gtk_builder_get_object (builder, "media-list"));
    
    box = GTK_WIDGET (gtk_builder_get_object (builder, "playlist-box"));
    
    parole_media_list_setup_view (list);
    
    gtk_builder_connect_signals (builder, list);

    gtk_box_pack_start (GTK_BOX (list), box, TRUE, TRUE, 0);

    list->priv->up = GTK_WIDGET (gtk_builder_get_object (builder, "media-up"));
    list->priv->down = GTK_WIDGET (gtk_builder_get_object (builder, "media-down"));
    list->priv->remove = GTK_WIDGET (gtk_builder_get_object (builder, "remove-media"));
    list->priv->save = GTK_WIDGET (gtk_builder_get_object (builder, "save-playlist"));

    g_object_unref (builder);
    
    gtk_widget_show_all (GTK_WIDGET (list));
    
    parole_media_list_dbus_init (list);
}

GtkWidget *
parole_media_list_get (void)
{
    static gpointer list = NULL;
    
    if ( G_LIKELY (list != NULL ) )
    {
      g_object_ref (list);
    }
    else
    {
      list = g_object_new (PAROLE_TYPE_MEDIA_LIST, NULL);
      g_object_add_weak_pointer (list, &list);
    }
    
    return GTK_WIDGET (list);
}

void parole_media_list_load (ParoleMediaList *list)
{
    gboolean    load_saved_list;
    GSList     *fileslist = NULL;
    
    load_saved_list = parole_rc_read_entry_bool ("SAVE_LIST_ON_EXIT", PAROLE_RC_GROUP_GENERAL, FALSE);
    
    if ( load_saved_list )
    {
      gchar *playlist_file;
      
      playlist_file = xfce_resource_save_location (XFCE_RESOURCE_DATA, 
                                           PAROLE_AUTO_SAVED_PLAYLIST, 
                                         FALSE);
      if ( playlist_file )
      {
          fileslist = parole_pl_parser_parse_from_file_by_extension (playlist_file);
          g_free (playlist_file);
          
          parole_media_list_files_opened_cb (NULL, FALSE, FALSE, fileslist, list);
          g_slist_free (fileslist);
      }
    }
    
}

gboolean
parole_media_list_add_by_path (ParoleMediaList *list, const gchar *path, gboolean emit)
{
    GSList *files_list = NULL;
    GtkFileFilter *filter;
    guint len;
    gboolean ret = FALSE;
    
    filter = parole_get_supported_media_filter ();
    g_object_ref_sink (filter);
    
    TRACE ("Path=%s", path);
    
    parole_get_media_files (filter, path, TRUE, &files_list);
    
    parole_media_list_files_open (list, files_list, FALSE, emit);
    
    len = g_slist_length (files_list);
    ret = len == 0 ? FALSE : TRUE;
    
    g_object_unref (filter);
    g_slist_free (files_list);
    return ret;
}

GtkTreeRowReference *parole_media_list_get_next_row (ParoleMediaList *list, 
                                         GtkTreeRowReference *row,
                                         gboolean repeat)
{
    GtkTreeRowReference *next = NULL;
    GtkTreePath *path;
    GtkTreeIter iter;
    
    g_return_val_if_fail (row != NULL, NULL);

    if ( !gtk_tree_row_reference_valid (row) )
      return NULL;
    
    path = gtk_tree_row_reference_get_path (row);
    
    gtk_tree_path_next (path);
    
    if ( gtk_tree_model_get_iter (GTK_TREE_MODEL (list->priv->store), &iter, path))
    {
      next = gtk_tree_row_reference_new (GTK_TREE_MODEL (list->priv->store), path);
      parole_media_list_select_path (list, path);
    }
    else if ( repeat ) /* Repeat playing ?*/
    {
      if ( gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list->priv->store), &iter))
      {
          next =  parole_media_list_get_row_reference_from_iter (list, &iter, TRUE);
      }
    }
    
    gtk_tree_path_free (path);
    
    return next;
}

GtkTreeRowReference *parole_media_list_get_prev_row (ParoleMediaList *list,
                                         GtkTreeRowReference *row)
{
    GtkTreeRowReference *prev = NULL;
    GtkTreePath *path;
    GtkTreeIter iter;
    
    g_return_val_if_fail (row != NULL, NULL);

    if ( !gtk_tree_row_reference_valid (row) )
      return NULL;
    
    path = gtk_tree_row_reference_get_path (row);
    
    gtk_tree_path_prev (path);
    
    if ( gtk_tree_model_get_iter (GTK_TREE_MODEL (list->priv->store), &iter, path))
    {
      prev = gtk_tree_row_reference_new (GTK_TREE_MODEL (list->priv->store), path);
      parole_media_list_select_path (list, path);
    }
    else
      prev = row;
    
    gtk_tree_path_free (path);
    
    return prev;
}

GtkTreeRowReference *parole_media_list_get_row_random (ParoleMediaList *list)
{
    GtkTreeRowReference *row = NULL;
    GtkTreeIter iter;
    GtkTreePath *path;
    gchar *path_str;
    gint nch;

    nch = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (list->priv->store), NULL);
    
    if ( nch == 1 || nch == 0 )
    {
      return  NULL;
    }
    
    path_str = g_strdup_printf ("%i", g_random_int_range (0, nch));
    
    path = gtk_tree_path_new_from_string (path_str);
    g_free (path_str);
    
    if ( gtk_tree_model_get_iter (GTK_TREE_MODEL (list->priv->store), &iter, path))
    {
      row  = gtk_tree_row_reference_new (GTK_TREE_MODEL (list->priv->store), path);
      parole_media_list_select_path (list, path);
    }
    
    gtk_tree_path_free (path);
    
    return row;
}

gboolean parole_media_list_is_selected_row  (ParoleMediaList *list)
{
    return gtk_tree_selection_count_selected_rows (list->priv->sel) > 0;
}

gboolean parole_media_list_is_empty (ParoleMediaList *list)
{
    GtkTreeIter iter;
    
    return !gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list->priv->store), &iter);
}

GtkTreeRowReference *parole_media_list_get_first_row (ParoleMediaList *list)
{
    GtkTreeRowReference *row = NULL;
    GtkTreeIter iter;
    
    if ( gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list->priv->store), &iter) )
    {
      row = parole_media_list_get_row_reference_from_iter (list, &iter, TRUE);
    }
    
    return row;
}

GtkTreeRowReference *parole_media_list_get_selected_row (ParoleMediaList *list)
{
    return parole_media_list_get_first_selected_row (list);
}

void parole_media_list_set_row_pixbuf  (ParoleMediaList *list, GtkTreeRowReference *row, GdkPixbuf *pix)
{
    GtkTreeIter iter;
    GtkTreePath *path;
    
    if ( gtk_tree_row_reference_valid (row) )
    {
      path = gtk_tree_row_reference_get_path (row);
      
      if ( gtk_tree_model_get_iter (GTK_TREE_MODEL (list->priv->store), &iter, path) )
      {
          gtk_list_store_set (list->priv->store, &iter, PIXBUF_COL, pix, -1);
      }
      gtk_tree_path_free (path);
    }
}

void parole_media_list_set_row_name (ParoleMediaList *list, GtkTreeRowReference *row, const gchar *name)
{
    GtkTreeIter iter;
    GtkTreePath *path;
    
    if ( gtk_tree_row_reference_valid (row) )
    {
      path = gtk_tree_row_reference_get_path (row);
      
      if ( gtk_tree_model_get_iter (GTK_TREE_MODEL (list->priv->store), &iter, path) )
      {
          gtk_list_store_set (list->priv->store, &iter, NAME_COL, name, -1);
      }
      gtk_tree_path_free (path);
    }
}

void parole_media_list_open (ParoleMediaList *list)
{
    parole_media_list_open_internal (list);
}

void parole_media_list_open_location (ParoleMediaList *list)
{
    parole_media_list_open_location_internal (list);
}

gboolean parole_media_list_add_files (ParoleMediaList *list, gchar **filenames)
{
    guint i;
    guint added = 0;
    
    for ( i = 0; filenames && filenames[i] != NULL; i++)
    {
      /**
       * File on disk
       **/
      if ( g_file_test (filenames[i], G_FILE_TEST_EXISTS ) )
      {
          added += parole_media_list_add_by_path (list, filenames[i], i == 0 ? TRUE : FALSE);
      }
      else
      {
          ParoleFile *file;
          TRACE ("File=%s", filenames[i]);
          file = parole_file_new (filenames[i]);
          parole_media_list_add (list, file, i == 0 ? TRUE : FALSE, i == 0 ? TRUE : FALSE);
          added++;
      }
    }
    
    return added == i;
}

void parole_media_list_save_list (ParoleMediaList *list)
{
    gboolean save;
    
    save = parole_rc_read_entry_bool ("SAVE_LIST_ON_EXIT", PAROLE_RC_GROUP_GENERAL, FALSE);
    
    if ( save )
    {
      GSList *fileslist;
      gchar *history;

      history = xfce_resource_save_location (XFCE_RESOURCE_DATA, PAROLE_AUTO_SAVED_PLAYLIST , TRUE);
      
      if ( !history )
      {
          g_warning ("Failed to save playlist");
          return;
      }
      
      fileslist = parole_media_list_get_files (list);
      if ( g_slist_length (fileslist) > 0 )
      {
          parole_pl_parser_save_from_files (fileslist, history, PAROLE_PL_FORMAT_M3U);
          g_slist_foreach (fileslist, (GFunc) g_object_unref, NULL);
      }
      g_slist_free (fileslist);
    }
}

static gboolean    parole_media_list_dbus_add_files (ParoleMediaList *list,
                                         gchar **in_files,
                                       GError **error);

#include "org.parole.media.list.h"

/*
 * DBus server implementation
 */
static void 
parole_media_list_dbus_class_init (ParoleMediaListClass *klass)
{
    dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass),
                             &dbus_glib_parole_media_list_object_info);
}

static void
parole_media_list_dbus_init (ParoleMediaList *list)
{
    dbus_g_connection_register_g_object (list->priv->bus,
                               PAROLE_DBUS_PLAYLIST_PATH,
                               G_OBJECT (list));
}

static gboolean    parole_media_list_dbus_add_files (ParoleMediaList *list,
                                       gchar **in_files,
                                       GError **error)
{
    TRACE ("Adding files for DBus request");
    gtk_window_present (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (list))));
    parole_media_list_add_files (list, in_files);
    
    return TRUE;
}

void parole_media_list_grab_focus (ParoleMediaList *list)
{
    if (GTK_WIDGET_VISIBLE (list->priv->view) )
      gtk_widget_grab_focus (list->priv->view);
}

Generated by  Doxygen 1.6.0   Back to index