1 /*
2 * Playlist Manager Plugin for Audacious
3 * Copyright 2006-2014 Giacomo Lozito, John Lindgren, and Thomas Lange
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions, and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions, and the following disclaimer in the documentation
13 * provided with the distribution.
14 *
15 * This software is provided "as is" and without any warranty, express or
16 * implied. In no event shall the authors be liable for any damages arising from
17 * the use of this software.
18 */
19
20 #include <libaudcore/audstrings.h>
21 #include <libaudcore/hook.h>
22 #include <libaudcore/i18n.h>
23 #include <libaudcore/playlist.h>
24 #include <libaudcore/plugin.h>
25 #include <libaudcore/runtime.h>
26
27 #include <libaudgui/libaudgui.h>
28 #include <libaudgui/libaudgui-gtk.h>
29 #include <libaudgui/list.h>
30
31 #include <string.h>
32
33 class PlaylistManager : public GeneralPlugin
34 {
35 public:
36 static constexpr PluginInfo info = {
37 N_("Playlist Manager"),
38 PACKAGE,
39 nullptr, // about
40 nullptr, // prefs
41 PluginGLibOnly
42 };
43
PlaylistManager()44 constexpr PlaylistManager () : GeneralPlugin (info, false) {}
45
46 void * get_gtk_widget ();
47 int take_message (const char * code, const void * data, int size);
48 };
49
50 EXPORT PlaylistManager aud_plugin_instance;
51
52 static GtkWidget * focus_widget;
53
54 static void activate_row (void * user, int row);
55
rename_cb(void * unused)56 static void rename_cb (void * unused)
57 {
58 audgui_show_playlist_rename (Playlist::active_playlist ());
59 }
60
delete_cb(void * unused)61 static void delete_cb (void * unused)
62 {
63 audgui_confirm_playlist_delete (Playlist::active_playlist ());
64 }
65
get_value(void * user,int row,int column,GValue * value)66 static void get_value (void * user, int row, int column, GValue * value)
67 {
68 auto list = Playlist::by_index (row);
69
70 switch (column)
71 {
72 case 0:
73 g_value_set_string (value, list.get_title ());
74 break;
75
76 case 1:
77 g_value_set_int (value, list.n_entries ());
78 break;
79 }
80 }
81
get_selected(void * user,int row)82 static bool get_selected (void * user, int row)
83 {
84 return (row == Playlist::active_playlist ().index ());
85 }
86
set_selected(void * user,int row,bool selected)87 static void set_selected (void * user, int row, bool selected)
88 {
89 if (selected)
90 Playlist::by_index (row).activate ();
91 }
92
select_all(void * user,bool selected)93 static void select_all (void * user, bool selected)
94 {
95 }
96
activate_row(void * user,int row)97 static void activate_row (void * user, int row)
98 {
99 auto playlist = Playlist::by_index (row);
100 playlist.activate ();
101 playlist.start_playback ();
102 }
103
shift_rows(void * user,int row,int before)104 static void shift_rows (void * user, int row, int before)
105 {
106 if (before < row)
107 Playlist::reorder_playlists (row, before, 1);
108 else if (before - 1 > row)
109 Playlist::reorder_playlists (row, before - 1, 1);
110 }
111
112 static const AudguiListCallbacks callbacks = {
113 get_value,
114 get_selected,
115 set_selected,
116 select_all,
117 activate_row,
118 nullptr, // right_click
119 shift_rows
120 };
121
search_cb(GtkTreeModel * model,int column,const char * key,GtkTreeIter * iter,void * user)122 static gboolean search_cb (GtkTreeModel * model, int column, const char * key,
123 GtkTreeIter * iter, void * user)
124 {
125 GtkTreePath * path = gtk_tree_model_get_path (model, iter);
126 g_return_val_if_fail (path, true);
127 int row = gtk_tree_path_get_indices (path)[0];
128 gtk_tree_path_free (path);
129
130 String title = Playlist::by_index (row).get_title ();
131 g_return_val_if_fail (title, true);
132
133 Index<String> keys = str_list_to_index (key, " ");
134
135 if (! keys.len ())
136 return true; /* not matched */
137
138 for (const String & key : keys)
139 {
140 if (! strstr_nocase_utf8 (title, key))
141 return true; /* not matched */
142 }
143
144 return false; /* matched */
145 }
146
update_hook(void * data,void * list_)147 static void update_hook (void * data, void * list_)
148 {
149 auto level = aud::from_ptr<Playlist::UpdateLevel> (data);
150 GtkWidget * list = (GtkWidget *) list_;
151 int rows = Playlist::n_playlists ();
152
153 if (level == Playlist::Structure)
154 {
155 int old_rows = audgui_list_row_count (list);
156
157 if (rows < old_rows)
158 audgui_list_delete_rows (list, rows, old_rows - rows);
159 else if (rows > old_rows)
160 audgui_list_insert_rows (list, old_rows, rows - old_rows);
161
162 audgui_list_set_focus (list, Playlist::active_playlist ().index ());
163 audgui_list_set_highlight (list, Playlist::playing_playlist ().index ());
164 audgui_list_update_selection (list, 0, rows);
165 }
166
167 if (level >= Playlist::Metadata)
168 audgui_list_update_rows (list, 0, rows);
169 }
170
activate_hook(void * data,void * list_)171 static void activate_hook (void * data, void * list_)
172 {
173 GtkWidget * list = (GtkWidget *) list_;
174 audgui_list_set_focus (list, Playlist::active_playlist ().index ());
175 audgui_list_update_selection (list, 0, Playlist::n_playlists ());
176 }
177
position_hook(void * data,void * list_)178 static void position_hook (void * data, void * list_)
179 {
180 GtkWidget * list = (GtkWidget *) list_;
181 audgui_list_set_highlight (list, Playlist::playing_playlist ().index ());
182 }
183
destroy_cb(GtkWidget * window)184 static void destroy_cb (GtkWidget * window)
185 {
186 hook_dissociate ("playlist update", update_hook);
187 hook_dissociate ("playlist activate", activate_hook);
188 hook_dissociate ("playlist set playing", position_hook);
189
190 focus_widget = nullptr;
191 }
192
get_gtk_widget()193 void * PlaylistManager::get_gtk_widget ()
194 {
195 GtkWidget * playman_vbox = gtk_vbox_new (false, 6);
196
197 /* ListView */
198 GtkWidget * playman_pl_lv = audgui_list_new (& callbacks, nullptr, Playlist::n_playlists ());
199 audgui_list_add_column (playman_pl_lv, _("Title"), 0, G_TYPE_STRING, -1);
200 audgui_list_add_column (playman_pl_lv, _("Entries"), 1, G_TYPE_INT, 7);
201 audgui_list_set_highlight (playman_pl_lv, Playlist::playing_playlist ().index ());
202 gtk_tree_view_set_search_equal_func ((GtkTreeView *) playman_pl_lv,
203 search_cb, nullptr, nullptr);
204 hook_associate ("playlist update", update_hook, playman_pl_lv);
205 hook_associate ("playlist activate", activate_hook, playman_pl_lv);
206 hook_associate ("playlist set playing", position_hook, playman_pl_lv);
207
208 GtkWidget * playman_pl_lv_sw = gtk_scrolled_window_new (nullptr, nullptr);
209 gtk_scrolled_window_set_shadow_type ((GtkScrolledWindow *) playman_pl_lv_sw,
210 GTK_SHADOW_IN);
211 gtk_scrolled_window_set_policy ((GtkScrolledWindow *) playman_pl_lv_sw,
212 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
213 gtk_container_add ((GtkContainer *) playman_pl_lv_sw, playman_pl_lv);
214 gtk_box_pack_start ((GtkBox *) playman_vbox, playman_pl_lv_sw, true, true, 0);
215
216 /* ButtonBox */
217 GtkWidget * playman_button_hbox = gtk_hbox_new (false, 6);
218 GtkWidget * new_button = audgui_button_new (_("_New"), "document-new",
219 [] (void *) { Playlist::new_playlist (); }, nullptr);
220 GtkWidget * delete_button = audgui_button_new (_("_Remove"), "edit-delete", delete_cb, nullptr);
221 GtkWidget * rename_button = audgui_button_new (_("Ren_ame"), "insert-text", rename_cb, nullptr);
222
223 gtk_box_pack_start ((GtkBox *) playman_button_hbox, new_button, false, false, 0);
224 gtk_box_pack_start ((GtkBox *) playman_button_hbox, delete_button, false, false, 0);
225 gtk_box_pack_end ((GtkBox *) playman_button_hbox, rename_button, false, false, 0);
226 gtk_box_pack_start ((GtkBox *) playman_vbox, playman_button_hbox, false, false, 0);
227
228 focus_widget = playman_pl_lv;
229
230 g_signal_connect (playman_vbox, "destroy", (GCallback) destroy_cb, nullptr);
231
232 return playman_vbox;
233 }
234
take_message(const char * code,const void * data,int size)235 int PlaylistManager::take_message (const char * code, const void * data, int size)
236 {
237 if (! strcmp (code, "grab focus"))
238 {
239 if (focus_widget)
240 gtk_widget_grab_focus (focus_widget);
241
242 return 0;
243 }
244
245 return -1;
246 }
247