diff --git a/src/parole-medialist.c b/src/parole-medialist.c index 9ad213f..41053d8 100644 --- a/src/parole-medialist.c +++ b/src/parole-medialist.c @@ -304,6 +304,8 @@ parole_media_list_add(ParoleMediaList *list, ParoleFile *file, gboolean disc, gb DATA_COL, file, LENGTH_COL, parole_taglibc_get_media_length(file), STATE_COL, PAROLE_MEDIA_STATE_NONE, + PREV_COL, NULL, + NEXT_COL, NULL, -1); if ( emit || select_row ) { @@ -935,6 +937,8 @@ parole_media_list_remove_clicked_cb(GtkButton *button, ParoleMediaList *list) { row = g_list_nth_data(row_list, i); path = gtk_tree_row_reference_get_path(row); + parole_media_list_remove_history(list, row); + if (G_LIKELY(gtk_tree_model_get_iter(model, &iter, path) == TRUE)) { gtk_list_store_remove(GTK_LIST_STORE(model), &iter); @@ -1406,8 +1410,8 @@ parole_media_list_setup_view(ParoleMediaList *list) { GtkListStore *list_store, *disc_list_store; GtkCellRenderer *renderer, *disc_renderer; - list_store = gtk_list_store_new(COL_NUMBERS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_OBJECT); - disc_list_store = gtk_list_store_new(COL_NUMBERS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_OBJECT); + list_store = gtk_list_store_new(COL_NUMBERS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_OBJECT, G_TYPE_POINTER, G_TYPE_POINTER); + disc_list_store = gtk_list_store_new(COL_NUMBERS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_OBJECT, G_TYPE_POINTER, G_TYPE_POINTER); gtk_tree_view_set_model(GTK_TREE_VIEW(list->priv->view), GTK_TREE_MODEL(list_store)); gtk_tree_view_set_model(GTK_TREE_VIEW(list->priv->disc_view), GTK_TREE_MODEL(disc_list_store)); @@ -2064,3 +2068,163 @@ void parole_media_list_connect_shuffle_action(ParoleMediaList *list, GSimpleActi /* Connect changed event to modify action */ g_signal_connect(G_OBJECT(list->priv->shuffle_button), "clicked", G_CALLBACK(shuffle_toggled), simple); } + +/* + * Link two tracks together in the history chain + */ +void parole_media_list_link_history(ParoleMediaList *list, GtkTreeRowReference *prev_row, GtkTreeRowReference *next_row) { + GtkTreeIter iter; + GtkTreePath *prev_path; + GtkTreePath *next_path; + + if ( gtk_tree_row_reference_valid(prev_row) && gtk_tree_row_reference_valid(next_row) ) { + prev_path = gtk_tree_row_reference_get_path(prev_row); + next_path = gtk_tree_row_reference_get_path(next_row); + + if ( ! gtk_tree_path_compare(prev_path, next_path) ) { + /* Make sure we're not forming an infinite history loop */ + TRACE("Trying to link row to itself in history"); + gtk_tree_path_free(prev_path); + gtk_tree_path_free(next_path); + return; + } + + if ( gtk_tree_model_get_iter(GTK_TREE_MODEL(list->priv->store), &iter, prev_path) ) { + gtk_list_store_set(list->priv->store, &iter, + NEXT_COL, gtk_tree_row_reference_copy(next_row), + -1); + } + + if ( gtk_tree_model_get_iter(GTK_TREE_MODEL(list->priv->store), &iter, next_path)) { + gtk_list_store_set(list->priv->store, &iter, + PREV_COL, gtk_tree_row_reference_copy(prev_row), + -1); + } + + gtk_tree_path_free(prev_path); + gtk_tree_path_free(next_path); + } +} + +/* Isolate a given row from its place in the history chain and link together its neighbors */ +void parole_media_list_remove_history(ParoleMediaList *list, GtkTreeRowReference *row) { + GtkTreeRowReference *prev = NULL; + GtkTreeRowReference *next = NULL; + 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_tree_model_get(GTK_TREE_MODEL(list->priv->store), &iter, PREV_COL, &prev, -1); + gtk_tree_model_get(GTK_TREE_MODEL(list->priv->store), &iter, NEXT_COL, &next, -1); + + parole_media_list_link_history(list, prev, next); + + gtk_list_store_set(list->priv->store, &iter, + NEXT_COL, NULL, + PREV_COL, NULL, + -1); + } + + gtk_tree_row_reference_free(prev); + gtk_tree_row_reference_free(next); + gtk_tree_path_free(path); + } +} + +GtkTreeRowReference *parole_media_list_get_next_row_by_history(ParoleMediaList *list, GtkTreeRowReference *row) { + GtkTreeRowReference *next = NULL; + GtkTreePath *path; + GtkTreeIter iter; + + if ( !gtk_tree_row_reference_valid (row) ) + return NULL; + + path = gtk_tree_row_reference_get_path(row); + + if ( gtk_tree_model_get_iter(GTK_TREE_MODEL(list->priv->store), &iter, path) ) + gtk_tree_model_get(GTK_TREE_MODEL(list->priv->store), &iter, NEXT_COL, &next, -1); + + gtk_tree_path_free(path); + + return next; +} + +GtkTreeRowReference *parole_media_list_get_prev_row_by_history(ParoleMediaList *list, GtkTreeRowReference *row) { + GtkTreeRowReference *next = NULL; + GtkTreePath *path; + GtkTreeIter iter; + + if ( !gtk_tree_row_reference_valid(row) ) + return NULL; + + path = gtk_tree_row_reference_get_path(row); + + if ( gtk_tree_model_get_iter(GTK_TREE_MODEL(list->priv->store), &iter, path) ) + gtk_tree_model_get(GTK_TREE_MODEL(list->priv->store), &iter, PREV_COL, &next, -1); + + gtk_tree_path_free(path); + + return next; +} + +void pml_print_history_links(ParoleMediaList *list) { + //// This is just just for testing, and will be removed + GtkTreeRowReference *row; + GtkTreePath *path; + GtkTreePath *path_temp; + GtkTreeIter iter; + gchar *name; + + if (gtk_notebook_get_current_page(GTK_NOTEBOOK(list->priv->playlist_notebook)) != 0) { + return; + } + + g_print("\n"); + + for (gint i=0; i < parole_media_list_get_playlist_count(list); i+=1) { + path = gtk_tree_path_new_from_indices(i, -1); + + if ( gtk_tree_model_get_iter(GTK_TREE_MODEL(list->priv->store), &iter, path) ) + gtk_tree_model_get(GTK_TREE_MODEL(list->priv->store), &iter, PREV_COL, &row, -1); + + if ( gtk_tree_row_reference_valid(row) ) { + path_temp = gtk_tree_row_reference_get_path(row); + name = gtk_tree_path_to_string(path_temp); + } else { + name = "*"; + } + + g_print(":: %s : ",name); + + if ( gtk_tree_row_reference_valid(row) ) { + gtk_tree_path_free(path_temp); + g_free(name); + } + + name = gtk_tree_path_to_string(path); + g_print("%s : ",name); + g_free(name); + + if ( gtk_tree_model_get_iter(GTK_TREE_MODEL(list->priv->store), &iter, path) ) + gtk_tree_model_get(GTK_TREE_MODEL(list->priv->store), &iter, NEXT_COL, &row, -1); + + if ( gtk_tree_row_reference_valid(row) ) { + path_temp = gtk_tree_row_reference_get_path(row); + name = gtk_tree_path_to_string(path_temp); + } else { + name = "*"; + } + + g_print("%s ::\n",name); + + if ( gtk_tree_row_reference_valid(row) ) { + gtk_tree_path_free(path_temp); + g_free(name); + } + + gtk_tree_path_free(path); +} +} \ No newline at end of file diff --git a/src/parole-medialist.h b/src/parole-medialist.h index cf06ff5..361bd29 100644 --- a/src/parole-medialist.h +++ b/src/parole-medialist.h @@ -38,6 +38,8 @@ enum { NAME_COL, LENGTH_COL, DATA_COL, + PREV_COL, + NEXT_COL, COL_NUMBERS }; @@ -192,6 +194,22 @@ parole_media_list_connect_shuffle_action (ParoleMediaList *list, void parole_media_list_add_dvd (ParoleMediaList *list, gchar *dvd_name); +void parole_media_list_link_history (ParoleMediaList *list, + GtkTreeRowReference *prev_row, + GtkTreeRowReference *next_row); + +void parole_media_list_remove_history (ParoleMediaList *list, + GtkTreeRowReference *row); + +GtkTreeRowReference +*parole_media_list_get_next_row_by_history (ParoleMediaList *list, + GtkTreeRowReference *row); + +GtkTreeRowReference +*parole_media_list_get_prev_row_by_history (ParoleMediaList *list, + GtkTreeRowReference *row); + +void pml_print_history_links (ParoleMediaList *list); // Test stuff to remove G_END_DECLS diff --git a/src/parole-player.c b/src/parole-player.c index 3181ed7..ba641f1 100644 --- a/src/parole-player.c +++ b/src/parole-player.c @@ -1044,11 +1044,13 @@ parole_player_select_custom_subtitle(GtkMenuItem *widget, gpointer data) { } static void -parole_player_media_activated_cb(ParoleMediaList *list, GtkTreeRowReference *row, ParolePlayer *player) { +parole_player_play_media(ParoleMediaList *list, GtkTreeRowReference *row, ParolePlayer *player) { ParoleFile *file; GtkTreeIter iter; GtkTreeModel *model; + pml_print_history_links(player->priv->list); // For testing, will be removed + model = gtk_tree_row_reference_get_model(row); if (gtk_tree_model_get_iter(model, &iter, gtk_tree_row_reference_get_path(row))) { @@ -1104,6 +1106,25 @@ parole_player_media_activated_cb(ParoleMediaList *list, GtkTreeRowReference *row } } +static void +parole_player_media_activated_cb(ParoleMediaList *list, GtkTreeRowReference *row, ParolePlayer *player) { + if ( gtk_tree_row_reference_valid(row) && gtk_tree_row_reference_valid(player->priv->row) ) { + GtkTreePath *prev_path = gtk_tree_row_reference_get_path(row); + GtkTreePath *next_path = gtk_tree_row_reference_get_path(player->priv->row); + + if ( gtk_tree_path_compare(prev_path, next_path) ) { + /* Move the chosen row to the front of the history chain before playing it */ + parole_media_list_remove_history(player->priv->list, row); + parole_media_list_link_history(player->priv->list, player->priv->row, row); + } + + gtk_tree_path_free(prev_path); + gtk_tree_path_free(next_path); + } + + parole_player_play_media(list, row, player); +} + static void parole_player_disc_selected_cb(ParoleDisc *disc, const gchar *uri, const gchar *device, ParolePlayer *player) { parole_player_reset(player); @@ -1469,7 +1490,7 @@ parole_player_play_selected_row(ParolePlayer *player) { row = parole_media_list_get_first_row(player->priv->list); if ( row ) - parole_player_media_activated_cb(player->priv->list, row, player); + parole_player_play_media(player->priv->list, row, player); } static void @@ -1490,13 +1511,22 @@ parole_player_play_next(ParolePlayer *player, gboolean allow_shuffle) { if ( player->priv->row ) { parole_media_list_set_row_playback_state(player->priv->list, player->priv->row, PAROLE_MEDIA_STATE_NONE); - if ( shuffle && allow_shuffle ) - row = parole_media_list_get_row_random(player->priv->list); - else + if ( shuffle && allow_shuffle ) { + /* Before actually shuffling, see if there is history to traverse forward through */ + row = parole_media_list_get_next_row_by_history(player->priv->list, player->priv->row); + if ( !row ) { + row = parole_media_list_get_row_random(player->priv->list); + /* Break any history links to prevent forming loops */ + parole_media_list_remove_history(player->priv->list, row); + } + } else { row = parole_media_list_get_next_row(player->priv->list, player->priv->row, repeat); + parole_media_list_remove_history(player->priv->list, row); + } if ( row ) { - parole_player_media_activated_cb(player->priv->list, row, player); + parole_media_list_link_history(player->priv->list, player->priv->row, row); + parole_player_play_media(player->priv->list, row, player); return; } else { TRACE("No remaining media in the list"); @@ -1510,20 +1540,34 @@ parole_player_play_next(ParolePlayer *player, gboolean allow_shuffle) { static void parole_player_play_prev(ParolePlayer *player) { - GtkTreeRowReference *row; + gboolean shuffle; + GtkTreeRowReference *row = NULL; if ( player->priv->current_media_type == PAROLE_MEDIA_TYPE_DVD ) { parole_gst_prev_dvd_chapter(PAROLE_GST(player->priv->gst)); return; } + g_object_get(G_OBJECT(player->priv->conf), + "shuffle", &shuffle, + NULL); + if ( player->priv->row ) { parole_media_list_set_row_playback_state(player->priv->list, player->priv->row, PAROLE_MEDIA_STATE_NONE); - row = parole_media_list_get_prev_row(player->priv->list, player->priv->row); + if ( shuffle ) { + row = parole_media_list_get_prev_row_by_history(player->priv->list, player->priv->row); + } else { + row = parole_media_list_get_prev_row(player->priv->list, player->priv->row); + /* If shuffle is off, pressing Previous Track will be treated in history as navigating to a new track, + * rather than traversing backwards through history. + */ + parole_media_list_remove_history(player->priv->list, row); + parole_media_list_link_history(player->priv->list, player->priv->row, row); + } if ( row ) { - parole_player_media_activated_cb(player->priv->list, row, player); + parole_player_play_media(player->priv->list, row, player); return; } else { TRACE("No remaining media in the list");