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 #include "../../deadbeef.h"
25 #include <gtk/gtk.h>
26 #include <stdlib.h>
27 #include <ctype.h>
28 #include <string.h>
29 #include "gtkui.h"
30 #include "ddblistview.h"
31 #include "progress.h"
32 #include "support.h"
33 
34 //void
35 //gtkpl_add_dir (DdbListview *ps, char *folder) {
36 //    ddb_playlist_t *plt = deadbeef->plt_get_curr ();
37 //    gtkui_original_plt_add_dir (plt, folder, gtkui_add_file_info_cb, NULL);
38 //    deadbeef->plt_unref (plt);
39 //    g_free (folder);
40 //}
41 
42 static void
gtkpl_adddir_cb(gpointer data,gpointer userdata)43 gtkpl_adddir_cb (gpointer data, gpointer userdata) {
44     ddb_playlist_t *plt = deadbeef->plt_get_curr ();
45     deadbeef->plt_add_dir2 (0, plt, data, NULL, NULL);
46     deadbeef->plt_unref (plt);
47     g_free (data);
48 }
49 
50 void
gtkpl_add_dirs(GSList * lst)51 gtkpl_add_dirs (GSList *lst) {
52     ddb_playlist_t *plt = deadbeef->plt_get_curr ();
53     int empty = 0 == deadbeef->plt_get_item_count (plt, PL_MAIN);
54     if (deadbeef->plt_add_files_begin (plt, 0) < 0) {
55         deadbeef->plt_unref (plt);
56         g_slist_free (lst);
57         return;
58     }
59     deadbeef->pl_lock ();
60     if (g_slist_length (lst) == 1
61             && deadbeef->conf_get_int ("gtkui.name_playlist_from_folder", 1)) {
62         char t[1000];
63         if (!deadbeef->plt_get_title (plt, t, sizeof (t))) {
64             char *def = _("New Playlist");
65             if (!strncmp (t, def, strlen (def)) || empty) {
66                 const char *folder = strrchr ((char*)lst->data, '/');
67                 if (!folder) {
68                     folder = lst->data;
69                 }
70                 deadbeef->plt_set_title (plt, folder+1);
71             }
72         }
73     }
74     deadbeef->pl_unlock ();
75     g_slist_foreach(lst, gtkpl_adddir_cb, NULL);
76     g_slist_free (lst);
77     deadbeef->plt_add_files_end (plt, 0);
78     deadbeef->plt_unref (plt);
79 }
80 
81 static void
gtkpl_addfile_cb(gpointer data,gpointer userdata)82 gtkpl_addfile_cb (gpointer data, gpointer userdata) {
83     ddb_playlist_t *plt = deadbeef->plt_get_curr ();
84     deadbeef->plt_add_file2 (0, plt, data, NULL, 0);
85     deadbeef->plt_unref (plt);
86     g_free (data);
87 }
88 
89 void
gtkpl_add_files(GSList * lst)90 gtkpl_add_files (GSList *lst) {
91     ddb_playlist_t *plt = deadbeef->plt_get_curr ();
92     if (deadbeef->plt_add_files_begin (plt, 0) < 0) {
93         g_slist_free (lst);
94         deadbeef->plt_unref (plt);
95         return;
96     }
97     g_slist_foreach(lst, gtkpl_addfile_cb, NULL);
98     g_slist_free (lst);
99     deadbeef->plt_add_files_end (plt, 0);
100     deadbeef->plt_save_config (plt);
101     deadbeef->plt_unref (plt);
102     deadbeef->conf_save ();
103 }
104 
105 static void
add_dirs_worker(void * data)106 add_dirs_worker (void *data) {
107     GSList *lst = (GSList *)data;
108     gtkpl_add_dirs (lst);
109     deadbeef->pl_save_current ();
110     deadbeef->conf_save ();
111 }
112 
113 void
gtkui_add_dirs(GSList * lst)114 gtkui_add_dirs (GSList *lst) {
115     intptr_t tid = deadbeef->thread_start (add_dirs_worker, lst);
116     deadbeef->thread_detach (tid);
117 }
118 
119 static void
add_files_worker(void * data)120 add_files_worker (void *data) {
121     GSList *lst = (GSList *)data;
122     gtkpl_add_files (lst);
123 }
124 
125 void
gtkui_add_files(struct _GSList * lst)126 gtkui_add_files (struct _GSList *lst) {
127     intptr_t tid = deadbeef->thread_start (add_files_worker, lst);
128     deadbeef->thread_detach (tid);
129 }
130 
131 static void
open_files_worker(void * data)132 open_files_worker (void *data) {
133     GSList *lst = (GSList *)data;
134     gtkpl_add_files (lst);
135     deadbeef->pl_save_current ();
136     deadbeef->pl_set_cursor (PL_MAIN, 0);
137     deadbeef->conf_save ();
138     deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, DDB_PLAYLIST_CHANGE_CONTENT, 0);
139     deadbeef->sendmessage (DB_EV_PLAY_NUM, 0, 0, 0);
140 }
141 
142 void
gtkui_open_files(struct _GSList * lst)143 gtkui_open_files (struct _GSList *lst) {
144     deadbeef->pl_clear ();
145     deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, DDB_PLAYLIST_CHANGE_CONTENT, 0);
146 
147     intptr_t tid = deadbeef->thread_start (open_files_worker, lst);
148     deadbeef->thread_detach (tid);
149 }
150 
151 void
strcopy_special(char * dest,const char * src,int len)152 strcopy_special (char *dest, const char *src, int len) {
153     while (len > 0) {
154         if (*src == '%' && len >= 3) {
155             int charcode = 0;
156             int byte;
157             byte = tolower (src[2]);
158             if (byte >= '0' && byte <= '9') {
159                 charcode = byte - '0';
160             }
161             else if (byte >= 'a' && byte <= 'f') {
162                 charcode = byte - 'a' + 10;
163             }
164             else {
165                 charcode = '?';
166             }
167             if (charcode != '?') {
168                 byte = tolower (src[1]);
169                 if (byte >= '0' && byte <= '9') {
170                     charcode |= (byte - '0') << 4;
171                 }
172                 else if (byte >= 'a' && byte <= 'f') {
173                     charcode |= (byte - 'a' + 10) << 4;
174                 }
175                 else {
176                     charcode = '?';
177                 }
178             }
179             *dest = charcode;
180             dest++;
181             src += 3;
182             len -= 3;
183             continue;
184         }
185         else {
186             *dest++ = *src++;
187             len--;
188         }
189     }
190     *dest = 0;
191 }
192 
193 static gboolean
set_dnd_cursor_idle(gpointer data)194 set_dnd_cursor_idle (gpointer data) {
195     if (!data) {
196         deadbeef->pl_set_cursor (PL_MAIN, -1);
197         deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, DDB_PLAYLIST_CHANGE_CONTENT, 0);
198         return FALSE;
199     }
200     int cursor = deadbeef->pl_get_idx_of (DB_PLAYITEM (data));
201     deadbeef->pl_set_cursor (PL_MAIN, cursor);
202     deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, DDB_PLAYLIST_CHANGE_CONTENT, 0);
203     return FALSE;
204 }
205 
206 void
gtkpl_add_fm_dropped_files(DB_playItem_t * drop_before,char * ptr,int length)207 gtkpl_add_fm_dropped_files (DB_playItem_t *drop_before, char *ptr, int length) {
208     ddb_playlist_t *plt = deadbeef->plt_get_curr ();
209     if (deadbeef->plt_add_files_begin (plt, 0) < 0) {
210         free (ptr);
211         deadbeef->plt_unref (plt);
212         return;
213     }
214 
215     DdbListviewIter first = NULL;
216     DdbListviewIter after = NULL;
217     if (drop_before) {
218         after = deadbeef->pl_get_prev (drop_before, PL_MAIN);
219     }
220     else {
221         after = deadbeef->pl_get_last (PL_MAIN);
222     }
223     const uint8_t *p = (const uint8_t*)ptr;
224     while (*p) {
225         const uint8_t *pe = p;
226         while (*pe && *pe > ' ') {
227             pe++;
228         }
229         if (pe - p < 4096 && pe - p > 7) {
230             char fname[(int)(pe - p)+1];
231             strcopy_special (fname, p, pe-p);
232             //strncpy (fname, p, pe - p);
233             //fname[pe - p] = 0;
234             int abort = 0;
235             DdbListviewIter inserted = deadbeef->plt_insert_dir2 (0, plt, after, fname, &abort, NULL, NULL);
236             if (!inserted && !abort) {
237                 inserted = deadbeef->plt_insert_file2 (0, plt, after, fname, &abort, NULL, NULL);
238                 if (!inserted && !abort) {
239                     inserted = deadbeef->plt_load2 (0, plt, after, fname, &abort, NULL, NULL);
240                 }
241             }
242             if (inserted) {
243                 if (!first) {
244                     first = inserted;
245                 }
246                 if (after) {
247                     deadbeef->pl_item_unref (after);
248                 }
249                 after = inserted;
250                 deadbeef->pl_item_ref (after);
251             }
252         }
253         p = pe;
254         // skip whitespace
255         while (*p && *p <= ' ') {
256             p++;
257         }
258     }
259     if (after) {
260         deadbeef->pl_item_unref (after);
261     }
262     free (ptr);
263 
264     deadbeef->plt_add_files_end (plt, 0);
265     deadbeef->plt_save_config (plt);
266     deadbeef->plt_unref (plt);
267     g_idle_add (set_dnd_cursor_idle, first);
268 }
269 
270 struct fmdrop_data {
271     char *mem;
272     int length;
273     DB_playItem_t *drop_before;
274 };
275 
276 static void
fmdrop_worker(void * ctx)277 fmdrop_worker (void *ctx) {
278     struct fmdrop_data *data = (struct fmdrop_data *)ctx;
279     gtkpl_add_fm_dropped_files (data->drop_before, data->mem, data->length);
280     if (data->drop_before) {
281         deadbeef->pl_item_unref (data->drop_before);
282     }
283     free (data);
284 }
285 
286 void
gtkui_receive_fm_drop(DB_playItem_t * before,char * mem,int length)287 gtkui_receive_fm_drop (DB_playItem_t *before, char *mem, int length) {
288     struct fmdrop_data *data = malloc (sizeof (struct fmdrop_data));
289     if (!data) {
290         fprintf (stderr, "gtkui_receive_fm_drop: malloc failed\n");
291         return;
292     }
293     data->mem = mem;
294     data->length = length;
295     if (before) {
296         deadbeef->pl_item_ref (before);
297     }
298     data->drop_before = before;
299     // since it happens in separate thread, we need to addref
300     intptr_t tid = deadbeef->thread_start (fmdrop_worker, data);
301     deadbeef->thread_detach (tid);
302 }
303