PlayQueue: Close the search entry on escape
[gmpc.git] / src / browsers / playlist3-current-playlist-browser.c
blob6464246bd9054822df7a56e4569127526361839b
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 "advanced-search.h"
34 #include "playlist3-messages.h"
36 #include "playlist3-playlist-editor.h"
41 enum
43 PLAY_QUEUE_DUMMY_PROPERTY,
44 PLAY_QUEUE_UID
46 /**
47 * Private data structure
49 typedef struct _PlayQueuePluginPrivate
51 gulong status_changed_handler;
52 gulong connection_changed_handler;
54 /* Quick Search */
55 gboolean search_keep_open;
56 GtkWidget *filter_entry;
57 GtkTreeModel *mod_fill;
58 gboolean quick_search;
59 guint quick_search_timeout;
60 /* Other widgets */
61 GtkWidget *pl3_cp_tree;
62 GtkWidget *pl3_cp_vbox;
63 /* reference to the row */
64 GtkTreeRowReference *pl3_curb_tree_ref;
65 /* Gchar *uid */
66 gchar *uid;
67 } _PlayQueuePluginPrivate;
69 /**
70 * Propperty setter/getter
72 static void play_queue_get_property(GObject * object, guint property_id, GValue * value, GParamSpec * pspec)
74 PlayQueuePlugin *self;
75 self = (PlayQueuePlugin *) (object);
76 switch (property_id)
78 case PLAY_QUEUE_UID:
79 g_value_set_string(value, self->priv->uid);
80 break;
81 default:
82 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
83 break;
88 static void play_queue_set_property(GObject * object, guint property_id, const GValue * value, GParamSpec * pspec)
90 PlayQueuePlugin *self;
91 self = (PlayQueuePlugin *) (object);
92 switch (property_id)
94 case PLAY_QUEUE_UID:
95 if (self->priv->uid)
96 g_free(self->priv->uid);
97 self->priv->uid = g_value_dup_string(value);
98 break;
99 default:
100 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
101 break;
105 static int pl3_current_playlist_browser_key_press_event(GtkTreeView * tree, GdkEventKey * event,
106 PlayQueuePlugin * self);
108 static void pl3_current_playlist_browser_add(GmpcPluginBrowserIface * obj, GtkWidget * cat_tree);
110 static void pl3_current_playlist_browser_selected(GmpcPluginBrowserIface * obj, GtkContainer * container);
111 static void pl3_current_playlist_browser_unselected(GmpcPluginBrowserIface * obj, GtkContainer * container);
113 static void pl3_current_playlist_browser_activate(PlayQueuePlugin * self);
115 /* just for here */
116 static void pl3_current_playlist_save_playlist(void);
117 static void pl3_current_playlist_browser_shuffle_playlist(void);
118 static void pl3_current_playlist_browser_clear_playlist(void);
120 static void pl3_current_playlist_browser_init(PlayQueuePlugin * self);
122 static void pl3_current_playlist_browser_clear_playlist_real(void)
124 mpd_playlist_clear(connection);
126 static void pl3_cp_current_song_changed(GmpcMpdDataModelPlaylist * model2, GtkTreePath * path, GtkTreeIter * iter,
127 PlayQueuePlugin * self)
129 GtkTreeModel *model;
130 if (self->priv->pl3_cp_tree == NULL)
131 return;
132 model = gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
133 if (GMPC_IS_MPDDATA_MODEL_PLAYLIST(model))
135 if (cfg_get_single_value_as_int_with_default(config, "playlist", "st_cur_song", 0))
137 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(self->priv->pl3_cp_tree), path, NULL, TRUE, 0.5, 0);
142 static void pl3_current_playlist_browser_crop_current_song(PlayQueuePlugin * self, const gchar * param)
144 mpd_Song *song = mpd_playlist_get_current_song(connection);
145 if (song)
147 int length = mpd_playlist_get_playlist_length(connection);
148 /* Move to first place, this avoid possible "issues" */
149 mpd_playlist_move_id(connection, song->id, 0);
150 for (; length > 1; length--)
152 mpd_playlist_queue_delete_pos(connection, length - 1);
153 if ((length & 16383) == 16383)
155 mpd_playlist_queue_commit(connection);
158 mpd_playlist_queue_commit(connection);
162 static void pl3_cp_ec_playlist(PlayQueuePlugin * self, const gchar * param)
164 pl3_find2_select_plugin_id(GMPC_PLUGIN_BASE(self)->id);
165 pl3_find2_do_search_any(param);
168 static void pl3_cp_init(PlayQueuePlugin * self)
170 g_signal_connect(G_OBJECT(playlist), "current_song_changed", G_CALLBACK(pl3_cp_current_song_changed), self);
172 gmpc_easy_command_add_entry(gmpc_easy_command,
173 _("switch play queue"), "",
174 _("Switch to play queue"),
175 (GmpcEasyCommandCallback *) pl3_current_playlist_browser_activate, self);
176 gmpc_easy_command_add_entry(gmpc_easy_command,
177 _("Clear"), "",
178 _("Clear play queue"),
179 (GmpcEasyCommandCallback *) pl3_current_playlist_browser_clear_playlist_real, self);
181 gmpc_easy_command_add_entry(gmpc_easy_command,
182 _("Crop current song"), "",
183 _("Crop the playlist so it only contains the current song"),
184 (GmpcEasyCommandCallback *) pl3_current_playlist_browser_crop_current_song, self);
186 gmpc_easy_command_add_entry(gmpc_easy_command,
187 _("search playlist"), ".*",
188 _("Search playlist <query>"),
189 (GmpcEasyCommandCallback *) pl3_cp_ec_playlist, self);
192 void pl3_current_playlist_destroy(PlayQueuePlugin * self);
194 /* internal */
196 static void pl3_current_playlist_column_changed(GtkTreeView * tree)
198 int position = 0;
199 GList *iter, *cols = gtk_tree_view_get_columns(tree);
200 for (iter = cols; iter; iter = g_list_next(iter))
202 gpointer data = g_object_get_data(G_OBJECT(iter->data), "colid");
203 int colid = GPOINTER_TO_INT(data);
204 char *string = g_strdup_printf("%i", position);
205 cfg_set_single_value_as_int(config, "current-playlist-column-pos", string, colid);
206 position++;
207 q_free(string);
209 g_list_free(cols);
212 void pl3_current_playlist_destroy(PlayQueuePlugin * self)
214 /* destroy the entry */
215 if (self->priv->pl3_curb_tree_ref)
217 GtkTreeIter piter;
218 GtkTreePath *path;
219 path = gtk_tree_row_reference_get_path(self->priv->pl3_curb_tree_ref);
220 if (path)
222 if (gtk_tree_model_get_iter
223 (GTK_TREE_MODEL(gtk_tree_row_reference_get_model(self->priv->pl3_curb_tree_ref)), &piter, path))
225 gtk_list_store_remove(GTK_LIST_STORE(gtk_tree_row_reference_get_model(self->priv->pl3_curb_tree_ref)),
226 &piter);
228 gtk_tree_path_free(path);
230 gtk_tree_row_reference_free(self->priv->pl3_curb_tree_ref);
231 self->priv->pl3_curb_tree_ref = NULL;
233 /* destroy the browser */
234 if (self->priv->pl3_cp_vbox)
236 /* remove the signal handler so when the widget is destroyed, the numbering of the labels are not changed again */
237 g_signal_handlers_disconnect_by_func(G_OBJECT(self->priv->pl3_cp_tree),
238 G_CALLBACK(pl3_current_playlist_column_changed), NULL);
239 g_object_unref(self->priv->pl3_cp_vbox);
240 self->priv->pl3_cp_vbox = NULL;
242 self->priv->pl3_cp_tree = NULL;
245 static gboolean mod_fill_do_entry_changed(PlayQueuePlugin * self)
247 const gchar *text2 = gtk_entry_get_text(GTK_ENTRY(self->priv->filter_entry));
249 if (strlen(text2) > 0)
252 MpdData *data = NULL;
253 data = advanced_search(text2, TRUE);
254 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(self->priv->mod_fill), data);
255 self->priv->quick_search = TRUE;
256 gtk_tree_view_set_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree), self->priv->mod_fill);
257 gtk_widget_show(self->priv->filter_entry);
258 } else
260 gtk_tree_view_set_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree), playlist);
262 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(self->priv->mod_fill), NULL);
263 if (!self->priv->search_keep_open)
265 self->priv->quick_search = 0;
266 gtk_widget_hide(self->priv->filter_entry);
267 gtk_widget_grab_focus(self->priv->pl3_cp_tree);
270 self->priv->quick_search_timeout = 0;
271 return FALSE;
274 static gboolean mod_fill_entry_key_press_event(GtkWidget * entry, GdkEventKey * event, PlayQueuePlugin * self)
276 const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry));
278 if (strlen(text) == 0)
280 if (event->keyval == GDK_KEY_BackSpace || event->keyval == GDK_KEY_Escape)
282 self->priv->search_keep_open = FALSE;
283 gtk_tree_view_set_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree), playlist);
285 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(self->priv->mod_fill), NULL);
286 self->priv->quick_search = 0;
287 gtk_widget_hide(entry);
288 gtk_widget_grab_focus(self->priv->pl3_cp_tree);
289 return TRUE;
291 } else if (event->keyval == GDK_KEY_Escape)
293 self->priv->search_keep_open = FALSE;
294 gtk_entry_set_text(GTK_ENTRY(entry), "");
295 } else if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_Down)
297 gtk_widget_grab_focus(self->priv->pl3_cp_tree);
298 return TRUE;
300 return FALSE;
303 static void mod_fill_entry_changed(GtkWidget * entry, PlayQueuePlugin * self)
305 if (self->priv->quick_search_timeout != 0)
307 g_source_remove(self->priv->quick_search_timeout);
308 self->priv->quick_search_timeout = 0;
311 if (cfg_get_single_value_as_int_with_default(config, "general", "search-as-you-type", 0) == 1)
313 self->priv->quick_search_timeout = g_timeout_add(250, (GSourceFunc) mod_fill_do_entry_changed, self);
315 gtk_widget_show(entry);
318 static void mod_fill_entry_activate(GtkWidget * entry, PlayQueuePlugin * self)
320 if (self->priv->quick_search_timeout != 0)
321 g_source_remove(self->priv->quick_search_timeout);
322 mod_fill_do_entry_changed(self);
323 gtk_widget_grab_focus(self->priv->pl3_cp_tree);
326 static void mod_fill_clear_search_entry(GtkEntry * entry, GtkEntryIconPosition icon_pos, GdkEvent * event,
327 gpointer user_data)
329 if (icon_pos == GTK_ENTRY_ICON_SECONDARY)
331 gtk_entry_set_text(GTK_ENTRY(entry), "");
336 * Row activation is handled by GmpcDataView.
337 * However when in quick-search it might be good to close the search mode.
338 * Do this by clearing the entry and call the entry changed.
340 static void pl3_current_browser_row_activated(GtkTreeView *tree, GtkTreePath *path,
341 GtkTreeViewColumn *column, PlayQueuePlugin *self)
343 if(self->priv->quick_search)
345 self->priv->search_keep_open = FALSE;
346 gtk_entry_set_text(GTK_ENTRY(self->priv->filter_entry), "");
347 mod_fill_do_entry_changed(self);
351 static void pl3_current_playlist_browser_init(PlayQueuePlugin * self)
353 GtkWidget *entry = NULL, *tree = NULL, *sw = NULL, *pl3_cp_sw;
354 /* Mark the plugin internal */
355 GMPC_PLUGIN_BASE(self)->plugin_type = GMPC_INTERNALL | GMPC_PLUGIN_PL_BROWSER;
357 self->priv->pl3_cp_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
359 tree = gmpc_data_view_new(self->priv->uid,
360 GMPC_DATA_VIEW_VIEW_TYPE_PLAY_QUEUE);
361 gtk_tree_view_set_model(GTK_TREE_VIEW(tree), GTK_TREE_MODEL(playlist));
365 g_signal_connect(G_OBJECT(tree), "row-activated",
366 G_CALLBACK(pl3_current_browser_row_activated),self);
368 /* Enable this for this model only */
369 //gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(tree), gmt_targetentries, 1, GDK_ACTION_DEFAULT|GDK_ACTION_MOVE|GDK_ACTION_COPY);
371 /* filter */
372 self->priv->mod_fill = (GtkTreeModel *) gmpc_mpddata_model_new();
373 entry = gtk_entry_new();
374 gtk_entry_set_icon_from_stock(GTK_ENTRY(entry), GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
375 g_signal_connect(GTK_ENTRY(entry), "icon-press", G_CALLBACK(mod_fill_clear_search_entry), NULL);
377 gtk_entry_set_icon_from_stock(GTK_ENTRY(entry), GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND);
378 gtk_entry_set_icon_activatable(GTK_ENTRY(entry), GTK_ENTRY_ICON_PRIMARY, FALSE);
381 gtk_box_pack_end(GTK_BOX(self->priv->pl3_cp_vbox), entry, FALSE, TRUE, 0);
382 self->priv->filter_entry = entry;
383 g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(mod_fill_entry_changed), self);
384 g_signal_connect(G_OBJECT(entry), "key-press-event", G_CALLBACK(mod_fill_entry_key_press_event), self);
385 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(mod_fill_entry_activate), self);
387 sw = gtk_scrolled_window_new(NULL, NULL);
388 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
389 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
390 gtk_container_add(GTK_CONTAINER(sw), tree);
391 gtk_box_pack_start(GTK_BOX(self->priv->pl3_cp_vbox), GTK_WIDGET(sw), TRUE, TRUE, 0);
392 gtk_widget_show_all(sw);
393 /* set up the tree */
394 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(tree), FALSE);
396 /* setup signals */
397 g_signal_connect(G_OBJECT(tree), "key-press-event", G_CALLBACK(pl3_current_playlist_browser_key_press_event),
398 self);
400 /* set up the scrolled window */
401 pl3_cp_sw = gtk_scrolled_window_new(NULL, NULL);
402 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(pl3_cp_sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
403 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(pl3_cp_sw), GTK_SHADOW_ETCHED_IN);
405 /* set initial state */
406 self->priv->pl3_cp_tree = tree;
407 g_object_ref_sink(G_OBJECT(self->priv->pl3_cp_vbox));
410 static void pl3_current_playlist_browser_select_current_song(PlayQueuePlugin * self)
412 if (self->priv->pl3_cp_tree == NULL || !gtk_widget_get_realized(self->priv->pl3_cp_tree))
413 return;
414 /* scroll to the playing song */
415 if (mpd_player_get_current_song_pos(connection) >= 0 && mpd_playlist_get_playlist_length(connection) > 0)
417 GtkTreePath *path = gtk_tree_path_new_from_indices(mpd_player_get_current_song_pos(connection), -1);
418 if (path != NULL
419 && GMPC_IS_MPDDATA_MODEL_PLAYLIST(gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree))))
421 gtk_tree_view_set_cursor(GTK_TREE_VIEW(self->priv->pl3_cp_tree), path, NULL, FALSE);
423 gtk_tree_path_free(path);
427 static void pl3_current_playlist_browser_scroll_to_current_song(PlayQueuePlugin * self)
429 if (self->priv->pl3_cp_tree == NULL || !gtk_widget_get_realized(self->priv->pl3_cp_tree))
430 return;
431 /* scroll to the playing song */
432 if (mpd_player_get_current_song_pos(connection) >= 0 && mpd_playlist_get_playlist_length(connection) > 0)
434 GtkTreePath *path = gtk_tree_path_new_from_indices(mpd_player_get_current_song_pos(connection), -1);
435 if (path != NULL)
437 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(self->priv->pl3_cp_tree), path, NULL, TRUE, 0.5, 0);
438 gtk_tree_path_free(path);
444 /* add's the toplevel entry for the current playlist view */
445 static void pl3_current_playlist_browser_add(GmpcPluginBrowserIface * obj, GtkWidget * cat_tree)
447 PlayQueuePlugin *self = (PlayQueuePlugin *) obj;
448 GtkTreeIter iter;
449 GtkTreePath *path;
450 playlist3_insert_browser(&iter, PL3_CAT_BROWSER_TOP+1);
451 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, */
452 PL3_CAT_TITLE, _(gmpc_plugin_base_get_name(GMPC_PLUGIN_BASE(self))),
453 PL3_CAT_ICON_ID, "playlist-browser", -1);
454 if (self->priv->pl3_curb_tree_ref)
456 gtk_tree_row_reference_free(self->priv->pl3_curb_tree_ref);
457 self->priv->pl3_curb_tree_ref = NULL;
459 path = gtk_tree_model_get_path(GTK_TREE_MODEL(pl3_tree), &iter);
460 if (path)
462 self->priv->pl3_curb_tree_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(pl3_tree), path);
463 gtk_tree_path_free(path);
467 static void pl3_current_playlist_browser_crop_selected_songs(PlayQueuePlugin * self)
469 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
470 /* grab the selection from the tree */
471 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
473 /* see if there is a row selected */
474 if (gtk_tree_selection_count_selected_rows(selection) > 0)
476 GtkTreeIter iter;
477 /* we want to delete from back to front, so we have to transverse this list */
478 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter))
483 if (!gtk_tree_selection_iter_is_selected(selection, &iter))
485 int id = 0;
486 /* song pos starts at 1, not a 0, compensate for that */
487 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, MPDDATA_MODEL_COL_SONG_ID, &id, -1);
488 mpd_playlist_queue_delete_id(connection, id);
490 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter));
492 mpd_playlist_queue_commit(connection);
493 /* update everything if where still connected */
494 gtk_tree_selection_unselect_all(selection);
496 mpd_status_queue_update(connection);
500 static void pl3_current_playlist_editor_add_to_playlist(GtkWidget * menu, gpointer cb_data)
502 PlayQueuePlugin *self = (PlayQueuePlugin *) cb_data;
503 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
504 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->priv->pl3_cp_tree));
505 gchar *data = g_object_get_data(G_OBJECT(menu), "playlist");
506 GList *iter, *list = gtk_tree_selection_get_selected_rows(selection, &model);
507 if (list)
509 iter = g_list_first(list);
512 GtkTreeIter giter;
513 if (gtk_tree_model_get_iter(model, &giter, (GtkTreePath *) iter->data))
515 gchar *file = NULL;
516 gtk_tree_model_get(model, &giter, MPDDATA_MODEL_COL_PATH, &file, -1);
517 mpd_database_playlist_list_add(connection, data, file);
518 g_free(file);
520 } while ((iter = g_list_next(iter)));
522 g_list_foreach(list, (GFunc) gtk_tree_path_free, NULL);
523 g_list_free(list);
526 playlist_editor_fill_list();
533 static void pl3_current_playlist_browser_selected(GmpcPluginBrowserIface * obj, GtkContainer * container)
535 PlayQueuePlugin *self = (PlayQueuePlugin *) obj;
536 gboolean init = FALSE;
537 if (self->priv->pl3_cp_vbox == NULL)
539 pl3_current_playlist_browser_init((PlayQueuePlugin *) obj);
540 init = TRUE;
542 gtk_container_add(GTK_CONTAINER(container), self->priv->pl3_cp_vbox);
543 gtk_widget_show(self->priv->pl3_cp_vbox);
545 gtk_widget_grab_focus(self->priv->pl3_cp_tree);
547 if (init && cfg_get_single_value_as_int_with_default(config, "playlist", "st_cur_song", 0))
549 pl3_current_playlist_browser_scroll_to_current_song(self);
550 pl3_current_playlist_browser_select_current_song(self);
554 static void pl3_current_playlist_browser_unselected(GmpcPluginBrowserIface * obj, GtkContainer * container)
556 PlayQueuePlugin *self = (PlayQueuePlugin *) obj;
557 gtk_container_remove(GTK_CONTAINER(container), self->priv->pl3_cp_vbox);
559 static void pl3_current_playlist_add_after_current_song(GtkWidget *item, GtkTreeView *tree)
561 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
562 int cur_song_pos = mpd_player_get_current_song_pos(connection);
563 GList *list, *list_iter;
564 GtkTreeModel *model;
565 list = gtk_tree_selection_get_selected_rows(selection, &model);
566 if(list)
568 for(list_iter = g_list_first(list); list_iter != NULL; list_iter = g_list_next(list_iter))
570 GtkTreeIter iter;
571 GtkTreePath *path = (GtkTreePath *)list_iter->data;
572 /* Convert the path into an actual iter we can use to get values from the model */
573 if(gtk_tree_model_get_iter(model, &iter,path))
575 mpd_Song*song = NULL;
576 gtk_tree_model_get(model, &iter, MPDDATA_MODEL_COL_MPDSONG, &song, -1);
577 if(song != NULL && song->file != NULL)
579 int songid = mpd_playlist_add_get_id(connection, (gchar *) song->file);
580 if(cur_song_pos >= 0)
582 cur_song_pos++;
583 mpd_playlist_move_id(connection, songid, cur_song_pos);
588 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
589 g_list_free (list);
593 static int pl3_current_playlist_song_list_option_menu (GmpcPluginBrowserIface *obj, GtkWidget *tree, GtkMenu *menu)
595 printf("list option menu\n");
596 if(mpd_check_connected(connection) && !mpd_player_get_random(connection))
598 GtkWidget *item = gtk_image_menu_item_new_with_label(_("Add after current song"));
599 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
600 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_add_after_current_song), tree);
601 return 1;
603 return 0;
606 static int pl3_current_playlist_browser_option_menu(GmpcPluginBrowserIface * obj, GtkMenu * menu)
608 /* here we have: Save, Clear */
609 GtkWidget *item;
611 /* add the save widget */
612 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_SAVE, NULL);
613 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
614 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_save_playlist), NULL);
616 /* add the clear widget */
617 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLEAR, NULL);
618 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
619 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_browser_clear_playlist), NULL);
621 return 1;
624 static int pl3_current_playlist_tool_menu_integration(GmpcPluginToolMenuIface * obj, GtkMenu * menu)
626 GtkWidget *item;
627 item = gtk_image_menu_item_new_with_label(_("Add URL"));
628 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
629 gtk_image_new_from_icon_name("add-url", GTK_ICON_SIZE_MENU));
630 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
631 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(url_start), NULL);
633 return 1;
636 static int pl3_current_playlist_browser_key_press_event(GtkTreeView * tree, GdkEventKey * event,
637 PlayQueuePlugin * self)
639 if (event->keyval == GDK_KEY_slash)
641 mod_fill_entry_changed(self->priv->filter_entry, self);
642 gtk_widget_grab_focus(self->priv->filter_entry);
643 self->priv->search_keep_open = TRUE;
644 return TRUE;
646 else if (event->keyval == GDK_KEY_Escape) {
647 // Clear and close.
648 self->priv->search_keep_open = FALSE;
649 gtk_entry_set_text(GTK_ENTRY(self->priv->filter_entry), "");
650 mod_fill_do_entry_changed(self);
652 return pl3_window_key_press_event(GTK_WIDGET(tree), event);
655 /* create a dialog that allows the user to save the current playlist */
656 static void pl3_current_playlist_save_playlist(void)
658 gchar *str;
659 GtkBuilder *xml = NULL;
660 int run = TRUE;
661 /* check if the connection is up */
662 if (!mpd_check_connected(connection))
664 return;
666 /* create the interface */
667 str = gmpc_get_full_glade_path("playlist-save-dialog.ui");
668 xml = gtk_builder_new();
669 gtk_builder_add_from_file(xml, str, NULL);
670 q_free(str);
672 /* run the interface */
675 switch (gtk_dialog_run(GTK_DIALOG(gtk_builder_get_object(xml, "save_pl"))))
677 case GTK_RESPONSE_OK:
678 run = FALSE;
679 /* if the users agrees do the following: */
680 /* get the song-name */
681 str = (gchar *) gtk_entry_get_text(GTK_ENTRY((GtkWidget *) gtk_builder_get_object(xml, "pl-entry")));
682 /* check if the user entered a name, we can't do withouth */
683 /* TODO: disable ok button when nothing is entered */
684 /* also check if there is a connection */
685 if (strlen(str) != 0 && mpd_check_connected(connection))
687 int retv = mpd_database_save_playlist(connection, str);
688 if (retv == MPD_DATABASE_PLAYLIST_EXIST)
690 gchar *errormsg =
691 g_markup_printf_escaped(_("<i>Playlist <b>\"%s\"</b> already exists\nOverwrite?</i>"), str);
692 gtk_label_set_markup(GTK_LABEL((GtkWidget *) gtk_builder_get_object(xml, "label_error")), errormsg);
693 gtk_widget_show((GtkWidget *) gtk_builder_get_object(xml, "hbox5"));
694 /* ask to replace */
695 gtk_widget_set_sensitive(GTK_WIDGET((GtkWidget *) gtk_builder_get_object(xml, "pl-entry")), FALSE);
696 switch (gtk_dialog_run(GTK_DIALOG((GtkWidget *) gtk_builder_get_object(xml, "save_pl"))))
698 case GTK_RESPONSE_OK:
699 run = FALSE;
700 mpd_database_delete_playlist(connection, str);
701 mpd_database_save_playlist(connection, str);
702 GmpcStatusChangedCallback(connection, MPD_CST_DATABASE, NULL);
703 break;
704 default:
705 run = TRUE;
707 /* return to stare */
708 gtk_widget_set_sensitive(GTK_WIDGET((GtkWidget *) gtk_builder_get_object(xml, "pl-entry")), TRUE);
709 gtk_widget_hide((GtkWidget *) gtk_builder_get_object(xml, "hbox5"));
711 q_free(errormsg);
712 } else if (retv != MPD_OK)
714 playlist3_show_error_message(_("Failed to save the playlist file."), ERROR_WARNING);
715 } else
717 GmpcStatusChangedCallback(connection, MPD_CST_DATABASE, NULL);
720 break;
721 default:
722 run = FALSE;
724 } while (run);
725 /* destroy the window */
726 gtk_widget_destroy((GtkWidget *) gtk_builder_get_object(xml, "save_pl"));
728 /* unref the gui description */
729 g_object_unref(xml);
732 static void pl3_current_playlist_browser_clear_playlist(void)
734 GtkWidget *delete;
735 if(cfg_get_single_value_as_int(config, "playlist","no-confirm-clear") != 1)
738 delete = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
739 g_signal_connect(G_OBJECT(delete),
740 "clicked",
741 G_CALLBACK(pl3_current_playlist_browser_clear_playlist_real),
742 NULL);
744 /* show message */
745 playlist3_message_show(pl3_messages,
746 _("Are you sure you want to clear the play queue?")
747 ,USER_FEEDBACK);
748 playlist3_message_add_widget(pl3_messages, delete);
749 gtk_widget_grab_focus(GTK_WIDGET(delete));
751 } else {
752 pl3_current_playlist_browser_clear_playlist_real();
756 static void pl3_current_playlist_browser_shuffle_playlist(void)
758 mpd_playlist_shuffle(connection);
761 static void pl3_current_playlist_status_changed(GmpcConnection * conn, MpdObj * mi, ChangedStatusType what,
762 PlayQueuePlugin * self)
764 if (self->priv->pl3_cp_vbox == NULL)
765 return;
766 if (what & MPD_CST_PLAYLIST)
769 if (self->priv->quick_search)
770 mod_fill_do_entry_changed(self);
774 static void pl3_current_playlist_browser_activate(PlayQueuePlugin * self)
776 GtkTreeSelection *selec = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtk_builder_get_object(pl3_xml, "cat_tree")));
778 GtkTreePath *path = gtk_tree_row_reference_get_path(self->priv->pl3_curb_tree_ref);
779 if (path)
781 gtk_tree_selection_select_path(selec, path);
782 gtk_tree_path_free(path);
784 gtk_widget_grab_focus(self->priv->pl3_cp_tree);
787 static void pl3_current_playlist_do_playlist_search(GmpcPluginBase * self)
789 pl3_find2_select_plugin_id(self->id);
792 static int pl3_current_playlist_browser_add_go_menu(GmpcPluginBrowserIface * obj, GtkMenu * menu)
794 GtkWidget *item = NULL;
796 item = gtk_image_menu_item_new_with_label(_("Play Queue"));
797 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
798 gtk_image_new_from_icon_name("playlist-browser", GTK_ICON_SIZE_MENU));
799 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
800 gtk_widget_add_accelerator(GTK_WIDGET(item), "activate", gtk_menu_get_accel_group(GTK_MENU(menu)), GDK_KEY_F1, 0,
801 GTK_ACCEL_VISIBLE);
802 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_browser_activate), obj);
804 item = gtk_image_menu_item_new_with_label(_("Search Playlist"));
805 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
806 gtk_image_new_from_icon_name("gtk-find", GTK_ICON_SIZE_MENU));
807 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
808 gtk_widget_add_accelerator(GTK_WIDGET(item), "activate", gtk_menu_get_accel_group(GTK_MENU(menu)), GDK_KEY_j,
809 GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
810 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(pl3_current_playlist_do_playlist_search), obj);
811 return 2;
814 static void pl3_current_playlist_connection_changed(GmpcConnection * conn, MpdObj * mi, int connect,
815 PlayQueuePlugin * self)
817 if (!connect && self->priv->filter_entry)
819 gtk_entry_set_text(GTK_ENTRY(self->priv->filter_entry), "");
823 /* function that saves the settings */
824 static void pl3_current_playlist_save_myself(GmpcPluginBase * obj)
826 PlayQueuePlugin *self = (PlayQueuePlugin *) obj;
827 if (self->priv->pl3_curb_tree_ref)
829 GtkTreePath *path = gtk_tree_row_reference_get_path(self->priv->pl3_curb_tree_ref);
830 if (path)
832 gint *indices = gtk_tree_path_get_indices(path);
833 cfg_set_single_value_as_int(config, "current-playlist", "position", indices[0]);
834 gtk_tree_path_free(path);
840 * GmpcPlugin
842 GType play_queue_plugin_get_type(void);
844 static int *play_queue_plugin_get_version(GmpcPluginBase * plug, int *length)
846 static int version[3] = { 0, 0, 1 };
847 if (length)
848 *length = 3;
849 return (int *)version;
852 static const char *play_queue_plugin_get_name(GmpcPluginBase * plug)
854 return N_("Play Queue");
857 static void play_queue_plugin_finalize(GObject * obj)
859 PlayQueuePlugin *self = (PlayQueuePlugin *) obj;
860 PlayQueuePluginClass *klass = (g_type_class_peek(play_queue_plugin_get_type()));
861 gpointer parent_class = g_type_class_peek_parent(klass);
862 pl3_current_playlist_destroy((PlayQueuePlugin *) obj);
864 if (self->priv)
866 if (self->priv->uid)
868 g_free(self->priv->uid);
869 self->priv->uid = NULL;
871 if (self->priv->mod_fill)
873 g_object_unref(self->priv->mod_fill);
874 self->priv->mod_fill = NULL;
876 if (g_signal_handler_is_connected(G_OBJECT(gmpcconn), self->priv->status_changed_handler))
877 g_signal_handler_disconnect(G_OBJECT(gmpcconn), self->priv->status_changed_handler);
879 if (g_signal_handler_is_connected(G_OBJECT(gmpcconn), self->priv->connection_changed_handler))
880 g_signal_handler_disconnect(G_OBJECT(gmpcconn), self->priv->connection_changed_handler);
881 g_free(self->priv);
882 self->priv = NULL;
884 if (parent_class)
885 G_OBJECT_CLASS(parent_class)->finalize(obj);
888 static GObject *play_queue_plugin_constructor(GType type, guint n_construct_properties,
889 GObjectConstructParam * construct_properties)
891 PlayQueuePluginClass *klass;
892 PlayQueuePlugin *self;
893 GObjectClass *parent_class;
894 klass = (g_type_class_peek(play_queue_plugin_get_type()));
895 parent_class = G_OBJECT_CLASS(g_type_class_peek_parent(klass));
896 self = (PlayQueuePlugin *) parent_class->constructor(type, n_construct_properties, construct_properties);
898 /* setup private structure */
899 self->priv = g_malloc0(sizeof(PlayQueuePluginPrivate));
900 self->priv->status_changed_handler =
901 g_signal_connect_object(G_OBJECT(gmpcconn), "status-changed", G_CALLBACK(pl3_current_playlist_status_changed),
902 self, 0);
903 self->priv->connection_changed_handler =
904 g_signal_connect_object(G_OBJECT(gmpcconn), "connection-changed",
905 G_CALLBACK(pl3_current_playlist_connection_changed), self, 0);
906 /* quick search */
907 self->priv->search_keep_open = FALSE;
908 self->priv->filter_entry = NULL;
909 self->priv->mod_fill = NULL;
910 self->priv->quick_search = 0;
911 self->priv->quick_search_timeout = 0;
912 /* other widgets */
913 self->priv->pl3_cp_tree = NULL;
914 self->priv->pl3_cp_vbox = NULL;
915 self->priv->pl3_curb_tree_ref = NULL;
917 /* Make it an internal plugin */
918 GMPC_PLUGIN_BASE(self)->plugin_type = GMPC_PLUGIN_PL_BROWSER | GMPC_INTERNALL;
919 pl3_cp_init(self);
921 return G_OBJECT(self);
925 * Base GmpcPluginBase class
927 static void play_queue_plugin_class_init(PlayQueuePluginClass * klass)
929 /* Connect destroy and construct */
930 G_OBJECT_CLASS(klass)->finalize = play_queue_plugin_finalize;
931 G_OBJECT_CLASS(klass)->constructor = play_queue_plugin_constructor;
932 G_OBJECT_CLASS(klass)->get_property = play_queue_get_property;
933 G_OBJECT_CLASS(klass)->set_property = play_queue_set_property;
934 /* Connect plugin functions */
935 GMPC_PLUGIN_BASE_CLASS(klass)->get_version = play_queue_plugin_get_version;
936 GMPC_PLUGIN_BASE_CLASS(klass)->get_name = play_queue_plugin_get_name;
938 GMPC_PLUGIN_BASE_CLASS(klass)->save_yourself = pl3_current_playlist_save_myself;
940 g_object_class_install_property(G_OBJECT_CLASS(klass), PLAY_QUEUE_UID,
941 g_param_spec_string("uid", "uid", "stores an unique id", "play-queue",
942 G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB
943 | G_PARAM_READABLE | G_PARAM_WRITABLE));
948 * Browser interface
950 static void play_queue_browser_iface_init(GmpcPluginBrowserIfaceIface * iface)
952 iface->browser_add = pl3_current_playlist_browser_add;
953 iface->browser_selected = pl3_current_playlist_browser_selected;
954 iface->browser_unselected = pl3_current_playlist_browser_unselected;
955 iface->browser_option_menu = pl3_current_playlist_browser_option_menu;
956 iface->browser_add_go_menu = pl3_current_playlist_browser_add_go_menu;
959 static void play_queue_song_list_iface_init(GmpcPluginSongListIfaceIface *iface)
961 iface->song_list = pl3_current_playlist_song_list_option_menu;
965 * Tool menu interface
967 static void play_queue_plugin_tool_menu_iface_init(GmpcPluginToolMenuIfaceIface * iface)
969 iface->tool_menu_integration = pl3_current_playlist_tool_menu_integration;
972 /* Integrate Search */
973 static gboolean play_queue_plugin_is_field_supported(GmpcPluginIntegrateSearchIface * obj, mpd_TagItems tag)
975 if (tag == MPD_TAG_NUM_OF_ITEM_TYPES)
976 return TRUE;
977 return mpd_server_tag_supported(connection, tag);
980 static MpdData *play_queue_plugin_is_search(GmpcPluginIntegrateSearchIface * obj, mpd_TagItems num_field,
981 const gchar * search_string)
983 MpdData *data_t = NULL;
984 if (num_field == MPD_TAG_NUM_OF_ITEM_TYPES)
986 data_t = advanced_search(search_string, TRUE);
987 } else
989 gchar **splitted = tokenize_string(search_string);
990 int i = 0;
991 gboolean found = FALSE;
992 for (i = 0; splitted && splitted[i]; i++)
994 if (!found)
996 mpd_playlist_search_start(connection, FALSE);
997 found = TRUE;
999 mpd_playlist_search_add_constraint(connection, num_field, splitted[i]);
1001 if (splitted)
1002 g_strfreev(splitted);
1003 if (found)
1005 data_t = mpd_playlist_search_commit(connection);
1008 return data_t;
1011 static void play_queue_plugin_is_iface_init(GmpcPluginIntegrateSearchIfaceIface * iface)
1013 iface->field_supported = play_queue_plugin_is_field_supported;
1014 iface->search = play_queue_plugin_is_search;
1017 GType play_queue_plugin_get_type(void)
1019 static GType play_queue_plugin_type_id = 0;
1020 if (play_queue_plugin_type_id == 0)
1022 static const GTypeInfo info = {
1023 .class_size = sizeof(PlayQueuePluginClass),
1024 .class_init = (GClassInitFunc) play_queue_plugin_class_init,
1025 .instance_size = sizeof(PlayQueuePlugin),
1026 .n_preallocs = 0
1028 static const GInterfaceInfo iface_info = {
1029 (GInterfaceInitFunc) play_queue_browser_iface_init,
1030 (GInterfaceFinalizeFunc) NULL, NULL
1032 static const GInterfaceInfo iface_song_list = {
1033 (GInterfaceInitFunc) play_queue_song_list_iface_init,
1034 (GInterfaceFinalizeFunc) NULL, NULL
1036 static const GInterfaceInfo iface_tm_info = {
1037 (GInterfaceInitFunc) play_queue_plugin_tool_menu_iface_init,
1038 (GInterfaceFinalizeFunc) NULL, NULL
1041 static const GInterfaceInfo iface_is_info = {
1042 (GInterfaceInitFunc) play_queue_plugin_is_iface_init,
1043 (GInterfaceFinalizeFunc) NULL, NULL
1045 play_queue_plugin_type_id = g_type_register_static(GMPC_PLUGIN_TYPE_BASE, "PlayQueuePlugin", &info, 0);
1047 g_type_add_interface_static(play_queue_plugin_type_id, GMPC_PLUGIN_TYPE_BROWSER_IFACE, &iface_info);
1048 g_type_add_interface_static(play_queue_plugin_type_id, GMPC_PLUGIN_TYPE_TOOL_MENU_IFACE, &iface_tm_info);
1049 g_type_add_interface_static(play_queue_plugin_type_id, GMPC_PLUGIN_TYPE_INTEGRATE_SEARCH_IFACE, &iface_is_info);
1050 g_type_add_interface_static(play_queue_plugin_type_id, GMPC_PLUGIN_TYPE_SONG_LIST_IFACE, &iface_song_list);
1052 return play_queue_plugin_type_id;
1055 PlayQueuePlugin *play_queue_plugin_new(const gchar * uid)
1057 PlayQueuePlugin *plug = g_object_new(play_queue_plugin_get_type(), "uid", uid, NULL);
1059 return plug;