1 /*
2 * Schism Tracker - a cross-platform Impulse Tracker clone
3 * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
4 * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
5 * copyright (c) 2009 Storlek & Mrs. Brisby
6 * copyright (c) 2010-2012 Storlek
7 * URL: http://schismtracker.org/
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #define NEED_DIRENT
25 #define NEED_TIME
26 #include "headers.h"
27
28 #include "it.h"
29 #include "song.h"
30 #include "page.h"
31 #include "dmoz.h"
32 #include "sample-edit.h"
33 #include "log.h"
34
35 #include <sys/types.h>
36 #include <sys/stat.h>
37
38 #include "sdlmain.h"
39
40 #include <fcntl.h>
41 #include <ctype.h>
42 #include <errno.h>
43
44 /* --------------------------------------------------------------------------------------------------------- */
45 /* the locals */
46 static struct vgamem_overlay sample_image = {
47 52,25,76,28,
48 NULL, 0, 0, 0,
49 };
50
51 static char current_filename[PATH_MAX];
52 static int sample_speed_pos = 0;
53 static int sample_loop_beg = 0;
54 static int sample_loop_end = 0;
55 static int sample_susloop_beg = 0;
56 static int sample_susloop_end = 0;
57
58
59 static int _library_mode = 0;
60 static struct widget widgets_loadsample[15];
61 static int fake_slot_changed = 0;
62 static int will_move_to = -1;
63 static int fake_slot = KEYJAZZ_NOINST;
64 static const char *const loop_states[] = {
65 "Off", "On Forwards", "On Ping Pong", NULL };
66
67 static void handle_preload(void);
68
69 /* --------------------------------------------------------------------------------------------------------- */
70
71 /* files:
72 file type color displayed title notes
73 --------- ----- --------------- -----
74 unchecked 4 <the filename> IT uses color 6 for these
75 directory 5 "........Directory........" dots are char 154 (same for libraries)
76 sample 3 <the sample name>
77 libraries 6 ".........Library........." IT uses color 3. maybe use module name here?
78 unknown 2 <the filename> any regular file that's not recognized
79 */
80
81 static int top_file = 0;
82 static time_t directory_mtime;
83 static dmoz_filelist_t flist;
84 #define current_file (flist.selected)
85
86 static int search_pos = -1;
87 static char search_str[PATH_MAX];
88
89 /* get a color index from a dmoz_file_t 'type' field */
get_type_color(int type)90 static inline int get_type_color(int type)
91 {
92 if (type == TYPE_DIRECTORY)
93 return 5;
94 if (!(type & TYPE_EXT_DATA_MASK))
95 return 4; /* unchecked */
96 if (type & TYPE_BROWSABLE_MASK)
97 return 6; /* library */
98 if (type == TYPE_UNKNOWN)
99 return 2;
100 return 3; /* sample */
101 }
102
clear_directory(void)103 static void clear_directory(void)
104 {
105 dmoz_free(&flist, NULL);
106 fake_slot = KEYJAZZ_NOINST;
107 fake_slot_changed = 0;
108 }
109
file_list_reposition(void)110 static void file_list_reposition(void)
111 {
112 dmoz_file_t *f;
113
114 current_file = CLAMP(current_file, 0, flist.num_files - 1);
115 // XXX use CLAMP() here too, I can't brain
116 if (current_file < top_file)
117 top_file = current_file;
118 else if (current_file > top_file + 34)
119 top_file = current_file - 34;
120 if (current_file >= 0 && current_file < flist.num_files) {
121 f = flist.files[current_file];
122
123 if (f && f->smp_filename) {
124 strncpy(current_filename, f->smp_filename,
125 PATH_MAX-1);
126 } else if (f && f->base) {
127 strncpy(current_filename, f->base,
128 PATH_MAX-1);
129 } else {
130 current_filename[0] = '\0';
131 }
132 widgets_loadsample[1].d.textentry.firstchar = 0;
133 widgets_loadsample[1].d.textentry.cursor_pos =
134 strlen(current_filename);
135
136 widgets_loadsample[2].d.numentry.value = f ? f->smp_speed : 0;
137
138 if (f && f->smp_flags & CHN_PINGPONGLOOP) {
139 widgets_loadsample[3].d.menutoggle.state = 2;
140 } else if (f && f->smp_flags & CHN_LOOP) {
141 widgets_loadsample[3].d.menutoggle.state = 1;
142 } else {
143 widgets_loadsample[3].d.menutoggle.state = 0;
144 }
145
146 widgets_loadsample[4].d.numentry.value = f ? f->smp_loop_start : 0;
147 widgets_loadsample[5].d.numentry.value = f ? f->smp_loop_end : 0;
148
149 if (f && f->smp_flags & CHN_PINGPONGSUSTAIN) {
150 widgets_loadsample[6].d.menutoggle.state = 2;
151 } else if (f && f->smp_flags & CHN_SUSTAINLOOP) {
152 widgets_loadsample[6].d.menutoggle.state = 1;
153 } else {
154 widgets_loadsample[6].d.menutoggle.state = 0;
155 }
156
157 widgets_loadsample[7].d.numentry.value = f ? f->smp_sustain_start : 0;
158 widgets_loadsample[8].d.numentry.value = f ? f->smp_sustain_end : 0;
159 widgets_loadsample[9].d.thumbbar.value = f ? f->smp_defvol : 64;
160 widgets_loadsample[10].d.thumbbar.value = f ? f->smp_gblvol : 64;
161 widgets_loadsample[11].d.thumbbar.value = f ? f->smp_vibrato_speed : 0;
162 widgets_loadsample[12].d.thumbbar.value = f ? f->smp_vibrato_depth : 0;
163 widgets_loadsample[13].d.thumbbar.value = f ? f->smp_vibrato_rate : 0;
164 if (f) {
165 /* autoload some files */
166 if (TYPE_SAMPLE_EXTD == (f->type & TYPE_SAMPLE_EXTD)
167 && f->filesize < 0x4000000 && f->smp_length < 0x1000000)
168 handle_preload();
169 }
170 }
171 }
172
173
read_directory(void)174 static void read_directory(void)
175 {
176 struct stat st;
177
178 clear_directory();
179 if (stat(cfg_dir_samples, &st) < 0)
180 directory_mtime = 0;
181 else
182 directory_mtime = st.st_mtime;
183 /* if the stat call failed, this will probably break as well, but
184 at the very least, it'll add an entry for the root directory. */
185 if (dmoz_read(cfg_dir_samples, &flist, NULL, dmoz_read_sample_library) < 0)
186 log_perror(cfg_dir_samples);
187
188 dmoz_filter_filelist(&flist, dmoz_fill_ext_data, ¤t_file, file_list_reposition);
189 dmoz_cache_lookup(cfg_dir_samples, &flist, NULL);
190 file_list_reposition();
191 }
192
193 /* return: 1 = success, 0 = failure
194 TODO: provide some sort of feedback if something went wrong. */
change_dir(const char * dir)195 static int change_dir(const char *dir)
196 {
197 char *ptr = dmoz_path_normal(dir);
198
199 if (!ptr)
200 return 0;
201
202 dmoz_cache_update(cfg_dir_samples, &flist, NULL);
203
204 /* FIXME: need to make sure it exists, and that it's a directory */
205 strncpy(cfg_dir_samples, ptr, PATH_MAX);
206 cfg_dir_samples[PATH_MAX] = 0;
207 free(ptr);
208
209 read_directory();
210 return 1;
211 }
212
213 /* --------------------------------------------------------------------------------------------------------- */
214
load_sample_draw_const(void)215 static void load_sample_draw_const(void)
216 {
217 dmoz_file_t *f;
218 song_sample_t *s;
219 char sbuf[64];
220
221 draw_box(5, 12, 50, 48, BOX_THICK | BOX_INNER | BOX_INSET);
222 draw_fill_chars(6, 13, 49, 47, 0);
223
224 draw_fill_chars(64, 13, 77, 22, 0);
225 draw_box(62, 32, 72, 35, BOX_THICK | BOX_INNER | BOX_INSET);
226 draw_box(62, 36, 72, 40, BOX_THICK | BOX_INNER | BOX_INSET);
227
228 draw_box(63, 12, 77, 23, BOX_THICK | BOX_INNER | BOX_INSET);
229
230 draw_box(51, 24, 77, 29, BOX_THICK | BOX_INNER | BOX_INSET);
231 draw_fill_chars(52, 25, 76, 28, 0);
232
233 draw_box(51, 30, 77, 42, BOX_THIN | BOX_INNER | BOX_INSET);
234
235 draw_fill_chars(59, 44, 76, 47, 0);
236 draw_box(58, 43, 77, 48, BOX_THICK | BOX_INNER | BOX_INSET);
237
238 f = NULL;
239 if (current_file >= 0 && current_file < flist.num_files && flist.files[current_file]) {
240 f = flist.files[current_file];
241
242 sprintf(sbuf, "%07d", f->smp_length);
243 draw_text_len(sbuf, 13, 64, 22, 2, 0);
244
245 if (!f->smp_length && !f->smp_filename && !f->smp_flags) {
246 draw_text_len("No sample",13, 64, 21, 2, 0);
247 } else if (f->smp_flags & CHN_STEREO) {
248 draw_text_len(
249 (f->smp_flags & CHN_16BIT
250 ? "16 bit Stereo" : "8 bit Stereo"),
251 13, 64, 21, 2, 0);
252 } else {
253 draw_text_len(
254 (f->smp_flags & CHN_16BIT
255 ? "16 bit" : "8 bit"),
256 13, 64, 21, 2, 0);
257 }
258 if (f->description) {
259 draw_text_len(f->description,
260 18,
261 59, 44, 5, 0);
262 } else {
263 switch (f->type) {
264 case TYPE_DIRECTORY:
265 draw_text("Directory",
266 59, 44, 5, 0);
267 break;
268 default:
269 draw_text("Unknown format",
270 59, 44, 5, 0);
271 break;
272 };
273 }
274 sprintf(sbuf, "%07ld", (long)f->filesize);
275 draw_text(sbuf, 59, 45, 5,0);
276 get_date_string(f->timestamp, sbuf);
277 draw_text(sbuf, 59, 46, 5,0);
278 get_time_string(f->timestamp, sbuf);
279 draw_text(sbuf, 59, 47, 5,0);
280 }
281
282 /* these are exactly the same as in page_samples.c, apart from
283 * 'quality' and 'length' being one line higher */
284 draw_text("Filename", 55, 13, 0, 2);
285 draw_text("Speed", 58, 14, 0, 2);
286 draw_text("Loop", 59, 15, 0, 2);
287 draw_text("LoopBeg", 56, 16, 0, 2);
288 draw_text("LoopEnd", 56, 17, 0, 2);
289 draw_text("SusLoop", 56, 18, 0, 2);
290 draw_text("SusLBeg", 56, 19, 0, 2);
291 draw_text("SusLEnd", 56, 20, 0, 2);
292 draw_text("Quality", 56, 21, 0, 2);
293 draw_text("Length", 57, 22, 0, 2);
294
295 /* these abbreviations are sucky and lame. any suggestions? */
296 draw_text("Def. Vol.", 53, 33, 0, 2);
297 draw_text("Glb. Vol.", 53, 34, 0, 2);
298 draw_text("Vib.Speed", 53, 37, 0, 2);
299 draw_text("Vib.Depth", 53, 38, 0, 2);
300 draw_text("Vib. Rate", 53, 39, 0, 2);
301
302 draw_text("Format", 52, 44, 0, 2);
303 draw_text("Size", 54, 45, 0, 2);
304 draw_text("Date", 54, 46, 0, 2);
305 draw_text("Time", 54, 47, 0, 2);
306
307 if (fake_slot != KEYJAZZ_NOINST) {
308 s = song_get_sample(fake_slot);
309 vgamem_ovl_clear(&sample_image, 0);
310 if (s)
311 draw_sample_data(&sample_image, s);
312 else
313 vgamem_ovl_apply(&sample_image);
314 }
315 }
316
317 /* --------------------------------------------------------------------------------------------------------- */
318
_common_set_page(void)319 static void _common_set_page(void)
320 {
321 struct stat st;
322
323 /* if we have a list, the directory didn't change, and the mtime is the same, we're set */
324 if (flist.num_files > 0
325 && (status.flags & DIR_SAMPLES_CHANGED) == 0
326 && stat(cfg_dir_samples, &st) == 0
327 && st.st_mtime == directory_mtime) {
328 return;
329 }
330
331 change_dir(cfg_dir_samples);
332
333 status.flags &= ~DIR_SAMPLES_CHANGED;
334 fake_slot = KEYJAZZ_NOINST;
335 fake_slot_changed = 0;
336
337 *selected_widget = 0;
338 search_pos = -1;
339 }
340
load_sample_set_page(void)341 static void load_sample_set_page(void)
342 {
343 _library_mode = 0;
344 _common_set_page();
345 }
346
library_sample_set_page(void)347 static void library_sample_set_page(void)
348 {
349 _library_mode = 1;
350 _common_set_page();
351 }
352
353 /* --------------------------------------------------------------------------------------------------------- */
354
file_list_draw(void)355 static void file_list_draw(void)
356 {
357 int n, i, pos, fg, bg;
358 char buf[8];
359 dmoz_file_t *file;
360
361 /* there's no need to have if (files) { ... } like in the load-module page,
362 because there will always be at least "/" in the list */
363 if (top_file < 0) top_file = 0;
364 if (current_file < 0) current_file = 0;
365 for (n = top_file, pos = 13; n < flist.num_files && pos < 48; n++, pos++) {
366 file = flist.files[n];
367
368 if (n == current_file && ACTIVE_PAGE.selected_widget == 0) {
369 fg = 0;
370 bg = 3;
371 } else {
372 fg = get_type_color(file->type);
373 bg = 0;
374 }
375 draw_text(numtostr(3, n+1, buf), 2, pos, 0, 2);
376 draw_text_len(file->title ?: "", 25, 6, pos, fg, bg);
377 draw_char(168, 31, pos, 2, bg);
378 draw_text_len(file->base ?: "", 18, 32, pos, fg, bg);
379 if (file->base && search_pos > -1) {
380 if (strncasecmp(file->base,search_str,search_pos) == 0) {
381 for (i = 0 ; i < search_pos; i++) {
382 if (tolower(file->base[i]) != tolower(search_str[i]))
383 break;
384 draw_char(file->base[i], 32+i, pos, 3,1);
385 }
386 }
387 }
388 }
389
390 /* draw the info for the current file (or directory...) */
391
392 while (pos < 48)
393 draw_char(168, 31, pos++, 2, 0);
394 }
395
396 /* --------------------------------------------------------------------------------------------------------- */
397 /* Nasty mess to load a sample and prompt for stereo convert / create host instrument as necessary. */
398
399 static struct widget stereo_cvt_widgets[4];
400
_create_host_ok(void * vpage)401 static void _create_host_ok(void *vpage)
402 {
403 intptr_t page = (intptr_t) vpage;
404 song_create_host_instrument(sample_get_current());
405 if (page >= 0)
406 set_page(page);
407 }
408
_create_host_cancel(void * vpage)409 static void _create_host_cancel(void *vpage)
410 {
411 intptr_t page = (intptr_t) vpage;
412 if (page >= 0)
413 set_page(page);
414 }
415
sample_host_dialog(int newpage)416 int sample_host_dialog(int newpage)
417 {
418 /* Actually IT defaults to No when the sample slot already had a sample in it, rather than checking if
419 it was assigned to an instrument. Maybe this is better, though?
420 (Not to mention, passing around the extra state that'd be required to do it that way would be kind of
421 messy...)
422
423 also the double pointer cast sucks.
424
425 also also, IT says Ok/No here instead of Yes/No... but do I care? */
426
427 if (song_is_instrument_mode()) {
428 int used = sample_is_used_by_instrument(sample_get_current());
429 dialog_create(DIALOG_YES_NO, "Create host instrument?",
430 _create_host_ok, _create_host_cancel, used ? 1 : 0, (void *) (intptr_t) newpage);
431 return 1;
432 }
433 if (newpage >= 0)
434 set_page(newpage);
435 return 0;
436 }
437
438 static void finish_load(int cur);
stereo_cvt_complete_left(void)439 static void stereo_cvt_complete_left(void)
440 {
441 int cur = sample_get_current();
442 song_sample_t *smp;
443 smp = song_get_sample(cur);
444 sample_mono_left(smp);
445 dialog_destroy();
446 finish_load(cur);
447 }
448
stereo_cvt_complete_right(void)449 static void stereo_cvt_complete_right(void)
450 {
451 int cur = sample_get_current();
452 song_sample_t *smp;
453 smp = song_get_sample(cur);
454 sample_mono_right(smp);
455 dialog_destroy();
456 finish_load(cur);
457 }
458
stereo_cvt_complete_both(void)459 static void stereo_cvt_complete_both(void)
460 {
461 memused_songchanged();
462 dialog_destroy();
463 sample_host_dialog(PAGE_SAMPLE_LIST);
464 }
465
stereo_cvt_dialog(void)466 static void stereo_cvt_dialog(void)
467 {
468 draw_text("Loading Stereo Sample", 30, 27, 0, 2);
469 }
470
stereo_cvt_hk(struct key_event * k)471 static int stereo_cvt_hk(struct key_event *k)
472 {
473 if (!NO_MODIFIER(k->mod))
474 return 0;
475
476 /* trap the default dialog keys - we don't want to escape this dialog without running something */
477 switch (k->sym) {
478 case SDLK_RETURN:
479 printf("why am I here\n");
480 case SDLK_ESCAPE: case SDLK_o: case SDLK_c:
481 return 1;
482 case SDLK_l:
483 if (k->state == KEY_RELEASE)
484 stereo_cvt_complete_left();
485 return 1;
486 case SDLK_r:
487 if (k->state == KEY_RELEASE)
488 stereo_cvt_complete_right();
489 return 1;
490 case SDLK_s:
491 case SDLK_b:
492 if (k->state == KEY_RELEASE)
493 stereo_cvt_complete_both();
494 return 1;
495 default:
496 return 0;
497 }
498 }
499
finish_load(int cur)500 static void finish_load(int cur)
501 {
502 song_sample_t *smp;
503
504 status.flags |= SONG_NEEDS_SAVE;
505 memused_songchanged();
506 smp = song_get_sample(cur);
507 if (smp->flags & CHN_STEREO) {
508 struct dialog *dd;
509 create_button(stereo_cvt_widgets+0, 27, 30, 6,
510 0, 0, 2, 1, 1,
511 stereo_cvt_complete_left, "Left", 2);
512
513 create_button(stereo_cvt_widgets+1, 37, 30, 6,
514 1, 1, 0, 2, 2,
515 stereo_cvt_complete_both, "Both", 2);
516
517 create_button(stereo_cvt_widgets+2, 47, 30, 6,
518 2, 2, 1, 0, 0,
519 stereo_cvt_complete_right, "Right", 1);
520
521 dd = dialog_create_custom(24, 25, 33, 8,
522 stereo_cvt_widgets, 3,
523 1,
524 stereo_cvt_dialog, NULL);
525 dd->handle_key = stereo_cvt_hk;
526 return;
527 }
528 sample_host_dialog(PAGE_SAMPLE_LIST);
529 }
530
reposition_at_slash_search(void)531 static void reposition_at_slash_search(void)
532 {
533 dmoz_file_t *f;
534 int i, j, b, bl;
535
536 if (search_pos < 0) return;
537 bl = b = -1;
538 for (i = 0; i < flist.num_files; i++) {
539 f = flist.files[i];
540 if (!f || !f->base) continue;
541 for (j = 0; j < search_pos; j++) {
542 if (tolower(f->base[j]) != tolower(search_str[j]))
543 break;
544 }
545 if (bl < j) {
546 bl = j;
547 b = i;
548 }
549 }
550 if (bl > 0) {
551 current_file = b;
552 file_list_reposition();
553 }
554 }
555
556 /* on the file list, that is */
handle_enter_key(void)557 static void handle_enter_key(void)
558 {
559 dmoz_file_t *file;
560 song_sample_t *smp;
561 int cur = sample_get_current();
562
563 if (current_file < 0 || current_file >= flist.num_files) return;
564
565 file = flist.files[current_file];
566 dmoz_cache_update(cfg_dir_samples, &flist, NULL);
567 dmoz_fill_ext_data(file);
568
569 if ((file->type & (TYPE_BROWSABLE_MASK|TYPE_INST_MASK))
570 && !(file->type & TYPE_SAMPLE_MASK)) {
571 change_dir(file->path);
572 status.flags |= NEED_UPDATE;
573 } else if (_library_mode) {
574 return;
575 } else if (file->sample) {
576 /* it's already been loaded, so copy it */
577 smp = song_get_sample(cur);
578 song_copy_sample(cur, file->sample);
579 strncpy(smp->name, file->title, 25);
580 smp->name[25] = 0;
581 strncpy(smp->filename, file->base, 12);
582 smp->filename[12] = 0;
583 finish_load(cur);
584 memused_songchanged();
585 } else if (file->type & TYPE_SAMPLE_MASK) {
586 /* load the sample */
587 song_load_sample(cur, file->path);
588 finish_load(cur);
589 memused_songchanged();
590 }
591 }
592
do_discard_changes_and_move(UNUSED void * gn)593 static void do_discard_changes_and_move(UNUSED void *gn)
594 {
595 fake_slot = KEYJAZZ_NOINST;
596 fake_slot_changed = 0;
597 search_pos = -1;
598 current_file = will_move_to;
599 file_list_reposition();
600 status.flags |= NEED_UPDATE;
601 }
602
do_delete_file(UNUSED void * data)603 static void do_delete_file(UNUSED void *data)
604 {
605 int old_top_file, old_current_file;
606 char *ptr;
607
608 if (current_file < 0 || current_file >= flist.num_files)
609 return;
610
611 ptr = flist.files[current_file]->path;
612
613 /* would be neat to send it to the trash can if there is one */
614 unlink(ptr);
615
616 /* remember the list positions */
617 old_top_file = top_file;
618 old_current_file = current_file;
619
620 read_directory();
621
622 /* put the list positions back */
623 top_file = old_top_file;
624 current_file = old_current_file;
625 /* edge case: if this was the last file, move the cursor up */
626 if (current_file >= flist.num_files)
627 current_file = flist.num_files - 1;
628 file_list_reposition();
629 }
630
file_list_handle_key(struct key_event * k)631 static int file_list_handle_key(struct key_event * k)
632 {
633 dmoz_file_t *f;
634 int new_file = current_file;
635 int c = unicode_to_ascii(k->unicode);
636
637 new_file = CLAMP(new_file, 0, flist.num_files - 1);
638
639 if (!(status.flags & CLASSIC_MODE) && k->sym == SDLK_n && (k->mod & KMOD_ALT)) {
640 if (k->state == KEY_RELEASE)
641 song_toggle_multichannel_mode();
642 return 1;
643 }
644
645 if (k->mouse) {
646 if (k->x >= 6 && k->x <= 49 && k->y >= 13 && k->y <= 47) {
647 search_pos = -1;
648 if (k->mouse == MOUSE_SCROLL_UP) {
649 new_file -= MOUSE_SCROLL_LINES;
650 } else if (k->mouse == MOUSE_SCROLL_DOWN) {
651 new_file += MOUSE_SCROLL_LINES;
652 } else {
653 new_file = top_file + (k->y - 13);
654 }
655 }
656 }
657 switch (k->sym) {
658 case SDLK_UP: new_file--; search_pos = -1; break;
659 case SDLK_DOWN: new_file++; search_pos = -1; break;
660 case SDLK_PAGEUP: new_file -= 35; search_pos = -1; break;
661 case SDLK_PAGEDOWN: new_file += 35; search_pos = -1; break;
662 case SDLK_HOME: new_file = 0; search_pos = -1; break;
663 case SDLK_END: new_file = flist.num_files - 1; search_pos = -1; break;
664
665 case SDLK_ESCAPE:
666 if (search_pos < 0) {
667 if (k->state == KEY_RELEASE && NO_MODIFIER(k->mod))
668 set_page(PAGE_SAMPLE_LIST);
669 return 1;
670 } /* else fall through */
671 case SDLK_RETURN:
672 if (search_pos < 0) {
673 if (k->state == KEY_PRESS)
674 return 0;
675 handle_enter_key();
676 search_pos = -1;
677 } else {
678 if (k->state == KEY_PRESS)
679 return 1;
680 search_pos = -1;
681 status.flags |= NEED_UPDATE;
682 return 1;
683 }
684 return 1;
685 case SDLK_DELETE:
686 if (k->state == KEY_RELEASE)
687 return 1;
688 search_pos = -1;
689 if (flist.num_files > 0)
690 dialog_create(DIALOG_OK_CANCEL, "Delete file?", do_delete_file, NULL, 1, NULL);
691 return 1;
692 case SDLK_BACKSPACE:
693 if (search_pos > -1) {
694 if (k->state == KEY_RELEASE)
695 return 1;
696 search_pos--;
697 status.flags |= NEED_UPDATE;
698 reposition_at_slash_search();
699 return 1;
700 }
701 case SDLK_SLASH:
702 if (search_pos < 0) {
703 if (k->orig_sym == SDLK_SLASH) {
704 if (k->state == KEY_PRESS)
705 return 0;
706 search_pos = 0;
707 status.flags |= NEED_UPDATE;
708 return 1;
709 }
710 return 0;
711 } /* else fall through */
712 default:
713 f = flist.files[current_file];
714 if (c >= 32 && (search_pos > -1 || (f && (f->type & TYPE_DIRECTORY)))) {
715 if (k->state == KEY_RELEASE)
716 return 1;
717 if (search_pos < 0) search_pos = 0;
718 if (search_pos < PATH_MAX) {
719 search_str[search_pos++] = c;
720 reposition_at_slash_search();
721 status.flags |= NEED_UPDATE;
722 }
723 return 1;
724 }
725 if (!k->mouse) return 0;
726 }
727
728 if (k->mouse == MOUSE_CLICK) {
729 if (k->state == KEY_PRESS)
730 return 0;
731 } else if (k->mouse == MOUSE_DBLCLICK) {
732 handle_enter_key();
733 return 1;
734 } else {
735 /* prevent moving the cursor twice from a single key press */
736 if (k->state == KEY_RELEASE)
737 return 1;
738 }
739
740 new_file = CLAMP(new_file, 0, flist.num_files - 1);
741 if (new_file != current_file) {
742 if (fake_slot != KEYJAZZ_NOINST && fake_slot_changed) {
743 will_move_to = new_file;
744 dialog_create(DIALOG_YES_NO,
745 "Discard Changes?",
746 do_discard_changes_and_move,
747 NULL,
748 0, NULL);
749 return 1;
750 /* support saving? XXX */
751 /*"Save Sample?" OK Cancel*/
752 /*"Discard Changes?" OK Cancel*/
753 }
754 fake_slot = KEYJAZZ_NOINST;
755 fake_slot_changed = 0;
756 search_pos = -1;
757 current_file = new_file;
758 file_list_reposition();
759 status.flags |= NEED_UPDATE;
760 }
761 return 1;
762 }
763
load_sample_handle_key(struct key_event * k)764 static void load_sample_handle_key(struct key_event * k)
765 {
766 int n, v;
767
768 if (k->state == KEY_PRESS && k->sym == SDLK_ESCAPE && NO_MODIFIER(k->mod)) {
769 set_page(PAGE_SAMPLE_LIST);
770 return;
771 }
772 if (!NO_MODIFIER(k->mod)) return;
773
774 if (k->midi_note > -1) {
775 n = k->midi_note;
776 if (k->midi_volume > -1) {
777 v = k->midi_volume / 2;
778 } else {
779 v = KEYJAZZ_DEFAULTVOL;
780 }
781 } else if (k->is_repeat) {
782 return;
783 } else {
784 n = kbd_get_note(k);
785 v = KEYJAZZ_DEFAULTVOL;
786 if (n <= 0 || n > 120)
787 return;
788 }
789
790 handle_preload();
791 if (fake_slot != KEYJAZZ_NOINST) {
792 if (k->state == KEY_PRESS)
793 song_keydown(KEYJAZZ_INST_FAKE, KEYJAZZ_NOINST, n, v, KEYJAZZ_CHAN_CURRENT);
794 else
795 song_keyup(KEYJAZZ_INST_FAKE, KEYJAZZ_NOINST, n);
796 }
797 }
798
799 /* --------------------------------------------------------------------------------------------------------- */
handle_preload(void)800 static void handle_preload(void)
801 {
802 dmoz_file_t *file;
803
804 if (fake_slot == KEYJAZZ_NOINST && current_file >= 0 && current_file < flist.num_files) {
805 file = flist.files[current_file];
806 if (file && (file->type & TYPE_SAMPLE_MASK)) {
807 fake_slot_changed = 0;
808 fake_slot = song_preload_sample(file); // either 0 or KEYJAZZ_NOTINST
809 }
810 }
811 }
812
handle_rename_op(void)813 static void handle_rename_op(void)
814 {
815 handle_preload();
816 }
817
handle_load_copy_uint(unsigned int s,unsigned int * d)818 static void handle_load_copy_uint(unsigned int s, unsigned int *d)
819 {
820 if (s != *d) {
821 *d = s;
822 fake_slot_changed = 1;
823 }
824 }
825
handle_load_copy(song_sample_t * s)826 static void handle_load_copy(song_sample_t *s)
827 {
828 handle_load_copy_uint(widgets_loadsample[2].d.numentry.value, &s->c5speed);
829 handle_load_copy_uint(widgets_loadsample[4].d.numentry.value, &s->loop_start);
830 handle_load_copy_uint(widgets_loadsample[5].d.numentry.value, &s->loop_end);
831 handle_load_copy_uint(widgets_loadsample[7].d.numentry.value, &s->sustain_start);
832 handle_load_copy_uint(widgets_loadsample[8].d.numentry.value, &s->sustain_end);
833 handle_load_copy_uint(widgets_loadsample[9].d.thumbbar.value, &s->volume);
834 if ((unsigned int)widgets_loadsample[9].d.thumbbar.value == (s->volume>>2)) {
835 s->volume = (widgets_loadsample[9].d.thumbbar.value << 2);
836 fake_slot_changed=1;
837 }
838 handle_load_copy_uint(widgets_loadsample[10].d.thumbbar.value, &s->global_volume);
839 handle_load_copy_uint(widgets_loadsample[11].d.thumbbar.value, &s->vib_rate);
840 handle_load_copy_uint(widgets_loadsample[12].d.thumbbar.value, &s->vib_depth);
841 handle_load_copy_uint(widgets_loadsample[13].d.thumbbar.value, &s->vib_speed);
842 switch (widgets_loadsample[3].d.menutoggle.state) {
843 case 0:
844 if (s->flags & (CHN_LOOP|CHN_PINGPONGLOOP)) {
845 s->flags &= ~(CHN_LOOP|CHN_PINGPONGLOOP);
846 fake_slot_changed=1;
847 }
848 break;
849 case 1:
850 if ((s->flags & (CHN_LOOP|CHN_PINGPONGLOOP)) == CHN_LOOP) {
851 s->flags &= ~(CHN_LOOP|CHN_PINGPONGLOOP);
852 s->flags |= (CHN_LOOP);
853 fake_slot_changed=1;
854 }
855 break;
856 case 2:
857 if ((s->flags & (CHN_LOOP|CHN_PINGPONGLOOP)) == CHN_PINGPONGLOOP) {
858 s->flags &= ~(CHN_LOOP|CHN_PINGPONGLOOP);
859 s->flags |= (CHN_PINGPONGLOOP);
860 fake_slot_changed=1;
861 }
862 break;
863 };
864 switch (widgets_loadsample[6].d.menutoggle.state) {
865 case 0:
866 if (s->flags & (CHN_SUSTAINLOOP|CHN_PINGPONGSUSTAIN)) {
867 s->flags &= ~(CHN_SUSTAINLOOP|CHN_PINGPONGSUSTAIN);
868 fake_slot_changed=1;
869 }
870 break;
871 case 1:
872 if ((s->flags & (CHN_SUSTAINLOOP|CHN_PINGPONGSUSTAIN)) == CHN_SUSTAINLOOP) {
873 s->flags &= ~(CHN_SUSTAINLOOP|CHN_PINGPONGSUSTAIN);
874 s->flags |= (CHN_SUSTAINLOOP);
875 fake_slot_changed=1;
876 }
877 break;
878 case 2:
879 if ((s->flags & (CHN_SUSTAINLOOP|CHN_PINGPONGSUSTAIN)) == CHN_PINGPONGSUSTAIN) {
880 s->flags &= ~(CHN_SUSTAINLOOP|CHN_PINGPONGSUSTAIN);
881 s->flags |= (CHN_PINGPONGSUSTAIN);
882 fake_slot_changed=1;
883 }
884 break;
885 };
886 }
887
handle_load_update(void)888 static void handle_load_update(void)
889 {
890 song_sample_t *s;
891 handle_preload();
892 if (fake_slot != KEYJAZZ_NOINST) {
893 s = song_get_sample(fake_slot);
894 if (s) {
895 handle_load_copy(s);
896 song_update_playing_sample(fake_slot);
897 }
898 }
899 }
900
901
load_sample_load_page(struct page * page)902 void load_sample_load_page(struct page *page)
903 {
904 vgamem_ovl_alloc(&sample_image);
905 clear_directory();
906
907
908 create_other(widgets_loadsample + 0, 0,
909 file_list_handle_key, file_list_draw);
910 widgets_loadsample[0].accept_text = 1;
911 widgets_loadsample[0].next.tab = 1;
912
913 create_textentry(widgets_loadsample+1,
914 64, 13,
915 13,
916 1,2, 9, handle_rename_op,
917 current_filename, sizeof(current_filename)-1);
918 sample_speed_pos = 0;
919 create_numentry(widgets_loadsample+2,
920 64, 14,
921 7,
922 1,3, 9, handle_load_update,
923 0, 9999999,
924 &sample_speed_pos);
925
926 create_menutoggle(widgets_loadsample+3,
927 64, 15,
928 2, 4, 0, 9,9, handle_load_update,
929 loop_states);
930
931 sample_loop_beg = 0;
932 create_numentry(widgets_loadsample+4,
933 64, 16,
934 7,
935 3,5, 9, handle_load_update,
936 0, 9999999,
937 &sample_loop_beg);
938 sample_loop_end = 0;
939 create_numentry(widgets_loadsample+5,
940 64, 17,
941 7,
942 4,6, 9, handle_load_update,
943 0, 9999999,
944 &sample_loop_end);
945
946 create_menutoggle(widgets_loadsample+6,
947 64, 18,
948 5, 7, 0, 9,9, handle_load_update,
949 loop_states);
950
951 sample_susloop_beg = 0;
952 create_numentry(widgets_loadsample+7,
953 64, 19,
954 7,
955 6,8, 9, handle_load_update,
956 0, 9999999,
957 &sample_susloop_beg);
958 sample_susloop_end = 0;
959 create_numentry(widgets_loadsample+8,
960 64, 20,
961 7,
962 7,9, 9, handle_load_update,
963 0, 9999999,
964 &sample_susloop_end);
965
966 create_thumbbar(widgets_loadsample+9,
967 63, 33,
968 9,
969 8, 10, 0, handle_load_update,
970 0,64);
971 create_thumbbar(widgets_loadsample+10,
972 63, 34,
973 9,
974 9, 11, 0, handle_load_update,
975 0,64);
976
977 create_thumbbar(widgets_loadsample+11,
978 63, 37,
979 9,
980 10, 12, 0, handle_load_update,
981 0,64);
982 create_thumbbar(widgets_loadsample+12,
983 63, 38,
984 9,
985 11, 13, 0, handle_load_update,
986 0,32);
987 create_thumbbar(widgets_loadsample+13,
988 63, 39,
989 9,
990 12, 13, 0, handle_load_update,
991 0,255);
992
993
994 page->title = "Load Sample";
995 page->draw_const = load_sample_draw_const;
996 page->set_page = load_sample_set_page;
997 page->handle_key = load_sample_handle_key;
998 page->total_widgets = 14;
999 page->widgets = widgets_loadsample;
1000 page->help_index = HELP_GLOBAL;
1001 }
1002
library_sample_load_page(struct page * page)1003 void library_sample_load_page(struct page *page)
1004 {
1005 /* this shares all the widgets from load_sample */
1006
1007 page->title = "Sample Library (Ctrl-F3)";
1008 page->draw_const = load_sample_draw_const;
1009 page->set_page = library_sample_set_page;
1010 page->handle_key = load_sample_handle_key;
1011 page->total_widgets = 14;
1012 page->widgets = widgets_loadsample;
1013 page->help_index = HELP_GLOBAL;
1014 }
1015
1016