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.
21 #include <gdk/gdkkeysyms.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"
43 PLAY_QUEUE_DUMMY_PROPERTY
,
47 * Private data structure
49 typedef struct _PlayQueuePluginPrivate
51 gulong status_changed_handler
;
52 gulong connection_changed_handler
;
55 gboolean search_keep_open
;
56 GtkWidget
*filter_entry
;
57 GtkTreeModel
*mod_fill
;
58 gboolean quick_search
;
59 guint quick_search_timeout
;
61 GtkWidget
*pl3_cp_tree
;
62 GtkWidget
*pl3_cp_vbox
;
63 /* reference to the row */
64 GtkTreeRowReference
*pl3_curb_tree_ref
;
67 } _PlayQueuePluginPrivate
;
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
);
79 g_value_set_string(value
, self
->priv
->uid
);
82 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, property_id
, pspec
);
88 static void play_queue_set_property(GObject
* object
, guint property_id
, const GValue
* value
, GParamSpec
* pspec
)
90 PlayQueuePlugin
*self
;
91 self
= (PlayQueuePlugin
*) (object
);
96 g_free(self
->priv
->uid
);
97 self
->priv
->uid
= g_value_dup_string(value
);
100 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, property_id
, pspec
);
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
);
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
)
130 if (self
->priv
->pl3_cp_tree
== NULL
)
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
);
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
,
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
);
196 static void pl3_current_playlist_column_changed(GtkTreeView
* tree
)
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
);
212 void pl3_current_playlist_destroy(PlayQueuePlugin
* self
)
214 /* destroy the entry */
215 if (self
->priv
->pl3_curb_tree_ref
)
219 path
= gtk_tree_row_reference_get_path(self
->priv
->pl3_curb_tree_ref
);
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
)),
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
);
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;
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
);
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
);
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
,
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);
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
);
397 g_signal_connect(G_OBJECT(tree
), "key-press-event", G_CALLBACK(pl3_current_playlist_browser_key_press_event
),
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
))
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);
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
))
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);
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
;
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
);
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)
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
))
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
);
509 iter
= g_list_first(list
);
513 if (gtk_tree_model_get_iter(model
, &giter
, (GtkTreePath
*) iter
->data
))
516 gtk_tree_model_get(model
, &giter
, MPDDATA_MODEL_COL_PATH
, &file
, -1);
517 mpd_database_playlist_list_add(connection
, data
, file
);
520 } while ((iter
= g_list_next(iter
)));
522 g_list_foreach(list
, (GFunc
) gtk_tree_path_free
, NULL
);
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
);
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
;
565 list
= gtk_tree_selection_get_selected_rows(selection
, &model
);
568 for(list_iter
= g_list_first(list
); list_iter
!= NULL
; list_iter
= g_list_next(list_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)
583 mpd_playlist_move_id(connection
, songid
, cur_song_pos
);
588 g_list_foreach (list
, (GFunc
) gtk_tree_path_free
, NULL
);
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
);
606 static int pl3_current_playlist_browser_option_menu(GmpcPluginBrowserIface
* obj
, GtkMenu
* menu
)
608 /* here we have: Save, Clear */
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
);
624 static int pl3_current_playlist_tool_menu_integration(GmpcPluginToolMenuIface
* obj
, GtkMenu
* menu
)
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
);
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
;
646 else if (event
->keyval
== GDK_KEY_Escape
) {
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)
659 GtkBuilder
*xml
= NULL
;
661 /* check if the connection is up */
662 if (!mpd_check_connected(connection
))
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
);
672 /* run the interface */
675 switch (gtk_dialog_run(GTK_DIALOG(gtk_builder_get_object(xml
, "save_pl"))))
677 case GTK_RESPONSE_OK
:
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
)
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"));
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
:
700 mpd_database_delete_playlist(connection
, str
);
701 mpd_database_save_playlist(connection
, str
);
702 GmpcStatusChangedCallback(connection
, MPD_CST_DATABASE
, NULL
);
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"));
712 } else if (retv
!= MPD_OK
)
714 playlist3_show_error_message(_("Failed to save the playlist file."), ERROR_WARNING
);
717 GmpcStatusChangedCallback(connection
, MPD_CST_DATABASE
, NULL
);
725 /* destroy the window */
726 gtk_widget_destroy((GtkWidget
*) gtk_builder_get_object(xml
, "save_pl"));
728 /* unref the gui description */
732 static void pl3_current_playlist_browser_clear_playlist(void)
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),
741 G_CALLBACK(pl3_current_playlist_browser_clear_playlist_real
),
745 playlist3_message_show(pl3_messages
,
746 _("Are you sure you want to clear the play queue?")
748 playlist3_message_add_widget(pl3_messages
, delete);
749 gtk_widget_grab_focus(GTK_WIDGET(delete));
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
)
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
);
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,
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
);
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
);
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
);
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 };
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
);
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
);
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
),
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);
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;
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
;
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
));
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
)
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
);
989 gchar
**splitted
= tokenize_string(search_string
);
991 gboolean found
= FALSE
;
992 for (i
= 0; splitted
&& splitted
[i
]; i
++)
996 mpd_playlist_search_start(connection
, FALSE
);
999 mpd_playlist_search_add_constraint(connection
, num_field
, splitted
[i
]);
1002 g_strfreev(splitted
);
1005 data_t
= mpd_playlist_search_commit(connection
);
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
),
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
);