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, &current_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