1 /*
2 * xmcd - Motif(R) CD Audio Player/Ripper
3 *
4 * Copyright (C) 1993-2004 Ti Kan
5 * E-mail: xmcd@amb.org
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 *
21 */
22 #ifndef lint
23 static char *_cdfunc_c_ident_ = "@(#)cdfunc.c 7.279 04/03/11";
24 #endif
25
26 #include "common_d/appenv.h"
27 #include "common_d/version.h"
28 #include "common_d/util.h"
29 #include "xmcd_d/xmcd.h"
30 #include "xmcd_d/widget.h"
31 #include "xmcd_d/callback.h"
32 #include "libdi_d/libdi.h"
33 #include "cdinfo_d/cdinfo.h"
34 #include "xmcd_d/dbprog.h"
35 #include "xmcd_d/wwwwarp.h"
36 #include "xmcd_d/geom.h"
37 #include "xmcd_d/hotkey.h"
38 #include "xmcd_d/help.h"
39 #include "xmcd_d/cdfunc.h"
40 #include "cdda_d/cdda.h"
41
42
43 #define CHGDISC_DELAY 1500 /* Disc chg delay */
44
45 #define WM_FUDGE_X 10 /* Window manager decoration */
46 #define WM_FUDGE_Y 25
47 #define WIN_MINWIDTH 150 /* Dialog box minimum width */
48 #define WIN_MINHEIGHT 100 /* Dialog box minimum height */
49
50
51 /* Macro to detemine a valid VMS file name character */
52 #define IS_LEGAL_VMSFILECHAR(c) \
53 (isalnum((int) (c)) || (c) == '$' || (c) == '-' || (c) == '_')
54
55
56 /* Confirmation/Working dialog box callback info structure */
57 typedef struct {
58 Widget widget0; /* Button 1 */
59 Widget widget1; /* Button 2 */
60 Widget widget2; /* Dialog box */
61 String type; /* Callback type */
62 XtCallbackProc func; /* Callback function */
63 XtPointer data; /* Callback arg */
64 } cbinfo_t;
65
66 /* Structure to save callback function pointer and its args */
67 typedef struct {
68 XtCallbackProc func; /* Callback function */
69 Widget w; /* Widget */
70 XtPointer client_data; /* Client data */
71 XtPointer call_data; /* Call data */
72 } callback_sav_t;
73
74
75 extern widgets_t widgets;
76 extern pixmaps_t pixmaps;
77 extern appdata_t app_data;
78 extern FILE *errfp;
79
80 wlist_t *cd_brhead = NULL, /* Bitrates menu head */
81 *cd_minbrhead = NULL, /* Min bitrate menu head */
82 *cd_maxbrhead = NULL; /* Max bitrate menu head */
83
84 STATIC char keystr[3]; /* Keypad number string */
85 STATIC int keypad_mode, /* Keypad mode */
86 cdda_fader_mode; /* CDDA fader mode */
87 STATIC long tm_blinkid = -1, /* Time dpy blink timer ID */
88 ab_blinkid = -1, /* A->B dpy blink timer ID */
89 dbmode_blinkid = -1, /* Dbmode dpy blink timer ID */
90 chgdisc_dlyid = -1, /* Disc chg delay timer ID */
91 cdda_fader_id = -1, /* CDDA fader timer ID */
92 infodiag_id = -1, /* Info dialog timer ID */
93 tooltip1_id = -1, /* Tooltip popup timer ID */
94 tooltip2_id = -1; /* Tooltip popdown timer ID */
95 STATIC sword32_t warp_offset = 0; /* Track warp block offset */
96 STATIC word32_t cdda_fader_intvl = 0; /* CDDA fader interval */
97 STATIC bool_t searching = FALSE, /* Running REW or FF */
98 confirm_pend = FALSE, /* Confirm response pending */
99 confirm_resp = FALSE, /* Confirm dialog response */
100 popup_ok = FALSE, /* Are popups allowed? */
101 pseudo_warp = FALSE, /* Warp slider only flag */
102 warp_busy = FALSE, /* Warp function active */
103 chgdelay = FALSE, /* Disc change delay */
104 mode_chg = FALSE, /* Changing main window mode */
105 pathexp_active = FALSE, /* Showing exp file path */
106 tooltip_active = FALSE, /* Tooltip is active */
107 skip_next_tooltip = FALSE;
108 /* Skip the next tooltip */
109 STATIC cdinfo_client_t cdinfo_cldata; /* Client info for libcdinfo */
110 STATIC di_client_t di_cldata; /* Client info for libdi */
111 STATIC callback_sav_t override_sav; /* Mode override callback */
112 STATIC cbinfo_t cbinfo0, /* Dialog box cbinfo structs */
113 cbinfo1,
114 cbinfo2;
115 STATIC XmString xs_qual, /* Label strings */
116 xs_algo;
117
118
119 #define OPT_CATEGS 9 /* Max options categories */
120
121 /* Options menu category list entries */
122 STATIC struct {
123 char *name;
124 wlist_t *widgets;
125 } opt_categ[OPT_CATEGS+1];
126
127
128 /* Forward declaration prototypes */
129 STATIC void cd_pause_blink(curstat_t *, bool_t),
130 cd_ab_blink(curstat_t *, bool_t),
131 cd_dbmode_blink(curstat_t *, bool_t);
132
133
134 /* Macro used in cd_options() */
135 #define PATHENT_APPEND(i, str, ent, type) \
136 { \
137 if ((i) < 2 && \
138 (strcmp((ent), "CDDB") == 0 || \
139 strcmp((ent), "CDTEXT") == 0)) { \
140 (void) strcat( \
141 (str), ((type) == CDINFO_RMT) ? "CDDB" : "CDTEXT" \
142 ); \
143 (i)++; \
144 } \
145 else \
146 (void) strcat((str), (ent)); \
147 }
148
149
150
151 /***********************
152 * internal routines *
153 ***********************/
154
155
156 /*
157 * filefmt_btn
158 * Given a file format code, return its menu selection button
159 * widget.
160 *
161 * Args:
162 * fmt - file format code
163 *
164 * Return:
165 * The associated menu button widget
166 */
167 STATIC Widget
filefmt_btn(int fmt)168 filefmt_btn(int fmt)
169 {
170 switch (fmt) {
171 case FILEFMT_AU:
172 return (widgets.options.mode_fmt_au_btn);
173 case FILEFMT_WAV:
174 return (widgets.options.mode_fmt_wav_btn);
175 case FILEFMT_AIFF:
176 return (widgets.options.mode_fmt_aiff_btn);
177 case FILEFMT_AIFC:
178 return (widgets.options.mode_fmt_aifc_btn);
179 case FILEFMT_MP3:
180 return (widgets.options.mode_fmt_mp3_btn);
181 case FILEFMT_OGG:
182 return (widgets.options.mode_fmt_ogg_btn);
183 case FILEFMT_FLAC:
184 return (widgets.options.mode_fmt_flac_btn);
185 case FILEFMT_AAC:
186 return (widgets.options.mode_fmt_aac_btn);
187 case FILEFMT_MP4:
188 return (widgets.options.mode_fmt_mp4_btn);
189 case FILEFMT_RAW:
190 default:
191 return (widgets.options.mode_fmt_raw_btn);
192 }
193 /*NOTREACHED*/
194 }
195
196
197 /*
198 * filefmt_code
199 * Given a file format menu selection button widget, return its
200 * file format code.
201 *
202 * Args:
203 * The file format menu button widget
204 *
205 * Return:
206 * The associated file format code
207 */
208 STATIC int
filefmt_code(Widget w)209 filefmt_code(Widget w)
210 {
211 if (w == widgets.options.mode_fmt_raw_btn)
212 return FILEFMT_RAW;
213 else if (w == widgets.options.mode_fmt_au_btn)
214 return FILEFMT_AU;
215 else if (w == widgets.options.mode_fmt_wav_btn)
216 return FILEFMT_WAV;
217 else if (w == widgets.options.mode_fmt_aiff_btn)
218 return FILEFMT_AIFF;
219 else if (w == widgets.options.mode_fmt_aifc_btn)
220 return FILEFMT_AIFC;
221 else if (w == widgets.options.mode_fmt_mp3_btn)
222 return FILEFMT_MP3;
223 else if (w == widgets.options.mode_fmt_ogg_btn)
224 return FILEFMT_OGG;
225 else if (w == widgets.options.mode_fmt_flac_btn)
226 return FILEFMT_FLAC;
227 else if (w == widgets.options.mode_fmt_aac_btn)
228 return FILEFMT_AAC;
229 else if (w == widgets.options.mode_fmt_mp4_btn)
230 return FILEFMT_MP4;
231
232 return FILEFMT_RAW;
233 }
234
235
236 /*
237 * disc_etime_norm
238 * Return the elapsed time of the disc in seconds. This is
239 * used during normal playback.
240 *
241 * Args:
242 * s - Pointer to the curstat_t structure.
243 *
244 * Return:
245 * The disc elapsed time in seconds.
246 */
247 STATIC sword32_t
disc_etime_norm(curstat_t * s)248 disc_etime_norm(curstat_t *s)
249 {
250 sword32_t secs;
251
252 secs = (s->curpos_tot.min * 60 + s->curpos_tot.sec) -
253 (MSF_OFFSET / FRAME_PER_SEC);
254 return ((secs >= 0) ? secs : 0);
255 }
256
257
258 /*
259 * disc_etime_prog
260 * Return the elapsed time of the disc in seconds. This is
261 * used during shuffle or program mode.
262 *
263 * Args:
264 * s - Pointer to the curstat_t structure.
265 *
266 * Return:
267 * The disc elapsed time in seconds.
268 */
269 STATIC sword32_t
disc_etime_prog(curstat_t * s)270 disc_etime_prog(curstat_t *s)
271 {
272 sword32_t i,
273 secs = 0;
274
275 /* Find the time of all played tracks */
276 for (i = 0; i < ((int) s->prog_cnt) - 1; i++) {
277 secs += ((s->trkinfo[s->trkinfo[i].playorder + 1].min * 60 +
278 s->trkinfo[s->trkinfo[i].playorder + 1].sec) -
279 (s->trkinfo[s->trkinfo[i].playorder].min * 60 +
280 s->trkinfo[s->trkinfo[i].playorder].sec));
281 }
282
283 /* Find the elapsed time of the current track */
284 for (i = 0; i < MAXTRACK; i++) {
285 if (s->trkinfo[i].trkno == LEAD_OUT_TRACK)
286 break;
287
288 if (s->trkinfo[i].trkno == s->cur_trk) {
289 secs += (s->curpos_trk.min * 60 + s->curpos_trk.sec);
290 break;
291 }
292 }
293
294 return ((secs >= 0) ? secs : 0);
295 }
296
297
298 /*
299 * track_rtime
300 * Return the remaining time of the current playing track in seconds.
301 *
302 * Args:
303 * s - Pointer to the curstat_t structure.
304 *
305 * Return:
306 * The track remaining time in seconds.
307 */
308 STATIC sword32_t
track_rtime(curstat_t * s)309 track_rtime(curstat_t *s)
310 {
311 sword32_t i,
312 secs,
313 tot_sec,
314 cur_sec;
315
316 if ((i = di_curtrk_pos(s)) < 0)
317 return 0;
318
319 tot_sec = (s->trkinfo[i+1].min * 60 + s->trkinfo[i+1].sec) -
320 (s->trkinfo[i].min * 60 + s->trkinfo[i].sec);
321
322 /* "Enhanced CD" / "CD Extra" hack */
323 if (s->trkinfo[i+1].addr > s->discpos_tot.addr) {
324 tot_sec -= ((s->trkinfo[i+1].addr - s->discpos_tot.addr) /
325 FRAME_PER_SEC);
326 }
327
328 cur_sec = s->curpos_trk.min * 60 + s->curpos_trk.sec;
329 secs = tot_sec - cur_sec;
330
331 return ((secs >= 0) ? secs : 0);
332 }
333
334
335 /*
336 * disc_rtime_norm
337 * Return the remaining time of the disc in seconds. This is
338 * used during normal playback.
339 *
340 * Args:
341 * s - Pointer to the curstat_t structure.
342 *
343 * Return:
344 * The disc remaining time in seconds.
345 */
346 STATIC sword32_t
disc_rtime_norm(curstat_t * s)347 disc_rtime_norm(curstat_t *s)
348 {
349 sword32_t secs;
350
351 secs = (s->discpos_tot.min * 60 + s->discpos_tot.sec) -
352 (s->curpos_tot.min * 60 + s->curpos_tot.sec);
353
354 return ((secs >= 0) ? secs : 0);
355 }
356
357
358 /*
359 * disc_rtime_prog
360 * Return the remaining time of the disc in seconds. This is
361 * used during shuffle or program mode.
362 *
363 * Args:
364 * s - Pointer to the curstat_t structure.
365 *
366 * Return:
367 * The disc remaining time in seconds.
368 */
369 STATIC sword32_t
disc_rtime_prog(curstat_t * s)370 disc_rtime_prog(curstat_t *s)
371 {
372 sword32_t i,
373 secs = 0;
374
375 /* Find the time of all unplayed tracks */
376 for (i = s->prog_cnt; i < (int) s->prog_tot; i++) {
377 secs += ((s->trkinfo[s->trkinfo[i].playorder + 1].min * 60 +
378 s->trkinfo[s->trkinfo[i].playorder + 1].sec) -
379 (s->trkinfo[s->trkinfo[i].playorder].min * 60 +
380 s->trkinfo[s->trkinfo[i].playorder].sec));
381 }
382
383 /* Find the remaining time of the current track */
384 for (i = 0; i < MAXTRACK; i++) {
385 if (s->trkinfo[i].trkno == LEAD_OUT_TRACK)
386 break;
387
388 if (s->trkinfo[i].trkno == s->cur_trk) {
389 secs += ((s->trkinfo[i+1].min * 60 +
390 s->trkinfo[i+1].sec) -
391 (s->curpos_tot.min * 60 + s->curpos_tot.sec));
392
393 /* "Enhanced CD" / "CD Extra" hack */
394 if (s->trkinfo[i+1].addr > s->discpos_tot.addr) {
395 secs -= ((s->trkinfo[i+1].addr -
396 s->discpos_tot.addr) /
397 FRAME_PER_SEC);
398 }
399
400 break;
401 }
402 }
403
404 return ((secs >= 0) ? secs : 0);
405 }
406
407
408 /*
409 * dpy_time_blink
410 * Make the time indicator region of the main window blink.
411 * This is used when the disc is paused.
412 *
413 * Args:
414 * s - Pointer to the curstat_t structure.
415 *
416 * Return:
417 * Nothing
418 */
419 STATIC void
dpy_time_blink(curstat_t * s)420 dpy_time_blink(curstat_t *s)
421 {
422 static bool_t bstate = TRUE;
423
424 if (bstate) {
425 tm_blinkid = cd_timeout(
426 app_data.blinkoff_interval,
427 dpy_time_blink,
428 (byte_t *) s
429 );
430 dpy_time(s, TRUE);
431 }
432 else {
433 tm_blinkid = cd_timeout(
434 app_data.blinkon_interval,
435 dpy_time_blink,
436 (byte_t *) s
437 );
438 dpy_time(s, FALSE);
439 }
440 bstate = !bstate;
441 }
442
443
444 /*
445 * dpy_ab_blink
446 * Make the a->b indicator of the main window blink.
447 *
448 * Args:
449 * s - Pointer to the curstat_t structure.
450 *
451 * Return:
452 * Nothing
453 */
454 STATIC void
dpy_ab_blink(curstat_t * s)455 dpy_ab_blink(curstat_t *s)
456 {
457 static bool_t bstate = TRUE;
458
459 if (bstate) {
460 ab_blinkid = cd_timeout(
461 app_data.blinkoff_interval,
462 dpy_ab_blink,
463 (byte_t *) s
464 );
465 dpy_progmode(s, TRUE);
466 }
467 else {
468 ab_blinkid = cd_timeout(
469 app_data.blinkon_interval,
470 dpy_ab_blink,
471 (byte_t *) s
472 );
473 dpy_progmode(s, FALSE);
474 }
475 bstate = !bstate;
476 }
477
478
479 /*
480 * dpy_dbmode_blink
481 * Make the dbmode indicator of the main window blink.
482 *
483 * Args:
484 * s - Pointer to the curstat_t structure.
485 *
486 * Return:
487 * Nothing
488 */
489 STATIC void
dpy_dbmode_blink(curstat_t * s)490 dpy_dbmode_blink(curstat_t *s)
491 {
492 static bool_t bstate = TRUE;
493
494 if (bstate) {
495 dbmode_blinkid = cd_timeout(
496 app_data.blinkoff_interval,
497 dpy_dbmode_blink,
498 (byte_t *) s
499 );
500 dpy_dbmode(s, TRUE);
501 }
502 else {
503 dbmode_blinkid = cd_timeout(
504 app_data.blinkon_interval,
505 dpy_dbmode_blink,
506 (byte_t *) s
507 );
508 dpy_dbmode(s, FALSE);
509 }
510 bstate = !bstate;
511 }
512
513
514 /*
515 * dpy_keypad_ind
516 * Update the digital indicator on the keypad window.
517 *
518 * Args:
519 * s - Pointer to the curstat_t structure.
520 *
521 * Return:
522 * Nothing.
523 */
524 STATIC void
dpy_keypad_ind(curstat_t * s)525 dpy_keypad_ind(curstat_t *s)
526 {
527 char str[24],
528 trk[16],
529 time[16];
530 byte_t min,
531 sec,
532 frame;
533 XmString xs;
534 static char prevstr[24];
535
536 if (!XtIsManaged(widgets.keypad.form))
537 return;
538
539 switch (keypad_mode) {
540 case KPMODE_DISC:
541 if (s->mode == MOD_BUSY)
542 (void) strcpy(str, "disc -");
543 else if (keystr[0] == '\0')
544 (void) sprintf(str, "disc %3d", s->cur_disc);
545 else
546 (void) sprintf(str, "( disc %3d )", atoi(keystr));
547 break;
548
549 case KPMODE_TRACK:
550 if (keystr[0] == '\0') {
551 if (warp_busy) {
552 util_blktomsf(
553 warp_offset,
554 &min,
555 &sec,
556 &frame,
557 0
558 );
559
560 (void) sprintf(trk, "%02u", s->cur_trk);
561
562 (void) sprintf(time, "+%02u:%02u", min, sec);
563 }
564 else if (di_curtrk_pos(s) < 0) {
565 (void) strcpy(trk, "--");
566 (void) strcpy(time, " --:--");
567 }
568 else if (curtrk_type(s) == TYP_DATA) {
569 (void) sprintf(trk, "%02u", s->cur_trk);
570 (void) strcpy(time, " --:--");
571 }
572 else if (s->cur_idx == 0 && app_data.subq_lba) {
573 /* LBA format doesn't have meaningful lead-in
574 * time, so just display blank.
575 */
576 (void) sprintf(trk, "%02u", s->cur_trk);
577 (void) strcpy(time, " : ");
578 }
579 else {
580 util_blktomsf(
581 s->curpos_trk.addr,
582 &min,
583 &sec,
584 &frame,
585 0
586 );
587 (void) sprintf(trk, "%02u", s->cur_trk);
588 (void) sprintf(time, "%c%02u:%02u",
589 (s->cur_idx == 0) ? '-' : '+', min, sec);
590 }
591
592 if (warp_busy)
593 (void) sprintf(str, "( %s %s )", trk, time);
594 else
595 (void) sprintf(str, "%s %s", trk, time);
596 }
597 else {
598 util_blktomsf(warp_offset, &min, &sec, &frame, 0);
599 (void) sprintf(trk, "%02u", atoi(keystr));
600 (void) sprintf(time, "+%02u:%02u", min, sec);
601
602 (void) sprintf(str, "( %s %s )", trk, time);
603 }
604 break;
605
606 default:
607 return;
608 }
609
610 if (strcmp(str, prevstr) == 0) {
611 /* No change */
612 return;
613 }
614
615 xs = create_xmstring(str, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
616
617 XtVaSetValues(
618 widgets.keypad.keypad_ind,
619 XmNlabelString,
620 xs,
621 NULL
622 );
623
624 XmStringFree(xs);
625 (void) strcpy(prevstr, str);
626 }
627
628
629 /*
630 * dpy_warp
631 * Update the warp slider position.
632 *
633 * Args:
634 * s - Pointer to the curstat_t structure.
635 *
636 * Return:
637 * Nothing.
638 */
639 STATIC void
dpy_warp(curstat_t * s)640 dpy_warp(curstat_t *s)
641 {
642 int i;
643
644 if (!XtIsManaged(widgets.keypad.form) || warp_busy)
645 return;
646
647 if ((i = di_curtrk_pos(s)) < 0 || s->cur_idx == 0 ||
648 curtrk_type(s) == TYP_DATA)
649 set_warp_slider(0, TRUE);
650 else
651 set_warp_slider(unscale_warp(s, i, s->curpos_trk.addr), TRUE);
652 }
653
654
655 /*
656 * dpy_cddaperf
657 * Update CDDA performance monitor display
658 *
659 * Args:
660 * s - Pointer to the curstat_t structure.
661 *
662 * Return:
663 * Nothing.
664 */
665 STATIC void
dpy_cddaperf(curstat_t * s)666 dpy_cddaperf(curstat_t *s)
667 {
668 XmString xs;
669 char speedstr[16],
670 framesstr[16],
671 fpsstr[16],
672 ttcstr[16];
673 static char prev_speed[16] = { '-', '\0' },
674 prev_frames[16] = { '-', '\0' },
675 prev_fps[16] = { '-', '\0' },
676 prev_ttc[16] = { '0', 'h', 'r', ' ',
677 '-', '-', ':', '-', '-', '\0' };
678
679 if (PLAYMODE_IS_STD(app_data.play_mode)) {
680 (void) strcpy(speedstr, "-");
681 (void) strcpy(framesstr, "-");
682 (void) strcpy(fpsstr, "-");
683 (void) strcpy(ttcstr, "0hr --:--");
684 }
685 else if (s->mode == MOD_PLAY || s->mode == MOD_PAUSE ||
686 s->mode == MOD_SAMPLE) {
687 int fps,
688 ttc,
689 hour,
690 min,
691 sec;
692
693 if (s->tot_frm > 0) {
694 fps = s->frm_per_sec;
695
696 /* Special hack for 1.0x situation to keep the
697 * display steady and accurate. This is to work
698 * around sound card sampling rate tolerances.
699 */
700 if (fps >= (FRAME_PER_SEC-1) &&
701 fps <= (FRAME_PER_SEC+1)) {
702 fps = FRAME_PER_SEC;
703 }
704
705 if (fps > 0)
706 ttc = (int) (s->tot_frm - s->frm_played) / fps;
707 else
708 ttc = 0;
709
710 hour = ttc / 3600;
711 ttc %= 3600;
712 min = ttc / 60;
713 sec = ttc % 60;
714
715 (void) sprintf(speedstr, "%0.1f",
716 (float) fps / (float) FRAME_PER_SEC);
717 (void) sprintf(framesstr, "%d (%d%%)",
718 (int) s->frm_played,
719 (int) (s->frm_played * 100 / s->tot_frm));
720 (void) sprintf(fpsstr, "%d", fps);
721 (void) sprintf(ttcstr, "%dhr %02d:%02d",
722 hour, min, sec);
723 }
724 else {
725 (void) strcpy(speedstr, "0.0");
726 (void) strcpy(framesstr, "0 (0%)");
727 (void) strcpy(fpsstr, "-");
728 (void) strcpy(ttcstr, "0hr --:--");
729 }
730 }
731 else {
732 (void) strcpy(speedstr, "-");
733 (void) strcpy(framesstr, "-");
734 (void) strcpy(fpsstr, "-");
735 (void) strcpy(ttcstr, "0hr --:--");
736 }
737
738 if (!XtIsManaged(widgets.perfmon.form))
739 return;
740
741 if (strcmp(prev_speed, speedstr) != 0) {
742 xs = create_xmstring(
743 speedstr, NULL, XmSTRING_DEFAULT_CHARSET, FALSE
744 );
745 XtVaSetValues(widgets.perfmon.speed_ind,
746 XmNlabelString, xs,
747 NULL
748 );
749 XmStringFree(xs);
750 (void) strcpy(prev_speed, speedstr);
751 }
752
753 if (strcmp(prev_frames, framesstr) != 0) {
754 xs = create_xmstring(
755 framesstr, NULL, XmSTRING_DEFAULT_CHARSET, FALSE
756 );
757 XtVaSetValues(widgets.perfmon.frames_ind,
758 XmNlabelString, xs,
759 NULL
760 );
761 XmStringFree(xs);
762 (void) strcpy(prev_frames, framesstr);
763 }
764
765 if (strcmp(prev_fps, fpsstr) != 0) {
766 xs = create_xmstring(
767 fpsstr, NULL, XmSTRING_DEFAULT_CHARSET, FALSE
768 );
769 XtVaSetValues(widgets.perfmon.fps_ind,
770 XmNlabelString, xs,
771 NULL
772 );
773 XmStringFree(xs);
774 (void) strcpy(prev_fps, fpsstr);
775 }
776
777 if (strcmp(prev_ttc, ttcstr) != 0) {
778 xs = create_xmstring(
779 ttcstr, NULL, XmSTRING_DEFAULT_CHARSET, FALSE
780 );
781 XtVaSetValues(widgets.perfmon.ttc_ind,
782 XmNlabelString, xs,
783 NULL
784 );
785 XmStringFree(xs);
786 (void) strcpy(prev_ttc, ttcstr);
787 }
788 }
789
790
791 /*
792 * set_btn_color
793 * Set the label color of a pushbutton widget
794 *
795 * Args:
796 * w - The pushbutton widget.
797 * px - The label pixmap, if applicable.
798 * color - The pixel value of the desired color.
799 *
800 * Return:
801 * Nothing.
802 */
803 STATIC void
set_btn_color(Widget w,Pixmap px,Pixel color)804 set_btn_color(Widget w, Pixmap px, Pixel color)
805 {
806 unsigned char labtype;
807
808 XtVaGetValues(w, XmNlabelType, &labtype, NULL);
809
810 if (labtype == XmPIXMAP)
811 XtVaSetValues(w, XmNlabelPixmap, px, NULL);
812 else
813 XtVaSetValues(w, XmNforeground, color, NULL);
814 }
815
816
817 /*
818 * set_scale_color
819 * Set the indicator color of a scale widget
820 *
821 * Args:
822 * w - The scale widget.
823 * pixmap - not used.
824 * color - The pixel value of the desired color.
825 *
826 * Return:
827 * Nothing.
828 */
829 /*ARGSUSED*/
830 STATIC void
set_scale_color(Widget w,Pixmap px,Pixel color)831 set_scale_color(Widget w, Pixmap px, Pixel color)
832 {
833 XtVaSetValues(w, XmNforeground, color, NULL);
834 }
835
836
837 /*
838 * cd_pause_blink
839 * Disable or enable the time indicator blinking.
840 *
841 * Args:
842 * s - Pointer to the curstat_t structure.
843 * enable - TRUE: start blink, FALSE: stop blink
844 *
845 * Return:
846 * Nothing.
847 */
848 STATIC void
cd_pause_blink(curstat_t * s,bool_t enable)849 cd_pause_blink(curstat_t *s, bool_t enable)
850 {
851 static bool_t blinking = FALSE;
852
853 if (enable) {
854 if (!blinking) {
855 /* Start time display blink */
856 blinking = TRUE;
857 dpy_time_blink(s);
858 }
859 }
860 else if (blinking) {
861 /* Stop time display blink */
862 cd_untimeout(tm_blinkid);
863
864 tm_blinkid = -1;
865 blinking = FALSE;
866 }
867 }
868
869
870 /*
871 * cd_ab_blink
872 * Disable or enable the a->b indicator blinking.
873 *
874 * Args:
875 * s - Pointer to the curstat_t structure.
876 * enable - TRUE: start blink, FALSE: stop blink
877 *
878 * Return:
879 * Nothing.
880 */
881 STATIC void
cd_ab_blink(curstat_t * s,bool_t enable)882 cd_ab_blink(curstat_t *s, bool_t enable)
883 {
884 static bool_t blinking = FALSE;
885
886 if (enable) {
887 if (!blinking) {
888 /* Start A->B display blink */
889 blinking = TRUE;
890 dpy_ab_blink(s);
891 }
892 }
893 else if (blinking) {
894 /* Stop A->B display blink */
895 cd_untimeout(ab_blinkid);
896
897 ab_blinkid = -1;
898 blinking = FALSE;
899 }
900 }
901
902
903 /*
904 * cd_dbmode_blink
905 * Disable or enable the dbmode indicator blinking.
906 *
907 * Args:
908 * s - Pointer to the curstat_t structure.
909 * enable - TRUE: start blink, FALSE: stop blink
910 *
911 * Return:
912 * Nothing.
913 */
914 STATIC void
cd_dbmode_blink(curstat_t * s,bool_t enable)915 cd_dbmode_blink(curstat_t *s, bool_t enable)
916 {
917 static bool_t blinking = FALSE;
918
919 if (enable) {
920 if (!blinking) {
921 /* Start dbmode display blink */
922 blinking = TRUE;
923 dpy_dbmode_blink(s);
924 }
925 }
926 else if (blinking) {
927 /* Stop A->B display blink */
928 cd_untimeout(dbmode_blinkid);
929
930 dbmode_blinkid = -1;
931 blinking = FALSE;
932 }
933 }
934
935
936 /*
937 * do_chgdisc
938 * Timer function to change discs on a multi-CD changer.
939 *
940 * Args:
941 * s - Pointer to the curstat_t structure.
942 *
943 * Return:
944 * Nothing.
945 */
946 STATIC void
do_chgdisc(curstat_t * s)947 do_chgdisc(curstat_t *s)
948 {
949 /* Reset timer ID */
950 chgdisc_dlyid = -1;
951 chgdelay = FALSE;
952
953 /* Change to watch cursor */
954 cd_busycurs(TRUE, CURS_ALL);
955
956 /* Do the disc change */
957 di_chgdisc(s);
958
959 /* Update display */
960 dpy_dbmode(s, FALSE);
961 dpy_playmode(s, FALSE);
962 dpy_progmode(s, FALSE);
963
964 /* Change to normal cursor */
965 cd_busycurs(FALSE, CURS_ALL);
966 }
967
968
969 /*
970 * cdda_fadeout
971 * Timer function to perform CDDA fade-out
972 *
973 * Args:
974 * s - Pointer to the curstat_t structure.
975 *
976 * Return:
977 * Nothing.
978 */
979 STATIC void
cdda_fadeout(curstat_t * s)980 cdda_fadeout(curstat_t *s)
981 {
982 if (s->cdda_att == 0) {
983 cdda_fader_mode = CDDA_FADER_NONE;
984 return;
985 }
986
987 if (s->cdda_att > 50)
988 s->cdda_att -= 10;
989 else if (s->cdda_att > 30)
990 s->cdda_att -= 5;
991 else if (s->cdda_att > 10)
992 s->cdda_att -= 2;
993 else if (s->cdda_att > 4)
994 s->cdda_att -= 1;
995 else if (s->cdda_att >= 1)
996 s->cdda_att -= 1;
997
998 set_att_slider(s->cdda_att);
999 cdda_att(s);
1000
1001 if (s->cdda_att > 0) {
1002 cdda_fader_id = cd_timeout(
1003 cdda_fader_intvl,
1004 cdda_fadeout,
1005 (byte_t *) s
1006 );
1007 }
1008 else {
1009 cdda_fader_id = -1;
1010 cdda_fader_mode = CDDA_FADER_NONE;
1011 }
1012 }
1013
1014
1015 /*
1016 * cdda_fadein
1017 * Timer function to perform CDDA fade-in
1018 *
1019 * Args:
1020 * s - Pointer to the curstat_t structure.
1021 *
1022 * Return:
1023 * Nothing.
1024 */
1025 STATIC void
cdda_fadein(curstat_t * s)1026 cdda_fadein(curstat_t *s)
1027 {
1028 if (s->cdda_att == 100) {
1029 cdda_fader_mode = CDDA_FADER_NONE;
1030 return;
1031 }
1032
1033 if (s->cdda_att < 10)
1034 s->cdda_att += 1;
1035 else if (s->cdda_att < 30)
1036 s->cdda_att += 2;
1037 else if (s->cdda_att < 50)
1038 s->cdda_att += 5;
1039 else if (s->cdda_att < 90)
1040 s->cdda_att += 10;
1041 else
1042 s->cdda_att = 100;
1043
1044 set_att_slider(s->cdda_att);
1045 cdda_att(s);
1046
1047 if (s->cdda_att < 100) {
1048 cdda_fader_id = cd_timeout(
1049 cdda_fader_intvl,
1050 cdda_fadein,
1051 (byte_t *) s
1052 );
1053 }
1054 else {
1055 cdda_fader_id = -1;
1056 cdda_fader_mode = CDDA_FADER_NONE;
1057 }
1058 }
1059
1060
1061 /*
1062 * cd_tooltip_popdown
1063 * Pop-down the tool-tip.
1064 *
1065 * Args:
1066 * w - The tooltip shell.
1067 *
1068 * Return:
1069 * Nothing.
1070 */
1071 STATIC void
cd_tooltip_popdown(Widget w)1072 cd_tooltip_popdown(Widget w)
1073 {
1074 /* Cancel pending timers */
1075 if (tooltip1_id >= 0) {
1076 cd_untimeout(tooltip1_id);
1077 tooltip1_id = -1;
1078 }
1079 if (tooltip2_id >= 0) {
1080 cd_untimeout(tooltip2_id);
1081 tooltip2_id = -1;
1082 }
1083
1084 /* Pop down the tooltip */
1085 if (tooltip_active) {
1086 tooltip_active = FALSE;
1087 XtPopdown(w);
1088 }
1089 }
1090
1091
1092 /*
1093 * cd_tooltip_sphandler
1094 * Special widget-specific handler for tool-tips
1095 *
1096 * Args:
1097 * w - The associated control widget
1098 * ret_tlbl - the return XmString label that will be displayed on the
1099 * tool-tip. The caller should XmStringFree() this when
1100 * done.
1101 *
1102 * Return:
1103 * 0: Don't pop-up tool-tip
1104 * 1: Pop-up tooltip with the returned ret_tlbl
1105 * 2: This is not a special widget: use the default handler.
1106 */
1107 STATIC int
cd_tooltip_sphandler(Widget w,XmString * ret_tlbl)1108 cd_tooltip_sphandler(Widget w, XmString *ret_tlbl)
1109 {
1110 char *ttitle,
1111 *artist,
1112 *title,
1113 dtitle[TITLEIND_LEN];
1114 XmString xs1,
1115 xs2,
1116 xs3;
1117 curstat_t *s = curstat_addr();
1118
1119 if (w == widgets.main.disc_ind) {
1120 /* Disc display */
1121 xs1 = create_xmstring(
1122 "Disc ", NULL, XmSTRING_DEFAULT_CHARSET, FALSE
1123 );
1124 XtVaGetValues(w, XmNlabelString, &xs2, NULL);
1125 *ret_tlbl = XmStringConcat(xs1, xs2);
1126 XmStringFree(xs1);
1127 XmStringFree(xs2);
1128 }
1129 else if (w == widgets.main.track_ind) {
1130 /* Track display */
1131 xs1 = create_xmstring(
1132 "Track ", NULL, XmSTRING_DEFAULT_CHARSET, FALSE
1133 );
1134 XtVaGetValues(w, XmNlabelString, &xs2, NULL);
1135 *ret_tlbl = XmStringConcat(xs1, xs2);
1136 XmStringFree(xs1);
1137 XmStringFree(xs2);
1138 }
1139 else if (w == widgets.main.index_ind) {
1140 /* Index display */
1141 xs1 = create_xmstring(
1142 "Index ", NULL, XmSTRING_DEFAULT_CHARSET, FALSE
1143 );
1144 XtVaGetValues(w, XmNlabelString, &xs2, NULL);
1145 *ret_tlbl = XmStringConcat(xs1, xs2);
1146 XmStringFree(xs1);
1147 XmStringFree(xs2);
1148 }
1149 else if (w == widgets.main.time_ind) {
1150 /* Time display */
1151 xs1 = create_xmstring(
1152 "Time ", NULL, XmSTRING_DEFAULT_CHARSET, FALSE
1153 );
1154 XtVaGetValues(widgets.main.timemode_ind,
1155 XmNlabelString, &xs2,
1156 NULL
1157 );
1158 xs3 = XmStringConcat(xs1, xs2);
1159 XmStringFree(xs1);
1160 XmStringFree(xs2);
1161
1162 switch (geom_main_getmode()) {
1163 case MAIN_NORMAL:
1164 /* Normal mode */
1165 *ret_tlbl = xs3;
1166 break;
1167
1168 case MAIN_BASIC:
1169 /* Basic mode */
1170
1171 /* Add disc title */
1172 artist = dbprog_curartist(s);
1173 title = dbprog_curtitle(s);
1174 if (artist == NULL && title == NULL) {
1175 /* No disc artist and title */
1176 *ret_tlbl = xs3;
1177 break;
1178 }
1179
1180 dtitle[0] = '\0';
1181 if (artist != NULL && artist[0] != '\0') {
1182 (void) sprintf(dtitle, "%.127s", artist);
1183 if (title != NULL && title[0] != '\0')
1184 (void) sprintf(dtitle, "%s / %.127s",
1185 dtitle, title);
1186 }
1187 else if (title != NULL && title[0] != '\0') {
1188 (void) sprintf(dtitle, "%.127s", title);
1189 }
1190 dtitle[TITLEIND_LEN - 1] = '\0';
1191
1192 xs1 = XmStringSeparatorCreate();
1193 xs2 = XmStringConcat(xs3, xs1);
1194 XmStringFree(xs1);
1195 XmStringFree(xs3);
1196
1197 xs1 = create_xmstring(
1198 dtitle,
1199 NULL,
1200 XmSTRING_DEFAULT_CHARSET,
1201 TRUE
1202 );
1203 xs3 = XmStringConcat(xs2, xs1);
1204 XmStringFree(xs1);
1205 XmStringFree(xs2);
1206
1207 /* Add track title */
1208 ttitle = dbprog_curttitle(s);
1209 if (ttitle[0] == '\0') {
1210 /* No track title */
1211 *ret_tlbl = xs3;
1212 }
1213 else {
1214 xs1 = XmStringSeparatorCreate();
1215 xs2 = XmStringConcat(xs3, xs1);
1216 XmStringFree(xs1);
1217 XmStringFree(xs3);
1218
1219 xs1 = create_xmstring(
1220 ttitle,
1221 NULL,
1222 XmSTRING_DEFAULT_CHARSET,
1223 TRUE
1224 );
1225 xs3 = XmStringConcat(xs2, xs1);
1226 XmStringFree(xs1);
1227 XmStringFree(xs2);
1228
1229 *ret_tlbl = xs3;
1230 }
1231 break;
1232 }
1233 }
1234 else if (w == widgets.main.rptcnt_ind) {
1235 xs1 = create_xmstring(
1236 "Repeat count: ", NULL, XmSTRING_DEFAULT_CHARSET, FALSE
1237 );
1238 XtVaGetValues(w, XmNlabelString, &xs2, NULL);
1239 *ret_tlbl = XmStringConcat(xs1, xs2);
1240 XmStringFree(xs1);
1241 XmStringFree(xs2);
1242 }
1243 else if (w == widgets.main.dbmode_ind) {
1244 if (s->qmode == QMODE_MATCH) {
1245 xs1 = create_xmstring(
1246 "CD info source: ", NULL,
1247 XmSTRING_DEFAULT_CHARSET, FALSE
1248 );
1249 XtVaGetValues(w, XmNlabelString, &xs2, NULL);
1250 *ret_tlbl = XmStringConcat(xs1, xs2);
1251 XmStringFree(xs1);
1252 XmStringFree(xs2);
1253 }
1254 else if (s->qmode == QMODE_NONE) {
1255 *ret_tlbl = create_xmstring(
1256 "CD info: none", NULL,
1257 XmSTRING_DEFAULT_CHARSET, FALSE
1258 );
1259 }
1260 else {
1261 xs1 = create_xmstring(
1262 "CD info status: ", NULL,
1263 XmSTRING_DEFAULT_CHARSET, FALSE
1264 );
1265 XtVaGetValues(w, XmNlabelString, &xs2, NULL);
1266 *ret_tlbl = XmStringConcat(xs1, xs2);
1267 XmStringFree(xs1);
1268 XmStringFree(xs2);
1269 }
1270 }
1271 else if (w == widgets.main.progmode_ind) {
1272 char *str;
1273
1274 if (s->program && !s->onetrk_prog && !s->shuffle)
1275 str = "Mode: program on";
1276 else if (s->segplay == SEGP_A)
1277 str = "Mode: a->?";
1278 else if (s->segplay == SEGP_AB)
1279 str = "Mode: a->b";
1280 else
1281 str = "Mode: program off";
1282
1283 *ret_tlbl = create_xmstring(
1284 str, NULL, XmSTRING_DEFAULT_CHARSET, FALSE
1285 );
1286 }
1287 else if (w == widgets.main.timemode_ind) {
1288 xs1 = create_xmstring(
1289 "Time display mode: ", NULL,
1290 XmSTRING_DEFAULT_CHARSET, FALSE
1291 );
1292 XtVaGetValues(w, XmNlabelString, &xs2, NULL);
1293 *ret_tlbl = XmStringConcat(xs1, xs2);
1294 XmStringFree(xs1);
1295 XmStringFree(xs2);
1296 }
1297 else if (w == widgets.main.playmode_ind) {
1298 char str[STR_BUF_SZ];
1299
1300 (void) strcpy(str, "Playback mode ");
1301
1302 if (PLAYMODE_IS_STD(app_data.play_mode)) {
1303 (void) strcat(str, "(Standard): ");
1304 }
1305 else {
1306 (void) strcat(str, "(CDDA ");
1307 if ((app_data.play_mode & PLAYMODE_CDDA) != 0)
1308 (void) strcat(str, "play");
1309 if ((app_data.play_mode & PLAYMODE_FILE) != 0) {
1310 if ((app_data.play_mode & PLAYMODE_CDDA) != 0)
1311 (void) strcat(str, "/save");
1312 else
1313 (void) strcat(str, "save");
1314 }
1315 if ((app_data.play_mode & PLAYMODE_PIPE) != 0) {
1316 if ((app_data.play_mode & PLAYMODE_CDDA) != 0 ||
1317 (app_data.play_mode & PLAYMODE_FILE) != 0)
1318 (void) strcat(str, "/pipe");
1319 else
1320 (void) strcat(str, "pipe");
1321 }
1322 (void) strcat(str, "): ");
1323 }
1324
1325 xs1 = create_xmstring(
1326 str, NULL, XmSTRING_DEFAULT_CHARSET, FALSE
1327 );
1328 XtVaGetValues(w, XmNlabelString, &xs2, NULL);
1329 *ret_tlbl = XmStringConcat(xs1, xs2);
1330 XmStringFree(xs1);
1331 XmStringFree(xs2);
1332 }
1333 else if (w == widgets.main.dtitle_ind) {
1334 artist = dbprog_curartist(s);
1335 title = dbprog_curtitle(s);
1336 if (artist == NULL && title == NULL) {
1337 /* No artist / title: don't popup tooltip */
1338 return 0;
1339 }
1340 /* Use label string */
1341 XtVaGetValues(w, XmNlabelString, ret_tlbl, NULL);
1342 }
1343 else if (w == widgets.main.ttitle_ind) {
1344 ttitle = dbprog_curttitle(s);
1345 if (ttitle[0] == '\0') {
1346 /* No track title: don't popup tooltip */
1347 return 0;
1348 }
1349 /* Use label string */
1350 XtVaGetValues(w, XmNlabelString, ret_tlbl, NULL);
1351 }
1352 else
1353 return 2;
1354
1355 return 1;
1356 }
1357
1358
1359 /*
1360 * cd_tooltip_popup
1361 * Timer function to pop-up the tool-tip.
1362 *
1363 * Args:
1364 * w - The associated control widget
1365 *
1366 * Return:
1367 * Nothing.
1368 */
1369 STATIC void
cd_tooltip_popup(Widget w)1370 cd_tooltip_popup(Widget w)
1371 {
1372 Display *display;
1373 int screen;
1374 Position end_x,
1375 end_y,
1376 abs_x,
1377 abs_y,
1378 x,
1379 y;
1380 Dimension width,
1381 height,
1382 off_x,
1383 off_y,
1384 swidth,
1385 sheight;
1386 WidgetClass wc;
1387 XtWidgetGeometry geom;
1388 XmString tlbl;
1389
1390 tooltip1_id = -1;
1391 tooltip_active = TRUE;
1392 display = XtDisplay(widgets.toplevel);
1393 screen = DefaultScreen(display);
1394 swidth = (Dimension) XDisplayWidth(display, screen);
1395 sheight = (Dimension) XDisplayHeight(display, screen);
1396
1397 XtVaGetValues(w,
1398 XmNwidth, &width,
1399 XmNheight, &height,
1400 NULL
1401 );
1402
1403 /* Perform widget-specific handling */
1404 switch (cd_tooltip_sphandler(w, &tlbl)) {
1405 case 0:
1406 /* tooltip popup refused by cd_tooltip_sphandler */
1407 tooltip_active = FALSE;
1408 return;
1409 case 1:
1410 /* cd_tooltip_sphandler handled it */
1411 break;
1412 default:
1413 wc = XtClass(w);
1414
1415 if (wc == xmPushButtonWidgetClass ||
1416 wc == xmCascadeButtonWidgetClass ||
1417 wc == xmToggleButtonWidgetClass) {
1418 /* Use label string */
1419 XtVaGetValues(w, XmNlabelString, &tlbl, NULL);
1420 }
1421 else if (wc == xmScaleWidgetClass) {
1422 /* Use title string */
1423 XtVaGetValues(w, XmNtitleString, &tlbl, NULL);
1424 }
1425 else {
1426 /* Use widget name */
1427 tlbl = create_xmstring(
1428 XtName(w), NULL,
1429 XmSTRING_DEFAULT_CHARSET, FALSE
1430 );
1431 }
1432 break;
1433 }
1434
1435 /* Set the tooltip label string and hotkey mnemonic, if any. */
1436 XtVaSetValues(widgets.tooltip.tooltip_lbl, XmNlabelString, tlbl, NULL);
1437 hotkey_tooltip_mnemonic(w);
1438
1439 /* Translate to screen absolute coordinates, and add desired offsets */
1440 XtTranslateCoords(w, 0, 0, &abs_x, &abs_y);
1441
1442 /* Make sure that the tooltip window doesn't go beyond
1443 * screen boundaries.
1444 */
1445 (void) XtQueryGeometry(widgets.tooltip.tooltip_lbl, NULL, &geom);
1446
1447 off_x = width / 2;
1448 end_x = abs_x + geom.width + off_x + 2;
1449 if (end_x > (Position) swidth)
1450 off_x = -(end_x - swidth - off_x);
1451 x = abs_x + off_x;
1452 if (x < 0)
1453 x = 2;
1454
1455 off_y = height + 5;
1456 end_y = abs_y + geom.height + off_y + 2;
1457 if (end_y > (Position) sheight)
1458 off_y = -(geom.height + 5);
1459 y = abs_y + off_y;
1460
1461 /* Move tooltip widget to desired location and pop it up */
1462 XtVaSetValues(widgets.tooltip.shell, XmNx, x, XmNy, y, NULL);
1463 XtPopup(widgets.tooltip.shell, XtGrabNone);
1464
1465 /* Set timer for auto-popdown */
1466 if (app_data.tooltip_time > 0) {
1467 int n;
1468
1469 /* If the tooltip label is longer than 40 characters,
1470 * add 50mS to the tooltip time for each additional
1471 * character.
1472 */
1473 n = XmStringLength(tlbl);
1474 if (n <= 40)
1475 n = 0;
1476 else
1477 n = ((n - 40) + 1) * 50;
1478
1479 tooltip2_id = cd_timeout(
1480 app_data.tooltip_time + n,
1481 cd_tooltip_popdown,
1482 (byte_t *) widgets.tooltip.shell
1483 );
1484 }
1485
1486 XmStringFree(tlbl);
1487 }
1488
1489
1490 /*
1491 * cd_keypad_ask_dsbl
1492 * Prompt the user whether to disable the shuffle or program modes
1493 * if the user tries to use the keypad to change the track/disc.
1494 *
1495 * Args:
1496 * s - Pointer to the curstat_t structure.
1497 * func - The callback function to call if the user answers yes, and
1498 * after the shuffle or program mode is disabled.
1499 * w - The widget that normally activates the specified callback function
1500 * call_data - The callback structure pointer
1501 * call_data_len - The callback structure size
1502 *
1503 * Return:
1504 * Nothing.
1505 */
1506 STATIC void
cd_keypad_ask_dsbl(curstat_t * s,XtCallbackProc func,Widget w,XtPointer call_data,int call_data_len)1507 cd_keypad_ask_dsbl(
1508 curstat_t *s,
1509 XtCallbackProc func,
1510 Widget w,
1511 XtPointer call_data,
1512 int call_data_len
1513 )
1514 {
1515 char *str;
1516
1517 override_sav.func = func;
1518 override_sav.w = w;
1519 override_sav.client_data = (XtPointer) s;
1520 override_sav.call_data = (XtPointer) MEM_ALLOC(
1521 "override_sav.call_data",
1522 call_data_len
1523 );
1524 if (override_sav.call_data == NULL) {
1525 CD_FATAL(app_data.str_nomemory);
1526 return;
1527 }
1528 memcpy(override_sav.call_data, call_data, call_data_len);
1529
1530 str = (char *) MEM_ALLOC(
1531 "ask_dsbl_str",
1532 strlen(app_data.str_kpmodedsbl) +
1533 strlen(app_data.str_askproceed) + 20
1534 );
1535 if (str == NULL) {
1536 CD_FATAL(app_data.str_nomemory);
1537 return;
1538 }
1539 (void) sprintf(str, app_data.str_kpmodedsbl,
1540 s->shuffle ? "shuffle" : "program");
1541 (void) sprintf(str, "%s\n%s", str, app_data.str_askproceed);
1542
1543 (void) cd_confirm_popup(
1544 app_data.str_confirm,
1545 str,
1546 (XtCallbackProc) cd_keypad_dsbl_modes_yes,
1547 (XtPointer) s,
1548 (XtCallbackProc) cd_keypad_dsbl_modes_no,
1549 (XtPointer) s
1550 );
1551
1552 MEM_FREE(str);
1553 }
1554
1555
1556 /*
1557 * cd_show_path
1558 * Pop up a dialog box and display the title and file path. On
1559 * UNIX, if the path is a relative path, it will be converted into
1560 * an absolute path for display.
1561 *
1562 * Args:
1563 * title - Title string
1564 * path - Path string
1565 *
1566 * Return:
1567 * Nothing.
1568 */
1569 STATIC void
cd_show_path(char * title,char * path)1570 cd_show_path(char *title, char *path)
1571 {
1572 char *cp = NULL,
1573 *dpypath,
1574 cwd[FILE_PATH_SZ * 2] = { '\0' };
1575 static char *prevpath = NULL;
1576
1577 if (!util_newstr(&cp, path)) {
1578 CD_FATAL(app_data.str_nomemory);
1579 return;
1580 }
1581
1582 #ifndef __VMS
1583 /* Convert to absolute path if appropriate */
1584 if (*cp != '/') {
1585 if (strcmp(cp, CUR_DIR) == 0)
1586 *cp = '\0';
1587
1588 #if defined(BSDCOMPAT) && defined(USE_GETWD)
1589 if (getwd(cwd) == NULL)
1590 #else
1591 if (getcwd(cwd, FILE_PATH_SZ * 2) == NULL)
1592 #endif
1593 {
1594 MEM_FREE(cp);
1595 CD_FATAL(app_data.str_longpatherr);
1596 return;
1597 }
1598 }
1599 #endif
1600
1601 dpypath = (char *) MEM_ALLOC("dpypath", strlen(cp) + strlen(cwd) + 8);
1602 if (dpypath == NULL) {
1603 MEM_FREE(cp);
1604 CD_FATAL(app_data.str_nomemory);
1605 return;
1606 }
1607 #ifndef __VMS
1608 (void) sprintf(dpypath, "%s%s%s",
1609 cwd, (*cp == '/' || *cp == '\0') ? "" : "/", cp);
1610 #else
1611 (void) strcpy(dpypath, cp);
1612 #endif
1613
1614 MEM_FREE(cp);
1615
1616 if (prevpath != NULL && strcmp(prevpath, dpypath) == 0) {
1617 /* No change */
1618 MEM_FREE(dpypath);
1619 return;
1620 }
1621
1622 DBGPRN(DBG_GEN)(errfp, "\n%s\n%s\n", title, dpypath);
1623 CD_INFO2(title, dpypath);
1624
1625 prevpath = dpypath;
1626 }
1627
1628
1629 /*
1630 * cd_mkdirs
1631 * Called at startup time to create some needed directories, if
1632 * they aren't already there.
1633 * Currently these are:
1634 * $HOME/.xmcdcfg
1635 * $HOME/.xmcdcfg/prog
1636 * /tmp/.cdaudio
1637 *
1638 * Args:
1639 * None.
1640 *
1641 * Return:
1642 * Nothing.
1643 */
1644 STATIC void
cd_mkdirs(void)1645 cd_mkdirs(void)
1646 {
1647 char *errmsg,
1648 *homepath,
1649 path[FILE_PATH_SZ + 16];
1650 struct stat stbuf;
1651 #ifndef __VMS
1652 pid_t cpid;
1653 waitret_t wstat;
1654 #endif
1655
1656 #ifndef NOMKTMPDIR
1657 errmsg = (char *) MEM_ALLOC(
1658 "errmsg",
1659 strlen(app_data.str_tmpdirerr) + strlen(TEMP_DIR)
1660 );
1661 if (errmsg == NULL) {
1662 CD_FATAL(app_data.str_nomemory);
1663 return;
1664 }
1665
1666 /* Make temporary directory, if needed */
1667 (void) sprintf(errmsg, app_data.str_tmpdirerr, TEMP_DIR);
1668 if (util_dirstat(TEMP_DIR, &stbuf, TRUE) < 0) {
1669 if (!util_mkdir(TEMP_DIR, 0777)) {
1670 CD_FATAL(errmsg);
1671 return;
1672 }
1673 }
1674 else if (!S_ISDIR(stbuf.st_mode)) {
1675 CD_FATAL(errmsg);
1676 return;
1677 }
1678
1679 MEM_FREE(errmsg);
1680 #endif /* NOMKTMPDIR */
1681
1682 #ifndef __VMS
1683 switch (cpid = FORK()) {
1684 case -1:
1685 /* fork failed */
1686 DBGPRN(DBG_GEN)(errfp,
1687 "cd_mkdirs: fork failed (errno=%d)\n",
1688 errno);
1689 return;
1690
1691 case 0:
1692 /* Child process */
1693 popup_ok = FALSE;
1694
1695 /* Force uid and gid to original setting */
1696 if (!util_set_ougid())
1697 _exit(1);
1698
1699 break;
1700
1701 default:
1702 /* Parent: wait for child to finish */
1703 (void) util_waitchild(cpid, app_data.srv_timeout + 5,
1704 NULL, 0, FALSE, &wstat);
1705 return;
1706 }
1707 #endif
1708
1709 homepath = util_homedir(util_get_ouid());
1710 if ((int) strlen(homepath) >= FILE_PATH_SZ) {
1711 CD_FATAL(app_data.str_longpatherr);
1712 return;
1713 }
1714
1715 /* Create the per-user config directory */
1716 (void) sprintf(path, USR_CFG_PATH, homepath);
1717 if (util_dirstat(path, &stbuf, TRUE) < 0) {
1718 if (errno == ENOENT) {
1719 if (util_mkdir(path, 0755)) {
1720 DBGPRN(DBG_GEN)(errfp,
1721 "cd_mkdirs: created directory %s\n",
1722 path
1723 );
1724 }
1725 else {
1726 DBGPRN(DBG_GEN)(errfp,
1727 "cd_mkdirs: cannot mkdir %s\n",
1728 path
1729 );
1730 }
1731 }
1732 else {
1733 DBGPRN(DBG_GEN)(errfp,
1734 "cd_mkdirs: cannot stat %s\n", path);
1735 }
1736 }
1737 else if (!S_ISDIR(stbuf.st_mode)) {
1738 DBGPRN(DBG_GEN)(errfp,
1739 "cd_mkdirs: %s is not a directory.\n", path);
1740 }
1741
1742 /* Create the per-user track program directory */
1743 (void) sprintf(path, USR_PROG_PATH, homepath);
1744 #ifdef __VMS
1745 (void) sprintf(path, "%s%c", path, DIR_END);
1746 #endif
1747 if (util_dirstat(path, &stbuf, TRUE) < 0) {
1748 if (errno == ENOENT) {
1749 if (util_mkdir(path, 0755)) {
1750 DBGPRN(DBG_GEN)(errfp,
1751 "cd_mkdirs: created directory %s\n",
1752 path
1753 );
1754 }
1755 else {
1756 DBGPRN(DBG_GEN)(errfp,
1757 "cd_mkdirs: cannot mkdir %s\n",
1758 path
1759 );
1760 }
1761 }
1762 else {
1763 DBGPRN(DBG_GEN)(errfp,
1764 "cd_mkdirs: cannot stat %s\n", path);
1765 }
1766 }
1767 else if (!S_ISDIR(stbuf.st_mode)) {
1768 DBGPRN(DBG_GEN)(errfp,
1769 "cd_mkdirs: %s is not a directory.\n", path);
1770 }
1771
1772 #ifndef __VMS
1773 _exit(0);
1774 /*NOTREACHED*/
1775 #endif
1776 }
1777
1778
1779 /*
1780 * cd_strcpy
1781 * Similar to strcpy(3), but skips some punctuation characters.
1782 *
1783 * Args:
1784 * tgt - Target string buffer
1785 * src - Source string buffer
1786 *
1787 * Return:
1788 * The number of characters copied
1789 */
1790 STATIC size_t
cd_strcpy(char * tgt,char * src)1791 cd_strcpy(char *tgt, char *src)
1792 {
1793 size_t n = 0;
1794 bool_t prev_space = FALSE;
1795
1796 if (src == NULL)
1797 return 0;
1798
1799 for (; *src != '\0'; src++) {
1800 switch (*src) {
1801 case '\'':
1802 case '"':
1803 case ',':
1804 case ':':
1805 case ';':
1806 case '|':
1807 /* Skip some punctuation characters */
1808 break;
1809
1810 case ' ':
1811 case '\t':
1812 case '/':
1813 case '\\':
1814 #ifdef __VMS
1815 case '[':
1816 case ']':
1817 case '<':
1818 case '>':
1819 case '(':
1820 case ')':
1821 case '{':
1822 case '}':
1823 #endif
1824 /* Substitute these with underscores */
1825 if (!prev_space) {
1826 *tgt++ = app_data.subst_underscore ? '_' : ' ';
1827 n++;
1828 prev_space = TRUE;
1829 }
1830 break;
1831
1832 default:
1833 #ifdef __VMS
1834 if (!IS_LEGAL_VMSFILECHAR(*src) && *src != '.') {
1835 /* Skip illegal characters */
1836 break;
1837 }
1838 #else
1839 if (!isprint((int) *src)) {
1840 /* Skip unprintable characters */
1841 break;
1842 }
1843 #endif
1844
1845 *tgt++ = *src;
1846 n++;
1847 prev_space = FALSE;
1848 break;
1849 }
1850 }
1851 *tgt = '\0';
1852 return (n);
1853 }
1854
1855
1856 /*
1857 * cd_unlink
1858 * Unlink the specified file
1859 *
1860 * Args:
1861 * path - The file path to unlink
1862 *
1863 * Return:
1864 * TRUE - success
1865 * FALSE - failure
1866 */
1867 STATIC bool_t
cd_unlink(char * path)1868 cd_unlink(char *path)
1869 {
1870 #ifdef __VMS
1871 if (path == NULL)
1872 return FALSE;
1873
1874 if (UNLINK(path) < 0 && errno != ENOENT) {
1875 DBGPRN(DBG_GEN)(errfp, "unlink failed (errno=%d)\n", errno);
1876 return FALSE;
1877 }
1878 return TRUE;
1879 #else
1880 pid_t cpid;
1881 int ret,
1882 wstat;
1883 bool_t success;
1884
1885 if (path == NULL)
1886 return FALSE;
1887
1888 switch (cpid = FORK()) {
1889 case -1:
1890 DBGPRN(DBG_GEN)(errfp,
1891 "cd_unlink: fork failed (errno=%d)\n",
1892 errno);
1893 success = FALSE;
1894 break;
1895
1896 case 0:
1897 /* Child process */
1898 popup_ok = FALSE;
1899
1900 /* Force uid and gid to original setting */
1901 if (!util_set_ougid())
1902 _exit(1);
1903
1904 DBGPRN(DBG_GEN)(errfp, "Unlinking [%s]\n", path);
1905
1906 if ((ret = UNLINK(path)) != 0 && errno == ENOENT)
1907 ret = 0;
1908
1909 if (ret != 0) {
1910 DBGPRN(DBG_GEN)(errfp,
1911 "unlink failed (errno=%d)\n",
1912 errno);
1913 }
1914 _exit((int) (ret != 0));
1915 /*NOTREACHED*/
1916
1917 default:
1918 /* Parent: wait for child to finish */
1919 ret = util_waitchild(cpid, app_data.srv_timeout + 5,
1920 NULL, 0, FALSE, &wstat);
1921 if (ret < 0) {
1922 DBGPRN(DBG_GEN)(errfp,
1923 "waitpid failed (errno=%d)\n", errno);
1924 success = FALSE;
1925 }
1926 else if (WIFEXITED(wstat)) {
1927 if ((ret = WEXITSTATUS(wstat)) != 0) {
1928 DBGPRN(DBG_GEN)(errfp,
1929 "unlink child exited (status=%d)\n",
1930 ret);
1931 }
1932 success = (bool_t) (ret == 0);
1933 }
1934 else if (WIFSIGNALED(wstat)) {
1935 DBGPRN(DBG_GEN)(errfp,
1936 "unlink child killed (signal=%d)\n",
1937 WTERMSIG(wstat));
1938 success = FALSE;
1939 }
1940 else {
1941 DBGPRN(DBG_GEN)(errfp, "unlink child error\n");
1942 success = FALSE;
1943 }
1944 break;
1945 }
1946
1947 return (success);
1948 #endif
1949 }
1950
1951
1952 #ifdef __VMS
1953
1954 #define VMS_MAX_FILECOMP 39 /* Max component length in VMS */
1955
1956 /*
1957 * cd_vms_fixfname
1958 * VMS only allows a max of 39 characters per file path name
1959 * component, so truncate things if needed.
1960 *
1961 * Args:
1962 * buf - File path name buffer
1963 *
1964 * Return:
1965 * Nothing.
1966 */
1967 STATIC void
cd_vms_fixfname(char * buf)1968 cd_vms_fixfname(char *buf)
1969 {
1970 char *tmpbuf,
1971 *p,
1972 *q;
1973 int i;
1974
1975 if ((tmpbuf = (char *) MEM_ALLOC("tmpbuf", strlen(buf) + 1)) == NULL) {
1976 CD_FATAL(app_data.str_nomemory);
1977 return;
1978 }
1979
1980 for (i = 0, p = buf, q = tmpbuf; *p != '\0'; i++, p++, q++) {
1981 if (i < VMS_MAX_FILECOMP) {
1982 *q = *p;
1983 if (!IS_LEGAL_VMSFILECHAR(*p))
1984 i = -1;
1985 }
1986 else {
1987 /*
1988 * File name component truncation:
1989 * Advance to the next component separator
1990 */
1991 while (*p != '\0' && IS_LEGAL_VMSFILECHAR(*p))
1992 p++;
1993
1994 if (*p == '\0')
1995 break;
1996 *q = *p;
1997 i = -1;
1998 }
1999 }
2000 *q = '\0';
2001
2002 (void) strcpy(buf, tmpbuf);
2003
2004 MEM_FREE(tmpbuf);
2005 }
2006 #endif /* __VMS */
2007
2008
2009 /*
2010 * cd_mkfname
2011 * Construct a file name based on the supplied template, and append
2012 * it to the target string.
2013 *
2014 * Args:
2015 * s - Pointer to the curstat_t structure
2016 * trkidx - Track index number (0-based), or -1 if not applicable
2017 * tgt - The target string
2018 * tmpl - The template string
2019 * tgtlen - Target string buffer size
2020 *
2021 * Returns:
2022 * TRUE - success
2023 * FALSE - failure
2024 */
2025 STATIC bool_t
cd_mkfname(curstat_t * s,int trkidx,char * tgt,char * tmpl,int tgtlen)2026 cd_mkfname(curstat_t *s, int trkidx, char *tgt, char *tmpl, int tgtlen)
2027 {
2028 int len,
2029 n,
2030 remain;
2031 char *cp,
2032 *cp2,
2033 *p,
2034 tmp[(STR_BUF_SZ * 2) + 4];
2035 bool_t err;
2036 cdinfo_incore_t *dbp;
2037
2038 dbp = dbprog_curdb(s);
2039
2040 err = FALSE;
2041 n = 0;
2042 len = strlen(tgt);
2043 cp = tgt + len;
2044
2045 for (cp2 = tmpl; *cp2 != '\0'; cp2++, cp += n, len += n) {
2046 if ((remain = (tgtlen - len)) <= 0)
2047 break;
2048
2049 switch (*cp2) {
2050 case '%':
2051 switch (*(++cp2)) {
2052 case 'X':
2053 n = strlen(PROGNAME);
2054 if (n < remain)
2055 (void) strcpy(cp, PROGNAME);
2056 else
2057 err = TRUE;
2058 break;
2059
2060 case 'V':
2061 n = strlen(VERSION_MAJ) +
2062 strlen(VERSION_MAJ) + 1;
2063 if (n < remain)
2064 (void) sprintf(cp, "%s.%s",
2065 VERSION_MAJ,
2066 VERSION_MIN);
2067 else
2068 err = TRUE;
2069 break;
2070
2071 case 'N':
2072 p = util_loginname();
2073 n = strlen(p);
2074 if (n < remain)
2075 (void) strcpy(cp, p);
2076 else
2077 err = TRUE;
2078 break;
2079
2080 case '~':
2081 p = util_homedir(util_get_ouid());
2082 n = strlen(p);
2083 if (n < remain)
2084 (void) strcpy(cp, p);
2085 else
2086 err = TRUE;
2087 break;
2088
2089 case 'H':
2090 p = util_get_uname()->nodename;
2091 n = strlen(p);
2092 if (n < remain)
2093 (void) strcpy(cp, p);
2094 else
2095 err = TRUE;
2096 break;
2097
2098 case 'L':
2099 p = app_data.libdir;
2100 n = strlen(p);
2101 if (n < remain)
2102 (void) strcpy(cp, p);
2103 else
2104 err = TRUE;
2105 break;
2106
2107 case 'S':
2108 p = app_data.libdir;
2109 n = strlen(p) + strlen("discog") + 1;
2110 if (n < remain) {
2111 (void) strcpy(cp, p);
2112 #ifdef __VMS
2113 (void) strcat(cp, ".discog");
2114 #else
2115 (void) strcat(cp, "/discog");
2116 #endif
2117 }
2118 else
2119 err = TRUE;
2120 break;
2121
2122 case 'C':
2123 p = cdinfo_genre_path(dbp->disc.genre);
2124 n = strlen(p);
2125 if (n < remain)
2126 (void) strcpy(cp, p);
2127 else
2128 err = TRUE;
2129 break;
2130
2131 case 'I':
2132 n = 8;
2133 if (n < remain)
2134 (void) sprintf(cp, "%08x",
2135 dbp->discid);
2136 else
2137 err = TRUE;
2138 break;
2139
2140 case 'A':
2141 case 'a':
2142 p = dbp->disc.artist;
2143 if (p == NULL)
2144 p = "artist";
2145 if (*cp2 == 'a') {
2146 p = util_text_reduce(p);
2147 if (p == NULL) {
2148 CD_FATAL(app_data.str_nomemory);
2149 return FALSE;
2150 }
2151 }
2152
2153 n = strlen(p);
2154 if (n < remain)
2155 n = cd_strcpy(cp, p);
2156 else
2157 err = TRUE;
2158
2159 if (*cp2 == 'a')
2160 MEM_FREE(p);
2161 break;
2162
2163 case 'D':
2164 case 'd':
2165 p = dbp->disc.title;
2166 if (p == NULL)
2167 p = "title";
2168 if (*cp2 == 'd') {
2169 p = util_text_reduce(p);
2170 if (p == NULL) {
2171 CD_FATAL(app_data.str_nomemory);
2172 return FALSE;
2173 }
2174 }
2175
2176 n = strlen(p);
2177 if (n < remain)
2178 n = cd_strcpy(cp, p);
2179 else
2180 err = TRUE;
2181
2182 if (*cp2 == 'd')
2183 MEM_FREE(p);
2184 break;
2185
2186 case 'R':
2187 case 'r':
2188 p = dbp->track[trkidx].artist;
2189 if (p == NULL)
2190 p = "trackartist";
2191 if (*cp2 == 'r') {
2192 p = util_text_reduce(p);
2193 if (p == NULL) {
2194 CD_FATAL(app_data.str_nomemory);
2195 return FALSE;
2196 }
2197 }
2198
2199 n = strlen(p);
2200 if (n < remain)
2201 n = cd_strcpy(cp, p);
2202 else
2203 err = TRUE;
2204
2205 if (*cp2 == 'r')
2206 MEM_FREE(p);
2207 break;
2208
2209 case 'T':
2210 case 't':
2211 p = dbp->track[trkidx].title;
2212 if (p == NULL)
2213 p = "track";
2214 if (*cp2 == 't') {
2215 p = util_text_reduce(p);
2216 if (p == NULL) {
2217 CD_FATAL(app_data.str_nomemory);
2218 return FALSE;
2219 }
2220 }
2221
2222 n = strlen(p);
2223 if (n < remain)
2224 n = cd_strcpy(cp, p);
2225 else
2226 err = TRUE;
2227
2228 if (*cp2 == 't')
2229 MEM_FREE(p);
2230 break;
2231
2232 case 'B':
2233 case 'b':
2234 (void) sprintf(tmp, "%.127s%s%.127s",
2235 (dbp->disc.artist == NULL) ?
2236 "artist" : dbp->disc.artist,
2237 (dbp->disc.artist != NULL &&
2238 dbp->disc.title != NULL) ?
2239 "-" : "",
2240 (dbp->disc.title == NULL) ?
2241 "title" : dbp->disc.title);
2242 p = tmp;
2243 if (*cp2 == 'b') {
2244 p = util_text_reduce(p);
2245 if (p == NULL) {
2246 CD_FATAL(app_data.str_nomemory);
2247 return FALSE;
2248 }
2249 }
2250
2251 n = strlen(p);
2252 if (n < remain)
2253 n = cd_strcpy(cp, p);
2254 else
2255 err = TRUE;
2256
2257 if (*cp2 == 'b')
2258 MEM_FREE(p);
2259 break;
2260
2261 case '#':
2262 n = 2;
2263 if (n < remain)
2264 (void) sprintf(cp, "%02d",
2265 s->trkinfo[trkidx].trkno);
2266 else
2267 err = TRUE;
2268 break;
2269
2270 case '%':
2271 case ' ':
2272 case '\t':
2273 case '\'':
2274 case '"':
2275 case ',':
2276 case ';':
2277 case ':':
2278 case '|':
2279 case '\\':
2280 /* Skip some punctuation characters */
2281 n = 1;
2282 if (n < remain)
2283 *cp = '%';
2284 else
2285 err = TRUE;
2286 break;
2287
2288 default:
2289 n = 2;
2290 if (n < remain)
2291 *cp = '%';
2292 else
2293 err = TRUE;
2294 break;
2295 }
2296 break;
2297
2298 case ' ':
2299 case '\t':
2300 #ifdef __VMS
2301 case '<':
2302 case '>':
2303 case '(':
2304 case ')':
2305 case '{':
2306 case '}':
2307 #endif
2308 n = 1;
2309 if (n < remain) {
2310 if (app_data.subst_underscore)
2311 *cp = '_';
2312 else
2313 *cp = ' ';
2314 }
2315 else
2316 err = TRUE;
2317 break;
2318
2319 #ifndef __VMS
2320 case ';':
2321 #endif
2322 case '\'':
2323 case '"':
2324 case ',':
2325 case '|':
2326 case '\\':
2327 /* Skip some punctuation characters */
2328 n = 0;
2329 break;
2330
2331 #ifdef __VMS
2332 /* Allow these because they are special in VMS file names */
2333 case '$':
2334 case '[':
2335 case ']':
2336 case '.':
2337 case ':':
2338 case ';':
2339 case '-':
2340 case '_':
2341 n = 1;
2342 if (n < remain)
2343 *cp = *cp2;
2344 else
2345 err = TRUE;
2346 break;
2347 #endif
2348
2349 default:
2350 #ifdef __VMS
2351 if (!isalnum((int) *cp2)) {
2352 /* Skip illegal characters */
2353 n = 0;
2354 break;
2355 }
2356 #else
2357 if (!isprint((int) *cp2)) {
2358 /* Skip unprintable characters */
2359 n = 0;
2360 break;
2361 }
2362 #endif
2363
2364 n = 1;
2365 if (n < remain)
2366 *cp = *cp2;
2367 else
2368 err = TRUE;
2369 break;
2370 }
2371
2372 if (err)
2373 break;
2374 }
2375 *cp = '\0';
2376
2377 if (err) {
2378 CD_INFO(app_data.str_longpatherr);
2379 return FALSE;
2380 }
2381
2382 #ifdef __VMS
2383 /* Check file path and truncate its components if needed */
2384 cd_vms_fixfname(tgt);
2385 #endif
2386
2387 return TRUE;
2388 }
2389
2390
2391 /*
2392 * cd_mkoutpath
2393 * Construct audio output file names and check for collision
2394 *
2395 * Args:
2396 * s - Pointer to the curstat_t structure
2397 *
2398 * Return
2399 * TRUE - passed
2400 * FALSE - failed
2401 */
2402 STATIC bool_t
cd_mkoutpath(curstat_t * s)2403 cd_mkoutpath(curstat_t *s)
2404 {
2405 char *str,
2406 *dpath,
2407 *path;
2408 int i,
2409 j;
2410 struct stat stbuf;
2411 bool_t ret;
2412
2413 if (s->outf_tmpl == NULL) {
2414 CD_INFO(app_data.str_noaudiopath);
2415 return FALSE;
2416 }
2417
2418 dpath = NULL;
2419
2420 if (app_data.cdda_trkfile) {
2421 /* Normal play, program, shuffle, sample and
2422 * segment play modes
2423 */
2424 for (i = 0; i < (int) s->tot_trks; i++) {
2425 if (s->program) {
2426 bool_t inprog;
2427
2428 inprog = FALSE;
2429 for (j = 0; j < (int) s->prog_tot; j++) {
2430 if (i == s->trkinfo[j].playorder) {
2431 inprog = TRUE;
2432 break;
2433 }
2434 }
2435
2436 if (!inprog)
2437 /* This track is not in the program */
2438 continue;
2439 }
2440 else if (s->segplay == SEGP_AB) {
2441 bool_t inseg;
2442 sword32_t sot,
2443 eot;
2444
2445 inseg = FALSE;
2446 sot = s->trkinfo[i].addr;
2447 eot = s->trkinfo[i+1].addr;
2448
2449 /* "Enhanced CD" / "CD Extra" hack */
2450 if (eot > s->discpos_tot.addr)
2451 eot = s->discpos_tot.addr;
2452
2453 if (s->bp_startpos_tot.addr >= sot &&
2454 s->bp_startpos_tot.addr < eot) {
2455 /* The segment spans the beginning
2456 * of this track
2457 */
2458 inseg = TRUE;
2459 }
2460 else if (s->bp_endpos_tot.addr >= sot &&
2461 s->bp_endpos_tot.addr < eot) {
2462 /* The segment spans the end
2463 * of this track
2464 */
2465 inseg = TRUE;
2466 }
2467 else if (s->bp_startpos_tot.addr >= sot &&
2468 s->bp_endpos_tot.addr < eot) {
2469 /* The segment is contained within
2470 * this track
2471 */
2472 inseg = TRUE;
2473 }
2474
2475 if (!inseg)
2476 continue;
2477 }
2478 else if (!s->shuffle &&
2479 s->cur_trk > s->trkinfo[i].trkno) {
2480 continue;
2481 }
2482
2483 path = (char *) MEM_ALLOC("path", FILE_PATH_SZ);
2484 if (path == NULL) {
2485 CD_FATAL(app_data.str_nomemory);
2486 return FALSE;
2487 }
2488 path[0] = '\0';
2489
2490 if (!cd_mkfname(s, i, path, s->outf_tmpl,
2491 FILE_PATH_SZ)) {
2492 MEM_FREE(path);
2493 return FALSE;
2494 }
2495
2496 if (!util_newstr(&s->trkinfo[i].outfile, path)) {
2497 MEM_FREE(path);
2498 CD_FATAL(app_data.str_nomemory);
2499 return FALSE;
2500 }
2501
2502 if (dpath == NULL &&
2503 (dpath = util_dirname(path)) == NULL) {
2504 MEM_FREE(path);
2505 CD_FATAL("File dirname error");
2506 return FALSE;
2507 }
2508
2509 if (LSTAT(path, &stbuf) < 0) {
2510 MEM_FREE(path);
2511 continue;
2512 }
2513
2514 if (S_ISREG(stbuf.st_mode)) {
2515 char *fname;
2516
2517 if ((fname = util_basename(path)) == NULL) {
2518 CD_FATAL(app_data.str_nomemory);
2519 return FALSE;
2520 }
2521
2522 str = (char *) MEM_ALLOC("str",
2523 strlen(app_data.str_overwrite) +
2524 strlen(fname) + 4
2525 );
2526 if (str == NULL) {
2527 MEM_FREE(fname);
2528 CD_FATAL(app_data.str_nomemory);
2529 return FALSE;
2530 }
2531
2532 (void) sprintf(str, "%s\n\n%s",
2533 fname,
2534 app_data.str_overwrite);
2535
2536 ret = cd_confirm_popup(
2537 app_data.str_confirm,
2538 str,
2539 (XtCallbackProc) NULL,
2540 NULL,
2541 (XtCallbackProc) NULL,
2542 NULL
2543 );
2544
2545 MEM_FREE(str);
2546 MEM_FREE(fname);
2547
2548 if (ret)
2549 ret = cd_unlink(path);
2550
2551 if (!ret) {
2552 if (s->onetrk_prog) {
2553 s->onetrk_prog = FALSE;
2554 dbprog_progclear(s);
2555 }
2556
2557 MEM_FREE(path);
2558 return FALSE;
2559 }
2560 }
2561 else {
2562 str = (char *) MEM_ALLOC("str",
2563 strlen(app_data.str_audiopathexists) +
2564 strlen(path) + 4
2565 );
2566 if (str == NULL) {
2567 CD_FATAL(app_data.str_nomemory);
2568 return FALSE;
2569 }
2570
2571 (void) sprintf(str, "%s\n\n%s",
2572 path,
2573 app_data.str_audiopathexists);
2574
2575 CD_INFO(str);
2576
2577 MEM_FREE(str);
2578 MEM_FREE(path);
2579 return FALSE;
2580 }
2581
2582 MEM_FREE(path);
2583 }
2584 }
2585 else {
2586 path = (char *) MEM_ALLOC("path", FILE_PATH_SZ);
2587 if (path == NULL) {
2588 CD_FATAL(app_data.str_nomemory);
2589 return FALSE;
2590 }
2591 path[0] = '\0';
2592
2593 if (!cd_mkfname(s, -1, path, s->outf_tmpl, FILE_PATH_SZ)) {
2594 MEM_FREE(path);
2595 return FALSE;
2596 }
2597
2598 if (!util_newstr(&s->trkinfo[0].outfile, path)) {
2599 MEM_FREE(path);
2600 CD_FATAL(app_data.str_nomemory);
2601 return FALSE;
2602 }
2603
2604 if (LSTAT(path, &stbuf) == 0) {
2605 if (S_ISREG(stbuf.st_mode)) {
2606 char *fname;
2607
2608 if ((fname = util_basename(path)) == NULL) {
2609 CD_FATAL(app_data.str_nomemory);
2610 return FALSE;
2611 }
2612
2613 str = (char *) MEM_ALLOC("str",
2614 strlen(app_data.str_overwrite) +
2615 strlen(fname) + 4
2616 );
2617 if (str == NULL) {
2618 MEM_FREE(fname);
2619 CD_FATAL(app_data.str_nomemory);
2620 return FALSE;
2621 }
2622
2623 (void) sprintf(str, "%s\n\n%s",
2624 fname, app_data.str_overwrite);
2625
2626 ret = cd_confirm_popup(
2627 app_data.str_confirm,
2628 str,
2629 (XtCallbackProc) NULL,
2630 NULL,
2631 (XtCallbackProc) NULL,
2632 NULL
2633 );
2634
2635 MEM_FREE(str);
2636 MEM_FREE(fname);
2637
2638 if (ret)
2639 ret = cd_unlink(path);
2640
2641 if (!ret) {
2642 if (s->onetrk_prog) {
2643 s->onetrk_prog = FALSE;
2644 dbprog_progclear(s);
2645 }
2646
2647 MEM_FREE(path);
2648 return FALSE;
2649 }
2650 }
2651 else {
2652 str = (char *) MEM_ALLOC("str",
2653 strlen(app_data.str_audiopathexists) +
2654 strlen(path) + 4
2655 );
2656 if (str == NULL) {
2657 CD_FATAL(app_data.str_nomemory);
2658 return FALSE;
2659 }
2660
2661 (void) sprintf(str, "%s\n\n%s",
2662 path,
2663 app_data.str_audiopathexists);
2664
2665 CD_INFO(str);
2666
2667 MEM_FREE(str);
2668 MEM_FREE(path);
2669 return FALSE;
2670 }
2671 }
2672
2673 if ((dpath = util_dirname(path)) == NULL) {
2674 MEM_FREE(path);
2675 CD_FATAL("File dirname error");
2676 return FALSE;
2677 }
2678
2679 MEM_FREE(path);
2680 }
2681
2682 /* Show output directory path in pop-up info window */
2683 cd_show_path(app_data.str_outdir, dpath);
2684
2685 MEM_FREE(dpath);
2686
2687 return TRUE;
2688 }
2689
2690
2691 /*
2692 * cd_ckpipeprog
2693 * Construct pipe to program path and check for validity
2694 *
2695 * Args:
2696 * s - Pointer to the curstat_t structure
2697 *
2698 * Return
2699 * TRUE - passed
2700 * FALSE - failed
2701 */
2702 /*ARGSUSED*/
2703 STATIC bool_t
cd_ckpipeprog(curstat_t * s)2704 cd_ckpipeprog(curstat_t *s)
2705 {
2706 char *str;
2707
2708 if (app_data.pipeprog == NULL) {
2709 CD_INFO(app_data.str_noprog);
2710 return FALSE;
2711 }
2712
2713 if (!util_checkcmd(app_data.pipeprog)) {
2714 str = (char *) MEM_ALLOC("str",
2715 strlen(app_data.pipeprog) +
2716 strlen(app_data.str_cannotinvoke) + 8
2717 );
2718 if (str == NULL) {
2719 CD_FATAL(app_data.str_nomemory);
2720 return FALSE;
2721 }
2722
2723 (void) sprintf(str, app_data.str_cannotinvoke,
2724 app_data.pipeprog);
2725 CD_INFO(str);
2726
2727 MEM_FREE(str);
2728 return FALSE;
2729 }
2730
2731 return TRUE;
2732 }
2733
2734
2735 /***********************
2736 * public routines *
2737 ***********************/
2738
2739
2740 /*
2741 * fix_outfile_path
2742 * Fix the CDDA audio output file path to make sure there is a proper
2743 * suffix based on the file format. Also, replace white spaces in
2744 * the file path string with underscores.
2745 *
2746 * Args:
2747 * s - Pointer to the curstat_t structure.
2748 *
2749 * Return:
2750 * Nothing.
2751 */
2752 void
fix_outfile_path(curstat_t * s)2753 fix_outfile_path(curstat_t *s)
2754 {
2755 filefmt_t *fmp;
2756 char *suf,
2757 *cp,
2758 tmp[FILE_PATH_SZ * 2 + 8];
2759
2760 /* File suffix */
2761 if ((fmp = cdda_filefmt(app_data.cdda_filefmt)) == NULL)
2762 suf = "";
2763 else
2764 suf = fmp->suf;
2765
2766 if (s->outf_tmpl == NULL) {
2767 /* No default outfile, set it */
2768 (void) sprintf(tmp,
2769 #ifdef __VMS
2770 "%%S.%%C.%%I]%s%s",
2771 #else
2772 "%%S/%%C/%%I/%s%s",
2773 #endif
2774 app_data.cdda_trkfile ? FILEPATH_TRACK : FILEPATH_DISC,
2775 suf
2776 );
2777
2778 if (!util_newstr(&s->outf_tmpl, tmp)) {
2779 CD_FATAL(app_data.str_nomemory);
2780 return;
2781 }
2782 }
2783 else if ((cp = strrchr(s->outf_tmpl, '.')) != NULL) {
2784 char *cp2 = strrchr(s->outf_tmpl, DIR_END);
2785
2786 if (cp2 == NULL || cp > cp2) {
2787 /* Change suffix if necessary */
2788 if (strcmp(cp, suf) != 0) {
2789 *cp = '\0';
2790 (void) strncpy(tmp, s->outf_tmpl,
2791 FILE_PATH_SZ - strlen(suf) - 1);
2792 (void) strcat(tmp, suf);
2793
2794 if (!util_newstr(&s->outf_tmpl, tmp)) {
2795 CD_FATAL(app_data.str_nomemory);
2796 return;
2797 }
2798 }
2799 }
2800 else {
2801 /* No suffix, add one */
2802 (void) strncpy(tmp, s->outf_tmpl,
2803 FILE_PATH_SZ - strlen(suf) - 1);
2804 (void) strcat(tmp, suf);
2805
2806 if (!util_newstr(&s->outf_tmpl, tmp)) {
2807 CD_FATAL(app_data.str_nomemory);
2808 return;
2809 }
2810 }
2811 }
2812 else {
2813 /* No suffix, add one */
2814 (void) strncpy(tmp, s->outf_tmpl,
2815 FILE_PATH_SZ - strlen(suf) - 1);
2816 (void) strcat(tmp, suf);
2817
2818 if (!util_newstr(&s->outf_tmpl, tmp)) {
2819 CD_FATAL(app_data.str_nomemory);
2820 return;
2821 }
2822 }
2823 }
2824
2825
2826 /*
2827 * curtrk_type
2828 * Return the track type of the currently playing track.
2829 *
2830 * Args:
2831 * s - Pointer to the curstat_t structure.
2832 *
2833 * Return:
2834 * TYP_AUDIO or TYP_DATA.
2835 */
2836 byte_t
curtrk_type(curstat_t * s)2837 curtrk_type(curstat_t *s)
2838 {
2839 sword32_t i;
2840
2841 if ((i = di_curtrk_pos(s)) >= 0)
2842 return (s->trkinfo[i].type);
2843
2844 return TYP_AUDIO;
2845 }
2846
2847
2848 /*
2849 * dpy_disc
2850 * Update the disc number display region of the main window.
2851 *
2852 * Args:
2853 * s - Pointer to the curstat_t structure.
2854 *
2855 * Return:
2856 * Nothing
2857 */
2858 void
dpy_disc(curstat_t * s)2859 dpy_disc(curstat_t *s)
2860 {
2861 XmString xs;
2862 char str[8];
2863 static char prev[8] = { '\0' };
2864
2865 (void) sprintf(str, "%u", s->cur_disc);
2866
2867 if (strcmp(str, prev) == 0)
2868 /* No change, just return */
2869 return;
2870
2871 xs = create_xmstring(str, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
2872
2873 XtVaSetValues(
2874 widgets.main.disc_ind,
2875 XmNlabelString, xs,
2876 NULL
2877 );
2878
2879 XmStringFree(xs);
2880
2881 /* Update the keypad indicator */
2882 dpy_keypad_ind(s);
2883
2884 (void) strcpy(prev, str);
2885 }
2886
2887
2888 /*
2889 * dpy_track
2890 * Update the track number display region of the main window.
2891 *
2892 * Args:
2893 * s - Pointer to the curstat_t structure.
2894 *
2895 * Return:
2896 * Nothing
2897 */
2898 void
dpy_track(curstat_t * s)2899 dpy_track(curstat_t *s)
2900 {
2901 XmString xs;
2902 char str[4];
2903 static char prev[4] = { '\0' };
2904 static int sav_trk = -1;
2905
2906
2907 if (s->cur_trk != sav_trk) {
2908 /* Update CD Info/program window current track display */
2909 dbprog_curtrkupd(s);
2910 /* Update main window track title display */
2911 dpy_ttitle(s);
2912 /* Update the keypad indicator */
2913 dpy_keypad_ind(s);
2914 /* Update warp slider */
2915 dpy_warp(s);
2916 }
2917
2918 sav_trk = s->cur_trk;
2919
2920 if (s->cur_trk < 0 || s->mode == MOD_BUSY ||
2921 s->mode == MOD_NODISC)
2922 (void) strcpy(str, "--");
2923 else if (s->time_dpy == T_REMAIN_DISC) {
2924 if (s->shuffle || s->program) {
2925 if (s->prog_tot >= s->prog_cnt)
2926 (void) sprintf(str, "-%u",
2927 s->prog_tot - s->prog_cnt);
2928 else
2929 (void) strcpy(str, "-0");
2930 }
2931 else
2932 (void) sprintf(str, "-%u",
2933 s->tot_trks - di_curtrk_pos(s) - 1);
2934 }
2935 else
2936 (void) sprintf(str, "%02u", s->cur_trk);
2937
2938 if (strcmp(str, prev) == 0 && !mode_chg)
2939 /* No change, just return */
2940 return;
2941
2942 xs = create_xmstring(
2943 str, NULL,
2944 (geom_main_getmode() == MAIN_NORMAL) ? CHSET1 : CHSET2,
2945 FALSE
2946 );
2947
2948 XtVaSetValues(
2949 widgets.main.track_ind,
2950 XmNlabelString, xs,
2951 NULL
2952 );
2953
2954 XmStringFree(xs);
2955
2956 (void) strcpy(prev, str);
2957
2958 /* Update CDDA save-to-file expanded path display if appropriate */
2959 if (pathexp_active && app_data.cdda_trkfile) {
2960 cd_filepath_exp(
2961 widgets.options.mode_pathexp_btn,
2962 (XtPointer) s,
2963 NULL
2964 );
2965 }
2966 }
2967
2968
2969 /*
2970 * dpy_index
2971 * Update the index number display region of the main window.
2972 *
2973 * Args:
2974 * s - Pointer to the curstat_t structure.
2975 *
2976 * Return:
2977 * Nothing
2978 */
2979 void
dpy_index(curstat_t * s)2980 dpy_index(curstat_t *s)
2981 {
2982 XmString xs;
2983 char str[4];
2984 static char prev[4] = { '\0' };
2985
2986 if (s->cur_idx <= 0 || s->mode == MOD_BUSY ||
2987 s->mode == MOD_NODISC || s->mode == MOD_STOP)
2988 (void) strcpy(str, "--");
2989 else
2990 (void) sprintf(str, "%02u", s->cur_idx);
2991
2992 if (strcmp(str, prev) == 0)
2993 /* No change, just return */
2994 return;
2995
2996 xs = create_xmstring(str, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
2997
2998 XtVaSetValues(
2999 widgets.main.index_ind,
3000 XmNlabelString, xs,
3001 NULL
3002 );
3003
3004 XmStringFree(xs);
3005
3006 (void) strcpy(prev, str);
3007 }
3008
3009
3010 /*
3011 * dpy_time
3012 * Update the time display region of the main window.
3013 *
3014 * Args:
3015 * s - Pointer to the curstat_t structure.
3016 * blank - Whether the display region should be blanked.
3017 *
3018 * Return:
3019 * Nothing
3020 */
3021 void
dpy_time(curstat_t * s,bool_t blank)3022 dpy_time(curstat_t *s, bool_t blank)
3023 {
3024 sword32_t time_sec;
3025 XmString xs;
3026 char str[16];
3027 static char prev[16] = { 'j', 'u', 'n', 'k', '\0' };
3028
3029 if (blank)
3030 str[0] = '\0';
3031 else if (s->mode == MOD_BUSY)
3032 (void) strcpy(str, app_data.str_busy);
3033 else if (s->mode == MOD_NODISC)
3034 (void) strcpy(str, app_data.str_nodisc);
3035 else if (curtrk_type(s) == TYP_DATA)
3036 (void) strcpy(str, app_data.str_data);
3037 else if (s->mode == MOD_STOP)
3038 (void) strcpy(str, " --:--");
3039 else {
3040 switch (s->time_dpy) {
3041 case T_ELAPSED_TRACK:
3042 if (app_data.subq_lba && s->cur_idx == 0)
3043 /* LBA format doesn't have meaningful lead-in
3044 * time, so just display blank.
3045 */
3046 (void) strcpy(str, " : ");
3047 else
3048 (void) sprintf(str, "%s%02u:%02u",
3049 (s->cur_idx == 0) ? "-" : "+",
3050 s->curpos_trk.min,
3051 s->curpos_trk.sec);
3052 break;
3053
3054 case T_ELAPSED_SEG:
3055 if (s->segplay != SEGP_AB) {
3056 (void) strcpy(str, " : ");
3057 break;
3058 }
3059 time_sec = (s->curpos_tot.addr -
3060 s->bp_startpos_tot.addr) / FRAME_PER_SEC;
3061 (void) sprintf(str, "+%02u:%02u",
3062 time_sec / 60, time_sec % 60);
3063 break;
3064
3065 case T_ELAPSED_DISC:
3066 if (s->shuffle || s->program) {
3067 if (s->cur_idx == 0) {
3068 (void) strcpy(str, " : ");
3069 break;
3070 }
3071 else
3072 time_sec = disc_etime_prog(s);
3073 }
3074 else
3075 time_sec = disc_etime_norm(s);
3076
3077 (void) sprintf(str, "+%02u:%02u",
3078 time_sec / 60, time_sec % 60);
3079 break;
3080
3081 case T_REMAIN_TRACK:
3082 if (s->cur_idx == 0)
3083 (void) strcpy(str, " : ");
3084 else {
3085 time_sec = track_rtime(s);
3086 (void) sprintf(str, "-%02u:%02u",
3087 time_sec / 60, time_sec % 60);
3088 }
3089 break;
3090
3091 case T_REMAIN_SEG:
3092 if (s->segplay != SEGP_AB) {
3093 (void) strcpy(str, " : ");
3094 break;
3095 }
3096 time_sec = (s->bp_endpos_tot.addr -
3097 s->curpos_tot.addr) / FRAME_PER_SEC;
3098 (void) sprintf(str, "-%02u:%02u",
3099 time_sec / 60, time_sec % 60);
3100 break;
3101
3102 case T_REMAIN_DISC:
3103 if (s->shuffle || s->program) {
3104 if (s->cur_idx == 0) {
3105 (void) strcpy(str, " : ");
3106 break;
3107 }
3108 else
3109 time_sec = disc_rtime_prog(s);
3110 }
3111 else
3112 time_sec = disc_rtime_norm(s);
3113
3114 (void) sprintf(str, "-%02u:%02u",
3115 time_sec / 60, time_sec % 60);
3116 break;
3117
3118 default:
3119 (void) strcpy(str, "??:??");
3120 break;
3121 }
3122 }
3123
3124 if (s->mode == MOD_PAUSE)
3125 cd_pause_blink(s, TRUE);
3126 else
3127 cd_pause_blink(s, FALSE);
3128
3129 /* Update the keypad indicator */
3130 dpy_keypad_ind(s);
3131
3132 /* Update warp slider */
3133 dpy_warp(s);
3134
3135 /* Display CDDA performance */
3136 dpy_cddaperf(s);
3137
3138 if (strcmp(str, prev) == 0 && !mode_chg)
3139 /* No change: just return */
3140 return;
3141
3142 xs = create_xmstring(
3143 str, NULL,
3144 (geom_main_getmode() == MAIN_NORMAL) ? CHSET1 : CHSET2,
3145 FALSE
3146 );
3147
3148 XtVaSetValues(
3149 widgets.main.time_ind,
3150 XmNlabelString, xs,
3151 NULL
3152 );
3153
3154 XmStringFree(xs);
3155 (void) strcpy(prev, str);
3156 }
3157
3158
3159 /*
3160 * dpy_dtitle
3161 * Update the disc title display region of the main window.
3162 *
3163 * Args:
3164 * s - Pointer to the curstat_t structure.
3165 *
3166 * Return:
3167 * Nothing
3168 */
3169 void
dpy_dtitle(curstat_t * s)3170 dpy_dtitle(curstat_t *s)
3171 {
3172 XmString xs;
3173 char *artist,
3174 *title,
3175 str[TITLEIND_LEN],
3176 icontitle[TITLEIND_LEN + 16];
3177 static char prev[TITLEIND_LEN];
3178
3179 str[0] = '\0';
3180 if (!chgdelay) {
3181 artist = dbprog_curartist(s);
3182 title = dbprog_curtitle(s);
3183 if (artist != NULL) {
3184 (void) sprintf(str, "%.127s", artist);
3185 if (title != NULL)
3186 (void) sprintf(str, "%s / %.127s", str, title);
3187 }
3188 else if (title != NULL)
3189 (void) sprintf(str, "%.127s", title);
3190
3191 str[TITLEIND_LEN - 1] = '\0';
3192 }
3193
3194 if (strcmp(str, prev) == 0)
3195 /* No change: just return */
3196 return;
3197
3198 xs = create_xmstring(str, NULL, XmSTRING_DEFAULT_CHARSET, TRUE);
3199
3200 XtVaSetValues(
3201 widgets.main.dtitle_ind,
3202 XmNlabelString, xs,
3203 NULL
3204 );
3205
3206 XmStringFree(xs);
3207
3208 (void) strcpy(prev, str);
3209
3210 /* Update icon title */
3211 if (str[0] == '\0')
3212 (void) strcpy(icontitle, PROGNAME);
3213 else {
3214 chset_conv_t *up;
3215 char *a = NULL;
3216
3217 /* Convert from UTF-8 to local charset if possible */
3218 if ((up = util_chset_open(CHSET_UTF8_TO_LOCALE)) == NULL)
3219 return;
3220
3221 if (!util_chset_conv(up, str, &a, FALSE) &&
3222 !util_newstr(&a, str)) {
3223 util_chset_close(up);
3224 return;
3225 }
3226
3227 util_chset_close(up);
3228
3229 #ifdef USE_SGI_DESKTOP
3230 (void) strcpy(icontitle, a);
3231 #else
3232 (void) sprintf(icontitle, "%s: %s", PROGNAME, a);
3233 #endif
3234
3235 MEM_FREE(a);
3236 }
3237
3238 XtVaSetValues(widgets.toplevel,
3239 XmNiconName, icontitle,
3240 NULL
3241 );
3242 }
3243
3244
3245 /*
3246 * dpy_ttitle
3247 * Update the track title display region of the main window.
3248 *
3249 * Args:
3250 * s - Pointer to the curstat_t structure.
3251 *
3252 * Return:
3253 * Nothing
3254 */
3255 void
dpy_ttitle(curstat_t * s)3256 dpy_ttitle(curstat_t *s)
3257 {
3258 XmString xs;
3259 char str[TITLEIND_LEN];
3260 static char prev[TITLEIND_LEN];
3261
3262 if (chgdelay)
3263 str[0] = '\0';
3264 else {
3265 (void) strncpy(str, dbprog_curttitle(s), TITLEIND_LEN - 1);
3266 str[TITLEIND_LEN - 1] = '\0';
3267 }
3268
3269 if (strcmp(str, prev) == 0)
3270 /* No change: just return */
3271 return;
3272
3273 xs = create_xmstring(str, NULL, XmSTRING_DEFAULT_CHARSET, TRUE);
3274
3275 XtVaSetValues(
3276 widgets.main.ttitle_ind,
3277 XmNlabelString, xs,
3278 NULL
3279 );
3280
3281 XmStringFree(xs);
3282
3283 (void) strcpy(prev, str);
3284 }
3285
3286
3287 /*
3288 * dpy_rptcnt
3289 * Update the repeat count indicator of the main window.
3290 *
3291 * Args:
3292 * s - Pointer to the curstat_t structure.
3293 *
3294 * Return:
3295 * Nothing
3296 */
3297 void
dpy_rptcnt(curstat_t * s)3298 dpy_rptcnt(curstat_t *s)
3299 {
3300 XmString xs;
3301 char str[16];
3302 static char prevstr[16];
3303
3304 if (s->repeat && (s->mode == MOD_PLAY || s->mode == MOD_PAUSE))
3305 (void) sprintf(str, "%u", s->rptcnt);
3306 else
3307 (void) strcpy(str, "-");
3308
3309 if (strcmp(str, prevstr) == 0)
3310 /* No change */
3311 return;
3312
3313 xs = create_xmstring(str, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
3314
3315 XtVaSetValues(
3316 widgets.main.rptcnt_ind,
3317 XmNlabelString, xs,
3318 NULL
3319 );
3320
3321 XmStringFree(xs);
3322
3323 (void) strcpy(prevstr, str);
3324 }
3325
3326
3327 /*
3328 * dpy_dbmode
3329 * Update the cdinfo indicator of the main window.
3330 *
3331 * Args:
3332 * s - Pointer to the curstat_t structure.
3333 * blank - Whether the indicator should be blanked
3334 *
3335 * Return:
3336 * Nothing
3337 */
3338 void
dpy_dbmode(curstat_t * s,bool_t blank)3339 dpy_dbmode(curstat_t *s, bool_t blank)
3340 {
3341 cdinfo_incore_t *dbp;
3342 char *str;
3343 XmString xs;
3344 static char prev[16] = { 'j', 'u', 'n', 'k', '\0' };
3345
3346 dbp = dbprog_curdb(s);
3347
3348 if (blank)
3349 str = "";
3350 else {
3351 switch (s->qmode) {
3352 case QMODE_MATCH:
3353 if (dbp->flags & CDINFO_FROMLOC)
3354 str = app_data.str_local;
3355 else if (dbp->flags & CDINFO_FROMCDT)
3356 str = app_data.str_cdtext;
3357 else
3358 str = app_data.str_cddb;
3359 break;
3360 case QMODE_WAIT:
3361 str = app_data.str_query;
3362 break;
3363 case QMODE_ERR:
3364 str = app_data.str_error;
3365 break;
3366 case QMODE_NONE:
3367 default:
3368 str = "";
3369 break;
3370 }
3371 }
3372
3373 if (strcmp(prev, str) == 0)
3374 /* No change: just return */
3375 return;
3376
3377 if (str == app_data.str_cddb) {
3378 char cddb_str[8];
3379 XmString xs1,
3380 xs2;
3381
3382 (void) strcpy(cddb_str, "CDDB");
3383 if (cdinfo_cddb_ver() == 2) {
3384 xs1 = create_xmstring(cddb_str, NULL, CHSET1, FALSE);
3385 cddb_str[0] = (char) '\262'; /* like <SUP>2</SUP> */
3386 cddb_str[1] = '\0';
3387 xs2 = create_xmstring(cddb_str, NULL, CHSET2, FALSE);
3388 xs = XmStringConcat(xs1, xs2);
3389 XmStringFree(xs1);
3390 XmStringFree(xs2);
3391 }
3392 else
3393 xs = create_xmstring(cddb_str, NULL, CHSET1, FALSE);
3394 }
3395 else if (str == app_data.str_cdtext)
3396 xs = create_xmstring(str, NULL, CHSET1, FALSE);
3397 else
3398 xs = create_xmstring(str, NULL, CHSET2, FALSE);
3399
3400 XtVaSetValues(
3401 widgets.main.dbmode_ind,
3402 XmNlabelString, xs,
3403 NULL
3404 );
3405
3406 XmStringFree(xs);
3407
3408 (void) strncpy(prev, str, sizeof(prev) - 1);
3409 prev[sizeof(prev) - 1] = '\0';
3410
3411 if (s->qmode == QMODE_WAIT)
3412 cd_dbmode_blink(s, TRUE);
3413 else
3414 cd_dbmode_blink(s, FALSE);
3415 }
3416
3417
3418 /*
3419 * dpy_progmode
3420 * Update the prog indicator of the main window.
3421 *
3422 * Args:
3423 * s - Pointer to the curstat_t structure.
3424 * blank - Whether the indicator should be blanked
3425 *
3426 * Return:
3427 * Nothing
3428 */
3429 void
dpy_progmode(curstat_t * s,bool_t blank)3430 dpy_progmode(curstat_t *s, bool_t blank)
3431 {
3432 XmString xs;
3433 char *str;
3434 bool_t state;
3435 static char prev[16] = { 'j', 'u', 'n', 'k', '\0' };
3436
3437 state = (bool_t) (s->program && !s->onetrk_prog && !s->shuffle);
3438
3439 if (blank)
3440 str = "";
3441 else {
3442 if (s->segplay == SEGP_A)
3443 str = "a->?";
3444 else if (s->segplay == SEGP_AB)
3445 str = "a->b";
3446 else if (state)
3447 str = app_data.str_progmode;
3448 else
3449 str = "";
3450 }
3451
3452 if (strcmp(prev, str) == 0)
3453 /* No change: just return */
3454 return;
3455
3456 xs = create_xmstring(str, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
3457
3458 XtVaSetValues(
3459 widgets.main.progmode_ind,
3460 XmNlabelString, xs,
3461 NULL
3462 );
3463
3464 XmStringFree(xs);
3465
3466 (void) strncpy(prev, str, sizeof(prev) - 1);
3467 prev[sizeof(prev) - 1] = '\0';
3468
3469 if (s->segplay == SEGP_NONE && strcmp(prev, "a->b") == 0)
3470 /* Cancel a->b mode in segments window */
3471 dbprog_segments_cancel(s);
3472
3473 /* Set segments window set/clear button sensitivity */
3474 dbprog_segments_setmode(s);
3475
3476 if (s->segplay == SEGP_A)
3477 cd_ab_blink(s, TRUE);
3478 else
3479 cd_ab_blink(s, FALSE);
3480
3481 }
3482
3483
3484 /*
3485 * dpy_timemode
3486 * Update the time mode indicator of the main window.
3487 *
3488 * Args:
3489 * s - Pointer to the curstat_t structure.
3490 *
3491 * Return:
3492 * Nothing
3493 */
3494 void
dpy_timemode(curstat_t * s)3495 dpy_timemode(curstat_t *s)
3496 {
3497 String str;
3498 XmString xs;
3499 static byte_t prev = 0xff;
3500
3501 if (prev == s->time_dpy)
3502 /* No change: just return */
3503 return;
3504
3505 switch (s->time_dpy) {
3506 case T_ELAPSED_TRACK:
3507 str = app_data.str_elapse;
3508 break;
3509
3510 case T_ELAPSED_SEG:
3511 str = app_data.str_elapseseg;
3512 break;
3513
3514 case T_ELAPSED_DISC:
3515 str = app_data.str_elapsedisc;
3516 break;
3517
3518 case T_REMAIN_TRACK:
3519 str = app_data.str_remaintrk;
3520 break;
3521
3522 case T_REMAIN_SEG:
3523 str = app_data.str_remainseg;
3524 break;
3525
3526 case T_REMAIN_DISC:
3527 str = app_data.str_remaindisc;
3528 break;
3529 default:
3530 /* Invalid mode */
3531 return;
3532 }
3533
3534 xs = create_xmstring(str, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
3535
3536 XtVaSetValues(
3537 widgets.main.timemode_ind,
3538 XmNlabelString, xs,
3539 NULL
3540 );
3541
3542 XmStringFree(xs);
3543
3544 prev = s->time_dpy;
3545 }
3546
3547
3548 /*
3549 * dpy_playmode
3550 * Update the play mode indicator of the main window.
3551 *
3552 * Args:
3553 * s - Pointer to the curstat_t structure.
3554 * blank - Whether the indicator should be blanked
3555 *
3556 * Return:
3557 * Nothing
3558 */
3559 void
dpy_playmode(curstat_t * s,bool_t blank)3560 dpy_playmode(curstat_t *s, bool_t blank)
3561 {
3562 char *str;
3563 XmString xs;
3564 bool_t expbtn_sens = FALSE;
3565 static char prev[64] = { 'j', 'u', 'n', 'k', '\0' };
3566
3567 if (blank)
3568 str = "";
3569 else {
3570 switch (s->mode) {
3571 case MOD_PLAY:
3572 str = app_data.str_play;
3573 if ((app_data.play_mode & PLAYMODE_FILE) != 0)
3574 expbtn_sens = TRUE;
3575 break;
3576 case MOD_PAUSE:
3577 str = app_data.str_pause;
3578 if ((app_data.play_mode & PLAYMODE_FILE) != 0)
3579 expbtn_sens = TRUE;
3580 break;
3581 case MOD_SAMPLE:
3582 str = app_data.str_sample;
3583 if ((app_data.play_mode & PLAYMODE_FILE) != 0)
3584 expbtn_sens = TRUE;
3585 break;
3586 case MOD_STOP:
3587 str = app_data.str_ready;
3588 break;
3589 default:
3590 str = "";
3591 break;
3592 }
3593 }
3594
3595 if (strcmp(prev, str) == 0)
3596 /* No change: just return */
3597 return;
3598
3599 xs = create_xmstring(str, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
3600
3601 XtVaSetValues(
3602 widgets.main.playmode_ind,
3603 XmNlabelString, xs,
3604 NULL
3605 );
3606
3607 XmStringFree(xs);
3608
3609 (void) strncpy(prev, str, sizeof(prev) - 1);
3610 prev[sizeof(prev) - 1] = '\0';
3611
3612 /* Set segments window set button sensitivity */
3613 dbprog_segments_setmode(s);
3614
3615 /* Set CDDA file path expand button sensitivity */
3616 XtSetSensitive(
3617 widgets.options.mode_pathexp_btn,
3618 (Boolean) expbtn_sens
3619 );
3620
3621 /* Pop down CDDA file path expand dialog if appropriate */
3622 if (pathexp_active && !expbtn_sens)
3623 cd_info2_popdown(NULL);
3624 }
3625
3626
3627 /*
3628 * dpy_all
3629 * Update all indicator of the main window.
3630 *
3631 * Args:
3632 * s - Pointer to the curstat_t structure.
3633 *
3634 * Return:
3635 * Nothing
3636 */
3637 void
dpy_all(curstat_t * s)3638 dpy_all(curstat_t *s)
3639 {
3640 dpy_disc(s);
3641 dpy_track(s);
3642 dpy_index(s);
3643 dpy_time(s, FALSE);
3644 dpy_dtitle(s);
3645 dpy_rptcnt(s);
3646 dpy_dbmode(s, FALSE);
3647 dpy_progmode(s, FALSE);
3648 dpy_timemode(s);
3649 dpy_playmode(s, FALSE);
3650 }
3651
3652
3653 /*
3654 * set_lock_btn
3655 * Set the lock button state
3656 *
3657 * Args:
3658 * state - TRUE=in, FALSE=out
3659 *
3660 * Return:
3661 * Nothing.
3662 */
3663 void
set_lock_btn(bool_t state)3664 set_lock_btn(bool_t state)
3665 {
3666 XmToggleButtonSetState(
3667 widgets.main.lock_btn, (Boolean) state, False
3668 );
3669 }
3670
3671
3672 /*
3673 * set_repeat_btn
3674 * Set the repeat button state
3675 *
3676 * Args:
3677 * state - TRUE=in, FALSE=out
3678 *
3679 * Return:
3680 * Nothing.
3681 */
3682 void
set_repeat_btn(bool_t state)3683 set_repeat_btn(bool_t state)
3684 {
3685 XmToggleButtonSetState(
3686 widgets.main.repeat_btn, (Boolean) state, False
3687 );
3688 }
3689
3690
3691 /*
3692 * set_shuffle_btn
3693 * Set the shuffle button state
3694 *
3695 * Args:
3696 * state - TRUE=in, FALSE=out
3697 *
3698 * Return:
3699 * Nothing.
3700 */
3701 void
set_shuffle_btn(bool_t state)3702 set_shuffle_btn(bool_t state)
3703 {
3704 XmToggleButtonSetState(
3705 widgets.main.shuffle_btn, (Boolean) state, False
3706 );
3707 }
3708
3709
3710 /*
3711 * set_vol_slider
3712 * Set the volume control slider position
3713 *
3714 * Args:
3715 * val - The value setting.
3716 *
3717 * Return:
3718 * Nothing.
3719 */
3720 void
set_vol_slider(int val)3721 set_vol_slider(int val)
3722 {
3723 /* Check bounds */
3724 if (val > 100)
3725 val = 100;
3726 if (val < 0)
3727 val = 0;
3728
3729 XmScaleSetValue(widgets.main.level_scale, val);
3730 }
3731
3732
3733 /*
3734 * set_warp_slider
3735 * Set the track warp slider position
3736 *
3737 * Args:
3738 * val - The value setting.
3739 * autoupd - This is an auto-update.
3740 *
3741 * Return:
3742 * Nothing.
3743 */
3744 void
set_warp_slider(int val,bool_t autoupd)3745 set_warp_slider(int val, bool_t autoupd)
3746 {
3747 if (autoupd && (keypad_mode != KPMODE_TRACK || keystr[0] != '\0')) {
3748 /* User using keypad: no updates */
3749 return;
3750 }
3751
3752 /* Check bounds */
3753 if (val > 255)
3754 val = 255;
3755 if (val < 0)
3756 val = 0;
3757
3758 pseudo_warp = TRUE;
3759 XmScaleSetValue(widgets.keypad.warp_scale, val);
3760 pseudo_warp = FALSE;
3761 }
3762
3763
3764 /*
3765 * set_bal_slider
3766 * Set the balance control slider position
3767 *
3768 * Args:
3769 * val - The value setting.
3770 *
3771 * Return:
3772 * Nothing.
3773 */
3774 void
set_bal_slider(int val)3775 set_bal_slider(int val)
3776 {
3777 /* Check bounds */
3778 if (val > 50)
3779 val = 50;
3780 if (val < -50)
3781 val = -50;
3782
3783 XmScaleSetValue(widgets.options.bal_scale, val);
3784 }
3785
3786
3787 /*
3788 * set_qualval_slider
3789 * Set the VBR quality factor slider position
3790 *
3791 * Args:
3792 * val - The value setting.
3793 *
3794 * Return:
3795 * Nothing.
3796 */
3797 void
set_qualval_slider(int val)3798 set_qualval_slider(int val)
3799 {
3800 XmString xs,
3801 xs2;
3802 char str[16];
3803
3804 /* Check bounds */
3805 if (val > 10)
3806 val = 10;
3807 if (val < 1)
3808 val = 1;
3809
3810 (void) sprintf(str, ": %d", val);
3811 xs = create_xmstring(str, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
3812 xs2 = XmStringConcat(xs_qual, xs);
3813 XtVaSetValues(widgets.options.qualval_lbl,
3814 XmNlabelString, xs2,
3815 NULL
3816 );
3817 XmStringFree(xs2);
3818 XmStringFree(xs);
3819
3820 XmScaleSetValue(widgets.options.qualval_scl, val);
3821 }
3822
3823
3824 /*
3825 * set_algo_slider
3826 * Set the compression algorithm slider position
3827 *
3828 * Args:
3829 * val - The value setting.
3830 *
3831 * Return:
3832 * Nothing.
3833 */
3834 void
set_algo_slider(int val)3835 set_algo_slider(int val)
3836 {
3837 XmString xs,
3838 xs2;
3839 char str[16];
3840
3841 /* Check bounds */
3842 if (val > 10)
3843 val = 10;
3844 if (val < 1)
3845 val = 1;
3846
3847 (void) sprintf(str, ": %d", val);
3848 xs = create_xmstring(str, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
3849 xs2 = XmStringConcat(xs_algo, xs);
3850 XtVaSetValues(widgets.options.compalgo_lbl,
3851 XmNlabelString, xs2,
3852 NULL
3853 );
3854 XmStringFree(xs2);
3855 XmStringFree(xs);
3856
3857 XmScaleSetValue(widgets.options.compalgo_scl, val);
3858 }
3859
3860
3861 /*
3862 * set_att_slider
3863 * Set the CDDA attenuator slider position
3864 *
3865 * Args:
3866 * val - The value setting.
3867 *
3868 * Return:
3869 * Nothing.
3870 */
3871 void
set_att_slider(int val)3872 set_att_slider(int val)
3873 {
3874 /* Check bounds */
3875 if (val > 100)
3876 val = 100;
3877 if (val < 0)
3878 val = 0;
3879
3880 XmScaleSetValue(widgets.options.cdda_att_scale, val);
3881 }
3882
3883
3884 /*
3885 * set_text_string
3886 * Set the text widget string.
3887 *
3888 * Args:
3889 * w - The text widget
3890 * str - The input text string
3891 * utf8 - If TRUE, the input string is UTF-8 encoded and will be
3892 * converted to a local charset before setting the widget
3893 *
3894 * Return:
3895 * Nothing.
3896 */
3897 void
set_text_string(Widget w,char * str,bool_t utf8)3898 set_text_string(Widget w, char *str, bool_t utf8)
3899 {
3900 chset_conv_t *up;
3901 char *a = NULL;
3902
3903 if (w == (Widget) NULL)
3904 return;
3905
3906 if (str == NULL || *str == '\0' || !utf8) {
3907 XmTextSetString(w, str);
3908 return;
3909 }
3910
3911 /* Convert from UTF-8 encoding */
3912 if ((up = util_chset_open(CHSET_UTF8_TO_LOCALE)) == NULL)
3913 return;
3914
3915 /* We set the roe flag of the util_chset_conv() function to
3916 * TRUE here because we don't want to cause a failed chset
3917 * conversion to corrupt the string, in case the user submits
3918 * the entry back to CDDB without editing these fields.
3919 * This also avoids a bogus CDINFO_CHANGED flag from being set.
3920 */
3921 if (!util_chset_conv(up, str, &a, TRUE)) {
3922 util_chset_close(up);
3923 return;
3924 }
3925
3926 util_chset_close(up);
3927
3928 if (a != NULL) {
3929 XmTextSetString(w, a);
3930 MEM_FREE(a);
3931 }
3932 }
3933
3934
3935 /*
3936 * get_text_string
3937 * Get the text widget string.
3938 *
3939 * Args:
3940 * w - The text widget
3941 * utf8 - If TRUE, the output string should be UTF-8 encoded and
3942 * a conversion from local charset will be made.
3943 *
3944 * Return:
3945 * The return string. This is internally allocated and should be
3946 * freed by the caller via MEM_FREE when done.
3947 */
3948 char *
get_text_string(Widget w,bool_t utf8)3949 get_text_string(Widget w, bool_t utf8)
3950 {
3951 chset_conv_t *up;
3952 char *str,
3953 *a = NULL;
3954
3955 if ((str = XmTextGetString(w)) == NULL)
3956 return NULL;
3957
3958 if (*str == '\0' || !utf8) {
3959 if (!util_newstr(&a, str))
3960 return NULL;
3961 XtFree(str);
3962 return (a);
3963 }
3964
3965 /* Convert to UTF-8 encoding */
3966 if ((up = util_chset_open(CHSET_LOCALE_TO_UTF8)) == NULL) {
3967 if (!util_newstr(&a, str))
3968 return NULL;
3969 XtFree(str);
3970 return (a);
3971 }
3972
3973 if (!util_chset_conv(up, str, &a, TRUE)) {
3974 util_chset_close(up);
3975 if (!util_newstr(&a, str))
3976 return NULL;
3977 XtFree(str);
3978 return (a);
3979 }
3980
3981 util_chset_close(up);
3982 XtFree(str);
3983
3984 return (a);
3985 }
3986
3987
3988 /*
3989 * create_xmstring
3990 * Create a Motif compound string from the input text string.
3991 *
3992 * Args:
3993 * in_text - The input UTF-8 text string
3994 * def_text - The fallback string to use if in_text is NULL or empty
3995 * tag - The tag component to be associated with the text
3996 * utf8 - If TRUE, the input string is UTF-8 encoded and will be
3997 * converted to a local charset.
3998 *
3999 * Return:
4000 * The compound string. The caller should call XmStringFree on
4001 * this when done using it.
4002 */
4003 XmString
create_xmstring(char * in_text,char * def_text,char * tag,bool_t utf8)4004 create_xmstring(char *in_text, char *def_text, char *tag, bool_t utf8)
4005 {
4006 chset_conv_t *up;
4007 char *a = NULL;
4008 XmString xs;
4009
4010 if (in_text == NULL || *in_text == '\0') {
4011 if (def_text != NULL)
4012 xs = XmStringCreateSimple(def_text);
4013 else
4014 xs = XmStringCreateSimple("");
4015
4016 return (xs);
4017 }
4018
4019 if (!utf8) {
4020 xs = XmStringCreateLtoR(in_text, tag);
4021 return (xs);
4022 }
4023
4024 /* Convert from UTF-8 to local charset if possible */
4025 if ((up = util_chset_open(CHSET_UTF8_TO_LOCALE)) == NULL) {
4026 xs = XmStringCreateLtoR(in_text, tag);
4027 return (xs);
4028 }
4029 if (!util_chset_conv(up, in_text, &a, FALSE) &&
4030 !util_newstr(&a, in_text)) {
4031 util_chset_close(up);
4032 xs = XmStringCreateLtoR(in_text, tag);
4033 return (xs);
4034 }
4035 util_chset_close(up);
4036
4037 xs = XmStringCreateLtoR(a, tag);
4038 MEM_FREE(a);
4039
4040 return (xs);
4041 }
4042
4043
4044 /*
4045 * scale_warp
4046 * Scale track warp value (0-255) to the number of CD logical audio
4047 * blocks.
4048 *
4049 * Args:
4050 * s - Pointer to the curstat_t structure.
4051 * pos - Track position.
4052 * val - Warp value.
4053 *
4054 * Return:
4055 * The number of CD logical audio blocks
4056 */
4057 int
scale_warp(curstat_t * s,int pos,int val)4058 scale_warp(curstat_t *s, int pos, int val)
4059 {
4060 int n;
4061 sword32_t eot;
4062
4063 eot = s->trkinfo[pos+1].addr;
4064
4065 /* "Enhanced CD" / "CD Extra" hack */
4066 if (eot > s->discpos_tot.addr)
4067 eot = s->discpos_tot.addr;
4068
4069 n = val * (eot - s->trkinfo[pos].addr) / 0xff;
4070 return ((n > 0) ? (n - 1) : n);
4071 }
4072
4073
4074 /*
4075 * unscale_warp
4076 * Scale the number of CD logical audio blocks to track warp
4077 * value (0-255).
4078 * Args:
4079 * s - Pointer to the curstat_t structure.
4080 * pos - Track position.
4081 * val - Number of logical audio blocks.
4082 *
4083 * Return:
4084 * The warp value
4085 */
4086 int
unscale_warp(curstat_t * s,int pos,int val)4087 unscale_warp(curstat_t *s, int pos, int val)
4088 {
4089 sword32_t eot;
4090
4091 eot = s->trkinfo[pos+1].addr;
4092
4093 /* "Enhanced CD" / "CD Extra" hack */
4094 if (eot > s->discpos_tot.addr)
4095 eot = s->discpos_tot.addr;
4096
4097 return (val * 0xff / (eot - s->trkinfo[pos].addr));
4098 }
4099
4100
4101 /*
4102 * cd_timeout
4103 * Alarm clock callback facility
4104 *
4105 * Args:
4106 * msec - When msec milliseconds has elapsed, the callback
4107 * occurs.
4108 * handler - Pointer to the callback function.
4109 * arg - An argument passed to the callback function.
4110 *
4111 * Return:
4112 * Timeout ID.
4113 */
4114 long
cd_timeout(word32_t msec,void (* handler)(),byte_t * arg)4115 cd_timeout(word32_t msec, void (*handler)(), byte_t *arg)
4116 {
4117 /* Note: This code assumes that sizeof(XtIntervalId) <= sizeof(long)
4118 * If this is not true then cd_timeout/cd_untimeout will not work
4119 * correctly.
4120 */
4121 return ((long)
4122 XtAppAddTimeOut(
4123 XtWidgetToApplicationContext(widgets.toplevel),
4124 (unsigned long) msec,
4125 (XtTimerCallbackProc) handler,
4126 (XtPointer) arg
4127 )
4128 );
4129 }
4130
4131
4132 /*
4133 * cd_untimeout
4134 * Cancel a pending alarm configured with cd_timeout.
4135 *
4136 * Args:
4137 * id - The timeout ID
4138 *
4139 * Return:
4140 * Nothing.
4141 */
4142 void
cd_untimeout(long id)4143 cd_untimeout(long id)
4144 {
4145 /* Note: This code assumes that sizeof(XtIntervalId) <= sizeof(long)
4146 * If this is not true then cd_timeout/cd_untimeout will not work
4147 * correctly.
4148 */
4149 XtRemoveTimeOut((XtIntervalId) id);
4150 }
4151
4152
4153 /*
4154 * cd_beep
4155 * Beep the workstation speaker.
4156 *
4157 * Args:
4158 * Nothing.
4159 *
4160 * Return:
4161 * Nothing.
4162 */
4163 void
cd_beep(void)4164 cd_beep(void)
4165 {
4166 XBell(XtDisplay(widgets.toplevel), 50);
4167 }
4168
4169
4170 /*
4171 * cd_dialog_setpos
4172 * Set up a dialog box's position to be near the pointer cursor.
4173 * This is used before popping up the dialog boxes.
4174 *
4175 * Args:
4176 * w - The dialog box shell widget to set up
4177 *
4178 * Return:
4179 * Nothing.
4180 */
4181 void
cd_dialog_setpos(Widget w)4182 cd_dialog_setpos(Widget w)
4183 {
4184 Display *display;
4185 int screen,
4186 junk,
4187 ptr_x,
4188 ptr_y;
4189 unsigned int keybtn;
4190 Position win_x,
4191 win_y;
4192 Dimension win_width,
4193 win_height,
4194 swidth,
4195 sheight;
4196 Window rootwin,
4197 childwin;
4198
4199 display = XtDisplay(widgets.toplevel);
4200 screen = DefaultScreen(display);
4201 swidth = (Dimension) XDisplayWidth(display, screen);
4202 sheight = (Dimension) XDisplayHeight(display, screen);
4203
4204 /* Get current pointer location */
4205 XQueryPointer(
4206 display,
4207 XtWindow(widgets.toplevel),
4208 &rootwin, &childwin,
4209 &ptr_x, &ptr_y,
4210 &junk, &junk,
4211 &keybtn
4212 );
4213
4214 /* Get dialog window location and dimensions */
4215 XtVaGetValues(w,
4216 XmNx, &win_x,
4217 XmNy, &win_y,
4218 XmNwidth, &win_width,
4219 XmNheight, &win_height,
4220 NULL
4221 );
4222
4223 if (win_width < WIN_MINWIDTH)
4224 win_width = WIN_MINWIDTH;
4225 if (win_height < WIN_MINHEIGHT)
4226 win_height = WIN_MINHEIGHT;
4227
4228 if ((Position) ptr_x > (Position) win_x &&
4229 (Position) ptr_x < (Position) (win_x + win_width) &&
4230 (Position) ptr_y > (Position) win_y &&
4231 (Position) ptr_y < (Position) (win_y + win_height)) {
4232 /* Pointer cursor is already within the dialog window */
4233 return;
4234 }
4235
4236 /* Set new position for dialog window */
4237 win_x = (Position) (ptr_x - (win_width / 2));
4238 win_y = (Position) (ptr_y - (win_height / 2));
4239
4240 if (win_x < 0)
4241 win_x = 0;
4242 else if ((Position) (win_x + win_width + WM_FUDGE_X) >
4243 (Position) swidth)
4244 win_x = (Position) (swidth - win_width - WM_FUDGE_X);
4245
4246 if (win_y < 0)
4247 win_y = 0;
4248 else if ((Position) (win_y + win_height + WM_FUDGE_Y) >
4249 (Position) sheight)
4250 win_y = (Position) (sheight - win_height - WM_FUDGE_Y);
4251
4252 XtVaSetValues(w, XmNx, win_x, XmNy, win_y, NULL);
4253 }
4254
4255
4256 /*
4257 * cd_info_popup
4258 * Pop up the information message dialog box.
4259 *
4260 * Args:
4261 * title - The title bar text string.
4262 * msg - The information message text string.
4263 *
4264 * Return:
4265 * Nothing.
4266 */
4267 void
cd_info_popup(char * title,char * msg)4268 cd_info_popup(char *title, char *msg)
4269 {
4270 XmString xs;
4271
4272 if (!popup_ok) {
4273 (void) fprintf(errfp, "%s %s:\n%s\n", PROGNAME, title, msg);
4274 return;
4275 }
4276
4277 /* Remove pending popdown timeout, if any */
4278 if (infodiag_id >= 0) {
4279 cd_untimeout(infodiag_id);
4280 infodiag_id = -1;
4281 }
4282
4283 /* Set the dialog box title */
4284 xs = create_xmstring(title, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
4285 XtVaSetValues(widgets.dialog.info, XmNdialogTitle, xs, NULL);
4286 XmStringFree(xs);
4287
4288 /* Set the dialog box message */
4289 xs = create_xmstring(msg, NULL, XmSTRING_DEFAULT_CHARSET, TRUE);
4290 XtVaSetValues(widgets.dialog.info, XmNmessageString, xs, NULL);
4291 XmStringFree(xs);
4292
4293 if (!XtIsManaged(widgets.dialog.info)) {
4294 /* Set up dialog box position */
4295 cd_dialog_setpos(XtParent(widgets.dialog.info));
4296
4297 /* Pop up the info dialog */
4298 XtManageChild(widgets.dialog.info);
4299 }
4300 else {
4301 /* Raise the window to top of stack */
4302 XRaiseWindow(
4303 XtDisplay(widgets.dialog.info),
4304 XtWindow(XtParent(widgets.dialog.info))
4305 );
4306 }
4307 }
4308
4309
4310 /*
4311 * cd_info_popup_auto
4312 * Pop up the information message dialog box, which will auto-popdown
4313 * in 5 seconds.
4314 *
4315 * Args:
4316 * title - The title bar text string.
4317 * msg - The information message text string.
4318 *
4319 * Return:
4320 * Nothing.
4321 */
4322 void
cd_info_popup_auto(char * title,char * msg)4323 cd_info_popup_auto(char *title, char *msg)
4324 {
4325 cd_info_popup(title, msg);
4326
4327 infodiag_id = cd_timeout(
4328 5000, /* popup interval */
4329 cd_info_popdown,
4330 NULL
4331 );
4332 }
4333
4334
4335 /*
4336 * cd_info_popdown
4337 * Pop down the information message dialog box.
4338 *
4339 * Args:
4340 * p - Not used at this time.
4341 *
4342 * Return:
4343 * Nothing.
4344 */
4345 /*ARGSUSED*/
4346 void
cd_info_popdown(byte_t * p)4347 cd_info_popdown(byte_t *p)
4348 {
4349 if (XtIsManaged(widgets.dialog.info))
4350 XtUnmanageChild(widgets.dialog.info);
4351
4352 infodiag_id = -1;
4353 }
4354
4355
4356 /*
4357 * cd_info2_popup
4358 * Pop up the information message dialog box. This dialog box
4359 * differs from the cd_info_popup in that it contains an extra
4360 * single line text widget.
4361 *
4362 * Args:
4363 * title - The title bar text string.
4364 * msg - The information message text string.
4365 * textmsg - The information message string to put in the text widget.
4366 *
4367 * Return:
4368 * Nothing.
4369 */
4370 void
cd_info2_popup(char * title,char * msg,char * textmsg)4371 cd_info2_popup(char *title, char *msg, char *textmsg)
4372 {
4373 XmString xs;
4374
4375 if (!popup_ok) {
4376 (void) fprintf(errfp, "%s %s:\n%s\n%s\n",
4377 PROGNAME, title, msg, textmsg);
4378 return;
4379 }
4380
4381 /* Set the dialog box title */
4382 xs = create_xmstring(title, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
4383 XtVaSetValues(widgets.dialog.info2, XmNdialogTitle, xs, NULL);
4384 XmStringFree(xs);
4385
4386 /* Set the dialog box message */
4387 xs = create_xmstring(msg, NULL, XmSTRING_DEFAULT_CHARSET, TRUE);
4388 #if XmVersion > 1001
4389 XtVaSetValues(widgets.dialog.info2, XmNmessageString, xs, NULL);
4390 #else
4391 XtVaSetValues(widgets.dialog.info2, XmNselectionLabelString, xs, NULL);
4392 #endif
4393 XmStringFree(xs);
4394
4395 /* Set the text field message */
4396 set_text_string(widgets.dialog.info2_txt, textmsg, TRUE);
4397
4398 if (!XtIsManaged(widgets.dialog.info2)) {
4399 /* Set up dialog box position */
4400 cd_dialog_setpos(XtParent(widgets.dialog.info2));
4401
4402 /* Pop up the info2 dialog */
4403 XtManageChild(widgets.dialog.info2);
4404 }
4405 else {
4406 /* Raise the window to top of stack */
4407 XRaiseWindow(
4408 XtDisplay(widgets.dialog.info2),
4409 XtWindow(XtParent(widgets.dialog.info2))
4410 );
4411 }
4412 }
4413
4414
4415 /*
4416 * cd_info2_popdown
4417 * Pop down the information message dialog box.
4418 *
4419 * Args:
4420 * p - Not used at this time.
4421 *
4422 * Return:
4423 * Nothing.
4424 */
4425 /*ARGSUSED*/
4426 void
cd_info2_popdown(byte_t * p)4427 cd_info2_popdown(byte_t *p)
4428 {
4429 if (XtIsManaged(widgets.dialog.info2))
4430 XtUnmanageChild(widgets.dialog.info2);
4431
4432 pathexp_active = FALSE;
4433 }
4434
4435
4436 /*
4437 * cd_warning_popup
4438 * Pop up the warning message dialog box.
4439 *
4440 * Args:
4441 * title - The title bar text string.
4442 * msg - The warning message text string.
4443 *
4444 * Return:
4445 * Nothing.
4446 */
4447 void
cd_warning_popup(char * title,char * msg)4448 cd_warning_popup(char *title, char *msg)
4449 {
4450 XmString xs;
4451
4452 if (!popup_ok) {
4453 (void) fprintf(errfp, "%s %s:\n%s\n", PROGNAME, title, msg);
4454 return;
4455 }
4456
4457 /* Set the dialog box title */
4458 xs = create_xmstring(title, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
4459 XtVaSetValues(widgets.dialog.warning, XmNdialogTitle, xs, NULL);
4460 XmStringFree(xs);
4461
4462 /* Set the dialog box message */
4463 xs = create_xmstring(msg, NULL, XmSTRING_DEFAULT_CHARSET, TRUE);
4464 XtVaSetValues(widgets.dialog.warning, XmNmessageString, xs, NULL);
4465 XmStringFree(xs);
4466
4467 if (!XtIsManaged(widgets.dialog.warning)) {
4468 /* Set up dialog box position */
4469 cd_dialog_setpos(XtParent(widgets.dialog.warning));
4470
4471 /* Pop up the warning dialog */
4472 XtManageChild(widgets.dialog.warning);
4473 }
4474 else {
4475 /* Raise the window to top of stack */
4476 XRaiseWindow(
4477 XtDisplay(widgets.dialog.warning),
4478 XtWindow(XtParent(widgets.dialog.warning))
4479 );
4480 }
4481 }
4482
4483
4484 /*
4485 * cd_fatal_popup
4486 * Pop up the fatal error message dialog box.
4487 *
4488 * Args:
4489 * title - The title bar text string.
4490 * msg - The fatal error message text string.
4491 *
4492 * Return:
4493 * Nothing.
4494 */
4495 void
cd_fatal_popup(char * title,char * msg)4496 cd_fatal_popup(char *title, char *msg)
4497 {
4498 XmString xs;
4499
4500 if (!popup_ok) {
4501 (void) fprintf(errfp, "%s %s:\n%s\n", PROGNAME, title, msg);
4502 exit(1);
4503 }
4504
4505 /* Make sure that the cursor is normal */
4506 cd_busycurs(FALSE, CURS_ALL);
4507
4508 if (!XtIsManaged(widgets.dialog.fatal)) {
4509 /* Set the dialog box title */
4510 xs = create_xmstring(
4511 title, NULL, XmSTRING_DEFAULT_CHARSET, FALSE
4512 );
4513 XtVaSetValues(widgets.dialog.fatal, XmNdialogTitle, xs, NULL);
4514 XmStringFree(xs);
4515
4516 /* Set the dialog box message */
4517 xs = create_xmstring(
4518 msg, NULL, XmSTRING_DEFAULT_CHARSET, TRUE
4519 );
4520 XtVaSetValues(widgets.dialog.fatal, XmNmessageString, xs, NULL);
4521 XmStringFree(xs);
4522
4523 /* Set up dialog box position */
4524 cd_dialog_setpos(XtParent(widgets.dialog.fatal));
4525
4526 /* Pop up the error dialog */
4527 XtManageChild(widgets.dialog.fatal);
4528 }
4529 }
4530
4531
4532 /*
4533 * cd_confirm_popup
4534 * Pop up the user-confirmation message dialog box. Note that this
4535 * facility is not re-entrant, so that the 'yes' and 'no' pushbutton
4536 * callback funcs cannot themselves call cd_confirm_popup. Note that
4537 * this function does not return to the caller until a user response
4538 * is received.
4539 *
4540 * Args:
4541 * title - The title bar text string.
4542 * msg - The message text string.
4543 * f_yes - Pointer to the callback function if user selects OK
4544 * a_yes - Argument passed to f_yes
4545 * f_no - Pointer to the callback function if user selects Cancel
4546 * a_no - Argument passed to f_no
4547 *
4548 * Return:
4549 * FALSE - The user selected 'No' or simply closed the dialog
4550 * TRUE - The user selected 'Yes'
4551 */
4552 bool_t
cd_confirm_popup(char * title,char * msg,XtCallbackProc f_yes,XtPointer a_yes,XtCallbackProc f_no,XtPointer a_no)4553 cd_confirm_popup(
4554 char *title,
4555 char *msg,
4556 XtCallbackProc f_yes,
4557 XtPointer a_yes,
4558 XtCallbackProc f_no,
4559 XtPointer a_no
4560 )
4561 {
4562 Widget yes_btn,
4563 no_btn;
4564 XmString xs;
4565 cbinfo_t *yes_cbinfo,
4566 *no_cbinfo;
4567 static bool_t first = TRUE;
4568
4569 if (!popup_ok)
4570 /* Not allowed */
4571 return FALSE;
4572
4573 if (XtIsManaged(widgets.dialog.confirm)) {
4574 /* Already popped up, pop it down and clean up first */
4575 cd_confirm_popdown();
4576 }
4577
4578 /* Set the dialog box title */
4579 xs = create_xmstring(title, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
4580 XtVaSetValues(widgets.dialog.confirm, XmNdialogTitle, xs, NULL);
4581 XmStringFree(xs);
4582
4583 /* Set the dialog box message */
4584 xs = create_xmstring(msg, NULL, XmSTRING_DEFAULT_CHARSET, TRUE);
4585 XtVaSetValues(widgets.dialog.confirm, XmNmessageString, xs, NULL);
4586 XmStringFree(xs);
4587
4588 /* Add callbacks */
4589 yes_btn = XmMessageBoxGetChild(
4590 widgets.dialog.confirm,
4591 XmDIALOG_OK_BUTTON
4592 );
4593 no_btn = XmMessageBoxGetChild(
4594 widgets.dialog.confirm,
4595 XmDIALOG_CANCEL_BUTTON
4596 );
4597
4598 if (f_yes != NULL) {
4599 yes_cbinfo = &cbinfo0;
4600 yes_cbinfo->widget0 = yes_btn;
4601 yes_cbinfo->widget1 = no_btn;
4602 yes_cbinfo->widget2 = (Widget) NULL;
4603 yes_cbinfo->type = XmNactivateCallback;
4604 yes_cbinfo->func = f_yes;
4605 yes_cbinfo->data = a_yes;
4606
4607 register_activate_cb(yes_btn, f_yes, a_yes);
4608 register_activate_cb(yes_btn, cd_rmcallback, yes_cbinfo);
4609 register_activate_cb(no_btn, cd_rmcallback, yes_cbinfo);
4610 }
4611
4612 if (f_no != NULL) {
4613 no_cbinfo = &cbinfo1;
4614 no_cbinfo->widget0 = no_btn;
4615 no_cbinfo->widget1 = yes_btn;
4616 no_cbinfo->widget2 = XtParent(widgets.dialog.confirm);
4617 no_cbinfo->type = XmNactivateCallback;
4618 no_cbinfo->func = f_no;
4619 no_cbinfo->data = a_no;
4620
4621 register_activate_cb(no_btn, f_no, a_no);
4622 register_activate_cb(no_btn, cd_rmcallback, no_cbinfo);
4623 register_activate_cb(yes_btn, cd_rmcallback, no_cbinfo);
4624
4625 /* Install WM_DELETE_WINDOW handler */
4626 add_delw_callback(
4627 XtParent(widgets.dialog.confirm),
4628 f_no,
4629 a_no
4630 );
4631 }
4632
4633 if (first) {
4634 first = FALSE;
4635 register_activate_cb(yes_btn, cd_confirm_resp, (XtPointer) 1);
4636 register_activate_cb(no_btn, cd_confirm_resp, NULL);
4637
4638 /* Install WM_DELETE_WINDOW handler */
4639 add_delw_callback(
4640 XtParent(widgets.dialog.confirm),
4641 cd_confirm_resp,
4642 NULL
4643 );
4644 }
4645
4646 /* Set up dialog box position */
4647 cd_dialog_setpos(XtParent(widgets.dialog.confirm));
4648
4649 /* Pop up the confirm dialog */
4650 XtManageChild(widgets.dialog.confirm);
4651
4652 /* Handle events locally until the confirm window is dismissed.
4653 * This is to prevent from returning to the calling function
4654 * until a user response to this dialog is received.
4655 */
4656 confirm_pend = TRUE;
4657 do {
4658 util_delayms(10);
4659 event_loop(0);
4660 } while (confirm_pend);
4661
4662 return (confirm_resp);
4663 }
4664
4665
4666 /*
4667 * cd_confirm_popdown
4668 * Pop down the confirm dialog box.
4669 *
4670 * Args:
4671 * None.
4672 *
4673 * Return:
4674 * Nothing.
4675 */
4676 void
cd_confirm_popdown(void)4677 cd_confirm_popdown(void)
4678 {
4679 cbinfo_t *yes_cbinfo = &cbinfo0,
4680 *no_cbinfo = &cbinfo1;
4681
4682 if (!XtIsManaged(widgets.dialog.confirm))
4683 /* Not popped up */
4684 return;
4685
4686 XtUnmanageChild(widgets.dialog.confirm);
4687
4688 cd_rmcallback(yes_cbinfo->widget0,
4689 (XtPointer) yes_cbinfo, (XtPointer) NULL);
4690 cd_rmcallback(yes_cbinfo->widget0,
4691 (XtPointer) no_cbinfo, (XtPointer) NULL);
4692 cd_rmcallback(no_cbinfo->widget1,
4693 (XtPointer) yes_cbinfo, (XtPointer) NULL);
4694 cd_rmcallback(no_cbinfo->widget1,
4695 (XtPointer) no_cbinfo, (XtPointer) NULL);
4696 }
4697
4698
4699 /*
4700 * cd_working_popup
4701 * Pop up the work-in-progress message dialog box. Note that this
4702 * facility is not re-entrant, so that the 'stop' pushbutton
4703 * callback func cannot itself call cd_working_popup.
4704 *
4705 * Args:
4706 * title - The title bar text string.
4707 * msg - The message text string.
4708 * f_stop - Pointer to the callback function if user selects Stop
4709 * a_stop - Argument passed to f_stop
4710 *
4711 * Return:
4712 * Nothing.
4713 */
4714 void
cd_working_popup(char * title,char * msg,XtCallbackProc f_stop,XtPointer a_stop)4715 cd_working_popup(
4716 char *title,
4717 char *msg,
4718 XtCallbackProc f_stop,
4719 XtPointer a_stop
4720 )
4721 {
4722 Widget stop_btn;
4723 XmString xs;
4724 cbinfo_t *stop_cbinfo;
4725
4726 if (!popup_ok)
4727 /* Not allowed */
4728 return;
4729
4730 if (XtIsManaged(widgets.dialog.working)) {
4731 /* Already popped up: pop it down and clean up first */
4732 cd_working_popdown();
4733 }
4734
4735 /* Set the dialog box title */
4736 xs = create_xmstring(title, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
4737 XtVaSetValues(widgets.dialog.working, XmNdialogTitle, xs, NULL);
4738 XmStringFree(xs);
4739
4740 /* Set the dialog box message */
4741 xs = create_xmstring(msg, NULL, XmSTRING_DEFAULT_CHARSET, TRUE);
4742 XtVaSetValues(widgets.dialog.working, XmNmessageString, xs, NULL);
4743 XmStringFree(xs);
4744
4745 /* Add callbacks */
4746 stop_btn = XmMessageBoxGetChild(
4747 widgets.dialog.working,
4748 XmDIALOG_CANCEL_BUTTON
4749 );
4750
4751 if (f_stop != NULL) {
4752 stop_cbinfo = &cbinfo2;
4753 stop_cbinfo->widget0 = stop_btn;
4754 stop_cbinfo->widget1 = (Widget) NULL;
4755 stop_cbinfo->widget2 = XtParent(widgets.dialog.working);
4756 stop_cbinfo->type = XmNactivateCallback;
4757 stop_cbinfo->func = f_stop;
4758 stop_cbinfo->data = a_stop;
4759
4760 register_activate_cb(stop_btn, f_stop, a_stop);
4761 register_activate_cb(stop_btn, cd_rmcallback, stop_cbinfo);
4762
4763 /* Install WM_DELETE_WINDOW handler */
4764 add_delw_callback(
4765 XtParent(widgets.dialog.working),
4766 f_stop,
4767 a_stop
4768 );
4769 }
4770
4771 /* Set up dialog box position */
4772 cd_dialog_setpos(XtParent(widgets.dialog.working));
4773
4774 /* Pop up the working dialog */
4775 XtManageChild(widgets.dialog.working);
4776 }
4777
4778
4779 /*
4780 * cd_working_popdown
4781 * Pop down the working message dialog box.
4782 *
4783 * Args:
4784 * None.
4785 *
4786 * Return:
4787 * Nothing.
4788 */
4789 void
cd_working_popdown(void)4790 cd_working_popdown(void)
4791 {
4792 cbinfo_t *p = &cbinfo2;
4793
4794 if (!XtIsManaged(widgets.dialog.working))
4795 /* Not popped up */
4796 return;
4797
4798 XtUnmanageChild(widgets.dialog.working);
4799 cd_rmcallback(p->widget0, (XtPointer) p, (XtPointer) NULL);
4800 }
4801
4802
4803 /*
4804 * cd_init
4805 * Top level function that initializes all subsystems. Used on
4806 * program startup.
4807 *
4808 * Args:
4809 * s - Pointer to the curstat_t structure.
4810 *
4811 * Return:
4812 * Nothing.
4813 */
4814 void
cd_init(curstat_t * s)4815 cd_init(curstat_t *s)
4816 {
4817 int i,
4818 j;
4819 char *cp,
4820 *bdevname,
4821 *hd;
4822 char version[STR_BUF_SZ / 2],
4823 titlestr[STR_BUF_SZ * 2],
4824 path[FILE_PATH_SZ * 2];
4825 struct utsname *up;
4826
4827 (void) sprintf(version, "%s.%s", VERSION_MAJ, VERSION_MIN);
4828 DBGPRN(DBG_ALL)(errfp, "XMCD %s.%s DEBUG MODE\n",
4829 version, VERSION_TEENY);
4830
4831 /* app-defaults file check */
4832 if (app_data.version == NULL ||
4833 strncmp(version, app_data.version, strlen(version)) != 0) {
4834 CD_FATAL(app_data.str_appdef);
4835 return;
4836 }
4837
4838 if ((cp = (char *) getenv("XMCD_LIBDIR")) == NULL) {
4839 /* No library directory specified */
4840 if (di_isdemo()) {
4841 /* Demo mode: just fake it */
4842 app_data.libdir = CUR_DIR;
4843 }
4844 else {
4845 /* Real application: this is a fatal error */
4846 CD_FATAL(app_data.str_libdirerr);
4847 return;
4848 }
4849 }
4850 else if (!util_newstr(&app_data.libdir, cp)) {
4851 CD_FATAL(app_data.str_nomemory);
4852 return;
4853 }
4854
4855 /* Paranoia: avoid overflowing buffers */
4856 if ((int) strlen(app_data.libdir) >= FILE_PATH_SZ) {
4857 CD_FATAL(app_data.str_longpatherr);
4858 return;
4859 }
4860 hd = util_homedir(util_get_ouid());
4861 if ((int) strlen(hd) >= FILE_PATH_SZ) {
4862 CD_FATAL(app_data.str_longpatherr);
4863 return;
4864 }
4865
4866 /* Set some defaults */
4867 app_data.cdinfo_maxhist = 100;
4868 app_data.aux = (void *) s;
4869
4870 /* Get system common configuration parameters */
4871 (void) sprintf(path, SYS_CMCFG_PATH, app_data.libdir);
4872 di_common_parmload(path, TRUE, FALSE);
4873
4874 /* Get user common configuration parameters */
4875 (void) sprintf(path, USR_CMCFG_PATH, hd);
4876 di_common_parmload(path, FALSE, FALSE);
4877
4878 /* Paranoia: avoid overflowing buffers */
4879 if (app_data.device != NULL &&
4880 ((int) strlen(app_data.device) >= FILE_PATH_SZ)) {
4881 CD_FATAL(app_data.str_longpatherr);
4882 return;
4883 }
4884
4885 #ifdef __VMS
4886 bdevname = NULL;
4887 if (!util_newstr(&bdevname, "device.cfg")) {
4888 CD_FATAL(app_data.str_nomemory);
4889 return;
4890 }
4891 #else
4892 if ((bdevname = util_basename(app_data.device)) == NULL) {
4893 CD_FATAL("Device path basename error: Aborting.");
4894 return;
4895 }
4896 if ((int) strlen(bdevname) >= FILE_BASE_SZ) {
4897 MEM_FREE(bdevname);
4898 CD_FATAL(app_data.str_longpatherr);
4899 return;
4900 }
4901 #endif
4902
4903 /* Initialize CD Information services */
4904 (void) strcpy(cdinfo_cldata.prog, PROGNAME);
4905 (void) strcpy(cdinfo_cldata.user, util_loginname());
4906 cdinfo_cldata.isdemo = di_isdemo;
4907 cdinfo_cldata.curstat_addr = curstat_addr;
4908 cdinfo_cldata.fatal_msg = cd_fatal_popup;
4909 cdinfo_cldata.warning_msg = cd_warning_popup;
4910 cdinfo_cldata.info_msg = cd_info_popup;
4911 cdinfo_cldata.info2_msg = cd_info2_popup;
4912 cdinfo_cldata.workproc = event_loop;
4913 cdinfo_cldata.arg = 0;
4914 cdinfo_init(&cdinfo_cldata);
4915
4916
4917 /* Get system-wide device-specific configuration parameters */
4918 (void) sprintf(path, SYS_DSCFG_PATH, app_data.libdir, bdevname);
4919 di_devspec_parmload(path, TRUE, FALSE);
4920
4921 /* Get user device-specific configuration parameters */
4922 (void) sprintf(path, USR_DSCFG_PATH, hd, bdevname);
4923 di_devspec_parmload(path, FALSE, FALSE);
4924
4925 MEM_FREE(bdevname);
4926
4927 /* Make some program directories if needed */
4928 cd_mkdirs();
4929
4930 /* Initialize help system */
4931 help_init();
4932
4933 /* Initialize the CD Information/program subsystem */
4934 dbprog_init(s);
4935
4936 /* Initialize the wwwWarp subsystem */
4937 wwwwarp_init(s);
4938
4939 /* Initialize the CD interface subsystem */
4940 di_cldata.curstat_addr = curstat_addr;
4941 di_cldata.quit = cd_quit;
4942 di_cldata.timeout = cd_timeout;
4943 di_cldata.untimeout = cd_untimeout;
4944 di_cldata.dbclear = dbprog_dbclear;
4945 di_cldata.dbget = dbprog_dbget;
4946 di_cldata.progclear = dbprog_progclear;
4947 di_cldata.progget = dbprog_progget;
4948 di_cldata.chgr_scan_stop = dbprog_chgr_scan_stop;
4949 di_cldata.mkoutpath = cd_mkoutpath;
4950 di_cldata.ckpipeprog = cd_ckpipeprog;
4951 di_cldata.fatal_msg = cd_fatal_popup;
4952 di_cldata.warning_msg = cd_warning_popup;
4953 di_cldata.info_msg = cd_info_popup;
4954 di_cldata.info2_msg = cd_info2_popup;
4955 di_cldata.beep = cd_beep;
4956 di_cldata.set_lock_btn = set_lock_btn;
4957 di_cldata.set_shuffle_btn = set_shuffle_btn;
4958 di_cldata.set_vol_slider = set_vol_slider;
4959 di_cldata.set_bal_slider = set_bal_slider;
4960 di_cldata.dpy_all = dpy_all;
4961 di_cldata.dpy_disc = dpy_disc;
4962 di_cldata.dpy_track = dpy_track;
4963 di_cldata.dpy_index = dpy_index;
4964 di_cldata.dpy_time = dpy_time;
4965 di_cldata.dpy_progmode = dpy_progmode;
4966 di_cldata.dpy_playmode = dpy_playmode;
4967 di_cldata.dpy_rptcnt = dpy_rptcnt;
4968 di_init(&di_cldata);
4969
4970 /* Set default modes */
4971 di_repeat(s, app_data.repeat_mode);
4972 set_repeat_btn(s->repeat);
4973 di_shuffle(s, app_data.shuffle_mode);
4974 set_shuffle_btn(s->shuffle);
4975 keypad_mode = KPMODE_TRACK;
4976 cdda_fader_mode = CDDA_FADER_NONE;
4977
4978 /* Set the sensitivity of play mode buttons according to
4979 * configuration capabilities
4980 */
4981 if ((di_cldata.capab & CAPAB_RDCDDA) == 0) {
4982 XtSetSensitive(widgets.options.mode_cdda_btn, False);
4983 XtSetSensitive(widgets.options.mode_file_btn, False);
4984 XtSetSensitive(widgets.options.mode_pipe_btn, False);
4985 }
4986 else {
4987 if ((di_cldata.capab & CAPAB_WRDEV) == 0)
4988 XtSetSensitive(widgets.options.mode_cdda_btn, False);
4989 if ((di_cldata.capab & CAPAB_WRFILE) == 0)
4990 XtSetSensitive(widgets.options.mode_file_btn, False);
4991 if ((di_cldata.capab & CAPAB_WRPIPE) == 0)
4992 XtSetSensitive(widgets.options.mode_pipe_btn, False);
4993 }
4994
4995 /* Play mode and capabilities */
4996 if ((di_cldata.capab & CAPAB_PLAYAUDIO) == 0) {
4997 (void) fprintf(errfp,
4998 "No CD playback capability. Aborting.\n");
4999 cd_quit(s);
5000 return;
5001 }
5002
5003 /* Set optional file formats menu entries sensitivity */
5004 if (!cdda_filefmt_supp(FILEFMT_MP3))
5005 XtSetSensitive(widgets.options.mode_fmt_mp3_btn, False);
5006 if (!cdda_filefmt_supp(FILEFMT_OGG))
5007 XtSetSensitive(widgets.options.mode_fmt_ogg_btn, False);
5008 if (!cdda_filefmt_supp(FILEFMT_FLAC))
5009 XtSetSensitive(widgets.options.mode_fmt_flac_btn, False);
5010 if (!cdda_filefmt_supp(FILEFMT_AAC))
5011 XtSetSensitive(widgets.options.mode_fmt_aac_btn, False);
5012 if (!cdda_filefmt_supp(FILEFMT_MP4))
5013 XtSetSensitive(widgets.options.mode_fmt_mp4_btn, False);
5014
5015 /* Get label strings */
5016 XtVaGetValues(widgets.options.qualval_lbl,
5017 XmNlabelString, &xs_qual,
5018 NULL
5019 );
5020 XtVaGetValues(widgets.options.compalgo_lbl,
5021 XmNlabelString, &xs_algo,
5022 NULL
5023 );
5024
5025 /* Create pushbutton widgets in bitrate menu */
5026 for (j = (cdda_bitrates() - 1); j >= 0; j--) {
5027 int k;
5028 wlist_t *l,
5029 **head;
5030 Widget w[3],
5031 pw;
5032 Arg arg[5];
5033 XtCallbackProc cbfunc;
5034
5035 if (cdda_bitrate_val(j) < 32)
5036 continue;
5037
5038 for (k = 0; k < 3; k++) {
5039 switch (k) {
5040 case 1:
5041 pw = widgets.options.minbrate_menu;
5042 head = &cd_minbrhead;
5043 cbfunc = cd_min_bitrate;
5044 break;
5045 case 2:
5046 pw = widgets.options.maxbrate_menu;
5047 head = &cd_maxbrhead;
5048 cbfunc = cd_max_bitrate;
5049 break;
5050 case 0:
5051 default:
5052 pw = widgets.options.bitrate_menu;
5053 head = &cd_brhead;
5054 cbfunc = cd_bitrate;
5055 break;
5056 }
5057
5058 i = 0;
5059 XtSetArg(arg[i], XmNshadowThickness, 2); i++;
5060 w[k] = XmCreatePushButton(
5061 pw, cdda_bitrate_name(j), arg, i
5062 );
5063
5064 XtManageChild(w[k]);
5065
5066 /* Register callback */
5067 register_activate_cb(w[k], cbfunc, s);
5068
5069 /* Add to list */
5070 l = (wlist_t *)(void *) MEM_ALLOC(
5071 "wlist_t", sizeof(wlist_t)
5072 );
5073 if (l == NULL) {
5074 CD_FATAL(app_data.str_nomemory);
5075 return;
5076 }
5077
5078 l->w = w[k];
5079 l->next = *head;
5080 *head = l;
5081 }
5082 }
5083
5084 s->time_dpy = (byte_t) app_data.timedpy_mode;
5085
5086 /* Force CD-TEXT config to False if it doesn't appear in the
5087 * CD info path list.
5088 */
5089 if (!cdinfo_cdtext_iscfg())
5090 app_data.cdtext_dsbl = TRUE;
5091
5092 /* Set default options */
5093 cd_options_reset(widgets.options.reset_btn, (XtPointer) s, NULL);
5094
5095 if (!app_data.mselvol_supp) {
5096 XtSetSensitive(widgets.options.vol_linear_btn, False);
5097 XtSetSensitive(widgets.options.vol_square_btn, False);
5098 XtSetSensitive(widgets.options.vol_invsqr_btn, False);
5099 }
5100
5101 if (!app_data.balance_supp)
5102 XtSetSensitive(widgets.options.bal_scale, False);
5103
5104 if (!app_data.chroute_supp) {
5105 XtSetSensitive(widgets.options.chroute_rev_btn, False);
5106 XtSetSensitive(widgets.options.chroute_left_btn, False);
5107 XtSetSensitive(widgets.options.chroute_right_btn, False);
5108 XtSetSensitive(widgets.options.chroute_mono_btn, False);
5109 }
5110
5111 /* Add options window category list entries */
5112 for (i = 0; i < OPT_CATEGS; i++) {
5113 XmString xs;
5114 wlist_t *wl;
5115 Widget *warray;
5116 int j;
5117
5118 switch (i) {
5119 case 0:
5120 /* Playback mode */
5121 opt_categ[i].name = app_data.str_playbackmode;
5122 warray = &widgets.options.mode_lbl;
5123 break;
5124 case 1:
5125 /* Encoding parameters */
5126 opt_categ[i].name = app_data.str_encodeparms;
5127 warray = &widgets.options.method_opt;
5128 break;
5129 case 2:
5130 /* LAME MP3 options */
5131 opt_categ[i].name = app_data.str_lameopts;
5132 warray = &widgets.options.lame_lbl;
5133 break;
5134 case 3:
5135 /* CDDA thread schedling parameters */
5136 opt_categ[i].name = app_data.str_schedparms;
5137 warray = &widgets.options.sched_lbl;
5138 break;
5139 case 4:
5140 /* Automation */
5141 opt_categ[i].name = app_data.str_autofuncs;
5142 warray = &widgets.options.load_lbl;
5143 break;
5144 case 5:
5145 /* CD Changer */
5146 opt_categ[i].name = app_data.str_cdchanger;
5147 warray = &widgets.options.chg_lbl;
5148 break;
5149 case 6:
5150 /* Channel routing */
5151 opt_categ[i].name = app_data.str_chroute;
5152 warray = &widgets.options.chroute_lbl;
5153 break;
5154 case 7:
5155 /* Volume / Balance */
5156 opt_categ[i].name = app_data.str_volbal;
5157 warray = &widgets.options.vol_lbl;
5158 break;
5159 case 8:
5160 /* CDDB / CD-TEXT */
5161 opt_categ[i].name = app_data.str_cddb_cdtext;
5162 warray = &widgets.options.cddb_lbl;
5163 break;
5164 default:
5165 opt_categ[i].name = NULL;
5166 warray = NULL;
5167 break;
5168 }
5169
5170 xs = create_xmstring(opt_categ[i].name, NULL, CHSET1, FALSE);
5171 XmListAddItemUnselected(widgets.options.categ_list, xs, i+1);
5172 XmStringFree(xs);
5173
5174 for (j = 0; warray[j] != (Widget) NULL; j++) {
5175 wl = (wlist_t *)(void *) MEM_ALLOC(
5176 "wlist_t", sizeof(wlist_t)
5177 );
5178 if (wl == NULL) {
5179 CD_FATAL(app_data.str_nomemory);
5180 return;
5181 }
5182 wl->next = NULL;
5183 wl->w = warray[j];
5184
5185 if (opt_categ[i].widgets == NULL)
5186 opt_categ[i].widgets = wl;
5187 else {
5188 wl->next = opt_categ[i].widgets;
5189 opt_categ[i].widgets = wl;
5190 }
5191 }
5192 }
5193 /* Pre-select the first category */
5194 XmListSelectPos(widgets.options.categ_list, 1, True);
5195
5196 /* Set the main window and icon titles */
5197 up = util_get_uname();
5198 (void) sprintf(titlestr, "%.32s: %.80s/%d",
5199 app_data.main_title,
5200 up->nodename,
5201 app_data.devnum);
5202 XtVaSetValues(widgets.toplevel,
5203 XmNtitle, titlestr,
5204 XmNiconName, PROGNAME,
5205 NULL
5206 );
5207 }
5208
5209
5210 /*
5211 * cd_start
5212 * Secondary startup functions
5213 *
5214 * Args:
5215 * s - Pointer to the curstat_t structure.
5216 *
5217 * Return:
5218 * Nothing.
5219 */
5220 void
cd_start(curstat_t * s)5221 cd_start(curstat_t *s)
5222 {
5223 /* Allow popup dialogs from here on */
5224 popup_ok = TRUE;
5225
5226 /* Start up libutil */
5227 util_start();
5228
5229 /* Start up I/O interface */
5230 di_start(s);
5231
5232 /* Start up help */
5233 help_start();
5234 }
5235
5236
5237 /*
5238 * cd_icon
5239 * Main window iconification/deiconification handler.
5240 *
5241 * Args:
5242 * s - Pointer to the curstat_t structure.
5243 * iconified - Whether the main window is iconified.
5244 *
5245 * Return:
5246 * Nothing.
5247 */
5248 void
cd_icon(curstat_t * s,bool_t iconified)5249 cd_icon(curstat_t *s, bool_t iconified)
5250 {
5251 di_icon(s, iconified);
5252 }
5253
5254
5255 /*
5256 * cd_halt
5257 * Top level function to shut down all subsystems. Used when
5258 * closing the application.
5259 *
5260 * Args:
5261 * s - Pointer to the curstat_t structure.
5262 *
5263 * Return:
5264 * Nothing.
5265 */
5266 void
cd_halt(curstat_t * s)5267 cd_halt(curstat_t *s)
5268 {
5269 di_halt(s);
5270 cdinfo_halt(s);
5271 }
5272
5273
5274 /*
5275 * cd_quit
5276 * Close the application.
5277 *
5278 * Args:
5279 * s - Pointer to the curstat_t structure.
5280 *
5281 * Return:
5282 * Nothing.
5283 */
5284 void
cd_quit(curstat_t * s)5285 cd_quit(curstat_t *s)
5286 {
5287 XmAnyCallbackStruct p;
5288
5289 if (XtIsRealized(widgets.toplevel))
5290 XtUnmapWidget(widgets.toplevel);
5291
5292 /* Cancel asynchronous CDDB load operation, if active */
5293 cdinfo_load_cancel();
5294
5295 /* Shut down all xmcd subsystems */
5296 cd_halt(s);
5297
5298 /* Uninstall current keyboard grabs */
5299 p.reason = XmCR_FOCUS;
5300 cd_shell_focus_chg(
5301 widgets.toplevel,
5302 (XtPointer) widgets.toplevel,
5303 (XtPointer) &p
5304 );
5305
5306 /* Let X events drain */
5307 event_loop(0);
5308
5309 /* Shut down GUI */
5310 shutdown_gui();
5311
5312 exit(0);
5313 }
5314
5315
5316 /*
5317 * cd_busycurs
5318 * Enable/disable the watch cursor.
5319 *
5320 * Args:
5321 * busy - Boolean value indicating whether to enable or disable the
5322 * watch cursor.
5323 * winmap - Bitmap of form widgets in which the cursor should be
5324 * affected
5325 *
5326 * Return:
5327 * Nothing.
5328 */
5329 void
cd_busycurs(bool_t busy,int winmap)5330 cd_busycurs(bool_t busy, int winmap)
5331 {
5332 Display *dpy = XtDisplay(widgets.toplevel);
5333 Window win;
5334 static Cursor wcur = (Cursor) 0;
5335
5336 if (wcur == (Cursor) 0)
5337 wcur = XCreateFontCursor(dpy, XC_watch);
5338
5339 if (winmap == 0)
5340 return;
5341
5342 if (busy) {
5343 if ((winmap & CURS_MAIN) &&
5344 (win = XtWindow(widgets.main.form)) != (Window) 0)
5345 XDefineCursor(dpy, win, wcur);
5346 if ((winmap & CURS_KEYPAD) &&
5347 (win = XtWindow(widgets.keypad.form)) != (Window) 0)
5348 XDefineCursor(dpy, win, wcur);
5349 if ((winmap & CURS_OPTIONS) &&
5350 (win = XtWindow(widgets.options.form)) != (Window) 0)
5351 XDefineCursor(dpy, win, wcur);
5352 if ((winmap & CURS_DBPROG) &&
5353 (win = XtWindow(widgets.dbprog.form)) != (Window) 0)
5354 XDefineCursor(dpy, win, wcur);
5355 if ((winmap & CURS_DLIST) &&
5356 (win = XtWindow(widgets.dlist.form)) != (Window) 0)
5357 XDefineCursor(dpy, win, wcur);
5358 if ((winmap & CURS_DBEXTD) &&
5359 (win = XtWindow(widgets.dbextd.form)) != (Window) 0)
5360 XDefineCursor(dpy, win, wcur);
5361 if ((winmap & CURS_DBEXTT) &&
5362 (win = XtWindow(widgets.dbextt.form)) != (Window) 0)
5363 XDefineCursor(dpy, win, wcur);
5364 if ((winmap & CURS_FULLNAME) &&
5365 (win = XtWindow(widgets.fullname.form)) != (Window) 0)
5366 XDefineCursor(dpy, win, wcur);
5367 if ((winmap & CURS_CREDITS) &&
5368 (win = XtWindow(widgets.credits.form)) != (Window) 0)
5369 XDefineCursor(dpy, win, wcur);
5370 if ((winmap & CURS_SEGMENTS) &&
5371 (win = XtWindow(widgets.segments.form)) != (Window) 0)
5372 XDefineCursor(dpy, win, wcur);
5373 if ((winmap & CURS_SUBMITURL) &&
5374 (win = XtWindow(widgets.submiturl.form)) != (Window) 0)
5375 XDefineCursor(dpy, win, wcur);
5376 if ((winmap & CURS_USERREG) &&
5377 (win = XtWindow(widgets.userreg.form)) != (Window) 0)
5378 XDefineCursor(dpy, win, wcur);
5379 if ((winmap & CURS_HELP) &&
5380 (win = XtWindow(widgets.help.form)) != (Window) 0)
5381 XDefineCursor(dpy, win, wcur);
5382 }
5383 else {
5384 if ((winmap & CURS_MAIN) &&
5385 (win = XtWindow(widgets.main.form)) != (Window) 0)
5386 XUndefineCursor(dpy, win);
5387 if ((winmap & CURS_KEYPAD) &&
5388 (win = XtWindow(widgets.keypad.form)) != (Window) 0)
5389 XUndefineCursor(dpy, win);
5390 if ((winmap & CURS_OPTIONS) &&
5391 (win = XtWindow(widgets.options.form)) != (Window) 0)
5392 XUndefineCursor(dpy, win);
5393 if ((winmap & CURS_DBPROG) &&
5394 (win = XtWindow(widgets.dbprog.form)) != (Window) 0)
5395 XUndefineCursor(dpy, win);
5396 if ((winmap & CURS_DLIST) &&
5397 (win = XtWindow(widgets.dlist.form)) != (Window) 0)
5398 XUndefineCursor(dpy, win);
5399 if ((winmap & CURS_DBEXTD) &&
5400 (win = XtWindow(widgets.dbextd.form)) != (Window) 0)
5401 XUndefineCursor(dpy, win);
5402 if ((winmap & CURS_DBEXTT) &&
5403 (win = XtWindow(widgets.dbextt.form)) != (Window) 0)
5404 XUndefineCursor(dpy, win);
5405 if ((winmap & CURS_FULLNAME) &&
5406 (win = XtWindow(widgets.fullname.form)) != (Window) 0)
5407 XUndefineCursor(dpy, win);
5408 if ((winmap & CURS_CREDITS) &&
5409 (win = XtWindow(widgets.credits.form)) != (Window) 0)
5410 XUndefineCursor(dpy, win);
5411 if ((winmap & CURS_SEGMENTS) &&
5412 (win = XtWindow(widgets.segments.form)) != (Window) 0)
5413 XUndefineCursor(dpy, win);
5414 if ((winmap & CURS_SUBMITURL) &&
5415 (win = XtWindow(widgets.submiturl.form)) != (Window) 0)
5416 XUndefineCursor(dpy, win);
5417 if ((winmap & CURS_USERREG) &&
5418 (win = XtWindow(widgets.userreg.form)) != (Window) 0)
5419 XUndefineCursor(dpy, win);
5420 if ((winmap & CURS_HELP) &&
5421 (win = XtWindow(widgets.help.form)) != (Window) 0)
5422 XUndefineCursor(dpy, win);
5423 }
5424 XFlush(dpy);
5425 }
5426
5427
5428 /*
5429 * cd_hostname
5430 * Return the system's host name (with fully qualified domain name
5431 * if possible). This function can only be used after the call
5432 * to cdinfo_init() is completed.
5433 *
5434 * Args:
5435 * None.
5436 *
5437 * Return:
5438 * The host name string.
5439 */
5440 char *
cd_hostname(void)5441 cd_hostname(void)
5442 {
5443 return (cdinfo_cldata.host);
5444 }
5445
5446
5447 /*
5448 * onsig
5449 * Signal handler. Causes the application to shut down gracefully.
5450 *
5451 * Args:
5452 * sig - The signal number received.
5453 *
5454 * Return:
5455 * Nothing.
5456 */
5457 void
onsig(int sig)5458 onsig(int sig)
5459 {
5460 (void) util_signal(sig, SIG_IGN);
5461 cd_quit(curstat_addr());
5462 }
5463
5464
5465 /**************** vv Callback routines vv ****************/
5466
5467 /*
5468 * cd_checkbox
5469 * Main window checkbox callback function
5470 */
5471 /*ARGSUSED*/
5472 void
cd_checkbox(Widget w,XtPointer client_data,XtPointer call_data)5473 cd_checkbox(Widget w, XtPointer client_data, XtPointer call_data)
5474 {
5475 XmRowColumnCallbackStruct *p =
5476 (XmRowColumnCallbackStruct *)(void *) call_data;
5477 XmToggleButtonCallbackStruct *q;
5478 curstat_t *s =
5479 (curstat_t *)(void *) client_data;
5480 char *str;
5481 bool_t paused = FALSE,
5482 need_restart = FALSE;
5483 size_t len = 0;
5484
5485 if (p->reason != XmCR_ACTIVATE)
5486 return;
5487
5488 q = (XmToggleButtonCallbackStruct *)(void *) p->callbackstruct;
5489 if (q == NULL)
5490 return;
5491
5492 str = q->set ? "Enable" : "Disable";
5493
5494 DBGPRN(DBG_UI)(errfp, "\n* CHKBOX: ");
5495
5496 if (p->widget == widgets.main.lock_btn) {
5497 DBGPRN(DBG_UI)(errfp, "lock: %s\n", str);
5498
5499 di_lock(s, (bool_t) q->set);
5500 }
5501 else if (p->widget == widgets.main.repeat_btn) {
5502 DBGPRN(DBG_UI)(errfp, "repeat: %s\n", str);
5503
5504 di_repeat(s, (bool_t) q->set);
5505 }
5506 else if (p->widget == widgets.main.shuffle_btn) {
5507 DBGPRN(DBG_UI)(errfp, "shuffle: %s\n", str);
5508
5509 if (!q->set) {
5510 /* Clear shuffle mode */
5511 di_shuffle(s, FALSE);
5512 return;
5513 }
5514
5515 switch (s->mode) {
5516 case MOD_PAUSE:
5517 paused = TRUE;
5518 /*FALLTHROUGH*/
5519 case MOD_PLAY:
5520 case MOD_SAMPLE:
5521 need_restart = TRUE;
5522
5523 if (s->program && !s->onetrk_prog) {
5524 len = strlen(app_data.str_clrprog) +
5525 strlen(app_data.str_restartplay) +
5526 strlen(app_data.str_askproceed) + 3;
5527 str = (char *) MEM_ALLOC("msg", len);
5528 if (str == NULL) {
5529 CD_FATAL(app_data.str_nomemory);
5530 return;
5531 }
5532 (void) sprintf(str, "%s\n%s\n%s",
5533 app_data.str_clrprog,
5534 app_data.str_restartplay,
5535 app_data.str_askproceed
5536 );
5537 }
5538 else if (s->segplay == SEGP_AB) {
5539 len = strlen(app_data.str_cancelseg) +
5540 strlen(app_data.str_restartplay) +
5541 strlen(app_data.str_askproceed) + 3;
5542 str = (char *) MEM_ALLOC("msg", len);
5543 if (str == NULL) {
5544 CD_FATAL(app_data.str_nomemory);
5545 return;
5546 }
5547 (void) sprintf(str, "%s\n%s\n%s",
5548 app_data.str_cancelseg,
5549 app_data.str_restartplay,
5550 app_data.str_askproceed
5551 );
5552 }
5553 else {
5554 len = strlen(app_data.str_restartplay) +
5555 strlen(app_data.str_askproceed) + 2;
5556 str = (char *) MEM_ALLOC("msg", len);
5557 if (str == NULL) {
5558 CD_FATAL(app_data.str_nomemory);
5559 return;
5560 }
5561 (void) sprintf(str, "%s\n%s",
5562 app_data.str_restartplay,
5563 app_data.str_askproceed
5564 );
5565 }
5566 break;
5567
5568 default:
5569 if (s->program && !s->onetrk_prog) {
5570 len = strlen(app_data.str_clrprog) +
5571 strlen(app_data.str_askproceed) + 2;
5572 str = (char *) MEM_ALLOC("msg", len);
5573 if (str == NULL) {
5574 CD_FATAL(app_data.str_nomemory);
5575 return;
5576 }
5577 (void) sprintf(str, "%s\n%s",
5578 app_data.str_clrprog,
5579 app_data.str_askproceed
5580 );
5581 }
5582 else if (s->segplay == SEGP_AB) {
5583 len = strlen(app_data.str_cancelseg) +
5584 strlen(app_data.str_askproceed) + 2;
5585 str = (char *) MEM_ALLOC("msg", len);
5586 if (str == NULL) {
5587 CD_FATAL(app_data.str_nomemory);
5588 return;
5589 }
5590 (void) sprintf(str, "%s\n%s",
5591 app_data.str_cancelseg,
5592 app_data.str_askproceed
5593 );
5594 }
5595 else
5596 str = NULL;
5597 break;
5598 }
5599
5600 if (str == NULL ||
5601 cd_confirm_popup(app_data.str_confirm,
5602 str,
5603 (XtCallbackProc) NULL, NULL,
5604 (XtCallbackProc) NULL, NULL)) {
5605
5606 if (need_restart) {
5607 /* Stop playback first */
5608 di_stop(s, FALSE);
5609 }
5610
5611 if (s->program) {
5612 /* Cancel program mode */
5613 dbprog_clrpgm(w, client_data, NULL);
5614 }
5615
5616 if (s->segplay != SEGP_NONE) {
5617 /* Cancel a->b mode */
5618 s->segplay = SEGP_NONE;
5619 dpy_progmode(s, FALSE);
5620 }
5621
5622 /* Set shuffle mode */
5623 di_shuffle(s, TRUE);
5624
5625 if (need_restart) {
5626 if (paused)
5627 /* Mute sound */
5628 di_mute_on(s);
5629
5630 /* Restart playback */
5631 s->cur_trk = -1;
5632 cd_play_pause(w, client_data, NULL);
5633
5634 if (paused) {
5635 /* This will cause the playback
5636 * to pause.
5637 */
5638 cd_play_pause(w, client_data, NULL);
5639
5640 /* Restore sound */
5641 di_mute_off(s);
5642 }
5643 }
5644 }
5645 else {
5646 DBGPRN(DBG_UI)(errfp,
5647 "\n* SHUFFLE: user aborted mode change\n");
5648
5649 /* Restore button state */
5650 set_shuffle_btn(FALSE);
5651 }
5652
5653 if (str != NULL)
5654 MEM_FREE(str);
5655 }
5656 }
5657
5658
5659 /*
5660 * cd_mode
5661 * Main window mode button callback function
5662 */
5663 /*ARGSUSED*/
5664 void
cd_mode(Widget w,XtPointer client_data,XtPointer call_data)5665 cd_mode(Widget w, XtPointer client_data, XtPointer call_data)
5666 {
5667 curstat_t *s = (curstat_t *)(void *) client_data;
5668
5669 DBGPRN(DBG_UI)(errfp, "\n* WINDOW MODE\n");
5670
5671 /* Change the main window arrangement. Unmap the form while
5672 * the change takes place so that the user does not see a
5673 * bunch of jumbled widgets while they adjust to the new size.
5674 */
5675 XtUnmapWidget(widgets.main.form);
5676 geom_main_chgmode(&widgets);
5677 XtMapWidget(widgets.main.form);
5678
5679 /* Force indicator font change */
5680 mode_chg = TRUE;
5681 dpy_track(s);
5682 dpy_time(s, FALSE);
5683 mode_chg = FALSE;
5684
5685 /* This is a hack to prevent the tooltip from popping up due to
5686 * the main window reconfiguration.
5687 */
5688 skip_next_tooltip = TRUE;
5689
5690 /* Overload the function of this button to also
5691 * dump the contents of the curstat_t structure
5692 * in debug mode
5693 */
5694 if (app_data.debug & DBG_ALL)
5695 di_dump_curstat(s);
5696 }
5697
5698
5699 /*
5700 * cd_load_eject
5701 * Main window load/eject button callback function
5702 */
5703 /*ARGSUSED*/
5704 void
cd_load_eject(Widget w,XtPointer client_data,XtPointer call_data)5705 cd_load_eject(Widget w, XtPointer client_data, XtPointer call_data)
5706 {
5707 curstat_t *s = (curstat_t *)(void *) client_data;
5708
5709 DBGPRN(DBG_UI)(errfp, "\n* LOAD_EJECT\n");
5710
5711 if (searching) {
5712 cd_beep();
5713 return;
5714 }
5715 if (s->mode == MOD_PAUSE)
5716 dpy_time(s, FALSE);
5717
5718 if (s->mode != MOD_BUSY && s->mode != MOD_NODISC) {
5719 s->flags |= STAT_EJECT;
5720
5721 /* Ask the user if the changed CD information should be
5722 * submitted to CDDB
5723 */
5724 if (!dbprog_chgsubmit(s))
5725 return;
5726 }
5727
5728 /* Change to watch cursor */
5729 cd_busycurs(TRUE, CURS_ALL);
5730
5731 /* Cancel asynchronous CDDB load operation, if active */
5732 cdinfo_load_cancel();
5733
5734 /* Load/Eject the CD */
5735 di_load_eject(s);
5736
5737 /* Change to normal cursor */
5738 cd_busycurs(FALSE, CURS_ALL);
5739
5740 s->flags &= ~STAT_EJECT;
5741 }
5742
5743
5744 /*
5745 * cd_quit_btn
5746 * Main window quit button callback function
5747 */
5748 /*ARGSUSED*/
5749 void
cd_quit_btn(Widget w,XtPointer client_data,XtPointer call_data)5750 cd_quit_btn(Widget w, XtPointer client_data, XtPointer call_data)
5751 {
5752 DBGPRN(DBG_UI)(errfp, "\n* QUIT\n");
5753
5754 (void) cd_confirm_popup(
5755 app_data.str_confirm,
5756 app_data.str_quit,
5757 (XtCallbackProc) cd_exit,
5758 client_data,
5759 (XtCallbackProc) NULL,
5760 NULL
5761 );
5762 }
5763
5764
5765 /*
5766 * cd_time
5767 * Main window time mode button callback function
5768 */
5769 /*ARGSUSED*/
5770 void
cd_time(Widget w,XtPointer client_data,XtPointer call_data)5771 cd_time(Widget w, XtPointer client_data, XtPointer call_data)
5772 {
5773 curstat_t *s = (curstat_t *)(void *) client_data;
5774
5775 switch (s->time_dpy) {
5776 case T_ELAPSED_TRACK:
5777 s->time_dpy = T_ELAPSED_SEG;
5778 break;
5779
5780 case T_ELAPSED_SEG:
5781 s->time_dpy = T_ELAPSED_DISC;
5782 break;
5783
5784 case T_ELAPSED_DISC:
5785 s->time_dpy = T_REMAIN_TRACK;
5786 break;
5787
5788 case T_REMAIN_TRACK:
5789 s->time_dpy = T_REMAIN_SEG;
5790 break;
5791
5792 case T_REMAIN_SEG:
5793 s->time_dpy = T_REMAIN_DISC;
5794 break;
5795
5796 case T_REMAIN_DISC:
5797 s->time_dpy = T_ELAPSED_TRACK;
5798 break;
5799 }
5800
5801 dpy_timemode(s);
5802 dpy_track(s);
5803 dpy_time(s, FALSE);
5804 }
5805
5806
5807 /*
5808 * cd_ab
5809 * Main window a->b mode button callback function
5810 */
5811 /*ARGSUSED*/
5812 void
cd_ab(Widget w,XtPointer client_data,XtPointer call_data)5813 cd_ab(Widget w, XtPointer client_data, XtPointer call_data)
5814 {
5815 curstat_t *s = (curstat_t *)(void *) client_data;
5816 char buf[16];
5817
5818 DBGPRN(DBG_UI)(errfp, "\n* A->B\n");
5819
5820 if (searching) {
5821 cd_beep();
5822 return;
5823 }
5824 switch (s->segplay) {
5825 case SEGP_NONE:
5826 if (s->mode != MOD_PLAY || s->program || s->shuffle) {
5827 /* Must be in normal playing mode */
5828 cd_beep();
5829 return;
5830 }
5831
5832 /* First click - set start position */
5833
5834 s->bp_startpos_tot = s->curpos_tot; /* Structure copy */
5835 s->bp_startpos_trk = s->curpos_trk; /* Structure copy */
5836 s->segplay = SEGP_A;
5837
5838 /* Set the segment start position if the segments window
5839 * is popped up
5840 */
5841 if (XtIsManaged(widgets.segments.form)) {
5842 (void) sprintf(buf, "%u", s->cur_trk);
5843 set_text_string(
5844 widgets.segments.starttrk_txt, buf, FALSE
5845 );
5846
5847 (void) sprintf(buf, "%u", s->curpos_trk.addr);
5848 set_text_string(
5849 widgets.segments.startfrm_txt, buf, FALSE
5850 );
5851 }
5852 break;
5853
5854 case SEGP_A:
5855 if (s->mode != MOD_PLAY || s->program || s->shuffle) {
5856 /* Must be in normal playing mode */
5857 cd_beep();
5858 return;
5859 }
5860
5861 /* Second click - set end position and stop playback. */
5862
5863 /* Get current position */
5864 di_status_upd(s);
5865
5866 s->bp_endpos_tot = s->curpos_tot; /* Structure copy */
5867 s->bp_endpos_trk = s->curpos_trk; /* Structure copy */
5868
5869 /* Set the segment end position if the segments window
5870 * is popped up
5871 */
5872 if (XtIsManaged(widgets.segments.form)) {
5873 (void) sprintf(buf, "%u", s->cur_trk);
5874 set_text_string(
5875 widgets.segments.endtrk_txt, buf, FALSE
5876 );
5877
5878 (void) sprintf(buf, "%u", s->curpos_trk.addr);
5879 set_text_string(
5880 widgets.segments.endfrm_txt, buf, FALSE
5881 );
5882 }
5883
5884 /* Make sure that the start + min_playblks < end */
5885 if ((s->bp_startpos_tot.addr + app_data.min_playblks) >=
5886 s->bp_endpos_tot.addr) {
5887 s->segplay = SEGP_NONE;
5888 CD_INFO(app_data.str_segposerr);
5889 }
5890
5891 if (w == widgets.main.ab_btn) {
5892 s->segplay = SEGP_AB;
5893 di_stop(s, TRUE);
5894 }
5895 else
5896 s->segplay = SEGP_NONE;
5897
5898 break;
5899
5900 case SEGP_AB:
5901 /* Third click - Disable a->b mode */
5902
5903 /* Clear the segment start/end positions if the segments
5904 * window is popped up
5905 */
5906 if (XtIsManaged(widgets.segments.form)) {
5907 set_text_string(
5908 widgets.segments.starttrk_txt, "", FALSE
5909 );
5910 set_text_string(
5911 widgets.segments.startfrm_txt, "", FALSE
5912 );
5913 set_text_string(
5914 widgets.segments.endtrk_txt, "", FALSE
5915 );
5916 set_text_string(
5917 widgets.segments.endfrm_txt, "", FALSE
5918 );
5919 }
5920
5921 s->segplay = SEGP_NONE;
5922 di_stop(s, TRUE);
5923 break;
5924 }
5925
5926 /* Set segments window set/clear button sensitivity */
5927 dbprog_segments_setmode(s);
5928
5929 dpy_progmode(s, FALSE);
5930 dpy_playmode(s, FALSE);
5931 }
5932
5933
5934 /*
5935 * cd_sample
5936 * Main window sample mode button callback function
5937 */
5938 /*ARGSUSED*/
5939 void
cd_sample(Widget w,XtPointer client_data,XtPointer call_data)5940 cd_sample(Widget w, XtPointer client_data, XtPointer call_data)
5941 {
5942 DBGPRN(DBG_UI)(errfp, "\n* SAMPLE\n");
5943
5944 if (searching) {
5945 cd_beep();
5946 return;
5947 }
5948 di_sample((curstat_t *)(void *) client_data);
5949 }
5950
5951
5952 /*
5953 * cd_level
5954 * Main window volume control slider callback function
5955 */
5956 /*ARGSUSED*/
5957 void
cd_level(Widget w,XtPointer client_data,XtPointer call_data)5958 cd_level(Widget w, XtPointer client_data, XtPointer call_data)
5959 {
5960 XmScaleCallbackStruct
5961 *p = (XmScaleCallbackStruct *)(void *) call_data;
5962
5963 DBGPRN(DBG_UI)(errfp, "\n* VOL: %d\n", (int) p->value);
5964
5965 di_level(
5966 (curstat_t *)(void *) client_data,
5967 (byte_t) p->value,
5968 (bool_t) (p->reason != XmCR_VALUE_CHANGED)
5969 );
5970 }
5971
5972
5973 /*
5974 * cd_play_pause
5975 * Main window play/pause button callback function
5976 */
5977 /*ARGSUSED*/
5978 void
cd_play_pause(Widget w,XtPointer client_data,XtPointer call_data)5979 cd_play_pause(Widget w, XtPointer client_data, XtPointer call_data)
5980 {
5981 curstat_t *s = (curstat_t *)(void *) client_data;
5982 sword32_t sav_trk;
5983
5984 DBGPRN(DBG_UI)(errfp, "\n* PLAY_PAUSE\n");
5985
5986 if (searching) {
5987 cd_beep();
5988 return;
5989 }
5990
5991 if (s->mode == MOD_STOP && s->program) {
5992 if (!dbprog_pgm_parse(s)) {
5993 cd_beep();
5994 return;
5995 }
5996 s->segplay = SEGP_NONE; /* Cancel a->b mode */
5997 dpy_progmode(s, FALSE);
5998 }
5999
6000 sav_trk = s->cur_trk;
6001
6002 di_play_pause(s);
6003
6004 if (sav_trk >= 0) {
6005 /* Update curfile */
6006 dbprog_curfileupd();
6007 }
6008
6009 switch (s->mode) {
6010 case MOD_PAUSE:
6011 dpy_time(s, FALSE);
6012 break;
6013 case MOD_PLAY:
6014 case MOD_STOP:
6015 case MOD_SAMPLE:
6016 dpy_time(s, FALSE);
6017
6018 cd_keypad_clear(w, client_data, NULL);
6019 warp_busy = FALSE;
6020 break;
6021 }
6022 }
6023
6024
6025 /*
6026 * cd_stop
6027 * Main window stop button callback function
6028 */
6029 /*ARGSUSED*/
6030 void
cd_stop(Widget w,XtPointer client_data,XtPointer call_data)6031 cd_stop(Widget w, XtPointer client_data, XtPointer call_data)
6032 {
6033 curstat_t *s = (curstat_t *)(void *) client_data;
6034
6035 DBGPRN(DBG_UI)(errfp, "\n* STOP\n");
6036
6037 if (searching) {
6038 cd_beep();
6039 return;
6040 }
6041
6042 /* If the user clicks "stop" while in a->? state, cancel it */
6043 if (s->segplay == SEGP_A) {
6044 s->segplay = SEGP_NONE;
6045 dpy_progmode(s, FALSE);
6046 }
6047
6048 dpy_time(s, FALSE);
6049
6050 di_stop(s, TRUE);
6051 }
6052
6053
6054 /*
6055 * cd_chgdisc
6056 * Main window disc change buttons callback function
6057 */
6058 /*ARGSUSED*/
6059 void
cd_chgdisc(Widget w,XtPointer client_data,XtPointer call_data)6060 cd_chgdisc(Widget w, XtPointer client_data, XtPointer call_data)
6061 {
6062 curstat_t *s = (curstat_t *)(void *) client_data;
6063 int newdisc;
6064
6065 if (s->first_disc == s->last_disc) {
6066 /* Single disc player */
6067 cd_beep();
6068 return;
6069 }
6070
6071 newdisc = s->cur_disc;
6072
6073 if (w == widgets.main.prevdisc_btn) {
6074 DBGPRN(DBG_UI)(errfp, "\n* PREV_DISC\n");
6075 if (newdisc > s->first_disc)
6076 newdisc--;
6077 }
6078 else if (w == widgets.main.nextdisc_btn) {
6079 DBGPRN(DBG_UI)(errfp, "\n* NEXT_DISC\n");
6080 if (newdisc < s->last_disc)
6081 newdisc++;
6082 }
6083 else
6084 return;
6085
6086 if (newdisc == s->cur_disc)
6087 /* No change */
6088 return;
6089
6090 s->prev_disc = s->cur_disc;
6091 s->cur_disc = newdisc;
6092 dpy_disc(s);
6093
6094 s->flags |= STAT_CHGDISC;
6095
6096 /* Ask the user if the changed disc info should be submitted to CDDB */
6097 if (!dbprog_chgsubmit(s))
6098 return;
6099
6100 s->flags &= ~STAT_CHGDISC;
6101
6102 /* Update display: clear disc/track titles on the main window */
6103 chgdelay = TRUE;
6104 dpy_dtitle(s);
6105 dpy_ttitle(s);
6106
6107 /* Use a timer callback routine to do the disc change.
6108 * This allows the user to click on the disc change buttons
6109 * multiple times to advance/reverse to the desired
6110 * target disc without causing the changer to actually
6111 * switch through all of them.
6112 */
6113 if (chgdisc_dlyid >= 0)
6114 cd_untimeout(chgdisc_dlyid);
6115
6116 chgdisc_dlyid = cd_timeout(CHGDISC_DELAY, do_chgdisc, (byte_t *) s);
6117 }
6118
6119
6120 /*
6121 * cd_prevtrk
6122 * Main window prev track button callback function
6123 */
6124 /*ARGSUSED*/
6125 void
cd_prevtrk(Widget w,XtPointer client_data,XtPointer call_data)6126 cd_prevtrk(Widget w, XtPointer client_data, XtPointer call_data)
6127 {
6128 DBGPRN(DBG_UI)(errfp, "\n* PREVTRK\n");
6129
6130 if (searching) {
6131 cd_beep();
6132 return;
6133 }
6134 di_prevtrk((curstat_t *)(void *) client_data);
6135 }
6136
6137
6138 /*
6139 * cd_nexttrk
6140 * Main window next track button callback function
6141 */
6142 /*ARGSUSED*/
6143 void
cd_nexttrk(Widget w,XtPointer client_data,XtPointer call_data)6144 cd_nexttrk(Widget w, XtPointer client_data, XtPointer call_data)
6145 {
6146 DBGPRN(DBG_UI)(errfp, "\n* NEXTTRK\n");
6147
6148 if (searching) {
6149 cd_beep();
6150 return;
6151 }
6152 di_nexttrk((curstat_t *)(void *) client_data);
6153 }
6154
6155
6156 /*
6157 * cd_previdx
6158 * Main window prev index button callback function
6159 */
6160 /*ARGSUSED*/
6161 void
cd_previdx(Widget w,XtPointer client_data,XtPointer call_data)6162 cd_previdx(Widget w, XtPointer client_data, XtPointer call_data)
6163 {
6164 DBGPRN(DBG_UI)(errfp, "\n* PREVIDX\n");
6165
6166 if (searching) {
6167 cd_beep();
6168 return;
6169 }
6170 di_previdx((curstat_t *)(void *) client_data);
6171 }
6172
6173
6174 /*
6175 * cd_previdx
6176 * Main window next index button callback function
6177 */
6178 /*ARGSUSED*/
6179 void
cd_nextidx(Widget w,XtPointer client_data,XtPointer call_data)6180 cd_nextidx(Widget w, XtPointer client_data, XtPointer call_data)
6181 {
6182 DBGPRN(DBG_UI)(errfp, "\n* NEXTIDX\n");
6183
6184 if (searching) {
6185 cd_beep();
6186 return;
6187 }
6188 di_nextidx((curstat_t *)(void *) client_data);
6189 }
6190
6191
6192 /*
6193 * cd_rew
6194 * Main window search rewind button callback function
6195 */
6196 /*ARGSUSED*/
6197 void
cd_rew(Widget w,XtPointer client_data,XtPointer call_data)6198 cd_rew(Widget w, XtPointer client_data, XtPointer call_data)
6199 {
6200 XmPushButtonCallbackStruct
6201 *p = (XmPushButtonCallbackStruct *)(void *) call_data;
6202 curstat_t *s = (curstat_t *)(void *) client_data;
6203 bool_t start;
6204 static bool_t rew_running = FALSE;
6205
6206 if (p->reason == XmCR_ARM) {
6207 DBGPRN(DBG_UI)(errfp, "\n* REW: down\n");
6208
6209 if (!rew_running) {
6210 if (searching) {
6211 /* Release running FF */
6212 XtCallActionProc(
6213 widgets.main.ff_btn,
6214 "Activate",
6215 p->event,
6216 NULL,
6217 0
6218 );
6219 XtCallActionProc(
6220 widgets.main.ff_btn,
6221 "Disarm",
6222 p->event,
6223 NULL,
6224 0
6225 );
6226 }
6227
6228 rew_running = TRUE;
6229 searching = TRUE;
6230 start = TRUE;
6231 }
6232 else
6233 /* Already running REW */
6234 return;
6235 }
6236 else {
6237 DBGPRN(DBG_UI)(errfp, "\n* REW: up\n");
6238
6239 if (rew_running) {
6240 rew_running = FALSE;
6241 searching = FALSE;
6242 start = FALSE;
6243 }
6244 else
6245 /* Not running REW */
6246 return;
6247 }
6248
6249 di_rew(s, start);
6250
6251 dpy_time(s, FALSE);
6252 }
6253
6254
6255 /*
6256 * cd_ff
6257 * Main window search fast-forward button callback function
6258 */
6259 /*ARGSUSED*/
6260 void
cd_ff(Widget w,XtPointer client_data,XtPointer call_data)6261 cd_ff(Widget w, XtPointer client_data, XtPointer call_data)
6262 {
6263 XmPushButtonCallbackStruct
6264 *p = (XmPushButtonCallbackStruct *)(void *) call_data;
6265 curstat_t *s = (curstat_t *)(void *) client_data;
6266 bool_t start;
6267 static bool_t ff_running = FALSE;
6268
6269 if (p->reason == XmCR_ARM) {
6270 DBGPRN(DBG_UI)(errfp, "\n* FF: down\n");
6271
6272 if (!ff_running) {
6273 if (searching) {
6274 /* Release running REW */
6275 XtCallActionProc(
6276 widgets.main.rew_btn,
6277 "Activate",
6278 p->event,
6279 NULL,
6280 0
6281 );
6282 XtCallActionProc(
6283 widgets.main.rew_btn,
6284 "Disarm",
6285 p->event,
6286 NULL,
6287 0
6288 );
6289 }
6290
6291 ff_running = TRUE;
6292 searching = TRUE;
6293 start = TRUE;
6294 }
6295 else
6296 /* Already running FF */
6297 return;
6298 }
6299 else {
6300 DBGPRN(DBG_UI)(errfp, "\n* FF: up\n");
6301
6302 if (ff_running) {
6303 ff_running = FALSE;
6304 searching = FALSE;
6305 start = FALSE;
6306 }
6307 else
6308 /* Not running FF */
6309 return;
6310 }
6311
6312 di_ff(s, start);
6313
6314 dpy_time(s, FALSE);
6315 }
6316
6317
6318 /*
6319 * cd_keypad_popup
6320 * Main window keypad button callback function
6321 */
6322 void
cd_keypad_popup(Widget w,XtPointer client_data,XtPointer call_data)6323 cd_keypad_popup(Widget w, XtPointer client_data, XtPointer call_data)
6324 {
6325 static bool_t first = TRUE;
6326
6327 if (XtIsManaged(widgets.keypad.form)) {
6328 /* Pop down keypad window */
6329 cd_keypad_popdown(w, client_data, call_data);
6330 return;
6331 }
6332
6333 /* Pop up keypad window.
6334 * The dialog has mappedWhenManaged set to False,
6335 * so we have to map/unmap explicitly. The reason for this
6336 * is we want to avoid a screen glitch when we move the window
6337 * in cd_dialog_setpos(), so we map the window afterwards.
6338 */
6339 XtManageChild(widgets.keypad.form);
6340 if (first) {
6341 first = FALSE;
6342 /* Set window position */
6343 cd_dialog_setpos(XtParent(widgets.keypad.form));
6344 }
6345 XtMapWidget(XtParent(widgets.keypad.form));
6346
6347 /* Reset keypad */
6348 cd_keypad_clear(w, client_data, NULL);
6349
6350 /* Update warp slider */
6351 dpy_warp((curstat_t *)(void *) client_data);
6352
6353 XmProcessTraversal(
6354 widgets.keypad.cancel_btn,
6355 XmTRAVERSE_CURRENT
6356 );
6357 }
6358
6359
6360 /*
6361 * cd_keypad_popdown
6362 * Keypad window popdown callback function
6363 */
6364 /*ARGSUSED*/
6365 void
cd_keypad_popdown(Widget w,XtPointer client_data,XtPointer call_data)6366 cd_keypad_popdown(Widget w, XtPointer client_data, XtPointer call_data)
6367 {
6368 /* Pop down keypad window */
6369 if (XtIsManaged(widgets.keypad.form)) {
6370 XtUnmapWidget(XtParent(widgets.keypad.form));
6371 XtUnmanageChild(widgets.keypad.form);
6372 }
6373 }
6374
6375
6376 /*
6377 * cd_keypad_mode
6378 * Keypad window mode selector callback function
6379 */
6380 void
cd_keypad_mode(Widget w,XtPointer client_data,XtPointer call_data)6381 cd_keypad_mode(Widget w, XtPointer client_data, XtPointer call_data)
6382 {
6383 XmRowColumnCallbackStruct *p =
6384 (XmRowColumnCallbackStruct *)(void *) call_data;
6385 XmToggleButtonCallbackStruct *q;
6386 curstat_t *s = (curstat_t *)(void *) client_data;
6387
6388 if (p == NULL)
6389 return;
6390
6391 q = (XmToggleButtonCallbackStruct *)(void *) p->callbackstruct;
6392
6393 if (q == NULL || !q->set)
6394 return;
6395
6396 if (p->widget == widgets.keypad.disc_btn) {
6397 if (keypad_mode == KPMODE_DISC)
6398 return; /* No change */
6399
6400 keypad_mode = KPMODE_DISC;
6401 }
6402 else if (p->widget == widgets.keypad.track_btn) {
6403 if (keypad_mode == KPMODE_TRACK)
6404 return; /* No change */
6405
6406 keypad_mode = KPMODE_TRACK;
6407 }
6408 else
6409 return; /* Invalid widget */
6410
6411 cd_keypad_clear(w, (XtPointer) s, NULL);
6412 warp_busy = FALSE;
6413 }
6414
6415
6416 /*
6417 * cd_keypad_num
6418 * Keypad window number button callback function
6419 */
6420 /*ARGSUSED*/
6421 void
cd_keypad_num(Widget w,XtPointer client_data,XtPointer call_data)6422 cd_keypad_num(Widget w, XtPointer client_data, XtPointer call_data)
6423 {
6424 curstat_t *s = curstat_addr();
6425 int n;
6426 char tmpstr[2];
6427
6428 /* The user entered a digit */
6429 if (strlen(keystr) >= sizeof(keystr) - 1) {
6430 cd_beep();
6431 return;
6432 }
6433
6434 (void) sprintf(tmpstr, "%lu", (unsigned long) client_data);
6435 (void) strcat(keystr, tmpstr);
6436
6437 switch (keypad_mode) {
6438 case KPMODE_DISC:
6439 n = atoi(keystr);
6440
6441 if (n < s->first_disc || n > s->last_disc) {
6442 /* Illegal disc entered */
6443 cd_keypad_clear(w, (XtPointer) s, NULL);
6444
6445 cd_beep();
6446 return;
6447 }
6448 break;
6449
6450 case KPMODE_TRACK:
6451 n = s->cur_trk;
6452 s->cur_trk = (sword32_t) atoi(keystr);
6453
6454 if (di_curtrk_pos(s) < 0) {
6455 /* Illegal track entered */
6456 cd_keypad_clear(w, (XtPointer) s, NULL);
6457 s->cur_trk = n;
6458
6459 cd_beep();
6460 return;
6461 }
6462 s->cur_trk = n;
6463 break;
6464
6465 default:
6466 /* Illegal mode */
6467 return;
6468 }
6469
6470 warp_offset = 0;
6471 set_warp_slider(0, FALSE);
6472 dpy_keypad_ind(s);
6473 }
6474
6475
6476 /*
6477 * cd_keypad_clear
6478 * Keypad window clear button callback function
6479 */
6480 /*ARGSUSED*/
6481 void
cd_keypad_clear(Widget w,XtPointer client_data,XtPointer call_data)6482 cd_keypad_clear(Widget w, XtPointer client_data, XtPointer call_data)
6483 {
6484 curstat_t *s = (curstat_t *)(void *) client_data;
6485
6486 /* Reset keypad */
6487 keystr[0] = '\0';
6488
6489 /* Hack: if the third arg is NULL, then it's an internal
6490 * call rather than a callback. We want to set s->cur_trk
6491 * to -1 only for callbacks, so that the keypad indicator
6492 * display gets updated correctly.
6493 */
6494 if (call_data != NULL)
6495 s->cur_trk = -1;
6496
6497 warp_offset = 0;
6498 set_warp_slider(0, FALSE);
6499 dpy_keypad_ind(s);
6500 }
6501
6502
6503 /*
6504 * cd_keypad_dsbl_modes_yes
6505 * User "yes" confirm callback to cancel shuffle or program modes after
6506 * activating the keypad.
6507 */
6508 /*ARGSUSED*/
6509 void
cd_keypad_dsbl_modes_yes(Widget w,XtPointer client_data,XtPointer call_data)6510 cd_keypad_dsbl_modes_yes(Widget w, XtPointer client_data, XtPointer call_data)
6511 {
6512 curstat_t *s = (curstat_t *)(void *) client_data;
6513
6514 if (override_sav.func == NULL) {
6515 cd_beep();
6516 return;
6517 }
6518
6519 /* Note: This assumes that shuffle and program modes are
6520 * mutually exclusive!
6521 */
6522 if (s->shuffle) {
6523 /* Disable shuffle mode */
6524 di_shuffle(s, FALSE);
6525 set_shuffle_btn(FALSE);
6526 }
6527 else if (s->program)
6528 dbprog_clrpgm(w, client_data, call_data);
6529
6530 (*override_sav.func)(
6531 override_sav.w,
6532 override_sav.client_data,
6533 override_sav.call_data
6534 );
6535
6536 override_sav.func = (XtCallbackProc) NULL;
6537 if (override_sav.call_data != NULL) {
6538 MEM_FREE(override_sav.call_data);
6539 override_sav.call_data = NULL;
6540 }
6541 }
6542
6543
6544 /*
6545 * cd_keypad_dsbl_modes_no
6546 * User "no" confirm callback to cancel shuffle or program modes after
6547 * activating the keypad.
6548 */
6549 /*ARGSUSED*/
6550 void
cd_keypad_dsbl_modes_no(Widget w,XtPointer client_data,XtPointer call_data)6551 cd_keypad_dsbl_modes_no(Widget w, XtPointer client_data, XtPointer call_data)
6552 {
6553 warp_busy = FALSE;
6554 cd_keypad_clear(w, client_data, call_data);
6555
6556 override_sav.func = (XtCallbackProc) NULL;
6557 if (override_sav.call_data != NULL) {
6558 MEM_FREE(override_sav.call_data);
6559 override_sav.call_data = NULL;
6560 }
6561 }
6562
6563
6564 /*
6565 * cd_keypad_enter
6566 * Keypad window enter button callback function
6567 */
6568 void
cd_keypad_enter(Widget w,XtPointer client_data,XtPointer call_data)6569 cd_keypad_enter(Widget w, XtPointer client_data, XtPointer call_data)
6570 {
6571 curstat_t *s = (curstat_t *)(void *) client_data;
6572 int i;
6573 sword32_t curr,
6574 next,
6575 sav_cur_trk;
6576 bool_t paused = FALSE;
6577
6578 /* The user activated the Enter key */
6579 if (keystr[0] == '\0') {
6580 /* No numeric input */
6581 cd_beep();
6582 return;
6583 }
6584
6585 switch (keypad_mode) {
6586 case KPMODE_DISC:
6587 /* Use disc number selected on keypad */
6588 curr = s->cur_disc;
6589 next = (sword32_t) atoi(keystr);
6590
6591 if (curr == next) {
6592 /* Nothing to do */
6593 cd_keypad_clear(w, client_data, NULL);
6594 break;
6595 }
6596
6597 if (s->program) {
6598 if (s->onetrk_prog)
6599 dbprog_clrpgm(w, client_data, call_data);
6600 else {
6601 /* Trying to use keypad while in shuffle or
6602 * program mode: ask user if shuffle/program
6603 * should be disabled.
6604 */
6605 cd_keypad_ask_dsbl(
6606 s,
6607 cd_keypad_enter,
6608 w,
6609 call_data,
6610 sizeof(XmPushButtonCallbackStruct)
6611 );
6612 return;
6613 }
6614 }
6615
6616 s->prev_disc = curr;
6617 s->cur_disc = next;
6618
6619 cd_keypad_clear(w, client_data, NULL);
6620
6621 s->flags |= STAT_CHGDISC;
6622
6623 /* Ask the user if the changed CDDB entry
6624 * should be saved to file.
6625 */
6626 if (!dbprog_chgsubmit(s))
6627 return;
6628
6629 s->flags &= ~STAT_CHGDISC;
6630
6631 /* Change to watch cursor */
6632 cd_busycurs(TRUE, CURS_ALL);
6633
6634 /* Do the disc change */
6635 di_chgdisc(s);
6636
6637 /* Update display */
6638 dpy_dbmode(s, FALSE);
6639 dpy_playmode(s, FALSE);
6640
6641 /* Change to normal cursor */
6642 cd_busycurs(FALSE, CURS_ALL);
6643
6644 break;
6645
6646 case KPMODE_TRACK:
6647 if (s->mode == MOD_NODISC || s->mode == MOD_BUSY) {
6648 /* Cannot go to a track when the disc is not ready */
6649 cd_keypad_clear(w, client_data, NULL);
6650 cd_beep();
6651 return;
6652 }
6653
6654 if (s->shuffle || s->program) {
6655 if (s->onetrk_prog)
6656 dbprog_clrpgm(w, client_data, call_data);
6657 else {
6658 /* Trying to use keypad while in shuffle or
6659 * program mode: ask user if shuffle/program
6660 * should be disabled.
6661 */
6662 cd_keypad_ask_dsbl(
6663 s,
6664 cd_keypad_enter,
6665 w,
6666 call_data,
6667 sizeof(XmPushButtonCallbackStruct)
6668 );
6669 return;
6670 }
6671 }
6672
6673 /* Use track number selected on keypad */
6674 sav_cur_trk = s->cur_trk;
6675 s->cur_trk = (word32_t) atoi(keystr);
6676
6677 if ((i = di_curtrk_pos(s)) < 0) {
6678 s->cur_trk = sav_cur_trk;
6679 cd_beep();
6680 return;
6681 }
6682
6683 switch (s->mode) {
6684 case MOD_PAUSE:
6685 /* Mute sound */
6686 di_mute_on(s);
6687 paused = TRUE;
6688
6689 /*FALLTHROUGH*/
6690 case MOD_PLAY:
6691 case MOD_SAMPLE:
6692 if (s->segplay == SEGP_AB) {
6693 s->segplay = SEGP_NONE; /* Cancel a->b mode */
6694 dpy_progmode(s, FALSE);
6695 }
6696
6697 sav_cur_trk = s->cur_trk;
6698
6699 /* Set play status to stop */
6700 di_stop(s, FALSE);
6701
6702 /* Restore s->cur_trk because di_stop
6703 * resets it
6704 */
6705 s->cur_trk = sav_cur_trk;
6706
6707 break;
6708
6709 default:
6710 break;
6711 }
6712
6713 s->curpos_trk.addr = warp_offset;
6714 util_blktomsf(
6715 s->curpos_trk.addr,
6716 &s->curpos_trk.min,
6717 &s->curpos_trk.sec,
6718 &s->curpos_trk.frame,
6719 0
6720 );
6721 s->curpos_tot.addr = s->trkinfo[i].addr + warp_offset;
6722 util_blktomsf(
6723 s->curpos_tot.addr,
6724 &s->curpos_tot.min,
6725 &s->curpos_tot.sec,
6726 &s->curpos_tot.frame,
6727 MSF_OFFSET
6728 );
6729
6730 /* Start playback at new position */
6731 cd_play_pause(w, client_data, call_data);
6732
6733 if (paused) {
6734 /* This will cause the playback to pause */
6735 cd_play_pause(w, client_data, call_data);
6736
6737 /* Restore sound */
6738 di_mute_off(s);
6739 }
6740
6741 break;
6742
6743 default:
6744 /* Illegal mode */
6745 break;
6746 }
6747 }
6748
6749
6750 /*
6751 * cd_warp
6752 * Track warp function
6753 */
6754 void
cd_warp(Widget w,XtPointer client_data,XtPointer call_data)6755 cd_warp(Widget w, XtPointer client_data, XtPointer call_data)
6756 {
6757 curstat_t *s = (curstat_t *)(void *) client_data;
6758 XmScaleCallbackStruct
6759 *p = (XmScaleCallbackStruct *)(void *) call_data;
6760 int i;
6761 sword32_t sav_cur_trk;
6762
6763 if (pseudo_warp) {
6764 warp_busy = FALSE;
6765 return;
6766 }
6767
6768 if (keypad_mode != KPMODE_TRACK ||
6769 s->mode == MOD_BUSY || s->mode == MOD_NODISC) {
6770 warp_offset = 0;
6771 warp_busy = FALSE;
6772 set_warp_slider(0, FALSE);
6773 return;
6774 }
6775
6776 sav_cur_trk = s->cur_trk;
6777 if (keystr[0] != '\0') {
6778 /* Use track number selected on keypad */
6779 s->cur_trk = atoi(keystr);
6780 }
6781
6782 if ((i = di_curtrk_pos(s)) < 0) {
6783 warp_offset = 0;
6784 warp_busy = FALSE;
6785 set_warp_slider(0, FALSE);
6786 s->cur_trk = sav_cur_trk;
6787 return;
6788 }
6789
6790 /* Translate slider position to block offset */
6791 warp_offset = (sword32_t) scale_warp(s, i, p->value);
6792
6793 if (p->reason == XmCR_VALUE_CHANGED) {
6794 if ((s->shuffle || s->program) &&
6795 s->mode != MOD_STOP && sav_cur_trk != s->cur_trk) {
6796 if (s->onetrk_prog)
6797 dbprog_clrpgm(w, client_data, call_data);
6798 else {
6799 /* Trying to warp to a different track while
6800 * in shuffle or program mode: ask user if
6801 * shuffle/program should be disabled.
6802 */
6803 cd_keypad_ask_dsbl(
6804 s,
6805 cd_warp,
6806 w,
6807 call_data,
6808 sizeof(XmScaleCallbackStruct)
6809 );
6810 return;
6811 }
6812 }
6813
6814 DBGPRN(DBG_UI)(errfp, "\n* TRACK WARP\n");
6815
6816 s->curpos_trk.addr = warp_offset;
6817 util_blktomsf(
6818 s->curpos_trk.addr,
6819 &s->curpos_trk.min,
6820 &s->curpos_trk.sec,
6821 &s->curpos_trk.frame,
6822 0
6823 );
6824 s->curpos_tot.addr = s->trkinfo[i].addr + warp_offset;
6825 util_blktomsf(
6826 s->curpos_tot.addr,
6827 &s->curpos_tot.min,
6828 &s->curpos_tot.sec,
6829 &s->curpos_tot.frame,
6830 MSF_OFFSET
6831 );
6832
6833 if (s->mode == MOD_STOP) {
6834 warp_busy = TRUE;
6835 dpy_keypad_ind(s);
6836 warp_busy = FALSE;
6837 return;
6838 }
6839
6840 /* Start playback at new position */
6841 di_warp(s);
6842
6843 cd_keypad_clear(w, client_data, NULL);
6844 warp_offset = 0;
6845 warp_busy = FALSE;
6846
6847 /* Update display */
6848 dpy_track(s);
6849 dpy_index(s);
6850 dpy_time(s, FALSE);
6851 }
6852 else {
6853 warp_busy = TRUE;
6854 }
6855
6856 dpy_keypad_ind(s);
6857
6858 /* Restore s->cur_trk to actual */
6859 s->cur_trk = sav_cur_trk;
6860 }
6861
6862
6863 /*
6864 * cd_options_popup
6865 * Options window popup callback function
6866 */
6867 /*ARGSUSED*/
6868 void
cd_options_popup(Widget w,XtPointer client_data,XtPointer call_data)6869 cd_options_popup(Widget w, XtPointer client_data, XtPointer call_data)
6870 {
6871 static bool_t first = TRUE;
6872
6873 if (XtIsManaged(widgets.options.form)) {
6874 /* Pop down options window */
6875 cd_options_popdown(w, client_data, call_data);
6876 return;
6877 }
6878
6879 /* Pop up options window.
6880 * The dialog has mappedWhenManaged set to False,
6881 * so we have to map/unmap explicitly. The reason for this
6882 * is we want to avoid a screen glitch when we move the window
6883 * in cd_dialog_setpos(), so we map the window afterwards.
6884 */
6885 XtManageChild(widgets.options.form);
6886 if (first) {
6887 first = FALSE;
6888 /* Set window position */
6889 cd_dialog_setpos(XtParent(widgets.options.form));
6890 }
6891 XtMapWidget(XtParent(widgets.options.form));
6892
6893 XmProcessTraversal(
6894 widgets.options.ok_btn,
6895 XmTRAVERSE_CURRENT
6896 );
6897 }
6898
6899
6900 /*
6901 * cd_options_popdown
6902 * Options window popdown callback function
6903 */
6904 /*ARGSUSED*/
6905 void
cd_options_popdown(Widget w,XtPointer client_data,XtPointer call_data)6906 cd_options_popdown(Widget w, XtPointer client_data, XtPointer call_data)
6907 {
6908 /* Pop down options window */
6909 if (XtIsManaged(widgets.options.form)) {
6910 XtUnmapWidget(XtParent(widgets.options.form));
6911 XtUnmanageChild(widgets.options.form);
6912 }
6913 }
6914
6915
6916 /*
6917 * cd_options_reset
6918 * Options window reset button callback function
6919 */
6920 /*ARGSUSED*/
6921 void
cd_options_reset(Widget w,XtPointer client_data,XtPointer call_data)6922 cd_options_reset(Widget w, XtPointer client_data, XtPointer call_data)
6923 {
6924 curstat_t *s = (curstat_t *)(void *) client_data;
6925 wlist_t *l;
6926 cdinfo_incore_t *cdp;
6927 cdinfo_path_t *pp;
6928 char *bdevname,
6929 *cp,
6930 str[16],
6931 path[FILE_PATH_SZ * 2];
6932 Widget menuw;
6933 int pri;
6934
6935 if (call_data != NULL) {
6936 /* Re-read defaults */
6937
6938 DBGPRN(DBG_UI)(errfp, "\n* OPTIONS RESET\n");
6939
6940 /* Get system-wide common configuration parameters */
6941 (void) sprintf(path, SYS_CMCFG_PATH, app_data.libdir);
6942 di_common_parmload(path, FALSE, TRUE);
6943
6944 /* Get user common configuration parameters */
6945 (void) sprintf(path, USR_CMCFG_PATH,
6946 util_homedir(util_get_ouid()));
6947 di_common_parmload(path, FALSE, TRUE);
6948
6949 /* Reinit CD info subsystem */
6950 cdinfo_reinit();
6951
6952 #ifdef __VMS
6953 bdevname = NULL;
6954 if (!util_newstr(&bdevname, "device.cfg")) {
6955 CD_FATAL(app_data.str_nomemory);
6956 return;
6957 }
6958 #else
6959 if ((bdevname = util_basename(app_data.device)) == NULL) {
6960 CD_FATAL("Device path basename error: Aborting.");
6961 return;
6962 }
6963 #endif
6964
6965 /* Get system-wide device-specific configuration parameters */
6966 (void) sprintf(path, SYS_DSCFG_PATH, app_data.libdir, bdevname);
6967 di_devspec_parmload(path, FALSE, TRUE);
6968
6969 /* Get user device-specific configuration parameters */
6970 (void) sprintf(path, USR_DSCFG_PATH,
6971 util_homedir(util_get_ouid()), bdevname);
6972 di_devspec_parmload(path, FALSE, TRUE);
6973
6974 MEM_FREE(bdevname);
6975
6976 /* Set the channel routing */
6977 di_route(s);
6978
6979 /* Set the volume level */
6980 di_level(s, (byte_t) s->level, TRUE);
6981 }
6982
6983 /* Set default play mode */
6984 if (!XtIsSensitive(widgets.options.mode_cdda_btn))
6985 app_data.play_mode &= ~PLAYMODE_CDDA;
6986 if (!XtIsSensitive(widgets.options.mode_file_btn))
6987 app_data.play_mode &= ~PLAYMODE_FILE;
6988 if (!XtIsSensitive(widgets.options.mode_pipe_btn))
6989 app_data.play_mode &= ~PLAYMODE_PIPE;
6990
6991 if (PLAYMODE_IS_STD(app_data.play_mode)) {
6992 XmToggleButtonSetState(
6993 widgets.options.mode_std_btn,
6994 True,
6995 True
6996 );
6997 }
6998 else {
6999 XmToggleButtonSetState(
7000 widgets.options.mode_cdda_btn,
7001 (Boolean) ((app_data.play_mode & PLAYMODE_CDDA) != 0),
7002 True
7003 );
7004 XmToggleButtonSetState(
7005 widgets.options.mode_file_btn,
7006 (Boolean) ((app_data.play_mode & PLAYMODE_FILE) != 0),
7007 True
7008 );
7009 XmToggleButtonSetState(
7010 widgets.options.mode_pipe_btn,
7011 (Boolean) ((app_data.play_mode & PLAYMODE_PIPE) != 0),
7012 True
7013 );
7014 }
7015
7016 XmToggleButtonSetState(
7017 widgets.options.mode_jitter_btn,
7018 (Boolean) app_data.cdda_jitter_corr,
7019 (Boolean) (call_data != NULL)
7020 );
7021
7022 XmToggleButtonSetState(
7023 widgets.options.mode_trkfile_btn,
7024 (Boolean) app_data.cdda_trkfile,
7025 (Boolean) (call_data != NULL)
7026 );
7027
7028 XmToggleButtonSetState(
7029 widgets.options.mode_subst_btn,
7030 (Boolean) app_data.subst_underscore,
7031 (Boolean) (call_data != NULL)
7032 );
7033
7034 if (!cdda_filefmt_supp(app_data.cdda_filefmt)) {
7035 /* Specified format not supported: force to RAW */
7036 DBGPRN(DBG_GEN)(errfp,
7037 "\nThe cddaFileFormat (%d) is not supported: "
7038 "reset to RAW\n", app_data.cdda_filefmt);
7039 app_data.cdda_filefmt = FILEFMT_RAW;
7040 }
7041
7042 /* Set file format button */
7043 menuw = filefmt_btn(app_data.cdda_filefmt);
7044 XtVaSetValues(widgets.options.mode_fmt_opt,
7045 XmNmenuHistory, menuw,
7046 NULL
7047 );
7048
7049 /* Set default output file format and path */
7050 if (app_data.cdda_tmpl != NULL &&
7051 !util_newstr(&s->outf_tmpl, app_data.cdda_tmpl)) {
7052 CD_FATAL(app_data.str_nomemory);
7053 return;
7054 }
7055 fix_outfile_path(s);
7056 set_text_string(widgets.options.mode_path_txt, s->outf_tmpl, TRUE);
7057
7058 /* Set default pipe program path and args */
7059 set_text_string(widgets.options.mode_prog_txt,
7060 app_data.pipeprog == NULL ? "" : app_data.pipeprog,
7061 TRUE);
7062
7063 /* Set bitrate & quality factor */
7064 switch (app_data.comp_mode) {
7065 case COMPMODE_1:
7066 menuw = widgets.options.mode1_btn;
7067 XtSetSensitive(widgets.options.qualval_lbl, True);
7068 XtSetSensitive(widgets.options.qualval1_lbl, True);
7069 XtSetSensitive(widgets.options.qualval2_lbl, True);
7070 XtSetSensitive(widgets.options.qualval_scl, True);
7071 XtSetSensitive(widgets.options.bitrate_opt, False);
7072 XtSetSensitive(widgets.options.minbrate_opt, True);
7073 XtSetSensitive(widgets.options.maxbrate_opt, True);
7074 break;
7075 case COMPMODE_2:
7076 menuw = widgets.options.mode2_btn;
7077 XtSetSensitive(widgets.options.qualval_lbl, True);
7078 XtSetSensitive(widgets.options.qualval1_lbl, True);
7079 XtSetSensitive(widgets.options.qualval2_lbl, True);
7080 XtSetSensitive(widgets.options.qualval_scl, True);
7081 XtSetSensitive(widgets.options.bitrate_opt, False);
7082 XtSetSensitive(widgets.options.minbrate_opt, True);
7083 XtSetSensitive(widgets.options.maxbrate_opt, True);
7084 break;
7085 case COMPMODE_3:
7086 menuw = widgets.options.mode3_btn;
7087 XtSetSensitive(widgets.options.qualval_lbl, False);
7088 XtSetSensitive(widgets.options.qualval1_lbl, False);
7089 XtSetSensitive(widgets.options.qualval2_lbl, False);
7090 XtSetSensitive(widgets.options.qualval_scl, False);
7091 XtSetSensitive(widgets.options.bitrate_opt, True);
7092 XtSetSensitive(widgets.options.minbrate_opt, True);
7093 XtSetSensitive(widgets.options.maxbrate_opt, True);
7094 break;
7095 case COMPMODE_0:
7096 default:
7097 menuw = widgets.options.mode0_btn;
7098 XtSetSensitive(widgets.options.qualval_lbl, False);
7099 XtSetSensitive(widgets.options.qualval1_lbl, False);
7100 XtSetSensitive(widgets.options.qualval2_lbl, False);
7101 XtSetSensitive(widgets.options.qualval_scl, False);
7102 XtSetSensitive(widgets.options.bitrate_opt, True);
7103 XtSetSensitive(widgets.options.minbrate_opt, False);
7104 XtSetSensitive(widgets.options.maxbrate_opt, False);
7105 break;
7106 }
7107
7108 /* Set compression method */
7109 XtVaSetValues(widgets.options.method_opt,
7110 XmNmenuHistory, menuw,
7111 NULL
7112 );
7113
7114 /* Set bitrate */
7115 if (app_data.bitrate == 0) {
7116 XtVaSetValues(widgets.options.bitrate_opt,
7117 XmNmenuHistory, widgets.options.bitrate_def_btn,
7118 NULL
7119 );
7120 }
7121 else for (l = cd_brhead; l != NULL; l = l->next) {
7122 if (l->w != (Widget) NULL &&
7123 atoi(XtName(l->w)) == app_data.bitrate) {
7124 XtVaSetValues(widgets.options.bitrate_opt,
7125 XmNmenuHistory, l->w,
7126 NULL
7127 );
7128 }
7129 }
7130
7131 /* Set minimum bitrate */
7132 if (app_data.bitrate_min == 0) {
7133 XtVaSetValues(widgets.options.minbrate_opt,
7134 XmNmenuHistory, widgets.options.minbrate_def_btn,
7135 NULL
7136 );
7137 }
7138 else for (l = cd_minbrhead; l != NULL; l = l->next) {
7139 if (l->w != (Widget) NULL &&
7140 atoi(XtName(l->w)) == app_data.bitrate_min) {
7141 XtVaSetValues(widgets.options.minbrate_opt,
7142 XmNmenuHistory, l->w,
7143 NULL
7144 );
7145 }
7146 }
7147
7148 /* Set maximum bitrate */
7149 if (app_data.bitrate_max == 0) {
7150 XtVaSetValues(widgets.options.maxbrate_opt,
7151 XmNmenuHistory, widgets.options.maxbrate_def_btn,
7152 NULL
7153 );
7154 }
7155 else for (l = cd_maxbrhead; l != NULL; l = l->next) {
7156 if (l->w != (Widget) NULL &&
7157 atoi(XtName(l->w)) == app_data.bitrate_max) {
7158 XtVaSetValues(widgets.options.maxbrate_opt,
7159 XmNmenuHistory, l->w,
7160 NULL
7161 );
7162 }
7163 }
7164
7165 /* Set quality factor */
7166 set_qualval_slider(app_data.qual_factor);
7167
7168 /* Set channel mode */
7169 switch (app_data.chan_mode) {
7170 case CH_JSTEREO:
7171 menuw = widgets.options.chmode_jstereo_btn;
7172 break;
7173 case CH_FORCEMS:
7174 menuw = widgets.options.chmode_forcems_btn;
7175 break;
7176 case CH_MONO:
7177 menuw = widgets.options.chmode_mono_btn;
7178 break;
7179 case CH_STEREO:
7180 default:
7181 menuw = widgets.options.chmode_stereo_btn;
7182 break;
7183 }
7184 XtVaSetValues(widgets.options.chmode_opt,
7185 XmNmenuHistory, menuw,
7186 NULL
7187 );
7188
7189 /* Set compression algorithm value */
7190 set_algo_slider(app_data.comp_algo);
7191
7192 /* Lowpass and highpass filters */
7193 switch (app_data.lowpass_mode) {
7194 case FILTER_AUTO:
7195 menuw = widgets.options.lp_auto_btn;
7196 break;
7197 case FILTER_MANUAL:
7198 menuw = widgets.options.lp_manual_btn;
7199 break;
7200 case FILTER_OFF:
7201 default:
7202 menuw = widgets.options.lp_off_btn;
7203 break;
7204 }
7205 XtVaSetValues(widgets.options.lp_opt,
7206 XmNmenuHistory, menuw,
7207 NULL
7208 );
7209
7210 switch (app_data.highpass_mode) {
7211 case FILTER_AUTO:
7212 menuw = widgets.options.hp_auto_btn;
7213 break;
7214 case FILTER_MANUAL:
7215 menuw = widgets.options.hp_manual_btn;
7216 break;
7217 case FILTER_OFF:
7218 default:
7219 menuw = widgets.options.hp_off_btn;
7220 break;
7221 }
7222 XtVaSetValues(widgets.options.hp_opt,
7223 XmNmenuHistory, menuw,
7224 NULL
7225 );
7226
7227 XtSetSensitive(
7228 widgets.options.lp_freq_txt,
7229 (Boolean) (app_data.lowpass_mode == FILTER_MANUAL)
7230 );
7231 XtSetSensitive(
7232 widgets.options.lp_width_txt,
7233 (Boolean) (app_data.lowpass_mode == FILTER_MANUAL)
7234 );
7235 XtSetSensitive(
7236 widgets.options.hp_freq_txt,
7237 (Boolean) (app_data.highpass_mode == FILTER_MANUAL)
7238 );
7239 XtSetSensitive(
7240 widgets.options.hp_width_txt,
7241 (Boolean) (app_data.highpass_mode == FILTER_MANUAL)
7242 );
7243
7244 (void) sprintf(str, "%d", app_data.lowpass_freq);
7245 set_text_string(widgets.options.lp_freq_txt, str, FALSE);
7246 (void) sprintf(str, "%d", app_data.lowpass_width);
7247 set_text_string(widgets.options.lp_width_txt, str, FALSE);
7248 (void) sprintf(str, "%d", app_data.highpass_freq);
7249 set_text_string(widgets.options.hp_freq_txt, str, FALSE);
7250 (void) sprintf(str, "%d", app_data.highpass_width);
7251 set_text_string(widgets.options.hp_width_txt, str, FALSE);
7252
7253 /* Set flags button states */
7254 XmToggleButtonSetState(
7255 widgets.options.copyrt_btn,
7256 (Boolean) app_data.copyright,
7257 False
7258 );
7259 XmToggleButtonSetState(
7260 widgets.options.orig_btn,
7261 (Boolean) app_data.original,
7262 False
7263 );
7264 XmToggleButtonSetState(
7265 widgets.options.nores_btn,
7266 (Boolean) app_data.nores,
7267 False
7268 );
7269 XmToggleButtonSetState(
7270 widgets.options.chksum_btn,
7271 (Boolean) app_data.checksum,
7272 False
7273 );
7274
7275 /* Set add CD info tag button state */
7276 XmToggleButtonSetState(
7277 widgets.options.addtag_btn,
7278 (Boolean) app_data.add_tag,
7279 False
7280 );
7281 XtSetSensitive(
7282 widgets.options.id3tag_ver_opt,
7283 (Boolean) app_data.add_tag
7284 );
7285
7286 /* Set ID3 tag mode */
7287 switch (app_data.id3tag_mode) {
7288 case ID3TAG_V1:
7289 menuw = widgets.options.id3tag_v1_btn;
7290 break;
7291 case ID3TAG_V2:
7292 menuw = widgets.options.id3tag_v2_btn;
7293 break;
7294 case ID3TAG_BOTH:
7295 default:
7296 menuw = widgets.options.id3tag_both_btn;
7297 break;
7298 }
7299 XtVaSetValues(widgets.options.id3tag_ver_opt,
7300 XmNmenuHistory, menuw,
7301 NULL
7302 );
7303
7304 /* Set LAME options mode buttons */
7305 XmToggleButtonSetState(
7306 widgets.options.lame_disable_btn,
7307 (Boolean) (app_data.lameopts_mode == LAMEOPTS_DISABLE),
7308 False
7309 );
7310 XmToggleButtonSetState(
7311 widgets.options.lame_insert_btn,
7312 (Boolean) (app_data.lameopts_mode == LAMEOPTS_INSERT),
7313 False
7314 );
7315 XmToggleButtonSetState(
7316 widgets.options.lame_append_btn,
7317 (Boolean) (app_data.lameopts_mode == LAMEOPTS_APPEND),
7318 False
7319 );
7320 XmToggleButtonSetState(
7321 widgets.options.lame_replace_btn,
7322 (Boolean) (app_data.lameopts_mode == LAMEOPTS_REPLACE),
7323 False
7324 );
7325
7326 /* Set LAME options string */
7327 set_text_string(
7328 widgets.options.lame_opts_txt,
7329 (app_data.lame_opts == NULL) ? "" : app_data.lame_opts,
7330 FALSE
7331 );
7332 XtSetSensitive(
7333 widgets.options.lame_opts_lbl,
7334 (Boolean) (app_data.lameopts_mode != LAMEOPTS_DISABLE)
7335 );
7336 XtSetSensitive(
7337 widgets.options.lame_opts_txt,
7338 (Boolean) (app_data.lameopts_mode != LAMEOPTS_DISABLE)
7339 );
7340
7341 /* Set CDDA read/write scheduling priority buttons */
7342 XmToggleButtonSetState(
7343 widgets.options.sched_rd_hipri_btn,
7344 (Boolean) ((app_data.cdda_sched & 0x01) != 0),
7345 False
7346 );
7347 XmToggleButtonSetState(
7348 widgets.options.sched_rd_norm_btn,
7349 (Boolean)
7350 !XmToggleButtonGetState(widgets.options.sched_rd_hipri_btn),
7351 False
7352 );
7353 XmToggleButtonSetState(
7354 widgets.options.sched_wr_hipri_btn,
7355 (Boolean) ((app_data.cdda_sched & 0x02) != 0),
7356 False
7357 );
7358 XmToggleButtonSetState(
7359 widgets.options.sched_wr_norm_btn,
7360 (Boolean)
7361 !XmToggleButtonGetState(widgets.options.sched_wr_hipri_btn),
7362 False
7363 );
7364
7365 (void) sprintf(str, "%d", app_data.hb_timeout);
7366 set_text_string(widgets.options.hb_tout_txt, str, FALSE);
7367
7368 /* Set automation states buttons */
7369 XmToggleButtonSetState(
7370 widgets.options.load_none_btn,
7371 (Boolean) (!app_data.load_spindown && !app_data.load_play),
7372 False
7373 );
7374 XmToggleButtonSetState(
7375 widgets.options.load_spdn_btn,
7376 (Boolean) app_data.load_spindown,
7377 False
7378 );
7379 XmToggleButtonSetState(
7380 widgets.options.load_play_btn,
7381 (Boolean) app_data.load_play,
7382 False
7383 );
7384 XmToggleButtonSetState(
7385 widgets.options.load_lock_btn,
7386 (Boolean) app_data.caddy_lock,
7387 False
7388 );
7389
7390 XmToggleButtonSetState(
7391 widgets.options.exit_none_btn,
7392 (Boolean) (!app_data.exit_stop && !app_data.exit_eject),
7393 False
7394 );
7395 XmToggleButtonSetState(
7396 widgets.options.exit_stop_btn,
7397 (Boolean) app_data.exit_stop,
7398 False
7399 );
7400 XmToggleButtonSetState(
7401 widgets.options.exit_eject_btn,
7402 (Boolean) app_data.exit_eject,
7403 False
7404 );
7405
7406 XmToggleButtonSetState(
7407 widgets.options.done_eject_btn,
7408 (Boolean) app_data.done_eject,
7409 False
7410 );
7411
7412 XmToggleButtonSetState(
7413 widgets.options.done_exit_btn,
7414 (Boolean) app_data.done_exit,
7415 False
7416 );
7417
7418 XmToggleButtonSetState(
7419 widgets.options.eject_exit_btn,
7420 (Boolean) app_data.eject_exit,
7421 False
7422 );
7423
7424 /* Set CD changer options buttons */
7425 XmToggleButtonSetState(
7426 widgets.options.chg_multiplay_btn,
7427 (Boolean) app_data.multi_play,
7428 False
7429 );
7430
7431 XmToggleButtonSetState(
7432 widgets.options.chg_reverse_btn,
7433 (Boolean) app_data.reverse,
7434 False
7435 );
7436
7437 /* Set volume/balance settings buttons */
7438 XmToggleButtonSetState(
7439 widgets.options.vol_linear_btn,
7440 (Boolean) (app_data.vol_taper == VOLTAPER_LINEAR),
7441 False
7442 );
7443 XmToggleButtonSetState(
7444 widgets.options.vol_square_btn,
7445 (Boolean) (app_data.vol_taper == VOLTAPER_SQR),
7446 False
7447 );
7448 XmToggleButtonSetState(
7449 widgets.options.vol_invsqr_btn,
7450 (Boolean) (app_data.vol_taper == VOLTAPER_INVSQR),
7451 False
7452 );
7453
7454 /* Set channel routing buttons */
7455 XmToggleButtonSetState(
7456 widgets.options.chroute_stereo_btn,
7457 (Boolean) (app_data.ch_route == CHROUTE_NORMAL),
7458 False
7459 );
7460 XmToggleButtonSetState(
7461 widgets.options.chroute_rev_btn,
7462 (Boolean) (app_data.ch_route == CHROUTE_REVERSE),
7463 False
7464 );
7465 XmToggleButtonSetState(
7466 widgets.options.chroute_left_btn,
7467 (Boolean) (app_data.ch_route == CHROUTE_L_MONO),
7468 False
7469 );
7470 XmToggleButtonSetState(
7471 widgets.options.chroute_right_btn,
7472 (Boolean) (app_data.ch_route == CHROUTE_R_MONO),
7473 False
7474 );
7475 XmToggleButtonSetState(
7476 widgets.options.chroute_mono_btn,
7477 (Boolean) (app_data.ch_route == CHROUTE_MONO),
7478 False
7479 );
7480
7481 /* Set CDDA playback output port selection buttons */
7482 XmToggleButtonSetState(
7483 widgets.options.outport_spkr_btn,
7484 (Boolean) ((app_data.outport & CDDA_OUT_SPEAKER) != 0),
7485 False
7486 );
7487 XmToggleButtonSetState(
7488 widgets.options.outport_phone_btn,
7489 (Boolean) ((app_data.outport & CDDA_OUT_HEADPHONE) != 0),
7490 False
7491 );
7492 XmToggleButtonSetState(
7493 widgets.options.outport_line_btn,
7494 (Boolean) ((app_data.outport & CDDA_OUT_LINEOUT) != 0),
7495 False
7496 );
7497
7498 /* Set CDDB & CD-TEXT config widgets */
7499 XmToggleButtonSetState(
7500 widgets.options.autobr_btn,
7501 (Boolean) app_data.auto_musicbrowser,
7502 False
7503 );
7504 XmToggleButtonSetState(
7505 widgets.options.manbr_btn,
7506 (Boolean) !app_data.auto_musicbrowser,
7507 False
7508 );
7509 XtSetSensitive(
7510 widgets.options.autobr_btn,
7511 (Boolean) (cdinfo_cddb_ver() == 2)
7512 );
7513 XtSetSensitive(
7514 widgets.options.manbr_btn,
7515 (Boolean) (cdinfo_cddb_ver() == 2)
7516 );
7517
7518 XtSetSensitive(
7519 widgets.options.cddb_pri_btn,
7520 (Boolean) cdinfo_cddb_iscfg()
7521 );
7522 XtSetSensitive(
7523 widgets.options.cdtext_pri_btn,
7524 (Boolean) (cdinfo_cdtext_iscfg() && !app_data.cdtext_dsbl)
7525 );
7526
7527 /* Find whether cdinfoPath gives CDDB or CD-TEXT priority */
7528 pri = CDINFO_RMT;
7529 cdp = cdinfo_addr();
7530 for (pp = cdp->pathlist; pp != NULL; pp = pp->next) {
7531 if (pp->type == CDINFO_RMT || pp->type == CDINFO_CDTEXT) {
7532 pri = pp->type;
7533 break;
7534 }
7535 }
7536
7537 /* Override cdinfoPath if necessary */
7538 if (pri == CDINFO_RMT &&
7539 !XtIsSensitive(widgets.options.cddb_pri_btn) &&
7540 XtIsSensitive(widgets.options.cdtext_pri_btn)) {
7541 pri = CDINFO_CDTEXT;
7542 }
7543 else if (pri == CDINFO_CDTEXT &&
7544 !XtIsSensitive(widgets.options.cdtext_pri_btn) &&
7545 XtIsSensitive(widgets.options.cddb_pri_btn)) {
7546 pri = CDINFO_RMT;
7547 }
7548
7549 /* Set the toggle button states */
7550 XmToggleButtonSetState(
7551 widgets.options.cddb_pri_btn,
7552 (Boolean) (pri == CDINFO_RMT),
7553 False
7554 );
7555 XmToggleButtonSetState(
7556 widgets.options.cdtext_pri_btn,
7557 (Boolean) (pri == CDINFO_CDTEXT),
7558 False
7559 );
7560
7561 (void) sprintf(str, "%d", app_data.srv_timeout);
7562 set_text_string(widgets.options.serv_tout_txt, str, FALSE);
7563
7564 (void) sprintf(str, "%d", app_data.cache_timeout);
7565 set_text_string(widgets.options.cache_tout_txt, str, FALSE);
7566
7567 XmToggleButtonSetState(
7568 widgets.options.use_proxy_btn,
7569 (Boolean) app_data.use_proxy,
7570 False
7571 );
7572 XmToggleButtonSetState(
7573 widgets.options.proxy_auth_btn,
7574 (Boolean) app_data.proxy_auth,
7575 False
7576 );
7577
7578 if ((cp = strrchr(app_data.proxy_server, ':')) != NULL) {
7579 *cp = '\0';
7580 set_text_string(
7581 widgets.options.proxy_srvr_txt,
7582 app_data.proxy_server,
7583 FALSE
7584 );
7585 set_text_string(widgets.options.proxy_port_txt, ++cp, FALSE);
7586 cp--;
7587 *cp = ':';
7588 }
7589 else {
7590 set_text_string(
7591 widgets.options.proxy_srvr_txt,
7592 app_data.proxy_server,
7593 FALSE
7594 );
7595 set_text_string(widgets.options.proxy_port_txt, "80", FALSE);
7596 }
7597
7598 XtSetSensitive(
7599 widgets.options.proxy_srvr_lbl,
7600 (Boolean) app_data.use_proxy
7601 );
7602 XtSetSensitive(
7603 widgets.options.proxy_srvr_txt,
7604 (Boolean) app_data.use_proxy
7605 );
7606 XtSetSensitive(
7607 widgets.options.proxy_port_lbl,
7608 (Boolean) app_data.use_proxy
7609 );
7610 XtSetSensitive(
7611 widgets.options.proxy_port_txt,
7612 (Boolean) app_data.use_proxy
7613 );
7614 XtSetSensitive(
7615 widgets.options.proxy_auth_btn,
7616 (Boolean) app_data.use_proxy
7617 );
7618
7619 /* Make the Reset and Save buttons insensitive */
7620 XtSetSensitive(widgets.options.reset_btn, False);
7621 XtSetSensitive(widgets.options.save_btn, False);
7622 }
7623
7624
7625 /*
7626 * cd_options_save
7627 * Options window save button callback function
7628 */
7629 /*ARGSUSED*/
7630 void
cd_options_save(Widget w,XtPointer client_data,XtPointer call_data)7631 cd_options_save(Widget w, XtPointer client_data, XtPointer call_data)
7632 {
7633 curstat_t *s = (curstat_t *)(void *) client_data;
7634 char *bdevname,
7635 path[FILE_PATH_SZ + 2];
7636
7637 DBGPRN(DBG_UI)(errfp, "\n* OPTIONS SAVE\n");
7638
7639 if (!util_newstr(&app_data.cdda_tmpl, s->outf_tmpl)) {
7640 CD_FATAL(app_data.str_nomemory);
7641 return;
7642 }
7643
7644 /* Change to watch cursor */
7645 cd_busycurs(TRUE, CURS_ALL);
7646
7647 #ifdef __VMS
7648 bdevname = NULL;
7649 if (!util_newstr(&bdevname, "device.cfg")) {
7650 CD_FATAL(app_data.str_nomemory);
7651 return;
7652 }
7653 #else
7654 if ((bdevname = util_basename(app_data.device)) == NULL) {
7655 CD_FATAL("Device path basename error: Aborting.");
7656 return;
7657 }
7658 #endif
7659
7660 /* Save user common configuration parameters */
7661 (void) sprintf(path, USR_CMCFG_PATH,
7662 util_homedir(util_get_ouid()));
7663 di_common_parmsave(path);
7664
7665 /* Save user device-specific configuration parameters */
7666 (void) sprintf(path, USR_DSCFG_PATH,
7667 util_homedir(util_get_ouid()),
7668 bdevname);
7669 di_devspec_parmsave(path);
7670
7671 MEM_FREE(bdevname);
7672
7673 /* Make the Reset and Save buttons insensitive */
7674 XtSetSensitive(widgets.options.reset_btn, False);
7675 XtSetSensitive(widgets.options.save_btn, False);
7676
7677 /* Change to normal cursor */
7678 cd_busycurs(FALSE, CURS_ALL);
7679 }
7680
7681
7682 /*
7683 * cd_options
7684 * Options window toggle button callback function
7685 */
7686 void
cd_options(Widget w,XtPointer client_data,XtPointer call_data)7687 cd_options(Widget w, XtPointer client_data, XtPointer call_data)
7688 {
7689 XmRowColumnCallbackStruct *p =
7690 (XmRowColumnCallbackStruct *)(void *) call_data;
7691 XmToggleButtonCallbackStruct *q;
7692 curstat_t *s = (curstat_t *)(void *) client_data;
7693 cdinfo_incore_t *cdp;
7694 cdinfo_path_t *pp;
7695
7696 if (p == NULL)
7697 return;
7698
7699 q = (XmToggleButtonCallbackStruct *)(void *) p->callbackstruct;
7700
7701 if (w == widgets.options.mode_chkbox) {
7702 switch (s->mode) {
7703 case MOD_PLAY:
7704 case MOD_PAUSE:
7705 case MOD_SAMPLE:
7706 /* Disallow changing the mode while playing */
7707 cd_beep();
7708
7709 XmToggleButtonSetState(
7710 widgets.options.mode_std_btn,
7711 (Boolean)
7712 PLAYMODE_IS_STD(app_data.play_mode),
7713 False
7714 );
7715 XmToggleButtonSetState(
7716 widgets.options.mode_cdda_btn,
7717 (Boolean)
7718 ((app_data.play_mode & PLAYMODE_CDDA) != 0),
7719 False
7720 );
7721 XmToggleButtonSetState(
7722 widgets.options.mode_file_btn,
7723 (Boolean)
7724 ((app_data.play_mode & PLAYMODE_FILE) != 0),
7725 False
7726 );
7727 XmToggleButtonSetState(
7728 widgets.options.mode_pipe_btn,
7729 (Boolean)
7730 ((app_data.play_mode & PLAYMODE_PIPE) != 0),
7731 False
7732 );
7733 return;
7734
7735 default:
7736 break;
7737 }
7738
7739 if (p->widget == widgets.options.mode_std_btn) {
7740 if (!q->set)
7741 return;
7742 DBGPRN(DBG_UI)(errfp, "\n* OPTION: playMode=STD\n");
7743 app_data.play_mode = PLAYMODE_STD;
7744 }
7745 else if (p->widget == widgets.options.mode_cdda_btn) {
7746 if (q->set) {
7747 DBGPRN(DBG_UI)(errfp,
7748 "\n* OPTION: playMode+=CDDA\n");
7749 app_data.play_mode |= PLAYMODE_CDDA;
7750 }
7751 else {
7752 DBGPRN(DBG_UI)(errfp,
7753 "\n* OPTION: playMode-=CDDA\n");
7754 app_data.play_mode &= ~PLAYMODE_CDDA;
7755 }
7756 }
7757 else if (p->widget == widgets.options.mode_file_btn) {
7758 if (q->set) {
7759 DBGPRN(DBG_UI)(errfp,
7760 "\n* OPTION: playMode+=FILE\n");
7761 app_data.play_mode |= PLAYMODE_FILE;
7762 }
7763 else {
7764 DBGPRN(DBG_UI)(errfp,
7765 "\n* OPTION: playMode-=FILE\n");
7766 app_data.play_mode &= ~PLAYMODE_FILE;
7767 }
7768 }
7769 else if (p->widget == widgets.options.mode_pipe_btn) {
7770 if (q->set) {
7771 DBGPRN(DBG_UI)(errfp,
7772 "\n* OPTION: playMode+=PIPE\n");
7773 app_data.play_mode |= PLAYMODE_PIPE;
7774 }
7775 else {
7776 DBGPRN(DBG_UI)(errfp,
7777 "\n* OPTION: playMode-=PIPE\n");
7778 app_data.play_mode &= ~PLAYMODE_PIPE;
7779 }
7780 }
7781
7782 XmToggleButtonSetState(
7783 widgets.options.mode_std_btn,
7784 (Boolean) PLAYMODE_IS_STD(app_data.play_mode),
7785 False
7786 );
7787
7788 if (!di_playmode(s)) {
7789 cd_beep();
7790 CD_INFO(app_data.str_cddainit_fail);
7791 XmToggleButtonSetState(
7792 p->widget,
7793 (Boolean) !q->set,
7794 False
7795 );
7796 XmToggleButtonSetState(
7797 widgets.options.mode_std_btn,
7798 True,
7799 False
7800 );
7801 app_data.play_mode = PLAYMODE_STD;
7802 }
7803
7804 if (PLAYMODE_IS_STD(app_data.play_mode)) {
7805 /* Playback mode related controls */
7806 XmToggleButtonSetState(
7807 widgets.options.mode_cdda_btn, False, False
7808 );
7809 XmToggleButtonSetState(
7810 widgets.options.mode_file_btn, False, False
7811 );
7812 XmToggleButtonSetState(
7813 widgets.options.mode_pipe_btn, False, False
7814 );
7815 XtSetSensitive(widgets.options.mode_jitter_btn, False);
7816 XtSetSensitive(widgets.options.mode_trkfile_btn, False);
7817 XtSetSensitive(widgets.options.mode_subst_btn, False);
7818 XtSetSensitive(widgets.options.mode_fmt_opt, False);
7819 XtSetSensitive(widgets.options.mode_path_lbl, False);
7820 XtSetSensitive(widgets.options.mode_path_txt, False);
7821 XtSetSensitive(widgets.options.mode_prog_lbl, False);
7822 XtSetSensitive(widgets.options.mode_prog_txt, False);
7823
7824 /* Channel routing controls */
7825 XtSetSensitive(
7826 widgets.options.chroute_rev_btn,
7827 (Boolean) app_data.chroute_supp
7828 );
7829 XtSetSensitive(
7830 widgets.options.chroute_left_btn,
7831 (Boolean) app_data.chroute_supp
7832 );
7833 XtSetSensitive(
7834 widgets.options.chroute_right_btn,
7835 (Boolean) app_data.chroute_supp
7836 );
7837 XtSetSensitive(
7838 widgets.options.chroute_mono_btn,
7839 (Boolean) app_data.chroute_supp
7840 );
7841
7842 /* Disable CDDA outport port selector */
7843 XtSetSensitive(widgets.options.outport_lbl, False);
7844 XtSetSensitive(widgets.options.outport_spkr_btn, False);
7845 XtSetSensitive(widgets.options.outport_phone_btn, False);
7846 XtSetSensitive(widgets.options.outport_line_btn, False);
7847
7848 /* Enable square/invsqr volume taper buttons */
7849 XtSetSensitive(widgets.options.vol_square_btn, True);
7850 XtSetSensitive(widgets.options.vol_invsqr_btn, True);
7851
7852 /* Enable balance control */
7853 XtSetSensitive(widgets.options.bal_lbl, True);
7854 XtSetSensitive(widgets.options.ball_lbl, True);
7855 XtSetSensitive(widgets.options.balr_lbl, True);
7856 XtSetSensitive(widgets.options.bal_scale, True);
7857 XtSetSensitive(widgets.options.balctr_btn, True);
7858
7859 /* Disable the CDDA attenuator slider */
7860 XtSetSensitive(widgets.options.cdda_att_lbl, False);
7861 XtSetSensitive(widgets.options.cdda_att_scale, False);
7862 XtSetSensitive(widgets.options.cdda_fadeout_btn, False);
7863 XtSetSensitive(widgets.options.cdda_fadein_btn, False);
7864 XtSetSensitive(widgets.options.cdda_fadeout_lbl, False);
7865 XtSetSensitive(widgets.options.cdda_fadein_lbl, False);
7866 }
7867 else {
7868 /* Playback mode related controls */
7869 XtSetSensitive(widgets.options.mode_jitter_btn, True);
7870 XtSetSensitive(widgets.options.mode_trkfile_btn,
7871 (Boolean) (app_data.play_mode & PLAYMODE_FILE)
7872 );
7873 XtSetSensitive(widgets.options.mode_subst_btn,
7874 (Boolean) (app_data.play_mode & PLAYMODE_FILE)
7875 );
7876 XtSetSensitive(widgets.options.mode_fmt_opt,
7877 (Boolean)
7878 (app_data.play_mode & (PLAYMODE_FILE | PLAYMODE_PIPE))
7879 );
7880 XtSetSensitive(widgets.options.mode_path_lbl,
7881 (Boolean) (app_data.play_mode & PLAYMODE_FILE)
7882 );
7883 XtSetSensitive(widgets.options.mode_path_txt,
7884 (Boolean) (app_data.play_mode & PLAYMODE_FILE)
7885 );
7886 XtSetSensitive(widgets.options.mode_prog_lbl,
7887 (Boolean) (app_data.play_mode & PLAYMODE_PIPE)
7888 );
7889 XtSetSensitive(widgets.options.mode_prog_txt,
7890 (Boolean) (app_data.play_mode & PLAYMODE_PIPE)
7891 );
7892
7893 /* Enable channel routing controls */
7894 XtSetSensitive(widgets.options.chroute_rev_btn, True);
7895 XtSetSensitive(widgets.options.chroute_left_btn, True);
7896 XtSetSensitive(widgets.options.chroute_right_btn, True);
7897 XtSetSensitive(widgets.options.chroute_mono_btn, True);
7898
7899 /* CDDA outport port selector */
7900 XtSetSensitive(widgets.options.outport_lbl,
7901 (Boolean) (app_data.play_mode & PLAYMODE_CDDA)
7902 );
7903 XtSetSensitive(widgets.options.outport_spkr_btn,
7904 (Boolean) (app_data.play_mode & PLAYMODE_CDDA)
7905 );
7906 XtSetSensitive(widgets.options.outport_phone_btn,
7907 (Boolean) (app_data.play_mode & PLAYMODE_CDDA)
7908 );
7909 XtSetSensitive(widgets.options.outport_line_btn,
7910 (Boolean) (app_data.play_mode & PLAYMODE_CDDA)
7911 );
7912
7913 /* Only allow linear volume taper in CDDA modes */
7914 app_data.vol_taper = VOLTAPER_LINEAR;
7915 XmToggleButtonSetState(
7916 widgets.options.vol_linear_btn, True, False
7917 );
7918 XmToggleButtonSetState(
7919 widgets.options.vol_square_btn, False, False
7920 );
7921 XmToggleButtonSetState(
7922 widgets.options.vol_invsqr_btn, False, False
7923 );
7924 XtSetSensitive(widgets.options.vol_square_btn, False);
7925 XtSetSensitive(widgets.options.vol_invsqr_btn, False);
7926
7927 /* Only enable balance control for CDDA playback */
7928 XtSetSensitive(
7929 widgets.options.bal_lbl,
7930 (Boolean)
7931 ((app_data.play_mode & PLAYMODE_CDDA) != 0)
7932 );
7933 XtSetSensitive(
7934 widgets.options.ball_lbl,
7935 (Boolean)
7936 ((app_data.play_mode & PLAYMODE_CDDA) != 0)
7937 );
7938 XtSetSensitive(
7939 widgets.options.balr_lbl,
7940 (Boolean)
7941 ((app_data.play_mode & PLAYMODE_CDDA) != 0)
7942 );
7943 XtSetSensitive(
7944 widgets.options.bal_scale,
7945 (Boolean)
7946 ((app_data.play_mode & PLAYMODE_CDDA) != 0)
7947 );
7948 XtSetSensitive(
7949 widgets.options.balctr_btn,
7950 (Boolean)
7951 ((app_data.play_mode & PLAYMODE_CDDA) != 0)
7952 );
7953
7954 /* Enable the CDDA attenuator slider */
7955 XtSetSensitive(widgets.options.cdda_att_lbl, True);
7956 XtSetSensitive(widgets.options.cdda_att_scale, True);
7957 XtSetSensitive(widgets.options.cdda_fadeout_btn, True);
7958 XtSetSensitive(widgets.options.cdda_fadein_btn, True);
7959 XtSetSensitive(widgets.options.cdda_fadeout_lbl, True);
7960 XtSetSensitive(widgets.options.cdda_fadein_lbl, True);
7961 }
7962 }
7963 else if (w == widgets.options.lame_radbox) {
7964 if (!q->set)
7965 return;
7966
7967 if (p->widget == widgets.options.lame_disable_btn) {
7968 if (app_data.lameopts_mode == LAMEOPTS_DISABLE)
7969 return; /* No change */
7970 app_data.lameopts_mode = LAMEOPTS_DISABLE;
7971 }
7972 else if (p->widget == widgets.options.lame_insert_btn) {
7973 if (app_data.lameopts_mode == LAMEOPTS_INSERT)
7974 return; /* No change */
7975 app_data.lameopts_mode = LAMEOPTS_INSERT;
7976 }
7977 else if (p->widget == widgets.options.lame_append_btn) {
7978 if (app_data.lameopts_mode == LAMEOPTS_APPEND)
7979 return; /* No change */
7980 app_data.lameopts_mode = LAMEOPTS_APPEND;
7981 }
7982 else if (p->widget == widgets.options.lame_replace_btn) {
7983 if (app_data.lameopts_mode == LAMEOPTS_REPLACE)
7984 return; /* No change */
7985 app_data.lameopts_mode = LAMEOPTS_REPLACE;
7986 }
7987
7988 XtSetSensitive(
7989 widgets.options.lame_opts_lbl,
7990 (Boolean) (app_data.lameopts_mode != LAMEOPTS_DISABLE)
7991 );
7992 XtSetSensitive(
7993 widgets.options.lame_opts_txt,
7994 (Boolean) (app_data.lameopts_mode != LAMEOPTS_DISABLE)
7995 );
7996 if (app_data.lameopts_mode != LAMEOPTS_DISABLE) {
7997 XmProcessTraversal(
7998 widgets.options.lame_opts_txt,
7999 XmTRAVERSE_CURRENT
8000 );
8001 }
8002
8003 DBGPRN(DBG_UI)(errfp, "\n* OPTION: lameOptionsMode=%d\n",
8004 app_data.lameopts_mode);
8005 }
8006 else if (w == widgets.options.sched_rd_radbox) {
8007 if (!q->set)
8008 return;
8009
8010 if (p->widget == widgets.options.sched_rd_norm_btn) {
8011 if ((app_data.cdda_sched & 0x01) == 0)
8012 return; /* No change */
8013
8014 app_data.cdda_sched &= ~0x01;
8015 }
8016 else if (p->widget == widgets.options.sched_rd_hipri_btn) {
8017 if ((app_data.cdda_sched & 0x01) != 0)
8018 return; /* No change */
8019
8020 app_data.cdda_sched |= 0x01;
8021 }
8022
8023 DBGPRN(DBG_UI)(errfp, "\n* OPTION: cddaSchedOptions=%d\n",
8024 app_data.cdda_sched);
8025 }
8026 else if (w == widgets.options.sched_wr_radbox) {
8027 if (!q->set)
8028 return;
8029
8030 if (p->widget == widgets.options.sched_wr_norm_btn) {
8031 if ((app_data.cdda_sched & 0x02) == 0)
8032 return; /* No change */
8033
8034 app_data.cdda_sched &= ~0x02;
8035 }
8036 else if (p->widget == widgets.options.sched_wr_hipri_btn) {
8037 if ((app_data.cdda_sched & 0x02) != 0)
8038 return; /* No change */
8039
8040 app_data.cdda_sched |= 0x02;
8041 }
8042
8043 DBGPRN(DBG_UI)(errfp, "\n* OPTION: cddaSchedOptions=%d\n",
8044 app_data.cdda_sched);
8045 }
8046 else if (w == widgets.options.load_chkbox) {
8047 if (p->widget == widgets.options.load_none_btn) {
8048 if (!q->set) {
8049 XmToggleButtonSetState(p->widget, True, False);
8050 return;
8051 }
8052 XmToggleButtonSetState(
8053 widgets.options.load_spdn_btn,
8054 False, False
8055 );
8056 XmToggleButtonSetState(
8057 widgets.options.load_play_btn,
8058 False, False
8059 );
8060 DBGPRN(DBG_UI)(errfp,
8061 "\n* OPTION: spinDownOnLoad=0 playOnLoad=0\n");
8062 app_data.load_spindown = FALSE;
8063 app_data.load_play = FALSE;
8064 }
8065 else if (p->widget == widgets.options.load_spdn_btn) {
8066 if (!q->set) {
8067 XmToggleButtonSetState(p->widget, True, False);
8068 return;
8069 }
8070 XmToggleButtonSetState(
8071 widgets.options.load_none_btn,
8072 False, False
8073 );
8074 XmToggleButtonSetState(
8075 widgets.options.load_play_btn,
8076 False, False
8077 );
8078 DBGPRN(DBG_UI)(errfp,
8079 "\n* OPTION: spinDownOnLoad=1 playOnLoad=0\n");
8080 app_data.load_spindown = TRUE;
8081 app_data.load_play = FALSE;
8082 }
8083 else if (p->widget == widgets.options.load_play_btn) {
8084 if (!q->set) {
8085 XmToggleButtonSetState(p->widget, True, False);
8086 return;
8087 }
8088 XmToggleButtonSetState(
8089 widgets.options.load_none_btn,
8090 False, False
8091 );
8092 XmToggleButtonSetState(
8093 widgets.options.load_spdn_btn,
8094 False, False
8095 );
8096 DBGPRN(DBG_UI)(errfp,
8097 "\n* OPTION: spinDownOnLoad=0 playOnLoad=1\n");
8098 app_data.load_play = TRUE;
8099 app_data.load_spindown = FALSE;
8100 }
8101 else if (p->widget == widgets.options.load_lock_btn) {
8102 if (app_data.caddylock_supp) {
8103 DBGPRN(DBG_UI)(errfp,
8104 "\n* OPTION: caddyLock=%d\n",
8105 q->set);
8106 app_data.caddy_lock = (bool_t) q->set;
8107 }
8108 else {
8109 DBGPRN(DBG_UI)(errfp,
8110 "\n* OPTION: caddyLock=0\n");
8111 cd_beep();
8112 XmToggleButtonSetState(
8113 p->widget,
8114 False,
8115 False
8116 );
8117 return;
8118 }
8119 }
8120 }
8121 else if (w == widgets.options.exit_radbox) {
8122 if (!q->set)
8123 return;
8124
8125 if (p->widget == widgets.options.exit_none_btn) {
8126 if (!app_data.exit_stop && !app_data.exit_eject)
8127 return; /* No change */
8128
8129 DBGPRN(DBG_UI)(errfp,
8130 "\n* OPTION: stopOnExit=0 ejectOnExit=0\n");
8131 app_data.exit_stop = app_data.exit_eject = FALSE;
8132 }
8133 else if (p->widget == widgets.options.exit_stop_btn) {
8134 if (app_data.exit_stop)
8135 return; /* No change */
8136
8137 DBGPRN(DBG_UI)(errfp,
8138 "\n* OPTION: stopOnExit=1 ejectOnExit=0\n");
8139 app_data.exit_stop = TRUE;
8140 app_data.exit_eject = FALSE;
8141 }
8142 else if (p->widget == widgets.options.exit_eject_btn) {
8143 if (app_data.exit_eject)
8144 return; /* No change */
8145
8146 if (app_data.eject_supp) {
8147 DBGPRN(DBG_UI)(errfp, "\n* OPTION: "
8148 "stopOnExit=0 ejectOnExit=1\n");
8149 app_data.exit_stop = FALSE;
8150 app_data.exit_eject = TRUE;
8151 }
8152 else {
8153 cd_beep();
8154 XmToggleButtonSetState(
8155 p->widget,
8156 False,
8157 False
8158 );
8159 if (app_data.exit_stop) {
8160 XmToggleButtonSetState(
8161 widgets.options.exit_stop_btn,
8162 True,
8163 False
8164 );
8165 }
8166 else {
8167 XmToggleButtonSetState(
8168 widgets.options.exit_none_btn,
8169 True,
8170 False
8171 );
8172 }
8173 }
8174 }
8175 }
8176 else if (w == widgets.options.done_chkbox) {
8177 if (p->widget == widgets.options.done_eject_btn) {
8178 if (app_data.eject_supp) {
8179 DBGPRN(DBG_UI)(errfp,
8180 "\n* OPTION: ejectOnDone=%d\n",
8181 q->set);
8182 app_data.done_eject = (bool_t) q->set;
8183 }
8184 else {
8185 DBGPRN(DBG_UI)(errfp,
8186 "\n* OPTION: ejectOnDone=0\n");
8187 cd_beep();
8188 XmToggleButtonSetState(
8189 p->widget,
8190 False,
8191 False
8192 );
8193 return;
8194 }
8195 }
8196 else if (p->widget == widgets.options.done_exit_btn) {
8197 DBGPRN(DBG_UI)(errfp, "\n* OPTION: exitOnDone=%d\n",
8198 q->set);
8199 app_data.done_exit = (bool_t) q->set;
8200 }
8201 }
8202 else if (w == widgets.options.eject_chkbox) {
8203 if (p->widget == widgets.options.eject_exit_btn) {
8204 if (app_data.eject_supp) {
8205 DBGPRN(DBG_UI)(errfp,
8206 "\n* OPTION: exitOnEject=%d\n",
8207 q->set);
8208 app_data.eject_exit = (bool_t) q->set;
8209 }
8210 else {
8211 DBGPRN(DBG_UI)(errfp,
8212 "\n* OPTION: exitOnEject=0\n");
8213 cd_beep();
8214 XmToggleButtonSetState(
8215 p->widget,
8216 False,
8217 False
8218 );
8219 return;
8220 }
8221 }
8222 }
8223 else if (w == widgets.options.chg_chkbox) {
8224 if (s->first_disc == s->last_disc) {
8225 /* Single-disc player: inhibit any change here */
8226 cd_beep();
8227 XmToggleButtonSetState(p->widget, False, False);
8228 return;
8229 }
8230
8231 if (p->widget == widgets.options.chg_multiplay_btn) {
8232 DBGPRN(DBG_UI)(errfp, "\n* OPTION: multiPlay=%d\n",
8233 q->set);
8234 app_data.multi_play = (bool_t) q->set;
8235
8236 if (!app_data.multi_play) {
8237 app_data.reverse = FALSE;
8238 DBGPRN(DBG_UI)(errfp,
8239 "\n* OPTION: reversePlay=0\n");
8240 XmToggleButtonSetState(
8241 widgets.options.chg_reverse_btn,
8242 False,
8243 False
8244 );
8245 }
8246 }
8247 else if (p->widget == widgets.options.chg_reverse_btn) {
8248 DBGPRN(DBG_UI)(errfp, "\n* OPTION: reversePlay=%d\n",
8249 q->set);
8250 app_data.reverse = (bool_t) q->set;
8251
8252 if (app_data.reverse) {
8253 app_data.multi_play = TRUE;
8254 DBGPRN(DBG_UI)(errfp,
8255 "\n* OPTION: multiPlay=0\n");
8256 XmToggleButtonSetState(
8257 widgets.options.chg_multiplay_btn,
8258 True,
8259 False
8260 );
8261 }
8262 }
8263 }
8264 else if (w == widgets.options.chroute_radbox) {
8265 if (!q->set)
8266 return;
8267
8268 if (p->widget == widgets.options.chroute_stereo_btn) {
8269 if (app_data.ch_route == CHROUTE_NORMAL)
8270 return; /* No change */
8271
8272 app_data.ch_route = CHROUTE_NORMAL;
8273 }
8274 else if (p->widget == widgets.options.chroute_rev_btn) {
8275 if (app_data.ch_route == CHROUTE_REVERSE)
8276 return; /* No change */
8277
8278 app_data.ch_route = CHROUTE_REVERSE;
8279 }
8280 else if (p->widget == widgets.options.chroute_left_btn) {
8281 if (app_data.ch_route == CHROUTE_L_MONO)
8282 return; /* No change */
8283
8284 app_data.ch_route = CHROUTE_L_MONO;
8285 }
8286 else if (p->widget == widgets.options.chroute_right_btn) {
8287 if (app_data.ch_route == CHROUTE_R_MONO)
8288 return; /* No change */
8289
8290 app_data.ch_route = CHROUTE_R_MONO;
8291 }
8292 else if (p->widget == widgets.options.chroute_mono_btn) {
8293 if (app_data.ch_route == CHROUTE_MONO)
8294 return; /* No change */
8295
8296 app_data.ch_route = CHROUTE_MONO;
8297 }
8298
8299 DBGPRN(DBG_UI)(errfp, "\n* OPTION: channelRoute=%d\n",
8300 app_data.ch_route);
8301 di_route(s);
8302 }
8303 else if (w == widgets.options.outport_chkbox) {
8304 char *port;
8305
8306 if (p->widget == widgets.options.outport_spkr_btn) {
8307 port = "SPEAKER";
8308
8309 if (q->set)
8310 app_data.outport |= CDDA_OUT_SPEAKER;
8311 else
8312 app_data.outport &= ~CDDA_OUT_SPEAKER;
8313 }
8314 else if (p->widget == widgets.options.outport_phone_btn) {
8315 port = "HEADPHONE";
8316
8317 if (q->set)
8318 app_data.outport |= CDDA_OUT_HEADPHONE;
8319 else
8320 app_data.outport &= ~CDDA_OUT_HEADPHONE;
8321 }
8322 else if (p->widget == widgets.options.outport_line_btn) {
8323 port = "LINEOUT";
8324
8325 if (q->set)
8326 app_data.outport |= CDDA_OUT_LINEOUT;
8327 else
8328 app_data.outport &= ~CDDA_OUT_LINEOUT;
8329 }
8330
8331 DBGPRN(DBG_UI)(errfp, "\n* OPTION: outport%s=%s\n",
8332 q->set ? "+" : "-", port);
8333
8334 cdda_outport();
8335 }
8336 else if (w == widgets.options.vol_radbox) {
8337 if (!q->set)
8338 return;
8339
8340 if (p->widget == widgets.options.vol_linear_btn) {
8341 if (app_data.vol_taper == VOLTAPER_LINEAR)
8342 return; /* No change */
8343
8344 app_data.vol_taper = VOLTAPER_LINEAR;
8345 }
8346 else if (p->widget == widgets.options.vol_square_btn) {
8347 if (app_data.vol_taper == VOLTAPER_SQR)
8348 return; /* No change */
8349
8350 app_data.vol_taper = VOLTAPER_SQR;
8351 }
8352 else if (p->widget == widgets.options.vol_invsqr_btn) {
8353 if (app_data.vol_taper == VOLTAPER_INVSQR)
8354 return; /* No change */
8355
8356 app_data.vol_taper = VOLTAPER_INVSQR;
8357 }
8358
8359 DBGPRN(DBG_UI)(errfp, "\n* OPTION: volumeControlTaper=%d\n",
8360 app_data.vol_taper);
8361 di_level(s, (byte_t) s->level, TRUE);
8362 }
8363 else if (w == widgets.options.mbrowser_radbox) {
8364 if (!q->set)
8365 return;
8366
8367 if (p->widget == widgets.options.autobr_btn) {
8368 if (app_data.auto_musicbrowser)
8369 return; /* No change */
8370
8371 app_data.auto_musicbrowser = TRUE;
8372 }
8373 else if (p->widget == widgets.options.manbr_btn) {
8374 if (!app_data.auto_musicbrowser)
8375 return; /* No change */
8376
8377 app_data.auto_musicbrowser = FALSE;
8378 }
8379 DBGPRN(DBG_UI)(errfp, "\n* OPTION: autoMusicBrowser=%d\n",
8380 (int) app_data.auto_musicbrowser);
8381 }
8382 else if (w == widgets.options.lookup_radbox) {
8383 char *path,
8384 *cp,
8385 *cp2;
8386 int i,
8387 type[2];
8388
8389 if (!q->set)
8390 return;
8391
8392 if (p->widget == widgets.options.cddb_pri_btn) {
8393 type[0] = CDINFO_RMT;
8394 type[1] = CDINFO_CDTEXT;
8395 }
8396 else if (p->widget == widgets.options.cdtext_pri_btn) {
8397 type[0] = CDINFO_CDTEXT;
8398 type[1] = CDINFO_RMT;
8399 }
8400
8401 cdp = cdinfo_addr();
8402
8403 i = 0;
8404 for (pp = cdp->pathlist; pp != NULL; pp = pp->next) {
8405 if (pp->type != CDINFO_RMT &&
8406 pp->type != CDINFO_CDTEXT)
8407 continue;
8408
8409 if (i == 0 && pp->type == type[i])
8410 return; /* No change */
8411
8412 pp->type = type[i];
8413 if (++i > 1)
8414 break;
8415 }
8416
8417 /* Allocate new cdinfo_path string */
8418 cp = (char *) MEM_ALLOC(
8419 "cdinfo_path",
8420 strlen(app_data.cdinfo_path) + 8
8421 );
8422 if (cp == NULL) {
8423 CD_FATAL(app_data.str_nomemory);
8424 return;
8425 }
8426 cp[0] = '\0';
8427
8428 i = 0;
8429 path = app_data.cdinfo_path;
8430 while ((cp2 = strchr(path, CDINFOPATH_SEPCHAR)) != NULL) {
8431 *cp2 = '\0';
8432
8433 PATHENT_APPEND(i, cp, path, type[i]);
8434
8435 (void) sprintf(cp, "%s%c", cp, CDINFOPATH_SEPCHAR);
8436 path = cp2 + 1;
8437 }
8438 PATHENT_APPEND(i, cp, path, type[i]);
8439
8440 /* Replace cdinfo_path string */
8441 MEM_FREE(app_data.cdinfo_path);
8442 app_data.cdinfo_path = cp;
8443
8444 DBGPRN(DBG_UI)(errfp, "\n* OPTION: Lookup priority: %s\n",
8445 (type[0] == CDINFO_RMT) ? "CDDB" : "CD-TEXT");
8446 }
8447
8448 /* Make the Reset and Save buttons sensitive */
8449 XtSetSensitive(widgets.options.reset_btn, True);
8450 XtSetSensitive(widgets.options.save_btn, True);
8451 }
8452
8453
8454 /*
8455 * cd_options_categsel
8456 * Options window category list selection callback function
8457 */
8458 /*ARGSUSED*/
8459 void
cd_options_categsel(Widget w,XtPointer client_data,XtPointer call_data)8460 cd_options_categsel(Widget w, XtPointer client_data, XtPointer call_data)
8461 {
8462 XmListCallbackStruct *p =
8463 (XmListCallbackStruct *)(void *) call_data;
8464 int i;
8465 wlist_t *wl;
8466 static int prev = -1;
8467
8468 if (p->reason != XmCR_BROWSE_SELECT && p->reason != XmCR_ACTIVATE)
8469 return;
8470
8471 i = p->item_position - 1;
8472 if (prev == i)
8473 return; /* No change */
8474
8475 if (prev != -1) {
8476 /* Unmanage the previously selected category */
8477 for (wl = opt_categ[prev].widgets; wl != NULL; wl = wl->next)
8478 XtUnmanageChild(wl->w);
8479 }
8480
8481 /* Manage the selected category widgets */
8482 for (wl = opt_categ[i].widgets; wl != NULL; wl = wl->next)
8483 XtManageChild(wl->w);
8484
8485 prev = i;
8486 }
8487
8488
8489 /*
8490 * cd_jitter_corr
8491 * Options window CDDA jitter correction toggle button callback
8492 */
8493 /*ARGSUSED*/
8494 void
cd_jitter_corr(Widget w,XtPointer client_data,XtPointer call_data)8495 cd_jitter_corr(Widget w, XtPointer client_data, XtPointer call_data)
8496 {
8497 XmToggleButtonCallbackStruct *p =
8498 (XmToggleButtonCallbackStruct *)(void *) call_data;
8499 curstat_t *s = (curstat_t *)(void *) client_data;
8500
8501 if (p->reason != XmCR_VALUE_CHANGED)
8502 return;
8503
8504 if ((bool_t) p->set == app_data.cdda_jitter_corr)
8505 return; /* No change */
8506
8507 DBGPRN(DBG_UI)(errfp,
8508 "\n* CDDA Jitter Correction: %s\n", p->set ? "On" : "Off");
8509
8510 if (di_isdemo())
8511 return; /* Don't actually do anything in demo mode */
8512
8513 app_data.cdda_jitter_corr = (bool_t) p->set;
8514
8515 /* Notify device interface of the change */
8516 di_cddajitter(s);
8517
8518 /* Make the Reset and Save buttons sensitive */
8519 XtSetSensitive(widgets.options.reset_btn, True);
8520 XtSetSensitive(widgets.options.save_btn, True);
8521 }
8522
8523
8524 /*
8525 * cd_file_per_trk
8526 * Options window CDDA file-per-track toggle button callback
8527 */
8528 /*ARGSUSED*/
8529 void
cd_file_per_trk(Widget w,XtPointer client_data,XtPointer call_data)8530 cd_file_per_trk(Widget w, XtPointer client_data, XtPointer call_data)
8531 {
8532 XmToggleButtonCallbackStruct *p =
8533 (XmToggleButtonCallbackStruct *)(void *) call_data;
8534 curstat_t *s = (curstat_t *)(void *) client_data;
8535
8536 if (p->reason != XmCR_VALUE_CHANGED)
8537 return;
8538
8539 if ((bool_t) p->set == app_data.cdda_trkfile)
8540 return; /* No change */
8541
8542 DBGPRN(DBG_UI)(errfp,
8543 "\n* CDDA File-per-track: %s\n", p->set ? "On" : "Off");
8544
8545 app_data.cdda_trkfile = (bool_t) p->set;
8546
8547 if (s->outf_tmpl != NULL) {
8548 MEM_FREE(s->outf_tmpl);
8549 s->outf_tmpl = NULL;
8550 }
8551
8552 /* Set new output file path template */
8553 fix_outfile_path(s);
8554
8555 set_text_string(widgets.options.mode_path_txt, s->outf_tmpl, TRUE);
8556
8557 /* Make the Reset and Save buttons sensitive */
8558 XtSetSensitive(widgets.options.reset_btn, True);
8559 XtSetSensitive(widgets.options.save_btn, True);
8560 }
8561
8562
8563 /*
8564 * cd_subst
8565 * Options window CDDA file name underscore substitution
8566 * toggle button callback
8567 */
8568 /*ARGSUSED*/
8569 void
cd_subst(Widget w,XtPointer client_data,XtPointer call_data)8570 cd_subst(Widget w, XtPointer client_data, XtPointer call_data)
8571 {
8572 XmToggleButtonCallbackStruct *p =
8573 (XmToggleButtonCallbackStruct *)(void *) call_data;
8574
8575 if (p->reason != XmCR_VALUE_CHANGED)
8576 return;
8577
8578 #ifdef __VMS
8579 /* On VMS, force this toggle to be enabled at all times,
8580 * because VMS file names do not allow spaces and tabs.
8581 */
8582 if (!p->set) {
8583 cd_beep();
8584 XmToggleButtonSetState(w, True, False);
8585 }
8586 #else
8587 if ((bool_t) p->set == app_data.subst_underscore)
8588 return; /* No change */
8589
8590 DBGPRN(DBG_UI)(errfp,
8591 "\n* CDDA Underscore Substitution: %s\n",
8592 p->set ? "On" : "Off");
8593
8594 app_data.subst_underscore = (bool_t) p->set;
8595
8596 /* Make the Reset and Save buttons sensitive */
8597 XtSetSensitive(widgets.options.reset_btn, True);
8598 XtSetSensitive(widgets.options.save_btn, True);
8599 #endif
8600 }
8601
8602
8603 /*
8604 * cd_filefmt_mode
8605 * File format mode selector callback function
8606 */
8607 /*ARGSUSED*/
8608 void
cd_filefmt_mode(Widget w,XtPointer client_data,XtPointer call_data)8609 cd_filefmt_mode(Widget w, XtPointer client_data, XtPointer call_data)
8610 {
8611 XmPushButtonCallbackStruct
8612 *p = (XmPushButtonCallbackStruct *)(void *) call_data;
8613 curstat_t *s = (curstat_t *)(void *) client_data;
8614 int newfmt;
8615
8616 if (p->reason != XmCR_ACTIVATE)
8617 return;
8618
8619 newfmt = filefmt_code(w);
8620
8621 if (newfmt == app_data.cdda_filefmt)
8622 return; /* No change */
8623
8624 app_data.cdda_filefmt = newfmt;
8625
8626 /* Fix output file suffix to match the file type */
8627 fix_outfile_path(s);
8628
8629 set_text_string(widgets.options.mode_path_txt, s->outf_tmpl, TRUE);
8630
8631 /* Make the Reset and Save buttons sensitive */
8632 XtSetSensitive(widgets.options.reset_btn, True);
8633 XtSetSensitive(widgets.options.save_btn, True);
8634 }
8635
8636
8637 /*
8638 * cd_filepath_new
8639 * Save-to-file path text widget callback function
8640 */
8641 /*ARGSUSED*/
8642 void
cd_filepath_new(Widget w,XtPointer client_data,XtPointer call_data)8643 cd_filepath_new(Widget w, XtPointer client_data, XtPointer call_data)
8644 {
8645 XmAnyCallbackStruct *p = (XmAnyCallbackStruct *)(void *) call_data;
8646 curstat_t *s = (curstat_t *)(void *) client_data;
8647 filefmt_t *fmp,
8648 *fmp2;
8649 char *cp,
8650 *cp2,
8651 *cp3,
8652 *suf,
8653 tmp[FILE_PATH_SZ * 2 + 8];
8654 int i,
8655 newfmt;
8656 Widget menuw;
8657
8658 switch (p->reason) {
8659 case XmCR_VALUE_CHANGED:
8660 case XmCR_ACTIVATE:
8661 case XmCR_LOSING_FOCUS:
8662 if ((cp = get_text_string(w, TRUE)) == NULL)
8663 return;
8664
8665 if (s->outf_tmpl != NULL && strcmp(cp, s->outf_tmpl) == 0) {
8666 /* Not changed */
8667 MEM_FREE(cp);
8668
8669 if (p->reason == XmCR_ACTIVATE ||
8670 p->reason == XmCR_LOSING_FOCUS) {
8671 /* Set cursor to beginning of text */
8672 XmTextSetInsertionPosition(w, 0);
8673 }
8674 return;
8675 }
8676
8677 newfmt = app_data.cdda_filefmt;
8678
8679 /* File suffix */
8680 if ((fmp = cdda_filefmt(app_data.cdda_filefmt)) == NULL)
8681 suf = "";
8682 else
8683 suf = fmp->suf;
8684
8685 /* Check if file suffix indicates a change in file format */
8686 menuw = (Widget) NULL;
8687 if ((cp2 = strrchr(cp, '.')) != NULL) {
8688 if ((cp3 = strrchr(cp, DIR_END)) != NULL &&
8689 cp2 > cp3) {
8690 if (strcmp(suf, cp2) != 0) {
8691 for (i = 0; i < MAX_FILEFMTS; i++) {
8692 if ((fmp2 = cdda_filefmt(i)) == NULL)
8693 continue;
8694
8695 if (util_strcasecmp(cp2, fmp2->suf) == 0)
8696 newfmt = fmp2->fmt;
8697 }
8698 }
8699 }
8700 else if (strcmp(suf, cp2) != 0) {
8701 for (i = 0; i < MAX_FILEFMTS; i++) {
8702 if ((fmp2 = cdda_filefmt(i)) == NULL)
8703 continue;
8704
8705 if (util_strcasecmp(cp2, fmp2->suf) == 0)
8706 newfmt = fmp2->fmt;
8707 }
8708 }
8709
8710 menuw = filefmt_btn(newfmt);
8711 }
8712 else
8713 cp2 = "";
8714
8715 if (!cdda_filefmt_supp(newfmt)) {
8716 /* Specified format not supported */
8717 if (p->reason == XmCR_ACTIVATE ||
8718 p->reason == XmCR_LOSING_FOCUS) {
8719 /* Force back to original format */
8720 fix_outfile_path(s);
8721 set_text_string(w, s->outf_tmpl, TRUE);
8722
8723 /* Set cursor to beginning of text */
8724 XmTextSetInsertionPosition(w, 0);
8725 }
8726 return;
8727 }
8728
8729 if (!util_newstr(&s->outf_tmpl, cp)) {
8730 MEM_FREE(cp);
8731 CD_FATAL(app_data.str_nomemory);
8732 return;
8733 }
8734
8735 MEM_FREE(cp);
8736
8737 /* File format changed */
8738 if (newfmt != app_data.cdda_filefmt) {
8739 XtVaSetValues(widgets.options.mode_fmt_opt,
8740 XmNmenuHistory, menuw,
8741 NULL
8742 );
8743 app_data.cdda_filefmt = newfmt;
8744 }
8745
8746 /* Fix output file suffix to match the file type */
8747 fix_outfile_path(s);
8748
8749 if (util_strstr(s->outf_tmpl, "%T") != NULL ||
8750 util_strstr(s->outf_tmpl, "%t") != NULL ||
8751 util_strstr(s->outf_tmpl, "%R") != NULL ||
8752 util_strstr(s->outf_tmpl, "%r") != NULL ||
8753 util_strstr(s->outf_tmpl, "%#") != NULL) {
8754 if (!app_data.cdda_trkfile) {
8755 XmToggleButtonSetState(
8756 widgets.options.mode_trkfile_btn,
8757 True,
8758 False
8759 );
8760 app_data.cdda_trkfile = TRUE;
8761 }
8762 }
8763 else if (app_data.cdda_trkfile) {
8764 XmToggleButtonSetState(
8765 widgets.options.mode_trkfile_btn,
8766 False,
8767 False
8768 );
8769 app_data.cdda_trkfile = FALSE;
8770 }
8771
8772 if (p->reason == XmCR_ACTIVATE ||
8773 p->reason == XmCR_LOSING_FOCUS) {
8774 set_text_string(w, s->outf_tmpl, TRUE);
8775
8776 /* Set cursor to beginning of text */
8777 XmTextSetInsertionPosition(w, 0);
8778
8779 DBGPRN(DBG_UI)(errfp, "\n* CDDA file template: %s\n",
8780 s->outf_tmpl);
8781 }
8782
8783 break;
8784
8785 default:
8786 cp2 = "";
8787 break;
8788 }
8789
8790 (void) sprintf(tmp,
8791 #ifdef __VMS
8792 "%%S.%%C.%%I]%s%s",
8793 #else
8794 "%%S/%%C/%%I/%s%s",
8795 #endif
8796 app_data.cdda_trkfile ? FILEPATH_TRACK : FILEPATH_DISC,
8797 cp2
8798 );
8799
8800 if (strcmp(s->outf_tmpl, tmp) != 0) {
8801 /* Make the Reset and Save buttons sensitive */
8802 XtSetSensitive(widgets.options.reset_btn, True);
8803 XtSetSensitive(widgets.options.save_btn, True);
8804 }
8805 }
8806
8807
8808 /*
8809 * cd_filepath_exp
8810 * File path expand pushbutton callback function
8811 */
8812 /*ARGSUSED*/
8813 void
cd_filepath_exp(Widget w,XtPointer client_data,XtPointer call_data)8814 cd_filepath_exp(Widget w, XtPointer client_data, XtPointer call_data)
8815 {
8816 curstat_t *s = (curstat_t *)(void *) client_data;
8817 chset_conv_t *up;
8818 char *a = NULL;
8819 int i;
8820 bool_t ret;
8821
8822 /* call_data is NULL when this function is called via
8823 * an update rather than a button click.
8824 */
8825 if (call_data != NULL) {
8826 ret = pathexp_active;
8827 cd_info2_popdown(NULL);
8828 if (ret)
8829 return;
8830 }
8831
8832 switch (s->mode) {
8833 case MOD_PLAY:
8834 case MOD_PAUSE:
8835 case MOD_SAMPLE:
8836 if ((app_data.play_mode & PLAYMODE_FILE) == 0)
8837 return;
8838 break;
8839 default:
8840 return;
8841 }
8842
8843 if (app_data.cdda_trkfile) {
8844 if ((i = di_curtrk_pos(s)) < 0)
8845 return;
8846 }
8847 else
8848 i = 0;
8849
8850 if (s->trkinfo[i].outfile == NULL)
8851 return;
8852
8853 pathexp_active = TRUE;
8854
8855 /* Convert text from UTF-8 to local charset if possible */
8856 if ((up = util_chset_open(CHSET_UTF8_TO_LOCALE)) == NULL)
8857 return;
8858
8859 if (!util_chset_conv(up, s->trkinfo[i].outfile, &a, FALSE) &&
8860 !util_newstr(&a, s->trkinfo[i].outfile)) {
8861 util_chset_close(up);
8862 return;
8863 }
8864 util_chset_close(up);
8865
8866 CD_INFO2(app_data.str_pathexp, a);
8867 MEM_FREE(a);
8868 }
8869
8870
8871 /*
8872 * cd_pipeprog_new
8873 * Pipe-to-program path text widget callback function
8874 */
8875 /*ARGSUSED*/
8876 void
cd_pipeprog_new(Widget w,XtPointer client_data,XtPointer call_data)8877 cd_pipeprog_new(Widget w, XtPointer client_data, XtPointer call_data)
8878 {
8879 XmAnyCallbackStruct *p = (XmAnyCallbackStruct *)(void *) call_data;
8880 char *cp;
8881
8882 switch (p->reason) {
8883 case XmCR_VALUE_CHANGED:
8884 case XmCR_ACTIVATE:
8885 case XmCR_LOSING_FOCUS:
8886 if ((cp = get_text_string(w, TRUE)) == NULL)
8887 return;
8888
8889 if (app_data.pipeprog != NULL &&
8890 strcmp(cp, app_data.pipeprog) == 0) {
8891 /* Not changed */
8892 MEM_FREE(cp);
8893
8894 if (p->reason == XmCR_ACTIVATE ||
8895 p->reason == XmCR_LOSING_FOCUS) {
8896 /* Set cursor to beginning of text */
8897 XmTextSetInsertionPosition(w, 0);
8898 }
8899 return;
8900 }
8901
8902 if (!util_newstr(&app_data.pipeprog, cp)) {
8903 MEM_FREE(cp);
8904 CD_FATAL(app_data.str_nomemory);
8905 return;
8906 }
8907
8908 MEM_FREE(cp);
8909
8910 if (p->reason == XmCR_ACTIVATE ||
8911 p->reason == XmCR_LOSING_FOCUS) {
8912 /* Set cursor to beginning of text */
8913 XmTextSetInsertionPosition(w, 0);
8914
8915 DBGPRN(DBG_UI)(errfp, "\n* CDDA pipe program: %s\n",
8916 app_data.pipeprog);
8917 }
8918
8919 /* Make the Reset and Save buttons sensitive */
8920 XtSetSensitive(widgets.options.reset_btn, True);
8921 XtSetSensitive(widgets.options.save_btn, True);
8922
8923 break;
8924
8925 default:
8926 break;
8927 }
8928 }
8929
8930
8931 /*
8932 * cd_comp_mode
8933 * Options window encoding method menu entry callback
8934 */
8935 /*ARGSUSED*/
8936 void
cd_comp_mode(Widget w,XtPointer client_data,XtPointer call_data)8937 cd_comp_mode(Widget w, XtPointer client_data, XtPointer call_data)
8938 {
8939 XmPushButtonCallbackStruct *p =
8940 (XmPushButtonCallbackStruct *)(void *) call_data;
8941 int newmode;
8942
8943 if (p->reason != XmCR_ACTIVATE)
8944 return;
8945
8946 if (w == widgets.options.mode0_btn)
8947 newmode = COMPMODE_0;
8948 else if (w == widgets.options.mode1_btn)
8949 newmode = COMPMODE_1;
8950 else if (w == widgets.options.mode2_btn)
8951 newmode = COMPMODE_2;
8952 else if (w == widgets.options.mode3_btn)
8953 newmode = COMPMODE_3;
8954 else
8955 return;
8956
8957 if (app_data.comp_mode == newmode)
8958 return; /* No change */
8959
8960 DBGPRN(DBG_UI)(errfp, "\n* Compression mode: Mode %d\n", newmode);
8961 app_data.comp_mode = newmode;
8962
8963 switch (newmode) {
8964 case COMPMODE_1:
8965 case COMPMODE_2:
8966 XtSetSensitive(widgets.options.qualval_lbl, True);
8967 XtSetSensitive(widgets.options.qualval1_lbl, True);
8968 XtSetSensitive(widgets.options.qualval2_lbl, True);
8969 XtSetSensitive(widgets.options.qualval_scl, True);
8970 XtSetSensitive(widgets.options.bitrate_opt, False);
8971 XtSetSensitive(widgets.options.minbrate_opt, True);
8972 XtSetSensitive(widgets.options.maxbrate_opt, True);
8973 break;
8974
8975 case COMPMODE_3:
8976 XtSetSensitive(widgets.options.qualval_lbl, False);
8977 XtSetSensitive(widgets.options.qualval1_lbl, False);
8978 XtSetSensitive(widgets.options.qualval2_lbl, False);
8979 XtSetSensitive(widgets.options.qualval_scl, False);
8980 XtSetSensitive(widgets.options.bitrate_opt, True);
8981 XtSetSensitive(widgets.options.minbrate_opt, True);
8982 XtSetSensitive(widgets.options.maxbrate_opt, True);
8983 break;
8984
8985 case COMPMODE_0:
8986 default:
8987 XtSetSensitive(widgets.options.qualval_lbl, False);
8988 XtSetSensitive(widgets.options.qualval1_lbl, False);
8989 XtSetSensitive(widgets.options.qualval2_lbl, False);
8990 XtSetSensitive(widgets.options.qualval_scl, False);
8991 XtSetSensitive(widgets.options.bitrate_opt, True);
8992 XtSetSensitive(widgets.options.minbrate_opt, False);
8993 XtSetSensitive(widgets.options.maxbrate_opt, False);
8994 break;
8995 }
8996
8997 /* Make the Reset and Save buttons sensitive */
8998 XtSetSensitive(widgets.options.reset_btn, True);
8999 XtSetSensitive(widgets.options.save_btn, True);
9000 }
9001
9002
9003 /*
9004 * cd_bitrate
9005 * CBR/ABR bitrate menu callback function
9006 */
9007 /*ARGSUSED*/
9008 void
cd_bitrate(Widget w,XtPointer client_data,XtPointer call_data)9009 cd_bitrate(Widget w, XtPointer client_data, XtPointer call_data)
9010 {
9011 XmPushButtonCallbackStruct
9012 *p = (XmPushButtonCallbackStruct *)(void *) call_data;
9013 wlist_t *l;
9014 Widget menuw = (Widget) NULL;
9015 int m,
9016 n;
9017
9018 if (p->reason != XmCR_ACTIVATE)
9019 return;
9020
9021 if (w == widgets.options.bitrate_def_btn)
9022 n = 0;
9023 else if ((n = atoi(XtName(w))) == 0)
9024 return;
9025
9026 DBGPRN(DBG_UI)(errfp, "\n* Bitrate: %d kb/s\n", n);
9027 app_data.bitrate = n;
9028
9029 if (n == 0) {
9030 /* Bitrate set to default: set the minimum and maximum to
9031 * default too.
9032 */
9033 if (app_data.bitrate_min > 0) {
9034 XtVaSetValues(widgets.options.minbrate_opt,
9035 XmNmenuHistory,
9036 widgets.options.minbrate_def_btn,
9037 NULL
9038 );
9039 app_data.bitrate_min = 0;
9040 DBGPRN(DBG_UI)(errfp,
9041 " Minimum bitrate reset to %d\n",
9042 app_data.bitrate_min);
9043 }
9044 if (app_data.bitrate_max > 0) {
9045 XtVaSetValues(widgets.options.maxbrate_opt,
9046 XmNmenuHistory,
9047 widgets.options.maxbrate_def_btn,
9048 NULL
9049 );
9050 app_data.bitrate_max = 0;
9051 DBGPRN(DBG_UI)(errfp,
9052 " Maximum bitrate reset to %d\n",
9053 app_data.bitrate_max);
9054 }
9055 }
9056 else {
9057 /* Adjust minimum and maximum bitrates accordingly */
9058 if (app_data.bitrate_min > 0 && n <= app_data.bitrate_min) {
9059 for (l = cd_minbrhead; l != NULL; l = l->next) {
9060 if (l->w != (Widget) NULL) {
9061 m = atoi(XtName(l->w));
9062 if (m < n) {
9063 menuw = l->w;
9064 n = m;
9065 }
9066 }
9067 }
9068 if (menuw != NULL) {
9069 n = atoi(XtName(menuw));
9070 if (n != app_data.bitrate_min) {
9071 XtVaSetValues(
9072 widgets.options.minbrate_opt,
9073 XmNmenuHistory, menuw,
9074 NULL
9075 );
9076 app_data.bitrate_min = n;
9077 DBGPRN(DBG_UI)(errfp,
9078 " Minimum bitrate reset to %d\n",
9079 n);
9080 }
9081 }
9082 }
9083
9084 n = app_data.bitrate;
9085 if (app_data.bitrate_max > 0 && n >= app_data.bitrate_max) {
9086 for (l = cd_maxbrhead; l != NULL; l = l->next) {
9087 if (l->w != (Widget) NULL) {
9088 m = atoi(XtName(l->w));
9089 if (m > n) {
9090 menuw = l->w;
9091 n = m;
9092 }
9093 }
9094 }
9095 if (menuw != NULL) {
9096 n = atoi(XtName(menuw));
9097 if (n != app_data.bitrate_max) {
9098 XtVaSetValues(
9099 widgets.options.maxbrate_opt,
9100 XmNmenuHistory, menuw,
9101 NULL
9102 );
9103 app_data.bitrate_max = n;
9104 DBGPRN(DBG_UI)(errfp,
9105 " Maximum bitrate reset to %d\n",
9106 n);
9107 }
9108 }
9109 }
9110 }
9111
9112 /* Make the Reset and Save buttons sensitive */
9113 XtSetSensitive(widgets.options.reset_btn, True);
9114 XtSetSensitive(widgets.options.save_btn, True);
9115 }
9116
9117
9118 /*
9119 * cd_min_bitrate
9120 * Minimum bitrate menu callback function
9121 */
9122 /*ARGSUSED*/
9123 void
cd_min_bitrate(Widget w,XtPointer client_data,XtPointer call_data)9124 cd_min_bitrate(Widget w, XtPointer client_data, XtPointer call_data)
9125 {
9126 XmPushButtonCallbackStruct
9127 *p = (XmPushButtonCallbackStruct *)(void *) call_data;
9128 wlist_t *l;
9129 Widget menuw = (Widget) NULL;
9130 int n;
9131 bool_t err = FALSE;
9132
9133 if (p->reason != XmCR_ACTIVATE)
9134 return;
9135
9136 if (w == widgets.options.minbrate_def_btn)
9137 n = 0;
9138 else if ((n = atoi(XtName(w))) == 0)
9139 return;
9140
9141 if (app_data.comp_mode == COMPMODE_3 &&
9142 app_data.bitrate > 0 && n > app_data.bitrate) {
9143 /* Error: minimum bitrate must be less than
9144 * or equal to the average bitrate.
9145 */
9146 CD_INFO(app_data.str_invbr_minmean);
9147 err = TRUE;
9148 }
9149
9150 if (n > 0 && app_data.bitrate_max > 0 && n > app_data.bitrate_max) {
9151 /* Error: minimum bitrate must be less than or
9152 * equal to the max bitrate.
9153 */
9154 CD_INFO(app_data.str_invbr_minmax);
9155 err = TRUE;
9156 }
9157
9158 if (err) {
9159 /* Restore setting */
9160 if (app_data.bitrate_min == 0) {
9161 menuw = widgets.options.minbrate_def_btn;
9162 }
9163 else for (l = cd_minbrhead; l != NULL; l = l->next) {
9164 if (l->w != (Widget) NULL &&
9165 atoi(XtName(l->w)) == app_data.bitrate_min) {
9166 menuw = l->w;
9167 break;
9168 }
9169 }
9170 if (menuw != NULL) {
9171 XtVaSetValues(widgets.options.minbrate_opt,
9172 XmNmenuHistory, menuw,
9173 NULL
9174 );
9175 }
9176 return;
9177 }
9178
9179 DBGPRN(DBG_UI)(errfp, "\n* Minimum bitrate: %d kb/s\n", n);
9180 app_data.bitrate_min = n;
9181
9182 /* Make the Reset and Save buttons sensitive */
9183 XtSetSensitive(widgets.options.reset_btn, True);
9184 XtSetSensitive(widgets.options.save_btn, True);
9185 }
9186
9187
9188 /*
9189 * cd_max_bitrate
9190 * Maximum bitrate menu callback function
9191 */
9192 /*ARGSUSED*/
9193 void
cd_max_bitrate(Widget w,XtPointer client_data,XtPointer call_data)9194 cd_max_bitrate(Widget w, XtPointer client_data, XtPointer call_data)
9195 {
9196 XmPushButtonCallbackStruct
9197 *p = (XmPushButtonCallbackStruct *)(void *) call_data;
9198 wlist_t *l;
9199 Widget menuw = (Widget) NULL;
9200 int n;
9201 bool_t err = FALSE;
9202
9203 if (p->reason != XmCR_ACTIVATE)
9204 return;
9205
9206 if (w == widgets.options.minbrate_def_btn)
9207 n = 0;
9208 else if ((n = atoi(XtName(w))) == 0)
9209 return;
9210
9211 if (app_data.comp_mode == COMPMODE_3 &&
9212 app_data.bitrate > 0 && n < app_data.bitrate) {
9213 /* Error: maximum bitrate must be greater than
9214 * or equal to the average bitrate.
9215 */
9216 CD_INFO(app_data.str_invbr_maxmean);
9217 err = TRUE;
9218 }
9219
9220 if (n > 0 && app_data.bitrate_min > 0 && n < app_data.bitrate_min) {
9221 /* Error: maximum bitrate must be greater than
9222 * or equal to the min bitrate.
9223 */
9224 CD_INFO(app_data.str_invbr_maxmin);
9225 err = TRUE;
9226 }
9227
9228 if (err) {
9229 /* Restore setting */
9230 if (app_data.bitrate_max == 0) {
9231 menuw = widgets.options.maxbrate_def_btn;
9232 }
9233 else for (l = cd_maxbrhead; l != NULL; l = l->next) {
9234 if (l->w != (Widget) NULL &&
9235 atoi(XtName(l->w)) == app_data.bitrate_max) {
9236 menuw = l->w;
9237 break;
9238 }
9239 }
9240 if (menuw != NULL) {
9241 XtVaSetValues(widgets.options.maxbrate_opt,
9242 XmNmenuHistory, menuw,
9243 NULL
9244 );
9245 }
9246 return;
9247 }
9248
9249 DBGPRN(DBG_UI)(errfp, "\n* Maximum bitrate: %d kb/s\n", n);
9250 app_data.bitrate_max = n;
9251
9252 /* Make the Reset and Save buttons sensitive */
9253 XtSetSensitive(widgets.options.reset_btn, True);
9254 XtSetSensitive(widgets.options.save_btn, True);
9255 }
9256
9257
9258 /*
9259 * cd_qualfactor
9260 * VBR Quality factor slider callback function
9261 */
9262 /*ARGSUSED*/
9263 void
cd_qualfactor(Widget w,XtPointer client_data,XtPointer call_data)9264 cd_qualfactor(Widget w, XtPointer client_data, XtPointer call_data)
9265 {
9266 XmScaleCallbackStruct
9267 *p = (XmScaleCallbackStruct *)(void *) call_data;
9268 XmString xs,
9269 xs2;
9270 char str[16];
9271
9272 if (p->value < 1 || p->value > 10)
9273 return;
9274
9275 if (p->value == app_data.qual_factor)
9276 return; /* No change */
9277
9278 DBGPRN(DBG_UI)(errfp, "\n* Quality factor: %d\n", p->value);
9279 app_data.qual_factor = p->value;
9280
9281 (void) sprintf(str, ": %d", app_data.qual_factor);
9282 xs = create_xmstring(str, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
9283 xs2 = XmStringConcat(xs_qual, xs);
9284 XtVaSetValues(widgets.options.qualval_lbl,
9285 XmNlabelString, xs2,
9286 NULL
9287 );
9288 XmStringFree(xs2);
9289 XmStringFree(xs);
9290
9291 /* Make the Reset and Save buttons sensitive */
9292 XtSetSensitive(widgets.options.reset_btn, True);
9293 XtSetSensitive(widgets.options.save_btn, True);
9294 }
9295
9296
9297 /*
9298 * cd_chanmode
9299 * Channel mode menu callback function
9300 */
9301 /*ARGSUSED*/
9302 void
cd_chanmode(Widget w,XtPointer client_data,XtPointer call_data)9303 cd_chanmode(Widget w, XtPointer client_data, XtPointer call_data)
9304 {
9305 XmPushButtonCallbackStruct
9306 *p = (XmPushButtonCallbackStruct *)(void *) call_data;
9307 char *modestr;
9308 int newmode;
9309
9310 if (p->reason != XmCR_ACTIVATE)
9311 return;
9312
9313 if (w == widgets.options.chmode_stereo_btn) {
9314 newmode = CH_STEREO;
9315 modestr = "stereo";
9316 }
9317 else if (w == widgets.options.chmode_jstereo_btn) {
9318 newmode = CH_JSTEREO;
9319 modestr = "joint-stereo";
9320 }
9321 else if (w == widgets.options.chmode_forcems_btn) {
9322 newmode = CH_FORCEMS;
9323 modestr = "force-ms";
9324 }
9325 else if (w == widgets.options.chmode_mono_btn) {
9326 newmode = CH_MONO;
9327 modestr = "mono";
9328 }
9329 else
9330 return;
9331
9332 if (app_data.chan_mode == newmode)
9333 return; /* No change */
9334
9335 DBGPRN(DBG_UI)(errfp, "\n* Channel mode: %s\n", modestr);
9336 app_data.chan_mode = newmode;
9337
9338 /* Make the Reset and Save buttons sensitive */
9339 XtSetSensitive(widgets.options.reset_btn, True);
9340 XtSetSensitive(widgets.options.save_btn, True);
9341 }
9342
9343
9344 /*
9345 * cd_compalgo
9346 * Compression algorithm slider callback function
9347 */
9348 /*ARGSUSED*/
9349 void
cd_compalgo(Widget w,XtPointer client_data,XtPointer call_data)9350 cd_compalgo(Widget w, XtPointer client_data, XtPointer call_data)
9351 {
9352 XmScaleCallbackStruct
9353 *p = (XmScaleCallbackStruct *)(void *) call_data;
9354 XmString xs,
9355 xs2;
9356 char str[16];
9357
9358 if (p->value < 1 || p->value > 10)
9359 return;
9360
9361 if (p->value == app_data.comp_algo)
9362 return; /* No change */
9363
9364 DBGPRN(DBG_UI)(errfp, "\n* Compression algorithm: %d\n", p->value);
9365 app_data.comp_algo = p->value;
9366
9367 (void) sprintf(str, ": %d", app_data.comp_algo);
9368 xs = create_xmstring(str, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
9369 xs2 = XmStringConcat(xs_algo, xs);
9370 XtVaSetValues(widgets.options.compalgo_lbl,
9371 XmNlabelString, xs2,
9372 NULL
9373 );
9374 XmStringFree(xs2);
9375 XmStringFree(xs);
9376
9377 /* Make the Reset and Save buttons sensitive */
9378 XtSetSensitive(widgets.options.reset_btn, True);
9379 XtSetSensitive(widgets.options.save_btn, True);
9380 }
9381
9382
9383 /*
9384 * cd_lowpass_mode
9385 * Lowpass filter mode menu callback function
9386 */
9387 /*ARGSUSED*/
9388 void
cd_lowpass_mode(Widget w,XtPointer client_data,XtPointer call_data)9389 cd_lowpass_mode(Widget w, XtPointer client_data, XtPointer call_data)
9390 {
9391 XmPushButtonCallbackStruct
9392 *p = (XmPushButtonCallbackStruct *)(void *) call_data;
9393 char *modestr;
9394 int newmode;
9395
9396 if (p->reason != XmCR_ACTIVATE)
9397 return;
9398
9399 if (w == widgets.options.lp_off_btn) {
9400 newmode = FILTER_OFF;
9401 modestr = "Off";
9402 }
9403 else if (w == widgets.options.lp_auto_btn) {
9404 newmode = FILTER_AUTO;
9405 modestr = "Auto";
9406 }
9407 else if (w == widgets.options.lp_manual_btn) {
9408 newmode = FILTER_MANUAL;
9409 modestr = "Manual";
9410 }
9411 else
9412 return;
9413
9414 if (app_data.lowpass_mode == newmode)
9415 return; /* No change */
9416
9417 DBGPRN(DBG_UI)(errfp, "\n* Lowpass filter: %s\n", modestr);
9418 app_data.lowpass_mode = newmode;
9419
9420 XtSetSensitive(
9421 widgets.options.lp_freq_txt,
9422 (Boolean) (newmode == FILTER_MANUAL)
9423 );
9424 XtSetSensitive(
9425 widgets.options.lp_width_txt,
9426 (Boolean) (newmode == FILTER_MANUAL)
9427 );
9428
9429 /* Make the Reset and Save buttons sensitive */
9430 XtSetSensitive(widgets.options.reset_btn, True);
9431 XtSetSensitive(widgets.options.save_btn, True);
9432 }
9433
9434
9435 /*
9436 * cd_highpass_mode
9437 * Highpass filter mode menu callback function
9438 */
9439 /*ARGSUSED*/
9440 void
cd_highpass_mode(Widget w,XtPointer client_data,XtPointer call_data)9441 cd_highpass_mode(Widget w, XtPointer client_data, XtPointer call_data)
9442 {
9443 XmPushButtonCallbackStruct
9444 *p = (XmPushButtonCallbackStruct *)(void *) call_data;
9445 char *modestr;
9446 int newmode;
9447
9448 if (p->reason != XmCR_ACTIVATE)
9449 return;
9450
9451 if (w == widgets.options.hp_off_btn) {
9452 newmode = FILTER_OFF;
9453 modestr = "Off";
9454 }
9455 else if (w == widgets.options.hp_auto_btn) {
9456 newmode = FILTER_AUTO;
9457 modestr = "Auto";
9458 }
9459 else if (w == widgets.options.hp_manual_btn) {
9460 newmode = FILTER_MANUAL;
9461 modestr = "Manual";
9462 }
9463 else
9464 return;
9465
9466 if (app_data.highpass_mode == newmode)
9467 return; /* No change */
9468
9469 DBGPRN(DBG_UI)(errfp, "\n* Highpass filter: %s\n", modestr);
9470 app_data.highpass_mode = newmode;
9471
9472 XtSetSensitive(
9473 widgets.options.hp_freq_txt,
9474 (Boolean) (newmode == FILTER_MANUAL)
9475 );
9476 XtSetSensitive(
9477 widgets.options.hp_width_txt,
9478 (Boolean) (newmode == FILTER_MANUAL)
9479 );
9480
9481 /* Make the Reset and Save buttons sensitive */
9482 XtSetSensitive(widgets.options.reset_btn, True);
9483 XtSetSensitive(widgets.options.save_btn, True);
9484 }
9485
9486
9487 /*
9488 * cd_filter_freq
9489 * Lowpass and highpass filter frequency text field callback
9490 */
9491 /*ARGSUSED*/
9492 void
cd_filter_freq(Widget w,XtPointer client_data,XtPointer call_data)9493 cd_filter_freq(Widget w, XtPointer client_data, XtPointer call_data)
9494 {
9495 XmAnyCallbackStruct *p = (XmAnyCallbackStruct *)(void *) call_data;
9496 char *cp,
9497 str[16];
9498 int newfreq;
9499
9500 if (p->reason == XmCR_VALUE_CHANGED) {
9501 /* Make the Reset and Save buttons sensitive */
9502 XtSetSensitive(widgets.options.reset_btn, True);
9503 XtSetSensitive(widgets.options.save_btn, True);
9504 return;
9505 }
9506 else if (p->reason != XmCR_ACTIVATE && p->reason != XmCR_LOSING_FOCUS)
9507 return;
9508
9509 if ((cp = get_text_string(w, FALSE)) != NULL) {
9510 newfreq = atoi(cp);
9511 MEM_FREE(cp);
9512 }
9513 else
9514 newfreq = 0;
9515
9516 /* Set cursor to beginning of text */
9517 XmTextSetInsertionPosition(w, 0);
9518
9519 if (w == widgets.options.lp_freq_txt) {
9520 if (app_data.lowpass_freq == newfreq)
9521 return; /* No change */
9522
9523 if (newfreq < MIN_LOWPASS_FREQ ||
9524 newfreq > MAX_LOWPASS_FREQ) {
9525 (void) sprintf(str, "%d", app_data.lowpass_freq);
9526 set_text_string(w, str, FALSE);
9527
9528 /* Set cursor to beginning of text */
9529 XmTextSetInsertionPosition(w, 0);
9530
9531 cp = (char *) MEM_ALLOC(
9532 "str_invfreq_lp",
9533 strlen(app_data.str_invfreq_lp) + 16
9534 );
9535 if (cp == NULL) {
9536 CD_FATAL(app_data.str_nomemory);
9537 return;
9538 }
9539 (void) sprintf(cp, app_data.str_invfreq_lp,
9540 MIN_LOWPASS_FREQ, MAX_LOWPASS_FREQ);
9541 CD_INFO(cp);
9542 MEM_FREE(cp);
9543 return;
9544 }
9545
9546 app_data.lowpass_freq = newfreq;
9547 cp = "Lowpass";
9548 }
9549 else if (w == widgets.options.hp_freq_txt) {
9550 if (app_data.highpass_freq == newfreq)
9551 return; /* No change */
9552
9553 if (newfreq < MIN_HIGHPASS_FREQ ||
9554 newfreq > MAX_HIGHPASS_FREQ) {
9555 (void) sprintf(str, "%d", app_data.highpass_freq);
9556 set_text_string(w, str, FALSE);
9557
9558 /* Set cursor to beginning of text */
9559 XmTextSetInsertionPosition(w, 0);
9560
9561 cp = (char *) MEM_ALLOC(
9562 "str_invfreq_hp",
9563 strlen(app_data.str_invfreq_hp) + 16
9564 );
9565 if (cp == NULL) {
9566 CD_FATAL(app_data.str_nomemory);
9567 return;
9568 }
9569 (void) sprintf(cp, app_data.str_invfreq_hp,
9570 MIN_HIGHPASS_FREQ, MAX_HIGHPASS_FREQ);
9571 CD_INFO(cp);
9572 MEM_FREE(cp);
9573 return;
9574 }
9575
9576 app_data.highpass_freq = newfreq;
9577 cp = "Highpass";
9578 }
9579
9580 DBGPRN(DBG_UI)(errfp, "\n* %s filter frequency: %d\n", cp, newfreq);
9581 }
9582
9583
9584 /*
9585 * cd_filter_width
9586 * Lowpass and highpass filter width text field callback
9587 */
9588 /*ARGSUSED*/
9589 void
cd_filter_width(Widget w,XtPointer client_data,XtPointer call_data)9590 cd_filter_width(Widget w, XtPointer client_data, XtPointer call_data)
9591 {
9592 XmAnyCallbackStruct *p = (XmAnyCallbackStruct *)(void *) call_data;
9593 char *cp;
9594 int newwidth;
9595
9596 if (p->reason == XmCR_VALUE_CHANGED) {
9597 /* Make the Reset and Save buttons sensitive */
9598 XtSetSensitive(widgets.options.reset_btn, True);
9599 XtSetSensitive(widgets.options.save_btn, True);
9600 return;
9601 }
9602 else if (p->reason != XmCR_ACTIVATE && p->reason != XmCR_LOSING_FOCUS)
9603 return;
9604
9605 if ((cp = get_text_string(w, FALSE)) != NULL) {
9606 newwidth = atoi(cp);
9607 MEM_FREE(cp);
9608 }
9609 else
9610 newwidth = 0;
9611
9612 /* Set cursor to beginning of text */
9613 XmTextSetInsertionPosition(w, 0);
9614
9615 if (newwidth < 0) {
9616 set_text_string(w, "0", FALSE);
9617
9618 /* Set cursor to beginning of text */
9619 XmTextSetInsertionPosition(w, 0);
9620
9621 XmProcessTraversal(w, XmTRAVERSE_CURRENT);
9622 return;
9623 }
9624
9625 if (w == widgets.options.lp_width_txt) {
9626 if (app_data.lowpass_width == newwidth)
9627 return; /* No change */
9628
9629 app_data.lowpass_width = newwidth;
9630 cp = "Lowpass";
9631 }
9632 else if (w == widgets.options.hp_width_txt) {
9633 if (app_data.highpass_width == newwidth)
9634 return; /* No change */
9635
9636 app_data.highpass_width = newwidth;
9637 cp = "Highpass";
9638 }
9639
9640 DBGPRN(DBG_UI)(errfp, "\n* %s filter width: %d\n", cp, newwidth);
9641 }
9642
9643
9644 /*
9645 * cd_addflag
9646 * Options window Copyright/Original/No res/Checksum flag
9647 * toggle buttons callback
9648 */
9649 /*ARGSUSED*/
9650 void
cd_addflag(Widget w,XtPointer client_data,XtPointer call_data)9651 cd_addflag(Widget w, XtPointer client_data, XtPointer call_data)
9652 {
9653 XmToggleButtonCallbackStruct *p =
9654 (XmToggleButtonCallbackStruct *)(void *) call_data;
9655 char *item;
9656
9657 if (p->reason != XmCR_VALUE_CHANGED)
9658 return;
9659
9660 if (w == widgets.options.copyrt_btn) {
9661 app_data.copyright = (bool_t) p->set;
9662 item = "Copyright";
9663 }
9664 else if (w == widgets.options.orig_btn) {
9665 app_data.original = (bool_t) p->set;
9666 item = "Original";
9667 }
9668 else if (w == widgets.options.nores_btn) {
9669 app_data.nores = (bool_t) p->set;
9670 item = "No bit reservoir";
9671 }
9672 else if (w == widgets.options.chksum_btn) {
9673 app_data.checksum = (bool_t) p->set;
9674 item = "Checksum";
9675 }
9676 else if (w == widgets.options.iso_btn) {
9677 app_data.strict_iso = (bool_t) p->set;
9678 item = "Strict-ISO";
9679 }
9680 else {
9681 /* Invalid button */
9682 return;
9683 }
9684
9685 DBGPRN(DBG_UI)(errfp, "\n* %s flag: %s\n",
9686 item, p->set ? "Enable" : "Disable");
9687
9688 /* Make the Reset and Save buttons sensitive */
9689 XtSetSensitive(widgets.options.reset_btn, True);
9690 XtSetSensitive(widgets.options.save_btn, True);
9691 }
9692
9693
9694 /*
9695 * cd_addtag
9696 * Options window Add ID3/comment tag toggle buttons callback
9697 */
9698 /*ARGSUSED*/
9699 void
cd_addtag(Widget w,XtPointer client_data,XtPointer call_data)9700 cd_addtag(Widget w, XtPointer client_data, XtPointer call_data)
9701 {
9702 XmToggleButtonCallbackStruct *p =
9703 (XmToggleButtonCallbackStruct *)(void *) call_data;
9704
9705 if (p->reason != XmCR_VALUE_CHANGED)
9706 return;
9707
9708 if (app_data.add_tag == (bool_t) p->set)
9709 return; /* No change */
9710
9711 DBGPRN(DBG_UI)(errfp, "\n* Add ID3/comment tag: %s\n",
9712 p->set ? "Enable" : "Disable");
9713 app_data.add_tag = (bool_t) p->set;
9714
9715 /* Change the ID3 tag version option menu sensitivity */
9716 XtSetSensitive(widgets.options.id3tag_ver_opt, p->set);
9717
9718 /* Make the Reset and Save buttons sensitive */
9719 XtSetSensitive(widgets.options.reset_btn, True);
9720 XtSetSensitive(widgets.options.save_btn, True);
9721 }
9722
9723
9724 /*
9725 * cd_id3tag_mode
9726 * ID3 tag mode menu callback function
9727 */
9728 /*ARGSUSED*/
9729 void
cd_id3tag_mode(Widget w,XtPointer client_data,XtPointer call_data)9730 cd_id3tag_mode(Widget w, XtPointer client_data, XtPointer call_data)
9731 {
9732 XmPushButtonCallbackStruct
9733 *p = (XmPushButtonCallbackStruct *)(void *) call_data;
9734 char *verstr;
9735 int newver;
9736
9737 if (p->reason != XmCR_ACTIVATE)
9738 return;
9739
9740 if (w == widgets.options.id3tag_v1_btn) {
9741 newver = ID3TAG_V1;
9742 verstr = "version 1";
9743 }
9744 else if (w == widgets.options.id3tag_v2_btn) {
9745 newver = ID3TAG_V2;
9746 verstr = "version 2";
9747 }
9748 else if (w == widgets.options.id3tag_both_btn) {
9749 newver = ID3TAG_BOTH;
9750 verstr = "version 1 and 2";
9751 }
9752 else
9753 return;
9754
9755 if (app_data.id3tag_mode == newver)
9756 return; /* No change */
9757
9758 DBGPRN(DBG_UI)(errfp, "\n* ID3 tag: %s\n", verstr);
9759 app_data.id3tag_mode = newver;
9760
9761 /* Make the Reset and Save buttons sensitive */
9762 XtSetSensitive(widgets.options.reset_btn, True);
9763 XtSetSensitive(widgets.options.save_btn, True);
9764 }
9765
9766
9767 /*
9768 * cd_set_lameopts
9769 * LAME options text widget callback function
9770 */
9771 /*ARGSUSED*/
9772 void
cd_set_lameopts(Widget w,XtPointer client_data,XtPointer call_data)9773 cd_set_lameopts(Widget w, XtPointer client_data, XtPointer call_data)
9774 {
9775 XmAnyCallbackStruct *p = (XmAnyCallbackStruct *)(void *) call_data;
9776 char *cp;
9777
9778 if (p->reason == XmCR_VALUE_CHANGED) {
9779 /* Make the Reset and Save buttons sensitive */
9780 XtSetSensitive(widgets.options.reset_btn, True);
9781 XtSetSensitive(widgets.options.save_btn, True);
9782 return;
9783 }
9784 if (p->reason != XmCR_ACTIVATE && p->reason != XmCR_LOSING_FOCUS)
9785 return;
9786
9787 if ((cp = get_text_string(w, FALSE)) == NULL)
9788 return;
9789
9790 /* Set cursor to beginning of text */
9791 XmTextSetInsertionPosition(w, 0);
9792
9793 if (app_data.lame_opts != NULL &&
9794 strcmp(app_data.lame_opts, cp) == 0) {
9795 MEM_FREE(cp);
9796 return; /* No change */
9797 }
9798
9799 if (!util_newstr(&app_data.lame_opts, cp)) {
9800 MEM_FREE(cp);
9801 CD_FATAL(app_data.str_nomemory);
9802 return;
9803 }
9804
9805 DBGPRN(DBG_UI)(errfp, "\n* LAME options: %s\n", cp);
9806
9807 MEM_FREE(cp);
9808 }
9809
9810
9811 /*
9812 * cd_balance
9813 * Balance control slider callback function
9814 */
9815 /*ARGSUSED*/
9816 void
cd_balance(Widget w,XtPointer client_data,XtPointer call_data)9817 cd_balance(Widget w, XtPointer client_data, XtPointer call_data)
9818 {
9819 XmScaleCallbackStruct
9820 *p = (XmScaleCallbackStruct *)(void *) call_data;
9821 curstat_t *s = (curstat_t *)(void *) client_data;
9822
9823 if (p->value == 0) {
9824 /* Center setting */
9825 s->level_left = s->level_right = 100;
9826 }
9827 else if (p->value < 0) {
9828 /* Attenuate the right channel */
9829 s->level_left = 100;
9830 s->level_right = 100 + (p->value * 2);
9831 }
9832 else {
9833 /* Attenuate the left channel */
9834 s->level_left = 100 - (p->value * 2);
9835 s->level_right = 100;
9836 }
9837
9838 di_level(
9839 s,
9840 (byte_t) s->level,
9841 (bool_t) (p->reason != XmCR_VALUE_CHANGED)
9842 );
9843
9844 DBGPRN(DBG_UI)(errfp, "\n* BALANCE: %d/%d\n",
9845 (int) s->level_left, (int) s->level_right);
9846 }
9847
9848
9849 /*
9850 * cd_balance_center
9851 * Balance control center button callback function
9852 */
9853 /*ARGSUSED*/
9854 void
cd_balance_center(Widget w,XtPointer client_data,XtPointer call_data)9855 cd_balance_center(Widget w, XtPointer client_data, XtPointer call_data)
9856 {
9857 curstat_t *s = (curstat_t *)(void *) client_data;
9858
9859 /* Force the balance control to the center position */
9860 set_bal_slider(0);
9861
9862 s->level_left = 100;
9863 s->level_right = 100;
9864
9865 di_level(s, (byte_t) s->level, FALSE);
9866
9867 DBGPRN(DBG_UI)(errfp, "\n* BALANCE: center\n");
9868 }
9869
9870
9871 /*
9872 * cd_cdda_att
9873 * CDDA attentuator slider callback function
9874 */
9875 /*ARGSUSED*/
9876 void
cd_cdda_att(Widget w,XtPointer client_data,XtPointer call_data)9877 cd_cdda_att(Widget w, XtPointer client_data, XtPointer call_data)
9878 {
9879 XmScaleCallbackStruct
9880 *p = (XmScaleCallbackStruct *)(void *) call_data;
9881 curstat_t *s = (curstat_t *)(void *) client_data;
9882
9883 if (cdda_fader_id > 0) {
9884 cd_untimeout(cdda_fader_id);
9885 cdda_fader_mode = CDDA_FADER_NONE;
9886 }
9887
9888 if ((int) s->cdda_att == p->value)
9889 return; /* No change */
9890
9891 s->cdda_att = (byte_t) p->value;
9892 cdda_att(s);
9893
9894 DBGPRN(DBG_UI)(errfp, "\n* CDDA ATTENUATOR: %d\n", (int) s->cdda_att);
9895 }
9896
9897
9898 /*
9899 * cd_cdda_fade
9900 * CDDA fader buttons callback function
9901 */
9902 /*ARGSUSED*/
9903 void
cd_cdda_fade(Widget w,XtPointer client_data,XtPointer call_data)9904 cd_cdda_fade(Widget w, XtPointer client_data, XtPointer call_data)
9905 {
9906 curstat_t *s = (curstat_t *)(void *) client_data;
9907 bool_t fadeout = FALSE;
9908
9909 fadeout = (bool_t) (w != widgets.options.cdda_fadein_btn);
9910
9911 if (fadeout && (int) s->cdda_att == 0)
9912 return; /* No change */
9913 else if (!fadeout && (int) s->cdda_att == 100)
9914 return; /* No change */
9915
9916 if ((fadeout && cdda_fader_mode == CDDA_FADER_OUT) ||
9917 (!fadeout && cdda_fader_mode == CDDA_FADER_IN)) {
9918 /* Already fading: speed up fader */
9919 if (cdda_fader_intvl > 20)
9920 cdda_fader_intvl >>= 1;
9921 DBGPRN(DBG_UI)(errfp, "\n* CDDA FADE-%s (speedup)\n",
9922 fadeout ? "OUT" : "IN");
9923 }
9924 else {
9925 /* Start fading at default speed */
9926 cdda_fader_intvl = CDDA_FADER_INTVL;
9927 DBGPRN(DBG_UI)(errfp, "\n* CDDA FADE-%s\n",
9928 fadeout ? "IN" : "OUT");
9929 }
9930
9931 cdda_fader_mode = fadeout ? CDDA_FADER_OUT : CDDA_FADER_IN;
9932
9933 if (cdda_fader_id > 0)
9934 cd_untimeout(cdda_fader_id);
9935
9936 cdda_fader_id = cd_timeout(
9937 cdda_fader_intvl,
9938 fadeout ? cdda_fadeout : cdda_fadein,
9939 (byte_t *) s
9940 );
9941 }
9942
9943
9944 /*
9945 * cd_set_timeouts
9946 * Heartbeat/server/cache timeout text widgets callback
9947 */
9948 /*ARGSUSED*/
9949 void
cd_set_timeouts(Widget w,XtPointer client_data,XtPointer call_data)9950 cd_set_timeouts(Widget w, XtPointer client_data, XtPointer call_data)
9951 {
9952 XmAnyCallbackStruct *p = (XmAnyCallbackStruct *)(void *) call_data;
9953 int *valp,
9954 val,
9955 min_val;
9956 char *cp,
9957 *name,
9958 str[16];
9959
9960 if (p->reason == XmCR_VALUE_CHANGED) {
9961 /* Make the Reset and Save buttons sensitive */
9962 XtSetSensitive(widgets.options.reset_btn, True);
9963 XtSetSensitive(widgets.options.save_btn, True);
9964 return;
9965 }
9966 else if (p->reason != XmCR_ACTIVATE && p->reason != XmCR_LOSING_FOCUS)
9967 return;
9968
9969 if ((cp = get_text_string(w, FALSE)) == NULL)
9970 return;
9971
9972 /* Set cursor to beginning of text */
9973 XmTextSetInsertionPosition(w, 0);
9974
9975 if (w == widgets.options.hb_tout_txt) {
9976 valp = &app_data.hb_timeout;
9977 min_val = MIN_HB_TIMEOUT;
9978 name = "Heartbeat";
9979 }
9980 else if (w == widgets.options.serv_tout_txt) {
9981 valp = &app_data.srv_timeout;
9982 min_val = 0;
9983 name = "Service";
9984 }
9985 else if (w == widgets.options.cache_tout_txt) {
9986 valp = &app_data.cache_timeout;
9987 min_val = 0;
9988 name = "Cache";
9989 }
9990 else {
9991 MEM_FREE(cp);
9992 return;
9993 }
9994
9995 if ((val = atoi(cp)) < min_val) {
9996 cd_beep();
9997
9998 (void) sprintf(str, "%d", *valp);
9999 set_text_string(w, str, FALSE);
10000
10001 /* Set cursor to beginning of text */
10002 XmTextSetInsertionPosition(w, 0);
10003
10004 MEM_FREE(cp);
10005 return;
10006 }
10007 else if (val == *valp) {
10008 MEM_FREE(cp);
10009 return; /* No change */
10010 }
10011
10012 DBGPRN(DBG_UI)(errfp, "\n* %s timeout: %d\n", name, val);
10013 *valp = val;
10014
10015 MEM_FREE(cp);
10016 }
10017
10018
10019 /*
10020 * cd_set_servers
10021 * Proxy server/port text widgets callback
10022 */
10023 /*ARGSUSED*/
10024 void
cd_set_servers(Widget w,XtPointer client_data,XtPointer call_data)10025 cd_set_servers(Widget w, XtPointer client_data, XtPointer call_data)
10026 {
10027 XmAnyCallbackStruct *p = (XmAnyCallbackStruct *)(void *) call_data;
10028 char *cp,
10029 *cp2 = NULL,
10030 str[STR_BUF_SZ * 2];
10031 int port;
10032
10033 if (p->reason == XmCR_VALUE_CHANGED) {
10034 /* Make the Reset and Save buttons sensitive */
10035 XtSetSensitive(widgets.options.reset_btn, True);
10036 XtSetSensitive(widgets.options.save_btn, True);
10037 return;
10038 }
10039 if (p->reason != XmCR_ACTIVATE && p->reason != XmCR_LOSING_FOCUS)
10040 return;
10041
10042 if ((cp = get_text_string(w, FALSE)) == NULL)
10043 return;
10044
10045 if (app_data.proxy_server != NULL)
10046 cp2 = strrchr(app_data.proxy_server, ':');
10047
10048 if (cp2 != NULL)
10049 *cp2++ = '\0';
10050 else
10051 cp2 = "80";
10052
10053 /* Set cursor to beginning of text */
10054 XmTextSetInsertionPosition(w, 0);
10055
10056 if (w == widgets.options.proxy_srvr_txt) {
10057 if ((port = atoi(cp2)) < 0)
10058 port = 80;
10059
10060 (void) sprintf(str, "%.64s:%d", cp, port);
10061 }
10062 else if (w == widgets.options.proxy_port_txt) {
10063 if ((port = atoi(cp)) < 0) {
10064 cd_beep();
10065
10066 set_text_string(w, cp2, FALSE);
10067
10068 /* Set cursor to beginning of text */
10069 XmTextSetInsertionPosition(w, 0);
10070 return;
10071 }
10072
10073 (void) sprintf(str, "%.64s:%d",
10074 (app_data.proxy_server == NULL) ?
10075 "" : app_data.proxy_server,
10076 port
10077 );
10078 }
10079
10080 MEM_FREE(cp);
10081
10082 if (app_data.proxy_server != NULL &&
10083 strcmp(app_data.proxy_server, str) == 0)
10084 return; /* No change */
10085
10086 if (!util_newstr(&app_data.proxy_server, str)) {
10087 CD_FATAL(app_data.str_nomemory);
10088 return;
10089 }
10090
10091 DBGPRN(DBG_UI)(errfp, "\n* Proxy server: %s\n", str);
10092 }
10093
10094
10095 /*
10096 * cd_set_proxy
10097 * CDDB proxy server toggle buttons callback
10098 */
10099 /*ARGSUSED*/
10100 void
cd_set_proxy(Widget w,XtPointer client_data,XtPointer call_data)10101 cd_set_proxy(Widget w, XtPointer client_data, XtPointer call_data)
10102 {
10103 XmToggleButtonCallbackStruct *p =
10104 (XmToggleButtonCallbackStruct *)(void *) call_data;
10105
10106 if (p->reason != XmCR_VALUE_CHANGED)
10107 return;
10108
10109 if (w == widgets.options.use_proxy_btn) {
10110 if (app_data.use_proxy == (bool_t) p->set)
10111 return; /* No change */
10112
10113 DBGPRN(DBG_UI)(errfp, "\n* Use proxy: %s\n",
10114 p->set ? "Enable" : "Disable");
10115 app_data.use_proxy = (bool_t) p->set;
10116
10117 /* Set the sensitivity of related widgets */
10118 XtSetSensitive(widgets.options.proxy_srvr_lbl, p->set);
10119 XtSetSensitive(widgets.options.proxy_srvr_txt, p->set);
10120 XtSetSensitive(widgets.options.proxy_port_lbl, p->set);
10121 XtSetSensitive(widgets.options.proxy_port_txt, p->set);
10122 XtSetSensitive(widgets.options.proxy_auth_btn, p->set);
10123 }
10124 else if (w == widgets.options.proxy_auth_btn) {
10125 if (app_data.proxy_auth == (bool_t) p->set)
10126 return; /* No change */
10127
10128 DBGPRN(DBG_UI)(errfp, "\n* Proxy authorization: %s\n",
10129 p->set ? "Enable" : "Disable");
10130 app_data.proxy_auth = (bool_t) p->set;
10131 }
10132
10133 /* Make the Reset and Save buttons sensitive */
10134 XtSetSensitive(widgets.options.reset_btn, True);
10135 XtSetSensitive(widgets.options.save_btn, True);
10136 }
10137
10138
10139 /*
10140 * cd_perfmon
10141 * CDDA Performance Monitor button callback function
10142 */
10143 /*ARGSUSED*/
10144 void
cd_perfmon_popdown(Widget w,XtPointer client_data,XtPointer call_data)10145 cd_perfmon_popdown(Widget w, XtPointer client_data, XtPointer call_data)
10146 {
10147 if (XtIsManaged(widgets.perfmon.form)) {
10148 /* Pop down performance monitor window */
10149 XtUnmapWidget(XtParent(widgets.perfmon.form));
10150 XtUnmanageChild(widgets.perfmon.form);
10151 }
10152 }
10153
10154
10155 /*
10156 * cd_perfmon
10157 * CDDA Performance Monitor button callback function
10158 */
10159 /*ARGSUSED*/
10160 void
cd_perfmon(Widget w,XtPointer client_data,XtPointer call_data)10161 cd_perfmon(Widget w, XtPointer client_data, XtPointer call_data)
10162 {
10163 static bool_t first = TRUE;
10164
10165 if (XtIsManaged(widgets.perfmon.form)) {
10166 cd_perfmon_popdown(w, client_data, call_data);
10167 return;
10168 }
10169
10170 /* Pop up performance monitor window.
10171 * The dialog has mappedWhenManaged set to False,
10172 * so we have to map/unmap explicitly. The reason for this
10173 * is we want to avoid a screen glitch when we move the window
10174 * in cd_dialog_setpos(), so we map the window afterwards.
10175 */
10176 XtManageChild(widgets.perfmon.form);
10177 if (first) {
10178 first = FALSE;
10179 /* Set window position */
10180 cd_dialog_setpos(XtParent(widgets.perfmon.form));
10181 }
10182 XtMapWidget(XtParent(widgets.perfmon.form));
10183
10184 XmProcessTraversal(
10185 widgets.perfmon.cancel_btn,
10186 XmTRAVERSE_CURRENT
10187 );
10188 }
10189
10190
10191 /*
10192 * cd_txtline_vfy
10193 * Single-line text widget user-input verification callback.
10194 */
10195 /*ARGSUSED*/
10196 void
cd_txtline_vfy(Widget w,XtPointer client_data,XtPointer call_data)10197 cd_txtline_vfy(Widget w, XtPointer client_data, XtPointer call_data)
10198 {
10199 XmTextVerifyCallbackStruct
10200 *p = (XmTextVerifyCallbackStruct *)(void *) call_data;
10201 int i;
10202
10203 if (p->reason != XmCR_MODIFYING_TEXT_VALUE)
10204 return;
10205
10206 p->doit = True;
10207
10208 if (p->startPos != p->endPos)
10209 /* Deleting text, no verification needed */
10210 return;
10211
10212 switch (p->text->format) {
10213 case XmFMT_8_BIT:
10214 for (i = 0; i < p->text->length; i++) {
10215 /* This is a single-line text widget, so a
10216 * newline is not allowed.
10217 */
10218 if (p->text->ptr[i] == '\n' ||
10219 p->text->ptr[i] == '\r') {
10220 p->doit = False;
10221 break;
10222 }
10223 }
10224 break;
10225
10226 case XmFMT_16_BIT:
10227 default:
10228 /* Nothing to do here */
10229 break;
10230 }
10231 }
10232
10233
10234 /*
10235 * cd_txtnline_vfy
10236 * Single-line text widget numerical user-input verification callback.
10237 */
10238 /*ARGSUSED*/
10239 void
cd_txtnline_vfy(Widget w,XtPointer client_data,XtPointer call_data)10240 cd_txtnline_vfy(Widget w, XtPointer client_data, XtPointer call_data)
10241 {
10242 XmTextVerifyCallbackStruct
10243 *p = (XmTextVerifyCallbackStruct *)(void *) call_data;
10244 int i;
10245
10246 if (p->reason != XmCR_MODIFYING_TEXT_VALUE)
10247 return;
10248
10249 p->doit = True;
10250
10251 if (p->startPos != p->endPos)
10252 /* Deleting text, no verification needed */
10253 return;
10254
10255 switch (p->text->format) {
10256 case XmFMT_8_BIT:
10257 for (i = 0; i < p->text->length; i++) {
10258 /* Only digits are allowed */
10259 if (!isdigit((int) p->text->ptr[i]) &&
10260 p->text->ptr[i] != '+' &&
10261 p->text->ptr[i] != '-') {
10262 p->doit = False;
10263 break;
10264 }
10265 }
10266 break;
10267
10268 case XmFMT_16_BIT:
10269 default:
10270 /* Nothing to do here */
10271 break;
10272 }
10273 }
10274
10275
10276 /*
10277 * cd_about
10278 * Program information popup callback function
10279 */
10280 /*ARGSUSED*/
10281 void
cd_about(Widget w,XtPointer client_data,XtPointer call_data)10282 cd_about(Widget w, XtPointer client_data, XtPointer call_data)
10283 {
10284 int allocsz;
10285 char *txt,
10286 *ctrlver;
10287 XmString xs_desc,
10288 xs_info,
10289 xs;
10290 curstat_t *s = (curstat_t *)(void *) client_data;
10291
10292 if (XtIsManaged(widgets.dialog.about)) {
10293 /* Pop down the about dialog box */
10294 XtUnmanageChild(widgets.dialog.about);
10295
10296 if (w == widgets.help.about_btn)
10297 return;
10298 }
10299
10300 allocsz = STR_BUF_SZ * 32;
10301
10302 if ((txt = (char *) MEM_ALLOC("about_allocsz", allocsz)) == NULL) {
10303 CD_FATAL(app_data.str_nomemory);
10304 return;
10305 }
10306
10307 (void) sprintf(txt, "%s version %s.%s.%s\n%s\n\n",
10308 PROGNAME, VERSION_MAJ, VERSION_MIN, VERSION_TEENY,
10309 "Motif\256 CD Audio Player / Ripper");
10310
10311 xs_desc = create_xmstring(txt, NULL, CHSET2, FALSE);
10312
10313 (void) sprintf(txt,
10314 "%s\nURL: %s\nE-mail: %s\n\n%s\n\n%s\n"
10315 "Drive: %s %s %s%s%s\nDevice: %s\n",
10316 COPYRIGHT,
10317 XMCD_URL,
10318 EMAIL,
10319 GNU_BANNER,
10320 di_methodstr(),
10321 (s->vendor[0] == '\0') ? "??" : s->vendor,
10322 s->prod,
10323 (s->revnum[0] == '\0') ? "" : "(",
10324 s->revnum,
10325 (s->revnum[0] == '\0') ? "" : ")",
10326 s->curdev
10327 );
10328
10329 ctrlver = cdinfo_cddbctrl_ver();
10330 (void) sprintf(txt, "%s\nCDDB%s service%s%s\n%s", txt,
10331 (cdinfo_cddb_ver() == 2) ? "\262" : " \"classic\"",
10332 (ctrlver[0] == '\0') ? "" : ": ",
10333 (ctrlver[0] == '\0') ? "\n" : ctrlver,
10334 CDDB_BANNER);
10335
10336 xs_info = create_xmstring(txt, NULL, CHSET1, FALSE);
10337
10338 /* Set the dialog box title */
10339 xs = create_xmstring(
10340 app_data.str_about, NULL, XmSTRING_DEFAULT_CHARSET, FALSE
10341 );
10342 XtVaSetValues(widgets.dialog.about, XmNdialogTitle, xs, NULL);
10343 XmStringFree(xs);
10344
10345 /* Set the dialog box message */
10346 xs = XmStringConcat(xs_desc, xs_info);
10347 XtVaSetValues(widgets.dialog.about, XmNmessageString, xs, NULL);
10348 XmStringFree(xs_desc);
10349 XmStringFree(xs_info);
10350 XmStringFree(xs);
10351
10352 MEM_FREE(txt);
10353
10354 /* Set up dialog box position */
10355 cd_dialog_setpos(XtParent(widgets.dialog.about));
10356
10357 /* Pop up the about dialog box */
10358 XtManageChild(widgets.dialog.about);
10359 }
10360
10361
10362 /*
10363 * cd_help_popup
10364 * Program help window popup callback function
10365 */
10366 /*ARGSUSED*/
10367 void
cd_help_popup(Widget w,XtPointer client_data,XtPointer call_data)10368 cd_help_popup(Widget w, XtPointer client_data, XtPointer call_data)
10369 {
10370 /* Pop up help window */
10371 help_popup(w);
10372 }
10373
10374
10375 /*
10376 * cd_help_cancel
10377 * Program help window popdown callback function
10378 */
10379 /*ARGSUSED*/
10380 void
cd_help_cancel(Widget w,XtPointer client_data,XtPointer call_data)10381 cd_help_cancel(Widget w, XtPointer client_data, XtPointer call_data)
10382 {
10383 /* Pop down help window */
10384 if (help_isactive())
10385 help_popdown();
10386 }
10387
10388
10389 /*
10390 * cd_info_ok
10391 * Information message dialog box OK button callback function.
10392 */
10393 /*ARGSUSED*/
10394 void
cd_info_ok(Widget w,XtPointer client_data,XtPointer call_data)10395 cd_info_ok(Widget w, XtPointer client_data, XtPointer call_data)
10396 {
10397 /* Remove pending popdown timeout, if any */
10398 if (infodiag_id >= 0) {
10399 cd_untimeout(infodiag_id);
10400 infodiag_id = -1;
10401 }
10402
10403 /* Pop down the info window */
10404 cd_info_popdown(NULL);
10405 }
10406
10407
10408 /*
10409 * cd_info2_ok
10410 * Info2 message dialog box OK button callback function.
10411 */
10412 /*ARGSUSED*/
10413 void
cd_info2_ok(Widget w,XtPointer client_data,XtPointer call_data)10414 cd_info2_ok(Widget w, XtPointer client_data, XtPointer call_data)
10415 {
10416 /* Pop down the info2 window */
10417 cd_info2_popdown(NULL);
10418 }
10419
10420
10421 /*
10422 * cd_warning_ok
10423 * Warning message dialog box OK button callback function
10424 */
10425 /*ARGSUSED*/
10426 void
cd_warning_ok(Widget w,XtPointer client_data,XtPointer call_data)10427 cd_warning_ok(Widget w, XtPointer client_data, XtPointer call_data)
10428 {
10429 /* Pop down the warning dialog */
10430 if (XtIsManaged(widgets.dialog.warning))
10431 XtUnmanageChild(widgets.dialog.warning);
10432 }
10433
10434
10435 /*
10436 * cd_fatal_ok
10437 * Fatal error message dialog box OK button callback function.
10438 * This causes the application to terminate.
10439 */
10440 /*ARGSUSED*/
10441 void
cd_fatal_ok(Widget w,XtPointer client_data,XtPointer call_data)10442 cd_fatal_ok(Widget w, XtPointer client_data, XtPointer call_data)
10443 {
10444 /* Pop down the error dialog */
10445 if (XtIsManaged(widgets.dialog.fatal))
10446 XtUnmanageChild(widgets.dialog.fatal);
10447
10448 /* Quit */
10449 cd_quit((curstat_t *)(void *) client_data);
10450 }
10451
10452
10453 /*
10454 * cd_confirm_resp
10455 * Set the confirm dialog user response state
10456 */
10457 /*ARGSUSED*/
10458 void
cd_confirm_resp(Widget w,XtPointer client_data,XtPointer call_data)10459 cd_confirm_resp(Widget w, XtPointer client_data, XtPointer call_data)
10460 {
10461 confirm_resp = (bool_t) (client_data != NULL);
10462 confirm_pend = FALSE;
10463 }
10464
10465
10466 /*
10467 * cd_rmcallback
10468 * Remove callback function specified in cdinfo_t.
10469 */
10470 /*ARGSUSED*/
10471 void
cd_rmcallback(Widget w,XtPointer client_data,XtPointer call_data)10472 cd_rmcallback(Widget w, XtPointer client_data, XtPointer call_data)
10473 {
10474 cbinfo_t *cb = (cbinfo_t *)(void *) client_data;
10475
10476 if (cb == NULL)
10477 return;
10478
10479 if (cb->widget0 != (Widget) NULL) {
10480 XtRemoveCallback(
10481 cb->widget0,
10482 cb->type,
10483 (XtCallbackProc) cb->func,
10484 (XtPointer) cb->data
10485 );
10486
10487 XtRemoveCallback(
10488 cb->widget0,
10489 cb->type,
10490 (XtCallbackProc) cd_rmcallback,
10491 client_data
10492 );
10493
10494 cb->widget0 = (Widget) NULL;
10495 }
10496
10497 if (cb->widget1 != (Widget) NULL) {
10498 XtRemoveCallback(
10499 cb->widget1,
10500 cb->type,
10501 (XtCallbackProc) cb->func,
10502 (XtPointer) cb->data
10503 );
10504
10505 XtRemoveCallback(
10506 cb->widget1,
10507 cb->type,
10508 (XtCallbackProc) cd_rmcallback,
10509 client_data
10510 );
10511
10512 cb->widget1 = (Widget) NULL;
10513 }
10514
10515 /* Remove WM_DELETE_WINDOW handler */
10516 if (cb->widget2 != (Widget) NULL) {
10517 rm_delw_callback(
10518 cb->widget2,
10519 (XtCallbackProc) cb->func,
10520 (XtPointer) cb->data
10521 );
10522 cb->widget2 = (Widget) NULL;
10523 }
10524 }
10525
10526
10527 /*
10528 * cd_shell_focus_chg
10529 * Focus change callback. Used to implement keyboard grabs for
10530 * hotkey handling.
10531 */
10532 /*ARGSUSED*/
10533 void
cd_shell_focus_chg(Widget w,XtPointer client_data,XtPointer call_data)10534 cd_shell_focus_chg(Widget w, XtPointer client_data, XtPointer call_data)
10535 {
10536 XmAnyCallbackStruct *p = (XmAnyCallbackStruct *)(void *) call_data;
10537 Widget shell = (Widget) client_data;
10538 static Widget prev_shell = (Widget) NULL;
10539
10540 if (p->reason != XmCR_FOCUS || shell == (Widget) NULL)
10541 return;
10542
10543 if (prev_shell != NULL) {
10544 if (shell == prev_shell)
10545 return;
10546 else
10547 hotkey_ungrabkeys(prev_shell);
10548 }
10549
10550 if (shell != widgets.toplevel) {
10551 hotkey_grabkeys(shell);
10552 prev_shell = shell;
10553 }
10554 }
10555
10556
10557 /*
10558 * cd_exit
10559 * Shut down the application gracefully.
10560 */
10561 /*ARGSUSED*/
10562 void
cd_exit(Widget w,XtPointer client_data,XtPointer call_data)10563 cd_exit(Widget w, XtPointer client_data, XtPointer call_data)
10564 {
10565 curstat_t *s = (curstat_t *)(void *) client_data;
10566
10567 /* Shut down CDDB - if there are more processing needed in the
10568 * CDDB, they will call us back.
10569 */
10570 s->flags |= STAT_EXIT;
10571 if (!dbprog_chgsubmit(s))
10572 return;
10573
10574 cd_quit(s);
10575 }
10576
10577
10578 /*
10579 * cd_tooltip_cancel
10580 * Cancel the tooltip window
10581 */
10582 /*ARGSUSED*/
10583 void
cd_tooltip_cancel(Widget w,XtPointer client_data,XtPointer call_data)10584 cd_tooltip_cancel(Widget w, XtPointer client_data, XtPointer call_data)
10585 {
10586 cd_tooltip_popdown(widgets.tooltip.shell);
10587 }
10588
10589
10590 /*
10591 * cd_not_implemented
10592 * Pop up a "not yet implemented" message. The client data
10593 * may point to a feature name string which will be displayed
10594 * along with the message.
10595 */
10596 /*ARGSUSED*/
10597 void
cd_not_implemented(Widget w,XtPointer client_data,XtPointer call_data)10598 cd_not_implemented(Widget w, XtPointer client_data, XtPointer call_data)
10599 {
10600 char *feature = (char *) client_data;
10601 char msg[STR_BUF_SZ * 2];
10602
10603 (void) sprintf(msg,
10604 "%s%sNot yet implemented.",
10605 feature != NULL ? feature : "",
10606 feature != NULL ? ": " : ""
10607 );
10608
10609 CD_INFO_AUTO(msg);
10610 }
10611
10612
10613 /**************** ^^ Callback routines ^^ ****************/
10614
10615 /***************** vv Event Handlers vv ******************/
10616
10617
10618 /* Mapping table for main window controls and their label color change
10619 * function pointer, and associated label pixmaps.
10620 */
10621 struct wpix_tab {
10622 Widget *wptr;
10623 void (*set_func)(Widget w, Pixmap px, Pixel color);
10624 Pixmap *fpx;
10625 Pixmap *hpx;
10626 } wpix_tab[] = {
10627 { &widgets.main.mode_btn, set_btn_color,
10628 &pixmaps.main.mode_pixmap, &pixmaps.main.mode_hlpixmap },
10629 { &widgets.main.eject_btn, set_btn_color,
10630 &pixmaps.main.eject_pixmap, &pixmaps.main.eject_hlpixmap },
10631 { &widgets.main.quit_btn, set_btn_color,
10632 &pixmaps.main.quit_pixmap, &pixmaps.main.quit_hlpixmap },
10633 { &widgets.main.dbprog_btn, set_btn_color,
10634 &pixmaps.main.dbprog_pixmap, &pixmaps.main.dbprog_hlpixmap },
10635 { &widgets.main.wwwwarp_btn, set_btn_color,
10636 &pixmaps.main.world_pixmap, &pixmaps.main.world_hlpixmap },
10637 { &widgets.main.options_btn, set_btn_color,
10638 &pixmaps.main.options_pixmap, &pixmaps.main.options_hlpixmap },
10639 { &widgets.main.time_btn, set_btn_color,
10640 &pixmaps.main.time_pixmap, &pixmaps.main.time_hlpixmap },
10641 { &widgets.main.ab_btn, set_btn_color,
10642 &pixmaps.main.ab_pixmap, &pixmaps.main.ab_hlpixmap },
10643 { &widgets.main.sample_btn, set_btn_color,
10644 &pixmaps.main.sample_pixmap, &pixmaps.main.sample_hlpixmap },
10645 { &widgets.main.keypad_btn, set_btn_color,
10646 &pixmaps.main.keypad_pixmap, &pixmaps.main.keypad_hlpixmap },
10647 { &widgets.main.level_scale, set_scale_color,
10648 NULL, NULL },
10649 { &widgets.main.playpause_btn, set_btn_color,
10650 &pixmaps.main.playpause_pixmap, &pixmaps.main.playpause_hlpixmap },
10651 { &widgets.main.stop_btn, set_btn_color,
10652 &pixmaps.main.stop_pixmap, &pixmaps.main.stop_hlpixmap },
10653 { &widgets.main.prevdisc_btn, set_btn_color,
10654 &pixmaps.main.prevdisc_pixmap, &pixmaps.main.prevdisc_hlpixmap },
10655 { &widgets.main.nextdisc_btn, set_btn_color,
10656 &pixmaps.main.nextdisc_pixmap, &pixmaps.main.nextdisc_hlpixmap },
10657 { &widgets.main.prevtrk_btn, set_btn_color,
10658 &pixmaps.main.prevtrk_pixmap, &pixmaps.main.prevtrk_hlpixmap },
10659 { &widgets.main.nexttrk_btn, set_btn_color,
10660 &pixmaps.main.nexttrk_pixmap, &pixmaps.main.nexttrk_hlpixmap },
10661 { &widgets.main.previdx_btn, set_btn_color,
10662 &pixmaps.main.previdx_pixmap, &pixmaps.main.previdx_hlpixmap },
10663 { &widgets.main.nextidx_btn, set_btn_color,
10664 &pixmaps.main.nextidx_pixmap, &pixmaps.main.nextidx_hlpixmap },
10665 { &widgets.main.rew_btn, set_btn_color,
10666 &pixmaps.main.rew_pixmap, &pixmaps.main.rew_hlpixmap },
10667 { &widgets.main.ff_btn, set_btn_color,
10668 &pixmaps.main.ff_pixmap, &pixmaps.main.ff_hlpixmap },
10669 { NULL, NULL, 0, 0 }
10670 };
10671
10672
10673 /*
10674 * cd_focus_chg
10675 * Widget keyboard focus change event handler. Used to change
10676 * the main window controls' label color.
10677 */
10678 /*ARGSUSED*/
10679 void
cd_focus_chg(Widget w,XtPointer client_data,XEvent * ev)10680 cd_focus_chg(Widget w, XtPointer client_data, XEvent *ev)
10681 {
10682 int i;
10683 unsigned char focuspolicy;
10684 static bool_t first = TRUE;
10685 static int count = 0;
10686 static Pixel fg,
10687 hl;
10688
10689 if (!app_data.main_showfocus)
10690 return;
10691
10692 if (first) {
10693 first = FALSE;
10694
10695 XtVaGetValues(
10696 widgets.toplevel,
10697 XmNkeyboardFocusPolicy,
10698 &focuspolicy,
10699 NULL
10700 );
10701 if (focuspolicy != XmEXPLICIT) {
10702 app_data.main_showfocus = FALSE;
10703 return;
10704 }
10705
10706 XtVaGetValues(w, XmNforeground, &fg, NULL);
10707 XtVaGetValues(w, XmNhighlightColor, &hl, NULL);
10708 }
10709
10710 if (ev->xfocus.mode != NotifyNormal ||
10711 ev->xfocus.detail != NotifyAncestor)
10712 return;
10713
10714 if (ev->type == FocusOut) {
10715 if (count <= 0)
10716 return;
10717
10718 /* Restore original foreground pixmap */
10719 for (i = 0; wpix_tab[i].set_func != NULL; i++) {
10720 if (w == *(wpix_tab[i].wptr)) {
10721 wpix_tab[i].set_func(w,
10722 (wpix_tab[i].fpx == NULL) ?
10723 (Pixmap) 0 : *(wpix_tab[i].fpx),
10724 fg
10725 );
10726 break;
10727 }
10728 }
10729 count--;
10730 }
10731 else if (ev->type == FocusIn) {
10732 if (count >= 1)
10733 return;
10734
10735 /* Set new highlighted foreground pixmap */
10736 for (i = 0; wpix_tab[i].set_func != NULL; i++) {
10737 if (w == *(wpix_tab[i].wptr)) {
10738 wpix_tab[i].set_func(w,
10739 (wpix_tab[i].fpx == NULL) ?
10740 (Pixmap) 0 : *(wpix_tab[i].hpx),
10741 hl
10742 );
10743 break;
10744 }
10745 }
10746 count++;
10747 }
10748 }
10749
10750 /*
10751 * cd_xing_chg
10752 * Widget enter/leave crossing event handler. Used to manage
10753 * pop-up tool-tips.
10754 */
10755 /*ARGSUSED*/
10756 void
cd_xing_chg(Widget w,XtPointer client_data,XEvent * ev)10757 cd_xing_chg(Widget w, XtPointer client_data, XEvent *ev)
10758 {
10759 if (!app_data.tooltip_enable ||
10760 ev->xcrossing.mode != NotifyNormal ||
10761 ev->xcrossing.detail == NotifyInferior)
10762 return;
10763
10764 if (ev->type == EnterNotify) {
10765 if (skip_next_tooltip) {
10766 skip_next_tooltip = FALSE;
10767 return;
10768 }
10769
10770 if (tooltip1_id < 0) {
10771 tooltip1_id = cd_timeout(
10772 app_data.tooltip_delay,
10773 cd_tooltip_popup,
10774 (byte_t *) w
10775 );
10776 }
10777 }
10778 else if (ev->type == LeaveNotify) {
10779 cd_tooltip_popdown(widgets.tooltip.shell);
10780 }
10781 }
10782
10783
10784 /*
10785 * cd_dbmode_ind
10786 * Main window dbmode indicator button release event callback function
10787 */
10788 /*ARGSUSED*/
10789 void
cd_dbmode_ind(Widget w,XtPointer client_data,XEvent * ev,Boolean * cont)10790 cd_dbmode_ind(Widget w, XtPointer client_data, XEvent *ev, Boolean *cont)
10791 {
10792 curstat_t *s = (curstat_t *)(void *) client_data;
10793
10794 *cont = True;
10795
10796 if (ev->xany.type != ButtonRelease || ev->xbutton.button != Button1)
10797 return;
10798
10799 if (s->qmode == QMODE_WAIT) {
10800 (void) dbprog_stopload_active(1, TRUE);
10801
10802 (void) cd_confirm_popup(
10803 app_data.str_confirm,
10804 app_data.str_stopload,
10805 (XtCallbackProc) dbprog_stop_load_yes, client_data,
10806 (XtCallbackProc) dbprog_stop_load_no, client_data
10807 );
10808 }
10809 else if (XtIsSensitive(widgets.dbprog.reload_btn)) {
10810 (void) cd_confirm_popup(
10811 app_data.str_confirm,
10812 app_data.str_reload,
10813 (XtCallbackProc) dbprog_load, client_data,
10814 (XtCallbackProc) NULL, NULL
10815 );
10816 }
10817 }
10818
10819
10820 /***************** ^^ Event Handlers ^^ ******************/
10821
10822