1 /*
2 DeaDBeeF -- the music player
3 Copyright (C) 2009-2015 Alexey Yakovenko and other contributors
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17
18 2. Altered source versions must be plainly marked as such, and must not be
19 misrepresented as being the original software.
20
21 3. This notice may not be removed or altered from any source distribution.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
27 #include <stdlib.h>
28 #include <string.h>
29 #include "../../gettext.h"
30 #include "../libparser/parser.h"
31 #include "gtkui.h"
32 #include "ddblistview.h"
33 #include "mainplaylist.h"
34 #include "search.h"
35 #include "interface.h"
36 #include "support.h"
37 #include "drawing.h"
38 #include "trkproperties.h"
39 #include "coverart.h"
40 #include "plcommon.h"
41
42 //#define trace(...) { fprintf(stderr, __VA_ARGS__); }
43 #define trace(fmt,...)
44
45 #define min(x,y) ((x)<(y)?(x):(y))
46
47 static int
main_get_count(void)48 main_get_count (void) {
49 return deadbeef->pl_getcount (PL_MAIN);
50 }
51
52 static int
main_get_sel_count(void)53 main_get_sel_count (void) {
54 return deadbeef->pl_getselcount ();
55 }
56
57 static int
main_get_cursor(void)58 main_get_cursor (void) {
59 return deadbeef->pl_get_cursor (PL_MAIN);
60 }
61
62 static void
main_set_cursor(int cursor)63 main_set_cursor (int cursor) {
64 return deadbeef->pl_set_cursor (PL_MAIN, cursor);
65 }
66
main_head(void)67 static DdbListviewIter main_head (void) {
68 return (DdbListviewIter)deadbeef->pl_get_first (PL_MAIN);
69 }
70
main_tail(void)71 static DdbListviewIter main_tail (void) {
72 return (DdbListviewIter)deadbeef->pl_get_last(PL_MAIN);
73 }
74
main_next(DdbListviewIter it)75 static DdbListviewIter main_next (DdbListviewIter it) {
76 return (DdbListviewIter)deadbeef->pl_get_next(it, PL_MAIN);
77 }
78
main_prev(DdbListviewIter it)79 static DdbListviewIter main_prev (DdbListviewIter it) {
80 return (DdbListviewIter)deadbeef->pl_get_prev(it, PL_MAIN);
81 }
82
main_get_for_idx(int idx)83 static DdbListviewIter main_get_for_idx (int idx) {
84 return deadbeef->pl_get_for_idx_and_iter (idx, PL_MAIN);
85 }
86
main_get_idx(DdbListviewIter it)87 int main_get_idx (DdbListviewIter it) {
88 DB_playItem_t *c = deadbeef->pl_get_first (PL_MAIN);
89 int idx = 0;
90 while (c && c != it) {
91 DB_playItem_t *next = deadbeef->pl_get_next (c, PL_MAIN);
92 deadbeef->pl_item_unref (c);
93 c = next;
94 idx++;
95 }
96 if (!c) {
97 return -1;
98 }
99 deadbeef->pl_item_unref (c);
100 return idx;
101 }
102
103 void
main_drag_n_drop(DdbListviewIter before,DdbPlaylistHandle from_playlist,uint32_t * indices,int length,int copy)104 main_drag_n_drop (DdbListviewIter before, DdbPlaylistHandle from_playlist, uint32_t *indices, int length, int copy) {
105 deadbeef->pl_lock ();
106 ddb_playlist_t *plt = deadbeef->plt_get_curr ();
107 if (copy) {
108 deadbeef->plt_copy_items (plt, PL_MAIN, (ddb_playlist_t *)from_playlist, (DB_playItem_t *)before, indices, length);
109 }
110 else {
111 deadbeef->plt_move_items (plt, PL_MAIN, (ddb_playlist_t *)from_playlist, (DB_playItem_t *)before, indices, length);
112 }
113 if (!copy && from_playlist != plt) {
114 deadbeef->plt_save_config (from_playlist);
115 }
116 deadbeef->plt_save_config (plt);
117 deadbeef->plt_unref (plt);
118 deadbeef->pl_unlock ();
119 }
120
main_external_drag_n_drop(DdbListviewIter before,char * mem,int length)121 void main_external_drag_n_drop (DdbListviewIter before, char *mem, int length) {
122 gtkui_receive_fm_drop ((DB_playItem_t *)before, mem, length);
123 }
124
125 gboolean
playlist_tooltip_handler(GtkWidget * widget,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip,gpointer unused)126 playlist_tooltip_handler (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer unused)
127 {
128 DdbListview *pl = DDB_LISTVIEW (g_object_get_data (G_OBJECT (widget), "owner"));
129 DB_playItem_t *it = (DB_playItem_t *)ddb_listview_get_iter_from_coord (pl, 0, y);
130 if (it) {
131 deadbeef->pl_lock ();
132 gtk_tooltip_set_text (tooltip, deadbeef->pl_find_meta (it, ":URI"));
133 deadbeef->pl_unlock ();
134 deadbeef->pl_item_unref (it);
135 return TRUE;
136 }
137 return FALSE;
138 }
139
140 // columns
141
142 void
main_col_sort(int col,int sort_order,void * user_data)143 main_col_sort (int col, int sort_order, void *user_data) {
144 col_info_t *c = (col_info_t*)user_data;
145 ddb_playlist_t *plt = deadbeef->plt_get_curr ();
146 deadbeef->plt_sort_v2 (plt, PL_MAIN, c->id, c->format, sort_order-1);
147 deadbeef->plt_unref (plt);
148 }
main_handle_doubleclick(DdbListview * listview,DdbListviewIter iter,int idx)149 void main_handle_doubleclick (DdbListview *listview, DdbListviewIter iter, int idx) {
150 deadbeef->sendmessage (DB_EV_PLAY_NUM, 0, idx, 0);
151 }
152
main_selection_changed(DdbListview * ps,DdbListviewIter it,int idx)153 void main_selection_changed (DdbListview *ps, DdbListviewIter it, int idx) {
154 DdbListview *search = DDB_LISTVIEW (lookup_widget (searchwin, "searchlist"));
155 if (idx == -1) {
156 ddb_listview_refresh (search, DDB_REFRESH_LIST);
157 }
158 else {
159 ddb_listview_draw_row (search, search_get_idx ((DB_playItem_t *)it), it);
160 }
161 deadbeef->sendmessage (DB_EV_SELCHANGED, (uintptr_t)ps, deadbeef->plt_get_curr_idx (), PL_MAIN);
162 }
163
164 void
main_delete_selected(void)165 main_delete_selected (void) {
166 deadbeef->pl_delete_selected ();
167 deadbeef->pl_save_current();
168 main_refresh ();
169 search_refresh ();
170 }
171
172 void
main_select(DdbListviewIter it,int sel)173 main_select (DdbListviewIter it, int sel) {
174 deadbeef->pl_set_selected ((DB_playItem_t *)it, sel);
175 }
176
177 int
main_is_selected(DdbListviewIter it)178 main_is_selected (DdbListviewIter it) {
179 return deadbeef->pl_is_selected ((DB_playItem_t *)it);
180 }
181
182 void
main_groups_changed(DdbListview * listview,const char * format)183 main_groups_changed (DdbListview *listview, const char* format) {
184 if (!format) {
185 return;
186 }
187 if (listview->group_format) {
188 free (listview->group_format);
189 }
190 if (listview->group_title_bytecode) {
191 free (listview->group_title_bytecode);
192 listview->group_title_bytecode = NULL;
193 }
194 deadbeef->conf_set_str ("gtkui.playlist.group_by_tf", format);
195 listview->group_format = strdup (format);
196 listview->group_title_bytecode = deadbeef->tf_compile (listview->group_format);
197 }
198
199 static int lock_column_config = 0;
200
201 void
main_columns_changed(DdbListview * listview)202 main_columns_changed (DdbListview *listview) {
203 if (!lock_column_config) {
204 rewrite_column_config (listview, "gtkui.columns.playlist");
205 }
206 }
207
main_col_free_user_data(void * data)208 void main_col_free_user_data (void *data) {
209 if (data) {
210 col_info_t *inf = data;
211 if (inf->format) {
212 free (inf->format);
213 }
214 if (inf->bytecode) {
215 free (inf->bytecode);
216 }
217 free (data);
218 }
219 }
220
221 void
main_vscroll_changed(int pos)222 main_vscroll_changed (int pos) {
223 coverart_reset_queue ();
224 ddb_playlist_t *plt = deadbeef->plt_get_curr ();
225 if (plt) {
226 deadbeef->plt_set_scroll (plt, pos);
227 deadbeef->plt_unref (plt);
228 }
229 }
230
231 void
main_header_context_menu(DdbListview * ps,int column)232 main_header_context_menu (DdbListview *ps, int column) {
233 GtkWidget *menu = create_headermenu (1);
234 set_last_playlist_cm (ps); // playlist ptr for context menu
235 set_active_column_cm (column);
236 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, ps, 3, gtk_get_current_event_time());
237 }
238
239 DdbListviewBinding main_binding = {
240 // rows
241 .count = main_get_count,
242 .sel_count = main_get_sel_count,
243
244 .cursor = main_get_cursor,
245 .set_cursor = main_set_cursor,
246
247 .head = main_head,
248 .tail = main_tail,
249 .next = main_next,
250 .prev = main_prev,
251
252 .get_for_idx = main_get_for_idx,
253 .get_idx = main_get_idx,
254
255 .is_selected = main_is_selected,
256 .select = main_select,
257
258 .get_group = pl_common_get_group,
259 .groups_changed = main_groups_changed,
260
261 .drag_n_drop = main_drag_n_drop,
262 .external_drag_n_drop = main_external_drag_n_drop,
263
264 .draw_column_data = draw_column_data,
265 .draw_album_art = draw_album_art,
266 .draw_group_title = pl_common_draw_group_title,
267
268 // columns
269 .col_sort = main_col_sort,
270 .columns_changed = main_columns_changed,
271 .col_free_user_data = main_col_free_user_data,
272
273 // callbacks
274 .handle_doubleclick = main_handle_doubleclick,
275 .selection_changed = main_selection_changed,
276 .header_context_menu = main_header_context_menu,
277 .list_context_menu = list_context_menu,
278 .delete_selected = main_delete_selected,
279 .vscroll_changed = main_vscroll_changed,
280 .modification_idx = gtkui_get_curr_playlist_mod,
281 };
282
283 void
main_playlist_init(GtkWidget * widget)284 main_playlist_init (GtkWidget *widget) {
285 // make listview widget and bind it to data
286 DdbListview *listview = DDB_LISTVIEW(widget);
287 main_binding.ref = (void (*) (DdbListviewIter))deadbeef->pl_item_ref;
288 main_binding.unref = (void (*) (DdbListviewIter))deadbeef->pl_item_unref;
289 ddb_listview_set_binding (listview, &main_binding);
290 lock_column_config = 1;
291 deadbeef->conf_lock ();
292 if (!deadbeef->conf_get_str_fast ("gtkui.columns.playlist", NULL)) {
293 import_column_config_0_6 ("playlist.column.", "gtkui.columns.playlist");
294 }
295 deadbeef->conf_unlock ();
296 if (load_column_config (listview, "gtkui.columns.playlist") < 0) {
297 // create default set of columns
298 add_column_helper (listview, "♫", 50, DB_COLUMN_PLAYING, "%playstatus%", 0);
299 add_column_helper (listview, _("Artist / Album"), 150, -1, "%artist% - %album%", 0);
300 add_column_helper (listview, _("Track No"), 50, -1, "%tracknumber%", 1);
301 add_column_helper (listview, _("Title"), 150, -1, "%title%", 0);
302 add_column_helper (listview, _("Duration"), 50, -1, "%length%", 0);
303 }
304 lock_column_config = 0;
305
306 deadbeef->conf_lock ();
307 listview->group_format = strdup (deadbeef->conf_get_str_fast ("gtkui.playlist.group_by_tf", ""));
308 deadbeef->conf_unlock ();
309 listview->group_title_bytecode = deadbeef->tf_compile (listview->group_format);
310
311 // FIXME: filepath should be in properties dialog, while tooltip should be
312 // used to show text that doesn't fit in column width
313 if (deadbeef->conf_get_int ("listview.showpathtooltip", 0)) {
314 GValue value = {0, };
315 g_value_init (&value, G_TYPE_BOOLEAN);
316 g_value_set_boolean (&value, TRUE);
317 DdbListview *pl = DDB_LISTVIEW (widget);
318 g_object_set_property (G_OBJECT (pl->list), "has-tooltip", &value);
319 g_signal_connect (G_OBJECT (pl->list), "query-tooltip", G_CALLBACK (playlist_tooltip_handler), NULL);
320 }
321 }
322
323 void
main_refresh(void)324 main_refresh (void) {
325 deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, DDB_PLAYLIST_CHANGE_CONTENT, 0);
326 }
327
328