1 /* MikMod module player
2 (c) 1998 - 2000 Miodrag Vallat and others - see file AUTHORS for
3 complete list.
4
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.
9
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.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18 02111-1307, USA.
19 */
20
21 /*==============================================================================
22
23 $Id: mlistedit.c,v 1.1.1.1 2004/01/16 02:07:43 raph Exp $
24
25 The playlist editor
26
27 ==============================================================================*/
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36
37 #include <stdlib.h>
38 #include <stdarg.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43
44 #if defined(__MINGW32__) || defined(__EMX__) || defined(__DJGPP__)
45 #include <dirent.h>
46 #elif defined(__OS2__) /* Watcom */
47 #include <direct.h>
48 #elif defined(_WIN32) /* MSVC, etc. */
49 #include <direct.h>
50 #else
51 #include <dirent.h>
52 #endif
53
54 #include <mikmod.h>
55 #include "mlistedit.h"
56 #include "mlist.h"
57 #include "player.h"
58 #include "mdialog.h"
59 #include "rcfile.h"
60 #include "mconfig.h"
61 #include "mconfedit.h"
62 #include "marchive.h"
63 #include "mwidget.h"
64 #include "keys.h"
65 #include "display.h"
66 #include "mutilities.h"
67
68 #define FREQ_SEL '*'
69 #define FREQ_SEL_STR "*"
70
71 #define FREQ_UNSEL ' '
72 #define FREQ_UNSEL_STR " "
73
74 /* Function, which is called on Ok/Cancel button select
75 button: 0: Ok 1: Cancel
76 path: Selected file
77 data: user-pointer which was passed to freq_open()
78 Return: close fileselector? */
79 typedef BOOL (*handleFreqFunc) (int button, char *file, void *data);
80
81 /* Function, which is called for every new directory during
82 directory scanning in scan_dir(). scan_dir() is canceled if
83 the function returns 1. */
84 typedef BOOL (*handleScandirFunc) (char *path, int added, int removed, void *data);
85
86 typedef enum {
87 FREQ_ADD,
88 FREQ_TOGGLE,
89 FREQ_REMOVE
90 } FREQ_MODE;
91
92 typedef struct {
93 WID_LIST *w; /* the directory list */
94 char path[PATH_MAX<<1]; /* path of currently displayed directory */
95 BOOL before_add; /* TRUE until first call of entry_add() */
96 int actline; /* pos in playlist for insertion */
97 /* -1 -> append entries */
98 int cnt_list;
99 char **searchlist; /* sorted playlist archives or files */
100 /* (if archives are not available)*/
101 handleFreqFunc handle_freq;
102 void *data;
103 } FREQ_DATA;
104
105 typedef struct {
106 WID_LIST *w; /* the directory list */
107 FREQ_DATA *freq;
108 } HLIST_DATA;
109
110 typedef struct {
111 MMENU *menu;
112 int *actLine;
113 } MENU_DATA;
114
115 typedef struct {
116 WID_LABEL *w;
117 BOOL stop;
118 } FREQ_SCAN_DATA;
119
120 /* compare function for qsort on the searchlist */
searchlist_cmp(char ** key,char ** member)121 static int searchlist_cmp (char **key, char **member)
122 {
123 return (filecmp(*key,*member));
124 }
125
126 /* compare function for bsearch on the searchlist */
searchlist_search_cmp(char * key,char ** member)127 static int searchlist_search_cmp (char *key, char **member)
128 {
129 return (filecmp(key,*member));
130 }
131
132 /* compare function for qsort on the directory list */
dirlist_cmp(char ** small,char ** big)133 static int dirlist_cmp (char **small, char **big)
134 {
135 if (IS_PATH_SEP((*small)[strlen(*small)-1])) {
136 if (IS_PATH_SEP((*big)[strlen(*big)-1]))
137 return(filecmp(*small+2,*big+2));
138 else
139 return -1;
140 } else if (IS_PATH_SEP((*big)[strlen(*big)-1]))
141 return 1;
142 return(filecmp(*small+2,*big+2));
143 }
144
145 /* compare function for bearch on the directory list */
dirlist_search_cmp(char * key,char ** member)146 static int dirlist_search_cmp (char *key, char **member)
147 {
148 if (IS_PATH_SEP(key[strlen(key)-1])) {
149 if (IS_PATH_SEP((*member)[strlen(*member)-1]))
150 return(filecmp(key,*member+2));
151 else
152 return -1;
153 } else if (IS_PATH_SEP((*member)[strlen(*member)-1]))
154 return 1;
155 return(filecmp(key,*member+2));
156 }
157
158 /* Add/Remove tag marks to the files in entries (count: cnt) from
159 directory path according to the searchlist */
freq_set_marks(char ** entries,int cnt,const char * path,FREQ_DATA * data)160 static void freq_set_marks (char **entries, int cnt, const char *path, FREQ_DATA *data)
161 {
162 int i;
163 char file[PATH_MAX<<1], *fstart;
164
165 strcpy (file,path);
166 fstart = file+strlen(file);
167 for (i=0; i<cnt; i++) {
168 strcpy (fstart,entries[i]+2);
169 if (!IS_PATH_SEP(fstart[strlen(fstart)]) &&
170 data->cnt_list > 0 &&
171 bsearch (file,data->searchlist,data->cnt_list,
172 sizeof(char*),(int(*)())searchlist_search_cmp))
173 *(entries[i]) = FREQ_SEL;
174 else
175 *(entries[i]) = FREQ_UNSEL;
176 }
177 }
178
179 /* Check if size of playlist has changed (due to e.g. resolving
180 of playlists). If so, rebuild searchlist. */
freq_check_searchlist(FREQ_DATA * data)181 static void freq_check_searchlist (FREQ_DATA *data)
182 {
183 int i, len = PL_GetLength(&playlist);
184
185 if (len != data->cnt_list) {
186 data->searchlist = (char **) realloc (data->searchlist, sizeof(char*) * len);
187 data->cnt_list = len;
188 if (len) {
189 for (i=0; i<len; i++) {
190 PLAYENTRY *entry = PL_GetEntry(&playlist, i);
191 if (entry->archive)
192 data->searchlist[i] = entry->archive;
193 else
194 data->searchlist[i] = entry->file;
195 }
196 qsort (data->searchlist, len, sizeof(char*),(int(*)())searchlist_cmp);
197 }
198 if (data->w) {
199 freq_set_marks (data->w->entries,data->w->cnt,data->path,data);
200 wid_repaint ((WIDGET*)data->w);
201 }
202 }
203 }
204
205 /* Insert ins in (already enlarged) searchlist pl.
206 pl must be sorted (according to filecmp()).*/
entry_insert(int left,int right,char ** pl,char * ins)207 static void entry_insert (int left, int right, char **pl, char *ins)
208 {
209 int pos=0, cmp=0, last = right;
210
211 if (right<0) {
212 pl[0] = ins;
213 } else {
214 while (left<=right) {
215 pos = (left+right)/2;
216 cmp = filecmp(ins,pl[pos]);
217 if (cmp<0)
218 right = pos-1;
219 else
220 left = pos+1;
221 }
222 if (cmp>0) pos++;
223 for (cmp=last; cmp>=pos; cmp--)
224 pl[cmp+1] = pl[cmp];
225 pl[pos] = ins;
226 }
227 }
228
229 /* Insert entry path+file at position data->actline into the playlist and
230 update the (before and afterwards sorted) searchlist and
231 actline from data.
232 Return: Number of added entries */
entry_add(char * path,char * file,FREQ_DATA * data)233 static int entry_add (char *path, char *file, FREQ_DATA *data)
234 {
235 int len, old_len = PL_GetLength(&playlist);
236 char buffer[STORAGELEN];
237
238 strcpy (buffer,path);
239 if (file) strcat (buffer,file);
240
241 if (data) {
242 if (data->actline < 0 && data->before_add) {
243 /* "Load" was selected -> Remove old entries */
244 data->before_add = 0;
245 PL_ClearList(&playlist);
246 old_len = PL_GetLength(&playlist);
247 freq_check_searchlist (data);
248 } else
249 PL_StartInsert(&playlist, data->actline);
250 }
251 MA_FindFiles(&playlist, buffer);
252 PL_StopInsert(&playlist);
253
254 len = PL_GetLength(&playlist);
255 if (!old_len && len)
256 PL_InitCurrent(&playlist);
257
258 /* Update the searchlist */
259 if (len>old_len && data) {
260 int i, start, end;
261
262 data->searchlist = (char **) realloc (data->searchlist, sizeof(char*) * len);
263
264 start = data->actline;
265 if (start<0) start = old_len;
266 end = start+len-old_len;
267 for (i=start; i<end; i++) {
268 PLAYENTRY *entry = PL_GetEntry(&playlist, i);
269 char *ins = entry->archive ? entry->archive:entry->file;
270 entry_insert (0,data->cnt_list-1,data->searchlist,ins);
271 data->cnt_list++;
272 }
273 if (data->actline>=0) data->actline += len-old_len;
274 }
275 return len-old_len;
276 }
277
278 /* remove all entries with archive==path+file (or file==path+file,
279 if archive not set) from the playlist and update the (before
280 and afterwards sorted) searchlist and actline from data.
281 Return: Number of removed entries */
entry_remove_by_name(char * path,char * file,FREQ_DATA * data)282 static int entry_remove_by_name(char *path, char *file, FREQ_DATA *data)
283 {
284 int len = PL_GetLength(&playlist);
285 char buffer[STORAGELEN];
286 int cnt_remove = 0, i;
287 char **pos;
288
289 strcpy (buffer,path);
290 if (file) strcat (buffer,file);
291
292 /* Update the searchlist */
293 while (data->cnt_list>0 &&
294 (pos = (char **) bsearch(buffer,data->searchlist,data->cnt_list,
295 sizeof(char*),(int(*)())searchlist_search_cmp))) {
296 while (pos < data->searchlist + data->cnt_list - 1) {
297 *pos = *(pos+1);
298 pos++;
299 }
300 data->cnt_list--;
301 }
302 data->searchlist = (char **) realloc (data->searchlist, sizeof(char*) * data->cnt_list);
303
304 /* Remove the entries from the playlist */
305 for (i=len-1; i>=0; i--) {
306 PLAYENTRY *entry = PL_GetEntry(&playlist, i);
307 if (!filecmp (entry->archive ? entry->archive:entry->file,
308 buffer)) {
309 PL_DelEntry(&playlist, i);
310 if (i < data->actline) data->actline--;
311 cnt_remove++;
312 }
313 }
314 return cnt_remove;
315 }
316
317 /* Scan directory path for modules and add all files to the playlist,
318 which are not already in data->searchlist (if data!=NULL).
319 recursive: Scan recursively
320 links : Follow links */
scan_dir(char * path,BOOL recursive,BOOL links,FREQ_DATA * freq_data,FREQ_MODE mode,handleScandirFunc func,void * data,int * added,int * removed)321 static void scan_dir (char *path, BOOL recursive, BOOL links,
322 FREQ_DATA *freq_data, FREQ_MODE mode,
323 handleScandirFunc func, void *data,
324 int *added, int *removed)
325 {
326 #define DIR_BLOCK 10
327 DIR *dir;
328 struct dirent *entry;
329 struct stat statbuf;
330 char file[PATH_MAX<<1], *pathend, **dirs=NULL;
331 int cnt = 0, max = 0, i;
332
333 if (
334 #if !defined(__OS2__)&&!defined(__EMX__)&&!defined(__DJGPP__)&&!defined(_WIN32)&&!defined(_mikmod_amiga)
335 !strcmp (path,"/proc/") ||
336 !strcmp (path,"/dev/") ||
337 #endif
338 !(dir = opendir (path_conv_sys(path))))
339 return;
340
341 if (func) {
342 int add=-1, rem=-1;
343 if (added) add = *added;
344 if (removed) rem = *removed;
345 if (func (path,add,rem,data)) {
346 closedir (dir);
347 return;
348 }
349 }
350
351 strcpy (file,path);
352 pathend = file+strlen(file);
353
354 while ((entry = readdir (dir))) {
355 strcpy (pathend,entry->d_name);
356 path_conv(pathend);
357 if (!lstat(path_conv_sys(file), &statbuf)) {
358 if (S_ISDIR(statbuf.st_mode)) {
359 /* if dir, process it after the files */
360 if (recursive &&
361 (links || !S_ISLNK(statbuf.st_mode)) &&
362 strcmp (entry->d_name,"..") &&
363 strcmp (entry->d_name,".")) {
364 /* FIXME: check for cyclic links is missing */
365 if (cnt >= max) {
366 max += DIR_BLOCK;
367 dirs = (char **) realloc (dirs, sizeof(char*) * max);
368 }
369 dirs[cnt++] = strdup (entry->d_name);
370 }
371 } else if (!S_ISCHR(statbuf.st_mode) && !S_ISBLK(statbuf.st_mode) &&
372 !S_ISFIFO(statbuf.st_mode) && !S_ISSOCK(statbuf.st_mode) &&
373 MA_TestName (file, 0 , 0)) {
374 /* file of known type: add/remove it */
375 char **pos = NULL;
376 int j = 0;
377 if (freq_data && freq_data->cnt_list > 0)
378 pos = (char **) bsearch(file,freq_data->searchlist,freq_data->cnt_list,
379 sizeof(char*),(int(*)())searchlist_search_cmp);
380 if (pos) {
381 if (mode != FREQ_ADD) {
382 j = entry_remove_by_name(file, NULL, freq_data);
383 if (removed) *removed += j;
384 } else if (freq_data->actline < 0 && freq_data->before_add) {
385 j = entry_add(file, NULL, freq_data);
386 if (added) *added += j;
387 }
388 } else {
389 if (mode != FREQ_REMOVE) {
390 j = entry_add(file, NULL, freq_data);
391 if (added) *added += j;
392 }
393 }
394 }
395 }
396 while (win_main_iteration());
397 }
398 /* now process dirs after files are already processed */
399 for (i=0; i<cnt; i++) {
400 strcpy (pathend, dirs[i]);
401 path_conv(pathend);
402 strcat (pathend,PATH_SEP_STR);
403 scan_dir (file, recursive, links, freq_data, mode,
404 func, data, added, removed);
405 free (dirs[i]);
406 }
407 if (dirs) free (dirs);
408 closedir (dir);
409 }
410
411 /* read directory path and store the entries in entries */
freq_readdir(const char * path,char *** entries,int * cnt,FREQ_DATA * data)412 static void freq_readdir (const char *path, char ***entries, int *cnt, FREQ_DATA *data)
413 {
414 #define ENT_BLOCK 10
415 int max;
416 DIR *dir = opendir (path_conv_sys(path));
417 char file[PATH_MAX<<1], *pathend, *help;
418 struct dirent *entry;
419 struct stat statbuf;
420
421 strcpy (file,path);
422 pathend = file+strlen(file);
423 *entries = NULL;
424 *cnt = 0;
425 if (dir) {
426 max = *cnt = 0;
427 #ifdef _mikmod_amiga
428 if (pathend != file && pathend[-1] != ':') {
429 /* on AmigaOS variants, we won't get a ".." parentdir entry --
430 add a fake one here. */
431 max += ENT_BLOCK;
432 *entries = (char **) realloc (*entries, sizeof(char*) * max);
433 strcpy (pathend,".."PATH_SEP_STR);
434 path_conv (pathend);
435 help = (char *) malloc (sizeof(char) * (strlen(pathend) + 3));
436 strcpy (help," ");
437 strcat (help,pathend);
438 (*entries)[(*cnt)++] = help;
439 }
440 #endif
441 while ((entry = readdir (dir))) {
442 if (*cnt >= max) {
443 max += ENT_BLOCK;
444 *entries = (char **) realloc (*entries, sizeof(char*) * max);
445 }
446 strcpy (pathend,entry->d_name);
447 path_conv (pathend);
448 if (!stat(path_conv_sys(file), &statbuf))
449 if (S_ISDIR(statbuf.st_mode))
450 strcat (pathend,PATH_SEP_STR);
451
452 help = (char *) malloc (sizeof(char) * (strlen(pathend) + 3));
453 strcpy (help," ");
454 strcat (help,pathend);
455 (*entries)[(*cnt)++] = help;
456 }
457 freq_set_marks (*entries,*cnt,path,data);
458 closedir (dir);
459 if (*cnt)
460 qsort (*entries, *cnt, sizeof(char*),(int(*)())dirlist_cmp);
461 }
462 }
463
464 /* free directory list read with freq_readdir() */
freq_freedir(char ** entries,int cnt)465 static void freq_freedir (char **entries, int cnt)
466 {
467 int i;
468 for (i=0; i<cnt; i++)
469 if (entries[i]) free (entries[i]);
470 free (entries);
471 }
472
freq_set_title(FREQ_DATA * data)473 static void freq_set_title (FREQ_DATA *data)
474 {
475 int max = data->w->w.width-2;
476
477 if (strlen(data->path) <= max)
478 wid_list_set_title (data->w, data->path);
479 else {
480 char path[MAXWIDTH];
481 strcpy (path, "...");
482 strcat (path, &data->path[strlen(data->path)-max+3]);
483 wid_list_set_title (data->w, path);
484 }
485 wid_repaint ((WIDGET*)data->w);
486 }
487
488 /* change directory to path (read directory and display it) */
freq_changedir(const char * path,FREQ_DATA * data)489 static void freq_changedir (const char *path, FREQ_DATA *data)
490 {
491 char **entries, *last= NULL, *end, **pos = NULL, ch;
492 int cnt;
493
494 freq_readdir (path,&entries,&cnt,data);
495 if (entries && cnt>0) {
496 /* Check if new path is part of the old one and
497 find position in entries where the old path continues
498 to correctly reposition active entry */
499 if (strlen(path) < strlen(data->path)) {
500 last = data->path+strlen(path);
501 ch = *last;
502 *last = '\0';
503 if (!filecmp (data->path, path)) {
504 *last = ch;
505 end = last;
506 while (*end && !IS_PATH_SEP(*end))
507 end++;
508 if (IS_PATH_SEP(*end)) {
509 *(end+1) = '\0';
510 pos=(char**) bsearch(last, entries, cnt, sizeof(char*),
511 (int(*)())dirlist_search_cmp);
512 } else
513 pos = NULL;
514 }
515 }
516 if (!pos) pos = entries;
517 strcpy (data->path, path);
518 wid_list_set_entries (data->w, (const char **)entries, 0, cnt);
519 wid_list_set_active (data->w, pos-entries);
520 freq_set_title (data);
521 freq_freedir (entries,cnt);
522 } else
523 dlg_error_show ("Unable to read directory \"%s\"!",path);
524 }
525
hlist_close(HLIST_DATA * data)526 static void hlist_close (HLIST_DATA *data)
527 {
528 dialog_close(data->w->w.d);
529 free (data);
530 }
531
cb_hlist_list_focus(struct WIDGET * w,int focus)532 static int cb_hlist_list_focus(struct WIDGET *w, int focus)
533 {
534 if (focus == FOCUS_ACTIVATE) {
535 HLIST_DATA *data = (HLIST_DATA *) w->data;
536 int cur = ((WID_LIST*)w)->cur;
537
538 /* return in hotlist -> change to the selected dir */
539 freq_check_searchlist (data->freq);
540 if (cur < config.cnt_hotlist)
541 freq_changedir (config.hotlist[cur],data->freq);
542 hlist_close(data);
543 return EVENT_HANDLED;
544 }
545 return focus;
546 }
547
cb_hlist_button_focus(struct WIDGET * w,int focus)548 static int cb_hlist_button_focus(struct WIDGET *w, int focus)
549 {
550 if (focus == FOCUS_ACTIVATE) {
551 HLIST_DATA *data = (HLIST_DATA *) w->data;
552 int button = ((WID_BUTTON *) w)->active;
553 int cur = data->w->cur;
554
555 freq_check_searchlist (data->freq);
556 switch (button) {
557 case 0: /* change To */
558 if (cur < config.cnt_hotlist)
559 freq_changedir (config.hotlist[cur],data->freq);
560 hlist_close(data);
561 break;
562 case 1: /* Add current */
563 CF_string_array_insert (cur,&config.hotlist,&config.cnt_hotlist,
564 data->freq->path,PATH_MAX);
565 wid_list_set_entries (data->w,(const char **)config.hotlist,-1,config.cnt_hotlist);
566 wid_repaint ((WIDGET*)data->w);
567 break;
568 case 2: /* Remove */
569 CF_string_array_remove (cur,&config.hotlist,&config.cnt_hotlist);
570 wid_list_set_entries (data->w,(const char **)config.hotlist,-1,config.cnt_hotlist);
571 wid_repaint ((WIDGET*)data->w);
572 break;
573 case 3: /* Cancel */
574 hlist_close(data);
575 break;
576 }
577 return EVENT_HANDLED;
578 }
579 return focus;
580 }
581
582 /* open the directory hotlist editor */
freq_hotlist(FREQ_DATA * freq_data)583 static void freq_hotlist (FREQ_DATA *freq_data)
584 {
585 DIALOG *d = dialog_new();
586 WIDGET *w;
587 HLIST_DATA *data = (HLIST_DATA *) malloc (sizeof(HLIST_DATA));
588
589 w = wid_list_add(d, 1, (const char **)config.hotlist, config.cnt_hotlist);
590 wid_set_size (w, 74, 10);
591 data->w = (WID_LIST*)w;
592 data->freq = freq_data;
593 wid_set_func(w, NULL, cb_hlist_list_focus, data);
594
595 w = wid_button_add(d, 1, "<change &To>|&Add current|&Remove|&Cancel", 0);
596 wid_set_func(w, NULL, cb_hlist_button_focus, data);
597
598 dialog_open(d, "Directory hotlist");
599 }
600
601 /* Check if file is a directory and copy the resulting path from
602 path and file to dest */
path_update(char * dest,char * path,char * file)603 static BOOL path_update (char *dest, char *path, char *file)
604 {
605 char *end;
606
607 if (!strcmp (file,".."PATH_SEP_STR)) {
608 strcpy (dest, path);
609 end = dest+strlen(dest)-2;
610 while (end>dest && !IS_PATH_SEP(*end))
611 *end-- = '\0';
612 } else if (!strcmp (file,"."PATH_SEP_STR)) {
613 strcpy (dest, path);
614 } else if (IS_PATH_SEP(file[strlen(file)-1])) {
615 strcpy (dest, path);
616 strcat (dest, file);
617 } else
618 return 0;
619 return 1;
620 }
621
cb_scan_dir_stop_focus(struct WIDGET * w,int focus)622 static int cb_scan_dir_stop_focus(struct WIDGET *w, int focus)
623 {
624 if (focus == FOCUS_ACTIVATE) {
625 if (((WID_BUTTON *) w)->active == 0)
626 ((FREQ_SCAN_DATA*)w->data)->stop = 1;
627
628 return EVENT_HANDLED;
629 }
630 return focus;
631 }
632
633 /* Show progress during directory scanning */
cb_freq_scan_dir(char * path,int added,int removed,void * data)634 BOOL cb_freq_scan_dir (char *path, int added, int removed, void *data)
635 {
636 FREQ_SCAN_DATA *scan_data = (FREQ_SCAN_DATA*)data;
637
638 if (strlen(path) > 50)
639 sprintf (storage,"Scanning ...%s...\n"
640 "%4d entrie(s) added, %4d entrie(s) removed",
641 &path[strlen(path)-47], added, removed);
642 else
643 sprintf (storage,"Scanning %s...\n"
644 "%4d entrie(s) added, %4d entrie(s) removed",
645 path, added, removed);
646
647 wid_label_set_label ((WID_LABEL*)(scan_data->w),storage);
648 dialog_repaint (scan_data->w->w.d->win);
649 win_refresh();
650 return scan_data->stop;
651 }
652
653 /* Scan directory path for modules and add/remove them to the playlist
654 according to mode */
freq_scan_dir(char * path,FREQ_DATA * data,FREQ_MODE mode)655 static void freq_scan_dir (char *path, FREQ_DATA *data, FREQ_MODE mode)
656 {
657 int added=0, removed=0;
658 DIALOG *d = dialog_new();
659 WIDGET *w;
660 FREQ_SCAN_DATA scan_data;
661
662 scan_data.stop = 0;
663 if (strlen(path) > 50)
664 sprintf (storage,"Scanning ...%-47s...\n"
665 " 0 entrie(s) added, 0 entrie(s) removed",
666 &path[strlen(path)-47]);
667 else
668 sprintf (storage,"Scanning %-50s...\n"
669 " 0 entrie(s) added, 0 entrie(s) removed",path);
670 scan_data.w = (WID_LABEL*)wid_label_add(d, 1, storage);
671 w = wid_button_add(d, 2, "&Stop", 0);
672 wid_set_func(w, NULL, cb_scan_dir_stop_focus, &scan_data);
673
674 dialog_open(d, "Message");
675 win_refresh();
676
677 scan_dir (path, 1, 0, data, mode,
678 cb_freq_scan_dir, &scan_data, &added, &removed);
679 dialog_close(d);
680
681 freq_set_marks (data->w->entries,data->w->cnt,data->path,data);
682 sprintf (storage,"Added %d entrie(s) and removed %d entrie(s).",
683 added,removed);
684 dlg_message_open(storage, "&Ok", 0, 0, NULL, NULL);
685 }
686
687 /* Add/Remove entries to/from the playlist */
freq_add(FREQ_DATA * data,FREQ_MODE mode)688 static void freq_add (FREQ_DATA *data, FREQ_MODE mode)
689 {
690 char *file = data->w->entries[data->w->cur];
691 char *path = data->path;
692 char help[PATH_MAX<<1];
693
694 if (path_update (help,path,file+2)) {
695 freq_scan_dir (help, data, mode);
696 } else if (*file == FREQ_SEL) {
697 if (mode != FREQ_ADD) {
698 if (entry_remove_by_name(path, file+2, data) > 0)
699 *file = FREQ_UNSEL;
700 } else if (data->actline < 0 && data->before_add)
701 if (entry_add(path, file+2, data) > 0)
702 *file = FREQ_SEL;
703 } else {
704 if (mode != FREQ_REMOVE) {
705 if (entry_add(path, file+2, data) > 0)
706 *file = FREQ_SEL;
707 }
708 }
709 wid_list_set_active (data->w,data->w->cur+1);
710 win_panel_repaint();
711 }
712
freq_close(FREQ_DATA * data)713 static void freq_close (FREQ_DATA *data)
714 {
715 if (data) {
716 if (data->w) dialog_close(data->w->w.d);
717 if (data->searchlist) free (data->searchlist);
718 free (data);
719 }
720 PL_DelDouble(&playlist);
721 }
722
723 /* Ok/Back was selected and data->handle_freq() is present -> call function
724 Return: close fileselector? */
freq_call_func(int button,FREQ_DATA * data)725 static BOOL freq_call_func (int button, FREQ_DATA *data)
726 {
727 char file[PATH_MAX<<1];
728
729 strcpy (file, data->path);
730 strcat (file, data->w->entries[data->w->cur]+2);
731 return data->handle_freq (button,file,data->data);
732 }
733
cb_freq_list_focus(struct WIDGET * w,int focus)734 static int cb_freq_list_focus(struct WIDGET *w, int focus)
735 {
736 if (focus == FOCUS_ACTIVATE) {
737 FREQ_DATA *data = (FREQ_DATA *) w->data;
738 int cur = ((WID_LIST*)w)->cur;
739 char path[PATH_MAX<<1], *cur_entry;
740
741 freq_check_searchlist (data);
742
743 path[0] = '\0';
744 cur_entry = ((WID_LIST*)w)->entries[cur]+2;
745
746 /* Default action for dirs: change dir
747 For files: call user-function or add entry to playlist */
748 if (!path_update(path,data->path,cur_entry)) {
749 if (data->handle_freq) {
750 if (freq_call_func (0,data))
751 freq_close (data);
752 } else
753 freq_add (data,FREQ_ADD);
754 }
755 if (path[0] != '\0')
756 freq_changedir (path,data);
757
758 return EVENT_HANDLED;
759 }
760 return focus;
761 }
762
cb_freq_cd_do(WIDGET * w,int button,void * input,void * data)763 static BOOL cb_freq_cd_do (WIDGET *w,int button, void *input, void *data)
764 {
765 if (button<=0) {
766 char *pos;
767 path_conv((char *)input);
768 pos = (char*)input + strlen((char*)input);
769 /* Check if path ends with '/' */
770 if (!IS_PATH_SEP(*(pos-1))) {
771 *pos = PATH_SEP;
772 *(pos+1) = '\0';
773 }
774 freq_check_searchlist ((FREQ_DATA *)data);
775 freq_changedir ((char *)input, (FREQ_DATA *)data);
776 }
777 return 1;
778 }
779
freq_cd(FREQ_DATA * data)780 static void freq_cd (FREQ_DATA *data)
781 {
782 dlg_input_str ("Change directory to:", "<&Ok>|&Cancel",
783 data->path, PATH_MAX, cb_freq_cd_do, data);
784 }
785
cb_freq_list_key(WIDGET * w,int ch)786 static int cb_freq_list_key(WIDGET *w, int ch)
787 {
788 FREQ_DATA *data = (FREQ_DATA *) w->data;
789
790 freq_check_searchlist (data);
791 if ((ch < 256) && (isalpha(ch)))
792 ch = toupper(ch);
793 switch (ch) {
794 case KEY_IC: /* Insert -> Add */
795 freq_add (data,FREQ_ADD);
796 break;
797 default:
798 return 0;
799 }
800 return EVENT_HANDLED;
801 }
802
cb_freq_button_focus(struct WIDGET * w,int focus)803 static int cb_freq_button_focus(struct WIDGET *w, int focus)
804 {
805 if (focus == FOCUS_ACTIVATE) {
806 FREQ_DATA *data = (FREQ_DATA *) w->data;
807 int button = ((WID_BUTTON *) w)->active;
808
809 freq_check_searchlist (data);
810 switch (button) {
811 case 0: /* Add */
812 freq_add (data,FREQ_ADD);
813 break;
814 case 1: /* Toggle */
815 freq_add (data,FREQ_TOGGLE);
816 break;
817 case 2: /* Cd */
818 freq_cd (data);
819 break;
820 case 3: /* HotList */
821 freq_hotlist (data);
822 break;
823 case 4: /* Back / Ok */
824 if (!data->handle_freq || freq_call_func (0,data))
825 freq_close (data);
826 break;
827 case 5: /* Back */
828 if (data->handle_freq && freq_call_func (1,data))
829 freq_close (data);
830 break;
831 }
832 return EVENT_HANDLED;
833 }
834 return focus;
835 }
836
837 /* Init initial path and searchlist */
freq_data_init(const char * path)838 static FREQ_DATA *freq_data_init (const char *path)
839 {
840 struct stat statbuf;
841 FREQ_DATA *data = (FREQ_DATA *) malloc(sizeof(FREQ_DATA));
842 char *pos;
843
844 data->path[0] = '\0';
845 if (path_relative(path)) {
846 getcwd (data->path,PATH_MAX);
847 path_conv (data->path);
848 if (!IS_PATH_SEP(data->path[strlen(data->path)-1]))
849 strcat (data->path, PATH_SEP_STR);
850 }
851 strcat (data->path,path);
852 if (stat(path_conv_sys(data->path), &statbuf) || !S_ISDIR(statbuf.st_mode))
853 if ((pos = FIND_LAST_DIRSEP(data->path)) != NULL)
854 *(pos+1) = '\0';
855
856 pos = data->path+strlen(data->path);
857 if (!IS_PATH_SEP(*(pos-1))) {
858 *pos = PATH_SEP;
859 *(pos+1) = '\0';
860 }
861 data->w = NULL;
862 data->before_add = 1;
863 data->actline = -1;
864 data->cnt_list = 0;
865 data->searchlist = NULL;
866
867 freq_check_searchlist (data);
868 return data;
869 }
870
871 /* Open a file requester.
872 func!=NULL: func is called if Ok or Cancel is selected
873 func==NULL: no Ok button, Add is default */
freq_open(const char * title,const char * path,int actline,handleFreqFunc func,void * data)874 void freq_open (const char *title, const char *path, int actline,
875 handleFreqFunc func, void *data)
876 {
877 FREQ_DATA *freq_data;
878 DIALOG *d = dialog_new();
879 WIDGET *w;
880 char **entries, *path_first = NULL;
881 int cnt;
882
883 freq_data = freq_data_init (path);
884 freq_data->actline = actline;
885 freq_data->handle_freq = func;
886 freq_data->data = data;
887
888 freq_readdir(freq_data->path,&entries,&cnt,freq_data);
889 if (!entries || !cnt) {
890 /* show error after file selector is open */
891 path_first = strdup (freq_data->path);
892
893 /* error on initial path -> try root directory */
894 #if defined(__OS2__)||defined(__EMX__)||defined(__DJGPP__)||defined(_WIN32)
895 strcpy (freq_data->path,"c:"PATH_SEP_STR);
896 #elif defined _mikmod_amiga
897 strcpy (freq_data->path,"SYS:"); /* or use ":" instead??? */
898 #else
899 strcpy (freq_data->path,PATH_SEP_STR);
900 #endif
901 freq_readdir(freq_data->path,&entries,&cnt,freq_data);
902 if (!entries || !cnt) {
903 /* again an error -> give up */
904 freq_close (freq_data);
905 if (path_first) free (path_first);
906 return;
907 }
908 }
909
910 w = wid_list_add(d, 1, (const char **)entries, cnt);
911 freq_data->w = (WID_LIST*)w;
912 wid_set_func(w, cb_freq_list_key, cb_freq_list_focus, freq_data);
913 freq_freedir(entries, cnt);
914
915 if (func)
916 w = wid_button_add(d, 1, "&Add|&Toggle|&Cd|&Hlist|<&Ok>|&Back", 0);
917 else
918 w = wid_button_add(d, 1, "<&Add>|&Toggle|&Cd|&Hlist|&Back", 0);
919 wid_set_func(w, NULL, cb_freq_button_focus, freq_data);
920
921 dialog_open(d, title);
922 /* Size of list widget is necessary -> set title after dialog_open() */
923 freq_set_title (freq_data);
924 if (path_first) {
925 dlg_error_show ("Unable to read directory \"%s\"!",path_first);
926 free (path_first);
927 }
928 }
929
cb_list_scan_dir(char * path,int added,int removed,void * data)930 static BOOL cb_list_scan_dir (char *path, int added, int removed, void *data)
931 {
932 BOOL quiet = (BOOL)(SINTPTR_T)data;
933 char str[70], *pos;
934 int i;
935
936 if (!quiet) {
937 if (strlen(path) > 43)
938 sprintf (str,"\rScanning ...%s... (%d added)",
939 &path[strlen(path)-40],added);
940 else
941 sprintf (str,"\rScanning %s... (%d added)",path,added);
942 pos = str+strlen(str);
943 for (i=strlen(str); i<(70-1); i++)
944 *pos++ = ' ';
945 *pos = '\0';
946 printf ("%s", str);
947 fflush(stdout);
948 }
949 return 0;
950 }
951
952 /* test if path is a directory and recursively scan it for modules */
list_scan_dir(char * path,BOOL quiet)953 int list_scan_dir (char *path, BOOL quiet)
954 {
955 struct stat statbuf;
956 int added = 0;
957 char dir[PATH_MAX<<1]="", *pos;
958
959 #if defined(__EMX__)||defined(__OS2__)||defined(__DJGPP__)||defined(_WIN32)
960 if (*path!=PATH_SEP && *(path+1)!=':')
961 #else
962 if (!IS_PATH_SEP(*path))
963 #endif
964 {
965 getcwd (dir,PATH_MAX);
966 path_conv (dir);
967 if (!IS_PATH_SEP(dir[strlen(dir)-1]))
968 strcat (dir, PATH_SEP_STR);
969 }
970 strcat (dir,path);
971 pos = dir+strlen(dir);
972 if (!IS_PATH_SEP(*(pos-1))) {
973 *pos = PATH_SEP;
974 *(pos+1) = '\0';
975 }
976
977 if (!stat(path_conv_sys(dir), &statbuf) && S_ISDIR(statbuf.st_mode))
978 scan_dir (dir, 1, 0, NULL, FREQ_ADD,
979 cb_list_scan_dir, (void *)(SINTPTR_T)quiet, &added, NULL);
980 return added;
981 }
982
983 /* remove an entry from the playlist */
entry_remove(int entry)984 static void entry_remove (int entry)
985 {
986 PL_DelEntry(&playlist, entry);
987 }
988
989 /* remove an entry from the playlist and delete the associated module */
cb_delete_entry(WIDGET * w,int button,void * input,void * entry)990 static BOOL cb_delete_entry(WIDGET *w, int button, void *input, void *entry)
991 {
992 if (button<=0) {
993 PLAYENTRY *cur = PL_GetEntry(&playlist, (SINTPTR_T)entry);
994 if (cur->archive) {
995 if (unlink(path_conv_sys(cur->archive)) == -1)
996 dlg_error_show("Error deleting archive \"%s\"!",cur->archive);
997 } else {
998 if (unlink(path_conv_sys(cur->file)) == -1)
999 dlg_error_show("Error deleting file \"%s\"!",cur->file);
1000 }
1001 entry_remove((SINTPTR_T)entry);
1002 }
1003 return 1;
1004 }
1005
1006 /* split a filename into the name and the last extension */
split_name(char * file,char ** name,char ** ext)1007 static void split_name(char *file, char **name, char **ext)
1008 {
1009 *name = FIND_LAST_DIRSEP(file);
1010 if (!*name)
1011 *name = file;
1012 *ext = strrchr(*name, '.');
1013 if (!*ext)
1014 *ext = &(*name[strlen(*name)]);
1015 }
1016
1017 static BOOL sort_rev = 0;
1018 /* *INDENT-OFF* */
1019 static enum {
1020 SORT_NAME,
1021 SORT_EXT,
1022 SORT_PATH,
1023 SORT_TIME
1024 } sort_mode = SORT_NAME;
1025 /* *INDENT-ON* */
1026
cb_cmp_sort(PLAYENTRY * small,PLAYENTRY * big)1027 static int cb_cmp_sort(PLAYENTRY * small, PLAYENTRY * big)
1028 {
1029 char ch_s = ' ', ch_b = ' ', *ext_s, *ext_b, *name_s, *name_b;
1030 int ret = 0;
1031
1032 switch (sort_mode) {
1033 case SORT_NAME:
1034 split_name(small->file, &name_s, &ext_s);
1035 split_name(big->file, &name_b, &ext_b);
1036 ch_s = *ext_s;
1037 ch_b = *ext_b;
1038 *ext_s = '\0';
1039 *ext_b = '\0';
1040 ret = strcasecmp(name_s, name_b);
1041 *ext_s = ch_s;
1042 *ext_b = ch_b;
1043 break;
1044 case SORT_EXT:
1045 split_name(small->file, &name_s, &ext_s);
1046 split_name(big->file, &name_b, &ext_b);
1047 ret = strcasecmp(ext_s, ext_b);
1048 break;
1049 case SORT_PATH:
1050 ext_s = small->archive;
1051 if (!ext_s)
1052 ext_s = small->file;
1053 name_s = FIND_LAST_DIRSEP(ext_s);
1054 if (name_s) {
1055 ch_s = *name_s;
1056 *name_s = '\0';
1057 }
1058 ext_b = big->archive;
1059 if (!ext_b)
1060 ext_b = big->file;
1061 name_b = FIND_LAST_DIRSEP(ext_b);
1062 if (name_b) {
1063 ch_b = *name_b;
1064 *name_b = '\0';
1065 }
1066 ret = strcasecmp(ext_s, ext_b);
1067 if (name_s)
1068 *name_s = ch_s;
1069 if (name_b)
1070 *name_b = ch_b;
1071 break;
1072 case SORT_TIME:
1073 ret = (small->time == big->time ? 0 :
1074 (small->time < big->time ? -1 : 1));
1075 break;
1076 }
1077 return (sort_rev) ? -ret : ret;
1078 }
1079
1080 /* overwrites an existdng playlist */
cb_overwrite(WIDGET * w,int button,void * input,void * file)1081 static BOOL cb_overwrite (WIDGET *w, int button, void *input, void *file)
1082 {
1083 if (button<=0) {
1084 path_conv((char *)file);
1085 if (PL_Save(&playlist, (char *)file))
1086 rc_set_string(&config.pl_name, (char *)file, PATH_MAX);
1087 else
1088 dlg_error_show("Error saving playlist \"%s\"!",file);
1089 }
1090 if (file) free(file);
1091 return 1;
1092 }
1093
cb_browse(int button,char * file,void * data)1094 static BOOL cb_browse (int button, char *file, void *data)
1095 {
1096 if (!button) {
1097 wid_str_set_input ((WID_STR*)data, file, -1);
1098 wid_repaint ((WIDGET*)data);
1099 }
1100 return 1;
1101 }
1102
1103 /* saves a playlist */
cb_save_as(WIDGET * w,int button,void * input,void * data)1104 static BOOL cb_save_as(WIDGET *w, int button, void *input, void *data)
1105 {
1106 path_conv((char *)input);
1107 if (button == 0) { /* Browse */
1108 freq_open ("Select directory/file",(char*)input,(SINTPTR_T)data,
1109 cb_browse,w);
1110 return 0;
1111 } else if (button == 1 || button == -1) { /* Ok / Str-Widget */
1112 if (file_exist((char*)input)) {
1113 char *f_copy = strdup((char*)input);
1114 char *msg = str_sprintf("File \"%s\" exists.\n"
1115 "Really overwrite the file?", f_copy);
1116 dlg_message_open(msg, "&Yes|&No", 1, 1, cb_overwrite, f_copy);
1117 free(msg);
1118 } else {
1119 if (PL_Save(&playlist, (char*)input))
1120 rc_set_string(&config.pl_name, (char*)input, PATH_MAX);
1121 else
1122 dlg_error_show("Error saving playlist \"%s\"!",input);
1123 }
1124 }
1125 return 1;
1126 }
1127
1128 /* playlist menu handler */
cb_handle_menu(MMENU * menu)1129 static void cb_handle_menu(MMENU * menu)
1130 {
1131 MENU_DATA *data = (MENU_DATA *) menu->data;
1132 int actLine = *data->actLine;
1133 PLAYENTRY *cur;
1134 char *name, *msg;
1135
1136 /* main menu */
1137 if (!menu->id) {
1138 switch (menu->cur) {
1139 case 0: /* play highlighted module */
1140 if (actLine >= 0)
1141 Player_SetNextMod(actLine);
1142 break;
1143 case 1: /* remove highlighted module */
1144 if (actLine >= 0)
1145 entry_remove(actLine);
1146 break;
1147 case 2: /* delete highlighted module */
1148 cur = PL_GetEntry(&playlist, actLine);
1149 if (!cur)
1150 break;
1151
1152 if (cur->archive) {
1153 name = FIND_LAST_DIRSEP(cur->file);
1154 if (name)
1155 name++;
1156 else
1157 name = cur->file;
1158
1159 if (strlen(cur->archive) > 60)
1160 msg = str_sprintf2("File \"%s\" is in an archive!\n"
1161 "Really delete whole archive\n"
1162 " \"...%s\"?", name,
1163 &(cur->
1164 archive[strlen(cur->archive) - 57]));
1165 else
1166 msg =
1167 str_sprintf2("File \"%s\" is in an archive!\n"
1168 "Really delete whole archive\n"
1169 " \"%s\"?", name, cur->archive);
1170 dlg_message_open(msg, "&Yes|&No", 1, 1, cb_delete_entry,
1171 (void *)(SINTPTR_T)actLine);
1172 } else {
1173 if (strlen(cur->file) > 50)
1174 msg = str_sprintf("Delete file \"...%s\"?",
1175 &(cur->file[strlen(cur->file) - 47]));
1176 else
1177 msg = str_sprintf("Delete file \"%s\"?", cur->file);
1178 dlg_message_open(msg, "&Yes|&No", 1, 1,
1179 cb_delete_entry, (void *)(SINTPTR_T)actLine);
1180 }
1181 free(msg);
1182 break;
1183 case 5: /* shuffle list */
1184 PL_Randomize(&playlist);
1185 break;
1186 case 7: /* cancel */
1187 break;
1188 default:
1189 return;
1190 }
1191 /* file menu */
1192 } else if (menu->id == 1) {
1193 switch (menu->cur) {
1194 case 0: /* load */
1195 freq_open ("Load modules/playlists",
1196 config.pl_name, -1, NULL, NULL);
1197 break;
1198 case 1: /* insert */
1199 freq_open ("Insert modules/playlists",
1200 config.pl_name, actLine, NULL, NULL);
1201 break;
1202 case 2: /* save */
1203 if (!PL_Save(&playlist, config.pl_name))
1204 dlg_error_show("Error saving playlist \"%s\"!",config.pl_name);
1205 break;
1206 case 3: /* save as */
1207 dlg_input_str("Save playlist as:", "&Browse|<&Ok>|&Cancel",
1208 config.pl_name, PATH_MAX, cb_save_as,
1209 (void*)(SINTPTR_T)actLine);
1210 break;
1211 default:
1212 return;
1213 }
1214 /* sort menu */
1215 } else {
1216 /* reverse flag */
1217 sort_rev = (SINTPTR_T)menu->entries[5].data;
1218 switch (menu->cur) {
1219 case 0: /* by name */
1220 sort_mode = SORT_NAME;
1221 PL_Sort(&playlist, cb_cmp_sort);
1222 break;
1223 case 1: /* by extension */
1224 sort_mode = SORT_EXT;
1225 PL_Sort(&playlist, cb_cmp_sort);
1226 break;
1227 case 2: /* by path */
1228 sort_mode = SORT_PATH;
1229 PL_Sort(&playlist, cb_cmp_sort);
1230 break;
1231 case 3: /* by time */
1232 sort_mode = SORT_TIME;
1233 PL_Sort(&playlist, cb_cmp_sort);
1234 break;
1235 default:
1236 return;
1237 }
1238 }
1239 menu_close(data->menu);
1240 return;
1241 }
1242
list_open(int * actLine)1243 void list_open(int *actLine)
1244 {
1245 static MENU_DATA menu_data;
1246
1247 static MENTRY file_entries[] = {
1248 {"&Load...", 0, "Load new playlists/modules"},
1249 {"&Insert...", 0, "Insert new playlists/modules in current playlist"},
1250 {"&Save", 0, NULL},
1251 {"Save &as...", 0, "Save playlist in a specified file"},
1252 {NULL,NULL,NULL}
1253 };
1254 static MMENU file_menu =
1255 { 0, 0, -1, 1, file_entries, cb_handle_menu, NULL, &menu_data, 1 };
1256
1257 static MENTRY sort_entries[] = {
1258 {"by &name", 0, "Sort list by name of modules"},
1259 {"by &extension", 0, "Sort list by extension of modules"},
1260 {"by &path", 0, "Sort list by path of modules/archives"},
1261 {"by &time", 0, "Sort list by playing time of modules"},
1262 {"%---------", 0, NULL},
1263 {"[%c] &reverse", 0, "Smaller to bigger or reverse sort"},
1264 {NULL,NULL,NULL}
1265 };
1266 static MMENU sort_menu =
1267 { 0, 0, -1, 1, sort_entries, cb_handle_menu, NULL, &menu_data, 2 };
1268
1269 static MENTRY entries[] = {
1270 {"&Play", 0, "Play selected entry"},
1271 {"&Remove", 0, "Remove selected entry from list"},
1272 {"&Delete...", 0,
1273 "Remove selected entry from list and delete it on disk"},
1274 {"%----------", 0, NULL},
1275 {"&File %>", &file_menu, "Load/Save playlist/modules"},
1276 {"&Shuffle", 0, "Shuffle the list"},
1277 {"S&ort %>", &sort_menu, "Sort the list"},
1278 {"&Back", 0, "Leave menu"},
1279 {NULL,NULL,NULL}
1280 };
1281 static MMENU menu =
1282 { 0, 0, -1, 1, entries, cb_handle_menu, NULL, &menu_data, 0 };
1283
1284 menu_data.menu = &menu;
1285 menu_data.actLine = actLine;
1286 set_help(&file_entries[2], "Save list in '%s'", config.pl_name);
1287 menu_open(&menu, 5, 5);
1288 }
1289