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