Add new gmpc-data-view (DOES NOT WORK COMPLETELY)
[gmpc.git] / src / browsers / playlist3-current-playlist-browser.c
blob8cdc91c980d76666b643aae882633f63587ecb6d
1 /* Gnome Music Player Client (GMPC)
2 * Copyright (C) 2004-2012 Qball Cow <qball@gmpclient.org>
3 * Project homepage: http://gmpclient.org/
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include <gtk/gtk.h>
21 #include <gdk/gdkkeysyms.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <config.h>
26 #include "main.h"
27 #include "misc.h"
28 #include "playlist3.h"
29 #include "playlist3-current-playlist-browser.h"
30 #include "playlist3-find2-browser.h"
31 #include "gmpc-mpddata-model.h"
32 #include "gmpc-mpddata-model-playlist.h"
33 #include "gmpc-mpddata-treeview.h"
34 #include "advanced-search.h"
35 #include "playlist3-messages.h"
37 #include "playlist3-playlist-editor.h"
39 static void pl3_current_playlist_browser_priority_raise_selected_songs(PlayQueuePlugin * self);
40 static void pl3_current_playlist_browser_priority_remove_selected_songs(PlayQueuePlugin * self);
44 enum
46 PLAY_QUEUE_DUMMY_PROPERTY,
47 PLAY_QUEUE_UID
49 /**
50 * Private data structure
52 typedef struct _PlayQueuePluginPrivate
54 gulong status_changed_handler;
55 gulong connection_changed_handler;
57 /* Quick Search */
58 gboolean search_keep_open;
59 GtkWidget *filter_entry;
60 GtkTreeModel *mod_fill;
61 gboolean quick_search;
62 guint quick_search_timeout;
63 /* Other widgets */
64 GtkWidget *pl3_cp_tree;
65 GtkWidget *pl3_cp_vbox;
66 /* reference to the row */
67 GtkTreeRowReference *pl3_curb_tree_ref;
68 /* Gchar *uid */
69 gchar *uid;
70 } _PlayQueuePluginPrivate;
72 /**
73 * Propperty setter/getter
75 static void play_queue_get_property(GObject * object, guint property_id, GValue * value, GParamSpec * pspec)
77 PlayQueuePlugin *self;
78 self = (PlayQueuePlugin *) (object);
79 switch (property_id)
81 case PLAY_QUEUE_UID:
82 g_value_set_string(value, self->priv->uid);
83 break;
84 default:
85 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
86 break;
91 static void play_queue_set_property(GObject * object, guint property_id, const GValue * value, GParamSpec * pspec)
93 PlayQueuePlugin *self;
94 self = (PlayQueuePlugin *) (object);
95 switch (property_id)
97 case PLAY_QUEUE_UID:
98 if (self->priv->uid)
99 g_free(self->priv->uid);
100 self->priv->uid = g_value_dup_string(value);
101 break;
102 default:
103 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
104 break;
108 static void pl3_current_playlist_browser_paste_after_songs(GtkTreeView * tree, GList * paste_list,
109 PlayQueuePlugin * self);
110 static void pl3_current_playlist_browser_paste_before_songs(GtkTreeView * tree, GList * paste_list,
111 PlayQueuePlugin * self);
112 static void pl3_current_playlist_browser_delete_selected_songs(PlayQueuePlugin * self);
114 static void pl3_current_playlist_browser_add(GmpcPluginBrowserIface * obj, GtkWidget * cat_tree);
116 static void pl3_current_playlist_browser_selected(GmpcPluginBrowserIface * obj, GtkContainer * container);
117 static void pl3_current_playlist_browser_unselected(GmpcPluginBrowserIface * obj, GtkContainer * container);
119 static void pl3_current_playlist_browser_activate(PlayQueuePlugin * self);
121 /* just for here */
122 static void pl3_current_playlist_browser_row_activated(GtkTreeView * tree, GtkTreePath * path, GtkTreeViewColumn * col,
123 PlayQueuePlugin * self);
124 static int pl3_current_playlist_browser_button_release_event(GtkTreeView * tree, GdkEventButton * event,
125 PlayQueuePlugin * self);
126 static int pl3_current_playlist_browser_key_press_event(GtkTreeView * tree, GdkEventKey * event,
127 PlayQueuePlugin * self);
128 static void pl3_current_playlist_browser_show_info(PlayQueuePlugin * self);
129 static void pl3_current_playlist_save_playlist(void);
130 static void pl3_current_playlist_browser_shuffle_playlist(void);
131 static void pl3_current_playlist_browser_clear_playlist(void);
133 static void pl3_current_playlist_browser_init(PlayQueuePlugin * self);
135 static void pl3_current_playlist_browser_clear_playlist_real(void)
137 mpd_playlist_clear(connection);
139 static void pl3_cp_current_song_changed(GmpcMpdDataModelPlaylist * model2, GtkTreePath * path, GtkTreeIter * iter,
140 PlayQueuePlugin * self)
142 GtkTreeModel *model;
143 if (self->priv->pl3_cp_tree == NULL)
144 return;
145 model = gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
146 if (GMPC_IS_MPDDATA_MODEL_PLAYLIST(model))
148 if (cfg_get_single_value_as_int_with_default(config, "playlist", "st_cur_song", 0))
150 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(self->priv->pl3_cp_tree), path, NULL, TRUE, 0.5, 0);
155 static void pl3_current_playlist_browser_crop_current_song(PlayQueuePlugin * self, const gchar * param)
157 mpd_Song *song = mpd_playlist_get_current_song(connection);
158 if (song)
160 int length = mpd_playlist_get_playlist_length(connection);
161 /* Move to first place, this avoid possible "issues" */
162 mpd_playlist_move_id(connection, song->id, 0);
163 for (; length > 1; length--)
165 mpd_playlist_queue_delete_pos(connection, length - 1);
166 if ((length & 16383) == 16383)
168 mpd_playlist_queue_commit(connection);
171 mpd_playlist_queue_commit(connection);
175 static void pl3_cp_ec_playlist(PlayQueuePlugin * self, const gchar * param)
177 pl3_find2_select_plugin_id(GMPC_PLUGIN_BASE(self)->id);
178 pl3_find2_do_search_any(param);
181 static void pl3_cp_init(PlayQueuePlugin * self)
183 g_signal_connect(G_OBJECT(playlist), "current_song_changed", G_CALLBACK(pl3_cp_current_song_changed), self);
185 gmpc_easy_command_add_entry(gmpc_easy_command,
186 _("switch play queue"), "",
187 _("Switch to play queue"),
188 (GmpcEasyCommandCallback *) pl3_current_playlist_browser_activate, self);
189 gmpc_easy_command_add_entry(gmpc_easy_command,
190 _("Clear"), "",
191 _("Clear play queue"),
192 (GmpcEasyCommandCallback *) pl3_current_playlist_browser_clear_playlist_real, self);
194 gmpc_easy_command_add_entry(gmpc_easy_command,
195 _("Crop current song"), "",
196 _("Crop the playlist so it only contains the current song"),
197 (GmpcEasyCommandCallback *) pl3_current_playlist_browser_crop_current_song, self);
199 gmpc_easy_command_add_entry(gmpc_easy_command,
200 _("search playlist"), ".*",
201 _("Search playlist <query>"),
202 (GmpcEasyCommandCallback *) pl3_cp_ec_playlist, self);
205 void pl3_current_playlist_destroy(PlayQueuePlugin * self);
207 /* internal */
209 static void pl3_current_playlist_column_changed(GtkTreeView * tree)
211 int position = 0;
212 GList *iter, *cols = gtk_tree_view_get_columns(tree);
213 for (iter = cols; iter; iter = g_list_next(iter))
215 gpointer data = g_object_get_data(G_OBJECT(iter->data), "colid");
216 int colid = GPOINTER_TO_INT(data);
217 char *string = g_strdup_printf("%i", position);
218 cfg_set_single_value_as_int(config, "current-playlist-column-pos", string, colid);
219 position++;
220 q_free(string);
222 g_list_free(cols);
225 void pl3_current_playlist_destroy(PlayQueuePlugin * self)
227 /* destroy the entry */
228 if (self->priv->pl3_curb_tree_ref)
230 GtkTreeIter piter;
231 GtkTreePath *path;
232 path = gtk_tree_row_reference_get_path(self->priv->pl3_curb_tree_ref);
233 if (path)
235 if (gtk_tree_model_get_iter
236 (GTK_TREE_MODEL(gtk_tree_row_reference_get_model(self->priv->pl3_curb_tree_ref)), &piter, path))
238 gtk_list_store_remove(GTK_LIST_STORE(gtk_tree_row_reference_get_model(self->priv->pl3_curb_tree_ref)),
239 &piter);
241 gtk_tree_path_free(path);
243 gtk_tree_row_reference_free(self->priv->pl3_curb_tree_ref);
244 self->priv->pl3_curb_tree_ref = NULL;
246 /* destroy the browser */
247 if (self->priv->pl3_cp_vbox)
249 /* remove the signal handler so when the widget is destroyed, the numbering of the labels are not changed again */
250 g_signal_handlers_disconnect_by_func(G_OBJECT(self->priv->pl3_cp_tree),
251 G_CALLBACK(pl3_current_playlist_column_changed), NULL);
252 g_object_unref(self->priv->pl3_cp_vbox);
253 self->priv->pl3_cp_vbox = NULL;
255 self->priv->pl3_cp_tree = NULL;
258 static gboolean mod_fill_do_entry_changed(PlayQueuePlugin * self)
260 const gchar *text2 = gtk_entry_get_text(GTK_ENTRY(self->priv->filter_entry));
262 if (strlen(text2) > 0)
265 MpdData *data = NULL;
266 data = advanced_search(text2, TRUE);
267 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(self->priv->mod_fill), data);
268 self->priv->quick_search = TRUE;
269 gtk_tree_view_set_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree), self->priv->mod_fill);
270 gtk_widget_show(self->priv->filter_entry);
271 } else
273 gtk_tree_view_set_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree), playlist);
275 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(self->priv->mod_fill), NULL);
276 if (!self->priv->search_keep_open)
278 self->priv->quick_search = 0;
279 gtk_widget_hide(self->priv->filter_entry);
280 gtk_widget_grab_focus(self->priv->pl3_cp_tree);
283 self->priv->quick_search_timeout = 0;
284 return FALSE;
287 static gboolean mod_fill_entry_key_press_event(GtkWidget * entry, GdkEventKey * event, PlayQueuePlugin * self)
289 const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry));
291 if (strlen(text) == 0)
293 if (event->keyval == GDK_KEY_BackSpace || event->keyval == GDK_KEY_Escape)
295 self->priv->search_keep_open = FALSE;
296 gtk_tree_view_set_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree), playlist);
298 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(self->priv->mod_fill), NULL);
299 self->priv->quick_search = 0;
300 gtk_widget_hide(entry);
301 gtk_widget_grab_focus(self->priv->pl3_cp_tree);
302 return TRUE;
304 } else if (event->keyval == GDK_KEY_Escape)
306 self->priv->search_keep_open = FALSE;
307 gtk_entry_set_text(GTK_ENTRY(entry), "");
308 } else if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_Down)
310 gtk_widget_grab_focus(self->priv->pl3_cp_tree);
311 return TRUE;
313 return FALSE;
316 static void mod_fill_entry_changed(GtkWidget * entry, PlayQueuePlugin * self)
318 if (self->priv->quick_search_timeout != 0)
320 g_source_remove(self->priv->quick_search_timeout);
321 self->priv->quick_search_timeout = 0;
324 if (cfg_get_single_value_as_int_with_default(config, "general", "search-as-you-type", 0) == 1)
326 self->priv->quick_search_timeout = g_timeout_add(250, (GSourceFunc) mod_fill_do_entry_changed, self);
328 gtk_widget_show(entry);
331 static void mod_fill_entry_activate(GtkWidget * entry, PlayQueuePlugin * self)
333 if (self->priv->quick_search_timeout != 0)
334 g_source_remove(self->priv->quick_search_timeout);
335 mod_fill_do_entry_changed(self);
336 gtk_widget_grab_focus(self->priv->pl3_cp_tree);
339 static void mod_fill_clear_search_entry(GtkEntry * entry, GtkEntryIconPosition icon_pos, GdkEvent * event,
340 gpointer user_data)
342 if (icon_pos == GTK_ENTRY_ICON_SECONDARY)
344 gtk_entry_set_text(GTK_ENTRY(entry), "");
349 static void pl3_current_playlist_browser_init(PlayQueuePlugin * self)
351 GtkWidget *entry = NULL, *tree = NULL, *sw = NULL, *pl3_cp_sw;
352 /* Mark the plugin internal */
353 GMPC_PLUGIN_BASE(self)->plugin_type = GMPC_INTERNALL | GMPC_PLUGIN_PL_BROWSER;
355 self->priv->pl3_cp_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
357 tree = gmpc_data_view_new(self->priv->uid);//gmpc_mpddata_treeview_new(self->priv->uid, FALSE, GTK_TREE_MODEL(playlist));
358 gmpc_data_view_set_is_play_queue(GMPC_DATA_VIEW(tree), TRUE);
359 gtk_tree_view_set_model(GTK_TREE_VIEW(tree), GTK_TREE_MODEL(playlist));
361 gmpc_data_view_populate(GMPC_DATA_VIEW(tree));
363 g_signal_connect(G_OBJECT(tree), "paste_before", G_CALLBACK(pl3_current_playlist_browser_paste_before_songs), self);
364 g_signal_connect(G_OBJECT(tree), "paste_after", G_CALLBACK(pl3_current_playlist_browser_paste_after_songs), self);
365 g_signal_connect_swapped(G_OBJECT(tree), "cut", G_CALLBACK(pl3_current_playlist_browser_delete_selected_songs),
366 self);
369 /* Enable this for this model only */
370 gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(tree), gmt_targetentries, 1, GDK_ACTION_DEFAULT|GDK_ACTION_MOVE|GDK_ACTION_COPY);
372 /* filter */
373 self->priv->mod_fill = (GtkTreeModel *) gmpc_mpddata_model_new();
374 entry = gtk_entry_new();
375 gtk_entry_set_icon_from_stock(GTK_ENTRY(entry), GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
376 g_signal_connect(GTK_ENTRY(entry), "icon-press", G_CALLBACK(mod_fill_clear_search_entry), NULL);
378 gtk_entry_set_icon_from_stock(GTK_ENTRY(entry), GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND);
379 gtk_entry_set_icon_activatable(GTK_ENTRY(entry), GTK_ENTRY_ICON_PRIMARY, FALSE);
382 gtk_box_pack_end(GTK_BOX(self->priv->pl3_cp_vbox), entry, FALSE, TRUE, 0);
383 self->priv->filter_entry = entry;
384 g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(mod_fill_entry_changed), self);
385 g_signal_connect(G_OBJECT(entry), "key-press-event", G_CALLBACK(mod_fill_entry_key_press_event), self);
386 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(mod_fill_entry_activate), self);
388 sw = gtk_scrolled_window_new(NULL, NULL);
389 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
390 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
391 gtk_container_add(GTK_CONTAINER(sw), tree);
392 gtk_box_pack_start(GTK_BOX(self->priv->pl3_cp_vbox), GTK_WIDGET(sw), TRUE, TRUE, 0);
393 gtk_widget_show_all(sw);
394 /* set up the tree */
395 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(tree), FALSE);
397 //gmpc_mpddata_treeview_enable_click_fix(GMPC_MPDDATA_TREEVIEW(tree));
398 /* setup signals */
399 g_signal_connect(G_OBJECT(tree), "row-activated", G_CALLBACK(pl3_current_playlist_browser_row_activated), self);
400 g_signal_connect(G_OBJECT(tree), "button-release-event",
401 G_CALLBACK(pl3_current_playlist_browser_button_release_event), self);
402 g_signal_connect(G_OBJECT(tree), "key-press-event", G_CALLBACK(pl3_current_playlist_browser_key_press_event),
403 self);
405 /* set up the scrolled window */
406 pl3_cp_sw = gtk_scrolled_window_new(NULL, NULL);
407 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(pl3_cp_sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
408 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(pl3_cp_sw), GTK_SHADOW_ETCHED_IN);
410 /* set initial state */
411 self->priv->pl3_cp_tree = tree;
412 g_object_ref_sink(G_OBJECT(self->priv->pl3_cp_vbox));
415 static void pl3_current_playlist_browser_select_current_song(PlayQueuePlugin * self)
417 if (self->priv->pl3_cp_tree == NULL || !gtk_widget_get_realized(self->priv->pl3_cp_tree))
418 return;
419 /* scroll to the playing song */
420 if (mpd_player_get_current_song_pos(connection) >= 0 && mpd_playlist_get_playlist_length(connection) > 0)
422 GtkTreePath *path = gtk_tree_path_new_from_indices(mpd_player_get_current_song_pos(connection), -1);
423 if (path != NULL
424 && GMPC_IS_MPDDATA_MODEL_PLAYLIST(gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree))))
426 gtk_tree_view_set_cursor(GTK_TREE_VIEW(self->priv->pl3_cp_tree), path, NULL, FALSE);
428 gtk_tree_path_free(path);
432 static void pl3_current_playlist_browser_scroll_to_current_song(PlayQueuePlugin * self)
434 if (self->priv->pl3_cp_tree == NULL || !gtk_widget_get_realized(self->priv->pl3_cp_tree))
435 return;
436 /* scroll to the playing song */
437 if (mpd_player_get_current_song_pos(connection) >= 0 && mpd_playlist_get_playlist_length(connection) > 0)
439 GtkTreePath *path = gtk_tree_path_new_from_indices(mpd_player_get_current_song_pos(connection), -1);
440 if (path != NULL)
442 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(self->priv->pl3_cp_tree), path, NULL, TRUE, 0.5, 0);
443 gtk_tree_path_free(path);
449 /* add's the toplevel entry for the current playlist view */
450 static void pl3_current_playlist_browser_add(GmpcPluginBrowserIface * obj, GtkWidget * cat_tree)
452 PlayQueuePlugin *self = (PlayQueuePlugin *) obj;
453 GtkTreeIter iter;
454 GtkTreePath *path;
455 playlist3_insert_browser(&iter, PL3_CAT_BROWSER_TOP+1);
456 gtk_list_store_set(GTK_LIST_STORE(pl3_tree), &iter, PL3_CAT_TYPE, GMPC_PLUGIN_BASE(self)->id /*current_playlist_plug.id */ , /*PL3_CURRENT_PLAYLIST, */
457 PL3_CAT_TITLE, _(gmpc_plugin_base_get_name(GMPC_PLUGIN_BASE(self))),
458 PL3_CAT_ICON_ID, "playlist-browser", -1);
459 if (self->priv->pl3_curb_tree_ref)
461 gtk_tree_row_reference_free(self->priv->pl3_curb_tree_ref);
462 self->priv->pl3_curb_tree_ref = NULL;
464 path = gtk_tree_model_get_path(GTK_TREE_MODEL(pl3_tree), &iter);
465 if (path)
467 self->priv->pl3_curb_tree_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(pl3_tree), path);
468 gtk_tree_path_free(path);
472 /* delete all selected songs,
473 * if no songs select ask the user if he want's to clear the list
475 static void pl3_current_playlist_browser_delete_selected_songs(PlayQueuePlugin * self)
477 /* grab the selection from the tree */
478 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
479 /* check if where connected */
480 /* see if there is a row selected */
481 if (gtk_tree_selection_count_selected_rows(selection) > 0)
483 GList *list = NULL, *llist = NULL;
484 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
485 /* start a command list */
486 /* grab the selected songs */
487 list = gtk_tree_selection_get_selected_rows(selection, &model);
488 /* grab the last song that is selected */
489 llist = g_list_last(list);
490 /* remove every selected song one by one */
493 GtkTreeIter iter;
494 int value;
495 gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) llist->data);
496 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_SONG_ID, &value, -1);
497 mpd_playlist_queue_delete_id(connection, value);
498 } while ((llist = g_list_previous(llist)));
500 /* close the list, so it will be executed */
501 mpd_playlist_queue_commit(connection);
502 /* unselect all if multiple songs were selected */
503 if (g_list_length(list) > 1)
504 gtk_tree_selection_unselect_all(selection);
505 /* free list */
506 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
507 g_list_free(list);
508 } else
510 pl3_current_playlist_browser_clear_playlist();
513 /* update everything if where still connected */
514 mpd_status_queue_update(connection);
517 static void pl3_current_playlist_browser_crop_selected_songs(PlayQueuePlugin * self)
519 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
520 /* grab the selection from the tree */
521 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
523 /* see if there is a row selected */
524 if (gtk_tree_selection_count_selected_rows(selection) > 0)
526 GtkTreeIter iter;
527 /* we want to delete from back to front, so we have to transverse this list */
528 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter))
533 if (!gtk_tree_selection_iter_is_selected(selection, &iter))
535 int id = 0;
536 /* song pos starts at 1, not a 0, compensate for that */
537 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, MPDDATA_MODEL_COL_SONG_ID, &id, -1);
538 mpd_playlist_queue_delete_id(connection, id);
540 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter));
542 mpd_playlist_queue_commit(connection);
543 /* update everything if where still connected */
544 gtk_tree_selection_unselect_all(selection);
546 mpd_status_queue_update(connection);
550 static void pl3_current_playlist_editor_add_to_playlist(GtkWidget * menu, gpointer cb_data)
552 PlayQueuePlugin *self = (PlayQueuePlugin *) cb_data;
553 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
554 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
555 gchar *data = g_object_get_data(G_OBJECT(menu), "playlist");
556 GList *iter, *list = gtk_tree_selection_get_selected_rows(selection, &model);
557 if (list)
559 iter = g_list_first(list);
562 GtkTreeIter giter;
563 if (gtk_tree_model_get_iter(model, &giter, (GtkTreePath *) iter->data))
565 gchar *file = NULL;
566 gtk_tree_model_get(model, &giter, MPDDATA_MODEL_COL_PATH, &file, -1);
567 mpd_database_playlist_list_add(connection, data, file);
568 g_free(file);
570 } while ((iter = g_list_next(iter)));
572 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
573 g_list_free(list);
576 playlist_editor_fill_list();
579 static void pl3_current_playlist_browser_paste_after_songs(GtkTreeView * tree, GList * paste_list,
580 PlayQueuePlugin * self)
582 /* grab the selection from the tree */
583 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
585 int seen = 0;
586 /* check if where connected */
587 /* see if there is a row selected */
588 if (gtk_tree_selection_count_selected_rows(selection) > 0)
590 GList *list = NULL, *llist = NULL;
591 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
592 /* start a command list */
593 /* grab the selected songs */
594 list = gtk_tree_selection_get_selected_rows(selection, &model);
595 /* grab the last song that is selected */
596 llist = g_list_last(list);
597 /* remove every selected song one by one */
598 if (llist)
600 GtkTreeIter iter;
601 gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) llist->data);
602 /* Trick that avoids roundtrip to mpd */
604 int id;
605 char *path = NULL;
606 int length = mpd_playlist_get_playlist_length(connection);
607 GList *liter = g_list_first(paste_list);
608 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_SONG_POS, &id, -1);
609 while (liter)
611 int song_id;
612 path = liter->data;
613 song_id = mpd_playlist_add_get_id(connection, path);
614 if (song_id == -1 && !seen)
616 playlist3_show_error_message(_("Your mpd has a broken 'addid', pasting will fail."),
617 ERROR_WARNING);
618 seen = 1;
620 mpd_playlist_move_pos(connection, length, id);
621 length++;
622 liter = g_list_next(liter);
626 /* free list */
627 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
628 g_list_free(list);
629 } else
631 GList *liter = g_list_first(paste_list);;
632 while (liter)
634 char *path = liter->data;
635 int song_id = mpd_playlist_add_get_id(connection, path);
636 if (song_id == -1 && !seen)
638 playlist3_show_error_message(_("Your mpd has a broken 'addid', pasting will fail."), ERROR_WARNING);
639 seen = 1;
641 liter = g_list_next(liter);
645 gtk_tree_selection_unselect_all(selection);
648 static void pl3_current_playlist_browser_paste_before_songs(GtkTreeView * tree, GList * paste_list,
649 PlayQueuePlugin * self)
651 /* grab the selection from the tree */
652 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
654 int seen = 0;
655 /* check if where connected */
656 /* see if there is a row selected */
657 if (gtk_tree_selection_count_selected_rows(selection) > 0)
659 GList *list = NULL, *llist = NULL;
660 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
661 /* start a command list */
662 /* grab the selected songs */
663 list = gtk_tree_selection_get_selected_rows(selection, &model);
664 /* grab the last song that is selected */
665 llist = g_list_first(list);
666 /* remove every selected song one by one */
667 if (llist)
669 GtkTreeIter iter;
670 gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) llist->data);
671 /* Trick that avoids roundtrip to mpd */
673 int id;
674 int length = mpd_playlist_get_playlist_length(connection);
675 GList *liter = g_list_first(paste_list);
676 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_SONG_POS, &id, -1);
677 while (liter)
679 char *path = liter->data;
680 int song_id = mpd_playlist_add_get_id(connection, path);
681 if (song_id == -1 && !seen)
683 playlist3_show_error_message(_("Your mpd has a broken 'addid', pasting will fail."),
684 ERROR_WARNING);
685 seen = 1;
687 mpd_playlist_move_pos(connection, length, id - 1);
688 /* The song is now one lower */
689 /* length one longer */
690 length++;
691 liter = g_list_next(liter);
695 /* free list */
696 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
697 g_list_free(list);
698 } else
700 GList *liter = g_list_first(paste_list);
701 while (liter)
703 char *path = liter->data;
704 int song_id = mpd_playlist_add_get_id(connection, path);
705 if (song_id == -1 && !seen)
707 playlist3_show_error_message(_("Your mpd has a broken 'addid', pasting will fail."), ERROR_WARNING);
708 seen = 1;
710 liter = g_list_next(liter);
715 gtk_tree_selection_unselect_all(selection);
718 static int pl3_current_playlist_browser_button_release_event(GtkTreeView * tree, GdkEventButton * event,
719 PlayQueuePlugin * self)
721 if (event->button == 3)
723 /* del, crop */
724 GtkWidget *item;
725 GtkWidget *menu = gtk_menu_new();
727 int rows = gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(tree));
728 /* add the delete widget */
729 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_REMOVE, NULL);
730 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
731 g_signal_connect_swapped(G_OBJECT(item), "activate",
732 G_CALLBACK(pl3_current_playlist_browser_delete_selected_songs), self);
734 if (rows)
736 /* add the delete widget */
737 item = gtk_image_menu_item_new_with_label(_("Crop"));
738 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
739 gtk_image_new_from_stock(GTK_STOCK_CUT, GTK_ICON_SIZE_MENU));
740 g_signal_connect_swapped(G_OBJECT(item), "activate",
741 G_CALLBACK(pl3_current_playlist_browser_crop_selected_songs), self);
742 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
744 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
745 /* add the clear widget */
746 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLEAR, NULL);
747 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
748 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_browser_clear_playlist), NULL);
750 /* add the shuffle widget */
751 item = gtk_image_menu_item_new_with_label(_("Shuffle"));
752 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
753 gtk_image_new_from_stock(GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU));
754 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_browser_shuffle_playlist), NULL);
755 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
757 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
759 if(mpd_server_check_command_allowed(connection, "prioid") == MPD_SERVER_COMMAND_ALLOWED)
761 /* priority */
762 item = gtk_menu_item_new_with_label(_("Raise priority"));
763 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
764 g_signal_connect_swapped(G_OBJECT(item), "activate",
765 G_CALLBACK(pl3_current_playlist_browser_priority_raise_selected_songs), self);
766 /* remove priority */
767 item = gtk_menu_item_new_with_label(_("Remove priority"));
768 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
769 g_signal_connect_swapped(G_OBJECT(item), "activate",
770 G_CALLBACK(pl3_current_playlist_browser_priority_remove_selected_songs), self);
773 if (rows == 1)
775 mpd_Song *song;
776 GtkTreePath *path;
777 GtkTreeModel *model = gtk_tree_view_get_model(tree);
778 GtkTreeIter iter;
779 GList *list =
780 gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)), &model);
781 path = list->data;
782 if (path && gtk_tree_model_get_iter(model, &iter, path))
784 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_MPDSONG, &song, -1);
785 if (song)
787 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_DIALOG_INFO, NULL);
788 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
789 g_signal_connect_swapped(G_OBJECT(item), "activate",
790 G_CALLBACK(pl3_current_playlist_browser_show_info), self);
792 /* Add song sebmenu */
793 submenu_for_song(menu, song);
796 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
797 g_list_free(list);
800 playlist_editor_right_mouse(menu, pl3_current_playlist_editor_add_to_playlist, self);
801 //gmpc_mpddata_treeview_right_mouse_intergration(GMPC_MPDDATA_TREEVIEW(tree), GTK_MENU(menu));
802 gtk_widget_show_all(menu);
803 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, event->time);
804 return TRUE;
806 return FALSE;
809 static void pl3_current_playlist_browser_row_activated(GtkTreeView * tree, GtkTreePath * path, GtkTreeViewColumn * col,
810 PlayQueuePlugin * self)
812 GtkTreeIter iter;
813 gint song_id;
814 GtkTreeModel *model = gtk_tree_view_get_model(tree);
815 gtk_tree_model_get_iter(model, &iter, path);
816 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_SONG_ID, &song_id, -1);
817 mpd_player_play_id(connection, song_id);
819 if (!self->priv->search_keep_open && model == self->priv->mod_fill)
821 gtk_tree_view_set_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree), playlist);
823 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(self->priv->mod_fill), NULL);
824 self->priv->quick_search = 0;
825 gtk_widget_hide(self->priv->filter_entry);
827 pl3_current_playlist_browser_select_current_song(self);
832 static void pl3_current_playlist_browser_show_info(PlayQueuePlugin * self)
835 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
836 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
837 if (gtk_tree_selection_count_selected_rows(selection) > 0)
839 GList *list = NULL;
840 list = gtk_tree_selection_get_selected_rows(selection, &model);
842 list = g_list_last(list);
845 GtkTreeIter iter;
846 mpd_Song *song = NULL;
847 gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) list->data);
848 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_MPDSONG, &song, -1);
850 info2_activate();
851 info2_fill_song_view(song);
854 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
855 g_list_free(list);
859 static void pl3_current_playlist_browser_selected(GmpcPluginBrowserIface * obj, GtkContainer * container)
861 PlayQueuePlugin *self = (PlayQueuePlugin *) obj;
862 gboolean init = FALSE;
863 if (self->priv->pl3_cp_vbox == NULL)
865 pl3_current_playlist_browser_init((PlayQueuePlugin *) obj);
866 init = TRUE;
868 gtk_container_add(GTK_CONTAINER(container), self->priv->pl3_cp_vbox);
869 gtk_widget_show(self->priv->pl3_cp_vbox);
871 gtk_widget_grab_focus(self->priv->pl3_cp_tree);
873 if (init && cfg_get_single_value_as_int_with_default(config, "playlist", "st_cur_song", 0))
875 pl3_current_playlist_browser_scroll_to_current_song(self);
876 pl3_current_playlist_browser_select_current_song(self);
880 static void pl3_current_playlist_browser_unselected(GmpcPluginBrowserIface * obj, GtkContainer * container)
882 PlayQueuePlugin *self = (PlayQueuePlugin *) obj;
883 gtk_container_remove(GTK_CONTAINER(container), self->priv->pl3_cp_vbox);
885 static void pl3_current_playlist_add_after_current_song(GtkWidget *item, GtkTreeView *tree)
887 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
888 int cur_song_pos = mpd_player_get_current_song_pos(connection);
889 GList *list, *list_iter;
890 GtkTreeModel *model;
891 list = gtk_tree_selection_get_selected_rows(selection, &model);
892 if(list)
894 for(list_iter = g_list_first(list); list_iter != NULL; list_iter = g_list_next(list_iter))
896 GtkTreeIter iter;
897 GtkTreePath *path = (GtkTreePath *)list_iter->data;
898 /* Convert the path into an actual iter we can use to get values from the model */
899 if(gtk_tree_model_get_iter(model, &iter,path))
901 mpd_Song*song = NULL;
902 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_MPDSONG, &song, -1);
903 if(song != NULL && song->file != NULL)
905 int songid = mpd_playlist_add_get_id(connection, (gchar *) song->file);
906 if(cur_song_pos >= 0)
908 cur_song_pos++;
909 mpd_playlist_move_id(connection, songid, cur_song_pos);
914 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
915 g_list_free (list);
919 static int pl3_current_playlist_song_list_option_menu (GmpcPluginBrowserIface *obj, GtkWidget *tree, GtkMenu *menu)
921 printf("list option menu\n");
922 if(mpd_check_connected(connection) && !mpd_player_get_random(connection))
924 GtkWidget *item = gtk_image_menu_item_new_with_label(_("Add after current song"));
925 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
926 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_add_after_current_song), tree);
927 return 1;
929 return 0;
932 static int pl3_current_playlist_browser_option_menu(GmpcPluginBrowserIface * obj, GtkMenu * menu)
934 /* here we have: Save, Clear */
935 GtkWidget *item;
937 /* add the save widget */
938 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_SAVE, NULL);
939 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
940 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_save_playlist), NULL);
942 /* add the clear widget */
943 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLEAR, NULL);
944 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
945 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_browser_clear_playlist), NULL);
947 return 1;
950 static int pl3_current_playlist_tool_menu_integration(GmpcPluginToolMenuIface * obj, GtkMenu * menu)
952 GtkWidget *item;
953 item = gtk_image_menu_item_new_with_label(_("Add URL"));
954 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
955 gtk_image_new_from_icon_name("add-url", GTK_ICON_SIZE_MENU));
956 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
957 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(url_start), NULL);
959 return 1;
962 static int pl3_current_playlist_browser_key_press_event(GtkTreeView * tree, GdkEventKey * event,
963 PlayQueuePlugin * self)
965 if (event->keyval == GDK_KEY_Delete)
967 pl3_current_playlist_browser_delete_selected_songs(self);
968 return TRUE;
969 } else if (event->keyval == GDK_KEY_i && event->state & GDK_MOD1_MASK)
971 pl3_current_playlist_browser_show_info(self);
972 return TRUE;
973 } else if (event->state&GDK_MOD1_MASK && event->keyval == GDK_KEY_q)
975 if(mpd_server_check_command_allowed(connection, "prioid") == MPD_SERVER_COMMAND_ALLOWED)
977 pl3_current_playlist_browser_priority_raise_selected_songs(self);
979 } else if (event->state&GDK_MOD1_MASK && event->keyval == GDK_KEY_d)
981 if(mpd_server_check_command_allowed(connection, "prioid") == MPD_SERVER_COMMAND_ALLOWED)
983 pl3_current_playlist_browser_priority_remove_selected_songs(self);
985 } else if (event->keyval == GDK_KEY_space)
987 pl3_current_playlist_browser_scroll_to_current_song(self);
988 pl3_current_playlist_browser_select_current_song(self);
989 return TRUE;
990 } else if (event->keyval == GDK_KEY_f && event->state & GDK_CONTROL_MASK)
992 mod_fill_entry_changed(self->priv->filter_entry, self);
993 gtk_widget_grab_focus(self->priv->filter_entry);
994 self->priv->search_keep_open = TRUE;
995 return TRUE;
996 } else if ((event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) == 0) /*&&
997 ((event->keyval >= GDK_space && event->keyval <= GDK_z))) */
999 char data[10];
1000 guint32 uc = gdk_keyval_to_unicode(event->keyval);
1001 if (uc && g_unichar_isalnum(uc))
1003 memset(data, '\0', 10);
1004 g_unichar_to_utf8(uc, data);
1005 gtk_widget_grab_focus(self->priv->filter_entry);
1006 gtk_entry_set_text(GTK_ENTRY(self->priv->filter_entry), data);
1007 gtk_editable_set_position(GTK_EDITABLE(self->priv->filter_entry), 1);
1009 return TRUE;
1012 return pl3_window_key_press_event(GTK_WIDGET(tree), event);
1015 /* create a dialog that allows the user to save the current playlist */
1016 static void pl3_current_playlist_save_playlist(void)
1018 gchar *str;
1019 GtkBuilder *xml = NULL;
1020 int run = TRUE;
1021 /* check if the connection is up */
1022 if (!mpd_check_connected(connection))
1024 return;
1026 /* create the interface */
1027 str = gmpc_get_full_glade_path("playlist-save-dialog.ui");
1028 xml = gtk_builder_new();
1029 gtk_builder_add_from_file(xml, str, NULL);
1030 q_free(str);
1032 /* run the interface */
1035 switch (gtk_dialog_run(GTK_DIALOG(gtk_builder_get_object(xml, "save_pl"))))
1037 case GTK_RESPONSE_OK:
1038 run = FALSE;
1039 /* if the users agrees do the following: */
1040 /* get the song-name */
1041 str = (gchar *) gtk_entry_get_text(GTK_ENTRY((GtkWidget *) gtk_builder_get_object(xml, "pl-entry")));
1042 /* check if the user entered a name, we can't do withouth */
1043 /* TODO: disable ok button when nothing is entered */
1044 /* also check if there is a connection */
1045 if (strlen(str) != 0 && mpd_check_connected(connection))
1047 int retv = mpd_database_save_playlist(connection, str);
1048 if (retv == MPD_DATABASE_PLAYLIST_EXIST)
1050 gchar *errormsg =
1051 g_markup_printf_escaped(_("<i>Playlist <b>\"%s\"</b> already exists\nOverwrite?</i>"), str);
1052 gtk_label_set_markup(GTK_LABEL((GtkWidget *) gtk_builder_get_object(xml, "label_error")), errormsg);
1053 gtk_widget_show((GtkWidget *) gtk_builder_get_object(xml, "hbox5"));
1054 /* ask to replace */
1055 gtk_widget_set_sensitive(GTK_WIDGET((GtkWidget *) gtk_builder_get_object(xml, "pl-entry")), FALSE);
1056 switch (gtk_dialog_run(GTK_DIALOG((GtkWidget *) gtk_builder_get_object(xml, "save_pl"))))
1058 case GTK_RESPONSE_OK:
1059 run = FALSE;
1060 mpd_database_delete_playlist(connection, str);
1061 mpd_database_save_playlist(connection, str);
1062 GmpcStatusChangedCallback(connection, MPD_CST_DATABASE, NULL);
1063 break;
1064 default:
1065 run = TRUE;
1067 /* return to stare */
1068 gtk_widget_set_sensitive(GTK_WIDGET((GtkWidget *) gtk_builder_get_object(xml, "pl-entry")), TRUE);
1069 gtk_widget_hide((GtkWidget *) gtk_builder_get_object(xml, "hbox5"));
1071 q_free(errormsg);
1072 } else if (retv != MPD_OK)
1074 playlist3_show_error_message(_("Failed to save the playlist file."), ERROR_WARNING);
1075 } else
1077 GmpcStatusChangedCallback(connection, MPD_CST_DATABASE, NULL);
1080 break;
1081 default:
1082 run = FALSE;
1084 } while (run);
1085 /* destroy the window */
1086 gtk_widget_destroy((GtkWidget *) gtk_builder_get_object(xml, "save_pl"));
1088 /* unref the gui description */
1089 g_object_unref(xml);
1092 static void pl3_current_playlist_browser_clear_playlist(void)
1094 GtkWidget *delete;
1095 if(cfg_get_single_value_as_int(config, "playlist","no-confirm-clear") != 1)
1098 delete = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
1099 g_signal_connect(G_OBJECT(delete),
1100 "clicked",
1101 G_CALLBACK(pl3_current_playlist_browser_clear_playlist_real),
1102 NULL);
1104 /* show message */
1105 playlist3_message_show(pl3_messages,
1106 _("Are you sure you want to clear the play queue?")
1107 ,USER_FEEDBACK);
1108 playlist3_message_add_widget(pl3_messages, delete);
1109 gtk_widget_grab_focus(GTK_WIDGET(delete));
1111 } else {
1112 pl3_current_playlist_browser_clear_playlist_real();
1116 static void pl3_current_playlist_browser_shuffle_playlist(void)
1118 mpd_playlist_shuffle(connection);
1121 static void pl3_current_playlist_status_changed(GmpcConnection * conn, MpdObj * mi, ChangedStatusType what,
1122 PlayQueuePlugin * self)
1124 if (self->priv->pl3_cp_vbox == NULL)
1125 return;
1126 if (what & MPD_CST_PLAYLIST)
1129 if (self->priv->quick_search)
1130 mod_fill_do_entry_changed(self);
1134 static void pl3_current_playlist_browser_activate(PlayQueuePlugin * self)
1136 GtkTreeSelection *selec = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtk_builder_get_object(pl3_xml, "cat_tree")));
1138 GtkTreePath *path = gtk_tree_row_reference_get_path(self->priv->pl3_curb_tree_ref);
1139 if (path)
1141 gtk_tree_selection_select_path(selec, path);
1142 gtk_tree_path_free(path);
1144 gtk_widget_grab_focus(self->priv->pl3_cp_tree);
1147 static void pl3_current_playlist_do_playlist_search(GmpcPluginBase * self)
1149 pl3_find2_select_plugin_id(self->id);
1152 static int pl3_current_playlist_browser_add_go_menu(GmpcPluginBrowserIface * obj, GtkMenu * menu)
1154 GtkWidget *item = NULL;
1156 item = gtk_image_menu_item_new_with_label(_("Play Queue"));
1157 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
1158 gtk_image_new_from_icon_name("playlist-browser", GTK_ICON_SIZE_MENU));
1159 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1160 gtk_widget_add_accelerator(GTK_WIDGET(item), "activate", gtk_menu_get_accel_group(GTK_MENU(menu)), GDK_KEY_F1, 0,
1161 GTK_ACCEL_VISIBLE);
1162 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_browser_activate), obj);
1164 item = gtk_image_menu_item_new_with_label(_("Search Playlist"));
1165 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
1166 gtk_image_new_from_icon_name("gtk-find", GTK_ICON_SIZE_MENU));
1167 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1168 gtk_widget_add_accelerator(GTK_WIDGET(item), "activate", gtk_menu_get_accel_group(GTK_MENU(menu)), GDK_KEY_j,
1169 GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
1170 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_do_playlist_search), obj);
1171 return 2;
1174 static void pl3_current_playlist_connection_changed(GmpcConnection * conn, MpdObj * mi, int connect,
1175 PlayQueuePlugin * self)
1177 if (!connect && self->priv->filter_entry)
1179 gtk_entry_set_text(GTK_ENTRY(self->priv->filter_entry), "");
1183 /* function that saves the settings */
1184 static void pl3_current_playlist_save_myself(GmpcPluginBase * obj)
1186 PlayQueuePlugin *self = (PlayQueuePlugin *) obj;
1187 if (self->priv->pl3_curb_tree_ref)
1189 GtkTreePath *path = gtk_tree_row_reference_get_path(self->priv->pl3_curb_tree_ref);
1190 if (path)
1192 gint *indices = gtk_tree_path_get_indices(path);
1193 cfg_set_single_value_as_int(config, "current-playlist", "position", indices[0]);
1194 gtk_tree_path_free(path);
1200 * GmpcPlugin
1202 GType play_queue_plugin_get_type(void);
1204 static int *play_queue_plugin_get_version(GmpcPluginBase * plug, int *length)
1206 static int version[3] = { 0, 0, 1 };
1207 if (length)
1208 *length = 3;
1209 return (int *)version;
1212 static const char *play_queue_plugin_get_name(GmpcPluginBase * plug)
1214 return N_("Play Queue");
1217 static void play_queue_plugin_finalize(GObject * obj)
1219 PlayQueuePlugin *self = (PlayQueuePlugin *) obj;
1220 PlayQueuePluginClass *klass = (g_type_class_peek(play_queue_plugin_get_type()));
1221 gpointer parent_class = g_type_class_peek_parent(klass);
1222 pl3_current_playlist_destroy((PlayQueuePlugin *) obj);
1224 if (self->priv)
1226 if (self->priv->uid)
1228 g_free(self->priv->uid);
1229 self->priv->uid = NULL;
1231 if (self->priv->mod_fill)
1233 g_object_unref(self->priv->mod_fill);
1234 self->priv->mod_fill = NULL;
1236 if (g_signal_handler_is_connected(G_OBJECT(gmpcconn), self->priv->status_changed_handler))
1237 g_signal_handler_disconnect(G_OBJECT(gmpcconn), self->priv->status_changed_handler);
1239 if (g_signal_handler_is_connected(G_OBJECT(gmpcconn), self->priv->connection_changed_handler))
1240 g_signal_handler_disconnect(G_OBJECT(gmpcconn), self->priv->connection_changed_handler);
1241 g_free(self->priv);
1242 self->priv = NULL;
1244 if (parent_class)
1245 G_OBJECT_CLASS(parent_class)->finalize(obj);
1248 static GObject *play_queue_plugin_constructor(GType type, guint n_construct_properties,
1249 GObjectConstructParam * construct_properties)
1251 PlayQueuePluginClass *klass;
1252 PlayQueuePlugin *self;
1253 GObjectClass *parent_class;
1254 klass = (g_type_class_peek(play_queue_plugin_get_type()));
1255 parent_class = G_OBJECT_CLASS(g_type_class_peek_parent(klass));
1256 self = (PlayQueuePlugin *) parent_class->constructor(type, n_construct_properties, construct_properties);
1258 /* setup private structure */
1259 self->priv = g_malloc0(sizeof(PlayQueuePluginPrivate));
1260 self->priv->status_changed_handler =
1261 g_signal_connect_object(G_OBJECT(gmpcconn), "status-changed", G_CALLBACK(pl3_current_playlist_status_changed),
1262 self, 0);
1263 self->priv->connection_changed_handler =
1264 g_signal_connect_object(G_OBJECT(gmpcconn), "connection-changed",
1265 G_CALLBACK(pl3_current_playlist_connection_changed), self, 0);
1266 /* quick search */
1267 self->priv->search_keep_open = FALSE;
1268 self->priv->filter_entry = NULL;
1269 self->priv->mod_fill = NULL;
1270 self->priv->quick_search = 0;
1271 self->priv->quick_search_timeout = 0;
1272 /* other widgets */
1273 self->priv->pl3_cp_tree = NULL;
1274 self->priv->pl3_cp_vbox = NULL;
1275 self->priv->pl3_curb_tree_ref = NULL;
1277 /* Make it an internal plugin */
1278 GMPC_PLUGIN_BASE(self)->plugin_type = GMPC_PLUGIN_PL_BROWSER | GMPC_INTERNALL;
1279 pl3_cp_init(self);
1281 return G_OBJECT(self);
1285 * Base GmpcPluginBase class
1287 static void play_queue_plugin_class_init(PlayQueuePluginClass * klass)
1289 /* Connect destroy and construct */
1290 G_OBJECT_CLASS(klass)->finalize = play_queue_plugin_finalize;
1291 G_OBJECT_CLASS(klass)->constructor = play_queue_plugin_constructor;
1292 G_OBJECT_CLASS(klass)->get_property = play_queue_get_property;
1293 G_OBJECT_CLASS(klass)->set_property = play_queue_set_property;
1294 /* Connect plugin functions */
1295 GMPC_PLUGIN_BASE_CLASS(klass)->get_version = play_queue_plugin_get_version;
1296 GMPC_PLUGIN_BASE_CLASS(klass)->get_name = play_queue_plugin_get_name;
1298 GMPC_PLUGIN_BASE_CLASS(klass)->save_yourself = pl3_current_playlist_save_myself;
1300 g_object_class_install_property(G_OBJECT_CLASS(klass), PLAY_QUEUE_UID,
1301 g_param_spec_string("uid", "uid", "stores an unique id", "play-queue",
1302 G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB
1303 | G_PARAM_READABLE | G_PARAM_WRITABLE));
1308 * Browser interface
1310 static void play_queue_browser_iface_init(GmpcPluginBrowserIfaceIface * iface)
1312 iface->browser_add = pl3_current_playlist_browser_add;
1313 iface->browser_selected = pl3_current_playlist_browser_selected;
1314 iface->browser_unselected = pl3_current_playlist_browser_unselected;
1315 iface->browser_option_menu = pl3_current_playlist_browser_option_menu;
1316 iface->browser_add_go_menu = pl3_current_playlist_browser_add_go_menu;
1319 static void play_queue_song_list_iface_init(GmpcPluginSongListIfaceIface *iface)
1321 iface->song_list = pl3_current_playlist_song_list_option_menu;
1325 * Tool menu interface
1327 static void play_queue_plugin_tool_menu_iface_init(GmpcPluginToolMenuIfaceIface * iface)
1329 iface->tool_menu_integration = pl3_current_playlist_tool_menu_integration;
1332 /* Integrate Search */
1333 static gboolean play_queue_plugin_is_field_supported(GmpcPluginIntegrateSearchIface * obj, mpd_TagItems tag)
1335 if (tag == MPD_TAG_NUM_OF_ITEM_TYPES)
1336 return TRUE;
1337 return mpd_server_tag_supported(connection, tag);
1340 static MpdData *play_queue_plugin_is_search(GmpcPluginIntegrateSearchIface * obj, mpd_TagItems num_field,
1341 const gchar * search_string)
1343 MpdData *data_t = NULL;
1344 if (num_field == MPD_TAG_NUM_OF_ITEM_TYPES)
1346 data_t = advanced_search(search_string, TRUE);
1347 } else
1349 gchar **splitted = tokenize_string(search_string);
1350 int i = 0;
1351 gboolean found = FALSE;
1352 for (i = 0; splitted && splitted[i]; i++)
1354 if (!found)
1356 mpd_playlist_search_start(connection, FALSE);
1357 found = TRUE;
1359 mpd_playlist_search_add_constraint(connection, num_field, splitted[i]);
1361 if (splitted)
1362 g_strfreev(splitted);
1363 if (found)
1365 data_t = mpd_playlist_search_commit(connection);
1368 return data_t;
1371 static void play_queue_plugin_is_iface_init(GmpcPluginIntegrateSearchIfaceIface * iface)
1373 iface->field_supported = play_queue_plugin_is_field_supported;
1374 iface->search = play_queue_plugin_is_search;
1377 GType play_queue_plugin_get_type(void)
1379 static GType play_queue_plugin_type_id = 0;
1380 if (play_queue_plugin_type_id == 0)
1382 static const GTypeInfo info = {
1383 .class_size = sizeof(PlayQueuePluginClass),
1384 .class_init = (GClassInitFunc) play_queue_plugin_class_init,
1385 .instance_size = sizeof(PlayQueuePlugin),
1386 .n_preallocs = 0
1388 static const GInterfaceInfo iface_info = {
1389 (GInterfaceInitFunc) play_queue_browser_iface_init,
1390 (GInterfaceFinalizeFunc) NULL, NULL
1392 static const GInterfaceInfo iface_song_list = {
1393 (GInterfaceInitFunc) play_queue_song_list_iface_init,
1394 (GInterfaceFinalizeFunc) NULL, NULL
1396 static const GInterfaceInfo iface_tm_info = {
1397 (GInterfaceInitFunc) play_queue_plugin_tool_menu_iface_init,
1398 (GInterfaceFinalizeFunc) NULL, NULL
1401 static const GInterfaceInfo iface_is_info = {
1402 (GInterfaceInitFunc) play_queue_plugin_is_iface_init,
1403 (GInterfaceFinalizeFunc) NULL, NULL
1405 play_queue_plugin_type_id = g_type_register_static(GMPC_PLUGIN_TYPE_BASE, "PlayQueuePlugin", &info, 0);
1407 g_type_add_interface_static(play_queue_plugin_type_id, GMPC_PLUGIN_TYPE_BROWSER_IFACE, &iface_info);
1408 g_type_add_interface_static(play_queue_plugin_type_id, GMPC_PLUGIN_TYPE_TOOL_MENU_IFACE, &iface_tm_info);
1409 g_type_add_interface_static(play_queue_plugin_type_id, GMPC_PLUGIN_TYPE_INTEGRATE_SEARCH_IFACE, &iface_is_info);
1410 g_type_add_interface_static(play_queue_plugin_type_id, GMPC_PLUGIN_TYPE_SONG_LIST_IFACE, &iface_song_list);
1412 return play_queue_plugin_type_id;
1415 PlayQueuePlugin *play_queue_plugin_new(const gchar * uid)
1417 PlayQueuePlugin *plug = g_object_new(play_queue_plugin_get_type(), "uid", uid, NULL);
1419 return plug;
1424 * Priority.
1427 static void pl3_current_playlist_browser_priority_raise_selected_songs(PlayQueuePlugin * self)
1429 GList *list = NULL, *llist = NULL;
1430 GtkTreeModel *model = NULL;
1432 /* grab the selection from the tree */
1433 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
1435 int sel_rows = gtk_tree_selection_count_selected_rows(selection);
1437 if(sel_rows >= 254) {
1438 playlist3_show_error_message(_("You can only queue 254 songs at the time."),
1439 ERROR_WARNING);
1440 return ;
1442 else if (sel_rows == 0)
1444 return;
1447 /* start a command list */
1448 /* grab the selected songs */
1449 list = gtk_tree_selection_get_selected_rows(selection, &model);
1450 /* grab the last song that is selected */
1451 /* remove every selected song one by one */
1452 if (list)
1454 int priority = 255;
1455 llist = g_list_first(list);
1456 while(llist)
1459 GtkTreeIter iter;
1460 if(gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) llist->data))
1462 int id;
1463 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_SONG_ID, &id, -1);
1464 mpd_playlist_set_priority(connection, id, priority--);
1466 llist = g_list_next(llist);
1469 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
1470 g_list_free(list);
1473 static void pl3_current_playlist_browser_priority_remove_selected_songs(PlayQueuePlugin * self)
1475 GList *list = NULL, *llist = NULL;
1476 GtkTreeModel *model = NULL;
1477 /* grab the selection from the tree */
1478 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
1480 int sel_rows = gtk_tree_selection_count_selected_rows(selection);
1482 if (sel_rows == 0)
1484 return;
1487 /* start a command list */
1488 /* grab the selected songs */
1489 list = gtk_tree_selection_get_selected_rows(selection, &model);
1490 /* grab the last song that is selected */
1491 /* remove every selected song one by one */
1492 if (list)
1494 llist = g_list_first(list);
1495 while(llist)
1498 GtkTreeIter iter;
1499 if(gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) llist->data))
1501 int id;
1502 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_SONG_ID, &id, -1);
1503 mpd_playlist_set_priority(connection, id, 0);
1505 llist = g_list_next(llist);
1508 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
1509 g_list_free(list);