Add a dequeue menu entry + keybindings
[gmpc.git] / src / browsers / playlist3-current-playlist-browser.c
blob5bcd65b1cae171978143455076a4867387222016
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_mpddata_treeview_new(self->priv->uid, FALSE, GTK_TREE_MODEL(playlist));
359 g_signal_connect(G_OBJECT(tree), "paste_before", G_CALLBACK(pl3_current_playlist_browser_paste_before_songs), self);
360 g_signal_connect(G_OBJECT(tree), "paste_after", G_CALLBACK(pl3_current_playlist_browser_paste_after_songs), self);
361 g_signal_connect_swapped(G_OBJECT(tree), "cut", G_CALLBACK(pl3_current_playlist_browser_delete_selected_songs),
362 self);
365 /* Enable this for this model only */
366 gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(tree), gmt_targetentries, 1, GDK_ACTION_DEFAULT|GDK_ACTION_MOVE|GDK_ACTION_COPY);
368 /* filter */
369 self->priv->mod_fill = (GtkTreeModel *) gmpc_mpddata_model_new();
370 entry = gtk_entry_new();
371 gtk_entry_set_icon_from_stock(GTK_ENTRY(entry), GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
372 g_signal_connect(GTK_ENTRY(entry), "icon-press", G_CALLBACK(mod_fill_clear_search_entry), NULL);
374 gtk_entry_set_icon_from_stock(GTK_ENTRY(entry), GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND);
375 gtk_entry_set_icon_activatable(GTK_ENTRY(entry), GTK_ENTRY_ICON_PRIMARY, FALSE);
378 gtk_box_pack_end(GTK_BOX(self->priv->pl3_cp_vbox), entry, FALSE, TRUE, 0);
379 self->priv->filter_entry = entry;
380 g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(mod_fill_entry_changed), self);
381 g_signal_connect(G_OBJECT(entry), "key-press-event", G_CALLBACK(mod_fill_entry_key_press_event), self);
382 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(mod_fill_entry_activate), self);
384 sw = gtk_scrolled_window_new(NULL, NULL);
385 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
386 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
387 gtk_container_add(GTK_CONTAINER(sw), tree);
388 gtk_box_pack_start(GTK_BOX(self->priv->pl3_cp_vbox), GTK_WIDGET(sw), TRUE, TRUE, 0);
389 gtk_widget_show_all(sw);
390 /* set up the tree */
391 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(tree), FALSE);
393 gmpc_mpddata_treeview_enable_click_fix(GMPC_MPDDATA_TREEVIEW(tree));
394 /* setup signals */
395 g_signal_connect(G_OBJECT(tree), "row-activated", G_CALLBACK(pl3_current_playlist_browser_row_activated), self);
396 g_signal_connect(G_OBJECT(tree), "button-release-event",
397 G_CALLBACK(pl3_current_playlist_browser_button_release_event), self);
398 g_signal_connect(G_OBJECT(tree), "key-press-event", G_CALLBACK(pl3_current_playlist_browser_key_press_event),
399 self);
401 /* set up the scrolled window */
402 pl3_cp_sw = gtk_scrolled_window_new(NULL, NULL);
403 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(pl3_cp_sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
404 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(pl3_cp_sw), GTK_SHADOW_ETCHED_IN);
406 /* set initial state */
407 self->priv->pl3_cp_tree = tree;
408 g_object_ref_sink(G_OBJECT(self->priv->pl3_cp_vbox));
411 static void pl3_current_playlist_browser_select_current_song(PlayQueuePlugin * self)
413 if (self->priv->pl3_cp_tree == NULL || !gtk_widget_get_realized(self->priv->pl3_cp_tree))
414 return;
415 /* scroll to the playing song */
416 if (mpd_player_get_current_song_pos(connection) >= 0 && mpd_playlist_get_playlist_length(connection) > 0)
418 GtkTreePath *path = gtk_tree_path_new_from_indices(mpd_player_get_current_song_pos(connection), -1);
419 if (path != NULL
420 && GMPC_IS_MPDDATA_MODEL_PLAYLIST(gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree))))
422 gtk_tree_view_set_cursor(GTK_TREE_VIEW(self->priv->pl3_cp_tree), path, NULL, FALSE);
424 gtk_tree_path_free(path);
428 static void pl3_current_playlist_browser_scroll_to_current_song(PlayQueuePlugin * self)
430 if (self->priv->pl3_cp_tree == NULL || !gtk_widget_get_realized(self->priv->pl3_cp_tree))
431 return;
432 /* scroll to the playing song */
433 if (mpd_player_get_current_song_pos(connection) >= 0 && mpd_playlist_get_playlist_length(connection) > 0)
435 GtkTreePath *path = gtk_tree_path_new_from_indices(mpd_player_get_current_song_pos(connection), -1);
436 if (path != NULL)
438 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(self->priv->pl3_cp_tree), path, NULL, TRUE, 0.5, 0);
439 gtk_tree_path_free(path);
445 /* add's the toplevel entry for the current playlist view */
446 static void pl3_current_playlist_browser_add(GmpcPluginBrowserIface * obj, GtkWidget * cat_tree)
448 PlayQueuePlugin *self = (PlayQueuePlugin *) obj;
449 GtkTreeIter iter;
450 GtkTreePath *path;
451 playlist3_insert_browser(&iter, PL3_CAT_BROWSER_TOP+1);
452 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, */
453 PL3_CAT_TITLE, _(gmpc_plugin_base_get_name(GMPC_PLUGIN_BASE(self))),
454 PL3_CAT_ICON_ID, "playlist-browser", -1);
455 if (self->priv->pl3_curb_tree_ref)
457 gtk_tree_row_reference_free(self->priv->pl3_curb_tree_ref);
458 self->priv->pl3_curb_tree_ref = NULL;
460 path = gtk_tree_model_get_path(GTK_TREE_MODEL(pl3_tree), &iter);
461 if (path)
463 self->priv->pl3_curb_tree_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(pl3_tree), path);
464 gtk_tree_path_free(path);
468 /* delete all selected songs,
469 * if no songs select ask the user if he want's to clear the list
471 static void pl3_current_playlist_browser_delete_selected_songs(PlayQueuePlugin * self)
473 /* grab the selection from the tree */
474 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
475 /* check if where connected */
476 /* see if there is a row selected */
477 if (gtk_tree_selection_count_selected_rows(selection) > 0)
479 GList *list = NULL, *llist = NULL;
480 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
481 /* start a command list */
482 /* grab the selected songs */
483 list = gtk_tree_selection_get_selected_rows(selection, &model);
484 /* grab the last song that is selected */
485 llist = g_list_last(list);
486 /* remove every selected song one by one */
489 GtkTreeIter iter;
490 int value;
491 gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) llist->data);
492 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_SONG_ID, &value, -1);
493 mpd_playlist_queue_delete_id(connection, value);
494 } while ((llist = g_list_previous(llist)));
496 /* close the list, so it will be executed */
497 mpd_playlist_queue_commit(connection);
498 /* unselect all if multiple songs were selected */
499 if (g_list_length(list) > 1)
500 gtk_tree_selection_unselect_all(selection);
501 /* free list */
502 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
503 g_list_free(list);
504 } else
506 pl3_current_playlist_browser_clear_playlist();
509 /* update everything if where still connected */
510 mpd_status_queue_update(connection);
513 static void pl3_current_playlist_browser_crop_selected_songs(PlayQueuePlugin * self)
515 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
516 /* grab the selection from the tree */
517 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
519 /* see if there is a row selected */
520 if (gtk_tree_selection_count_selected_rows(selection) > 0)
522 GtkTreeIter iter;
523 /* we want to delete from back to front, so we have to transverse this list */
524 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter))
529 if (!gtk_tree_selection_iter_is_selected(selection, &iter))
531 int id = 0;
532 /* song pos starts at 1, not a 0, compensate for that */
533 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, MPDDATA_MODEL_COL_SONG_ID, &id, -1);
534 mpd_playlist_queue_delete_id(connection, id);
536 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter));
538 mpd_playlist_queue_commit(connection);
539 /* update everything if where still connected */
540 gtk_tree_selection_unselect_all(selection);
542 mpd_status_queue_update(connection);
546 static void pl3_current_playlist_editor_add_to_playlist(GtkWidget * menu, gpointer cb_data)
548 PlayQueuePlugin *self = (PlayQueuePlugin *) cb_data;
549 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
550 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
551 gchar *data = g_object_get_data(G_OBJECT(menu), "playlist");
552 GList *iter, *list = gtk_tree_selection_get_selected_rows(selection, &model);
553 if (list)
555 iter = g_list_first(list);
558 GtkTreeIter giter;
559 if (gtk_tree_model_get_iter(model, &giter, (GtkTreePath *) iter->data))
561 gchar *file = NULL;
562 gtk_tree_model_get(model, &giter, MPDDATA_MODEL_COL_PATH, &file, -1);
563 mpd_database_playlist_list_add(connection, data, file);
564 g_free(file);
566 } while ((iter = g_list_next(iter)));
568 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
569 g_list_free(list);
572 playlist_editor_fill_list();
575 static void pl3_current_playlist_browser_paste_after_songs(GtkTreeView * tree, GList * paste_list,
576 PlayQueuePlugin * self)
578 /* grab the selection from the tree */
579 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
581 int seen = 0;
582 /* check if where connected */
583 /* see if there is a row selected */
584 if (gtk_tree_selection_count_selected_rows(selection) > 0)
586 GList *list = NULL, *llist = NULL;
587 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
588 /* start a command list */
589 /* grab the selected songs */
590 list = gtk_tree_selection_get_selected_rows(selection, &model);
591 /* grab the last song that is selected */
592 llist = g_list_last(list);
593 /* remove every selected song one by one */
594 if (llist)
596 GtkTreeIter iter;
597 gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) llist->data);
598 /* Trick that avoids roundtrip to mpd */
600 int id;
601 char *path = NULL;
602 int length = mpd_playlist_get_playlist_length(connection);
603 GList *liter = g_list_first(paste_list);
604 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_SONG_POS, &id, -1);
605 while (liter)
607 int song_id;
608 path = liter->data;
609 song_id = mpd_playlist_add_get_id(connection, path);
610 if (song_id == -1 && !seen)
612 playlist3_show_error_message(_("Your mpd has a broken 'addid', pasting will fail."),
613 ERROR_WARNING);
614 seen = 1;
616 mpd_playlist_move_pos(connection, length, id);
617 length++;
618 liter = g_list_next(liter);
622 /* free list */
623 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
624 g_list_free(list);
625 } else
627 GList *liter = g_list_first(paste_list);;
628 while (liter)
630 char *path = liter->data;
631 int song_id = mpd_playlist_add_get_id(connection, path);
632 if (song_id == -1 && !seen)
634 playlist3_show_error_message(_("Your mpd has a broken 'addid', pasting will fail."), ERROR_WARNING);
635 seen = 1;
637 liter = g_list_next(liter);
641 gtk_tree_selection_unselect_all(selection);
644 static void pl3_current_playlist_browser_paste_before_songs(GtkTreeView * tree, GList * paste_list,
645 PlayQueuePlugin * self)
647 /* grab the selection from the tree */
648 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
650 int seen = 0;
651 /* check if where connected */
652 /* see if there is a row selected */
653 if (gtk_tree_selection_count_selected_rows(selection) > 0)
655 GList *list = NULL, *llist = NULL;
656 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
657 /* start a command list */
658 /* grab the selected songs */
659 list = gtk_tree_selection_get_selected_rows(selection, &model);
660 /* grab the last song that is selected */
661 llist = g_list_first(list);
662 /* remove every selected song one by one */
663 if (llist)
665 GtkTreeIter iter;
666 gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) llist->data);
667 /* Trick that avoids roundtrip to mpd */
669 int id;
670 int length = mpd_playlist_get_playlist_length(connection);
671 GList *liter = g_list_first(paste_list);
672 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_SONG_POS, &id, -1);
673 while (liter)
675 char *path = liter->data;
676 int song_id = mpd_playlist_add_get_id(connection, path);
677 if (song_id == -1 && !seen)
679 playlist3_show_error_message(_("Your mpd has a broken 'addid', pasting will fail."),
680 ERROR_WARNING);
681 seen = 1;
683 mpd_playlist_move_pos(connection, length, id - 1);
684 /* The song is now one lower */
685 /* length one longer */
686 length++;
687 liter = g_list_next(liter);
691 /* free list */
692 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
693 g_list_free(list);
694 } else
696 GList *liter = g_list_first(paste_list);
697 while (liter)
699 char *path = liter->data;
700 int song_id = mpd_playlist_add_get_id(connection, path);
701 if (song_id == -1 && !seen)
703 playlist3_show_error_message(_("Your mpd has a broken 'addid', pasting will fail."), ERROR_WARNING);
704 seen = 1;
706 liter = g_list_next(liter);
711 gtk_tree_selection_unselect_all(selection);
714 static int pl3_current_playlist_browser_button_release_event(GtkTreeView * tree, GdkEventButton * event,
715 PlayQueuePlugin * self)
717 if (event->button == 3)
719 /* del, crop */
720 GtkWidget *item;
721 GtkWidget *menu = gtk_menu_new();
723 int rows = gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(tree));
724 /* add the delete widget */
725 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_REMOVE, NULL);
726 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
727 g_signal_connect_swapped(G_OBJECT(item), "activate",
728 G_CALLBACK(pl3_current_playlist_browser_delete_selected_songs), self);
730 if (rows)
732 /* add the delete widget */
733 item = gtk_image_menu_item_new_with_label(_("Crop"));
734 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
735 gtk_image_new_from_stock(GTK_STOCK_CUT, GTK_ICON_SIZE_MENU));
736 g_signal_connect_swapped(G_OBJECT(item), "activate",
737 G_CALLBACK(pl3_current_playlist_browser_crop_selected_songs), self);
738 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
740 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
741 /* add the clear widget */
742 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLEAR, NULL);
743 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
744 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_browser_clear_playlist), NULL);
746 /* add the shuffle widget */
747 item = gtk_image_menu_item_new_with_label(_("Shuffle"));
748 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
749 gtk_image_new_from_stock(GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU));
750 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_browser_shuffle_playlist), NULL);
751 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
753 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
755 if(mpd_server_check_command_allowed(connection, "prioid") == MPD_SERVER_COMMAND_ALLOWED)
757 /* priority */
758 item = gtk_menu_item_new_with_label(_("Raise priority"));
759 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
760 g_signal_connect_swapped(G_OBJECT(item), "activate",
761 G_CALLBACK(pl3_current_playlist_browser_priority_raise_selected_songs), self);
762 /* remove priority */
763 item = gtk_menu_item_new_with_label(_("Remove priority"));
764 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
765 g_signal_connect_swapped(G_OBJECT(item), "activate",
766 G_CALLBACK(pl3_current_playlist_browser_priority_remove_selected_songs), self);
769 if (rows == 1)
771 mpd_Song *song;
772 GtkTreePath *path;
773 GtkTreeModel *model = gtk_tree_view_get_model(tree);
774 GtkTreeIter iter;
775 GList *list =
776 gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)), &model);
777 path = list->data;
778 if (path && gtk_tree_model_get_iter(model, &iter, path))
780 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_MPDSONG, &song, -1);
781 if (song)
783 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_DIALOG_INFO, NULL);
784 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
785 g_signal_connect_swapped(G_OBJECT(item), "activate",
786 G_CALLBACK(pl3_current_playlist_browser_show_info), self);
788 /* Add song sebmenu */
789 submenu_for_song(menu, song);
792 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
793 g_list_free(list);
796 playlist_editor_right_mouse(menu, pl3_current_playlist_editor_add_to_playlist, self);
797 gmpc_mpddata_treeview_right_mouse_intergration(GMPC_MPDDATA_TREEVIEW(tree), GTK_MENU(menu));
798 gtk_widget_show_all(menu);
799 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, event->time);
800 return TRUE;
802 return FALSE;
805 static void pl3_current_playlist_browser_row_activated(GtkTreeView * tree, GtkTreePath * path, GtkTreeViewColumn * col,
806 PlayQueuePlugin * self)
808 GtkTreeIter iter;
809 gint song_id;
810 GtkTreeModel *model = gtk_tree_view_get_model(tree);
811 gtk_tree_model_get_iter(model, &iter, path);
812 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_SONG_ID, &song_id, -1);
813 mpd_player_play_id(connection, song_id);
815 if (!self->priv->search_keep_open && model == self->priv->mod_fill)
817 gtk_tree_view_set_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree), playlist);
819 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(self->priv->mod_fill), NULL);
820 self->priv->quick_search = 0;
821 gtk_widget_hide(self->priv->filter_entry);
823 pl3_current_playlist_browser_select_current_song(self);
828 static void pl3_current_playlist_browser_show_info(PlayQueuePlugin * self)
831 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
832 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
833 if (gtk_tree_selection_count_selected_rows(selection) > 0)
835 GList *list = NULL;
836 list = gtk_tree_selection_get_selected_rows(selection, &model);
838 list = g_list_last(list);
841 GtkTreeIter iter;
842 mpd_Song *song = NULL;
843 gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) list->data);
844 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_MPDSONG, &song, -1);
846 info2_activate();
847 info2_fill_song_view(song);
850 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
851 g_list_free(list);
855 static void pl3_current_playlist_browser_selected(GmpcPluginBrowserIface * obj, GtkContainer * container)
857 PlayQueuePlugin *self = (PlayQueuePlugin *) obj;
858 gboolean init = FALSE;
859 if (self->priv->pl3_cp_vbox == NULL)
861 pl3_current_playlist_browser_init((PlayQueuePlugin *) obj);
862 init = TRUE;
864 gtk_container_add(GTK_CONTAINER(container), self->priv->pl3_cp_vbox);
865 gtk_widget_show(self->priv->pl3_cp_vbox);
867 gtk_widget_grab_focus(self->priv->pl3_cp_tree);
869 if (init && cfg_get_single_value_as_int_with_default(config, "playlist", "st_cur_song", 0))
871 pl3_current_playlist_browser_scroll_to_current_song(self);
872 pl3_current_playlist_browser_select_current_song(self);
876 static void pl3_current_playlist_browser_unselected(GmpcPluginBrowserIface * obj, GtkContainer * container)
878 PlayQueuePlugin *self = (PlayQueuePlugin *) obj;
879 gtk_container_remove(GTK_CONTAINER(container), self->priv->pl3_cp_vbox);
881 static void pl3_current_playlist_add_after_current_song(GtkWidget *item, GtkTreeView *tree)
883 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
884 int cur_song_pos = mpd_player_get_current_song_pos(connection);
885 GList *list, *list_iter;
886 GtkTreeModel *model;
887 list = gtk_tree_selection_get_selected_rows(selection, &model);
888 if(list)
890 for(list_iter = g_list_first(list); list_iter != NULL; list_iter = g_list_next(list_iter))
892 GtkTreeIter iter;
893 GtkTreePath *path = (GtkTreePath *)list_iter->data;
894 /* Convert the path into an actual iter we can use to get values from the model */
895 if(gtk_tree_model_get_iter(model, &iter,path))
897 mpd_Song*song = NULL;
898 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_MPDSONG, &song, -1);
899 if(song != NULL && song->file != NULL)
901 int songid = mpd_playlist_add_get_id(connection, (gchar *) song->file);
902 if(cur_song_pos >= 0)
904 cur_song_pos++;
905 mpd_playlist_move_id(connection, songid, cur_song_pos);
910 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
911 g_list_free (list);
915 static int pl3_current_playlist_song_list_option_menu (GmpcPluginBrowserIface *obj, GtkWidget *tree, GtkMenu *menu)
917 printf("list option menu\n");
918 if(mpd_check_connected(connection) && !mpd_player_get_random(connection))
920 GtkWidget *item = gtk_image_menu_item_new_with_label(_("Add after current song"));
921 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
922 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_add_after_current_song), tree);
923 return 1;
925 return 0;
928 static int pl3_current_playlist_browser_option_menu(GmpcPluginBrowserIface * obj, GtkMenu * menu)
930 /* here we have: Save, Clear */
931 GtkWidget *item;
933 /* add the save widget */
934 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_SAVE, NULL);
935 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
936 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_save_playlist), NULL);
938 /* add the clear widget */
939 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLEAR, NULL);
940 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
941 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_browser_clear_playlist), NULL);
943 return 1;
946 static int pl3_current_playlist_tool_menu_integration(GmpcPluginToolMenuIface * obj, GtkMenu * menu)
948 GtkWidget *item;
949 item = gtk_image_menu_item_new_with_label(_("Add URL"));
950 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
951 gtk_image_new_from_icon_name("add-url", GTK_ICON_SIZE_MENU));
952 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
953 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(url_start), NULL);
955 return 1;
958 static int pl3_current_playlist_browser_key_press_event(GtkTreeView * tree, GdkEventKey * event,
959 PlayQueuePlugin * self)
961 if (event->keyval == GDK_KEY_Delete)
963 pl3_current_playlist_browser_delete_selected_songs(self);
964 return TRUE;
965 } else if (event->keyval == GDK_KEY_i && event->state & GDK_MOD1_MASK)
967 pl3_current_playlist_browser_show_info(self);
968 return TRUE;
969 } else if (event->state&GDK_MOD1_MASK && event->keyval == GDK_KEY_q)
971 if(mpd_server_check_command_allowed(connection, "prioid") == MPD_SERVER_COMMAND_ALLOWED)
973 pl3_current_playlist_browser_priority_raise_selected_songs(self);
975 } else if (event->state&GDK_MOD1_MASK && event->keyval == GDK_KEY_d)
977 if(mpd_server_check_command_allowed(connection, "prioid") == MPD_SERVER_COMMAND_ALLOWED)
979 pl3_current_playlist_browser_priority_remove_selected_songs(self);
981 } else if (event->keyval == GDK_KEY_space)
983 pl3_current_playlist_browser_scroll_to_current_song(self);
984 pl3_current_playlist_browser_select_current_song(self);
985 return TRUE;
986 } else if (event->keyval == GDK_KEY_f && event->state & GDK_CONTROL_MASK)
988 mod_fill_entry_changed(self->priv->filter_entry, self);
989 gtk_widget_grab_focus(self->priv->filter_entry);
990 self->priv->search_keep_open = TRUE;
991 return TRUE;
992 } else if ((event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) == 0) /*&&
993 ((event->keyval >= GDK_space && event->keyval <= GDK_z))) */
995 char data[10];
996 guint32 uc = gdk_keyval_to_unicode(event->keyval);
997 if (uc && g_unichar_isalnum(uc))
999 memset(data, '\0', 10);
1000 g_unichar_to_utf8(uc, data);
1001 gtk_widget_grab_focus(self->priv->filter_entry);
1002 gtk_entry_set_text(GTK_ENTRY(self->priv->filter_entry), data);
1003 gtk_editable_set_position(GTK_EDITABLE(self->priv->filter_entry), 1);
1005 return TRUE;
1008 return pl3_window_key_press_event(GTK_WIDGET(tree), event);
1011 /* create a dialog that allows the user to save the current playlist */
1012 static void pl3_current_playlist_save_playlist(void)
1014 gchar *str;
1015 GtkBuilder *xml = NULL;
1016 int run = TRUE;
1017 /* check if the connection is up */
1018 if (!mpd_check_connected(connection))
1020 return;
1022 /* create the interface */
1023 str = gmpc_get_full_glade_path("playlist-save-dialog.ui");
1024 xml = gtk_builder_new();
1025 gtk_builder_add_from_file(xml, str, NULL);
1026 q_free(str);
1028 /* run the interface */
1031 switch (gtk_dialog_run(GTK_DIALOG(gtk_builder_get_object(xml, "save_pl"))))
1033 case GTK_RESPONSE_OK:
1034 run = FALSE;
1035 /* if the users agrees do the following: */
1036 /* get the song-name */
1037 str = (gchar *) gtk_entry_get_text(GTK_ENTRY((GtkWidget *) gtk_builder_get_object(xml, "pl-entry")));
1038 /* check if the user entered a name, we can't do withouth */
1039 /* TODO: disable ok button when nothing is entered */
1040 /* also check if there is a connection */
1041 if (strlen(str) != 0 && mpd_check_connected(connection))
1043 int retv = mpd_database_save_playlist(connection, str);
1044 if (retv == MPD_DATABASE_PLAYLIST_EXIST)
1046 gchar *errormsg =
1047 g_markup_printf_escaped(_("<i>Playlist <b>\"%s\"</b> already exists\nOverwrite?</i>"), str);
1048 gtk_label_set_markup(GTK_LABEL((GtkWidget *) gtk_builder_get_object(xml, "label_error")), errormsg);
1049 gtk_widget_show((GtkWidget *) gtk_builder_get_object(xml, "hbox5"));
1050 /* ask to replace */
1051 gtk_widget_set_sensitive(GTK_WIDGET((GtkWidget *) gtk_builder_get_object(xml, "pl-entry")), FALSE);
1052 switch (gtk_dialog_run(GTK_DIALOG((GtkWidget *) gtk_builder_get_object(xml, "save_pl"))))
1054 case GTK_RESPONSE_OK:
1055 run = FALSE;
1056 mpd_database_delete_playlist(connection, str);
1057 mpd_database_save_playlist(connection, str);
1058 GmpcStatusChangedCallback(connection, MPD_CST_DATABASE, NULL);
1059 break;
1060 default:
1061 run = TRUE;
1063 /* return to stare */
1064 gtk_widget_set_sensitive(GTK_WIDGET((GtkWidget *) gtk_builder_get_object(xml, "pl-entry")), TRUE);
1065 gtk_widget_hide((GtkWidget *) gtk_builder_get_object(xml, "hbox5"));
1067 q_free(errormsg);
1068 } else if (retv != MPD_OK)
1070 playlist3_show_error_message(_("Failed to save the playlist file."), ERROR_WARNING);
1071 } else
1073 GmpcStatusChangedCallback(connection, MPD_CST_DATABASE, NULL);
1076 break;
1077 default:
1078 run = FALSE;
1080 } while (run);
1081 /* destroy the window */
1082 gtk_widget_destroy((GtkWidget *) gtk_builder_get_object(xml, "save_pl"));
1084 /* unref the gui description */
1085 g_object_unref(xml);
1088 static void pl3_current_playlist_browser_clear_playlist(void)
1090 GtkWidget *delete;
1091 if(cfg_get_single_value_as_int(config, "playlist","no-confirm-clear") != 1)
1094 delete = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
1095 g_signal_connect(G_OBJECT(delete),
1096 "clicked",
1097 G_CALLBACK(pl3_current_playlist_browser_clear_playlist_real),
1098 NULL);
1100 /* show message */
1101 playlist3_message_show(pl3_messages,
1102 _("Are you sure you want to clear the play queue?")
1103 ,USER_FEEDBACK);
1104 playlist3_message_add_widget(pl3_messages, delete);
1105 gtk_widget_grab_focus(GTK_WIDGET(delete));
1107 } else {
1108 pl3_current_playlist_browser_clear_playlist_real();
1112 static void pl3_current_playlist_browser_shuffle_playlist(void)
1114 mpd_playlist_shuffle(connection);
1117 static void pl3_current_playlist_status_changed(GmpcConnection * conn, MpdObj * mi, ChangedStatusType what,
1118 PlayQueuePlugin * self)
1120 if (self->priv->pl3_cp_vbox == NULL)
1121 return;
1122 if (what & MPD_CST_PLAYLIST)
1125 if (self->priv->quick_search)
1126 mod_fill_do_entry_changed(self);
1130 static void pl3_current_playlist_browser_activate(PlayQueuePlugin * self)
1132 GtkTreeSelection *selec = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtk_builder_get_object(pl3_xml, "cat_tree")));
1134 GtkTreePath *path = gtk_tree_row_reference_get_path(self->priv->pl3_curb_tree_ref);
1135 if (path)
1137 gtk_tree_selection_select_path(selec, path);
1138 gtk_tree_path_free(path);
1140 gtk_widget_grab_focus(self->priv->pl3_cp_tree);
1143 static void pl3_current_playlist_do_playlist_search(GmpcPluginBase * self)
1145 pl3_find2_select_plugin_id(self->id);
1148 static int pl3_current_playlist_browser_add_go_menu(GmpcPluginBrowserIface * obj, GtkMenu * menu)
1150 GtkWidget *item = NULL;
1152 item = gtk_image_menu_item_new_with_label(_("Play Queue"));
1153 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
1154 gtk_image_new_from_icon_name("playlist-browser", GTK_ICON_SIZE_MENU));
1155 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1156 gtk_widget_add_accelerator(GTK_WIDGET(item), "activate", gtk_menu_get_accel_group(GTK_MENU(menu)), GDK_KEY_F1, 0,
1157 GTK_ACCEL_VISIBLE);
1158 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_browser_activate), obj);
1160 item = gtk_image_menu_item_new_with_label(_("Search Playlist"));
1161 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
1162 gtk_image_new_from_icon_name("gtk-find", GTK_ICON_SIZE_MENU));
1163 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1164 gtk_widget_add_accelerator(GTK_WIDGET(item), "activate", gtk_menu_get_accel_group(GTK_MENU(menu)), GDK_KEY_j,
1165 GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
1166 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_do_playlist_search), obj);
1167 return 2;
1170 static void pl3_current_playlist_connection_changed(GmpcConnection * conn, MpdObj * mi, int connect,
1171 PlayQueuePlugin * self)
1173 if (!connect && self->priv->filter_entry)
1175 gtk_entry_set_text(GTK_ENTRY(self->priv->filter_entry), "");
1179 /* function that saves the settings */
1180 static void pl3_current_playlist_save_myself(GmpcPluginBase * obj)
1182 PlayQueuePlugin *self = (PlayQueuePlugin *) obj;
1183 if (self->priv->pl3_curb_tree_ref)
1185 GtkTreePath *path = gtk_tree_row_reference_get_path(self->priv->pl3_curb_tree_ref);
1186 if (path)
1188 gint *indices = gtk_tree_path_get_indices(path);
1189 cfg_set_single_value_as_int(config, "current-playlist", "position", indices[0]);
1190 gtk_tree_path_free(path);
1196 * GmpcPlugin
1198 GType play_queue_plugin_get_type(void);
1200 static int *play_queue_plugin_get_version(GmpcPluginBase * plug, int *length)
1202 static int version[3] = { 0, 0, 1 };
1203 if (length)
1204 *length = 3;
1205 return (int *)version;
1208 static const char *play_queue_plugin_get_name(GmpcPluginBase * plug)
1210 return N_("Play Queue");
1213 static void play_queue_plugin_finalize(GObject * obj)
1215 PlayQueuePlugin *self = (PlayQueuePlugin *) obj;
1216 PlayQueuePluginClass *klass = (g_type_class_peek(play_queue_plugin_get_type()));
1217 gpointer parent_class = g_type_class_peek_parent(klass);
1218 pl3_current_playlist_destroy((PlayQueuePlugin *) obj);
1220 if (self->priv)
1222 if (self->priv->uid)
1224 g_free(self->priv->uid);
1225 self->priv->uid = NULL;
1227 if (self->priv->mod_fill)
1229 g_object_unref(self->priv->mod_fill);
1230 self->priv->mod_fill = NULL;
1232 if (g_signal_handler_is_connected(G_OBJECT(gmpcconn), self->priv->status_changed_handler))
1233 g_signal_handler_disconnect(G_OBJECT(gmpcconn), self->priv->status_changed_handler);
1235 if (g_signal_handler_is_connected(G_OBJECT(gmpcconn), self->priv->connection_changed_handler))
1236 g_signal_handler_disconnect(G_OBJECT(gmpcconn), self->priv->connection_changed_handler);
1237 g_free(self->priv);
1238 self->priv = NULL;
1240 if (parent_class)
1241 G_OBJECT_CLASS(parent_class)->finalize(obj);
1244 static GObject *play_queue_plugin_constructor(GType type, guint n_construct_properties,
1245 GObjectConstructParam * construct_properties)
1247 PlayQueuePluginClass *klass;
1248 PlayQueuePlugin *self;
1249 GObjectClass *parent_class;
1250 klass = (g_type_class_peek(play_queue_plugin_get_type()));
1251 parent_class = G_OBJECT_CLASS(g_type_class_peek_parent(klass));
1252 self = (PlayQueuePlugin *) parent_class->constructor(type, n_construct_properties, construct_properties);
1254 /* setup private structure */
1255 self->priv = g_malloc0(sizeof(PlayQueuePluginPrivate));
1256 self->priv->status_changed_handler =
1257 g_signal_connect_object(G_OBJECT(gmpcconn), "status-changed", G_CALLBACK(pl3_current_playlist_status_changed),
1258 self, 0);
1259 self->priv->connection_changed_handler =
1260 g_signal_connect_object(G_OBJECT(gmpcconn), "connection-changed",
1261 G_CALLBACK(pl3_current_playlist_connection_changed), self, 0);
1262 /* quick search */
1263 self->priv->search_keep_open = FALSE;
1264 self->priv->filter_entry = NULL;
1265 self->priv->mod_fill = NULL;
1266 self->priv->quick_search = 0;
1267 self->priv->quick_search_timeout = 0;
1268 /* other widgets */
1269 self->priv->pl3_cp_tree = NULL;
1270 self->priv->pl3_cp_vbox = NULL;
1271 self->priv->pl3_curb_tree_ref = NULL;
1273 /* Make it an internal plugin */
1274 GMPC_PLUGIN_BASE(self)->plugin_type = GMPC_PLUGIN_PL_BROWSER | GMPC_INTERNALL;
1275 pl3_cp_init(self);
1277 return G_OBJECT(self);
1281 * Base GmpcPluginBase class
1283 static void play_queue_plugin_class_init(PlayQueuePluginClass * klass)
1285 /* Connect destroy and construct */
1286 G_OBJECT_CLASS(klass)->finalize = play_queue_plugin_finalize;
1287 G_OBJECT_CLASS(klass)->constructor = play_queue_plugin_constructor;
1288 G_OBJECT_CLASS(klass)->get_property = play_queue_get_property;
1289 G_OBJECT_CLASS(klass)->set_property = play_queue_set_property;
1290 /* Connect plugin functions */
1291 GMPC_PLUGIN_BASE_CLASS(klass)->get_version = play_queue_plugin_get_version;
1292 GMPC_PLUGIN_BASE_CLASS(klass)->get_name = play_queue_plugin_get_name;
1294 GMPC_PLUGIN_BASE_CLASS(klass)->save_yourself = pl3_current_playlist_save_myself;
1296 g_object_class_install_property(G_OBJECT_CLASS(klass), PLAY_QUEUE_UID,
1297 g_param_spec_string("uid", "uid", "stores an unique id", "play-queue",
1298 G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB
1299 | G_PARAM_READABLE | G_PARAM_WRITABLE));
1304 * Browser interface
1306 static void play_queue_browser_iface_init(GmpcPluginBrowserIfaceIface * iface)
1308 iface->browser_add = pl3_current_playlist_browser_add;
1309 iface->browser_selected = pl3_current_playlist_browser_selected;
1310 iface->browser_unselected = pl3_current_playlist_browser_unselected;
1311 iface->browser_option_menu = pl3_current_playlist_browser_option_menu;
1312 iface->browser_add_go_menu = pl3_current_playlist_browser_add_go_menu;
1315 static void play_queue_song_list_iface_init(GmpcPluginSongListIfaceIface *iface)
1317 iface->song_list = pl3_current_playlist_song_list_option_menu;
1321 * Tool menu interface
1323 static void play_queue_plugin_tool_menu_iface_init(GmpcPluginToolMenuIfaceIface * iface)
1325 iface->tool_menu_integration = pl3_current_playlist_tool_menu_integration;
1328 /* Integrate Search */
1329 static gboolean play_queue_plugin_is_field_supported(GmpcPluginIntegrateSearchIface * obj, mpd_TagItems tag)
1331 if (tag == MPD_TAG_NUM_OF_ITEM_TYPES)
1332 return TRUE;
1333 return mpd_server_tag_supported(connection, tag);
1336 static MpdData *play_queue_plugin_is_search(GmpcPluginIntegrateSearchIface * obj, mpd_TagItems num_field,
1337 const gchar * search_string)
1339 MpdData *data_t = NULL;
1340 if (num_field == MPD_TAG_NUM_OF_ITEM_TYPES)
1342 data_t = advanced_search(search_string, TRUE);
1343 } else
1345 gchar **splitted = tokenize_string(search_string);
1346 int i = 0;
1347 gboolean found = FALSE;
1348 for (i = 0; splitted && splitted[i]; i++)
1350 if (!found)
1352 mpd_playlist_search_start(connection, FALSE);
1353 found = TRUE;
1355 mpd_playlist_search_add_constraint(connection, num_field, splitted[i]);
1357 if (splitted)
1358 g_strfreev(splitted);
1359 if (found)
1361 data_t = mpd_playlist_search_commit(connection);
1364 return data_t;
1367 static void play_queue_plugin_is_iface_init(GmpcPluginIntegrateSearchIfaceIface * iface)
1369 iface->field_supported = play_queue_plugin_is_field_supported;
1370 iface->search = play_queue_plugin_is_search;
1373 GType play_queue_plugin_get_type(void)
1375 static GType play_queue_plugin_type_id = 0;
1376 if (play_queue_plugin_type_id == 0)
1378 static const GTypeInfo info = {
1379 .class_size = sizeof(PlayQueuePluginClass),
1380 .class_init = (GClassInitFunc) play_queue_plugin_class_init,
1381 .instance_size = sizeof(PlayQueuePlugin),
1382 .n_preallocs = 0
1384 static const GInterfaceInfo iface_info = {
1385 (GInterfaceInitFunc) play_queue_browser_iface_init,
1386 (GInterfaceFinalizeFunc) NULL, NULL
1388 static const GInterfaceInfo iface_song_list = {
1389 (GInterfaceInitFunc) play_queue_song_list_iface_init,
1390 (GInterfaceFinalizeFunc) NULL, NULL
1392 static const GInterfaceInfo iface_tm_info = {
1393 (GInterfaceInitFunc) play_queue_plugin_tool_menu_iface_init,
1394 (GInterfaceFinalizeFunc) NULL, NULL
1397 static const GInterfaceInfo iface_is_info = {
1398 (GInterfaceInitFunc) play_queue_plugin_is_iface_init,
1399 (GInterfaceFinalizeFunc) NULL, NULL
1401 play_queue_plugin_type_id = g_type_register_static(GMPC_PLUGIN_TYPE_BASE, "PlayQueuePlugin", &info, 0);
1403 g_type_add_interface_static(play_queue_plugin_type_id, GMPC_PLUGIN_TYPE_BROWSER_IFACE, &iface_info);
1404 g_type_add_interface_static(play_queue_plugin_type_id, GMPC_PLUGIN_TYPE_TOOL_MENU_IFACE, &iface_tm_info);
1405 g_type_add_interface_static(play_queue_plugin_type_id, GMPC_PLUGIN_TYPE_INTEGRATE_SEARCH_IFACE, &iface_is_info);
1406 g_type_add_interface_static(play_queue_plugin_type_id, GMPC_PLUGIN_TYPE_SONG_LIST_IFACE, &iface_song_list);
1408 return play_queue_plugin_type_id;
1411 PlayQueuePlugin *play_queue_plugin_new(const gchar * uid)
1413 PlayQueuePlugin *plug = g_object_new(play_queue_plugin_get_type(), "uid", uid, NULL);
1415 return plug;
1420 * Priority.
1423 static void pl3_current_playlist_browser_priority_raise_selected_songs(PlayQueuePlugin * self)
1425 GList *list = NULL, *llist = NULL;
1426 GtkTreeModel *model = NULL;
1428 /* grab the selection from the tree */
1429 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
1431 int sel_rows = gtk_tree_selection_count_selected_rows(selection);
1433 if(sel_rows >= 254) {
1434 playlist3_show_error_message(_("You can only queue 254 songs at the time."),
1435 ERROR_WARNING);
1436 return ;
1438 else if (sel_rows == 0)
1440 return;
1443 /* start a command list */
1444 /* grab the selected songs */
1445 list = gtk_tree_selection_get_selected_rows(selection, &model);
1446 /* grab the last song that is selected */
1447 /* remove every selected song one by one */
1448 if (list)
1450 int priority = 255;
1451 llist = g_list_first(list);
1452 while(llist)
1455 GtkTreeIter iter;
1456 if(gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) llist->data))
1458 int id;
1459 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_SONG_ID, &id, -1);
1460 mpd_playlist_set_priority(connection, id, priority--);
1462 llist = g_list_next(llist);
1465 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
1466 g_list_free(list);
1469 static void pl3_current_playlist_browser_priority_remove_selected_songs(PlayQueuePlugin * self)
1471 GList *list = NULL, *llist = NULL;
1472 GtkTreeModel *model = NULL;
1473 /* grab the selection from the tree */
1474 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
1476 int sel_rows = gtk_tree_selection_count_selected_rows(selection);
1478 if (sel_rows == 0)
1480 return;
1483 /* start a command list */
1484 /* grab the selected songs */
1485 list = gtk_tree_selection_get_selected_rows(selection, &model);
1486 /* grab the last song that is selected */
1487 /* remove every selected song one by one */
1488 if (list)
1490 llist = g_list_first(list);
1491 while(llist)
1494 GtkTreeIter iter;
1495 if(gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) llist->data))
1497 int id;
1498 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_SONG_ID, &id, -1);
1499 mpd_playlist_set_priority(connection, id, 0);
1501 llist = g_list_next(llist);
1504 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
1505 g_list_free(list);