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 *_dbprog_c_ident_ = "@(#)dbprog.c 7.215 04/04/20";
24 #endif
25
26 #include "common_d/appenv.h"
27 #include "common_d/util.h"
28 #include "xmcd_d/xmcd.h"
29 #include "xmcd_d/widget.h"
30 #include "libdi_d/libdi.h"
31 #include "cdinfo_d/cdinfo.h"
32 #include "cdinfo_d/motd.h"
33 #include "xmcd_d/callback.h"
34 #include "xmcd_d/cdfunc.h"
35 #include "xmcd_d/wwwwarp.h"
36 #include "xmcd_d/userreg.h"
37 #include "xmcd_d/dbprog.h"
38
39
40 extern widgets_t widgets;
41 extern appdata_t app_data;
42 extern FILE *errfp;
43
44 STATIC int time_mode, /* Time display mode flag */
45 sel_pos = -1, /* Track list select position */
46 ind_pos = -1, /* Track list highlight pos */
47 extt_pos = -1, /* Selected track position */
48 cred_pos = -1, /* Cred list select position */
49 seg_pos = -1, /* Seg list select position */
50 dlist_pos = -1, /* Disc list select position */
51 dlist_mode, /* Disc list mode */
52 fname_mode, /* Full name window mode */
53 reg_mode, /* Region selector mode */
54 credits_mode, /* Credits window mode */
55 hist_cnt = 0, /* Hist list count */
56 reg_cnt = 0, /* Region list count */
57 lang_cnt = 0, /* Language list count */
58 match_cnt = 0, /* Matchsel list count */
59 start_slot = -1, /* Changer scan start slot */
60 scan_slot = -1; /* Changer scan current slot */
61 STATIC long scan_id = -1; /* Changer scan timeout ID */
62 STATIC bool_t title_edited = FALSE, /* Track title edited flag */
63 extt_setup = FALSE, /* Track details setup */
64 auth_initted = FALSE, /* User entered auth info */
65 hist_initted = FALSE, /* History initialized */
66 auto_trk = FALSE, /* Auto-track */
67 sav_mplay = FALSE, /* Original multiplay mode */
68 sav_rev = FALSE, /* Original reverse mode */
69 wwwwarp_cleared = FALSE,/* wwwWarp menu cleared */
70 stopload_active = FALSE,/* Stop load dialog active */
71 fname_changed = FALSE; /* Full name text changed */
72 STATIC XmString xs_extd_lbl, /* Disc details lbl */
73 xs_extd_lblx, /* Disc details lbl (*) */
74 xs_dcred_lbl, /* Disc cred lbl */
75 xs_dcred_lblx, /* Disc cred lbl (*) */
76 xs_seg_lbl, /* Segments lbl */
77 xs_seg_lblx, /* Segments lbl (*) */
78 xs_extt_lbl, /* Track details lbl */
79 xs_extt_lblx, /* Track details lbl (*) */
80 xs_tcred_lbl, /* Track credits lbl */
81 xs_tcred_lblx, /* Track credits lbl (*) */
82 xs_scred_lbl, /* Segment credits lbl */
83 xs_scred_lblx; /* Segment credits lbl (*) */
84 STATIC char *sav_ttitle; /* Saved track title */
85 STATIC cdinfo_incore_t *dbp; /* Pointer to CD info struct */
86 STATIC cdinfo_credit_t w_cred; /* Work credit struct */
87 STATIC cdinfo_segment_t w_seg; /* Work segment struct */
88
89 /* A copy of the track list data - needed because Motif does not
90 * provide something like XmListGetItem(w, pos)
91 */
92 STATIC struct {
93 char *title;
94 bool_t highlighted;
95 } trklist_copy[MAXTRACK];
96
97 /* Function prototypes */
98 STATIC void dbprog_hist_new(curstat_t *),
99 dbprog_chgr_new(curstat_t *);
100
101
102 /***********************
103 * internal routines *
104 ***********************/
105
106
107 /*
108 * dbprog_fullname_ck
109 * Check the fullname structure fields for correctness. Pop up a
110 * informational message dialog box if it does not pass.
111 *
112 * Args:
113 * s - Pointer to the curstat_t structure.
114 *
115 * Return:
116 * TRUE - passed
117 * FALSE - failed
118 */
119 STATIC bool_t
dbprog_fullname_ck(curstat_t * s)120 dbprog_fullname_ck(curstat_t *s)
121 {
122 cdinfo_fname_t *fnp;
123 char *msg,
124 buf[STR_BUF_SZ],
125 buf2[STR_BUF_SZ];
126
127 switch (fname_mode) {
128 case FNAME_DISC:
129 fnp = &dbp->disc.artistfname;
130 (void) sprintf(buf, "%.31s", app_data.str_albumartist);
131 break;
132
133 case FNAME_TRACK:
134 if (extt_pos < 0)
135 return TRUE; /* Nothing to check */
136
137 fnp = &dbp->track[extt_pos].artistfname;
138 (void) sprintf(buf2, "%.31s", app_data.str_trackartist);
139 (void) sprintf(buf, buf2, s->trkinfo[extt_pos].trkno);
140 break;
141
142 case FNAME_CREDITS:
143 fnp = &w_cred.crinfo.fullname;
144 (void) sprintf(buf, "%.31s", app_data.str_credit);
145 break;
146
147 default:
148 return TRUE; /* Nothing to check */
149 }
150 (void) strcat(buf, " full name:\n");
151
152 /* Check fullname for correctness */
153 if (fnp->firstname == NULL && fnp->lastname != NULL) {
154 msg = (char *) MEM_ALLOC(
155 "fullname_ck_msg",
156 strlen(buf) + strlen(app_data.str_nofirst) + 1
157 );
158 if (msg == NULL) {
159 CD_FATAL(app_data.str_nomemory);
160 return FALSE;
161 }
162 (void) strcpy(msg, buf);
163 (void) strcat(msg, app_data.str_nofirst);
164 CD_INFO(msg);
165 MEM_FREE(msg);
166
167 /* Put the focus on the first name field */
168 XmProcessTraversal(
169 widgets.fullname.firstname_txt,
170 XmTRAVERSE_CURRENT
171 );
172 return FALSE;
173 }
174 if (fnp->the != NULL && fnp->firstname == NULL &&
175 fnp->lastname == NULL) {
176 msg = (char *) MEM_ALLOC(
177 "fullname_ck_msg",
178 strlen(buf) + strlen(app_data.str_nofirstlast) + 1
179 );
180 if (msg == NULL) {
181 CD_FATAL(app_data.str_nomemory);
182 return FALSE;
183 }
184 (void) strcpy(msg, buf);
185 (void) strcat(msg, app_data.str_nofirstlast);
186 CD_INFO(msg);
187 MEM_FREE(msg);
188
189 /* Put the focus on the first name field */
190 XmProcessTraversal(
191 widgets.fullname.firstname_txt,
192 XmTRAVERSE_CURRENT
193 );
194 return FALSE;
195 }
196
197 return TRUE;
198 }
199
200
201 /*
202 * dbprog_dpytottime
203 * Display the disc total time in the total time indicator
204 *
205 * Args:
206 * s - Pointer to the curstat_t structure.
207 *
208 * Return:
209 * Nothing.
210 */
211 STATIC void
dbprog_dpytottime(curstat_t * s)212 dbprog_dpytottime(curstat_t *s)
213 {
214 XmString xs;
215 char str[STR_BUF_SZ];
216 static char prev[STR_BUF_SZ];
217
218 if (s->mode == MOD_BUSY || s->mode == MOD_NODISC)
219 (void) strcpy(str, "Total: --:--");
220 else
221 (void) sprintf(str, "Total: %02d:%02d",
222 s->discpos_tot.min, s->discpos_tot.sec);
223
224 if (strcmp(str, prev) == 0)
225 return;
226
227 xs = create_xmstring(str, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
228
229 XtVaSetValues(
230 widgets.dbprog.tottime_ind,
231 XmNlabelString,
232 xs,
233 NULL
234 );
235
236 XmStringFree(xs);
237
238 (void) strcpy(prev, str);
239 }
240
241
242 /*
243 * dbprog_list_autoscroll
244 * Scroll a list if necessary to make the specified item visible.
245 *
246 * Args:
247 * list - The list widget to operate on
248 * nitems - The total number of items in the list
249 * pos - The list position to make visible
250 *
251 * Return:
252 * Nothing.
253 */
254 STATIC void
dbprog_list_autoscroll(Widget list,int nitems,int pos)255 dbprog_list_autoscroll(Widget list, int nitems, int pos)
256 {
257 int n,
258 top_pos,
259 bottom_pos,
260 vis_cnt;
261
262 if (nitems == 0)
263 return; /* Nothing to do */
264
265 /* Determine range of items visible in current list */
266 XtVaGetValues(
267 list,
268 XmNtopItemPosition, &top_pos,
269 XmNvisibleItemCount, &vis_cnt,
270 NULL
271 );
272
273 bottom_pos = top_pos + vis_cnt - 1;
274
275 /* Try to keep the desired items near the middle of the visible
276 * portion of the list.
277 */
278 if (pos < top_pos) {
279 /* Scroll up */
280 n = pos - (vis_cnt / 2);
281 if (n < 1)
282 n = 1;
283 XmListSetPos(list, n);
284 }
285 else if (pos > bottom_pos) {
286 /* Scroll down */
287 n = pos + (vis_cnt / 2);
288 if (n > nitems)
289 n = nitems;
290 XmListSetBottomPos(list, n);
291 }
292 }
293
294
295 /*
296 * dbprog_listupd_ent
297 * Update a track entry in the track list.
298 *
299 * Args:
300 * s - Pointer to the curstat_t structure.
301 * pos - Track position to update.
302 * title - Track title string.
303 * newent - Whether this is a new entry or a replacement entry.
304 *
305 * Return:
306 * Nothing.
307 */
308 STATIC void
dbprog_listupd_ent(curstat_t * s,int pos,char * title,bool_t newent)309 dbprog_listupd_ent(curstat_t *s, int pos, char *title, bool_t newent)
310 {
311 int n,
312 secs,
313 min,
314 sec;
315 char *str;
316 XmString xs;
317 bool_t highlighted = FALSE;
318
319 if (s->trkinfo[pos].trkno < 0)
320 return;
321
322 if (time_mode == TIME_TOTAL) {
323 min = s->trkinfo[pos].min;
324 sec = s->trkinfo[pos].sec;
325 }
326 else {
327 secs = ((s->trkinfo[pos+1].min * 60 + s->trkinfo[pos+1].sec) -
328 (s->trkinfo[pos].min * 60 + s->trkinfo[pos].sec));
329
330 /* "Enhanced CD" / "CD Extra" hack */
331 if (s->trkinfo[pos].type == TYP_AUDIO &&
332 s->trkinfo[pos+1].addr > s->discpos_tot.addr) {
333 secs -= ((s->trkinfo[pos+1].addr - s->discpos_tot.addr) /
334 FRAME_PER_SEC);
335 }
336
337 min = (byte_t) (secs / 60);
338 sec = (byte_t) (secs % 60);
339 }
340
341 n = strlen(title) + strlen(TRKLIST_FMT) + 1;
342 if ((str = (char *) MEM_ALLOC("listupd_ent_str", n)) == NULL) {
343 CD_FATAL(app_data.str_nomemory);
344 return;
345 }
346
347 (void) sprintf(str, TRKLIST_FMT,
348 s->trkinfo[pos].trkno, min, sec, title,
349 (dbp->track[pos].notes != NULL ||
350 dbp->track[pos].credit_list != NULL) ?
351 ASTERISK_STR : "");
352
353 highlighted = (s->mode != MOD_BUSY &&
354 s->mode != MOD_NODISC &&
355 s->cur_trk >= 0 &&
356 di_curtrk_pos(s) == pos);
357
358 if (!newent && trklist_copy[pos].title != NULL &&
359 strcmp(trklist_copy[pos].title, str) == 0 &&
360 highlighted == trklist_copy[pos].highlighted) {
361 /* No change: don't update list to avoid unnecessary flicker */
362 MEM_FREE(str);
363 return;
364 }
365
366 /* Save private copy of tracklist data entry */
367 if (!util_newstr(&trklist_copy[pos].title, str)) {
368 MEM_FREE(str);
369 CD_FATAL(app_data.str_nomemory);
370 return;
371 }
372 trklist_copy[pos].highlighted = highlighted;
373
374 xs = create_xmstring(str, NULL, highlighted ? CHSET2 : CHSET1, TRUE);
375
376 if (newent)
377 XmListAddItemUnselected(widgets.dbprog.trk_list, xs, pos+1);
378 else
379 XmListReplaceItemsPos(widgets.dbprog.trk_list, &xs, 1, pos+1);
380
381 XmStringFree(xs);
382 MEM_FREE(str);
383 }
384
385
386 /*
387 * dbprog_listupd_all
388 * Update the track list display to reflect the contents of
389 * the trkinfo table in the curstat_t structure.
390 *
391 * Args:
392 * s - Pointer to the curstat_t structure.
393 * newent - Whether this is a new entry or a replacement entry.
394 *
395 * Return:
396 * Nothing.
397 */
398 STATIC void
dbprog_listupd_all(curstat_t * s,bool_t newent)399 dbprog_listupd_all(curstat_t *s, bool_t newent)
400 {
401 int i;
402
403 if (newent)
404 XmListDeleteAllItems(widgets.dbprog.trk_list);
405
406 for (i = 0; i < (int) s->tot_trks; i++) {
407 /* Update track list entry */
408 dbprog_listupd_ent(
409 s, i,
410 (dbp->track[i].title != NULL) ?
411 dbp->track[i].title : UNDEF_STR,
412 newent
413 );
414 }
415
416 /* If this item is previously selected, re-select it */
417 if (sel_pos > 0)
418 XmListSelectPos(widgets.dbprog.trk_list, sel_pos, False);
419 else if (ind_pos > 0)
420 XmListSelectPos(widgets.dbprog.trk_list, ind_pos, False);
421 }
422
423
424 /*
425 * dbprog_extd_lblupd
426 * Update the disc details button label to indicate whether there is
427 * text contained in the disc notes.
428 *
429 * Args:
430 * None.
431 *
432 * Return:
433 * Nothing.
434 */
435 STATIC void
dbprog_extd_lblupd(void)436 dbprog_extd_lblupd(void)
437 {
438 XmString xs;
439 bool_t newstate;
440 static bool_t state = FALSE;
441
442 newstate = (bool_t) (dbp->disc.notes != NULL);
443 if (newstate) {
444 if (state)
445 return; /* no change */
446 xs = xs_extd_lblx;
447 }
448 else {
449 if (!state)
450 return; /* no change */
451 xs = xs_extd_lbl;
452 }
453
454 XtVaSetValues(widgets.dbprog.extd_btn,
455 XmNlabelString, xs,
456 NULL
457 );
458
459 state = newstate;
460 }
461
462
463 /*
464 * dbprog_dcred_lblupd
465 * Update the disc credits button label to indicate whether there are
466 * disc credits defined.
467 *
468 * Args:
469 * None.
470 *
471 * Return:
472 * Nothing.
473 */
474 STATIC void
dbprog_dcred_lblupd(void)475 dbprog_dcred_lblupd(void)
476 {
477 XmString xs;
478 bool_t newstate;
479 static bool_t state = FALSE;
480
481 newstate = (bool_t) (dbp->disc.credit_list != NULL);
482 if (newstate) {
483 if (state)
484 return; /* no change */
485 xs = xs_dcred_lblx;
486 }
487 else {
488 if (!state)
489 return; /* no change */
490 xs = xs_dcred_lbl;
491 }
492
493 XtVaSetValues(widgets.dbprog.dcredits_btn,
494 XmNlabelString, xs,
495 NULL
496 );
497
498 state = newstate;
499 }
500
501
502 /*
503 * dbprog_seg_lblupd
504 * Update the segments button label to indicate whether there are
505 * segments defined.
506 *
507 * Args:
508 * None.
509 *
510 * Return:
511 * Nothing.
512 */
513 STATIC void
dbprog_seg_lblupd(void)514 dbprog_seg_lblupd(void)
515 {
516 XmString xs;
517 bool_t newstate;
518 static bool_t state = FALSE;
519
520 newstate = (bool_t) (dbp->disc.segment_list != NULL);
521 if (newstate) {
522 if (state)
523 return; /* no change */
524 xs = xs_seg_lblx;
525 }
526 else {
527 if (!state)
528 return; /* no change */
529 xs = xs_seg_lbl;
530 }
531
532 XtVaSetValues(widgets.dbprog.segments_btn,
533 XmNlabelString, xs,
534 NULL
535 );
536
537 state = newstate;
538 }
539
540
541 /*
542 * dbprog_extt_lblupd
543 * Update the track details button label to indicate whether there is
544 * text contained in the track notes or track credits section.
545 *
546 * Args:
547 * t - Pointer to the cdinfo_track_t structure pertaining to the
548 * currently selected track.
549 *
550 * Return:
551 * Nothing.
552 */
553 STATIC void
dbprog_extt_lblupd(cdinfo_track_t * t)554 dbprog_extt_lblupd(cdinfo_track_t *t)
555 {
556 XmString xs;
557 bool_t newstate;
558 static bool_t state = FALSE;
559
560 newstate = (bool_t) (t != NULL && t->notes != NULL);
561 if (newstate) {
562 if (state)
563 return; /* no change */
564 xs = xs_extt_lblx;
565 }
566 else {
567 if (!state)
568 return; /* no change */
569 xs = xs_extt_lbl;
570 }
571
572 XtVaSetValues(widgets.dbprog.extt_btn,
573 XmNlabelString, xs,
574 NULL
575 );
576
577 state = newstate;
578 }
579
580
581 /*
582 * dbprog_tcred_lblupd
583 * Update the track credits button label to indicate whether there are
584 * track credits defined for the designated track.
585 *
586 * Args:
587 * t - Pointer to the cdinfo_track_t structure pertaining to the
588 * currently selected track.
589 *
590 * Return:
591 * Nothing.
592 */
593 STATIC void
dbprog_tcred_lblupd(cdinfo_track_t * t)594 dbprog_tcred_lblupd(cdinfo_track_t *t)
595 {
596 XmString xs;
597 bool_t newstate;
598 static bool_t state = FALSE;
599
600 newstate = (bool_t) (t != NULL && t->credit_list != NULL);
601 if (newstate) {
602 if (state)
603 return; /* no change */
604 xs = xs_tcred_lblx;
605 }
606 else {
607 if (!state)
608 return; /* no change */
609 xs = xs_tcred_lbl;
610 }
611
612 XtVaSetValues(widgets.dbprog.tcredits_btn,
613 XmNlabelString, xs,
614 NULL
615 );
616
617 state = newstate;
618 }
619
620
621 /*
622 * dbprog_scred_lblupd
623 * Update the segment credits button label to indicate whether there are
624 * segment credits defined.
625 *
626 * Args:
627 * None.
628 *
629 * Return:
630 * Nothing.
631 */
632 STATIC void
dbprog_scred_lblupd(void)633 dbprog_scred_lblupd(void)
634 {
635 XmString xs;
636 bool_t newstate;
637 static bool_t state = FALSE;
638
639 newstate = (bool_t) (w_seg.credit_list != NULL);
640 if (newstate) {
641 if (state)
642 return; /* no change */
643 xs = xs_scred_lblx;
644 }
645 else {
646 if (!state)
647 return; /* no change */
648 xs = xs_scred_lbl;
649 }
650
651 XtVaSetValues(widgets.segments.credits_btn,
652 XmNlabelString, xs,
653 NULL
654 );
655
656 state = newstate;
657 }
658
659
660 /*
661 * dbprog_set_disc_title
662 * Set the disc title label on the disc details and credits windows
663 *
664 * Args:
665 * discno - The disc number
666 * artist_str - The artist string
667 * title_str - The title string
668 *
669 * Return:
670 * Nothing;
671 */
672 STATIC void
dbprog_set_disc_title(int discno,char * artist,char * title)673 dbprog_set_disc_title(int discno, char *artist, char *title)
674 {
675 XmString xs;
676 char buf[16],
677 dtitle[TITLEIND_LEN];
678
679 (void) sprintf(buf, "Disc %d", discno);
680 (void) sprintf(dtitle, "%.127s%s%.127s",
681 (artist == NULL) ? "" : artist,
682 (artist != NULL && title != NULL) ? " / " : "",
683 (title == NULL) ? app_data.str_unkndisc : title);
684
685 xs = create_xmstring(buf, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
686 XtVaSetValues(widgets.dbextd.discno_lbl,
687 XmNlabelString, xs,
688 NULL
689 );
690 XtVaSetValues(widgets.segments.discno_lbl,
691 XmNlabelString, xs,
692 NULL
693 );
694 if (credits_mode == CREDITS_DISC) {
695 XtVaSetValues(widgets.credits.disctrk_lbl,
696 XmNlabelString, xs,
697 NULL
698 );
699 }
700 XmStringFree(xs);
701
702 xs = create_xmstring(dtitle, NULL, XmSTRING_DEFAULT_CHARSET, TRUE);
703
704 XtVaSetValues(widgets.dbextd.disc_lbl,
705 XmNlabelString, xs,
706 NULL
707 );
708 XtVaSetValues(widgets.segments.disc_lbl,
709 XmNlabelString, xs,
710 NULL
711 );
712 if (credits_mode == CREDITS_DISC) {
713 XtVaSetValues(widgets.credits.title_lbl,
714 XmNlabelString, xs,
715 NULL
716 );
717 }
718 XmStringFree(xs);
719 }
720
721
722 /*
723 * dbprog_set_track_title
724 * Set the track title label on the track details window
725 *
726 * Args:
727 * trkno - The track number
728 * str - The label string
729 *
730 * Return:
731 * Nothing;
732 */
733 STATIC void
dbprog_set_track_title(int trkno,char * str)734 dbprog_set_track_title(int trkno, char *str)
735 {
736 XmString xs;
737 char buf[16];
738
739 (void) sprintf(buf, "Track %d", trkno);
740
741 xs = create_xmstring(buf, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
742 XtVaSetValues(widgets.dbextt.trkno_lbl,
743 XmNlabelString, xs,
744 NULL
745 );
746 if (credits_mode == CREDITS_TRACK) {
747 XtVaSetValues(widgets.credits.disctrk_lbl,
748 XmNlabelString, xs,
749 NULL
750 );
751 }
752 XmStringFree(xs);
753
754 xs = create_xmstring(
755 str, "<Untitled>", XmSTRING_DEFAULT_CHARSET, TRUE
756 );
757
758 XtVaSetValues(widgets.dbextt.trk_lbl,
759 XmNlabelString, xs,
760 NULL
761 );
762 if (credits_mode == CREDITS_TRACK) {
763 XtVaSetValues(widgets.credits.title_lbl,
764 XmNlabelString, xs,
765 NULL
766 );
767 }
768 XmStringFree(xs);
769 }
770
771
772 /*
773 * dbprog_set_seg_title
774 * Set the segment title label on the credits window
775 *
776 * Args:
777 * None.
778 *
779 * Return:
780 * Nothing;
781 */
782 STATIC void
dbprog_set_seg_title(void)783 dbprog_set_seg_title(void)
784 {
785 XmString xs;
786
787 if (credits_mode != CREDITS_SEG)
788 return;
789
790 XtVaGetValues(widgets.segments.segment_lbl,
791 XmNlabelString, &xs,
792 NULL
793 );
794 XtVaSetValues(widgets.credits.disctrk_lbl,
795 XmNlabelString, xs,
796 NULL
797 );
798 XmStringFree(xs);
799
800 xs = create_xmstring(
801 w_seg.name, "<new_segment>", XmSTRING_DEFAULT_CHARSET, TRUE
802 );
803
804 XtVaSetValues(widgets.credits.title_lbl,
805 XmNlabelString, xs,
806 NULL
807 );
808
809 XmStringFree(xs);
810 }
811
812
813 /*
814 * dbprog_extt_autotrk_upd
815 * If auto-track is enabled, display the new track details and
816 * credits info.
817 *
818 * Args:
819 * s - Pointer to the curstat_t structure.
820 * new_pos - The new track list position to go to
821 *
822 * Return:
823 * Nothing.
824 */
825 STATIC void
dbprog_extt_autotrk_upd(curstat_t * s,int new_pos)826 dbprog_extt_autotrk_upd(curstat_t *s, int new_pos)
827 {
828 if (auto_trk && new_pos > 0 && sel_pos != new_pos) {
829 /* Make track details window go to the new track */
830 if (sel_pos > 0) {
831 XmListDeselectPos(widgets.dbprog.trk_list, sel_pos);
832 sel_pos = -1;
833 }
834 XmListSelectPos(widgets.dbprog.trk_list, new_pos, True);
835
836 /* Scroll track list if necessary */
837 dbprog_list_autoscroll(
838 widgets.dbprog.trk_list,
839 (int) s->tot_trks,
840 new_pos
841 );
842 }
843 }
844
845
846 /*
847 * dbprog_curgenre
848 * Return genre structure pointers for the current genre setting.
849 *
850 * Args:
851 * trkpos - -1 for the album genre information, or track position
852 * (0 based) for genre information about a track.
853 * genrep - Return structure pointer for the primary genre.
854 * subgenrep - Return structure pointer for the primary subgenre.
855 * genre2p - Return structure pointer for the secondary genre.
856 * subgenre2p - Return structure pointer for the secondary subgenre.
857 *
858 * Each of the return pointers may be set to NULL if no genre is
859 * currently set.
860 *
861 * Return:
862 * Nothing.
863 */
864 STATIC void
dbprog_curgenre(int trkpos,cdinfo_genre_t ** genrep,cdinfo_genre_t ** subgenrep,cdinfo_genre_t ** genre2p,cdinfo_genre_t ** subgenre2p)865 dbprog_curgenre(
866 int trkpos,
867 cdinfo_genre_t **genrep,
868 cdinfo_genre_t **subgenrep,
869 cdinfo_genre_t **genre2p,
870 cdinfo_genre_t **subgenre2p
871 )
872 {
873 cdinfo_genre_t *p,
874 *q;
875 char *genre,
876 *genre2;
877
878 *genrep = *subgenrep = *genre2p = *subgenre2p = NULL;
879
880 if (trkpos == -1) {
881 genre = dbp->disc.genre;
882 genre2 = dbp->disc.genre2;
883 }
884 else {
885 genre = dbp->track[trkpos].genre;
886 genre2 = dbp->track[trkpos].genre2;
887 }
888
889 if (genre != NULL) {
890 for (p = dbp->genrelist; p != NULL; p = p->next) {
891 if (strcmp(p->id, genre) == 0) {
892 *genrep = p;
893 *subgenrep = NULL;
894 break;
895 }
896 for (q = p->child; q != NULL; q = q->next) {
897 if (strcmp(q->id, genre) == 0) {
898 *genrep = q->parent;
899 *subgenrep = q;
900 break;
901 }
902 }
903 if (*genrep != NULL)
904 break;
905 }
906 }
907 if (genre2 != NULL) {
908 for (p = dbp->genrelist; p != NULL; p = p->next) {
909 if (strcmp(p->id, genre2) == 0) {
910 *genre2p = p;
911 *subgenre2p = NULL;
912 break;
913 }
914 for (q = p->child; q != NULL; q = q->next) {
915 if (strcmp(q->id, genre2) == 0) {
916 *genre2p = q->parent;
917 *subgenre2p = q;
918 break;
919 }
920 }
921 if (*genre2p != NULL)
922 break;
923 }
924 }
925 }
926
927
928 /*
929 * dbprog_genreupd
930 * Update the genre selector menus to indicate the currently set
931 * genres.
932 *
933 * Args:
934 * trkpos - If -1, update the album genre widgets, or specify a
935 * track position (0 based) to update the track genre
936 * widgets.
937 *
938 * Return:
939 * Nothing.
940 */
941 STATIC void
dbprog_genreupd(int trkpos)942 dbprog_genreupd(int trkpos)
943 {
944 int i;
945 Dimension x;
946 Arg arg[10];
947 Widget w;
948 cdinfo_genre_t *p,
949 *q,
950 *sg,
951 *sg2,
952 *genrep,
953 *subgenrep,
954 *genre2p,
955 *subgenre2p;
956 static cdinfo_genre_t *save_genre = NULL,
957 *save_genre2 = NULL,
958 *save_tgenre = NULL,
959 *save_tgenre2 = NULL;
960 static bool_t first = TRUE;
961
962 if (dbp->genrelist == NULL)
963 return;
964
965 if (first) {
966 first = FALSE;
967
968 /* Create primary genre menu entries */
969 for (p = dbp->genrelist; p != NULL; p = p->next) {
970 /* Create primary genre entries */
971 i = 0;
972 XtSetArg(arg[i], XmNinitialResourcesPersistent,
973 False); i++;
974 XtSetArg(arg[i], XmNshadowThickness, 2); i++;
975 p->aux = (void *) XmCreatePushButton(
976 widgets.dbextd.genre_menu[0],
977 p->name,
978 arg,
979 i
980 );
981 p->aux2 = (void *) XmCreatePushButton(
982 widgets.dbextd.genre_menu[1],
983 p->name,
984 arg,
985 i
986 );
987 p->aux3 = (void *) XmCreatePushButton(
988 widgets.dbextt.genre_menu[0],
989 p->name,
990 arg,
991 i
992 );
993 p->aux4 = (void *) XmCreatePushButton(
994 widgets.dbextt.genre_menu[1],
995 p->name,
996 arg,
997 i
998 );
999
1000 XtManageChild((Widget) p->aux);
1001 XtManageChild((Widget) p->aux2);
1002 XtManageChild((Widget) p->aux3);
1003 XtManageChild((Widget) p->aux4);
1004
1005 register_activate_cb((Widget) p->aux,
1006 dbprog_genre_sel, p);
1007 register_activate_cb((Widget) p->aux2,
1008 dbprog_genre_sel, p);
1009 register_activate_cb((Widget) p->aux3,
1010 dbprog_genre_sel, p);
1011 register_activate_cb((Widget) p->aux4,
1012 dbprog_genre_sel, p);
1013 }
1014 }
1015
1016 /* Find current genre and subgenre entries */
1017 dbprog_curgenre(trkpos, &genrep, &subgenrep, &genre2p, &subgenre2p);
1018
1019 if (trkpos == -1) {
1020 sg = save_genre;
1021 sg2 = save_genre2;
1022 }
1023 else {
1024 sg = save_tgenre;
1025 sg2 = save_tgenre2;
1026 }
1027
1028 /* If the primary genre changed, re-create the associated
1029 * sub-genre menu entries
1030 */
1031 if (sg != NULL &&
1032 (genrep == NULL || subgenrep == NULL || genrep != sg)) {
1033 for (q = sg->child; q != NULL; q = q->next) {
1034 /* Delete old sub-genre entries */
1035 if (trkpos == -1) {
1036 if (q->aux != NULL) {
1037 XtUnmanageChild((Widget) q->aux);
1038 XtDestroyWidget((Widget) q->aux);
1039 q->aux = NULL;
1040 }
1041 }
1042 else {
1043 if (q->aux3 != NULL) {
1044 XtUnmanageChild((Widget) q->aux3);
1045 XtDestroyWidget((Widget) q->aux3);
1046 q->aux3 = NULL;
1047 }
1048 }
1049 }
1050 }
1051 if (sg2 != NULL &&
1052 (genre2p == NULL || subgenre2p == NULL || genre2p != sg2)) {
1053 for (q = sg2->child; q != NULL; q = q->next) {
1054 /* Delete old sub-genre entries */
1055 if (trkpos == -1) {
1056 if (q->aux2 != NULL) {
1057 XtUnmanageChild((Widget) q->aux2);
1058 XtDestroyWidget((Widget) q->aux2);
1059 q->aux2 = NULL;
1060 }
1061 }
1062 else {
1063 if (q->aux4 != NULL) {
1064 XtUnmanageChild((Widget) q->aux4);
1065 XtDestroyWidget((Widget) q->aux4);
1066 q->aux4 = NULL;
1067 }
1068 }
1069 }
1070 }
1071
1072 if (genrep != NULL && genrep != sg) {
1073 for (q = genrep->child; q != NULL; q = q->next) {
1074 /* Create new sub-genre entries */
1075 i = 0;
1076 XtSetArg(arg[i], XmNinitialResourcesPersistent,
1077 False); i++;
1078 XtSetArg(arg[i], XmNshadowThickness, 2); i++;
1079 if (trkpos == -1) {
1080 q->aux = (void *) XmCreatePushButton(
1081 widgets.dbextd.subgenre_menu[0],
1082 q->name,
1083 arg,
1084 i
1085 );
1086 XtManageChild((Widget) q->aux);
1087 register_activate_cb((Widget) q->aux,
1088 dbprog_subgenre_sel, q);
1089 }
1090 else {
1091 q->aux3 = (void *) XmCreatePushButton(
1092 widgets.dbextt.subgenre_menu[0],
1093 q->name,
1094 arg,
1095 i
1096 );
1097 XtManageChild((Widget) q->aux3);
1098 register_activate_cb((Widget) q->aux3,
1099 dbprog_subgenre_sel, q);
1100 }
1101 }
1102 }
1103
1104 if (genre2p != NULL && genre2p != sg2) {
1105 for (q = genre2p->child; q != NULL; q = q->next) {
1106 /* Create new sub-genre entries */
1107 i = 0;
1108 XtSetArg(arg[i], XmNinitialResourcesPersistent,
1109 False); i++;
1110 XtSetArg(arg[i], XmNshadowThickness, 2); i++;
1111 if (trkpos == -1) {
1112 q->aux2 = (void *) XmCreatePushButton(
1113 widgets.dbextd.subgenre_menu[1],
1114 q->name,
1115 arg,
1116 i
1117 );
1118 XtManageChild((Widget) q->aux2);
1119 register_activate_cb((Widget) q->aux2,
1120 dbprog_subgenre_sel, q);
1121 }
1122 else {
1123 q->aux4 = (void *) XmCreatePushButton(
1124 widgets.dbextt.subgenre_menu[1],
1125 q->name,
1126 arg,
1127 i
1128 );
1129 XtManageChild((Widget) q->aux4);
1130 register_activate_cb((Widget) q->aux4,
1131 dbprog_subgenre_sel, q);
1132 }
1133 }
1134 }
1135
1136 if (genrep != NULL) {
1137 if (trkpos == -1) {
1138 if (genrep->aux != NULL)
1139 XtVaSetValues(widgets.dbextd.genre_opt[0],
1140 XmNmenuHistory, genrep->aux,
1141 NULL
1142 );
1143 }
1144 else {
1145 if (genrep->aux3 != NULL)
1146 XtVaSetValues(widgets.dbextt.genre_opt[0],
1147 XmNmenuHistory, genrep->aux3,
1148 NULL
1149 );
1150 }
1151 }
1152 else {
1153 if (trkpos == -1) {
1154 XtVaSetValues(widgets.dbextd.genre_opt[0],
1155 XmNmenuHistory,
1156 widgets.dbextd.genre_none_btn[0],
1157 NULL
1158 );
1159 }
1160 else {
1161 XtVaSetValues(widgets.dbextt.genre_opt[0],
1162 XmNmenuHistory,
1163 widgets.dbextt.genre_none_btn[0],
1164 NULL
1165 );
1166 }
1167 }
1168
1169 if (subgenrep != NULL) {
1170 if (trkpos == -1) {
1171 if (subgenrep->aux != NULL)
1172 XtVaSetValues(widgets.dbextd.subgenre_opt[0],
1173 XmNmenuHistory, subgenrep->aux,
1174 NULL
1175 );
1176 }
1177 else {
1178 if (subgenrep->aux3 != NULL)
1179 XtVaSetValues(widgets.dbextt.subgenre_opt[0],
1180 XmNmenuHistory, subgenrep->aux3,
1181 NULL
1182 );
1183 }
1184 }
1185 else {
1186 if (trkpos == -1) {
1187 XtVaSetValues(widgets.dbextd.subgenre_opt[0],
1188 XmNmenuHistory,
1189 widgets.dbextd.subgenre_none_btn[0],
1190 NULL
1191 );
1192 }
1193 else {
1194 XtVaSetValues(widgets.dbextt.subgenre_opt[0],
1195 XmNmenuHistory,
1196 widgets.dbextt.subgenre_none_btn[0],
1197 NULL
1198 );
1199 }
1200 }
1201
1202 if (genre2p != NULL) {
1203 if (trkpos == -1) {
1204 if (genre2p->aux2 != NULL)
1205 XtVaSetValues(widgets.dbextd.genre_opt[1],
1206 XmNmenuHistory, genre2p->aux2,
1207 NULL
1208 );
1209 }
1210 else {
1211 if (genre2p->aux4 != NULL)
1212 XtVaSetValues(widgets.dbextt.genre_opt[1],
1213 XmNmenuHistory, genre2p->aux4,
1214 NULL
1215 );
1216 }
1217 }
1218 else {
1219 if (trkpos == -1) {
1220 XtVaSetValues(widgets.dbextd.genre_opt[1],
1221 XmNmenuHistory,
1222 widgets.dbextd.genre_none_btn[1],
1223 NULL
1224 );
1225 }
1226 else {
1227 XtVaSetValues(widgets.dbextt.genre_opt[1],
1228 XmNmenuHistory,
1229 widgets.dbextt.genre_none_btn[1],
1230 NULL
1231 );
1232 }
1233 }
1234
1235 if (subgenre2p != NULL) {
1236 if (trkpos == -1) {
1237 if (subgenre2p->aux2 != NULL)
1238 XtVaSetValues(widgets.dbextd.subgenre_opt[1],
1239 XmNmenuHistory, subgenre2p->aux2,
1240 NULL
1241 );
1242 }
1243 else {
1244 if (subgenre2p->aux4 != NULL)
1245 XtVaSetValues(widgets.dbextt.subgenre_opt[1],
1246 XmNmenuHistory, subgenre2p->aux4,
1247 NULL
1248 );
1249 }
1250 }
1251 else {
1252 if (trkpos == -1) {
1253 XtVaSetValues(widgets.dbextd.subgenre_opt[1],
1254 XmNmenuHistory,
1255 widgets.dbextd.subgenre_none_btn[1],
1256 NULL
1257 );
1258 }
1259 else {
1260 XtVaSetValues(widgets.dbextt.subgenre_opt[1],
1261 XmNmenuHistory,
1262 widgets.dbextt.subgenre_none_btn[1],
1263 NULL
1264 );
1265 }
1266 }
1267
1268 if (trkpos == -1) {
1269 save_genre = genrep;
1270 save_genre2 = genre2p;
1271 w = widgets.dbextd.form;
1272 }
1273 else {
1274 save_tgenre = genrep;
1275 save_tgenre2 = genre2p;
1276 w = widgets.dbextt.form;
1277 }
1278
1279 if (genrep != NULL || genre2p != NULL) {
1280 /* Hack: Force the subgenre option menu to resize to
1281 * match the selected label.
1282 */
1283 XtVaGetValues(w, XmNwidth, &x, NULL);
1284 XtVaSetValues(w, XmNwidth, x+1, NULL);
1285 XtVaSetValues(w, XmNwidth, x, NULL);
1286 }
1287 }
1288
1289
1290 /*
1291 * dbprog_regionupd
1292 * Update the region display to match the current region setting.
1293 *
1294 * Args:
1295 * None.
1296 *
1297 * Return:
1298 * Nothing.
1299 */
1300 STATIC void
dbprog_regionupd(void)1301 dbprog_regionupd(void)
1302 {
1303 cdinfo_region_t *rp;
1304 XmString xs;
1305 int i;
1306 static bool_t first = TRUE;
1307
1308 if (dbp->regionlist == NULL) {
1309 XtSetSensitive(widgets.dbextd.region_chg_btn, False);
1310 XtSetSensitive(widgets.userreg.region_chg_btn, False);
1311 return;
1312 }
1313
1314 if (first) {
1315 first = FALSE;
1316 /* Set up region selector */
1317 for (i = 1, rp = dbp->regionlist; rp != NULL;
1318 i++, rp = rp->next) {
1319 xs = create_xmstring(rp->name, NULL, CHSET1, TRUE);
1320
1321 XmListAddItemUnselected(widgets.regionsel.region_list,
1322 xs, i);
1323 XmStringFree(xs);
1324 }
1325 reg_cnt = i - 1;
1326
1327 if (reg_cnt > 0) {
1328 XtSetSensitive(widgets.dbextd.region_chg_btn, True);
1329 XtSetSensitive(widgets.userreg.region_chg_btn, True);
1330 }
1331 }
1332
1333 if (dbp->disc.region != NULL)
1334 set_text_string(widgets.dbextd.region_txt,
1335 cdinfo_region_name(dbp->disc.region),
1336 TRUE);
1337 else
1338 set_text_string(widgets.dbextd.region_txt, "", FALSE);
1339 }
1340
1341
1342 /*
1343 * dbprog_langupd
1344 * Update the language display to match the current language setting.
1345 *
1346 * Args:
1347 * None.
1348 *
1349 * Return:
1350 * Nothing.
1351 */
1352 STATIC void
dbprog_langupd(void)1353 dbprog_langupd(void)
1354 {
1355 cdinfo_lang_t *lp;
1356 XmString xs;
1357 int i;
1358 static bool_t first = TRUE;
1359
1360 if (dbp->langlist == NULL) {
1361 XtSetSensitive(widgets.dbextd.lang_chg_btn, False);
1362 return;
1363 }
1364
1365 if (first) {
1366 first = FALSE;
1367 /* Set up language selector */
1368 for (i = 1, lp = dbp->langlist; lp != NULL;
1369 i++, lp = lp->next) {
1370 xs = create_xmstring(lp->name, NULL, CHSET1, TRUE);
1371
1372 XmListAddItemUnselected(widgets.langsel.lang_list,
1373 xs, i);
1374 XmStringFree(xs);
1375 }
1376 lang_cnt = i - 1;
1377
1378 if (lang_cnt > 0)
1379 XtSetSensitive(widgets.dbextd.lang_chg_btn, True);
1380 }
1381
1382 if (dbp->disc.lang != NULL)
1383 set_text_string(widgets.dbextd.lang_txt,
1384 cdinfo_lang_name(dbp->disc.lang),
1385 TRUE);
1386 else
1387 set_text_string(widgets.dbextd.lang_txt, "", FALSE);
1388 }
1389
1390
1391 /*
1392 * dbprog_creditupd
1393 * Update the credits window widgets
1394 *
1395 * Args:
1396 * s - Pointer to the curstat_t structure
1397 * pos - Track position (0-based) for track credit, or -1 for
1398 * album credit.
1399 *
1400 * Return:
1401 * Nothing
1402 */
1403 STATIC void
dbprog_creditupd(curstat_t * s,int pos)1404 dbprog_creditupd(curstat_t *s, int pos)
1405 {
1406 int i;
1407 Arg arg[10];
1408 cdinfo_role_t *p;
1409 cdinfo_credit_t *q;
1410 char *str;
1411 XmString xs;
1412 static bool_t first = TRUE;
1413
1414 if (dbp->rolelist != NULL && first) {
1415 first = FALSE;
1416
1417 /* Create primary role menu entries */
1418 for (p = dbp->rolelist; p != NULL; p = p->next) {
1419 /* Create primary role entries */
1420 i = 0;
1421 XtSetArg(arg[i], XmNinitialResourcesPersistent,
1422 False); i++;
1423 XtSetArg(arg[i], XmNshadowThickness, 2); i++;
1424 p->aux = (void *) XmCreatePushButton(
1425 widgets.credits.prirole_menu,
1426 p->name,
1427 arg,
1428 i
1429 );
1430 XtManageChild((Widget) p->aux);
1431 register_activate_cb((Widget) p->aux,
1432 dbprog_role_sel, p);
1433 }
1434 }
1435
1436 switch (credits_mode) {
1437 case CREDITS_DISC:
1438 q = dbp->disc.credit_list;
1439 dbprog_set_disc_title(s->cur_disc,
1440 dbp->disc.artist,
1441 dbp->disc.title);
1442 break;
1443
1444 case CREDITS_TRACK:
1445 if (pos < 0)
1446 return; /* Invalid track */
1447 q = dbp->track[pos].credit_list;
1448 dbprog_set_track_title(
1449 s->trkinfo[pos].trkno,
1450 dbp->track[pos].title
1451 );
1452 break;
1453
1454 case CREDITS_SEG:
1455 q = w_seg.credit_list;
1456 dbprog_set_seg_title();
1457 break;
1458
1459 default:
1460 return; /* Invalid mode */
1461 }
1462
1463 /* Update credits list widget */
1464 XmListDeleteAllItems(widgets.credits.cred_list);
1465 cred_pos = -1;
1466
1467 for (i = 1; q != NULL; q = q->next, i++) {
1468 char *rolename,
1469 *name;
1470
1471 rolename = q->crinfo.role == NULL ? "unknown" :
1472 cdinfo_role_name(q->crinfo.role->id);
1473 name = q->crinfo.name == NULL ? "unknown" : q->crinfo.name;
1474
1475 str = (char *) MEM_ALLOC("credlist_ent",
1476 strlen(rolename) + strlen(name) + 8
1477 );
1478 if (str == NULL) {
1479 CD_FATAL(app_data.str_nomemory);
1480 return;
1481 }
1482 (void) sprintf(str, CREDITLIST_FMT,
1483 name, rolename,
1484 q->notes != NULL ? ASTERISK_STR : "");
1485
1486 xs = create_xmstring(str, NULL, CHSET1, TRUE);
1487
1488 XmListAddItemUnselected(widgets.credits.cred_list, xs, i);
1489
1490 XmStringFree(xs);
1491 MEM_FREE(str);
1492 }
1493
1494 /* Set role selectors to "None" */
1495 XtVaSetValues(
1496 widgets.credits.prirole_opt,
1497 XmNmenuHistory, widgets.credits.prirole_none_btn,
1498 NULL
1499 );
1500 XtCallCallbacks(
1501 widgets.credits.prirole_none_btn,
1502 XmNactivateCallback, (XtPointer) NULL
1503 );
1504
1505 w_cred.crinfo.role = NULL;
1506
1507 /* Set fields to null */
1508 /* Explicitly set fullname fields */
1509 if (w_cred.crinfo.fullname.dispname != NULL) {
1510 MEM_FREE(w_cred.crinfo.fullname.dispname);
1511 w_cred.crinfo.fullname.dispname = NULL;
1512 }
1513 if (w_cred.crinfo.fullname.lastname != NULL) {
1514 MEM_FREE(w_cred.crinfo.fullname.lastname);
1515 w_cred.crinfo.fullname.lastname = NULL;
1516 }
1517 if (w_cred.crinfo.fullname.firstname != NULL) {
1518 MEM_FREE(w_cred.crinfo.fullname.firstname);
1519 w_cred.crinfo.fullname.firstname = NULL;
1520 }
1521 /* The fields are set via callback for these */
1522 set_text_string(widgets.credits.name_txt, "", FALSE);
1523 set_text_string(widgets.credits.notes_txt, "", FALSE);
1524
1525 if (fname_mode == FNAME_CREDITS) {
1526 /* Update fullname window: note that call_data is passed
1527 * a NULL pointer. The dbprog_fullname function doesn't
1528 * use that field currently, but if that is changed in the
1529 * future this will have to change to match.
1530 */
1531 fname_changed = FALSE;
1532 dbprog_fullname(
1533 widgets.credits.fullname_btn,
1534 (XtPointer) FALSE, NULL
1535 );
1536 }
1537
1538 XtSetSensitive(widgets.credits.add_btn, False);
1539 XtSetSensitive(widgets.credits.del_btn, False);
1540 XtSetSensitive(widgets.credits.mod_btn, False);
1541
1542 switch (credits_mode) {
1543 case CREDITS_DISC:
1544 dbprog_dcred_lblupd();
1545 break;
1546 case CREDITS_TRACK:
1547 dbprog_tcred_lblupd(&dbp->track[pos]);
1548 dbprog_listupd_ent(s, pos,
1549 (dbp->track[pos].title != NULL) ?
1550 dbp->track[pos].title : UNDEF_STR,
1551 FALSE);
1552 /* Re-select the list entry if necessary */
1553 if ((pos + 1) == sel_pos) {
1554 XmListSelectPos(widgets.dbprog.trk_list,
1555 sel_pos, False);
1556 }
1557 else if ((pos + 1) == ind_pos) {
1558 XmListSelectPos(widgets.dbprog.trk_list,
1559 ind_pos, False);
1560 }
1561 break;
1562 case CREDITS_SEG:
1563 dbprog_scred_lblupd();
1564 break;
1565 default:
1566 break;
1567 }
1568 }
1569
1570
1571 /*
1572 * dbprog_credit_ck
1573 * Perform sanity checking of a credit structure
1574 *
1575 * Args:
1576 * p - Pointer to the credit structure to be checked
1577 * pos - Track position (0-based) for the credit, or -1 for album credit
1578 * s - Pointer to the curstat_t structure
1579 *
1580 * Return:
1581 * TRUE - success
1582 * FALSE - failure
1583 */
1584 STATIC bool_t
dbprog_credit_ck(cdinfo_credit_t * cp,int pos,curstat_t * s)1585 dbprog_credit_ck(cdinfo_credit_t *cp, int pos, curstat_t *s)
1586 {
1587 cdinfo_credit_t *list,
1588 *p;
1589 char buf[16],
1590 *str;
1591 int i,
1592 j;
1593
1594 if (cp->crinfo.role == NULL) {
1595 CD_INFO(app_data.str_needrole);
1596 return FALSE;
1597 }
1598 if (cp->crinfo.name == NULL) {
1599 CD_INFO(app_data.str_needrolename);
1600 return FALSE;
1601 }
1602
1603 switch (credits_mode) {
1604 case CREDITS_DISC:
1605 list = dbp->disc.credit_list;
1606 break;
1607 case CREDITS_TRACK:
1608 if (pos < 0)
1609 return FALSE;
1610 list = dbp->track[pos].credit_list;
1611 break;
1612 case CREDITS_SEG:
1613 list = w_seg.credit_list;
1614 break;
1615 default:
1616 return FALSE;
1617 }
1618
1619 /* Check to make sure this credit is not a duplicate of an
1620 * another credit in the same list
1621 */
1622 i = 0;
1623 for (p = list; p != NULL; p = p->next) {
1624 if (cred_pos > 0 && ++i == cred_pos)
1625 continue; /* Don't check the one being modified */
1626
1627 if (cp->crinfo.role == p->crinfo.role &&
1628 strcmp(cp->crinfo.name, p->crinfo.name) == 0) {
1629 if (cp->notes != NULL && p->notes != NULL) {
1630 if (strcmp(cp->notes, p->notes) == 0) {
1631 CD_INFO(app_data.str_dupcredit);
1632 return FALSE;
1633 }
1634 }
1635 else if (cp->notes == NULL && p->notes == NULL) {
1636 CD_INFO(app_data.str_dupcredit);
1637 return FALSE;
1638 }
1639 }
1640 }
1641
1642 if (credits_mode == CREDITS_DISC) {
1643 /* Check to make sure that the credit is not a duplicate
1644 * of a track credit
1645 */
1646 for (j = 0; j < (int) s->tot_trks; j++) {
1647 for (p = dbp->track[j].credit_list; p != NULL;
1648 p = p->next) {
1649 if (cp->crinfo.role == p->crinfo.role &&
1650 p->crinfo.name != NULL &&
1651 strcmp(cp->crinfo.name, p->crinfo.name) == 0) {
1652 if (cp->notes != NULL && p->notes != NULL) {
1653 if (strcmp(cp->notes, p->notes) == 0) {
1654 str = (char *) MEM_ALLOC(
1655 "str_duptrkcredit",
1656 strlen(app_data.str_duptrkcredit) + 8
1657 );
1658 (void) sprintf(buf, "%d",
1659 s->trkinfo[j].trkno);
1660 (void) sprintf(str,
1661 app_data.str_duptrkcredit,
1662 buf);
1663 CD_INFO(str);
1664 MEM_FREE(str);
1665 return FALSE;
1666 }
1667 }
1668 else if (cp->notes == NULL && p->notes == NULL) {
1669 str = (char *) MEM_ALLOC(
1670 "str_duptrkcredit",
1671 strlen(app_data.str_duptrkcredit) + 8
1672 );
1673 (void) sprintf(buf, "%d", s->trkinfo[j].trkno);
1674 (void) sprintf(str, app_data.str_duptrkcredit,
1675 buf);
1676 CD_INFO(str);
1677 MEM_FREE(str);
1678 return FALSE;
1679 }
1680 }
1681 }
1682 }
1683 }
1684 else if (credits_mode == CREDITS_TRACK) {
1685 /* Check to make sure the credit is not a duplicate of
1686 * a disc credit
1687 */
1688 for (p = dbp->disc.credit_list; p != NULL; p = p->next) {
1689 if (cp->crinfo.role == p->crinfo.role &&
1690 p->crinfo.name != NULL &&
1691 strcmp(cp->crinfo.name, p->crinfo.name) == 0) {
1692 if (cp->notes != NULL && p->notes != NULL) {
1693 if (strcmp(cp->notes, p->notes) == 0) {
1694 CD_INFO(app_data.str_dupdisccredit);
1695 return FALSE;
1696 }
1697 }
1698 else if (cp->notes == NULL && p->notes == NULL) {
1699 CD_INFO(app_data.str_dupdisccredit);
1700 return FALSE;
1701 }
1702 }
1703 }
1704 }
1705
1706 return TRUE;
1707 }
1708
1709
1710 /*
1711 * dbprog_segmentupd
1712 * Update the segments window widgets
1713 *
1714 * Args:
1715 * s - Pointer to the curstat_t structure
1716 *
1717 * Return:
1718 * Nothing
1719 */
1720 STATIC void
dbprog_segmentupd(curstat_t * s)1721 dbprog_segmentupd(curstat_t *s)
1722 {
1723 int i;
1724 cdinfo_segment_t *q;
1725 cdinfo_credit_t *cp,
1726 *cp2;
1727 char *str;
1728 XmString xs;
1729
1730 if (!XtIsManaged(widgets.segments.form))
1731 /* No need to do anything now */
1732 return;
1733
1734 /* Set credits window title if appropriate */
1735 dbprog_set_seg_title();
1736
1737 /* Update segment window set button mode */
1738 dbprog_segments_setmode(s);
1739
1740 /* Update segments list widget */
1741 XmListDeleteAllItems(widgets.segments.seg_list);
1742 seg_pos = -1;
1743
1744 for (q = dbp->disc.segment_list, i = 1; q != NULL; q = q->next, i++) {
1745 char *segname,
1746 *st,
1747 *sf,
1748 *et,
1749 *ef;
1750
1751 segname = q->name == NULL ? "unknown" : q->name;
1752 st = q->start_track == NULL ? "??" : q->start_track;
1753 sf = q->start_frame == NULL ? "?" : q->start_frame;
1754 et = q->end_track == NULL ? "??" : q->end_track;
1755 ef = q->end_frame == NULL ? "?" : q->end_frame;
1756
1757 str = (char *) MEM_ALLOC("seglist_ent",
1758 strlen(segname) + strlen(st) + strlen(sf) +
1759 strlen(et) + strlen(ef) + 8
1760 );
1761 if (str == NULL) {
1762 CD_FATAL(app_data.str_nomemory);
1763 return;
1764 }
1765 (void) sprintf(str, SEGLIST_FMT,
1766 segname, st, sf, et, ef,
1767 (q->notes != NULL || q->credit_list != NULL) ?
1768 ASTERISK_STR : "");
1769
1770 xs = create_xmstring(str, NULL, CHSET1, TRUE);
1771
1772 XmListAddItemUnselected(widgets.segments.seg_list, xs, i);
1773
1774 XmStringFree(xs);
1775 MEM_FREE(str);
1776 }
1777
1778 /* Set fields to null. The fields are set via callback for these */
1779 set_text_string(widgets.segments.name_txt, "", FALSE);
1780 set_text_string(widgets.segments.starttrk_txt, "", FALSE);
1781 set_text_string(widgets.segments.startfrm_txt, "", FALSE);
1782 set_text_string(widgets.segments.endtrk_txt, "", FALSE);
1783 set_text_string(widgets.segments.endfrm_txt, "", FALSE);
1784 set_text_string(widgets.segments.notes_txt, "", FALSE);
1785
1786 /* Clear credit list */
1787 for (cp = cp2 = w_seg.credit_list; cp != NULL; cp = cp2) {
1788 cp2 = cp->next;
1789
1790 if (cp->crinfo.name != NULL)
1791 MEM_FREE(cp->crinfo.name);
1792 if (cp->crinfo.fullname.dispname != NULL)
1793 MEM_FREE(cp->crinfo.fullname.dispname);
1794 if (cp->crinfo.fullname.lastname != NULL)
1795 MEM_FREE(cp->crinfo.fullname.lastname);
1796 if (cp->crinfo.fullname.firstname != NULL)
1797 MEM_FREE(cp->crinfo.fullname.firstname);
1798 if (cp->crinfo.fullname.the != NULL)
1799 MEM_FREE(cp->crinfo.fullname.the);
1800 if (cp->notes != NULL)
1801 MEM_FREE(cp->notes);
1802 MEM_FREE(cp);
1803 }
1804 w_seg.credit_list = NULL;
1805
1806 XtSetSensitive(widgets.segments.add_btn, False);
1807 XtSetSensitive(widgets.segments.del_btn, False);
1808 XtSetSensitive(widgets.segments.mod_btn, False);
1809
1810 /* Update segments button label if needed */
1811 dbprog_seg_lblupd();
1812
1813 s->segplay = SEGP_NONE;
1814 dpy_progmode(s, FALSE);
1815 }
1816
1817
1818 /*
1819 * dbprog_segment_ck
1820 * Perform sanity checking of a segment structure
1821 *
1822 * Args:
1823 * p - Pointer to the segment structure to be checked
1824 * s - Pointer to the curstat_t structure
1825 *
1826 * Return:
1827 * TRUE - success
1828 * FALSE - failure
1829 */
1830 STATIC bool_t
dbprog_segment_ck(cdinfo_segment_t * cp,curstat_t * s)1831 dbprog_segment_ck(cdinfo_segment_t *cp, curstat_t *s)
1832 {
1833 int i;
1834 sword32_t st,
1835 et,
1836 sf,
1837 ef,
1838 startaddr,
1839 endaddr,
1840 eot;
1841
1842 if (cp->name == NULL ||
1843 cp->start_track == NULL || cp->start_frame == NULL ||
1844 cp->end_track == NULL || cp->end_frame == NULL) {
1845 CD_INFO(app_data.str_incseginfo);
1846 return FALSE;
1847 }
1848
1849 st = atoi(cp->start_track);
1850 et = atoi(cp->end_track);
1851 sf = atoi(cp->start_frame);
1852 ef = atoi(cp->end_frame);
1853
1854 /* Check start track and frame numbers */
1855 for (i = 0; i < (int) s->tot_trks; i++) {
1856 if (s->trkinfo[i].trkno == st)
1857 break;
1858 }
1859
1860 eot = s->trkinfo[i+1].addr;
1861
1862 /* "Enhanced CD" / "CD Extra" hack */
1863 if (eot > s->discpos_tot.addr)
1864 eot = s->discpos_tot.addr;
1865
1866 if (i == s->tot_trks || sf >= (eot - s->trkinfo[i].addr)) {
1867 CD_INFO(app_data.str_invseginfo);
1868 return FALSE;
1869 }
1870 startaddr = s->trkinfo[i].addr + sf;
1871
1872 /* Check end track and frame numbers */
1873 for (i = 0; i < (int) s->tot_trks; i++) {
1874 if (s->trkinfo[i].trkno == et)
1875 break;
1876 }
1877
1878 eot = s->trkinfo[i+1].addr;
1879
1880 /* "Enhanced CD" / "CD Extra" hack */
1881 if (eot > s->discpos_tot.addr)
1882 eot = s->discpos_tot.addr;
1883
1884 if (i == s->tot_trks || ef >= (eot - s->trkinfo[i].addr)) {
1885 CD_INFO(app_data.str_invseginfo);
1886 return FALSE;
1887 }
1888 endaddr = s->trkinfo[i].addr + ef;
1889
1890 /* Check start < end */
1891 if (endaddr <= (startaddr + app_data.min_playblks)) {
1892 CD_INFO(app_data.str_segposerr);
1893 return FALSE;
1894 }
1895
1896 return TRUE;
1897 }
1898
1899
1900 /*
1901 * dbprog_extdupd
1902 * Update the state of the disc details window widgets to match that
1903 * of the cdinfo_incore_t structure.
1904 *
1905 * Args:
1906 * s - Pointer to the curstat_t structure.
1907 *
1908 * Return:
1909 * Nothing.
1910 */
1911 STATIC void
dbprog_extdupd(curstat_t * s)1912 dbprog_extdupd(curstat_t *s)
1913 {
1914 char *p,
1915 discid[16];
1916 XmString xs;
1917
1918 /* Sort title */
1919 if (dbp->disc.sorttitle != NULL) {
1920 p = get_text_string(widgets.dbextd.sorttitle_txt, TRUE);
1921 if (p == NULL || strcmp(p, dbp->disc.sorttitle) != 0) {
1922 set_text_string(widgets.dbextd.sorttitle_txt,
1923 dbp->disc.sorttitle,
1924 TRUE);
1925 }
1926 if (p != NULL)
1927 MEM_FREE(p);
1928 }
1929 else
1930 set_text_string(widgets.dbextd.sorttitle_txt, "", FALSE);
1931
1932 /* Title "The" */
1933 if (dbp->disc.title_the != NULL) {
1934 p = get_text_string(widgets.dbextd.the_txt, TRUE);
1935 if (p == NULL || strcmp(p, dbp->disc.title_the) != 0) {
1936 set_text_string(widgets.dbextd.the_txt,
1937 dbp->disc.title_the,
1938 TRUE);
1939 XmToggleButtonSetState(widgets.dbextd.the_btn,
1940 True, False);
1941 XtSetSensitive(widgets.dbextd.the_txt, True);
1942 }
1943 if (p != NULL)
1944 MEM_FREE(p);
1945 }
1946 else {
1947 set_text_string(widgets.dbextd.the_txt, "", FALSE);
1948 XmToggleButtonSetState(widgets.dbextd.the_btn, False, False);
1949 XtSetSensitive(widgets.dbextd.the_txt, False);
1950 }
1951
1952 /* Year */
1953 if (dbp->disc.year != NULL) {
1954 p = get_text_string(widgets.dbextd.year_txt, TRUE);
1955 if (p == NULL || strcmp(p, dbp->disc.year) != 0) {
1956 set_text_string(widgets.dbextd.year_txt,
1957 dbp->disc.year,
1958 TRUE);
1959 }
1960 if (p != NULL)
1961 MEM_FREE(p);
1962 }
1963 else
1964 set_text_string(widgets.dbextd.year_txt, "", FALSE);
1965
1966 /* Label */
1967 if (dbp->disc.label != NULL) {
1968 p = get_text_string(widgets.dbextd.label_txt, TRUE);
1969 if (p == NULL || strcmp(p, dbp->disc.label) != 0) {
1970 set_text_string(widgets.dbextd.label_txt,
1971 dbp->disc.label,
1972 TRUE);
1973 }
1974 if (p != NULL)
1975 MEM_FREE(p);
1976 }
1977 else
1978 set_text_string(widgets.dbextd.label_txt, "", FALSE);
1979
1980 /* Compilation */
1981 XmToggleButtonSetState(widgets.dbextd.comp_btn,
1982 dbp->disc.compilation, False);
1983
1984 /* Genres and subgenres */
1985 dbprog_genreupd(-1);
1986
1987 /* Disc number in set */
1988 if (dbp->disc.dnum != NULL) {
1989 p = get_text_string(widgets.dbextd.dnum_txt, TRUE);
1990 if (p == NULL || strcmp(p, dbp->disc.dnum) != 0)
1991 set_text_string(widgets.dbextd.dnum_txt,
1992 dbp->disc.dnum,
1993 TRUE);
1994 if (p != NULL)
1995 MEM_FREE(p);
1996 }
1997 else
1998 set_text_string(widgets.dbextd.dnum_txt, "", FALSE);
1999
2000 /* Total discs in set */
2001 if (dbp->disc.tnum != NULL) {
2002 p = get_text_string(widgets.dbextd.tnum_txt, TRUE);
2003 if (p == NULL || strcmp(p, dbp->disc.tnum) != 0)
2004 set_text_string(widgets.dbextd.tnum_txt,
2005 dbp->disc.tnum,
2006 TRUE);
2007 if (p != NULL)
2008 MEM_FREE(p);
2009 }
2010 else
2011 set_text_string(widgets.dbextd.tnum_txt, "", FALSE);
2012
2013 /* Album Region */
2014 dbprog_regionupd();
2015
2016 /* Album Language */
2017 dbprog_langupd();
2018
2019 /* Disc notes */
2020 if (dbp->disc.notes != NULL) {
2021 p = get_text_string(widgets.dbextd.notes_txt, TRUE);
2022 if (p == NULL || strcmp(p, dbp->disc.notes) != 0)
2023 set_text_string(widgets.dbextd.notes_txt,
2024 dbp->disc.notes,
2025 TRUE);
2026 if (p != NULL)
2027 MEM_FREE(p);
2028 }
2029 else
2030 set_text_string(widgets.dbextd.notes_txt, "", FALSE);
2031
2032 /* Disc ID */
2033 if (dbp->discid != 0)
2034 (void) sprintf(discid, "%08x", dbp->discid);
2035 else
2036 discid[0] = '\0';
2037 xs = create_xmstring(discid, NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
2038 XtVaSetValues(widgets.dbextd.discid_ind,
2039 XmNlabelString, xs,
2040 NULL
2041 );
2042 XmStringFree(xs);
2043
2044 /* Media catalog number */
2045 xs = create_xmstring(
2046 (char *) s->mcn, "-", XmSTRING_DEFAULT_CHARSET, FALSE
2047 );
2048 XtVaSetValues(widgets.dbextd.mcn_ind,
2049 XmNlabelString, xs,
2050 NULL
2051 );
2052 XmStringFree(xs);
2053
2054 /* Revision */
2055 xs = create_xmstring(
2056 dbp->disc.revision, "-", XmSTRING_DEFAULT_CHARSET, TRUE
2057 );
2058 XtVaSetValues(widgets.dbextd.rev_ind,
2059 XmNlabelString, xs,
2060 NULL
2061 );
2062 XmStringFree(xs);
2063
2064 /* Certifier */
2065 xs = create_xmstring(
2066 dbp->disc.certifier, "-", XmSTRING_DEFAULT_CHARSET, TRUE
2067 );
2068
2069 XtVaSetValues(widgets.dbextd.cert_ind,
2070 XmNlabelString, xs,
2071 NULL
2072 );
2073 XmStringFree(xs);
2074
2075 /* Update disc details window and credits window headings */
2076 dbprog_set_disc_title(s->cur_disc, dbp->disc.artist, dbp->disc.title);
2077 dbprog_set_seg_title();
2078
2079 /* Update button labels */
2080 dbprog_extd_lblupd();
2081 dbprog_dcred_lblupd();
2082 dbprog_seg_lblupd();
2083 }
2084
2085
2086 /*
2087 * dbprog_exttupd
2088 * Update the state of the track details window widgets to match
2089 * that of the cdinfo_incore_t structure for the specified track.
2090 *
2091 * Args:
2092 * s - Pointer to the curstat_t structure.
2093 * trkpos - Track position.
2094 *
2095 * Return:
2096 * Nothing.
2097 */
2098 STATIC void
dbprog_exttupd(curstat_t * s,int trkpos)2099 dbprog_exttupd(curstat_t *s, int trkpos)
2100 {
2101 char *p;
2102 XmString xs;
2103
2104 /* Set track details track title */
2105 dbprog_set_track_title(
2106 s->trkinfo[trkpos].trkno,
2107 dbp->track[trkpos].title
2108 );
2109
2110 /* Sort title */
2111 if (dbp->track[trkpos].sorttitle != NULL) {
2112 p = get_text_string(widgets.dbextt.sorttitle_txt, TRUE);
2113 if (p == NULL || strcmp(p, dbp->track[trkpos].sorttitle) != 0){
2114 set_text_string(
2115 widgets.dbextt.sorttitle_txt,
2116 dbp->track[trkpos].sorttitle,
2117 TRUE
2118 );
2119 }
2120 if (p != NULL)
2121 MEM_FREE(p);
2122 }
2123 else
2124 set_text_string(widgets.dbextt.sorttitle_txt, "", FALSE);
2125
2126 /* Title "The" */
2127 if (dbp->track[trkpos].title_the != NULL) {
2128 p = get_text_string(widgets.dbextt.the_txt, TRUE);
2129 if (p == NULL || strcmp(p, dbp->track[trkpos].title_the) != 0){
2130 set_text_string(widgets.dbextt.the_txt,
2131 dbp->track[trkpos].title_the,
2132 TRUE);
2133 XmToggleButtonSetState(widgets.dbextt.the_btn,
2134 True, False);
2135 XtSetSensitive(widgets.dbextt.the_txt, True);
2136 }
2137 if (p != NULL)
2138 MEM_FREE(p);
2139 }
2140 else {
2141 set_text_string(widgets.dbextt.the_txt, "", FALSE);
2142 XmToggleButtonSetState(widgets.dbextt.the_btn, False, False);
2143 XtSetSensitive(widgets.dbextt.the_txt, False);
2144 }
2145
2146 /* Artist */
2147 if (dbp->track[trkpos].artist != NULL) {
2148 p = get_text_string(widgets.dbextt.artist_txt, TRUE);
2149 if (p == NULL || strcmp(p, dbp->track[trkpos].artist) != 0) {
2150 set_text_string(
2151 widgets.dbextt.artist_txt,
2152 dbp->track[trkpos].artist,
2153 TRUE
2154 );
2155 XtVaSetValues(widgets.dbextt.artist_txt,
2156 XmNcursorPosition, 0,
2157 NULL
2158 );
2159 }
2160 if (p != NULL)
2161 MEM_FREE(p);
2162 }
2163 else
2164 set_text_string(widgets.dbextt.artist_txt, "", FALSE);
2165
2166 /* Year */
2167 if (dbp->track[trkpos].year != NULL) {
2168 p = get_text_string(widgets.dbextt.year_txt, TRUE);
2169 if (p == NULL || strcmp(p, dbp->track[trkpos].year) != 0) {
2170 set_text_string(
2171 widgets.dbextt.year_txt,
2172 dbp->track[trkpos].year,
2173 TRUE
2174 );
2175 }
2176 if (p != NULL)
2177 MEM_FREE(p);
2178 }
2179 else
2180 set_text_string(widgets.dbextt.year_txt, "", FALSE);
2181
2182 /* Label */
2183 if (dbp->track[trkpos].label != NULL) {
2184 p = get_text_string(widgets.dbextt.label_txt, TRUE);
2185 if (p == NULL || strcmp(p, dbp->track[trkpos].label) != 0) {
2186 set_text_string(
2187 widgets.dbextt.label_txt,
2188 dbp->track[trkpos].label,
2189 TRUE
2190 );
2191 }
2192 if (p != NULL)
2193 MEM_FREE(p);
2194 }
2195 else
2196 set_text_string(widgets.dbextt.label_txt, "", FALSE);
2197
2198 /* BPM */
2199 if (dbp->track[trkpos].bpm != NULL) {
2200 p = get_text_string(widgets.dbextt.bpm_txt, TRUE);
2201 if (p == NULL || strcmp(p, dbp->track[trkpos].bpm) != 0) {
2202 set_text_string(
2203 widgets.dbextt.bpm_txt,
2204 dbp->track[trkpos].bpm,
2205 TRUE
2206 );
2207 }
2208 if (p != NULL)
2209 MEM_FREE(p);
2210 }
2211 else
2212 set_text_string(widgets.dbextt.bpm_txt, "", FALSE);
2213
2214 /* Genres and subgenres */
2215 dbprog_genreupd(trkpos);
2216
2217 /* Track notes */
2218 if (dbp->track[trkpos].notes != NULL) {
2219 p = get_text_string(widgets.dbextt.notes_txt, TRUE);
2220 if (p == NULL || strcmp(p, dbp->track[trkpos].notes) != 0) {
2221 set_text_string(
2222 widgets.dbextt.notes_txt,
2223 dbp->track[trkpos].notes,
2224 TRUE
2225 );
2226 }
2227 if (p != NULL)
2228 MEM_FREE(p);
2229 }
2230 else
2231 set_text_string(widgets.dbextt.notes_txt, "", FALSE);
2232
2233 /* ISRC */
2234 xs = create_xmstring(
2235 dbp->track[trkpos].isrc, "-", XmSTRING_DEFAULT_CHARSET, TRUE
2236 );
2237 XtVaSetValues(widgets.dbextt.isrc_ind,
2238 XmNlabelString, xs,
2239 NULL
2240 );
2241 XmStringFree(xs);
2242 }
2243
2244
2245 /*
2246 * dbprog_structupd
2247 * Update the state of the various widgets fields in the
2248 * CD info/program window to match that of the cdinfo_incore_t structure.
2249 *
2250 * Args:
2251 * s - Pointer to the curstat_t structure.
2252 *
2253 * Return:
2254 * Nothing.
2255 */
2256 STATIC void
dbprog_structupd(curstat_t * s)2257 dbprog_structupd(curstat_t *s)
2258 {
2259 char *p;
2260
2261 /* Total time */
2262 dbprog_dpytottime(s);
2263
2264 /* Disc artist */
2265 if (dbp->disc.artist != NULL) {
2266 p = get_text_string(widgets.dbprog.artist_txt, TRUE);
2267 if (p == NULL || strcmp(p, dbp->disc.artist) != 0) {
2268 set_text_string(widgets.dbprog.artist_txt,
2269 dbp->disc.artist,
2270 TRUE);
2271 XtVaSetValues(widgets.dbprog.artist_txt,
2272 XmNcursorPosition, 0,
2273 NULL
2274 );
2275 }
2276 if (p != NULL)
2277 MEM_FREE(p);
2278 }
2279 else {
2280 set_text_string(widgets.dbprog.artist_txt, "", FALSE);
2281 }
2282
2283 /* Disc title */
2284 if (dbp->disc.title != NULL) {
2285 p = get_text_string(widgets.dbprog.title_txt, TRUE);
2286 if (p == NULL || strcmp(p, dbp->disc.title) != 0) {
2287 set_text_string(widgets.dbprog.title_txt,
2288 dbp->disc.title,
2289 TRUE);
2290 }
2291 if (p != NULL)
2292 MEM_FREE(p);
2293 }
2294 else {
2295 set_text_string(widgets.dbprog.title_txt, "", FALSE);
2296 }
2297
2298 /* Track title list */
2299 sel_pos = -1;
2300 dbprog_listupd_all(s, TRUE);
2301
2302 /* Program sequence */
2303 if (dbp->playorder != NULL && !s->onetrk_prog) {
2304 p = get_text_string(widgets.dbprog.pgmseq_txt, FALSE);
2305 if (p == NULL || strcmp(p, dbp->playorder) != 0) {
2306 char *str;
2307 int n;
2308
2309 set_text_string(widgets.dbprog.pgmseq_txt,
2310 dbp->playorder,
2311 FALSE);
2312
2313 str = XmTextGetString(widgets.dbprog.pgmseq_txt);
2314 n = strlen(str);
2315 XtFree(str);
2316
2317 XmTextSetInsertionPosition(
2318 widgets.dbprog.pgmseq_txt,
2319 n
2320 );
2321 }
2322 if (p != NULL)
2323 MEM_FREE(p);
2324 XtSetSensitive(widgets.dbprog.clrpgm_btn, True);
2325 }
2326 else {
2327 set_text_string(widgets.dbprog.pgmseq_txt, "", FALSE);
2328 XtSetSensitive(widgets.dbprog.clrpgm_btn, False);
2329 XtSetSensitive(widgets.dbprog.savepgm_btn, False);
2330 }
2331
2332 /* Update the disc details window widgets */
2333 dbprog_extdupd(s);
2334
2335 /* Update button labels */
2336 dbprog_extt_lblupd(NULL);
2337 dbprog_tcred_lblupd(NULL);
2338
2339 /* Update segments window if needed */
2340 dbprog_segmentupd(s);
2341
2342 /* Update credits window if needed */
2343 switch (credits_mode) {
2344 case CREDITS_DISC:
2345 case CREDITS_SEG:
2346 dbprog_creditupd(s, -1);
2347 break;
2348 case CREDITS_TRACK:
2349 dbprog_creditupd(s, extt_pos);
2350 break;
2351 default:
2352 break;
2353 }
2354
2355 /* Note: extt, credits and fullname window widgets are updated when
2356 * the user pops up these windows, repectively.
2357 */
2358
2359 dbp->flags &= ~CDINFO_CHANGED;
2360 XtSetSensitive(widgets.dbprog.submit_btn, False);
2361 XtSetSensitive(widgets.dbprog.addpgm_btn, False);
2362 XtSetSensitive(widgets.dbprog.fullname_btn, True);
2363 XtSetSensitive(widgets.dbprog.extd_btn, True);
2364 XtSetSensitive(widgets.dbprog.dcredits_btn, True);
2365 XtSetSensitive(widgets.dbprog.segments_btn, True);
2366 XtSetSensitive(widgets.dbprog.extt_btn, False);
2367 XtSetSensitive(widgets.dbprog.tcredits_btn, False);
2368
2369 /* Update display */
2370 dpy_dbmode(s, FALSE);
2371 }
2372
2373
2374 /*
2375 * dbprog_do_submit_popup
2376 * Pop up the CDDB submit prompt dialog.
2377 *
2378 * Args:
2379 * None
2380 *
2381 * Return:
2382 * Nothing
2383 */
2384 STATIC void
dbprog_do_submit_popup(void)2385 dbprog_do_submit_popup(void)
2386 {
2387 char *msg;
2388
2389 msg = (char *) MEM_ALLOC(
2390 "msg",
2391 strlen(app_data.str_chgsubmit) +
2392 (dbp->disc.artist == NULL ? 0 : strlen(dbp->disc.artist)) +
2393 (dbp->disc.title == NULL ? 0 : strlen(dbp->disc.title)) + 4
2394 );
2395
2396 if (msg == NULL) {
2397 CD_FATAL(app_data.str_nomemory);
2398 return;
2399 }
2400
2401 (void) sprintf(msg, app_data.str_chgsubmit,
2402 dbp->disc.artist == NULL ?
2403 app_data.str_unknartist : dbp->disc.artist,
2404 dbp->disc.title == NULL ?
2405 app_data.str_unkndisc : dbp->disc.title);
2406
2407 /* Pop-up the dialog box */
2408 (void) cd_confirm_popup(
2409 app_data.str_confirm, msg,
2410 dbprog_do_clear, (XtPointer) STAT_SUBMIT,
2411 dbprog_do_clear, (XtPointer) 0
2412 );
2413
2414 MEM_FREE(msg);
2415 }
2416
2417
2418 /*
2419 * dbprog_dbsubmit
2420 * Submit current CD info to CDDB server.
2421 *
2422 * Args:
2423 * s - Pointer to the curstat_t structure.
2424 *
2425 * Return:
2426 * TRUE - success
2427 * FALSE - failure
2428 */
2429 STATIC bool_t
dbprog_dbsubmit(curstat_t * s)2430 dbprog_dbsubmit(curstat_t *s)
2431 {
2432 cdinfo_ret_t ret;
2433 bool_t err;
2434
2435 /* Check fullname for correctness, if needed */
2436 if (!dbprog_fullname_ck(s))
2437 return FALSE;
2438
2439 /* Change to the watch cursor */
2440 cd_busycurs(TRUE, CURS_ALL);
2441
2442 /* Configure the wwwWarp menu */
2443 wwwwarp_sel_cfg(s);
2444
2445 /* Submit the CD information */
2446 if ((ret = cdinfo_submit(s)) != 0) {
2447 DBGPRN(DBG_CDI)(errfp, "cdinfo_submit: status=%d arg=%d\n",
2448 CDINFO_GET_STAT(ret), CDINFO_GET_ARG(ret));
2449 }
2450
2451 /* Change to the normal cursor */
2452 cd_busycurs(FALSE, CURS_ALL);
2453
2454 switch (CDINFO_GET_STAT(ret)) {
2455 case 0:
2456 if ((dbp->flags & CDINFO_NEEDREG) != 0) {
2457 err = TRUE;
2458
2459 /* User not registered with CDDB:
2460 * Pop up CDDB user registration dialog
2461 */
2462 userreg_do_popup(s, TRUE);
2463 }
2464 else {
2465 err = FALSE;
2466
2467 /* Clear changed flag */
2468 dbp->flags &= ~CDINFO_CHANGED;
2469
2470 /* Make the submit button insensitive */
2471 XtSetSensitive(widgets.dbprog.submit_btn, False);
2472
2473 CD_INFO_AUTO(app_data.str_submitok);
2474 }
2475 break;
2476
2477 case SUBMIT_ERR:
2478 default:
2479 err = TRUE;
2480
2481 CD_INFO(app_data.str_submiterr);
2482 break;
2483 }
2484
2485 if (XtIsManaged(widgets.dbprog.form))
2486 /* Put focus on the OK button */
2487 XmProcessTraversal(widgets.dbprog.ok_btn, XmTRAVERSE_CURRENT);
2488
2489 return (!err);
2490 }
2491
2492
2493 /*
2494 * dbprog_pgm_active
2495 * Indicate whether a play program is currently defined.
2496 *
2497 * Args:
2498 * None.
2499 *
2500 * Return:
2501 * TRUE = program is active,
2502 * FALSE = program is not active.
2503 */
2504 STATIC bool_t
dbprog_pgm_active(void)2505 dbprog_pgm_active(void)
2506 {
2507 return (dbp->playorder != NULL && dbp->playorder[0] != '\0');
2508 }
2509
2510
2511 /*
2512 * dbprog_hist_addent
2513 * Add a new entry to the disc list widget in history mode.
2514 *
2515 * Args:
2516 * hp - Pointer to the associated cdinfo_dlist_t structure.
2517 * s - Pointer to the curstat_t structure.
2518 * pos - The list position to add to.
2519 *
2520 * Return:
2521 * Nothing.
2522 */
2523 /*ARGSUSED*/
2524 STATIC void
dbprog_hist_addent(cdinfo_dlist_t * hp,curstat_t * s,int pos)2525 dbprog_hist_addent(cdinfo_dlist_t *hp, curstat_t *s, int pos)
2526 {
2527 struct tm *tm;
2528 XmString xs;
2529 char *cp,
2530 str[DLIST_BUF_SZ + STR_BUF_SZ];
2531
2532 #if !defined(__VMS) || ((__VMS_VER >= 70000000) && (__DECC_VER > 50230003))
2533 tzset();
2534 #endif
2535 tm = localtime(&hp->time);
2536 cp = util_monname(tm->tm_mon);
2537
2538 (void) sprintf(str, HISTLIST_FMT,
2539 cp,
2540 tm->tm_mday,
2541 tm->tm_hour,
2542 tm->tm_min,
2543 (hp->artist == NULL) ? "" : hp->artist,
2544 (hp->artist != NULL && hp->title != NULL) ? " / " : "",
2545 (hp->title == NULL) ? "-" : hp->title
2546 );
2547
2548 xs = create_xmstring(str, NULL, CHSET1, TRUE);
2549
2550 XmListAddItemUnselected(widgets.dlist.disc_list, xs, pos);
2551
2552 XmStringFree(xs);
2553
2554 /* Check against max history limit */
2555 if (++hist_cnt > app_data.cdinfo_maxhist) {
2556 XmListDeletePos(widgets.dlist.disc_list, hist_cnt);
2557 hist_cnt--;
2558 }
2559 }
2560
2561
2562 /*
2563 * dbprog_hist_addall
2564 * Make the disc list widget display the in-core history list.
2565 *
2566 * Args:
2567 * s - Pointer to the curstat_t structure.
2568 *
2569 * Return:
2570 * Nothing.
2571 */
2572 STATIC void
dbprog_hist_addall(curstat_t * s)2573 dbprog_hist_addall(curstat_t *s)
2574 {
2575 int i;
2576 cdinfo_dlist_t *hp;
2577
2578 /* Put the history list in the list widget */
2579 for (i = 1, hp = cdinfo_hist_list(); hp != NULL; hp = hp->next, i++) {
2580 dbprog_hist_addent(hp, s, i);
2581
2582 if (i == 1)
2583 XtSetSensitive(widgets.dlist.delall_btn, True);
2584 }
2585 }
2586
2587
2588 /*
2589 * dbprog_hist_new
2590 * Add current CD to the history list.
2591 *
2592 * Args:
2593 * s - Pointer to the curstat_t structure.
2594 *
2595 * Return:
2596 * Nothing.
2597 */
2598 STATIC void
dbprog_hist_new(curstat_t * s)2599 dbprog_hist_new(curstat_t *s)
2600 {
2601 cdinfo_dlist_t h1,
2602 h2,
2603 *hp;
2604
2605 if (s->mode == MOD_NODISC || s->mode == MOD_BUSY || s->chgrscan)
2606 /* Don't add to history if no disc or changer scanning */
2607 return;
2608
2609 h1.device = s->curdev;
2610 h1.discno = (int) s->cur_disc;
2611 h1.type = (s->qmode != QMODE_MATCH) ? 0 :
2612 ((dbp->flags & (CDINFO_FROMLOC | CDINFO_FROMCDT)) ?
2613 CDINFO_DLIST_LOCAL : CDINFO_DLIST_REMOTE);
2614 h1.discid = dbp->discid;
2615 h1.genre = dbp->disc.genre;
2616 h1.artist = dbp->disc.artist;
2617 h1.title = dbp->disc.title;
2618 h1.time = time(NULL);
2619
2620 /* Save a copy of the old entry for comparison later */
2621 if ((hp = cdinfo_hist_list()) != NULL)
2622 h2 = *hp; /* Structure copy */
2623 else
2624 (void) memset(&h2, 0, sizeof(cdinfo_dlist_t));
2625
2626 /* Add to in-core history list */
2627 (void) cdinfo_hist_addent(&h1, TRUE);
2628
2629 /* If in history mode, add to disc list widget */
2630 if (dlist_mode == DLIST_HIST) {
2631 if (hist_initted && h1.discid != h2.discid) {
2632 /* Add entry to the list widget,
2633 * if it's different than what's
2634 * already there.
2635 */
2636 dbprog_hist_addent(&h1, s, 1);
2637 }
2638
2639 if (!XtIsSensitive(widgets.dlist.delall_btn))
2640 XtSetSensitive(widgets.dlist.delall_btn, True);
2641 }
2642 }
2643
2644
2645 /*
2646 * dbprog_chgr_scan_next
2647 * Scan next slot of the CD changer.
2648 *
2649 * Args:
2650 * s - Pointer to the curstat_t structure.
2651 *
2652 * Return:
2653 * Nothing.
2654 */
2655 STATIC void
dbprog_chgr_scan_next(curstat_t * s)2656 dbprog_chgr_scan_next(curstat_t *s)
2657 {
2658 scan_id = -1;
2659
2660 if (scan_slot < 0)
2661 return; /* Error */
2662
2663 /* Skip forward to the next loaded slot */
2664 if (scan_slot > 0 && s->cur_disc > scan_slot)
2665 scan_slot = s->cur_disc;
2666
2667 if (++scan_slot > app_data.numdiscs) {
2668 /* Done scanning */
2669 scan_slot = -1;
2670 dbprog_chgr_scan_stop(s);
2671 return;
2672 }
2673
2674 /* Go to next slot */
2675 s->cur_disc = scan_slot;
2676
2677 s->flags |= STAT_CHGDISC;
2678
2679 /* Ask the user if the changed CD information
2680 * should be submitted to CDDB.
2681 */
2682 if (!dbprog_chgsubmit(s))
2683 return;
2684
2685 s->flags &= ~STAT_CHGDISC;
2686
2687 /* Do the disc change */
2688 di_chgdisc(s);
2689
2690 /* Update display */
2691 dpy_dbmode(s, FALSE);
2692 dpy_playmode(s, FALSE);
2693 }
2694
2695
2696 /*
2697 * dbprog_chgr_addent
2698 * Add or update an entry in the disc list widget in CD changer mode
2699 *
2700 * Args:
2701 * cp - Pointer to the associated cdinfo_dlist_t structure.
2702 * s - Pointer to the curstat_t structure.
2703 * pos - List position to add to.
2704 *
2705 * Return:
2706 * Nothing.
2707 */
2708 STATIC void
dbprog_chgr_addent(cdinfo_dlist_t * cp,curstat_t * s,int pos)2709 dbprog_chgr_addent(cdinfo_dlist_t *cp, curstat_t *s, int pos)
2710 {
2711 XmString xs;
2712 char str[DLIST_BUF_SZ + STR_BUF_SZ];
2713
2714 (void) sprintf(str, CHGRLIST_FMT,
2715 cp->discno,
2716 (cp->artist == NULL) ? "" : cp->artist,
2717 (cp->artist != NULL && cp->title != NULL) ? " / " : "",
2718 (cp->title == NULL) ? "-" : cp->title
2719 );
2720
2721 xs = create_xmstring(
2722 str, NULL, (pos == s->cur_disc) ? CHSET2 : CHSET1, TRUE
2723 );
2724
2725 if (cp->aux == NULL) {
2726 /* New entry */
2727 cp->aux = (void *) widgets.dlist.disc_list;
2728 XmListAddItemUnselected(widgets.dlist.disc_list, xs, pos);
2729 }
2730 else {
2731 /* Replace existing entry */
2732 XmListReplaceItemsPos(widgets.dlist.disc_list, &xs, 1, pos);
2733 }
2734
2735 XmStringFree(xs);
2736
2737 if (dlist_pos >= 0)
2738 /* This entry was previously selected */
2739 XmListSelectPos(widgets.dlist.disc_list, dlist_pos, False);
2740 }
2741
2742
2743 /*
2744 * dbprog_chgr_addall
2745 * Make the disc list widget display the in-core CD changer list.
2746 *
2747 * Args:
2748 * s- Pointer to the curstat_t structure.
2749 *
2750 * Return:
2751 * Nothing.
2752 */
2753 STATIC void
dbprog_chgr_addall(curstat_t * s)2754 dbprog_chgr_addall(curstat_t *s)
2755 {
2756 cdinfo_dlist_t *cp;
2757
2758 /* Put the CD changer list in the list widget */
2759 for (cp = cdinfo_chgr_list(); cp != NULL; cp = cp->next) {
2760 cp->aux = NULL; /* Denote new entry */
2761
2762 dbprog_chgr_addent(cp, s, cp->discno);
2763 }
2764
2765 dbprog_list_autoscroll(
2766 widgets.dlist.disc_list,
2767 (int) app_data.numdiscs,
2768 s->cur_disc
2769 );
2770 }
2771
2772
2773 /*
2774 * dbprog_chgr_new
2775 * Add current CD to the CD changer list.
2776 *
2777 * Args:
2778 * s - Pointer to the curstat_t structure.
2779 *
2780 * Return:
2781 * Nothing.
2782 */
2783 STATIC void
dbprog_chgr_new(curstat_t * s)2784 dbprog_chgr_new(curstat_t *s)
2785 {
2786 int i;
2787 cdinfo_dlist_t ch,
2788 *cp;
2789
2790 if (s->cur_disc == 0)
2791 return;
2792
2793 ch.device = s->curdev;
2794 ch.discno = (int) s->cur_disc;
2795 ch.type = (s->qmode != QMODE_MATCH) ? 0 :
2796 ((dbp->flags & (CDINFO_FROMLOC | CDINFO_FROMCDT)) ?
2797 CDINFO_DLIST_LOCAL : CDINFO_DLIST_REMOTE);
2798 ch.discid = dbp->discid;
2799 ch.genre = dbp->disc.genre;
2800 ch.artist = dbp->disc.artist;
2801 ch.title = dbp->disc.title;
2802 ch.time = time(NULL);
2803
2804 /* Add to in-core CD changer list */
2805 (void) cdinfo_chgr_addent(&ch);
2806
2807 if (dlist_mode != DLIST_CHGR)
2808 return;
2809
2810 /* If in changer mode, add to disc list widget */
2811 for (i = 1, cp = cdinfo_chgr_list(); cp != NULL; cp = cp->next, i++)
2812 dbprog_chgr_addent(cp, s, i);
2813
2814 /* Scroll to make current disc visible if no item
2815 * of the list is currently selected.
2816 */
2817 if (dlist_pos < 0) {
2818 dbprog_list_autoscroll(
2819 widgets.dlist.disc_list,
2820 (int) app_data.numdiscs,
2821 s->cur_disc
2822 );
2823 }
2824
2825 if (s->chgrscan && s->mode != MOD_NODISC && s->mode != MOD_BUSY) {
2826 /* Schedule for the next slot */
2827 scan_id = cd_timeout(
2828 1000,
2829 dbprog_chgr_scan_next,
2830 (byte_t *) s
2831 );
2832 }
2833 }
2834
2835
2836 /*
2837 * dbprog_matchsel_popup
2838 * Pop up the match selector window. Note that this function
2839 * does not return to the caller until a user response is received.
2840 *
2841 * Args:
2842 * s- Pointer to the curstat_t structure.
2843 *
2844 * Return:
2845 * None.
2846 */
2847 STATIC void
dbprog_matchsel_popup(curstat_t * s)2848 dbprog_matchsel_popup(curstat_t *s)
2849 {
2850 int i,
2851 genrestr_len;
2852 cdinfo_match_t *mp;
2853 char *str,
2854 *genrestr;
2855 XmString xs;
2856
2857 if (dbp->matchlist == NULL) {
2858 /* Shouldn't be calling this when matchlist is empty */
2859 cd_beep();
2860 return;
2861 }
2862
2863 /* Build matchsel list entries */
2864 mp = dbp->matchlist;
2865 for (i = 1; mp != NULL; i++, mp = mp->next) {
2866 if (mp->genre != NULL) {
2867 genrestr = cdinfo_genre_name(mp->genre);
2868 genrestr_len = strlen(genrestr);
2869 }
2870 else {
2871 genrestr = NULL;
2872 genrestr_len = 0;
2873 }
2874
2875 str = (char *) MEM_ALLOC("match_str",
2876 (mp->artist == NULL ?
2877 strlen(app_data.str_unknartist) :
2878 strlen(mp->artist)) +
2879 (mp->title == NULL ?
2880 strlen(app_data.str_unkndisc) :
2881 strlen(mp->title)) +
2882 genrestr_len + 8
2883 );
2884 if (str == NULL) {
2885 CD_FATAL(app_data.str_nomemory);
2886 return;
2887 }
2888
2889 (void) sprintf(str, MATCHLIST_FMT,
2890 mp->artist == NULL ?
2891 app_data.str_unknartist : mp->artist,
2892 mp->title == NULL ?
2893 app_data.str_unkndisc : mp->title,
2894 genrestr == NULL ? "" : "(",
2895 genrestr == NULL ? "" : genrestr,
2896 genrestr == NULL ? "" : ")"
2897 );
2898
2899 xs = create_xmstring(str, NULL, CHSET1, TRUE);
2900
2901 XmListAddItemUnselected(widgets.matchsel.matchsel_list, xs, i);
2902
2903 XmStringFree(xs);
2904 MEM_FREE(str);
2905 }
2906
2907 xs = create_xmstring(
2908 app_data.str_noneofabove,
2909 NULL,
2910 XmSTRING_DEFAULT_CHARSET,
2911 FALSE
2912 );
2913
2914 XmListAddItemUnselected(widgets.matchsel.matchsel_list, xs, i);
2915
2916 XmStringFree(xs);
2917
2918 match_cnt = i;
2919
2920 /* Configure the wwwWarp menu */
2921 wwwwarp_sel_cfg(s);
2922
2923 XtSetSensitive(widgets.matchsel.ok_btn, False);
2924
2925 /* The matchsel popup has mappedWhenManaged set to False,
2926 * so we have to map/unmap explicitly. The reason for this
2927 * is we want to avoid a screen glitch when we move the window
2928 * in cd_dialog_setpos(), so we map the window afterwards.
2929 */
2930 if (!XtIsManaged(widgets.matchsel.form)) {
2931 XtManageChild(widgets.matchsel.form);
2932
2933 /* Set up dialog box position */
2934 cd_dialog_setpos(XtParent(widgets.matchsel.form));
2935
2936 XtMapWidget(XtParent(widgets.matchsel.form));
2937 }
2938
2939 /* Handle X events locally to prevent from returning to the
2940 * caller until a user response has been received.
2941 */
2942 do {
2943 util_delayms(10);
2944 event_loop(0);
2945 } while (XtIsManaged(widgets.matchsel.form));
2946 }
2947
2948
2949 /*
2950 * dbprog_auth_popup
2951 * Pop up the proxy authorization dialog box. Note that this
2952 * function does not return to the caller until a user response
2953 * is received.
2954 *
2955 * Args:
2956 * None.
2957 *
2958 * Return:
2959 * Nothing.
2960 */
2961 STATIC void
dbprog_auth_popup(void)2962 dbprog_auth_popup(void)
2963 {
2964 /* Pop up authorization dialog.
2965 * The dialog has mappedWhenManaged set to False,
2966 * so we have to map/unmap explicitly. The reason
2967 * for this is we want to avoid a screen glitch when
2968 * we move the window in cd_dialog_setpos(), so we
2969 * map the window afterwards.
2970 */
2971 if (!XtIsManaged(widgets.auth.form)) {
2972 XtManageChild(widgets.auth.form);
2973
2974 /* Set up dialog box position */
2975 cd_dialog_setpos(XtParent(widgets.auth.form));
2976
2977 XtMapWidget(XtParent(widgets.auth.form));
2978 }
2979
2980 /* Set keyboard focus to the user name field */
2981 XmProcessTraversal(
2982 widgets.auth.name_txt,
2983 XmTRAVERSE_CURRENT
2984 );
2985
2986 /* Handle X events locally to prevent from returning to the caller
2987 * until we receive a response from the user.
2988 */
2989 do {
2990 util_delayms(10);
2991 event_loop(0);
2992 } while (XtIsManaged(widgets.auth.form));
2993 }
2994
2995
2996 /*
2997 * dbprog_autoname
2998 * Auto-generate the dispname of a fullname from the lastname,
2999 * firstname and the components.
3000 *
3001 * Args:
3002 * Pointer to the fullname structure.
3003 *
3004 * Return:
3005 * The generated text string. This string should be freed by the
3006 * caller via MEM_FREE when done. If memory allocation for the
3007 * generated string failed, this call will return NULL.
3008 */
3009 STATIC char *
dbprog_autoname(cdinfo_fname_t * fnp)3010 dbprog_autoname(cdinfo_fname_t *fnp)
3011 {
3012 char *str;
3013
3014 str = (char *) MEM_ALLOC("dispname",
3015 (fnp->the == NULL ? 0 : (strlen(fnp->the) + 1)) +
3016 (fnp->lastname == NULL ? 0 : (strlen(fnp->lastname) + 1)) +
3017 (fnp->firstname == NULL ? 0 : (strlen(fnp->firstname) + 1)) + 1
3018 );
3019 if (str == NULL)
3020 return NULL;
3021
3022 str[0] = '\0';
3023
3024 if (fnp->the != NULL)
3025 (void) sprintf(str, "%s ", fnp->the);
3026 if (fnp->firstname != NULL)
3027 (void) strcat(str, fnp->firstname);
3028 if (fnp->lastname != NULL)
3029 (void) sprintf(str, "%s%s%s",
3030 str,
3031 fnp->firstname == NULL ? "" : " ",
3032 fnp->lastname);
3033 return (str);
3034 }
3035
3036
3037 /***********************
3038 * public routines *
3039 ***********************/
3040
3041
3042 /*
3043 * dbprog_curfileupd
3044 * Update the curr.XXX file to show the current disc status.
3045 *
3046 * Args:
3047 * None.
3048 *
3049 * Return:
3050 * Nothing.
3051 */
3052 void
dbprog_curfileupd(void)3053 dbprog_curfileupd(void)
3054 {
3055 cdinfo_curfileupd();
3056 }
3057
3058
3059 /*
3060 * dbprog_curtrkupd
3061 * Update the track list display to show the current playing
3062 * track entry in bold font.
3063 *
3064 * Args:
3065 * s - Pointer to the curstat_t structure.
3066 *
3067 * Return:
3068 * Nothing.
3069 */
3070 void
dbprog_curtrkupd(curstat_t * s)3071 dbprog_curtrkupd(curstat_t *s)
3072 {
3073 int pos,
3074 list_pos;
3075 static int sav_pos = -1;
3076
3077 /* Update curfile */
3078 dbprog_curfileupd();
3079
3080 if (sav_pos >= 0) {
3081 /* Update track list entry: un-highlight previous track */
3082 dbprog_listupd_ent(
3083 s, sav_pos,
3084 (dbp->track[sav_pos].title != NULL) ?
3085 dbp->track[sav_pos].title : UNDEF_STR,
3086 FALSE
3087 );
3088
3089 if (sel_pos == (sav_pos + 1))
3090 /* This item is previously selected */
3091 XmListSelectPos(widgets.dbprog.trk_list,
3092 sel_pos, False);
3093 }
3094
3095 if (s->cur_trk <= 0 || s->mode == MOD_BUSY || s->mode == MOD_NODISC) {
3096 sav_pos = -1;
3097 return;
3098 }
3099
3100 sav_pos = pos = di_curtrk_pos(s);
3101 list_pos = pos + 1;
3102
3103 /* Update track list entry: highlight current track */
3104 dbprog_listupd_ent(
3105 s, sav_pos,
3106 (dbp->track[sav_pos].title != NULL) ?
3107 dbp->track[sav_pos].title : UNDEF_STR,
3108 FALSE
3109 );
3110
3111 /* If this item is previously selected, re-select it */
3112 if (sel_pos == list_pos)
3113 XmListSelectPos(widgets.dbprog.trk_list, sel_pos, False);
3114 else if (ind_pos == list_pos)
3115 XmListSelectPos(widgets.dbprog.trk_list, ind_pos, False);
3116
3117 /* Auto-scroll the track list if the current track is not visible.
3118 * No scrolling is done while a track list entry is selected
3119 * or if a track title is being edited.
3120 */
3121 if (sel_pos < 0 && ind_pos < 0) {
3122 dbprog_list_autoscroll(
3123 widgets.dbprog.trk_list,
3124 (int) s->tot_trks,
3125 list_pos
3126 );
3127 }
3128
3129 dbprog_extt_autotrk_upd(s, list_pos);
3130 }
3131
3132
3133 /*
3134 * dbprog_progclear
3135 * Clear program sequence.
3136 *
3137 * Args:
3138 * s - Pointer to the curstat_t structure.
3139 *
3140 * Return:
3141 * Nothing.
3142 */
3143 void
dbprog_progclear(curstat_t * s)3144 dbprog_progclear(curstat_t *s)
3145 {
3146 cdinfo_ret_t ret;
3147
3148 /* Delete track program file */
3149 if (!s->onetrk_prog && (ret = cdinfo_del_prog()) != 0) {
3150 DBGPRN(DBG_CDI)(errfp, "cdinfo_del_prog: status=%d arg=%d\n",
3151 CDINFO_GET_STAT(ret), CDINFO_GET_ARG(ret));
3152 }
3153
3154 if (dbp->playorder != NULL) {
3155 MEM_FREE(dbp->playorder);
3156 dbp->playorder = NULL;
3157 }
3158
3159 s->prog_tot = 0;
3160 s->prog_cnt = 0;
3161 s->program = FALSE;
3162
3163 /* Update display */
3164 dpy_progmode(s, FALSE);
3165 }
3166
3167
3168 /*
3169 * dbprog_dbclear
3170 * Clear in-core CD information.
3171 *
3172 * Args:
3173 * s - Pointer to the curstat_t structure.
3174 * reload - Whether we are going to be re-loading the CD information.
3175 *
3176 * Return:
3177 * Nothing.
3178 */
3179 void
dbprog_dbclear(curstat_t * s,bool_t reload)3180 dbprog_dbclear(curstat_t *s, bool_t reload)
3181 {
3182 int i;
3183 word16_t flags;
3184 bool_t upd_display = FALSE;
3185 static bool_t first_time = TRUE;
3186
3187 /* Pop down the fullname, track details and credits window (if in
3188 * track credit mode) if necessary
3189 */
3190 if (XtIsManaged(widgets.fullname.form)) {
3191 dbprog_fullname_ok(
3192 widgets.fullname.ok_btn,
3193 (XtPointer) s,
3194 (XtPointer) NULL
3195 );
3196 }
3197 if (XtIsManaged(widgets.dbextt.form)) {
3198 dbprog_extt_ok(
3199 widgets.dbextt.ok_btn,
3200 (XtPointer) s,
3201 (XtPointer) NULL
3202 );
3203 }
3204 if (credits_mode == CREDITS_TRACK) {
3205 dbprog_credits_ok(
3206 widgets.credits.ok_btn,
3207 (XtPointer) s,
3208 (XtPointer) NULL
3209 );
3210 }
3211
3212 if (s->flags & STAT_SUBMIT) {
3213 /* Submit in-core information to CDDB, if so designated */
3214 (void) dbprog_dbsubmit(s);
3215 }
3216
3217 if (first_time || s->mode == MOD_NODISC) {
3218 /* Pop down the disc details, credits, and segments window */
3219 if (XtIsManaged(widgets.dbextd.form)) {
3220 dbprog_extd_ok(
3221 widgets.dbextd.ok_btn,
3222 (XtPointer) s,
3223 (XtPointer) NULL
3224 );
3225 }
3226 if (credits_mode == CREDITS_DISC ||
3227 credits_mode == CREDITS_SEG) {
3228 dbprog_credits_ok(
3229 widgets.credits.ok_btn,
3230 (XtPointer) s,
3231 (XtPointer) NULL
3232 );
3233 }
3234 if (XtIsManaged(widgets.segments.form)) {
3235 dbprog_segments_ok(
3236 widgets.segments.ok_btn,
3237 (XtPointer) s,
3238 (XtPointer) NULL
3239 );
3240 }
3241
3242 first_time = FALSE;
3243 upd_display = TRUE;
3244 }
3245
3246 /* Save flags */
3247 flags = s->flags;
3248
3249 /* Clear album-specific URLs in the wwwWarp menu */
3250 if (!wwwwarp_cleared)
3251 wwwwarp_disc_url_clear(s);
3252
3253 /* Clear CD information structure */
3254 cdinfo_clear(reload);
3255
3256 /* Update CD changer list */
3257 dbprog_chgr_new(s);
3258
3259 /* Clear flags */
3260 s->flags &= ~(STAT_SUBMIT | STAT_EJECT | STAT_EXIT | STAT_CHGDISC);
3261
3262 /* Set qmode flag */
3263 s->qmode = QMODE_NONE;
3264
3265 /* Configure the wwwWarp menu */
3266 if (!wwwwarp_cleared)
3267 wwwwarp_sel_cfg(s);
3268
3269 /* Set the wwwwarp_cleared flag so that if this routine gets
3270 * called again before we successfully get CDDB data, we don't
3271 * go back into wwwwarp_disc_url_clear() or wwwwarp_sel_cfg()
3272 * which has the effect of popping down the wwwwarp menu.
3273 */
3274 wwwwarp_cleared = TRUE;
3275
3276 /* Reset private copy of track list data */
3277 for (i = 0; i < MAXTRACK; i++) {
3278 if (trklist_copy[i].title != NULL) {
3279 MEM_FREE(trklist_copy[i].title);
3280 trklist_copy[i].title = NULL;
3281 }
3282 trklist_copy[i].highlighted = FALSE;
3283 }
3284
3285 if (upd_display) {
3286 /* Update display */
3287 dbprog_dpytottime(s);
3288 dpy_dtitle(s);
3289 dpy_ttitle(s);
3290
3291 /* Update curfile */
3292 dbprog_curfileupd();
3293
3294 /* Update CD info/program display */
3295 set_text_string(widgets.dbprog.artist_txt, "", FALSE);
3296 set_text_string(widgets.dbprog.title_txt, "", FALSE);
3297 XmListDeleteAllItems(widgets.dbprog.trk_list);
3298 set_text_string(widgets.dbprog.pgmseq_txt, "", FALSE);
3299 set_text_string(widgets.dbextd.notes_txt, "", FALSE);
3300 set_text_string(widgets.dbextt.notes_txt, "", FALSE);
3301
3302 /* Make some buttons insensitive */
3303 XtSetSensitive(widgets.dbprog.submit_btn, False);
3304 XtSetSensitive(widgets.dbprog.reload_btn, False);
3305 XtSetSensitive(widgets.dbprog.fullname_btn, False);
3306 XtSetSensitive(widgets.dbprog.extd_btn, False);
3307 XtSetSensitive(widgets.dbprog.dcredits_btn, False);
3308 XtSetSensitive(widgets.dbprog.segments_btn, False);
3309 XtSetSensitive(widgets.dbprog.extt_btn, False);
3310 XtSetSensitive(widgets.dbprog.tcredits_btn, False);
3311
3312 /* Update button labels */
3313 dbprog_extt_lblupd(NULL);
3314 dbprog_tcred_lblupd(NULL);
3315 }
3316
3317 /* Clear track title editor field in all cases */
3318 set_text_string(widgets.dbprog.ttitle_txt, "", FALSE);
3319
3320 /* Clear changed flag */
3321 dbp->flags &= ~CDINFO_CHANGED;
3322
3323 /* Eject the CD, if so specified */
3324 if (flags & STAT_EJECT) {
3325 cd_busycurs(TRUE, CURS_ALL);
3326
3327 di_load_eject(s);
3328
3329 cd_busycurs(FALSE, CURS_ALL);
3330 }
3331
3332 /* Change disc, if so specified */
3333 if (flags & STAT_CHGDISC) {
3334 if (!s->chgrscan)
3335 cd_busycurs(TRUE, CURS_ALL);
3336
3337 di_chgdisc(s);
3338
3339 /* Update display */
3340 dpy_dbmode(s, FALSE);
3341 dpy_playmode(s, FALSE);
3342
3343 if (!s->chgrscan)
3344 cd_busycurs(FALSE, CURS_ALL);
3345 }
3346
3347 /* Quit the application, if so specified */
3348 if (flags & STAT_EXIT)
3349 cd_quit(s);
3350 }
3351
3352
3353 /*
3354 * dbprog_progget
3355 * Get saved track program from file if available.
3356 *
3357 * Args:
3358 * s - Pointer to the curstat_t structure.
3359 *
3360 * Return:
3361 * Nothing.
3362 */
3363 void
dbprog_progget(curstat_t * s)3364 dbprog_progget(curstat_t *s)
3365 {
3366 cdinfo_ret_t ret;
3367
3368 /* Load user-defined track program, if available */
3369 if ((ret = cdinfo_load_prog(s)) != 0) {
3370 DBGPRN(DBG_CDI)(errfp, "cdinfo_load_prog: status=%d arg=%d\n",
3371 CDINFO_GET_STAT(ret), CDINFO_GET_ARG(ret));
3372 }
3373
3374 s->program = dbprog_pgm_active();
3375
3376 /* Update widgets */
3377 dbprog_structupd(s);
3378
3379 /* Just loaded from file, so no need to enable the save button
3380 * until the program sequence is changed.
3381 */
3382 XtSetSensitive(widgets.dbprog.savepgm_btn, False);
3383
3384 /* Parse playorder string */
3385 dbprog_pgm_parse(s);
3386
3387 /* Update display */
3388 dpy_progmode(s, FALSE);
3389 }
3390
3391
3392 /*
3393 * dbprog_dbget
3394 * Look up information about the currently loaded disc, if available.
3395 *
3396 * Args:
3397 * s - Pointer to the curstat_t structure.
3398 *
3399 * Return:
3400 * Nothing.
3401 */
3402 void
dbprog_dbget(curstat_t * s)3403 dbprog_dbget(curstat_t *s)
3404 {
3405 cdinfo_ret_t ret;
3406 static bool_t do_motd = TRUE;
3407
3408 /* Clear the in-core entry */
3409 dbprog_dbclear(s, TRUE);
3410
3411 if (s->onetrk_prog) {
3412 /* Currently playing a single-track program: clear it first */
3413 s->onetrk_prog = FALSE;
3414 dbprog_progclear(s);
3415 }
3416
3417 /* Set qmode flag */
3418 s->qmode = QMODE_WAIT;
3419
3420 /* Update widgets */
3421 dbprog_structupd(s);
3422 dpy_dtitle(s);
3423 dpy_ttitle(s);
3424
3425 /* Configure the wwwWarp menu */
3426 wwwwarp_sel_cfg(s);
3427
3428 /* Update curfile */
3429 dbprog_curfileupd();
3430
3431 XtSetSensitive(widgets.dbprog.reload_btn, False);
3432
3433 /* If in synchronous mode, change to watch cursor, otherwise,
3434 * force normal cursor.
3435 */
3436 if (!s->chgrscan)
3437 cd_busycurs(cdinfo_issync(), CURS_ALL);
3438
3439 /* Load CD information */
3440 if ((ret = cdinfo_load(s)) != 0) {
3441 DBGPRN(DBG_CDI)(errfp, "cdinfo_load: status=%d arg=%d\n",
3442 CDINFO_GET_STAT(ret), CDINFO_GET_ARG(ret));
3443 }
3444
3445 /* Change to normal cursor */
3446 if (!s->chgrscan && cdinfo_issync())
3447 cd_busycurs(FALSE, CURS_ALL);
3448
3449 if (stopload_active) {
3450 /* Pop down the stop load dialog */
3451 cd_confirm_popdown();
3452 stopload_active = FALSE;
3453 }
3454
3455 switch (CDINFO_GET_STAT(ret)) {
3456 case 0:
3457 /* Success */
3458 if (dbp->matchlist != NULL) {
3459 cdinfo_match_t *mp;
3460 int n = 0;
3461
3462 for (mp = dbp->matchlist; mp != NULL; mp = mp->next)
3463 n++;
3464
3465 if (n == 1 && app_data.single_fuzzy) {
3466 dbp->match_tag = 1;
3467 dbprog_matchsel_ok(
3468 widgets.matchsel.ok_btn,
3469 (XtPointer) s,
3470 NULL
3471 );
3472
3473 CD_INFO_AUTO(app_data.str_submitcorr);
3474 return;
3475 }
3476
3477 /* Multiple matches found */
3478 s->qmode = QMODE_NONE;
3479
3480 /* Pop up dialog */
3481 dbprog_matchsel_popup(s);
3482 return;
3483 }
3484 else if ((dbp->flags & CDINFO_NEEDREG) != 0) {
3485 /* User not registered with CDDB */
3486 s->qmode = QMODE_NONE;
3487
3488 /* Pop up CDDB user registration dialog */
3489 userreg_do_popup(s, TRUE);
3490 }
3491 else if ((dbp->flags & CDINFO_MATCH) != 0) {
3492 /* Exact match found */
3493 s->qmode = QMODE_MATCH;
3494
3495 /* Got CDDB data, clear the wwwwarp_cleared flag */
3496 wwwwarp_cleared = FALSE;
3497 }
3498 else
3499 s->qmode = QMODE_NONE;
3500
3501 break;
3502
3503 case AUTH_ERR:
3504 /* Authorization failed */
3505
3506 /* Set qmode flag */
3507 s->qmode = QMODE_NONE;
3508
3509 if (auth_initted) {
3510 (void) cd_confirm_popup(
3511 app_data.str_confirm, app_data.str_authfail,
3512 dbprog_auth_retry, (XtPointer) 1,
3513 dbprog_auth_retry, NULL
3514 );
3515 }
3516 else {
3517 /* Clear out user name and password */
3518 set_text_string(widgets.auth.name_txt, "", FALSE);
3519 set_text_string(widgets.auth.pass_txt, "", FALSE);
3520 if (dbp->proxy_passwd != NULL) {
3521 (void) memset(dbp->proxy_passwd, 0,
3522 strlen(dbp->proxy_passwd));
3523 MEM_FREE(dbp->proxy_passwd);
3524 dbp->proxy_passwd = NULL;
3525 }
3526
3527 /* Pop up authorization dialog */
3528 dbprog_auth_popup();
3529 }
3530 return;
3531 /*NOTREACHED*/
3532
3533 default:
3534 /* Query error */
3535
3536 /* Set qmode flag */
3537 s->qmode = QMODE_ERR;
3538 break;
3539 }
3540
3541 /* Update widgets */
3542 dbprog_structupd(s);
3543
3544 /* Configure the wwwWarp menu */
3545 wwwwarp_sel_cfg(s);
3546
3547 XtSetSensitive(widgets.dbprog.reload_btn, True);
3548
3549 /* Update display */
3550 dpy_dtitle(s);
3551 dpy_ttitle(s);
3552
3553 /* Update curfile */
3554 dbprog_curfileupd();
3555
3556 /* Add to history and changer lists */
3557 dbprog_hist_new(s);
3558 dbprog_chgr_new(s);
3559
3560 /* Get xmcd MOTD: do this automatically just once if configured */
3561 if (!app_data.automotd_dsbl && !app_data.cdinfo_inetoffln && do_motd) {
3562 do_motd = FALSE;
3563 motd_get(NULL);
3564 }
3565 }
3566
3567
3568 /*
3569 * dbprog_chgr_scan_stop
3570 * Stop the CD changer scan.
3571 *
3572 * Args:
3573 * s - Pointer to the curstat_t structure.
3574 *
3575 * Return:
3576 * Nothing.
3577 */
3578 /*ARGSUSED*/
3579 void
dbprog_chgr_scan_stop(curstat_t * s)3580 dbprog_chgr_scan_stop(curstat_t *s)
3581 {
3582 if (!s->chgrscan)
3583 return;
3584
3585 if (scan_id >= 0) {
3586 cd_untimeout(scan_id);
3587 scan_id = -1;
3588 }
3589
3590 /* Restore original multiplay and reverse modes */
3591 app_data.multi_play = sav_mplay;
3592 app_data.reverse = sav_rev;
3593
3594 s->chgrscan = FALSE;
3595
3596 if (scan_slot >= 0) {
3597 /* The user clicked "stop", add the current disc to
3598 * the history list.
3599 */
3600 dbprog_hist_new(s);
3601 scan_slot = -1;
3602 }
3603 else if (s->cur_disc != start_slot) {
3604 /* Go back to original slot */
3605 if (s->mode != MOD_NODISC && s->mode != MOD_BUSY)
3606 s->prev_disc = s->cur_disc;
3607 s->cur_disc = start_slot;
3608 di_chgdisc(s);
3609
3610 /* Update display */
3611 dpy_dbmode(s, FALSE);
3612 dpy_playmode(s, FALSE);
3613 }
3614
3615 start_slot = -1;
3616
3617 /* Update changer list */
3618 dbprog_chgr_new(s);
3619
3620 XtSetSensitive(widgets.dlist.rescan_btn, True);
3621
3622 /* Pop down the working dialog box */
3623 cd_working_popdown();
3624
3625 /* Set to normal cursor */
3626 cd_busycurs(FALSE, CURS_ALL);
3627 }
3628
3629
3630 /*
3631 * dbprog_init
3632 * Initialize the CD info/program subsystem.
3633 *
3634 * Args:
3635 * s - Pointer to the curstat_t structure.
3636 *
3637 * Return:
3638 * Nothing.
3639 */
3640 void
dbprog_init(curstat_t * s)3641 dbprog_init(curstat_t *s)
3642 {
3643 XmString xs;
3644 int i;
3645 char *cp;
3646
3647 /* Check various error message strings to ensure we don't
3648 * overflow our error message buffer later.
3649 */
3650 if (((int) (strlen(app_data.str_saverr_fork) + 16) >= ERR_BUF_SZ) ||
3651 ((int) (strlen(app_data.str_saverr_suid) + 32) >= ERR_BUF_SZ) ||
3652 ((int) (strlen(app_data.str_saverr_open) + 1) >= ERR_BUF_SZ) ||
3653 ((int) (strlen(app_data.str_saverr_close) + 1) >= ERR_BUF_SZ) ||
3654 ((int) (strlen(app_data.str_saverr_killed) + 16) >= ERR_BUF_SZ) ||
3655 ((int) (strlen(app_data.str_saverr_write) + 1) >= ERR_BUF_SZ)) {
3656 CD_FATAL(app_data.str_longpatherr);
3657 return;
3658 }
3659
3660 time_mode = TIME_TRACK;
3661 dlist_mode = DLIST_HIST;
3662 reg_mode = REGION_NONE;
3663 fname_mode = FNAME_NONE;
3664
3665 (void) memset(&w_cred, 0, sizeof(cdinfo_credit_t));
3666 (void) memset(&w_seg, 0, sizeof(cdinfo_segment_t));
3667
3668 /* Set pointer to CD info struct */
3669 dbp = dbprog_curdb(s);
3670
3671 /* Seed some numbers */
3672 for (i = 0, cp = PROGNAME; i < 4 && *cp != '\0'; i++, cp++)
3673 s->aux[i] = (byte_t) *cp ^ 0xff;
3674
3675 /* Clear the in-core structure */
3676 dbprog_dbclear(s, FALSE);
3677
3678 /* Save disc details and track details labels,
3679 * create versions with asterisk
3680 */
3681 XtVaGetValues(widgets.dbprog.extd_btn,
3682 XmNlabelString, &xs_extd_lbl,
3683 NULL
3684 );
3685 XtVaGetValues(widgets.dbprog.dcredits_btn,
3686 XmNlabelString, &xs_dcred_lbl,
3687 NULL
3688 );
3689 XtVaGetValues(widgets.dbprog.segments_btn,
3690 XmNlabelString, &xs_seg_lbl,
3691 NULL
3692 );
3693 XtVaGetValues(widgets.dbprog.extt_btn,
3694 XmNlabelString, &xs_extt_lbl,
3695 NULL
3696 );
3697 XtVaGetValues(widgets.dbprog.tcredits_btn,
3698 XmNlabelString, &xs_tcred_lbl,
3699 NULL
3700 );
3701 XtVaGetValues(widgets.segments.credits_btn,
3702 XmNlabelString, &xs_scred_lbl,
3703 NULL
3704 );
3705 xs = create_xmstring("*", NULL, XmSTRING_DEFAULT_CHARSET, FALSE);
3706 xs_extd_lblx = XmStringConcat(xs_extd_lbl, xs);
3707 xs_dcred_lblx = XmStringConcat(xs_dcred_lbl, xs);
3708 xs_seg_lblx = XmStringConcat(xs_seg_lbl, xs);
3709 xs_extt_lblx = XmStringConcat(xs_extt_lbl, xs);
3710 xs_tcred_lblx = XmStringConcat(xs_tcred_lbl, xs);
3711 xs_scred_lblx = XmStringConcat(xs_scred_lbl, xs);
3712 XmStringFree(xs);
3713
3714 /* Initialize the CDDB offline button state */
3715 XmToggleButtonSetState(
3716 widgets.dbprog.inetoffln_btn,
3717 app_data.cdinfo_inetoffln, False
3718 );
3719
3720 XtSetSensitive(widgets.dbprog.addpgm_btn, False);
3721 XtSetSensitive(widgets.dbprog.clrpgm_btn, False);
3722 XtSetSensitive(widgets.dbprog.savepgm_btn, False);
3723 XtSetSensitive(widgets.dbprog.submit_btn, False);
3724 XtSetSensitive(widgets.dbprog.flush_btn,
3725 (Boolean) !app_data.cdinfo_inetoffln);
3726 XtSetSensitive(widgets.dbprog.userreg_btn,
3727 (Boolean) (cdinfo_cddb_ver() == 2));
3728
3729 /* Initialize in-core history and CD changer lists */
3730 cdinfo_hist_init();
3731 cdinfo_chgr_init();
3732
3733 XtVaSetValues(widgets.dlist.type_opt,
3734 XmNmenuHistory, widgets.dlist.hist_btn,
3735 NULL
3736 );
3737
3738 /* Set disc details and track details window genre selectors */
3739 for (i = 0; i < 2; i++) {
3740 XtVaSetValues(widgets.dbextd.genre_opt[i],
3741 XmNmenuHistory, widgets.dbextd.genre_none_btn[i],
3742 NULL
3743 );
3744 XtVaSetValues(widgets.dbextd.subgenre_opt[i],
3745 XmNmenuHistory, widgets.dbextd.subgenre_none_btn[i],
3746 NULL
3747 );
3748 XtVaSetValues(widgets.dbextt.genre_opt[i],
3749 XmNmenuHistory, widgets.dbextt.genre_none_btn[i],
3750 NULL
3751 );
3752 XtVaSetValues(widgets.dbextt.subgenre_opt[i],
3753 XmNmenuHistory, widgets.dbextt.subgenre_none_btn[i],
3754 NULL
3755 );
3756 }
3757
3758 /* Set credits window role selectors */
3759 XtVaSetValues(widgets.credits.prirole_opt,
3760 XmNmenuHistory, widgets.credits.prirole_none_btn,
3761 NULL
3762 );
3763 XtVaSetValues(widgets.credits.subrole_opt,
3764 XmNmenuHistory, widgets.credits.subrole_none_btn,
3765 NULL
3766 );
3767 }
3768
3769
3770 /*
3771 * dbprog_chgsubmit
3772 * If in-core info has been changed, ask user if it should
3773 * be submitted to CDDB.
3774 *
3775 * Args:
3776 * s - Pointer to the curstat_t structure.
3777 *
3778 * Return:
3779 * Whether the application should proceed to shut down
3780 */
3781 /*ARGSUSED*/
3782 bool_t
dbprog_chgsubmit(curstat_t * s)3783 dbprog_chgsubmit(curstat_t *s)
3784 {
3785 if ((dbp->flags & CDINFO_CHANGED) && !app_data.cdinfo_inetoffln) {
3786 /* We use cd_timeout to schedule a pop-up of the
3787 * confirm dialog, because this thread could be
3788 * called from a cd_confirm_dialog button callback,
3789 * and we cd_confirm_callback is not re-entrant in
3790 * this manner
3791 */
3792 (void) cd_timeout(50, dbprog_submit_popup, NULL);
3793 return FALSE;
3794 }
3795
3796 return TRUE;
3797 }
3798
3799
3800 /*
3801 * dbprog_curartist
3802 * Return the current disc artist string.
3803 *
3804 * Args:
3805 * s - Pointer to the curstat_t structure.
3806 *
3807 * Return:
3808 * Disc title text string, or the null string if there
3809 * is no title available.
3810 */
3811 char *
dbprog_curartist(curstat_t * s)3812 dbprog_curartist(curstat_t *s)
3813 {
3814 if (s->mode == MOD_BUSY || s->mode == MOD_NODISC ||
3815 s->qmode == QMODE_WAIT)
3816 return NULL;
3817
3818 return (dbp->disc.artist);
3819 }
3820
3821
3822 /*
3823 * dbprog_curtitle
3824 * Return the current disc title string.
3825 *
3826 * Args:
3827 * s - Pointer to the curstat_t structure.
3828 *
3829 * Return:
3830 * Disc title text string, or the null string if there
3831 * is no title available.
3832 */
3833 char *
dbprog_curtitle(curstat_t * s)3834 dbprog_curtitle(curstat_t *s)
3835 {
3836 if (s->mode == MOD_BUSY || s->mode == MOD_NODISC ||
3837 s->qmode == QMODE_WAIT)
3838 return NULL;
3839
3840 return (dbp->disc.title == NULL ?
3841 app_data.str_unkndisc : dbp->disc.title);
3842 }
3843
3844
3845 /*
3846 * dbprog_curttitle
3847 * Return the current track title string.
3848 *
3849 * Args:
3850 * s - Pointer to the curstat_t structure.
3851 *
3852 * Return:
3853 * Track title text string, or the null string if there
3854 * is no title available.
3855 */
3856 char *
dbprog_curttitle(curstat_t * s)3857 dbprog_curttitle(curstat_t *s)
3858 {
3859 int n = di_curtrk_pos(s);
3860
3861 if (s->mode == MOD_BUSY || s->mode == MOD_NODISC ||
3862 (int) s->cur_trk < 0 || s->qmode == QMODE_WAIT)
3863 return ("");
3864
3865 if (n < 0 || dbp->track[n].title == NULL)
3866 return (app_data.str_unkntrk);
3867
3868 return (dbp->track[n].title);
3869 }
3870
3871
3872 /*
3873 * dbprog_curdb
3874 * Obtain the cdinfo_incore_t structure for the current disc.
3875 *
3876 * Args:
3877 * s - Pointer to the curstat_t structure.
3878 *
3879 * Return:
3880 * Pointer to the cdinfo_incore_t structure.
3881 */
3882 /*ARGSUSED*/
3883 cdinfo_incore_t *
dbprog_curdb(curstat_t * s)3884 dbprog_curdb(curstat_t *s)
3885 {
3886 return (cdinfo_addr());
3887 }
3888
3889
3890 /*
3891 * dbprog_curseltrk
3892 * Obtain the currently selected track number in the track list
3893 *
3894 * Args:
3895 * s - Pointer to the curstat_t structure.
3896 *
3897 * Return:
3898 * The selected track number, or -1 if not selected.
3899 */
3900 /*ARGSUSED*/
3901 int
dbprog_curseltrk(curstat_t * s)3902 dbprog_curseltrk(curstat_t *s)
3903 {
3904 return (sel_pos);
3905 }
3906
3907
3908 /*
3909 * dbprog_pgm_parse
3910 * Parse the program mode play sequence text string, and
3911 * update the playorder table in the curstat_t structure.
3912 *
3913 * Args:
3914 * s - Pointer to the curstat_t structure.
3915 *
3916 * Return:
3917 * TRUE=success, FALSE=error.
3918 */
3919 bool_t
dbprog_pgm_parse(curstat_t * s)3920 dbprog_pgm_parse(curstat_t *s)
3921 {
3922 int i,
3923 j;
3924 char *p,
3925 *q,
3926 *tmpbuf;
3927 bool_t last = FALSE,
3928 skipped = FALSE;
3929
3930 if (dbp->playorder == NULL)
3931 /* Nothing to do */
3932 return TRUE;
3933
3934 tmpbuf = NULL;
3935 if (!util_newstr(&tmpbuf, dbp->playorder)) {
3936 CD_FATAL(app_data.str_nomemory);
3937 return FALSE;
3938 }
3939
3940 s->prog_tot = 0;
3941
3942 for (i = 0, p = q = tmpbuf; i < MAXTRACK; p = ++q) {
3943 /* Skip p to the next digit */
3944 for (; !isdigit((int) *p) && *p != '\0'; p++)
3945 ;
3946
3947 if (*p == '\0')
3948 /* No more to do */
3949 break;
3950
3951 /* Skip q to the next non-digit */
3952 for (q = p; isdigit((int) *q); q++)
3953 ;
3954
3955 if (*q == ',')
3956 *q = '\0';
3957 else if (*q == '\0')
3958 last = TRUE;
3959 else {
3960 MEM_FREE(tmpbuf);
3961 CD_WARNING(app_data.str_seqfmterr);
3962 return FALSE;
3963 }
3964
3965 if (q > p) {
3966 /* Update play sequence */
3967 for (j = 0; j < MAXTRACK; j++) {
3968 if (s->trkinfo[j].trkno == atoi(p)) {
3969 s->trkinfo[i].playorder = j;
3970 s->prog_tot++;
3971 i++;
3972 break;
3973 }
3974 }
3975
3976 if (j >= MAXTRACK)
3977 skipped = TRUE;
3978 }
3979
3980 if (last)
3981 break;
3982 }
3983
3984 if (skipped) {
3985 /* Delete invalid tracks from list */
3986
3987 tmpbuf[0] = '\0';
3988 for (i = 0; i < (int) s->prog_tot; i++) {
3989 if (i == 0)
3990 (void) sprintf(tmpbuf, "%u",
3991 s->trkinfo[s->trkinfo[i].playorder].trkno);
3992 else
3993 (void) sprintf(tmpbuf, "%s,%u", tmpbuf,
3994 s->trkinfo[s->trkinfo[i].playorder].trkno);
3995 }
3996
3997 set_text_string(widgets.dbprog.pgmseq_txt, tmpbuf, FALSE);
3998
3999 CD_WARNING(app_data.str_invpgmtrk);
4000 }
4001
4002 MEM_FREE(tmpbuf);
4003
4004 return TRUE;
4005 }
4006
4007
4008 /*
4009 * dbprog_segments_setmode
4010 * Set the "set" button sensitivity and pointer label configuration
4011 * in the segments window.
4012 *
4013 * Args:
4014 * s - Pointer to the curstat_t structure.
4015 *
4016 * Return:
4017 * Nothing.
4018 */
4019 void
dbprog_segments_setmode(curstat_t * s)4020 dbprog_segments_setmode(curstat_t *s)
4021 {
4022 int segptr_mode = 0;
4023 static XmString xs_set,
4024 xs_clear;
4025 XmListCallbackStruct cb;
4026 static int segptr_prev = -1;
4027 static bool_t first = TRUE;
4028
4029 if (!XtIsManaged(widgets.segments.form))
4030 return;
4031
4032 if (first) {
4033 first = FALSE;
4034 XtVaGetValues(widgets.segments.set_btn,
4035 XmNlabelString, &xs_set,
4036 NULL
4037 );
4038 XtVaGetValues(widgets.dbprog.clrpgm_btn,
4039 XmNlabelString, &xs_clear,
4040 NULL
4041 );
4042 }
4043
4044 switch (s->mode) {
4045 case MOD_PLAY:
4046 case MOD_PAUSE:
4047 case MOD_STOP:
4048 if (s->program || s->shuffle)
4049 segptr_mode = 0;
4050 else if (s->segplay == SEGP_AB)
4051 segptr_mode = 3;
4052 else if (s->mode != MOD_STOP) {
4053 if (s->segplay == SEGP_NONE)
4054 segptr_mode = 1;
4055 else if (s->segplay == SEGP_A)
4056 segptr_mode = 2;
4057 }
4058 else
4059 segptr_mode = 0;
4060 break;
4061 case MOD_SAMPLE:
4062 default:
4063 segptr_mode = 0;
4064 break;
4065 }
4066
4067 if (segptr_prev == segptr_mode)
4068 /* No change */
4069 return;
4070
4071 XtVaSetValues(widgets.segments.set_btn,
4072 XmNlabelString, (segptr_mode == 3) ? xs_clear : xs_set,
4073 NULL
4074 );
4075
4076 XtSetSensitive(widgets.segments.set_btn, (Boolean) (segptr_mode > 0));
4077
4078 switch (segptr_mode) {
4079 case 1:
4080 XtMapWidget(widgets.segments.startptr_lbl);
4081 XtUnmapWidget(widgets.segments.endptr_lbl);
4082 break;
4083 case 2:
4084 XtUnmapWidget(widgets.segments.startptr_lbl);
4085 XtMapWidget(widgets.segments.endptr_lbl);
4086 break;
4087 case 3:
4088 XtMapWidget(widgets.segments.startptr_lbl);
4089 XtMapWidget(widgets.segments.endptr_lbl);
4090 break;
4091 case 0:
4092 default:
4093 XtUnmapWidget(widgets.segments.startptr_lbl);
4094 XtUnmapWidget(widgets.segments.endptr_lbl);
4095
4096 /* Put focus on the OK button */
4097 XmProcessTraversal(widgets.segments.ok_btn,
4098 XmTRAVERSE_CURRENT);
4099
4100 if (seg_pos > 0) {
4101 /* If dbprog_segments_select is ever changed to use
4102 * more fields of the callback struct, this will need
4103 * change too.
4104 */
4105 cb.item_position = seg_pos;
4106
4107 /* Fake a callback */
4108 dbprog_segments_select(
4109 widgets.segments.seg_list,
4110 (XtPointer) s,
4111 (XtPointer) &cb
4112 );
4113 }
4114 break;
4115 }
4116
4117 segptr_prev = segptr_mode;
4118 }
4119
4120
4121 /*
4122 * dbprog_segments_cancel
4123 * Cancel a->b mode in the segments window
4124 *
4125 * Args:
4126 * s - Pointer to the curstat_t structure.
4127 *
4128 * Return:
4129 * Nothing.
4130 */
4131 void
dbprog_segments_cancel(curstat_t * s)4132 dbprog_segments_cancel(curstat_t *s)
4133 {
4134 XmListCallbackStruct cb;
4135
4136 if (!XtIsManaged(widgets.segments.form))
4137 /* Nothing to do */
4138 return;
4139
4140 if (seg_pos < 0) {
4141 /* Simply set some fields to blank */
4142 set_text_string(widgets.segments.starttrk_txt, "", FALSE);
4143 set_text_string(widgets.segments.startfrm_txt, "", FALSE);
4144 set_text_string(widgets.segments.endtrk_txt, "", FALSE);
4145 set_text_string(widgets.segments.endfrm_txt, "", FALSE);
4146 return;
4147 }
4148
4149 /* If dbprog_segments_select is ever changed to use more
4150 * fields of the callback struct, this will need change too.
4151 */
4152 cb.item_position = seg_pos;
4153
4154 /* Fake a callback */
4155 dbprog_segments_select(
4156 widgets.segments.seg_list,
4157 (XtPointer) s,
4158 (XtPointer) &cb
4159 );
4160 }
4161
4162
4163 /*
4164 * dbprog_stopload_active
4165 * Query and/or set the stopload_active boolean flag.
4166 *
4167 * Args:
4168 * mode - 0 for query only, none-zero to set the stopload_active value
4169 * newval - If mode is non-zero, the value to set stopload_active to.
4170 *
4171 * Return:
4172 * If mode is 0, the current stopload_active value.
4173 * If mode is non-zero, the original stopload_active value.
4174 */
4175 bool_t
dbprog_stopload_active(int mode,bool_t newval)4176 dbprog_stopload_active(int mode, bool_t newval)
4177 {
4178 bool_t val;
4179
4180 val = stopload_active;
4181 if (mode != 0)
4182 stopload_active = newval;
4183
4184 return (val);
4185 }
4186
4187
4188 /**************** vv Callback routines vv ****************/
4189
4190 /*
4191 * dbprog_popup
4192 * Pop up the CD info/program subsystem window.
4193 */
4194 /*ARGSUSED*/
4195 void
dbprog_popup(Widget w,XtPointer client_data,XtPointer call_data)4196 dbprog_popup(Widget w, XtPointer client_data, XtPointer call_data)
4197 {
4198 static bool_t first = TRUE;
4199
4200 if (XtIsManaged(widgets.dbprog.form)) {
4201 /* Already popped up: pop it down */
4202 dbprog_ok(widgets.dbprog.ok_btn, client_data, call_data);
4203 return;
4204 }
4205
4206 /* Pop up the dbprog window.
4207 * The dialog has mappedWhenManaged set to False,
4208 * so we have to map/unmap explicitly. The reason
4209 * for this is we want to avoid a screen glitch when
4210 * we move the window in cd_dialog_setpos(), so we
4211 * map the window afterwards.
4212 */
4213 XtManageChild(widgets.dbprog.form);
4214 if (first) {
4215 first = FALSE;
4216 /* Set window position */
4217 cd_dialog_setpos(XtParent(widgets.dbprog.form));
4218 }
4219 XtMapWidget(XtParent(widgets.dbprog.form));
4220
4221 /* Put focus on the OK button */
4222 XmProcessTraversal(widgets.dbprog.ok_btn, XmTRAVERSE_CURRENT);
4223 }
4224
4225
4226 /*
4227 * dbprog_inetoffln
4228 * The Internet Offline button callback.
4229 */
4230 /*ARGSUSED*/
4231 void
dbprog_inetoffln(Widget w,XtPointer client_data,XtPointer call_data)4232 dbprog_inetoffln(Widget w, XtPointer client_data, XtPointer call_data)
4233 {
4234 XmToggleButtonCallbackStruct *p =
4235 (XmToggleButtonCallbackStruct *)(void *) call_data;
4236 curstat_t *s = (curstat_t *)(void *) client_data;
4237 cdinfo_ret_t ret;
4238
4239 if (p->reason != XmCR_VALUE_CHANGED)
4240 return;
4241
4242 DBGPRN(DBG_CDI|DBG_UI)(errfp,
4243 "\n* OFFLINE-CDDB: %s\n", p->set ? "On" : "Off");
4244
4245 /* If CD information lookup is in progress, cancel it, then
4246 * schedule a reload in one second.
4247 */
4248 if (s->qmode == QMODE_WAIT) {
4249 cdinfo_load_cancel();
4250 (void) cd_timeout(1000, dbprog_dbget, (byte_t *) s);
4251 }
4252
4253 app_data.cdinfo_inetoffln = (bool_t) p->set;
4254
4255 if ((ret = cdinfo_offline(s)) != 0) {
4256 DBGPRN(DBG_CDI)(errfp,
4257 "cdinfo_offline: status=%d arg=%d\n",
4258 CDINFO_GET_STAT(ret), CDINFO_GET_ARG(ret));
4259
4260 cd_beep();
4261 XmToggleButtonSetState(w, (Boolean) !p->set, False);
4262 app_data.cdinfo_inetoffln = (bool_t) !p->set;
4263 return;
4264 }
4265
4266 if (!app_data.cdinfo_inetoffln && (dbp->flags & CDINFO_CHANGED))
4267 XtSetSensitive(widgets.dbprog.submit_btn, True);
4268 else
4269 XtSetSensitive(widgets.dbprog.submit_btn, False);
4270
4271 XtSetSensitive(widgets.dbprog.flush_btn, !app_data.cdinfo_inetoffln);
4272
4273 /* Configure the wwwWarp menu */
4274 wwwwarp_sel_cfg(s);
4275 }
4276
4277
4278 /*
4279 * dbprog_text_new
4280 * Generic text widget callback function.
4281 */
4282 /*ARGSUSED*/
4283 void
dbprog_text_new(Widget w,XtPointer client_data,XtPointer call_data)4284 dbprog_text_new(Widget w, XtPointer client_data, XtPointer call_data)
4285 {
4286 XmAnyCallbackStruct *p = (XmAnyCallbackStruct *)(void *) call_data;
4287 curstat_t *s = (curstat_t *)(void *) client_data;
4288 char *str,
4289 **ent;
4290 bool_t credits_handling,
4291 segments_handling,
4292 fname_handling;
4293 cdinfo_fname_t *fnp = NULL;
4294
4295 credits_handling = segments_handling = fname_handling = FALSE;
4296
4297 /* Figure out which widget changed */
4298 if (w == widgets.dbprog.artist_txt)
4299 ent = &dbp->disc.artist;
4300 else if (w == widgets.dbprog.title_txt)
4301 ent = &dbp->disc.title;
4302 else if (w == widgets.dbextd.sorttitle_txt)
4303 ent = &dbp->disc.sorttitle;
4304 else if (w == widgets.dbextd.the_txt)
4305 ent = &dbp->disc.title_the;
4306 else if (w == widgets.dbextd.year_txt)
4307 ent = &dbp->disc.year;
4308 else if (w == widgets.dbextd.label_txt)
4309 ent = &dbp->disc.label;
4310 else if (w == widgets.dbextd.dnum_txt)
4311 ent = &dbp->disc.dnum;
4312 else if (w == widgets.dbextd.tnum_txt)
4313 ent = &dbp->disc.tnum;
4314 else if (w == widgets.dbextd.notes_txt)
4315 ent = &dbp->disc.notes;
4316 else if (w == widgets.dbextt.sorttitle_txt)
4317 ent = &dbp->track[extt_pos].sorttitle;
4318 else if (w == widgets.dbextt.the_txt)
4319 ent = &dbp->track[extt_pos].title_the;
4320 else if (w == widgets.dbextt.artist_txt)
4321 ent = &dbp->track[extt_pos].artist;
4322 else if (w == widgets.dbextt.year_txt)
4323 ent = &dbp->track[extt_pos].year;
4324 else if (w == widgets.dbextt.label_txt)
4325 ent = &dbp->track[extt_pos].label;
4326 else if (w == widgets.dbextt.bpm_txt)
4327 ent = &dbp->track[extt_pos].bpm;
4328 else if (w == widgets.dbextt.notes_txt)
4329 ent = &dbp->track[extt_pos].notes;
4330 else if (w == widgets.credits.name_txt) {
4331 ent = &w_cred.crinfo.name;
4332 credits_handling = TRUE;
4333 }
4334 else if (w == widgets.credits.notes_txt) {
4335 ent = &w_cred.notes;
4336 credits_handling = TRUE;
4337 }
4338 else if (w == widgets.segments.name_txt) {
4339 ent = &w_seg.name;
4340 segments_handling = TRUE;
4341 }
4342 else if (w == widgets.segments.starttrk_txt) {
4343 ent = &w_seg.start_track;
4344 segments_handling = TRUE;
4345 }
4346 else if (w == widgets.segments.startfrm_txt) {
4347 ent = &w_seg.start_frame;
4348 segments_handling = TRUE;
4349 }
4350 else if (w == widgets.segments.endtrk_txt) {
4351 ent = &w_seg.end_track;
4352 segments_handling = TRUE;
4353 }
4354 else if (w == widgets.segments.endfrm_txt) {
4355 ent = &w_seg.end_frame;
4356 segments_handling = TRUE;
4357 }
4358 else if (w == widgets.segments.notes_txt) {
4359 ent = &w_seg.notes;
4360 segments_handling = TRUE;
4361 }
4362 else if (w == widgets.fullname.dispname_txt) {
4363 switch (fname_mode) {
4364 case FNAME_DISC:
4365 fnp = &dbp->disc.artistfname;
4366 break;
4367 case FNAME_TRACK:
4368 fnp = &dbp->track[extt_pos].artistfname;
4369 break;
4370 case FNAME_CREDITS:
4371 fnp = &w_cred.crinfo.fullname;
4372 credits_handling = TRUE;
4373 break;
4374 default:
4375 /* Unsupported fname mode */
4376 return;
4377 }
4378 ent = &fnp->dispname;
4379 fname_handling = TRUE;
4380 }
4381 else if (w == widgets.fullname.lastname_txt) {
4382 switch (fname_mode) {
4383 case FNAME_DISC:
4384 fnp = &dbp->disc.artistfname;
4385 break;
4386 case FNAME_TRACK:
4387 fnp = &dbp->track[extt_pos].artistfname;
4388 break;
4389 case FNAME_CREDITS:
4390 fnp = &w_cred.crinfo.fullname;
4391 credits_handling = TRUE;
4392 break;
4393 default:
4394 /* Unsupported fname mode */
4395 return;
4396 }
4397 ent = &fnp->lastname;
4398 fname_handling = TRUE;
4399 }
4400 else if (w == widgets.fullname.firstname_txt) {
4401 switch (fname_mode) {
4402 case FNAME_DISC:
4403 fnp = &dbp->disc.artistfname;
4404 break;
4405 case FNAME_TRACK:
4406 fnp = &dbp->track[extt_pos].artistfname;
4407 break;
4408 case FNAME_CREDITS:
4409 fnp = &w_cred.crinfo.fullname;
4410 credits_handling = TRUE;
4411 break;
4412 default:
4413 /* Unsupported fname mode */
4414 return;
4415 }
4416 ent = &fnp->firstname;
4417 fname_handling = TRUE;
4418 }
4419 else if (w == widgets.fullname.the_txt) {
4420 switch (fname_mode) {
4421 case FNAME_DISC:
4422 fnp = &dbp->disc.artistfname;
4423 break;
4424 case FNAME_TRACK:
4425 fnp = &dbp->track[extt_pos].artistfname;
4426 break;
4427 case FNAME_CREDITS:
4428 fnp = &w_cred.crinfo.fullname;
4429 credits_handling = TRUE;
4430 break;
4431 default:
4432 /* Unsupported fname mode */
4433 return;
4434 }
4435 ent = &fnp->the;
4436 fname_handling = TRUE;
4437 }
4438 else {
4439 /* Unsupported by this function */
4440 return;
4441 }
4442
4443 /* Update the appropriate in-core structure entry */
4444 switch (p->reason) {
4445 case XmCR_VALUE_CHANGED:
4446 if ((str = get_text_string(w, TRUE)) == NULL)
4447 return;
4448
4449 if (*ent != NULL) {
4450 if (strcmp(str, *ent) == 0) {
4451 /* not changed */
4452 MEM_FREE(str);
4453 break;
4454 }
4455 MEM_FREE(*ent);
4456 *ent = NULL;
4457 }
4458
4459 if (!util_newstr(ent, str)) {
4460 CD_FATAL(app_data.str_nomemory);
4461 MEM_FREE(str);
4462 return;
4463 }
4464
4465 if (fname_handling) {
4466 fname_changed = TRUE;
4467
4468 if (w != widgets.fullname.dispname_txt &&
4469 XmToggleButtonGetState(
4470 widgets.fullname.autogen_btn)) {
4471 char *cp;
4472
4473 /* Auto-generate the fullname's dispname */
4474 if ((cp = dbprog_autoname(fnp)) == NULL) {
4475 CD_FATAL(app_data.str_nomemory);
4476 return;
4477 }
4478
4479 set_text_string(
4480 widgets.fullname.dispname_txt,
4481 cp,
4482 TRUE
4483 );
4484 MEM_FREE(cp);
4485 }
4486 }
4487
4488 /* Special handling for fullname.dispname field:
4489 * update the associated fields for album, track,
4490 * or credit to match.
4491 */
4492 if (w == widgets.fullname.dispname_txt) {
4493 switch (fname_mode) {
4494 case FNAME_DISC:
4495 set_text_string(
4496 widgets.dbprog.artist_txt, str, TRUE
4497 );
4498 break;
4499 case FNAME_TRACK:
4500 set_text_string(
4501 widgets.dbextt.artist_txt, str, TRUE
4502 );
4503 break;
4504 case FNAME_CREDITS:
4505 set_text_string(
4506 widgets.credits.name_txt, str, TRUE
4507 );
4508 break;
4509 }
4510 }
4511
4512 /* Update disc details button label if needed */
4513 if (w == widgets.dbextd.notes_txt)
4514 dbprog_extd_lblupd();
4515
4516 /* Update track list entry and track details
4517 * button label if needed
4518 */
4519 if (w == widgets.dbextt.notes_txt && extt_pos >= 0) {
4520 if (sel_pos > 0)
4521 dbprog_extt_lblupd(&dbp->track[sel_pos-1]);
4522
4523 dbprog_listupd_ent(
4524 s,
4525 extt_pos,
4526 (dbp->track[extt_pos].title != NULL) ?
4527 dbp->track[extt_pos].title : UNDEF_STR,
4528 FALSE
4529 );
4530
4531 /* Re-select the list entry if necessary */
4532 if ((extt_pos + 1) == sel_pos) {
4533 XmListSelectPos(widgets.dbprog.trk_list,
4534 sel_pos, False);
4535 }
4536 else if ((extt_pos + 1) == ind_pos) {
4537 XmListSelectPos(widgets.dbprog.trk_list,
4538 ind_pos, False);
4539 }
4540 }
4541
4542 /* Update credits window title if needed */
4543 if (w == widgets.segments.name_txt)
4544 dbprog_set_seg_title();
4545
4546 /* Update the disc details window and credits window titles
4547 * if needed
4548 */
4549 if (w == widgets.dbprog.artist_txt ||
4550 w == widgets.dbprog.title_txt) {
4551 if (XtIsManaged(widgets.dbextd.form)) {
4552 /* Update disc details disc title */
4553 dbprog_set_disc_title(
4554 s->cur_disc,
4555 dbp->disc.artist,
4556 dbp->disc.title
4557 );
4558 }
4559
4560 /* Update main window title display */
4561 dpy_dtitle(s);
4562 }
4563
4564 MEM_FREE(str);
4565
4566 if (credits_handling) {
4567 if (cred_pos > 0)
4568 XtSetSensitive(widgets.credits.mod_btn, True);
4569 else
4570 XtSetSensitive(widgets.credits.add_btn, True);
4571 }
4572 else if (segments_handling) {
4573 if (seg_pos > 0)
4574 XtSetSensitive(widgets.segments.mod_btn, True);
4575 else
4576 XtSetSensitive(widgets.segments.add_btn, True);
4577 }
4578 else {
4579 dbp->flags |= CDINFO_CHANGED;
4580 if (!app_data.cdinfo_inetoffln) {
4581 XtSetSensitive(widgets.dbprog.submit_btn,
4582 True);
4583 }
4584 }
4585
4586 break;
4587
4588 case XmCR_ACTIVATE:
4589 case XmCR_LOSING_FOCUS:
4590 if (w == widgets.dbprog.title_txt ||
4591 w == widgets.dbextd.sorttitle_txt ||
4592 w == widgets.dbextd.year_txt ||
4593 w == widgets.dbextd.label_txt ||
4594 w == widgets.dbextd.dnum_txt ||
4595 w == widgets.dbextd.tnum_txt ||
4596 w == widgets.dbextt.sorttitle_txt ||
4597 w == widgets.dbextt.artist_txt ||
4598 w == widgets.dbextt.year_txt ||
4599 w == widgets.dbextt.label_txt ||
4600 w == widgets.dbextt.bpm_txt ||
4601 w == widgets.fullname.dispname_txt ||
4602 w == widgets.fullname.lastname_txt ||
4603 w == widgets.fullname.firstname_txt) {
4604
4605 /* Set cursor to beginning of text */
4606 XmTextSetInsertionPosition(w, 0);
4607
4608 if (w == widgets.dbprog.title_txt) {
4609 /* Update curfile */
4610 dbprog_curfileupd();
4611 }
4612 }
4613 break;
4614
4615 default:
4616 break;
4617 }
4618 }
4619
4620
4621 /*
4622 * dbprog_focus_next
4623 * Change focus to the next widget on done
4624 */
4625 /*ARGSUSED*/
4626 void
dbprog_focus_next(Widget w,XtPointer client_data,XtPointer call_data)4627 dbprog_focus_next(Widget w, XtPointer client_data, XtPointer call_data)
4628 {
4629 Widget nextw;
4630
4631 if (w == widgets.dbprog.artist_txt)
4632 nextw = widgets.dbprog.title_txt;
4633 else if (w == widgets.dbprog.title_txt)
4634 nextw = widgets.dbprog.ttitle_txt;
4635 else if (w == widgets.dbextd.sorttitle_txt)
4636 nextw = widgets.dbextd.the_btn;
4637 else if (w == widgets.dbextd.the_txt)
4638 nextw = widgets.dbextd.year_txt;
4639 else if (w == widgets.dbextd.year_txt)
4640 nextw = widgets.dbextd.label_txt;
4641 else if (w == widgets.dbextd.label_txt)
4642 nextw = widgets.dbextd.comp_btn;
4643 else if (w == widgets.dbextd.dnum_txt)
4644 nextw = widgets.dbextd.tnum_txt;
4645 else if (w == widgets.dbextd.tnum_txt)
4646 nextw = widgets.dbextd.notes_txt;
4647 else if (w == widgets.dbextt.sorttitle_txt)
4648 nextw = widgets.dbextt.the_btn;
4649 else if (w == widgets.dbextt.the_txt)
4650 nextw = widgets.dbextt.artist_txt;
4651 else if (w == widgets.dbextt.artist_txt)
4652 nextw = widgets.dbextt.fullname_btn;
4653 else if (w == widgets.dbextt.year_txt)
4654 nextw = widgets.dbextt.label_txt;
4655 else if (w == widgets.dbextt.label_txt)
4656 nextw = widgets.dbextt.bpm_txt;
4657 else if (w == widgets.credits.name_txt)
4658 nextw = widgets.credits.fullname_btn;
4659 else if (w == widgets.segments.name_txt)
4660 nextw = widgets.segments.starttrk_txt;
4661 else if (w == widgets.segments.starttrk_txt)
4662 nextw = widgets.segments.startfrm_txt;
4663 else if (w == widgets.segments.startfrm_txt)
4664 nextw = widgets.segments.endtrk_txt;
4665 else if (w == widgets.segments.endtrk_txt)
4666 nextw = widgets.segments.endfrm_txt;
4667 else if (w == widgets.segments.endfrm_txt)
4668 nextw = widgets.segments.notes_txt;
4669 else if (w == widgets.fullname.dispname_txt)
4670 nextw = widgets.fullname.firstname_txt;
4671 else if (w == widgets.fullname.firstname_txt)
4672 nextw = widgets.fullname.lastname_txt;
4673 else if (w == widgets.fullname.lastname_txt)
4674 nextw = widgets.fullname.the_btn;
4675 else if (w == widgets.fullname.the_txt)
4676 nextw = widgets.fullname.ok_btn;
4677 else if (w == widgets.auth.name_txt)
4678 nextw = widgets.auth.pass_txt;
4679 else if (w == widgets.auth.pass_txt)
4680 nextw = widgets.auth.ok_btn;
4681 else
4682 return;
4683
4684 /* Put the input focus on the next widget */
4685 XmProcessTraversal(nextw, XmTRAVERSE_CURRENT);
4686 }
4687
4688
4689 /*
4690 * dbprog_trklist_play
4691 * Track list entry selection default action callback.
4692 */
4693 void
dbprog_trklist_play(Widget w,XtPointer client_data,XtPointer call_data)4694 dbprog_trklist_play(Widget w, XtPointer client_data, XtPointer call_data)
4695 {
4696 XmListCallbackStruct *p = (XmListCallbackStruct *)(void *) call_data;
4697 curstat_t *s = (curstat_t *)(void *) client_data;
4698
4699 if (p->reason != XmCR_DEFAULT_ACTION)
4700 return;
4701
4702 /* Stop current playback */
4703 if (s->mode != MOD_STOP)
4704 di_stop(s, FALSE);
4705
4706 sel_pos = p->item_position;
4707
4708 /* Clear previous program */
4709 set_text_string(widgets.dbprog.pgmseq_txt, "", FALSE);
4710 XtSetSensitive(widgets.dbprog.addpgm_btn, False);
4711 XtSetSensitive(widgets.dbprog.clrpgm_btn, False);
4712 XtSetSensitive(widgets.dbprog.savepgm_btn, False);
4713 dbprog_progclear(s);
4714
4715 /* This is a single-track program as a result of double clicking
4716 * on a track (or pressing return).
4717 */
4718 s->onetrk_prog = TRUE;
4719
4720 /* Add selected track to program */
4721 dbprog_addpgm(w, client_data, call_data);
4722
4723 /* Play selected track */
4724 cd_play_pause(w, client_data, call_data);
4725
4726 if (sel_pos > 0) {
4727 XmListDeselectPos(w, sel_pos);
4728 sel_pos = -1;
4729 }
4730 set_text_string(widgets.dbprog.ttitle_txt, "", FALSE);
4731
4732 XtSetSensitive(widgets.dbprog.addpgm_btn, False);
4733 XtSetSensitive(widgets.dbprog.extt_btn, False);
4734 XtSetSensitive(widgets.dbprog.tcredits_btn, False);
4735
4736 /* Update button labels */
4737 dbprog_extt_lblupd(NULL);
4738 dbprog_tcred_lblupd(NULL);
4739 }
4740
4741
4742 /*
4743 * dbprog_trklist_select
4744 * Track list entry selection callback.
4745 */
4746 void
dbprog_trklist_select(Widget w,XtPointer client_data,XtPointer call_data)4747 dbprog_trklist_select(Widget w, XtPointer client_data, XtPointer call_data)
4748 {
4749 XmListCallbackStruct *p = (XmListCallbackStruct *)(void *) call_data;
4750 curstat_t *s = (curstat_t *)(void *) client_data;
4751 int pos;
4752 char *cp;
4753 bool_t resel_list;
4754
4755 if (p->reason != XmCR_BROWSE_SELECT)
4756 return;
4757
4758 if (s->mode == MOD_BUSY || s->mode == MOD_NODISC)
4759 return;
4760
4761 pos = p->item_position - 1;
4762
4763 if (title_edited) {
4764 title_edited = FALSE;
4765
4766 cp = get_text_string(widgets.dbprog.ttitle_txt, TRUE);
4767 if (cp == NULL)
4768 return;
4769
4770 /* Update track list entry */
4771 dbprog_listupd_ent(s, pos, cp, FALSE);
4772
4773 if (sel_pos > 0) {
4774 XmListDeselectPos(w, sel_pos);
4775 sel_pos = -1;
4776 }
4777 set_text_string(widgets.dbprog.ttitle_txt, "", FALSE);
4778
4779 if (!util_newstr(&dbp->track[pos].title, cp)) {
4780 CD_FATAL(app_data.str_nomemory);
4781 return;
4782 }
4783
4784 MEM_FREE(cp);
4785
4786 dbp->flags |= CDINFO_CHANGED;
4787 if (!app_data.cdinfo_inetoffln)
4788 XtSetSensitive(widgets.dbprog.submit_btn, True);
4789 XtSetSensitive(widgets.dbprog.addpgm_btn, False);
4790 XtSetSensitive(widgets.dbprog.extt_btn, False);
4791 XtSetSensitive(widgets.dbprog.tcredits_btn, False);
4792
4793 /* Update button labels */
4794 dbprog_extt_lblupd(NULL);
4795 dbprog_tcred_lblupd(NULL);
4796
4797 /* Update the track details window if necessary */
4798 if (extt_pos == pos &&
4799 (XtIsManaged(widgets.dbextt.form) ||
4800 XtIsManaged(widgets.credits.form))) {
4801 dbprog_set_track_title(
4802 s->trkinfo[pos].trkno,
4803 dbp->track[pos].title
4804 );
4805 }
4806
4807 /* Update the main window if necessary */
4808 if (di_curtrk_pos(s) == pos) {
4809 dpy_ttitle(s);
4810
4811 /* Update curfile */
4812 dbprog_curfileupd();
4813 }
4814
4815 /* Return the input focus to the track title editor */
4816 XmProcessTraversal(
4817 widgets.dbprog.ttitle_txt,
4818 XmTRAVERSE_CURRENT
4819 );
4820 }
4821 else if (sel_pos == p->item_position) {
4822 /* This item is already selected: deselect it */
4823
4824 XmListDeselectPos(w, sel_pos);
4825 sel_pos = ind_pos = -1;
4826 set_text_string(widgets.dbprog.ttitle_txt, "", FALSE);
4827
4828 XtSetSensitive(widgets.dbprog.addpgm_btn, False);
4829 XtSetSensitive(widgets.dbprog.extt_btn, False);
4830 XtSetSensitive(widgets.dbprog.tcredits_btn, False);
4831
4832 /* Update button labels */
4833 dbprog_extt_lblupd(NULL);
4834 dbprog_tcred_lblupd(NULL);
4835 }
4836 else {
4837 /* Check full name data and abort if needed */
4838 if (!dbprog_fullname_ck(s)) {
4839 cd_beep();
4840 XmListDeselectPos(w, p->item_position);
4841 XmListSelectPos(w, sel_pos, False);
4842 return;
4843 }
4844
4845 sel_pos = p->item_position;
4846
4847 if (dbp->track[pos].title == NULL) {
4848 set_text_string(widgets.dbprog.ttitle_txt, "", FALSE);
4849 }
4850 else {
4851 char *str;
4852 int n;
4853
4854 set_text_string(widgets.dbprog.ttitle_txt,
4855 dbp->track[pos].title,
4856 TRUE);
4857
4858 str = XmTextGetString(widgets.dbprog.ttitle_txt);
4859 n = strlen(str);
4860 XtFree(str);
4861
4862 XmTextSetInsertionPosition(
4863 widgets.dbprog.ttitle_txt,
4864 n
4865 );
4866 }
4867
4868 XtSetSensitive(widgets.dbprog.addpgm_btn, True);
4869 XtSetSensitive(widgets.dbprog.extt_btn, True);
4870 XtSetSensitive(widgets.dbprog.tcredits_btn, True);
4871
4872 /* Update button labels */
4873 dbprog_extt_lblupd(&dbp->track[pos]);
4874 dbprog_tcred_lblupd(&dbp->track[pos]);
4875
4876 resel_list = FALSE;
4877
4878 /* Warp the track details window to the new selected track,
4879 * if it is popped up.
4880 */
4881 if (XtIsManaged(widgets.dbextt.form)) {
4882 extt_pos = pos;
4883 dbprog_extt(w, (XtPointer) FALSE, call_data);
4884 resel_list = TRUE;
4885 }
4886
4887 /* Warp the credits window to the new selected track,
4888 * if it is popped up and in track mode.
4889 */
4890 if (credits_mode == CREDITS_TRACK) {
4891 /* Update credits window if needed */
4892 extt_pos = pos;
4893 dbprog_creditupd(s, pos);
4894 resel_list = TRUE;
4895 }
4896
4897 if (resel_list)
4898 XmListSelectPos(w, sel_pos, False);
4899
4900 /* Update fullname window if necessary */
4901 if (fname_mode == FNAME_TRACK) {
4902 dbprog_fullname(widgets.dbextt.fullname_btn,
4903 (XtPointer) FALSE, call_data);
4904 }
4905 else if (fname_mode == FNAME_CREDITS &&
4906 credits_mode == CREDITS_TRACK) {
4907 dbprog_fullname(widgets.credits.fullname_btn,
4908 (XtPointer) FALSE, call_data);
4909 }
4910 }
4911 }
4912
4913
4914 /*
4915 * dbprog_ttitle_focuschg
4916 * Track title editor text widget keyboard focus change callback.
4917 */
4918 /*ARGSUSED*/
4919 void
dbprog_ttitle_focuschg(Widget w,XtPointer client_data,XtPointer call_data)4920 dbprog_ttitle_focuschg(Widget w, XtPointer client_data, XtPointer call_data)
4921 {
4922 XmAnyCallbackStruct *p = (XmAnyCallbackStruct *)(void *) call_data;
4923 curstat_t *s = (curstat_t *)(void *) client_data;
4924 int i;
4925
4926 switch (p->reason) {
4927 case XmCR_FOCUS:
4928 if (sel_pos < 0) {
4929 for (i = 0; i < (int) s->tot_trks; i++) {
4930 if (dbp->track[i].title == NULL) {
4931 ind_pos = i + 1;
4932
4933 XmListSelectPos(
4934 widgets.dbprog.trk_list,
4935 ind_pos,
4936 False
4937 );
4938 XmListSetBottomPos(
4939 widgets.dbprog.trk_list,
4940 ind_pos
4941 );
4942 break;
4943 }
4944 }
4945 }
4946 break;
4947
4948 case XmCR_LOSING_FOCUS:
4949 if (ind_pos > 0) {
4950 /* Save a copy of the ttitle */
4951 if (sav_ttitle != NULL)
4952 MEM_FREE(sav_ttitle);
4953 sav_ttitle = get_text_string(
4954 widgets.dbprog.ttitle_txt, TRUE
4955 );
4956 if (sav_ttitle != NULL && sav_ttitle[0] == '\0') {
4957 MEM_FREE(sav_ttitle);
4958 sav_ttitle = NULL;
4959 }
4960
4961 XmListDeselectPos(widgets.dbprog.trk_list, ind_pos);
4962 ind_pos = -1;
4963 }
4964 break;
4965
4966 default:
4967 break;
4968 }
4969 }
4970
4971
4972 /*
4973 * dbprog_ttitle_new
4974 * Track title editor text widget callback function.
4975 */
4976 void
dbprog_ttitle_new(Widget w,XtPointer client_data,XtPointer call_data)4977 dbprog_ttitle_new(Widget w, XtPointer client_data, XtPointer call_data)
4978 {
4979 XmAnyCallbackStruct *p = (XmAnyCallbackStruct *)(void *) call_data;
4980 curstat_t *s = (curstat_t *)(void *) client_data;
4981 char *cp;
4982 int *pos,
4983 i;
4984 XmListCallbackStruct cb;
4985
4986 if (p->reason == XmCR_VALUE_CHANGED) {
4987 if ((cp = get_text_string(w, TRUE)) == NULL)
4988 return;
4989
4990 if (cp == NULL || *cp == '\0')
4991 title_edited = FALSE;
4992 else if (sel_pos < 0)
4993 title_edited = TRUE;
4994
4995 MEM_FREE(cp);
4996 return;
4997 }
4998 else if (p->reason == XmCR_ACTIVATE) {
4999 /* Force w to be the ttitle_txt because this callback
5000 * function can also be called as a result of clicking
5001 * on the apply_btn.
5002 */
5003 if (w == widgets.dbprog.apply_btn) {
5004 /* Put focus back on the ttitle_txt widget */
5005 XmProcessTraversal(
5006 widgets.dbprog.ttitle_txt,
5007 XmTRAVERSE_CURRENT
5008 );
5009 w = widgets.dbprog.ttitle_txt;
5010
5011 /* If a saved ttitle string exists, put it back */
5012 if (sav_ttitle != NULL) {
5013 set_text_string(w, sav_ttitle, TRUE);
5014 MEM_FREE(sav_ttitle);
5015 sav_ttitle = NULL;
5016 }
5017 }
5018 }
5019 else
5020 return;
5021
5022 if (sel_pos > 0 &&
5023 XmListGetSelectedPos(widgets.dbprog.trk_list, &pos, &i)) {
5024 if ((cp = get_text_string(w, TRUE)) == NULL)
5025 return;
5026
5027 if (pos == NULL) {
5028 MEM_FREE(cp);
5029 return;
5030 }
5031
5032 if (i != 1)
5033 MEM_FREE(cp);
5034 else {
5035 /* Update track list entry */
5036 dbprog_listupd_ent(s, (*pos)-1, cp, FALSE);
5037
5038 XmListDeselectPos(widgets.dbprog.trk_list, sel_pos);
5039 sel_pos = -1;
5040
5041 if (!util_newstr(&dbp->track[(*pos)-1].title, cp)) {
5042 CD_FATAL(app_data.str_nomemory);
5043 return;
5044 }
5045
5046 MEM_FREE(cp);
5047
5048 dbp->flags |= CDINFO_CHANGED;
5049 if (!app_data.cdinfo_inetoffln)
5050 XtSetSensitive(widgets.dbprog.submit_btn, True);
5051 XtSetSensitive(widgets.dbprog.addpgm_btn, False);
5052 XtSetSensitive(widgets.dbprog.extt_btn, False);
5053 XtSetSensitive(widgets.dbprog.tcredits_btn, False);
5054
5055 /* Update button labels */
5056 dbprog_extt_lblupd(NULL);
5057 dbprog_tcred_lblupd(NULL);
5058
5059 /* Update the track details and credits windows if
5060 * necessary
5061 */
5062 if (extt_pos == (*pos)-1 &&
5063 (XtIsManaged(widgets.dbextt.form) ||
5064 XtIsManaged(widgets.credits.form))) {
5065 dbprog_set_track_title(
5066 s->trkinfo[extt_pos].trkno,
5067 dbp->track[extt_pos].title
5068 );
5069 }
5070
5071 /* Update the main window if necessary */
5072 if (di_curtrk_pos(s) == (*pos)-1) {
5073 dpy_ttitle(s);
5074
5075 /* Update curfile */
5076 dbprog_curfileupd();
5077 }
5078 }
5079
5080 set_text_string(w, "", FALSE);
5081
5082 XtFree((XtPointer) pos);
5083 }
5084 else {
5085 /* Pressing Return in this case is equivalent to clicking
5086 * on the first title-less track on the track list.
5087 */
5088 for (i = 0; i < (int) s->tot_trks; i++) {
5089 if (dbp->track[i].title == NULL) {
5090 cb.item_position = i + 1;
5091 cb.reason = XmCR_BROWSE_SELECT;
5092 cb.event = p->event;
5093
5094 dbprog_trklist_select(
5095 widgets.dbprog.trk_list,
5096 (XtPointer) s,
5097 (XtPointer) &cb
5098 );
5099 break;
5100 }
5101 }
5102 }
5103
5104 for (i = 0; i < (int) s->tot_trks; i++) {
5105 if (dbp->track[i].title == NULL) {
5106 ind_pos = i + 1;
5107
5108 XmListSelectPos(
5109 widgets.dbprog.trk_list,
5110 ind_pos,
5111 False
5112 );
5113 XmListSetBottomPos(
5114 widgets.dbprog.trk_list,
5115 ind_pos
5116 );
5117 break;
5118 }
5119 }
5120 }
5121
5122
5123 /*
5124 * dbprog_pgmseq_verify
5125 * Play sequence editor text widget user-input verification callback.
5126 */
5127 /*ARGSUSED*/
5128 void
dbprog_pgmseq_verify(Widget w,XtPointer client_data,XtPointer call_data)5129 dbprog_pgmseq_verify(Widget w, XtPointer client_data, XtPointer call_data)
5130 {
5131 XmTextVerifyCallbackStruct
5132 *p = (XmTextVerifyCallbackStruct *)(void *) call_data;
5133 int i;
5134 char prev,
5135 *currstr;
5136
5137 if (p->reason != XmCR_MODIFYING_TEXT_VALUE)
5138 return;
5139
5140 p->doit = True;
5141
5142 if (p->startPos != p->endPos)
5143 /* Deleting text, no verification needed */
5144 return;
5145
5146 currstr = get_text_string(w, FALSE);
5147
5148 switch (p->text->format) {
5149 case XmFMT_8_BIT:
5150 if (p->currInsert > 0 && currstr != NULL)
5151 prev = currstr[p->currInsert - 1];
5152 else
5153 prev = ',';
5154
5155 for (i = 0; i < p->text->length; i++) {
5156 /* Only allowed input is digits, ',' or ' ' */
5157 if (p->text->ptr[i] == ',' ||
5158 p->text->ptr[i] == ' ') {
5159 if (prev == ',') {
5160 p->doit = False;
5161 break;
5162 }
5163 /* Substitute ' ' with ',' */
5164 if (p->text->ptr[i] == ' ')
5165 p->text->ptr[i] = ',';
5166 }
5167 else if (!isdigit((int) p->text->ptr[i])) {
5168 p->doit = False;
5169 break;
5170 }
5171 prev = p->text->ptr[i];
5172 }
5173 break;
5174
5175 case XmFMT_16_BIT:
5176 default:
5177 /* Don't know how to handle other character sets yet */
5178 p->doit = False;
5179 break;
5180 }
5181
5182 if (currstr != NULL)
5183 MEM_FREE(currstr);
5184 }
5185
5186
5187 /*
5188 * dbprog_pgmseq_txtchg
5189 * Play sequence editor text widget text changed callback.
5190 */
5191 /*ARGSUSED*/
5192 void
dbprog_pgmseq_txtchg(Widget w,XtPointer client_data,XtPointer call_data)5193 dbprog_pgmseq_txtchg(Widget w, XtPointer client_data, XtPointer call_data)
5194 {
5195 XmAnyCallbackStruct *p = (XmAnyCallbackStruct *)(void *) call_data;
5196 curstat_t *s = (curstat_t *)(void *) client_data;
5197
5198 if (p->reason != XmCR_VALUE_CHANGED)
5199 return;
5200
5201 if (s->onetrk_prog) {
5202 /* Currently playing a single-track program: clear it first */
5203 s->onetrk_prog = FALSE;
5204 dbprog_progclear(s);
5205 }
5206
5207 /* Disable shuffle mode */
5208 if (s->shuffle) {
5209 di_shuffle(s, FALSE);
5210 set_shuffle_btn(FALSE);
5211 }
5212
5213 /* Disable a->b mode */
5214 if (s->segplay != SEGP_NONE)
5215 s->segplay = SEGP_NONE;
5216
5217 if (dbp->playorder != NULL)
5218 MEM_FREE(dbp->playorder);
5219 dbp->playorder = get_text_string(w, FALSE);
5220
5221 if ((s->program = dbprog_pgm_active()) == FALSE)
5222 dbprog_progclear(s);
5223
5224 /* Update display */
5225 dpy_progmode(s, FALSE);
5226
5227 XtSetSensitive(widgets.dbprog.clrpgm_btn, (Boolean) s->program);
5228 XtSetSensitive(widgets.dbprog.savepgm_btn, (Boolean) s->program);
5229 }
5230
5231
5232 /*
5233 * dbprog_addpgm
5234 * Program Add button callback.
5235 */
5236 /*ARGSUSED*/
5237 void
dbprog_addpgm(Widget w,XtPointer client_data,XtPointer call_data)5238 dbprog_addpgm(Widget w, XtPointer client_data, XtPointer call_data)
5239 {
5240 curstat_t *s = (curstat_t *)(void *) client_data;
5241 char tmpbuf[8];
5242
5243 if (sel_pos < 0 || s->mode == MOD_BUSY || s->mode == MOD_NODISC) {
5244 cd_beep();
5245 return;
5246 }
5247
5248 if (s->onetrk_prog) {
5249 /* Currently playing a single-track program: clear it first */
5250 if (w == widgets.dbprog.addpgm_btn)
5251 s->onetrk_prog = FALSE;
5252 dbprog_progclear(s);
5253 }
5254
5255 /* Disable shuffle mode */
5256 if (s->shuffle) {
5257 di_shuffle(s, FALSE);
5258 set_shuffle_btn(FALSE);
5259 }
5260
5261 /* Disable a->b mode */
5262 if (s->segplay != SEGP_NONE)
5263 s->segplay = SEGP_NONE;
5264
5265 if (dbp->playorder != NULL && dbp->playorder[0] == '\0') {
5266 MEM_FREE(dbp->playorder);
5267 dbp->playorder = NULL;
5268 }
5269
5270 if (dbp->playorder == NULL) {
5271 (void) sprintf(tmpbuf, "%u", s->trkinfo[sel_pos-1].trkno);
5272 if (!util_newstr(&dbp->playorder, tmpbuf)) {
5273 CD_FATAL(app_data.str_nomemory);
5274 return;
5275 }
5276 }
5277 else {
5278 (void) sprintf(tmpbuf, ",%u", s->trkinfo[sel_pos-1].trkno);
5279 dbp->playorder = (char *) MEM_REALLOC(
5280 "addpgm",
5281 dbp->playorder,
5282 strlen(dbp->playorder) + strlen(tmpbuf) + 1
5283 );
5284 if (dbp->playorder == NULL) {
5285 CD_FATAL(app_data.str_nomemory);
5286 return;
5287 }
5288 (void) strcat(dbp->playorder, tmpbuf);
5289 }
5290
5291 if (!s->onetrk_prog) {
5292 char *str;
5293 int n;
5294
5295 set_text_string(
5296 widgets.dbprog.pgmseq_txt, dbp->playorder, FALSE
5297 );
5298
5299 str = XmTextGetString(widgets.dbprog.pgmseq_txt);
5300 n = strlen(str);
5301 XtFree(str);
5302
5303 XmTextSetInsertionPosition(widgets.dbprog.pgmseq_txt, n);
5304 }
5305
5306 s->program = TRUE;
5307
5308 /* Update display */
5309 dpy_progmode(s, FALSE);
5310
5311 if (!s->onetrk_prog) {
5312 XtSetSensitive(widgets.dbprog.clrpgm_btn, True);
5313 XtSetSensitive(widgets.dbprog.savepgm_btn, True);
5314 }
5315
5316 XtSetSensitive(widgets.dbprog.addpgm_btn, False);
5317 XtSetSensitive(widgets.dbprog.extt_btn, False);
5318 XtSetSensitive(widgets.dbprog.tcredits_btn, False);
5319
5320 /* Put focus on clear button */
5321 XmProcessTraversal(widgets.dbprog.clrpgm_btn, XmTRAVERSE_CURRENT);
5322
5323 /* Update button labels */
5324 dbprog_extt_lblupd(NULL);
5325 dbprog_tcred_lblupd(NULL);
5326
5327 if (sel_pos > 0) {
5328 XmListDeselectPos(widgets.dbprog.trk_list, sel_pos);
5329 sel_pos = -1;
5330 }
5331
5332 set_text_string(widgets.dbprog.ttitle_txt, "", FALSE);
5333 }
5334
5335
5336 /*
5337 * dbprog_clrpgm
5338 * Program Clear button callback.
5339 */
5340 /*ARGSUSED*/
5341 void
dbprog_clrpgm(Widget w,XtPointer client_data,XtPointer call_data)5342 dbprog_clrpgm(Widget w, XtPointer client_data, XtPointer call_data)
5343 {
5344 curstat_t *s = (curstat_t *)(void *) client_data;
5345
5346 if (s->onetrk_prog) {
5347 /* Currently playing a single-track program: clear it first */
5348 s->onetrk_prog = FALSE;
5349 dbprog_progclear(s);
5350 }
5351 else if (sel_pos > 0) {
5352 XmListDeselectPos(widgets.dbprog.trk_list, sel_pos);
5353 sel_pos = -1;
5354 set_text_string(widgets.dbprog.ttitle_txt, "", FALSE);
5355 }
5356
5357 set_text_string(widgets.dbprog.pgmseq_txt, "", FALSE);
5358 XtSetSensitive(widgets.dbprog.addpgm_btn, False);
5359 XtSetSensitive(widgets.dbprog.clrpgm_btn, False);
5360 XtSetSensitive(widgets.dbprog.savepgm_btn, False);
5361 XtSetSensitive(widgets.dbprog.extt_btn, False);
5362 XtSetSensitive(widgets.dbprog.tcredits_btn, False);
5363
5364 /* Update button labels */
5365 dbprog_extt_lblupd(NULL);
5366 dbprog_tcred_lblupd(NULL);
5367
5368 /* Put focus on OK button */
5369 XmProcessTraversal(widgets.dbprog.ok_btn, XmTRAVERSE_CURRENT);
5370 }
5371
5372
5373 /*
5374 * dbprog_savepgm
5375 * Program Save button callback.
5376 */
5377 /*ARGSUSED*/
5378 void
dbprog_savepgm(Widget w,XtPointer client_data,XtPointer call_data)5379 dbprog_savepgm(Widget w, XtPointer client_data, XtPointer call_data)
5380 {
5381 curstat_t *s = (curstat_t *)(void *) client_data;
5382 cdinfo_ret_t ret;
5383
5384 if (!s->program)
5385 return;
5386
5387 /* Save user-defined track program to file */
5388 if ((ret = cdinfo_save_prog(s)) != 0) {
5389 cd_beep();
5390 DBGPRN(DBG_CDI)(errfp, "cdinfo_save_prog: status=%d arg=%d\n",
5391 CDINFO_GET_STAT(ret), CDINFO_GET_ARG(ret));
5392 return;
5393 }
5394
5395 XtSetSensitive(widgets.dbprog.savepgm_btn, False);
5396
5397 /* Put focus on OK button */
5398 XmProcessTraversal(widgets.dbprog.ok_btn, XmTRAVERSE_CURRENT);
5399 }
5400
5401
5402 /*
5403 * dbprog_submit
5404 * CDDB Submit pushbutton callback.
5405 */
5406 /*ARGSUSED*/
5407 void
dbprog_submit(Widget w,XtPointer client_data,XtPointer call_data)5408 dbprog_submit(Widget w, XtPointer client_data, XtPointer call_data)
5409 {
5410 (void) cd_confirm_popup(
5411 app_data.str_confirm, app_data.str_submit,
5412 (XtCallbackProc) dbprog_submit_yes, client_data,
5413 (XtCallbackProc) NULL, NULL
5414 );
5415 }
5416
5417
5418 /*
5419 * dbprog_submit_popup
5420 * CDDB Submit dialog popup timer function
5421 */
5422 /*ARGSUSED*/
5423 void
dbprog_submit_popup(Widget w,XtPointer client_data,XtPointer call_data)5424 dbprog_submit_popup(Widget w, XtPointer client_data, XtPointer call_data)
5425 {
5426 dbprog_do_submit_popup();
5427 }
5428
5429
5430 /*
5431 * dbprog_submit_yes
5432 * CDDB Submit dialog "Yes" pushbutton callback.
5433 */
5434 /*ARGSUSED*/
5435 void
dbprog_submit_yes(Widget w,XtPointer client_data,XtPointer call_data)5436 dbprog_submit_yes(Widget w, XtPointer client_data, XtPointer call_data)
5437 {
5438 (void) dbprog_dbsubmit((curstat_t *)(void *) client_data);
5439 }
5440
5441
5442 /*
5443 * dbprog_flush
5444 * CD information cache FLUSH button callback.
5445 */
5446 /*ARGSUSED*/
5447 void
dbprog_flush(Widget w,XtPointer client_data,XtPointer call_data)5448 dbprog_flush(Widget w, XtPointer client_data, XtPointer call_data)
5449 {
5450 curstat_t *s = (curstat_t *)(void *) client_data;
5451 cdinfo_ret_t ret;
5452
5453 /* Change to watch cursor */
5454 cd_busycurs(TRUE, CURS_ALL);
5455
5456 if ((ret = cdinfo_flush(s)) != 0) {
5457 cd_beep();
5458 DBGPRN(DBG_CDI)(errfp, "cdinfo_flush: status=%d arg=%d\n",
5459 CDINFO_GET_STAT(ret), CDINFO_GET_ARG(ret));
5460 }
5461
5462 /* Change to normal cursor */
5463 cd_busycurs(FALSE, CURS_ALL);
5464 }
5465
5466
5467 /*
5468 * dbprog_load
5469 * CD information RELOAD button callback.
5470 */
5471 /*ARGSUSED*/
5472 void
dbprog_load(Widget w,XtPointer client_data,XtPointer call_data)5473 dbprog_load(Widget w, XtPointer client_data, XtPointer call_data)
5474 {
5475 curstat_t *s = (curstat_t *)(void *) client_data;
5476
5477 if (s->mode == MOD_BUSY || s->mode == MOD_NODISC) {
5478 cd_beep();
5479 return;
5480 }
5481
5482 /* Re-load CD information */
5483 dbprog_dbget(s);
5484 }
5485
5486
5487 /*
5488 * dbprog_stop_load_yes
5489 * CD information STOP LOAD dialog 'yes' button callback.
5490 */
5491 /*ARGSUSED*/
5492 void
dbprog_stop_load_yes(Widget w,XtPointer client_data,XtPointer call_data)5493 dbprog_stop_load_yes(Widget w, XtPointer client_data, XtPointer call_data)
5494 {
5495 stopload_active = FALSE;
5496 cdinfo_load_cancel();
5497 }
5498
5499
5500 /*
5501 * dbprog_stop_load_no
5502 * CD information STOP LOAD dialog 'no' button callback.
5503 */
5504 /*ARGSUSED*/
5505 void
dbprog_stop_load_no(Widget w,XtPointer client_data,XtPointer call_data)5506 dbprog_stop_load_no(Widget w, XtPointer client_data, XtPointer call_data)
5507 {
5508 stopload_active = FALSE;
5509 }
5510
5511
5512 /*
5513 * dbprog_ok
5514 * Pop down CD info/program window.
5515 */
5516 /*ARGSUSED*/
5517 void
dbprog_ok(Widget w,XtPointer client_data,XtPointer call_data)5518 dbprog_ok(Widget w, XtPointer client_data, XtPointer call_data)
5519 {
5520 XmPushButtonCallbackStruct
5521 *p = (XmPushButtonCallbackStruct *)(void *) call_data;
5522 curstat_t *s = (curstat_t *)(void *) client_data;
5523
5524 if (XtIsManaged(widgets.dlist.form)) {
5525 /* Force a popdown of the dlist window */
5526 dbprog_dlist_cancel(
5527 widgets.dlist.cancel_btn,
5528 (XtPointer) s,
5529 (XtPointer) p
5530 );
5531 }
5532 if (XtIsManaged(widgets.fullname.form)) {
5533 /* Force a popdown of the fullname window */
5534 dbprog_fullname_ok(
5535 widgets.fullname.ok_btn,
5536 (XtPointer) s,
5537 (XtPointer) p
5538 );
5539 }
5540 if (XtIsManaged(widgets.dbextd.form)) {
5541 /* Force a popdown of the disc details window */
5542 dbprog_extd_ok(
5543 widgets.dbextd.ok_btn,
5544 (XtPointer) s,
5545 (XtPointer) p
5546 );
5547 }
5548 if (XtIsManaged(widgets.dbextt.form)) {
5549 /* Force a popdown of the track details window */
5550 dbprog_extt_ok(
5551 widgets.dbextt.ok_btn,
5552 (XtPointer) s,
5553 (XtPointer) p
5554 );
5555 }
5556 if (XtIsManaged(widgets.credits.form)) {
5557 /* Force a popdown of the credits window */
5558 dbprog_credits_popdown(
5559 widgets.credits.ok_btn,
5560 (XtPointer) s,
5561 (XtPointer) p
5562 );
5563 }
5564 if (XtIsManaged(widgets.segments.form)) {
5565 /* Force a popdown of the segments window */
5566 dbprog_segments_popdown(
5567 widgets.segments.ok_btn,
5568 (XtPointer) s,
5569 (XtPointer) p
5570 );
5571 }
5572
5573 /* Pop down the CD info/program window */
5574 XtUnmapWidget(XtParent(widgets.dbprog.form));
5575 XtUnmanageChild(widgets.dbprog.form);
5576 }
5577
5578
5579 /*
5580 * dbprog_do_clear
5581 * Changed-save dialog box pushbuttons callback.
5582 */
5583 /*ARGSUSED*/
5584 void
dbprog_do_clear(Widget w,XtPointer client_data,XtPointer call_data)5585 dbprog_do_clear(Widget w, XtPointer client_data, XtPointer call_data)
5586 {
5587 curstat_t *s = curstat_addr();
5588 word32_t flags = (word32_t)(unsigned long) client_data;
5589
5590 dbp->flags &= ~CDINFO_CHANGED;
5591 s->flags |= (flags & 0xffff);
5592
5593 /* Clear in-core CD information */
5594 dbprog_dbclear(s, FALSE);
5595 }
5596
5597
5598 /*
5599 * dbprog_timedpy
5600 * Toggle the time display mode in the track list.
5601 */
5602 /*ARGSUSED*/
5603 void
dbprog_timedpy(Widget w,XtPointer client_data,XtPointer call_data)5604 dbprog_timedpy(Widget w, XtPointer client_data, XtPointer call_data)
5605 {
5606 XmRowColumnCallbackStruct *p =
5607 (XmRowColumnCallbackStruct *)(void *) call_data;
5608 XmToggleButtonCallbackStruct *q;
5609 curstat_t *s = (curstat_t *)(void *) client_data;
5610
5611 if (p == NULL)
5612 return;
5613
5614 q = (XmToggleButtonCallbackStruct *)(void *) p->callbackstruct;
5615
5616 if (!q->set)
5617 return;
5618
5619 /* Overload the function of these buttons to also
5620 * dump the contents of the cdinfo_incore_t structure
5621 * in debug mode
5622 */
5623 if (app_data.debug & DBG_ALL)
5624 cdinfo_dump_incore(s);
5625
5626 if (p->widget == widgets.dbprog.tottime_btn) {
5627 if (time_mode == TIME_TOTAL)
5628 return; /* No change */
5629
5630 time_mode = TIME_TOTAL;
5631 }
5632 else if (p->widget == widgets.dbprog.trktime_btn) {
5633 if (time_mode == TIME_TRACK)
5634 return; /* No change */
5635
5636 time_mode = TIME_TRACK;
5637 }
5638 else
5639 return; /* Invalid widget */
5640
5641 if (s->mode != MOD_BUSY && s->mode != MOD_NODISC)
5642 /* Update track list with new time display mode */
5643 dbprog_listupd_all(s, FALSE);
5644 }
5645
5646
5647 /*
5648 * dbprog_set_changed
5649 * Set the flag indicating that the user has made changes to the
5650 * in-core CD information.
5651 */
5652 /*ARGSUSED*/
5653 void
dbprog_set_changed(Widget w,XtPointer client_data,XtPointer call_data)5654 dbprog_set_changed(Widget w, XtPointer client_data, XtPointer call_data)
5655 {
5656 XmAnyCallbackStruct *p = (XmAnyCallbackStruct *)(void *) call_data;
5657
5658 if (p->reason != XmCR_VALUE_CHANGED)
5659 return;
5660
5661 /* Setup of the track details window is not a user change */
5662 if (!extt_setup) {
5663 dbp->flags |= CDINFO_CHANGED;
5664 if (!app_data.cdinfo_inetoffln)
5665 XtSetSensitive(widgets.dbprog.submit_btn, True);
5666 }
5667 }
5668
5669
5670 /*
5671 * dbprog_fullname
5672 * Pop up/down the fullname window.
5673 */
5674 void
dbprog_fullname(Widget w,XtPointer client_data,XtPointer call_data)5675 dbprog_fullname(Widget w, XtPointer client_data, XtPointer call_data)
5676 {
5677 bool_t user_req = (bool_t)(unsigned long) client_data;
5678 curstat_t *s = curstat_addr();
5679 int new_mode;
5680 cdinfo_fname_t *p;
5681 XmString xs;
5682 Widget nextw;
5683 char *str,
5684 buf2[32],
5685 buf[STR_BUF_SZ * 2];
5686 bool_t fname_err;
5687
5688 /* Check existing fullname for correctness, if needed */
5689 fname_err = !dbprog_fullname_ck(s);
5690 if (fname_err && user_req && fname_mode != FNAME_NONE)
5691 return;
5692
5693 /* Set fullname window mode */
5694 if (w == widgets.dbprog.fullname_btn) {
5695 if (user_req && fname_mode == FNAME_DISC) {
5696 /* Pop down the full name window */
5697 dbprog_fullname_ok(
5698 widgets.fullname.ok_btn,
5699 (XtPointer) s, call_data
5700 );
5701 return;
5702 }
5703 new_mode = FNAME_DISC;
5704 p = &dbp->disc.artistfname;
5705 }
5706 else if (w == widgets.dbextt.fullname_btn) {
5707 if (user_req && fname_mode == FNAME_TRACK) {
5708 /* Pop down the full name window */
5709 dbprog_fullname_ok(
5710 widgets.fullname.ok_btn,
5711 (XtPointer) s, call_data
5712 );
5713 return;
5714 }
5715 new_mode = FNAME_TRACK;
5716 p = &dbp->track[extt_pos].artistfname;
5717 }
5718 else if (w == widgets.credits.fullname_btn) {
5719 if (user_req && fname_mode == FNAME_CREDITS) {
5720 /* Pop down the full name window */
5721 dbprog_fullname_ok(
5722 widgets.fullname.ok_btn,
5723 (XtPointer) s, call_data
5724 );
5725 return;
5726 }
5727 new_mode = FNAME_CREDITS;
5728 p = &w_cred.crinfo.fullname;
5729 }
5730 else
5731 /* Invalid widget */
5732 return;
5733
5734 if (fname_mode != FNAME_NONE && new_mode != fname_mode) {
5735 /* Pop down the full name window */
5736 dbprog_fullname_ok(
5737 widgets.fullname.ok_btn,
5738 (XtPointer) s, call_data
5739 );
5740 }
5741
5742 fname_mode = new_mode;
5743
5744 /* Update fullname window head label */
5745 switch (fname_mode) {
5746 case FNAME_DISC:
5747 (void) sprintf(buf, "%.31s", app_data.str_albumartist);
5748 break;
5749 case FNAME_TRACK:
5750 (void) sprintf(buf2, "%.31s", app_data.str_trackartist);
5751 (void) sprintf(buf, buf2, s->trkinfo[extt_pos].trkno);
5752 break;
5753 case FNAME_CREDITS:
5754 (void) sprintf(buf, "%.31s", app_data.str_credit);
5755 break;
5756 }
5757 (void) sprintf(buf, "%s\n%.90s", buf, app_data.str_fnameguide);
5758
5759 xs = create_xmstring(buf, NULL, XmSTRING_DEFAULT_CHARSET, TRUE);
5760 XtVaSetValues(widgets.fullname.head_lbl,
5761 XmNlabelString, xs,
5762 NULL
5763 );
5764 XmStringFree(xs);
5765
5766 /* Set autogen button default state */
5767 if ((str = dbprog_autoname(p)) == NULL) {
5768 CD_FATAL(app_data.str_nomemory);
5769 return;
5770 }
5771 if (p->dispname != NULL && strcmp(p->dispname, str) != 0) {
5772 XmToggleButtonSetState(
5773 widgets.fullname.autogen_btn, False, False
5774 );
5775 XtVaSetValues(
5776 widgets.fullname.dispname_txt,
5777 XmNeditable, True,
5778 XmNcursorPositionVisible, True,
5779 XmNcursorPosition, 0,
5780 NULL
5781 );
5782 }
5783 else {
5784 XmToggleButtonSetState(
5785 widgets.fullname.autogen_btn, True, False
5786 );
5787 XtVaSetValues(
5788 widgets.fullname.dispname_txt,
5789 XmNeditable, False,
5790 XmNcursorPositionVisible, False,
5791 XmNcursorPosition, 0,
5792 NULL
5793 );
5794 }
5795 MEM_FREE(str);
5796
5797 /* Update fullname widgets */
5798 if (p->dispname != NULL)
5799 set_text_string(
5800 widgets.fullname.dispname_txt, p->dispname, TRUE
5801 );
5802 else
5803 set_text_string(widgets.fullname.dispname_txt, "", FALSE);
5804
5805 if (p->lastname != NULL)
5806 set_text_string(
5807 widgets.fullname.lastname_txt, p->lastname, TRUE
5808 );
5809 else
5810 set_text_string(widgets.fullname.lastname_txt, "", FALSE);
5811
5812 if (p->firstname != NULL)
5813 set_text_string(
5814 widgets.fullname.firstname_txt, p->firstname, TRUE
5815 );
5816 else
5817 set_text_string(widgets.fullname.firstname_txt, "", FALSE);
5818
5819 if (p->the != NULL) {
5820 XmToggleButtonSetState(widgets.fullname.the_btn, True, False);
5821 set_text_string(widgets.fullname.the_txt, p->the, TRUE);
5822 XtSetSensitive(widgets.fullname.the_txt, True);
5823 }
5824 else {
5825 XmToggleButtonSetState(widgets.fullname.the_btn, False, False);
5826 set_text_string(widgets.fullname.the_txt, "", FALSE);
5827 XtSetSensitive(widgets.fullname.the_txt, False);
5828 }
5829
5830 /* Pop up the fullname window.
5831 * The dialog has mappedWhenManaged set to False,
5832 * so we have to map/unmap explicitly. The reason
5833 * for this is we want to avoid a screen glitch when
5834 * we move the window in cd_dialog_setpos(), so we
5835 * map the window afterwards.
5836 */
5837 if (!XtIsManaged(widgets.fullname.form)) {
5838 XtManageChild(widgets.fullname.form);
5839
5840 /* Set up dialog box position */
5841 cd_dialog_setpos(XtParent(widgets.fullname.form));
5842
5843 XtMapWidget(XtParent(widgets.fullname.form));
5844 }
5845
5846 /* Put input focus on the appropriate widget */
5847 if (p->firstname == NULL)
5848 nextw = widgets.fullname.firstname_txt;
5849 else if (p->lastname == NULL)
5850 nextw = widgets.fullname.lastname_txt;
5851 else
5852 nextw = widgets.fullname.ok_btn;
5853
5854 XmProcessTraversal(nextw, XmTRAVERSE_CURRENT);
5855 }
5856
5857
5858 /*
5859 * dbprog_fullname_autogen
5860 * Full name window autogen button callback.
5861 */
5862 /*ARGSUSED*/
5863 void
dbprog_fullname_autogen(Widget w,XtPointer client_data,XtPointer call_data)5864 dbprog_fullname_autogen(Widget w, XtPointer client_data, XtPointer call_data)
5865 {
5866 XmToggleButtonCallbackStruct *p =
5867 (XmToggleButtonCallbackStruct *)(void *) call_data;
5868 cdinfo_fname_t *ent;
5869
5870 XtVaSetValues(widgets.fullname.dispname_txt,
5871 XmNeditable, !p->set,
5872 XmNcursorPositionVisible, !p->set,
5873 XmNcursorPosition, 0,
5874 NULL
5875 );
5876
5877 switch (fname_mode) {
5878 case FNAME_DISC:
5879 ent = &dbp->disc.artistfname;
5880 break;
5881 case FNAME_TRACK:
5882 if (extt_pos < 0)
5883 return;
5884 ent = &dbp->track[extt_pos].artistfname;
5885 break;
5886 case FNAME_CREDITS:
5887 ent = &w_cred.crinfo.fullname;
5888 break;
5889 default:
5890 return;
5891 }
5892
5893 if (p->set) {
5894 char *cp;
5895
5896 /* Auto-generate the name */
5897 if ((cp = dbprog_autoname(ent)) == NULL) {
5898 CD_FATAL(app_data.str_nomemory);
5899 return;
5900 }
5901 if (cp[0] != '\0') {
5902 set_text_string(
5903 widgets.fullname.dispname_txt, cp, TRUE
5904 );
5905 }
5906 MEM_FREE(cp);
5907
5908 /* Put focus on firstname field */
5909 XmProcessTraversal(
5910 widgets.fullname.firstname_txt,
5911 XmTRAVERSE_CURRENT
5912 );
5913 }
5914 else {
5915 /* The Display name is now editable, so put focus on it */
5916 XmProcessTraversal(
5917 widgets.fullname.dispname_txt,
5918 XmTRAVERSE_CURRENT
5919 );
5920 }
5921 }
5922
5923
5924 /*
5925 * dbprog_fullname_ok
5926 * Full name window OK button callback.
5927 */
5928 /*ARGSUSED*/
5929 void
dbprog_fullname_ok(Widget w,XtPointer client_data,XtPointer call_data)5930 dbprog_fullname_ok(Widget w, XtPointer client_data, XtPointer call_data)
5931 {
5932 curstat_t *s = (curstat_t *)(void *) client_data;
5933 Widget nextw;
5934
5935 switch (fname_mode) {
5936 case FNAME_DISC:
5937 nextw = NULL;
5938 break;
5939 case FNAME_TRACK:
5940 nextw = widgets.dbextt.year_txt;
5941 break;
5942 case FNAME_CREDITS:
5943 nextw = widgets.credits.notes_txt;
5944 break;
5945 default:
5946 return;
5947 }
5948
5949 /* Check fullname for correctness */
5950 if (!dbprog_fullname_ck(s))
5951 return;
5952
5953 /* Pop down the fullname popup */
5954 XtUnmapWidget(XtParent(widgets.fullname.form));
5955 XtUnmanageChild(widgets.fullname.form);
5956
5957 if (nextw != NULL)
5958 /* Put keyboard focus on next widget */
5959 XmProcessTraversal(nextw, XmTRAVERSE_CURRENT);
5960
5961 /* Update curfile if needed */
5962 if (fname_changed && fname_mode == FNAME_DISC)
5963 dbprog_curfileupd();
5964
5965 fname_changed = FALSE;
5966 fname_mode = FNAME_NONE;
5967 }
5968
5969
5970 /*
5971 * dbprog_genre_sel
5972 * Genre selector callback
5973 */
5974 /*ARGSUSED*/
5975 void
dbprog_genre_sel(Widget w,XtPointer client_data,XtPointer call_data)5976 dbprog_genre_sel(Widget w, XtPointer client_data, XtPointer call_data)
5977 {
5978 cdinfo_genre_t *p = (cdinfo_genre_t *)(void *) client_data,
5979 *gp,
5980 *genrep,
5981 *subgenrep,
5982 *genre2p,
5983 *subgenre2p;
5984 char *newid,
5985 **ent;
5986 Widget pw;
5987 int pos;
5988
5989 if (dbp->genrelist == NULL)
5990 return;
5991
5992 pw = XtParent(w);
5993 if (pw == widgets.dbextd.genre_menu[0]) {
5994 pos = -1;
5995 dbprog_curgenre(pos, &genrep, &subgenrep,
5996 &genre2p, &subgenre2p);
5997 gp = genrep;
5998 ent = &dbp->disc.genre;
5999 }
6000 else if (pw == widgets.dbextd.genre_menu[1]) {
6001 pos = -1;
6002 dbprog_curgenre(pos, &genrep, &subgenrep,
6003 &genre2p, &subgenre2p);
6004 gp = genre2p;
6005 ent = &dbp->disc.genre2;
6006 }
6007 else if (pw == widgets.dbextt.genre_menu[0]) {
6008 if (extt_pos < 0)
6009 return;
6010 pos = extt_pos;
6011 dbprog_curgenre(pos, &genrep, &subgenrep,
6012 &genre2p, &subgenre2p);
6013 gp = genrep;
6014 ent = &dbp->track[pos].genre;
6015 }
6016 else if (pw == widgets.dbextt.genre_menu[1]) {
6017 if (extt_pos < 0)
6018 return;
6019 pos = extt_pos;
6020 dbprog_curgenre(pos, &genrep, &subgenrep,
6021 &genre2p, &subgenre2p);
6022 gp = genre2p;
6023 ent = &dbp->track[pos].genre2;
6024 }
6025 else
6026 return; /* Invalid widget */
6027
6028 if (p != gp) {
6029 dbp->flags |= CDINFO_CHANGED;
6030 if (!app_data.cdinfo_inetoffln)
6031 XtSetSensitive(widgets.dbprog.submit_btn, True);
6032 }
6033
6034 if (*ent != NULL) {
6035 MEM_FREE(*ent);
6036 *ent = NULL;
6037 }
6038
6039 if (p != NULL) {
6040 newid = NULL;
6041
6042 for (genrep = dbp->genrelist; genrep != NULL;
6043 genrep = genrep->next) {
6044 if (strcmp(genrep->id, p->id) != 0)
6045 continue;
6046
6047 if (genrep->child != NULL) {
6048 /* Look for the first "General xxx" where
6049 * xxx is the main genre, and set
6050 * the default subgenre to it.
6051 */
6052 for (subgenrep = genrep->child;
6053 subgenrep != NULL;
6054 subgenrep = subgenrep->next) {
6055 if (strncmp(subgenrep->name,
6056 "General ", 8) == 0 &&
6057 strcmp(subgenrep->name + 8,
6058 genrep->name) == 0)
6059 break;
6060 }
6061
6062 if (subgenrep != NULL) {
6063 /* Found it */
6064 newid = subgenrep->id;
6065 }
6066 else {
6067 /* Not found: just set the default
6068 * subgenre to the first one in the
6069 * list
6070 */
6071 newid = genrep->child->id;
6072 }
6073 }
6074 else {
6075 /* This shouldn't happen, but in case a
6076 * there is associated no subgenres,
6077 * then just set it to the primary genre
6078 * itself
6079 */
6080 newid = genrep->id;
6081 }
6082 }
6083
6084 if (!util_newstr(ent, newid)) {
6085 CD_FATAL(app_data.str_nomemory);
6086 return;
6087 }
6088 }
6089
6090 dbprog_genreupd(pos);
6091 }
6092
6093
6094 /*
6095 * dbprog_subgenre_sel
6096 * Subgenre selector callback
6097 */
6098 /*ARGSUSED*/
6099 void
dbprog_subgenre_sel(Widget w,XtPointer client_data,XtPointer call_data)6100 dbprog_subgenre_sel(Widget w, XtPointer client_data, XtPointer call_data)
6101 {
6102 cdinfo_genre_t *p = (cdinfo_genre_t *)(void *) client_data,
6103 *gp,
6104 *genrep,
6105 *subgenrep,
6106 *genre2p,
6107 *subgenre2p;
6108 char **ent;
6109 Widget pw,
6110 optw,
6111 nonew;
6112 int pos;
6113
6114 if (dbp->genrelist == NULL)
6115 return;
6116
6117 pw = XtParent(w);
6118 if (pw == widgets.dbextd.subgenre_menu[0]) {
6119 pos = -1;
6120 optw = widgets.dbextd.subgenre_opt[0];
6121 nonew = widgets.dbextd.genre_none_btn[0];
6122 dbprog_curgenre(pos, &genrep, &subgenrep,
6123 &genre2p, &subgenre2p);
6124 gp = subgenrep;
6125 ent = &dbp->disc.genre;
6126 }
6127 else if (pw == widgets.dbextd.subgenre_menu[1]) {
6128 pos = -1;
6129 optw = widgets.dbextd.subgenre_opt[1];
6130 nonew = widgets.dbextd.genre_none_btn[1];
6131 dbprog_curgenre(pos, &genrep, &subgenrep,
6132 &genre2p, &subgenre2p);
6133 gp = subgenre2p;
6134 ent = &dbp->disc.genre2;
6135 }
6136 else if (pw == widgets.dbextt.subgenre_menu[0]) {
6137 if (extt_pos < 0)
6138 return;
6139
6140 pos = extt_pos;
6141 optw = widgets.dbextt.subgenre_opt[0];
6142 nonew = widgets.dbextt.genre_none_btn[0];
6143 dbprog_curgenre(pos, &genrep, &subgenrep,
6144 &genre2p, &subgenre2p);
6145 gp = subgenrep;
6146 ent = &dbp->track[pos].genre;
6147 }
6148 else if (pw == widgets.dbextt.subgenre_menu[1]) {
6149 if (extt_pos < 0)
6150 return;
6151
6152 pos = extt_pos;
6153 optw = widgets.dbextt.subgenre_opt[1];
6154 nonew = widgets.dbextt.genre_none_btn[1];
6155 dbprog_curgenre(pos, &genrep, &subgenrep,
6156 &genre2p, &subgenre2p);
6157 gp = subgenre2p;
6158 ent = &dbp->track[pos].genre2;
6159 }
6160 else
6161 return; /* Invalid widget */
6162
6163 if (p != gp) {
6164 dbp->flags |= CDINFO_CHANGED;
6165 if (!app_data.cdinfo_inetoffln)
6166 XtSetSensitive(widgets.dbprog.submit_btn, True);
6167 }
6168
6169 if (*ent != NULL) {
6170 MEM_FREE(*ent);
6171 *ent = NULL;
6172 }
6173
6174 if (p == NULL || p->id == NULL) {
6175 XtVaSetValues(optw, XmNmenuHistory, nonew, NULL);
6176 }
6177 else if (!util_newstr(ent, p->id)) {
6178 CD_FATAL(app_data.str_nomemory);
6179 return;
6180 }
6181
6182 dbprog_genreupd(pos);
6183 }
6184
6185
6186 /*
6187 * dbprog_role_sel
6188 * Role selector callback
6189 */
6190 /*ARGSUSED*/
6191 void
dbprog_role_sel(Widget w,XtPointer client_data,XtPointer call_data)6192 dbprog_role_sel(Widget w, XtPointer client_data, XtPointer call_data)
6193 {
6194 cdinfo_role_t *p = (cdinfo_role_t *)(void *) client_data,
6195 *q,
6196 *sr;
6197 int i;
6198 Dimension x;
6199 Arg arg[10];
6200 static cdinfo_role_t *save_role = NULL,
6201 *save_trole = NULL,
6202 *save_srole = NULL;
6203
6204 if (dbp->rolelist == NULL)
6205 return;
6206
6207 switch (credits_mode) {
6208 case CREDITS_DISC:
6209 sr = save_role;
6210 break;
6211 case CREDITS_TRACK:
6212 sr = save_trole;
6213 break;
6214 case CREDITS_SEG:
6215 sr = save_srole;
6216 break;
6217 default:
6218 return; /* Invalid mode */
6219 }
6220
6221 if (cred_pos > 0)
6222 XtSetSensitive(widgets.credits.mod_btn, True);
6223 else
6224 XtSetSensitive(widgets.credits.add_btn, True);
6225
6226 /* If the primary role changed, re-create the associated
6227 * sub-role menu entries
6228 */
6229 if (sr != NULL) {
6230 for (q = sr->child; q != NULL; q = q->next) {
6231 /* Delete old sub-role entries */
6232 if (q->aux != NULL) {
6233 XtUnmanageChild((Widget) q->aux);
6234 XtDestroyWidget((Widget) q->aux);
6235 q->aux = NULL;
6236 }
6237 }
6238 }
6239
6240 if (p != NULL) {
6241 for (q = p->child; q != NULL; q = q->next) {
6242 /* Create new sub-role entries */
6243 i = 0;
6244 XtSetArg(arg[i], XmNinitialResourcesPersistent,
6245 False); i++;
6246 XtSetArg(arg[i], XmNshadowThickness, 2); i++;
6247 q->aux = (void *) XmCreatePushButton(
6248 widgets.credits.subrole_menu,
6249 q->name,
6250 arg,
6251 i
6252 );
6253 XtManageChild((Widget) q->aux);
6254 register_activate_cb((Widget) q->aux,
6255 dbprog_subrole_sel, q);
6256
6257 if (q == p->child) {
6258 /* Set default sub-role to the first entry */
6259 XtVaSetValues(
6260 widgets.credits.subrole_opt,
6261 XmNmenuHistory, q->aux,
6262 NULL
6263 );
6264 w_cred.crinfo.role = q;
6265 }
6266 }
6267 }
6268 else {
6269 /* Set sub-role selector to "none" */
6270 XtVaSetValues(
6271 widgets.credits.subrole_opt,
6272 XmNmenuHistory, widgets.credits.subrole_none_btn,
6273 NULL
6274 );
6275 w_cred.crinfo.role = NULL;
6276 }
6277
6278 switch (credits_mode) {
6279 case CREDITS_DISC:
6280 save_role = p;
6281 break;
6282 case CREDITS_TRACK:
6283 save_trole = p;
6284 break;
6285 case CREDITS_SEG:
6286 save_srole = p;
6287 break;
6288 default:
6289 break;
6290 }
6291
6292 /* Hack: Force the subrole option menu to resize to match
6293 * the selected label.
6294 */
6295 XtVaGetValues(widgets.credits.form, XmNwidth, &x, NULL);
6296 XtVaSetValues(widgets.credits.form, XmNwidth, x+1, NULL);
6297 XtVaSetValues(widgets.credits.form, XmNwidth, x, NULL);
6298 }
6299
6300
6301 /*
6302 * dbprog_subrole_sel
6303 * Subrole selector callback
6304 */
6305 /*ARGSUSED*/
6306 void
dbprog_subrole_sel(Widget w,XtPointer client_data,XtPointer call_data)6307 dbprog_subrole_sel(Widget w, XtPointer client_data, XtPointer call_data)
6308 {
6309 cdinfo_role_t *p = (cdinfo_role_t *)(void *) client_data;
6310
6311 if (dbp->rolelist == NULL)
6312 return;
6313
6314 if (p == NULL) {
6315 /* "None" selected: set the primary role to "None" too */
6316 XtVaSetValues(
6317 widgets.credits.prirole_opt,
6318 XmNmenuHistory, widgets.credits.prirole_none_btn,
6319 NULL
6320 );
6321 XtCallCallbacks(
6322 widgets.credits.prirole_none_btn,
6323 XmNactivateCallback, (XtPointer) NULL
6324 );
6325 }
6326
6327 if (cred_pos > 0)
6328 XtSetSensitive(widgets.credits.mod_btn, True);
6329 else
6330 XtSetSensitive(widgets.credits.add_btn, True);
6331
6332 w_cred.crinfo.role = p;
6333 }
6334
6335
6336 /*
6337 * dbprog_extd
6338 * Pop up/down the disc details window.
6339 */
6340 void
dbprog_extd(Widget w,XtPointer client_data,XtPointer call_data)6341 dbprog_extd(Widget w, XtPointer client_data, XtPointer call_data)
6342 {
6343 curstat_t *s = (curstat_t *)(void *) client_data;
6344 static bool_t first = TRUE;
6345
6346 if (XtIsManaged(widgets.dbextd.form)) {
6347 /* Pop down the Disc details window */
6348 dbprog_extd_ok(w, client_data, call_data);
6349 return;
6350 }
6351
6352 /* Update the disc title */
6353 dbprog_set_disc_title(s->cur_disc, dbp->disc.artist, dbp->disc.title);
6354
6355 /* Pop up the disc details window.
6356 * The dialog has mappedWhenManaged set to False,
6357 * so we have to map/unmap explicitly. The reason
6358 * for this is we want to avoid a screen glitch when
6359 * we move the window in cd_dialog_setpos(), so we
6360 * map the window afterwards.
6361 */
6362 XtManageChild(widgets.dbextd.form);
6363 if (first) {
6364 first = FALSE;
6365 /* Set up window position */
6366 cd_dialog_setpos(XtParent(widgets.dbextd.form));
6367 }
6368 XtMapWidget(XtParent(widgets.dbextd.form));
6369
6370 /* Put input focus on the OK button */
6371 XmProcessTraversal(widgets.dbextd.ok_btn, XmTRAVERSE_CURRENT);
6372 }
6373
6374
6375 /*
6376 * dbprog_extd_compilation
6377 * Disc details compilation toggle button callback
6378 */
6379 /*ARGSUSED*/
6380 void
dbprog_extd_compilation(Widget w,XtPointer client_data,XtPointer call_data)6381 dbprog_extd_compilation(Widget w, XtPointer client_data, XtPointer call_data)
6382 {
6383 XmToggleButtonCallbackStruct *p =
6384 (XmToggleButtonCallbackStruct *)(void *) call_data;
6385
6386 if (dbp->disc.compilation == p->set)
6387 /* No change */
6388 return;
6389
6390 dbp->disc.compilation = p->set;
6391 dbp->flags |= CDINFO_CHANGED;
6392 if (!app_data.cdinfo_inetoffln)
6393 XtSetSensitive(widgets.dbprog.submit_btn, True);
6394 }
6395
6396
6397 /*
6398 * dbprog_extd_ok
6399 * Disc details window OK button callback.
6400 */
6401 /*ARGSUSED*/
6402 void
dbprog_extd_ok(Widget w,XtPointer client_data,XtPointer call_data)6403 dbprog_extd_ok(Widget w, XtPointer client_data, XtPointer call_data)
6404 {
6405 /* Pop down the Disc details window */
6406 XtUnmapWidget(XtParent(widgets.dbextd.form));
6407 XtUnmanageChild(widgets.dbextd.form);
6408 }
6409
6410
6411 /*
6412 * dbprog_extt
6413 * Pop up/down the track details window.
6414 */
6415 void
dbprog_extt(Widget w,XtPointer client_data,XtPointer call_data)6416 dbprog_extt(Widget w, XtPointer client_data, XtPointer call_data)
6417 {
6418 bool_t from_main = (bool_t)(unsigned long) client_data,
6419 managed;
6420 curstat_t *s = curstat_addr();
6421 static bool_t first = TRUE;
6422
6423 managed = (bool_t) XtIsManaged(widgets.dbextt.form);
6424 if (managed) {
6425 if (from_main) {
6426 /* Pop down the Track details window */
6427 dbprog_extt_ok(w, client_data, call_data);
6428 return;
6429 }
6430 else if (credits_mode != CREDITS_TRACK)
6431 extt_pos = -1;
6432 }
6433
6434 if (sel_pos > 0)
6435 extt_pos = sel_pos - 1;
6436 else if (extt_pos < 0)
6437 return;
6438
6439 /* Enter track details setup mode */
6440 extt_setup = TRUE;
6441
6442 /* Update widgets */
6443 dbprog_exttupd(s, extt_pos);
6444
6445 if (!managed) {
6446 /* Pop up the track details popup.
6447 * The dialog has mappedWhenManaged set to False,
6448 * so we have to map/unmap explicitly. The reason
6449 * for this is we want to avoid a screen glitch when
6450 * we move the window in cd_dialog_setpos(), so we
6451 * map the window afterwards.
6452 */
6453 XtManageChild(widgets.dbextt.form);
6454 if (first) {
6455 first = FALSE;
6456 /* Set window position */
6457 cd_dialog_setpos(XtParent(widgets.dbextt.form));
6458 }
6459 XtMapWidget(XtParent(widgets.dbextt.form));
6460
6461 /* Put input focus on the OK button */
6462 XmProcessTraversal(
6463 widgets.dbextt.ok_btn,
6464 XmTRAVERSE_CURRENT
6465 );
6466 }
6467
6468 /* Exit track details setup mode */
6469 extt_setup = FALSE;
6470 }
6471
6472
6473 /*
6474 * dbprog_extt_prev
6475 * Track details window and credits window previous track button callback.
6476 */
6477 /*ARGSUSED*/
6478 void
dbprog_extt_prev(Widget w,XtPointer client_data,XtPointer call_data)6479 dbprog_extt_prev(Widget w, XtPointer client_data, XtPointer call_data)
6480 {
6481 curstat_t *s = (curstat_t *)(void *) client_data;
6482
6483 /* Check full name data and abort if needed */
6484 if (!dbprog_fullname_ck(s)) {
6485 cd_beep();
6486 return;
6487 }
6488
6489 /* Put input focus on the OK button */
6490 if (XtIsManaged(widgets.dbextt.form))
6491 XmProcessTraversal(widgets.dbextt.ok_btn, XmTRAVERSE_CURRENT);
6492 if (XtIsManaged(widgets.credits.form))
6493 XmProcessTraversal(widgets.credits.ok_btn, XmTRAVERSE_CURRENT);
6494
6495 if (extt_pos <= 0) {
6496 cd_beep();
6497 return;
6498 }
6499
6500 /* Select the previous track */
6501 if (sel_pos > 0) {
6502 XmListDeselectPos(widgets.dbprog.trk_list, sel_pos);
6503 sel_pos = -1;
6504 }
6505 XmListSelectPos(widgets.dbprog.trk_list, extt_pos, True);
6506
6507 /* Scroll track list if necessary */
6508 dbprog_list_autoscroll(
6509 widgets.dbprog.trk_list,
6510 (int) s->tot_trks,
6511 extt_pos + 1
6512 );
6513
6514 /* Update fullname window if necessary */
6515 if (fname_mode == FNAME_TRACK) {
6516 dbprog_fullname(widgets.dbextt.fullname_btn,
6517 (XtPointer) FALSE, call_data);
6518 }
6519 else if (fname_mode == FNAME_CREDITS &&
6520 credits_mode == CREDITS_TRACK) {
6521 dbprog_fullname(widgets.credits.fullname_btn,
6522 (XtPointer) FALSE, call_data);
6523 }
6524 }
6525
6526
6527 /*
6528 * dbprog_extt_next
6529 * Track details and credits window next track button callback.
6530 */
6531 /*ARGSUSED*/
6532 void
dbprog_extt_next(Widget w,XtPointer client_data,XtPointer call_data)6533 dbprog_extt_next(Widget w, XtPointer client_data, XtPointer call_data)
6534 {
6535 curstat_t *s = (curstat_t *)(void *) client_data;
6536 int n;
6537
6538 /* Check full name data and abort if needed */
6539 if (!dbprog_fullname_ck(s)) {
6540 cd_beep();
6541 return;
6542 }
6543
6544 /* Put keyboard focus on the OK button */
6545 if (XtIsManaged(widgets.dbextt.form))
6546 XmProcessTraversal(widgets.dbextt.ok_btn, XmTRAVERSE_CURRENT);
6547 if (XtIsManaged(widgets.credits.form))
6548 XmProcessTraversal(widgets.credits.ok_btn, XmTRAVERSE_CURRENT);
6549
6550 if (extt_pos < 0) {
6551 cd_beep();
6552 return;
6553 }
6554
6555 n = extt_pos + 2;
6556 if (n > (int) s->tot_trks) {
6557 cd_beep();
6558 return;
6559 }
6560
6561 /* Select the next track */
6562 if (sel_pos > 0) {
6563 XmListDeselectPos(widgets.dbprog.trk_list, sel_pos);
6564 sel_pos = -1;
6565 }
6566 XmListSelectPos(widgets.dbprog.trk_list, n, True);
6567
6568 /* Scroll track list if necessary */
6569 dbprog_list_autoscroll(
6570 widgets.dbprog.trk_list,
6571 (int) s->tot_trks,
6572 n
6573 );
6574
6575 /* Update fullname window if necessary */
6576 if (fname_mode == FNAME_TRACK) {
6577 dbprog_fullname(widgets.dbextt.fullname_btn,
6578 (XtPointer) FALSE, call_data);
6579 }
6580 else if (fname_mode == FNAME_CREDITS &&
6581 credits_mode == CREDITS_TRACK) {
6582 dbprog_fullname(widgets.credits.fullname_btn,
6583 (XtPointer) FALSE, call_data);
6584 }
6585 }
6586
6587
6588 /*
6589 * dbprog_extt_autotrk
6590 * Track details window Auto-track button callback.
6591 */
6592 /*ARGSUSED*/
6593 void
dbprog_extt_autotrk(Widget w,XtPointer client_data,XtPointer call_data)6594 dbprog_extt_autotrk(Widget w, XtPointer client_data, XtPointer call_data)
6595 {
6596 XmToggleButtonCallbackStruct *p =
6597 (XmToggleButtonCallbackStruct *)(void *) call_data;
6598 curstat_t *s =
6599 (curstat_t *)(void *) client_data;
6600
6601 if (p->reason != XmCR_VALUE_CHANGED)
6602 return;
6603
6604 DBGPRN(DBG_UI)(errfp, "\n* AUTO-TRACK: %s\n", p->set ? "On" : "Off");
6605
6606 auto_trk = (bool_t) p->set;
6607
6608 /* The auto-track buttons in the trackk details and credits
6609 * windows are "ganged" together.
6610 */
6611 if (w == widgets.dbextt.autotrk_btn) {
6612 XmToggleButtonSetState(
6613 widgets.credits.autotrk_btn, (Boolean) p->set, False
6614 );
6615 }
6616 else if (w == widgets.credits.autotrk_btn) {
6617 XmToggleButtonSetState(
6618 widgets.dbextt.autotrk_btn, (Boolean) p->set, False
6619 );
6620 }
6621
6622 dbprog_extt_autotrk_upd(s, di_curtrk_pos(s) + 1);
6623 }
6624
6625
6626 /*
6627 * dbprog_extt_ok
6628 * Track details window OK button callback.
6629 */
6630 /*ARGSUSED*/
6631 void
dbprog_extt_ok(Widget w,XtPointer client_data,XtPointer call_data)6632 dbprog_extt_ok(Widget w, XtPointer client_data, XtPointer call_data)
6633 {
6634 cdinfo_track_t *t;
6635
6636 if (fname_mode == FNAME_TRACK) {
6637 /* Pop down the full name window */
6638 dbprog_fullname_ok(
6639 widgets.fullname.ok_btn,
6640 client_data, call_data
6641 );
6642 }
6643
6644 if (credits_mode != CREDITS_TRACK)
6645 extt_pos = -1;
6646
6647 /* Pop down the Track details window */
6648 XtUnmapWidget(XtParent(widgets.dbextt.form));
6649 XtUnmanageChild(widgets.dbextt.form);
6650
6651 /* Update button labels */
6652 t = (sel_pos > 0) ? &dbp->track[sel_pos - 1] : NULL;
6653 dbprog_extt_lblupd(t);
6654 dbprog_tcred_lblupd(t);
6655 }
6656
6657
6658 /*
6659 * dbprog_credits_popup
6660 * Credits button callback.
6661 */
6662 /*ARGSUSED*/
6663 void
dbprog_credits_popup(Widget w,XtPointer client_data,XtPointer call_data)6664 dbprog_credits_popup(Widget w, XtPointer client_data, XtPointer call_data)
6665 {
6666 curstat_t *s = (curstat_t *)(void *) client_data;
6667 static bool_t first = TRUE;
6668
6669 if (w == widgets.dbprog.dcredits_btn) {
6670 if (credits_mode == CREDITS_DISC) {
6671 /* Already popped up - pop it down */
6672 dbprog_credits_ok(w, client_data, call_data);
6673 return;
6674 }
6675
6676 credits_mode = CREDITS_DISC;
6677 dbprog_creditupd(s, -1);
6678
6679 if (XtIsManaged(widgets.credits.autotrk_btn))
6680 XtUnmanageChild(widgets.credits.autotrk_btn);
6681 if (XtIsManaged(widgets.credits.prev_btn))
6682 XtUnmanageChild(widgets.credits.prev_btn);
6683 if (XtIsManaged(widgets.credits.next_btn))
6684 XtUnmanageChild(widgets.credits.next_btn);
6685 }
6686 else if (w == widgets.dbprog.tcredits_btn) {
6687 if (credits_mode == CREDITS_TRACK) {
6688 /* Already popped up - pop it down */
6689 dbprog_credits_ok(w, client_data, call_data);
6690 return;
6691 }
6692
6693 if (sel_pos > 0)
6694 extt_pos = sel_pos - 1;
6695 else if (extt_pos < 0)
6696 return;
6697
6698 credits_mode = CREDITS_TRACK;
6699 dbprog_creditupd(s, extt_pos);
6700
6701 if (!XtIsManaged(widgets.credits.autotrk_btn))
6702 XtManageChild(widgets.credits.autotrk_btn);
6703 if (!XtIsManaged(widgets.credits.prev_btn))
6704 XtManageChild(widgets.credits.prev_btn);
6705 if (!XtIsManaged(widgets.credits.next_btn))
6706 XtManageChild(widgets.credits.next_btn);
6707 }
6708 else if (w == widgets.segments.credits_btn) {
6709 if (credits_mode == CREDITS_SEG) {
6710 /* Already popped up - pop it down */
6711 dbprog_credits_ok(w, client_data, call_data);
6712 return;
6713 }
6714
6715 credits_mode = CREDITS_SEG;
6716 dbprog_creditupd(s, -1);
6717
6718 if (XtIsManaged(widgets.credits.autotrk_btn))
6719 XtUnmanageChild(widgets.credits.autotrk_btn);
6720 if (XtIsManaged(widgets.credits.prev_btn))
6721 XtUnmanageChild(widgets.credits.prev_btn);
6722 if (XtIsManaged(widgets.credits.next_btn))
6723 XtUnmanageChild(widgets.credits.next_btn);
6724 }
6725 else
6726 return;
6727
6728 /* Initialize role selectors to "None -> None" */
6729 XtVaSetValues(widgets.credits.prirole_opt,
6730 XmNmenuHistory,
6731 widgets.credits.prirole_none_btn,
6732 NULL
6733 );
6734 XtVaSetValues(widgets.credits.subrole_opt,
6735 XmNmenuHistory,
6736 widgets.credits.subrole_none_btn,
6737 NULL
6738 );
6739
6740 /* Pop up credits window.
6741 * The dialog has mappedWhenManaged set to False,
6742 * so we have to map/unmap explicitly. The reason
6743 * for this is we want to avoid a screen glitch when
6744 * we move the window in cd_dialog_setpos(), so we
6745 * map the window afterwards.
6746 */
6747 XtManageChild(widgets.credits.form);
6748 if (first) {
6749 first = FALSE;
6750 /* Set up window position */
6751 cd_dialog_setpos(XtParent(widgets.credits.form));
6752 }
6753 XtMapWidget(XtParent(widgets.credits.form));
6754
6755 /* Put focus on OK button */
6756 XmProcessTraversal(widgets.credits.ok_btn, XmTRAVERSE_CURRENT);
6757 }
6758
6759
6760 /*
6761 * dbprog_credits_popdown
6762 * Pop down the credits window and do some cleanup.
6763 *
6764 * Args:
6765 * s - Pointer to the curstat_t structure
6766 *
6767 * Return:
6768 * Nothing.
6769 */
6770 /*ARGSUSED*/
6771 void
dbprog_credits_popdown(Widget w,XtPointer client_data,XtPointer call_data)6772 dbprog_credits_popdown(Widget w, XtPointer client_data, XtPointer call_data)
6773 {
6774 if (fname_mode == FNAME_CREDITS) {
6775 /* Pop down the full name window
6776 * Currently dbprog_fullname_ok doesn't use the call_data
6777 * arg, so we pass NULL. If that changes this needs to
6778 * change accordingly.
6779 */
6780 dbprog_fullname_ok(
6781 widgets.fullname.ok_btn, NULL, client_data
6782 );
6783 }
6784
6785 if (!XtIsManaged(widgets.dbextt.form))
6786 extt_pos = -1;
6787
6788 /* Pop down credits window */
6789 XtUnmapWidget(XtParent(widgets.credits.form));
6790 XtUnmanageChild(widgets.credits.form);
6791
6792 if (cred_pos > 0) {
6793 XmListDeselectPos(widgets.credits.cred_list, cred_pos);
6794 cred_pos = -1;
6795 }
6796
6797 credits_mode = CREDITS_NONE;
6798 }
6799
6800
6801 /*
6802 * dbprog_credits_select
6803 * Credits list selector callback
6804 */
6805 /*ARGSUSED*/
6806 void
dbprog_credits_select(Widget w,XtPointer client_data,XtPointer call_data)6807 dbprog_credits_select(Widget w, XtPointer client_data, XtPointer call_data)
6808 {
6809 XmListCallbackStruct *p =
6810 (XmListCallbackStruct *)(void *) call_data;
6811 cdinfo_credit_t *list,
6812 *cp;
6813 int i;
6814
6815 if (cred_pos == p->item_position) {
6816 /* Already selected: de-select it */
6817 XmListDeselectPos(w, cred_pos);
6818 cred_pos = -1;
6819
6820 /* Set role selectors to "None" */
6821 XtVaSetValues(
6822 widgets.credits.prirole_opt,
6823 XmNmenuHistory, widgets.credits.prirole_none_btn,
6824 NULL
6825 );
6826 XtCallCallbacks(
6827 widgets.credits.prirole_none_btn,
6828 XmNactivateCallback, (XtPointer) NULL
6829 );
6830 set_text_string(widgets.credits.name_txt, "", FALSE);
6831 set_text_string(widgets.credits.notes_txt, "", FALSE);
6832
6833 XtSetSensitive(widgets.credits.add_btn, False);
6834 XtSetSensitive(widgets.credits.del_btn, False);
6835 XtSetSensitive(widgets.credits.mod_btn, False);
6836
6837 if (fname_mode == FNAME_CREDITS) {
6838 set_text_string(
6839 widgets.fullname.dispname_txt, "", FALSE
6840 );
6841 set_text_string(
6842 widgets.fullname.lastname_txt, "", FALSE
6843 );
6844 set_text_string(
6845 widgets.fullname.firstname_txt, "", FALSE
6846 );
6847 set_text_string(
6848 widgets.fullname.the_txt, "", FALSE
6849 );
6850 fname_changed = FALSE;
6851 }
6852 }
6853 else {
6854 switch (credits_mode) {
6855 case CREDITS_DISC:
6856 list = dbp->disc.credit_list;
6857 break;
6858 case CREDITS_TRACK:
6859 if (extt_pos < 0)
6860 return;
6861 list = dbp->track[extt_pos].credit_list;
6862 break;
6863 case CREDITS_SEG:
6864 list = w_seg.credit_list;
6865 break;
6866 default:
6867 return;
6868 }
6869
6870 cred_pos = p->item_position;
6871
6872 /* Look for matching credits list entry */
6873 i = 0;
6874 for (cp = list; cp != NULL; cp = cp->next) {
6875 if (++i == p->item_position)
6876 break;
6877 }
6878
6879 if (cp == NULL || i != p->item_position) {
6880 /* Error: this shouldn't happen */
6881 return;
6882 }
6883
6884 /* Set role selectors - this causes callbacks to be called,
6885 * and thus the w_cred structure will be appropriately
6886 * filled.
6887 */
6888
6889 if (cp->crinfo.role != NULL) {
6890 if (cp->crinfo.role->parent != NULL &&
6891 cp->crinfo.role->parent->aux != NULL) {
6892 XtVaSetValues(
6893 widgets.credits.prirole_opt,
6894 XmNmenuHistory, cp->crinfo.role->parent->aux,
6895 NULL
6896 );
6897 XtCallCallbacks(
6898 cp->crinfo.role->parent->aux,
6899 XmNactivateCallback,
6900 (XtPointer) cp->crinfo.role
6901 );
6902 }
6903 if (cp->crinfo.role->aux != NULL) {
6904 XtVaSetValues(
6905 widgets.credits.subrole_opt,
6906 XmNmenuHistory, cp->crinfo.role->aux,
6907 NULL
6908 );
6909 XtCallCallbacks(
6910 cp->crinfo.role->aux,
6911 XmNactivateCallback,
6912 (XtPointer) cp->crinfo.role
6913 );
6914 }
6915 }
6916
6917 /* Explicitly copy fullname structure data */
6918 if (!util_newstr(&w_cred.crinfo.fullname.dispname,
6919 cp->crinfo.fullname.dispname)) {
6920 CD_FATAL(app_data.str_nomemory);
6921 return;
6922 }
6923 if (!util_newstr(&w_cred.crinfo.fullname.lastname,
6924 cp->crinfo.fullname.lastname)) {
6925 CD_FATAL(app_data.str_nomemory);
6926 return;
6927 }
6928 if (!util_newstr(&w_cred.crinfo.fullname.firstname,
6929 cp->crinfo.fullname.firstname)) {
6930 CD_FATAL(app_data.str_nomemory);
6931 return;
6932 }
6933 if (!util_newstr(&w_cred.crinfo.fullname.the,
6934 cp->crinfo.fullname.the)) {
6935 CD_FATAL(app_data.str_nomemory);
6936 return;
6937 }
6938
6939 /* Set text fields - this causes callbacks to be called,
6940 * and thus the w_cred structure will be appropriately
6941 * filled.
6942 */
6943
6944 if (cp->crinfo.name != NULL)
6945 set_text_string(widgets.credits.name_txt,
6946 cp->crinfo.name, TRUE);
6947 else
6948 set_text_string(widgets.credits.name_txt, "", FALSE);
6949
6950 if (cp->notes != NULL)
6951 set_text_string(
6952 widgets.credits.notes_txt, cp->notes, TRUE
6953 );
6954 else
6955 set_text_string(widgets.credits.notes_txt, "", FALSE);
6956
6957 /* Set button sensitivities */
6958 XtSetSensitive(widgets.credits.add_btn, False);
6959 XtSetSensitive(widgets.credits.mod_btn, False);
6960 XtSetSensitive(widgets.credits.del_btn, True);
6961 }
6962
6963 if (fname_mode == FNAME_CREDITS) {
6964 /* Update the full name window */
6965 dbprog_fullname(
6966 widgets.credits.fullname_btn,
6967 (XtPointer) FALSE, call_data
6968 );
6969 }
6970 }
6971
6972
6973 /*
6974 * dbprog_credits_ok
6975 * Credits window OK button callback.
6976 */
6977 /*ARGSUSED*/
6978 void
dbprog_credits_ok(Widget w,XtPointer client_data,XtPointer call_data)6979 dbprog_credits_ok(Widget w, XtPointer client_data, XtPointer call_data)
6980 {
6981 if ((w == widgets.credits.ok_btn ||
6982 w == widgets.dbprog.dcredits_btn ||
6983 w == widgets.dbprog.tcredits_btn ||
6984 w == widgets.segments.credits_btn) &&
6985 (XtIsSensitive(widgets.credits.add_btn) ||
6986 XtIsSensitive(widgets.credits.mod_btn))) {
6987 (void) cd_confirm_popup(
6988 app_data.str_confirm,
6989 app_data.str_discardchg,
6990 (XtCallbackProc) dbprog_credits_popdown, client_data,
6991 (XtCallbackProc) NULL, NULL
6992 );
6993 return;
6994 }
6995
6996 dbprog_credits_popdown(w, client_data, call_data);
6997 }
6998
6999
7000 /*
7001 * dbprog_credits_add
7002 * Credits window Add button callback.
7003 */
7004 /*ARGSUSED*/
7005 void
dbprog_credits_add(Widget w,XtPointer client_data,XtPointer call_data)7006 dbprog_credits_add(Widget w, XtPointer client_data, XtPointer call_data)
7007 {
7008 curstat_t *s = (curstat_t *)(void *) client_data;
7009 cdinfo_credit_t **list,
7010 *p,
7011 *q;
7012 int pos,
7013 n;
7014
7015 switch (credits_mode) {
7016 case CREDITS_DISC:
7017 list = &dbp->disc.credit_list;
7018 pos = -1;
7019 break;
7020 case CREDITS_TRACK:
7021 if (extt_pos < 0)
7022 return;
7023 list = &dbp->track[extt_pos].credit_list;
7024 pos = extt_pos;
7025 break;
7026 case CREDITS_SEG:
7027 list = &w_seg.credit_list;
7028 pos = -1;
7029 break;
7030 default:
7031 return;
7032 }
7033
7034 /* Check entered data for correctness */
7035 if (!dbprog_credit_ck(&w_cred, pos, s) || !dbprog_fullname_ck(s))
7036 return;
7037
7038 p = (cdinfo_credit_t *)(void *) MEM_ALLOC(
7039 "cdinfo_credit_t",
7040 sizeof(cdinfo_credit_t)
7041 );
7042 if (p == NULL) {
7043 CD_FATAL(app_data.str_nomemory);
7044 return;
7045 }
7046 (void) memset(p, 0, sizeof(cdinfo_credit_t));
7047
7048 /* Copy tmp credit structure content to newly allocated struct */
7049 q = &w_cred;
7050 p->crinfo.role = q->crinfo.role;
7051 p->crinfo.name = q->crinfo.name;
7052 p->crinfo.fullname.dispname = q->crinfo.fullname.dispname;
7053 p->crinfo.fullname.lastname = q->crinfo.fullname.lastname;
7054 p->crinfo.fullname.firstname = q->crinfo.fullname.firstname;
7055 p->crinfo.fullname.the = q->crinfo.fullname.the;
7056 p->notes = q->notes;
7057
7058 /* Clean up tmp credit structure */
7059 q->crinfo.role = NULL;
7060 q->crinfo.name = NULL;
7061 q->crinfo.fullname.dispname = NULL;
7062 q->crinfo.fullname.lastname = NULL;
7063 q->crinfo.fullname.firstname = NULL;
7064 q->crinfo.fullname.the = NULL;
7065 q->notes = NULL;
7066
7067 /* Add to incore credits list */
7068 n = 0;
7069 if (*list == NULL) {
7070 /* First entry in list */
7071 *list = p;
7072 }
7073 else {
7074 /* Look for end of credits list and append to it */
7075 for (n = 1, q = *list; q->next != NULL; q = q->next, n++)
7076 ;
7077 q->next = p;
7078 p->prev = q;
7079 }
7080 n++;
7081
7082 dbprog_creditupd(s, pos);
7083 dbprog_list_autoscroll(widgets.credits.cred_list, n, n);
7084
7085 if (credits_mode == CREDITS_SEG) {
7086 if (seg_pos < 0)
7087 XtSetSensitive(widgets.segments.add_btn, True);
7088 else
7089 XtSetSensitive(widgets.segments.mod_btn, True);
7090 }
7091
7092 dbp->flags |= CDINFO_CHANGED;
7093 if (!app_data.cdinfo_inetoffln)
7094 XtSetSensitive(widgets.dbprog.submit_btn, True);
7095 }
7096
7097
7098 /*
7099 * dbprog_credits_mod
7100 * Credits window Modify button callback.
7101 */
7102 /*ARGSUSED*/
7103 void
dbprog_credits_mod(Widget w,XtPointer client_data,XtPointer call_data)7104 dbprog_credits_mod(Widget w, XtPointer client_data, XtPointer call_data)
7105 {
7106 curstat_t *s = (curstat_t *)(void *) client_data;
7107 cdinfo_credit_t *list,
7108 *p,
7109 *q;
7110 int pos,
7111 sav_credpos,
7112 n;
7113
7114 if (cred_pos < 0)
7115 return; /* Invalid */
7116
7117 switch (credits_mode) {
7118 case CREDITS_DISC:
7119 list = dbp->disc.credit_list;
7120 pos = -1;
7121 break;
7122 case CREDITS_TRACK:
7123 if (extt_pos < 0)
7124 return;
7125 list = dbp->track[extt_pos].credit_list;
7126 pos = extt_pos;
7127 break;
7128 case CREDITS_SEG:
7129 list = w_seg.credit_list;
7130 pos = -1;
7131 break;
7132 default:
7133 return;
7134 }
7135
7136 /* Check entered data for correctness */
7137 if (!dbprog_credit_ck(&w_cred, pos, s) || !dbprog_fullname_ck(s))
7138 return;
7139
7140 for (n = 0, p = list; p != NULL; p = p->next, n++) {
7141 if ((n+1) != cred_pos)
7142 continue;
7143
7144 /* Update appropriate incore credit entry */
7145 q = &w_cred;
7146 if (p->crinfo.name != NULL)
7147 MEM_FREE(p->crinfo.name);
7148 if (p->crinfo.fullname.dispname != NULL)
7149 MEM_FREE(p->crinfo.fullname.dispname);
7150 if (p->crinfo.fullname.lastname != NULL)
7151 MEM_FREE(p->crinfo.fullname.lastname);
7152 if (p->crinfo.fullname.firstname != NULL)
7153 MEM_FREE(p->crinfo.fullname.firstname);
7154 if (p->crinfo.fullname.the != NULL)
7155 MEM_FREE(p->crinfo.fullname.the);
7156 if (p->notes != NULL)
7157 MEM_FREE(p->notes);
7158
7159 p->crinfo.role = q->crinfo.role;
7160 p->crinfo.name = q->crinfo.name;
7161 p->crinfo.fullname.dispname = q->crinfo.fullname.dispname;
7162 p->crinfo.fullname.lastname = q->crinfo.fullname.lastname;
7163 p->crinfo.fullname.firstname = q->crinfo.fullname.firstname;
7164 p->crinfo.fullname.the = q->crinfo.fullname.the;
7165 p->notes = q->notes;
7166
7167 /* Clean up tmp structure */
7168 q->crinfo.role = NULL;
7169 q->crinfo.name = NULL;
7170 q->crinfo.fullname.dispname = NULL;
7171 q->crinfo.fullname.lastname = NULL;
7172 q->crinfo.fullname.firstname = NULL;
7173 q->crinfo.fullname.the = NULL;
7174 q->notes = NULL;
7175 }
7176
7177 sav_credpos = cred_pos;
7178
7179 dbprog_creditupd(s, pos);
7180 dbprog_list_autoscroll(widgets.credits.cred_list, n, sav_credpos);
7181
7182 if (credits_mode == CREDITS_SEG) {
7183 if (seg_pos < 0)
7184 XtSetSensitive(widgets.segments.add_btn, True);
7185 else
7186 XtSetSensitive(widgets.segments.mod_btn, True);
7187 }
7188
7189 dbp->flags |= CDINFO_CHANGED;
7190 if (!app_data.cdinfo_inetoffln)
7191 XtSetSensitive(widgets.dbprog.submit_btn, True);
7192 }
7193
7194
7195 /*
7196 * dbprog_credits_del
7197 * Credits window Delete button callback.
7198 */
7199 /*ARGSUSED*/
7200 void
dbprog_credits_del(Widget w,XtPointer client_data,XtPointer call_data)7201 dbprog_credits_del(Widget w, XtPointer client_data, XtPointer call_data)
7202 {
7203 curstat_t *s = (curstat_t *)(void *) client_data;
7204 cdinfo_credit_t **list,
7205 *p;
7206 int pos,
7207 scrollpos,
7208 i,
7209 n;
7210
7211 if (cred_pos < 0)
7212 return; /* Invalid */
7213
7214 switch (credits_mode) {
7215 case CREDITS_DISC:
7216 list = &dbp->disc.credit_list;
7217 pos = -1;
7218 break;
7219 case CREDITS_TRACK:
7220 if (extt_pos < 0)
7221 return;
7222 list = &dbp->track[extt_pos].credit_list;
7223 pos = extt_pos;
7224 break;
7225 case CREDITS_SEG:
7226 list = &w_seg.credit_list;
7227 pos = -1;
7228 break;
7229 default:
7230 return;
7231 }
7232
7233 /* Delete from incore credit list */
7234 if (cred_pos == 1) {
7235 p = *list;
7236 *list = p->next;
7237 if (p->next != NULL)
7238 p->next->prev = NULL;
7239 }
7240 else {
7241 i = 0;
7242 for (p = *list; p != NULL; p = p->next) {
7243 if (++i < cred_pos)
7244 continue;
7245
7246 if (p->next != NULL)
7247 p->next->prev = p->prev;
7248 if (p->prev != NULL)
7249 p->prev->next = p->next;
7250
7251 break;
7252 }
7253 }
7254 if (p != NULL)
7255 MEM_FREE(p);
7256
7257 for (n = 0, p = *list; p != NULL; p = p->next, n++)
7258 ;
7259
7260 if (n > cred_pos)
7261 scrollpos = cred_pos;
7262 else
7263 scrollpos = n;
7264
7265 dbprog_creditupd(s, pos);
7266 dbprog_list_autoscroll(widgets.credits.cred_list, n, scrollpos);
7267
7268 if (credits_mode == CREDITS_SEG) {
7269 if (seg_pos < 0)
7270 XtSetSensitive(widgets.segments.add_btn, True);
7271 else
7272 XtSetSensitive(widgets.segments.mod_btn, True);
7273 }
7274
7275 dbp->flags |= CDINFO_CHANGED;
7276 if (!app_data.cdinfo_inetoffln)
7277 XtSetSensitive(widgets.dbprog.submit_btn, True);
7278 }
7279
7280
7281 /*
7282 * dbprog_segments_popup
7283 * Credits button callback.
7284 */
7285 /*ARGSUSED*/
7286 void
dbprog_segments_popup(Widget w,XtPointer client_data,XtPointer call_data)7287 dbprog_segments_popup(Widget w, XtPointer client_data, XtPointer call_data)
7288 {
7289 curstat_t *s = (curstat_t *)(void *) client_data;
7290 static bool_t first = TRUE;
7291
7292 if (XtIsManaged(widgets.segments.form)) {
7293 /* Already popped up - pop it down */
7294 dbprog_segments_ok(w, client_data, call_data);
7295 return;
7296 }
7297
7298 /* Pop up segments window.
7299 * The dialog has mappedWhenManaged set to False,
7300 * so we have to map/unmap explicitly. The reason
7301 * for this is we want to avoid a screen glitch when
7302 * we move the window in cd_dialog_setpos(), so we
7303 * map the window afterwards.
7304 */
7305 XtManageChild(widgets.segments.form);
7306
7307 /* Update segment window */
7308 dbprog_segmentupd(s);
7309
7310 if (first) {
7311 first = FALSE;
7312 /* Set up window position */
7313 cd_dialog_setpos(XtParent(widgets.segments.form));
7314 }
7315 XtMapWidget(XtParent(widgets.segments.form));
7316
7317 /* Put focus on OK button */
7318 XmProcessTraversal(widgets.segments.ok_btn, XmTRAVERSE_CURRENT);
7319 }
7320
7321
7322 /*
7323 * dbprog_segments_popdown
7324 * Pop down the segments window and do some cleanup.
7325 *
7326 * Args:
7327 * s - Pointer to the curstat_t structure
7328 *
7329 * Return:
7330 * Nothing.
7331 */
7332 /*ARGSUSED*/
7333 void
dbprog_segments_popdown(Widget w,XtPointer client_data,XtPointer call_data)7334 dbprog_segments_popdown(Widget w, XtPointer client_data, XtPointer call_data)
7335 {
7336 if (credits_mode == CREDITS_SEG) {
7337 /* Force a popdown of the credits window */
7338 dbprog_credits_popdown(
7339 widgets.credits.ok_btn, client_data, call_data
7340 );
7341 }
7342
7343 /* Pop down segments window */
7344 XtUnmapWidget(XtParent(widgets.segments.form));
7345 XtUnmanageChild(widgets.segments.form);
7346
7347 if (seg_pos > 0) {
7348 XmListDeselectPos(widgets.segments.seg_list, seg_pos);
7349 seg_pos = -1;
7350 }
7351 }
7352
7353
7354 /*
7355 * dbprog_segments_select
7356 * Credits list selector callback
7357 */
7358 /*ARGSUSED*/
7359 void
dbprog_segments_select(Widget w,XtPointer client_data,XtPointer call_data)7360 dbprog_segments_select(Widget w, XtPointer client_data, XtPointer call_data)
7361 {
7362 XmListCallbackStruct *p =
7363 (XmListCallbackStruct *)(void *) call_data;
7364 curstat_t *s = (curstat_t *)(void *) client_data;
7365 cdinfo_segment_t *sp;
7366 cdinfo_credit_t *cp,
7367 *cp2,
7368 *cp3;
7369 char *buf;
7370 int i,
7371 st,
7372 sf,
7373 et,
7374 ef;
7375 sword32_t eot;
7376
7377 if (seg_pos == p->item_position) {
7378 /* Already selected: de-select it */
7379 XmListDeselectPos(w, seg_pos);
7380 seg_pos = -1;
7381
7382 set_text_string(widgets.segments.name_txt, "", FALSE);
7383 set_text_string(widgets.segments.starttrk_txt, "", FALSE);
7384 set_text_string(widgets.segments.startfrm_txt, "", FALSE);
7385 set_text_string(widgets.segments.endtrk_txt, "", FALSE);
7386 set_text_string(widgets.segments.endfrm_txt, "", FALSE);
7387 set_text_string(widgets.segments.notes_txt, "", FALSE);
7388
7389 for (cp = cp2 = w_seg.credit_list; cp != NULL; cp = cp2) {
7390 cp2 = cp->next;
7391
7392 if (cp->crinfo.name != NULL)
7393 MEM_FREE(cp->crinfo.name);
7394 if (cp->crinfo.fullname.dispname != NULL)
7395 MEM_FREE(cp->crinfo.fullname.dispname);
7396 if (cp->crinfo.fullname.lastname != NULL)
7397 MEM_FREE(cp->crinfo.fullname.lastname);
7398 if (cp->crinfo.fullname.firstname != NULL)
7399 MEM_FREE(cp->crinfo.fullname.firstname);
7400 if (cp->crinfo.fullname.the != NULL)
7401 MEM_FREE(cp->crinfo.fullname.the);
7402 if (cp->notes != NULL)
7403 MEM_FREE(cp->notes);
7404 MEM_FREE(cp);
7405 }
7406 w_seg.credit_list = NULL;
7407
7408 XtSetSensitive(widgets.segments.add_btn, False);
7409 XtSetSensitive(widgets.segments.mod_btn, False);
7410 XtSetSensitive(widgets.segments.del_btn, False);
7411
7412 s->segplay = SEGP_NONE;
7413 dpy_progmode(s, FALSE);
7414 }
7415 else {
7416 seg_pos = p->item_position;
7417
7418 /* Look for matching segments list entry */
7419 i = 0;
7420 for (sp = dbp->disc.segment_list; sp != NULL; sp = sp->next) {
7421 if (++i == p->item_position)
7422 break;
7423 }
7424
7425 if (sp == NULL || i != p->item_position) {
7426 /* Error: this shouldn't happen */
7427 return;
7428 }
7429
7430 if (w_seg.name != NULL) {
7431 MEM_FREE(w_seg.name);
7432 w_seg.name = NULL;
7433 }
7434 if (w_seg.start_track != NULL) {
7435 MEM_FREE(w_seg.start_track);
7436 w_seg.start_track = NULL;
7437 }
7438 if (w_seg.start_frame != NULL) {
7439 MEM_FREE(w_seg.start_frame);
7440 w_seg.start_frame = NULL;
7441 }
7442 if (w_seg.end_track != NULL) {
7443 MEM_FREE(w_seg.end_track);
7444 w_seg.end_track = NULL;
7445 }
7446 if (w_seg.end_frame != NULL) {
7447 MEM_FREE(w_seg.end_frame);
7448 w_seg.end_frame = NULL;
7449 }
7450 if (w_seg.notes != NULL) {
7451 MEM_FREE(w_seg.notes);
7452 w_seg.notes = NULL;
7453 }
7454 for (cp = cp2 = w_seg.credit_list; cp != NULL; cp = cp2) {
7455 cp2 = cp->next;
7456
7457 if (cp->crinfo.name != NULL)
7458 MEM_FREE(cp->crinfo.name);
7459 if (cp->crinfo.fullname.dispname != NULL)
7460 MEM_FREE(cp->crinfo.fullname.dispname);
7461 if (cp->crinfo.fullname.lastname != NULL)
7462 MEM_FREE(cp->crinfo.fullname.lastname);
7463 if (cp->crinfo.fullname.firstname != NULL)
7464 MEM_FREE(cp->crinfo.fullname.firstname);
7465 if (cp->crinfo.fullname.the != NULL)
7466 MEM_FREE(cp->crinfo.fullname.the);
7467 if (cp->notes != NULL)
7468 MEM_FREE(cp->notes);
7469 MEM_FREE(cp);
7470 }
7471 w_seg.credit_list = NULL;
7472
7473 /* Set text fields - this causes callbacks to be called,
7474 * and thus the w_seg structure will be appropriately
7475 * filled.
7476 */
7477
7478 if (sp->name != NULL)
7479 set_text_string(widgets.segments.name_txt,
7480 sp->name, TRUE);
7481 else
7482 set_text_string(widgets.segments.name_txt, "", FALSE);
7483
7484 if (sp->start_track != NULL)
7485 set_text_string(widgets.segments.starttrk_txt,
7486 sp->start_track, FALSE);
7487 else
7488 set_text_string(widgets.segments.starttrk_txt,
7489 "", FALSE);
7490
7491 if (sp->start_frame != NULL)
7492 set_text_string(widgets.segments.startfrm_txt,
7493 sp->start_frame, FALSE);
7494 else
7495 set_text_string(widgets.segments.startfrm_txt,
7496 "", FALSE);
7497
7498 if (sp->end_track != NULL)
7499 set_text_string(widgets.segments.endtrk_txt,
7500 sp->end_track, FALSE);
7501 else
7502 set_text_string(widgets.segments.endtrk_txt,
7503 "", FALSE);
7504
7505 if (sp->end_frame != NULL)
7506 set_text_string(widgets.segments.endfrm_txt,
7507 sp->end_frame, FALSE);
7508 else
7509 set_text_string(widgets.segments.endfrm_txt,
7510 "", FALSE);
7511
7512 if (sp->notes != NULL)
7513 set_text_string(widgets.segments.notes_txt,
7514 sp->notes, TRUE);
7515 else
7516 set_text_string(widgets.segments.notes_txt,
7517 "", FALSE);
7518
7519 /* Copy credit list to the working seg structure */
7520 cp3 = NULL;
7521 for (cp = sp->credit_list; cp != NULL; cp = cp->next) {
7522 cp2 = (cdinfo_credit_t *)(void *) MEM_ALLOC(
7523 "cdinfo_credit_t",
7524 sizeof(cdinfo_credit_t)
7525 );
7526 if (cp2 == NULL) {
7527 CD_FATAL(app_data.str_nomemory);
7528 return;
7529 }
7530 (void) memset(cp2, 0, sizeof(cdinfo_credit_t));
7531
7532 cp2->crinfo.role = cp->crinfo.role;
7533
7534 if (!util_newstr(&cp2->crinfo.name, cp->crinfo.name)) {
7535 CD_FATAL(app_data.str_nomemory);
7536 return;
7537 }
7538 if (!util_newstr(&cp2->crinfo.fullname.dispname,
7539 cp->crinfo.fullname.dispname)) {
7540 CD_FATAL(app_data.str_nomemory);
7541 return;
7542 }
7543 if (!util_newstr(&cp2->crinfo.fullname.lastname,
7544 cp->crinfo.fullname.lastname)) {
7545 CD_FATAL(app_data.str_nomemory);
7546 return;
7547 }
7548 if (!util_newstr(&cp2->crinfo.fullname.firstname,
7549 cp->crinfo.fullname.firstname)) {
7550 CD_FATAL(app_data.str_nomemory);
7551 return;
7552 }
7553 if (!util_newstr(&cp2->crinfo.fullname.the,
7554 cp->crinfo.fullname.the)) {
7555 CD_FATAL(app_data.str_nomemory);
7556 return;
7557 }
7558 if (!util_newstr(&cp2->notes, cp->notes)) {
7559 CD_FATAL(app_data.str_nomemory);
7560 return;
7561 }
7562
7563 if (w_seg.credit_list == NULL) {
7564 w_seg.credit_list = cp2;
7565 cp2->prev = NULL;
7566 }
7567 else {
7568 cp3->next = cp2;
7569 cp2->prev = cp3;
7570 }
7571 cp2->next = NULL;
7572 cp3 = cp2;
7573 }
7574
7575 /* Set button sensitivities */
7576 XtSetSensitive(widgets.segments.add_btn, False);
7577 XtSetSensitive(widgets.segments.mod_btn, False);
7578 XtSetSensitive(widgets.segments.del_btn, True);
7579
7580 /*
7581 * Set a->b mode based on this segment
7582 */
7583
7584 buf = get_text_string(widgets.segments.starttrk_txt, FALSE);
7585 if (buf != NULL) {
7586 st = atoi(buf);
7587 MEM_FREE(buf);
7588 }
7589 else
7590 st = 0; /* shrug */
7591
7592 buf = get_text_string(widgets.segments.startfrm_txt, FALSE);
7593 if (buf != NULL) {
7594 sf = atoi(buf);
7595 MEM_FREE(buf);
7596 }
7597 else
7598 sf = 0; /* shrug */
7599
7600 buf = get_text_string(widgets.segments.endtrk_txt, FALSE);
7601 if (buf != NULL) {
7602 et = atoi(buf);
7603 MEM_FREE(buf);
7604 }
7605 else
7606 et = 0; /* shrug */
7607
7608 buf = get_text_string(widgets.segments.endfrm_txt, FALSE);
7609 if (buf != NULL) {
7610 ef = atoi(buf);
7611 MEM_FREE(buf);
7612 }
7613 else
7614 ef = 0; /* shrug */
7615
7616 for (i = 0; i < (int) s->tot_trks; i++) {
7617 if (s->trkinfo[i].trkno == st)
7618 break;
7619 }
7620 if (i == (int) s->tot_trks)
7621 i = 0; /* This shouldn't happen */
7622
7623 s->bp_startpos_tot.addr = s->trkinfo[i].addr + sf;
7624 util_blktomsf(
7625 s->bp_startpos_tot.addr,
7626 &s->bp_startpos_tot.min,
7627 &s->bp_startpos_tot.sec,
7628 &s->bp_startpos_tot.frame,
7629 MSF_OFFSET
7630 );
7631
7632 s->bp_startpos_trk.addr = sf;
7633 util_blktomsf(
7634 s->bp_startpos_trk.addr,
7635 &s->bp_startpos_trk.min,
7636 &s->bp_startpos_trk.sec,
7637 &s->bp_startpos_trk.frame,
7638 MSF_OFFSET
7639 );
7640
7641 for (i = 0; i < (int) s->tot_trks; i++) {
7642 if (s->trkinfo[i].trkno == et)
7643 break;
7644 }
7645 if (i == (int) s->tot_trks) {
7646 i--; /* This shouldn't happen */
7647
7648 eot = s->trkinfo[i+1].addr;
7649
7650 /* "Enhanced CD" / "CD Extra" hack */
7651 if (eot > s->discpos_tot.addr)
7652 eot = s->discpos_tot.addr;
7653
7654 ef = eot - s->trkinfo[i].addr - 25;
7655 }
7656
7657 s->bp_endpos_tot.addr = s->trkinfo[i].addr + ef;
7658 util_blktomsf(
7659 s->bp_endpos_tot.addr,
7660 &s->bp_endpos_tot.min,
7661 &s->bp_endpos_tot.sec,
7662 &s->bp_endpos_tot.frame,
7663 MSF_OFFSET
7664 );
7665
7666 s->bp_endpos_trk.addr = ef;
7667 util_blktomsf(
7668 s->bp_endpos_trk.addr,
7669 &s->bp_endpos_trk.min,
7670 &s->bp_endpos_trk.sec,
7671 &s->bp_endpos_trk.frame,
7672 MSF_OFFSET
7673 );
7674
7675 /* Clear any play programs */
7676 set_text_string(widgets.dbprog.pgmseq_txt, "", FALSE);
7677 XtSetSensitive(widgets.dbprog.addpgm_btn, False);
7678 XtSetSensitive(widgets.dbprog.clrpgm_btn, False);
7679 XtSetSensitive(widgets.dbprog.savepgm_btn, False);
7680 s->onetrk_prog = FALSE;
7681 dbprog_progclear(s);
7682
7683 /* Disable shuffle mode */
7684 if (s->shuffle) {
7685 di_shuffle(s, FALSE);
7686 set_shuffle_btn(FALSE);
7687 }
7688
7689 /* Stop playback */
7690 if (s->mode != MOD_STOP)
7691 di_stop(s, TRUE);
7692
7693 /* Set a->b mode */
7694 s->segplay = SEGP_AB;
7695 dpy_progmode(s, FALSE);
7696 }
7697
7698 /* Update credits window */
7699 dbprog_creditupd(s, -1);
7700 }
7701
7702
7703 /*
7704 * dbprog_segments_ok
7705 * Credits window OK button callback.
7706 */
7707 /*ARGSUSED*/
7708 void
dbprog_segments_ok(Widget w,XtPointer client_data,XtPointer call_data)7709 dbprog_segments_ok(Widget w, XtPointer client_data, XtPointer call_data)
7710 {
7711 if ((w == widgets.segments.ok_btn ||
7712 w == widgets.dbprog.segments_btn) &&
7713 (XtIsSensitive(widgets.segments.add_btn) ||
7714 XtIsSensitive(widgets.segments.mod_btn))) {
7715 (void) cd_confirm_popup(
7716 app_data.str_confirm,
7717 app_data.str_discardchg,
7718 (XtCallbackProc) dbprog_segments_popdown, client_data,
7719 (XtCallbackProc) NULL, NULL
7720 );
7721 return;
7722 }
7723
7724 dbprog_segments_popdown(w, client_data, call_data);
7725 }
7726
7727
7728 /*
7729 * dbprog_segments_add
7730 * Credits window Add button callback.
7731 */
7732 /*ARGSUSED*/
7733 void
dbprog_segments_add(Widget w,XtPointer client_data,XtPointer call_data)7734 dbprog_segments_add(Widget w, XtPointer client_data, XtPointer call_data)
7735 {
7736 curstat_t *s = (curstat_t *)(void *) client_data;
7737 cdinfo_segment_t *p,
7738 *q;
7739 int n;
7740
7741 if (!dbprog_segment_ck(&w_seg, s))
7742 return;
7743
7744 p = (cdinfo_segment_t *)(void *) MEM_ALLOC(
7745 "cdinfo_segment_t",
7746 sizeof(cdinfo_segment_t)
7747 );
7748 if (p == NULL) {
7749 CD_FATAL(app_data.str_nomemory);
7750 return;
7751 }
7752 (void) memset(p, 0, sizeof(cdinfo_segment_t));
7753
7754 /* Copy tmp segment structure content to newly allocated struct */
7755 q = &w_seg;
7756 p->name = q->name;
7757 p->start_track = q->start_track;
7758 p->start_frame = q->start_frame;
7759 p->end_track = q->end_track;
7760 p->end_frame = q->end_frame;
7761 p->notes = q->notes;
7762 p->credit_list = q->credit_list;
7763
7764 /* Clean up work structure */
7765 q->name = NULL;
7766 q->start_track = NULL;
7767 q->start_frame = NULL;
7768 q->end_track = NULL;
7769 q->end_frame = NULL;
7770 q->notes = NULL;
7771 q->credit_list = NULL;
7772
7773 /* Add to incore segments list */
7774 n = 0;
7775 if (dbp->disc.segment_list == NULL) {
7776 /* First entry in list */
7777 dbp->disc.segment_list = p;
7778 }
7779 else {
7780 /* Look for end of segments list and append to it */
7781 for (n = 1, q = dbp->disc.segment_list; q->next != NULL;
7782 q = q->next, n++)
7783 ;
7784 q->next = p;
7785 p->prev = q;
7786 }
7787 n++;
7788
7789 dbprog_segmentupd(s);
7790 dbprog_list_autoscroll(widgets.segments.seg_list, n, n);
7791
7792 if (credits_mode == CREDITS_SEG)
7793 dbprog_creditupd(s, -1);
7794
7795 dbp->flags |= CDINFO_CHANGED;
7796 if (!app_data.cdinfo_inetoffln)
7797 XtSetSensitive(widgets.dbprog.submit_btn, True);
7798 }
7799
7800
7801 /*
7802 * dbprog_segments_mod
7803 * Credits window Modify button callback.
7804 */
7805 /*ARGSUSED*/
7806 void
dbprog_segments_mod(Widget w,XtPointer client_data,XtPointer call_data)7807 dbprog_segments_mod(Widget w, XtPointer client_data, XtPointer call_data)
7808 {
7809 curstat_t *s = (curstat_t *)(void *) client_data;
7810 cdinfo_segment_t *p,
7811 *q;
7812 cdinfo_credit_t *cp,
7813 *cp2;
7814 int n,
7815 sav_segpos;
7816
7817 if (seg_pos < 0)
7818 return; /* Invalid */
7819
7820 if (!dbprog_segment_ck(&w_seg, s))
7821 return;
7822
7823 for (n = 0, p = dbp->disc.segment_list; p != NULL; p = p->next, n++) {
7824 if ((n+1) != seg_pos)
7825 continue;
7826
7827 /* Update appropriate incore segment entry */
7828 q = &w_seg;
7829 if (p->name != NULL)
7830 MEM_FREE(p->name);
7831 if (p->start_track != NULL)
7832 MEM_FREE(p->start_track);
7833 if (p->start_frame != NULL)
7834 MEM_FREE(p->start_frame);
7835 if (p->end_track != NULL)
7836 MEM_FREE(p->end_track);
7837 if (p->end_frame != NULL)
7838 MEM_FREE(p->end_frame);
7839 if (p->notes != NULL)
7840 MEM_FREE(p->notes);
7841 for (cp = cp2 = p->credit_list; cp != NULL; cp = cp2) {
7842 cp2 = cp->next;
7843
7844 if (cp->crinfo.name != NULL)
7845 MEM_FREE(cp->crinfo.name);
7846 if (cp->crinfo.fullname.dispname != NULL)
7847 MEM_FREE(cp->crinfo.fullname.dispname);
7848 if (cp->crinfo.fullname.lastname != NULL)
7849 MEM_FREE(cp->crinfo.fullname.lastname);
7850 if (cp->crinfo.fullname.firstname != NULL)
7851 MEM_FREE(cp->crinfo.fullname.firstname);
7852 if (cp->crinfo.fullname.the != NULL)
7853 MEM_FREE(cp->crinfo.fullname.the);
7854 if (cp->notes != NULL)
7855 MEM_FREE(cp->notes);
7856 MEM_FREE(cp);
7857 }
7858 p->credit_list = NULL;
7859
7860 p->name = q->name;
7861 p->start_track = q->start_track;
7862 p->start_frame = q->start_frame;
7863 p->end_track = q->end_track;
7864 p->end_frame = q->end_frame;
7865 p->notes = q->notes;
7866 p->credit_list = q->credit_list;
7867
7868 /* Clean up work structure */
7869 q->name = NULL;
7870 q->start_track = NULL;
7871 q->start_frame = NULL;
7872 q->end_track = NULL;
7873 q->end_frame = NULL;
7874 q->notes = NULL;
7875 q->credit_list = NULL;
7876 }
7877
7878 sav_segpos = seg_pos;
7879
7880 dbprog_segmentupd(s);
7881 dbprog_list_autoscroll(widgets.segments.seg_list, n, sav_segpos);
7882
7883 if (credits_mode == CREDITS_SEG)
7884 dbprog_creditupd(s, -1);
7885
7886 dbp->flags |= CDINFO_CHANGED;
7887 if (!app_data.cdinfo_inetoffln)
7888 XtSetSensitive(widgets.dbprog.submit_btn, True);
7889 }
7890
7891
7892 /*
7893 * dbprog_segments_del
7894 * Credits window Delete button callback.
7895 */
7896 /*ARGSUSED*/
7897 void
dbprog_segments_del(Widget w,XtPointer client_data,XtPointer call_data)7898 dbprog_segments_del(Widget w, XtPointer client_data, XtPointer call_data)
7899 {
7900 curstat_t *s = (curstat_t *)(void *) client_data;
7901 cdinfo_segment_t *p;
7902 cdinfo_credit_t *cp,
7903 *cp2;
7904 int i,
7905 n,
7906 scrollpos;
7907
7908 if (seg_pos < 0)
7909 return; /* Invalid */
7910
7911 /* Delete from incore segment list */
7912 if (seg_pos == 1) {
7913 p = dbp->disc.segment_list;
7914 dbp->disc.segment_list = p->next;
7915 if (p->next != NULL)
7916 p->next->prev = NULL;
7917 }
7918 else {
7919 i = 0;
7920 for (p = dbp->disc.segment_list; p != NULL; p = p->next) {
7921 if (++i < seg_pos)
7922 continue;
7923
7924 if (p->next != NULL)
7925 p->next->prev = p->prev;
7926 if (p->prev != NULL)
7927 p->prev->next = p->next;
7928
7929 break;
7930 }
7931 }
7932
7933 if (p != NULL) {
7934 if (p->name != NULL)
7935 MEM_FREE(p->name);
7936 if (p->start_track != NULL)
7937 MEM_FREE(p->start_track);
7938 if (p->start_frame != NULL)
7939 MEM_FREE(p->start_frame);
7940 if (p->end_track != NULL)
7941 MEM_FREE(p->end_track);
7942 if (p->end_frame != NULL)
7943 MEM_FREE(p->end_frame);
7944 if (p->notes != NULL)
7945 MEM_FREE(p->notes);
7946 for (cp = cp2 = p->credit_list; cp != NULL; cp = cp2) {
7947 cp2 = cp->next;
7948
7949 if (cp->crinfo.name != NULL)
7950 MEM_FREE(cp->crinfo.name);
7951 if (cp->crinfo.fullname.dispname != NULL)
7952 MEM_FREE(cp->crinfo.fullname.dispname);
7953 if (cp->crinfo.fullname.lastname != NULL)
7954 MEM_FREE(cp->crinfo.fullname.lastname);
7955 if (cp->crinfo.fullname.firstname != NULL)
7956 MEM_FREE(cp->crinfo.fullname.firstname);
7957 if (cp->crinfo.fullname.the != NULL)
7958 MEM_FREE(cp->crinfo.fullname.the);
7959 if (cp->notes != NULL)
7960 MEM_FREE(cp->notes);
7961 MEM_FREE(cp);
7962 }
7963
7964 MEM_FREE(p);
7965 }
7966
7967 for (n = 0, p = dbp->disc.segment_list; p != NULL; p = p->next, n++)
7968 ;
7969
7970 if (n > seg_pos)
7971 scrollpos = seg_pos;
7972 else
7973 scrollpos = n;
7974
7975 dbprog_segmentupd(s);
7976 dbprog_list_autoscroll(widgets.segments.seg_list, n, scrollpos);
7977
7978 if (credits_mode == CREDITS_SEG)
7979 dbprog_creditupd(s, -1);
7980
7981 dbp->flags |= CDINFO_CHANGED;
7982 if (!app_data.cdinfo_inetoffln)
7983 XtSetSensitive(widgets.dbprog.submit_btn, True);
7984 }
7985
7986
7987 /*
7988 * dbprog_regionsel_popup
7989 * Region change button callback.
7990 */
7991 /*ARGSUSED*/
7992 void
dbprog_regionsel_popup(Widget w,XtPointer client_data,XtPointer call_data)7993 dbprog_regionsel_popup(Widget w, XtPointer client_data, XtPointer call_data)
7994 {
7995 int i;
7996 cdinfo_region_t *rp;
7997 char *region;
7998
7999 if (w == widgets.dbextd.region_chg_btn) {
8000 reg_mode = REGION_DISC;
8001 region = dbp->disc.region;
8002 }
8003 else if (w == widgets.userreg.region_chg_btn) {
8004 reg_mode = REGION_USERREG;
8005 region = dbp->userreg.region;
8006 }
8007 else
8008 return;
8009
8010 /* Pre-select the current region if any */
8011 if (region != NULL) {
8012 rp = dbp->regionlist;
8013 for (i = 1; rp != NULL; i++, rp = rp->next) {
8014 if (strcmp(region, rp->id) == 0) {
8015 /* Pre-select the current region */
8016 XmListSelectPos(widgets.regionsel.region_list,
8017 i, False);
8018 dbprog_list_autoscroll(
8019 widgets.regionsel.region_list,
8020 reg_cnt, i
8021 );
8022 break;
8023 }
8024 }
8025 }
8026
8027 /* Pop up the region selector window.
8028 * The dialog has mappedWhenManaged set to False,
8029 * so we have to map/unmap explicitly. The reason
8030 * for this is we want to avoid a screen glitch when
8031 * we move the window in cd_dialog_setpos(), so we
8032 * map the window afterwards.
8033 */
8034 if (!XtIsManaged(widgets.regionsel.form)) {
8035 XtManageChild(widgets.regionsel.form);
8036
8037 /* Set up dialog box position */
8038 cd_dialog_setpos(XtParent(widgets.regionsel.form));
8039
8040 XtMapWidget(XtParent(widgets.regionsel.form));
8041
8042 /* Put focus on the region list */
8043 XmProcessTraversal(
8044 widgets.regionsel.region_list, XmTRAVERSE_CURRENT
8045 );
8046 }
8047 }
8048
8049
8050 /*
8051 * dbprog_regionsel_select
8052 * Region selection list callback.
8053 */
8054 /*ARGSUSED*/
8055 void
dbprog_regionsel_select(Widget w,XtPointer client_data,XtPointer call_data)8056 dbprog_regionsel_select(Widget w, XtPointer client_data, XtPointer call_data)
8057 {
8058 int i;
8059 XmListCallbackStruct *p =
8060 (XmListCallbackStruct *)(void *) call_data;
8061 cdinfo_region_t *rp;
8062 char **fptr;
8063
8064 if (p->reason != XmCR_BROWSE_SELECT)
8065 return;
8066
8067 if (reg_mode == REGION_DISC)
8068 fptr = &dbp->disc.region;
8069 else if (reg_mode == REGION_USERREG)
8070 fptr = &dbp->userreg.region;
8071 else
8072 return;
8073
8074 /* Look for matching region list entry */
8075 i = 0;
8076 for (rp = dbp->regionlist; rp != NULL; rp = rp->next) {
8077 if (++i == p->item_position)
8078 break;
8079 }
8080
8081 if (rp == NULL || rp->id == NULL || i != p->item_position) {
8082 /* Error: this shouldn't happen */
8083 return;
8084 }
8085
8086 if (*fptr != NULL && strcmp(rp->id, *fptr) == 0) {
8087 /* Not changed - de-select the entry */
8088 XmListDeselectPos(w, p->item_position);
8089
8090 MEM_FREE(*fptr);
8091 *fptr = NULL;
8092
8093 if (reg_mode == REGION_DISC)
8094 set_text_string(widgets.dbextd.region_txt, "", FALSE);
8095 else if (reg_mode == REGION_USERREG)
8096 set_text_string(widgets.userreg.region_txt, "", FALSE);
8097 return;
8098 }
8099
8100 if (reg_mode == REGION_DISC) {
8101 dbp->flags |= CDINFO_CHANGED;
8102 if (!app_data.cdinfo_inetoffln)
8103 XtSetSensitive(widgets.dbprog.submit_btn, True);
8104 }
8105
8106 if (!util_newstr(fptr, rp->id)) {
8107 CD_FATAL(app_data.str_nomemory);
8108 return;
8109 }
8110
8111 if (reg_mode == REGION_DISC)
8112 set_text_string(widgets.dbextd.region_txt, rp->name, TRUE);
8113 else if (reg_mode == REGION_USERREG)
8114 set_text_string(widgets.userreg.region_txt, rp->name, TRUE);
8115 }
8116
8117
8118 /*
8119 * dbprog_regionsel_ok
8120 * Region selection window OK button callback.
8121 */
8122 /*ARGSUSED*/
8123 void
dbprog_regionsel_ok(Widget w,XtPointer client_data,XtPointer call_data)8124 dbprog_regionsel_ok(Widget w, XtPointer client_data, XtPointer call_data)
8125 {
8126 int i;
8127 XmListCallbackStruct *p =
8128 (XmListCallbackStruct *)(void *) call_data;
8129 cdinfo_region_t *rp;
8130
8131 if (w == widgets.regionsel.region_list &&
8132 p->reason == XmCR_DEFAULT_ACTION) {
8133 /* Look for matching region list entry */
8134 i = 0;
8135 for (rp = dbp->regionlist; rp != NULL; rp = rp->next) {
8136 if (++i == p->item_position)
8137 break;
8138 }
8139
8140 if (rp == NULL || rp->id == NULL || i != p->item_position) {
8141 /* Error: this shouldn't happen */
8142 return;
8143 }
8144
8145 if (reg_mode == REGION_DISC)
8146 set_text_string(widgets.dbextd.region_txt,
8147 rp->name, TRUE);
8148 else if (reg_mode == REGION_USERREG)
8149 set_text_string(widgets.userreg.region_txt,
8150 rp->name, TRUE);
8151 }
8152
8153 /* Pop down the region selector popup dialog */
8154 XtUnmapWidget(XtParent(widgets.regionsel.form));
8155 XtUnmanageChild(widgets.regionsel.form);
8156
8157 XmListDeselectAllItems(widgets.regionsel.region_list);
8158
8159 /* Put the input focus on the next widget */
8160 if (reg_mode == REGION_DISC) {
8161 XmProcessTraversal(
8162 widgets.dbextd.lang_chg_btn,
8163 XmTRAVERSE_CURRENT
8164 );
8165 }
8166 else if (reg_mode == REGION_USERREG) {
8167 XmProcessTraversal(
8168 widgets.userreg.postal_txt,
8169 XmTRAVERSE_CURRENT
8170 );
8171 }
8172
8173 reg_mode = REGION_NONE;
8174 }
8175
8176
8177 /*
8178 * dbprog_langsel_popup
8179 * Language change button callback.
8180 */
8181 /*ARGSUSED*/
8182 void
dbprog_langsel_popup(Widget w,XtPointer client_data,XtPointer call_data)8183 dbprog_langsel_popup(Widget w, XtPointer client_data, XtPointer call_data)
8184 {
8185 int i;
8186 cdinfo_lang_t *lp;
8187
8188 /* Pre-select the current language if any */
8189 if (dbp->disc.lang != NULL) {
8190 lp = dbp->langlist;
8191 for (i = 1; lp != NULL; i++, lp = lp->next) {
8192 if (strcmp(dbp->disc.lang, lp->id) == 0) {
8193 /* Pre-select the current language */
8194 XmListSelectPos(widgets.langsel.lang_list,
8195 i, False);
8196 dbprog_list_autoscroll(
8197 widgets.langsel.lang_list,
8198 lang_cnt, i
8199 );
8200 break;
8201 }
8202 }
8203 }
8204
8205 /* Pop up the language selector window.
8206 * The dialog has mappedWhenManaged set to False,
8207 * so we have to map/unmap explicitly. The reason
8208 * for this is we want to avoid a screen glitch when
8209 * we move the window in cd_dialog_setpos(), so we
8210 * map the window afterwards.
8211 */
8212 if (!XtIsManaged(widgets.langsel.form)) {
8213 XtManageChild(widgets.langsel.form);
8214
8215 /* Set up dialog box position */
8216 cd_dialog_setpos(XtParent(widgets.langsel.form));
8217
8218 XtMapWidget(XtParent(widgets.langsel.form));
8219
8220 /* Put focus on the language list */
8221 XmProcessTraversal(
8222 widgets.langsel.lang_list, XmTRAVERSE_CURRENT
8223 );
8224 }
8225 }
8226
8227
8228 /*
8229 * dbprog_langsel_select
8230 * Language selection list callback.
8231 */
8232 /*ARGSUSED*/
8233 void
dbprog_langsel_select(Widget w,XtPointer client_data,XtPointer call_data)8234 dbprog_langsel_select(Widget w, XtPointer client_data, XtPointer call_data)
8235 {
8236 int i;
8237 XmListCallbackStruct *p =
8238 (XmListCallbackStruct *)(void *) call_data;
8239 cdinfo_lang_t *lp;
8240
8241 if (p->reason != XmCR_BROWSE_SELECT)
8242 return;
8243
8244 /* Look for matching region list entry */
8245 i = 0;
8246 for (lp = dbp->langlist; lp != NULL; lp = lp->next) {
8247 if (++i == p->item_position)
8248 break;
8249 }
8250
8251 if (lp == NULL || lp->id == NULL || i != p->item_position) {
8252 /* Error: this shouldn't happen */
8253 return;
8254 }
8255
8256 if (dbp->disc.lang != NULL && strcmp(lp->id, dbp->disc.lang) == 0) {
8257 /* Not changed - de-select the entry */
8258 XmListDeselectPos(w, p->item_position);
8259
8260 MEM_FREE(dbp->disc.lang);
8261 dbp->disc.lang = NULL;
8262
8263 set_text_string(widgets.dbextd.lang_txt, "", FALSE);
8264 return;
8265 }
8266
8267 dbp->flags |= CDINFO_CHANGED;
8268 if (!app_data.cdinfo_inetoffln)
8269 XtSetSensitive(widgets.dbprog.submit_btn, True);
8270
8271 if (!util_newstr(&dbp->disc.lang, lp->id)) {
8272 CD_FATAL(app_data.str_nomemory);
8273 return;
8274 }
8275
8276 set_text_string(widgets.dbextd.lang_txt, lp->name, TRUE);
8277 }
8278
8279
8280 /*
8281 * dbprog_langsel_ok
8282 * Language selection window OK button callback.
8283 */
8284 /*ARGSUSED*/
8285 void
dbprog_langsel_ok(Widget w,XtPointer client_data,XtPointer call_data)8286 dbprog_langsel_ok(Widget w, XtPointer client_data, XtPointer call_data)
8287 {
8288 int i;
8289 XmListCallbackStruct *p =
8290 (XmListCallbackStruct *)(void *) call_data;
8291 cdinfo_lang_t *lp;
8292
8293 if (w == widgets.langsel.lang_list &&
8294 p->reason == XmCR_DEFAULT_ACTION) {
8295 /* Look for matching language list entry */
8296 i = 0;
8297 for (lp = dbp->langlist; lp != NULL; lp = lp->next) {
8298 if (++i == p->item_position)
8299 break;
8300 }
8301
8302 if (lp == NULL || lp->id == NULL || i != p->item_position) {
8303 /* Error: this shouldn't happen */
8304 return;
8305 }
8306
8307 set_text_string(widgets.dbextd.lang_txt, lp->name, TRUE);
8308 }
8309
8310 /* Pop down the language selector popup dialog */
8311 XtUnmapWidget(XtParent(widgets.langsel.form));
8312 XtUnmanageChild(widgets.langsel.form);
8313
8314 XmListDeselectAllItems(widgets.langsel.lang_list);
8315
8316 /* Put the input focus on the next widget */
8317 XmProcessTraversal(
8318 widgets.dbextd.dnum_txt,
8319 XmTRAVERSE_CURRENT
8320 );
8321 }
8322
8323
8324 /*
8325 * dbprog_matchsel_select
8326 * Match selection list callback.
8327 */
8328 /*ARGSUSED*/
8329 void
dbprog_matchsel_select(Widget w,XtPointer client_data,XtPointer call_data)8330 dbprog_matchsel_select(Widget w, XtPointer client_data, XtPointer call_data)
8331 {
8332 XmListCallbackStruct *p =
8333 (XmListCallbackStruct *)(void *) call_data;
8334
8335 if (p->reason != XmCR_BROWSE_SELECT)
8336 return;
8337
8338 if (p->item_position == match_cnt)
8339 dbp->match_tag = 0; /* User chose "none of the above" */
8340 else
8341 dbp->match_tag = (long) p->item_position;
8342
8343 if (!XtIsSensitive(widgets.matchsel.ok_btn))
8344 XtSetSensitive(widgets.matchsel.ok_btn, True);
8345 }
8346
8347
8348 /*
8349 * dbprog_matchsel_ok
8350 * Match selection window OK button callback.
8351 */
8352 /*ARGSUSED*/
8353 void
dbprog_matchsel_ok(Widget w,XtPointer client_data,XtPointer call_data)8354 dbprog_matchsel_ok(Widget w, XtPointer client_data, XtPointer call_data)
8355 {
8356 curstat_t *s = (curstat_t *)(void *) client_data;
8357 cdinfo_ret_t ret;
8358
8359 if (XtIsManaged(widgets.matchsel.form)) {
8360 /* Pop down the match selector popup dialog */
8361 XtUnmapWidget(XtParent(widgets.matchsel.form));
8362 XtUnmanageChild(widgets.matchsel.form);
8363
8364 /* Remove all matchsel list items */
8365 XmListDeleteAllItems(widgets.matchsel.matchsel_list);
8366 match_cnt = 0;
8367 }
8368
8369 if (dbp->match_tag == 0)
8370 s->qmode = QMODE_NONE;
8371 else
8372 s->qmode = QMODE_WAIT;
8373
8374 dpy_dbmode(s, FALSE);
8375
8376 /* Re-query CDDB for the user selected entry */
8377 if ((ret = cdinfo_load_matchsel(s)) != 0) {
8378 DBGPRN(DBG_CDI)(errfp,
8379 "cdinfo_load_matchsel: status=%d arg=%d\n",
8380 CDINFO_GET_STAT(ret), CDINFO_GET_ARG(ret));
8381
8382 /* Set qmode flag */
8383 s->qmode = QMODE_ERR;
8384 }
8385 else if ((dbp->flags & CDINFO_MATCH) != 0) {
8386 s->qmode = QMODE_MATCH;
8387
8388 /* Got CDDB data, clear the wwwwarp_cleared flag */
8389 wwwwarp_cleared = FALSE;
8390 }
8391 else
8392 s->qmode = QMODE_NONE;
8393
8394 if (stopload_active) {
8395 /* Pop down the stop load dialog */
8396 stopload_active = FALSE;
8397 cd_confirm_popdown();
8398 }
8399
8400 /* Update widgets */
8401 dbprog_structupd(s);
8402
8403 /* Configure the wwwWarp menu */
8404 wwwwarp_sel_cfg(s);
8405
8406 XtSetSensitive(widgets.dbprog.reload_btn, True);
8407
8408 s->program = dbprog_pgm_active();
8409
8410 /* Update display */
8411 dpy_progmode(s, FALSE);
8412 dpy_dtitle(s);
8413 dpy_ttitle(s);
8414
8415 /* Update curfile */
8416 dbprog_curfileupd();
8417
8418 /* Add to history and changer lists */
8419 dbprog_hist_new(s);
8420 dbprog_chgr_new(s);
8421 }
8422
8423
8424 /*
8425 * dbprog_the
8426 * "The" toggle button callback
8427 */
8428 /*ARGSUSED*/
8429 void
dbprog_the(Widget w,XtPointer client_data,XtPointer call_data)8430 dbprog_the(Widget w, XtPointer client_data, XtPointer call_data)
8431 {
8432 XmToggleButtonCallbackStruct *p =
8433 (XmToggleButtonCallbackStruct *)(void *) call_data;
8434 Widget txtw;
8435
8436 if (w == widgets.fullname.the_btn)
8437 txtw = widgets.fullname.the_txt;
8438 else if (w == widgets.dbextd.the_btn)
8439 txtw = widgets.dbextd.the_txt;
8440 else if (w == widgets.dbextt.the_btn)
8441 txtw = widgets.dbextt.the_txt;
8442 else {
8443 cd_beep();
8444 return;
8445 }
8446
8447 if (XtIsSensitive(txtw) == p->set)
8448 /* No change */
8449 return;
8450
8451 XtSetSensitive(txtw, p->set);
8452 set_text_string(txtw, p->set ? app_data.str_the : "", TRUE);
8453
8454 /* Put keyboard focus on the The text widget */
8455 XmProcessTraversal(txtw, XmTRAVERSE_CURRENT);
8456
8457 dbp->flags |= CDINFO_CHANGED;
8458 if (!app_data.cdinfo_inetoffln)
8459 XtSetSensitive(widgets.dbprog.submit_btn, True);
8460 }
8461
8462
8463 /*
8464 * dbprog_auth_retry
8465 * Let the user have the option of retrying the proxy-authorization.
8466 */
8467 /*ARGSUSED*/
8468 void
dbprog_auth_retry(Widget w,XtPointer client_data,XtPointer call_data)8469 dbprog_auth_retry(Widget w, XtPointer client_data, XtPointer call_data)
8470 {
8471 curstat_t *s = curstat_addr();
8472
8473 /* Clear out previously entered password */
8474 if (dbp->proxy_passwd != NULL) {
8475 (void) memset(dbp->proxy_passwd, 0, strlen(dbp->proxy_passwd));
8476 MEM_FREE(dbp->proxy_passwd);
8477 dbp->proxy_passwd = NULL;
8478 }
8479
8480 if (client_data != NULL) {
8481 /* Clear out password field */
8482 set_text_string(widgets.auth.pass_txt, "", FALSE);
8483
8484 /* Pop up authorization dialog */
8485 dbprog_auth_popup();
8486 }
8487 else {
8488 /* Auth failed: cannot get CD info */
8489
8490 /* Set qmode flag */
8491 s->qmode = QMODE_NONE;
8492
8493 /* Update widgets */
8494 dbprog_structupd(s);
8495
8496 /* Configure the wwwWarp menu */
8497 wwwwarp_sel_cfg(s);
8498
8499 XtSetSensitive(widgets.dbprog.reload_btn, True);
8500
8501 /* Update display */
8502 dpy_dtitle(s);
8503 dpy_ttitle(s);
8504
8505 /* Update curfile */
8506 dbprog_curfileupd();
8507
8508 /* Add to history and changer lists */
8509 dbprog_hist_new(s);
8510 dbprog_chgr_new(s);
8511 }
8512 }
8513
8514
8515 /*
8516 * dbprog_password_vfy
8517 * Verify Callback for password text widgets
8518 */
8519 /*ARGSUSED*/
8520 void
dbprog_password_vfy(Widget w,XtPointer client_data,XtPointer call_data)8521 dbprog_password_vfy(Widget w, XtPointer client_data, XtPointer call_data)
8522 {
8523 XmTextVerifyCallbackStruct
8524 *p = (XmTextVerifyCallbackStruct *)(void *) call_data;
8525 char **fptr,
8526 *str,
8527 *tp,
8528 tc;
8529 int i,
8530 len,
8531 start;
8532
8533 if (p->reason != XmCR_MODIFYING_TEXT_VALUE)
8534 return;
8535
8536 if (w == widgets.auth.pass_txt)
8537 fptr = &dbp->proxy_passwd;
8538 else if (w == widgets.userreg.passwd_txt)
8539 fptr = &dbp->userreg.passwd;
8540 else if (w == widgets.userreg.vpasswd_txt)
8541 fptr = &dbp->userreg.vpasswd;
8542 else
8543 return; /* Invalid widget */
8544
8545 if (p->text->length >= 1 && p->text->ptr != NULL) {
8546 if (*fptr == NULL) {
8547 *fptr = (char *) MEM_ALLOC(
8548 "passwd",
8549 p->text->length + 1
8550 );
8551 if (*fptr == NULL) {
8552 CD_FATAL(app_data.str_nomemory);
8553 return;
8554 }
8555
8556 (void) strncpy(*fptr, p->text->ptr, p->text->length);
8557 (*fptr)[p->text->length] = '\0';
8558 }
8559 else {
8560 len = strlen(*fptr);
8561 start = (p->startPos < len) ? p->startPos : len;
8562 tp = *fptr + start;
8563 tc = *tp;
8564 *tp = '\0';
8565
8566 str = (char *) MEM_ALLOC(
8567 "passwd",
8568 len + p->text->length + 1
8569 );
8570 if (str == NULL) {
8571 CD_FATAL(app_data.str_nomemory);
8572 return;
8573 }
8574
8575 (void) strcpy(str, *fptr);
8576 (void) strncat(str, p->text->ptr, p->text->length);
8577 str[start + p->text->length] = '\0';
8578 *tp = tc;
8579 (void) strcat(str, tp);
8580
8581 (void) memset(*fptr, 0, strlen(*fptr));
8582 MEM_FREE(*fptr);
8583 *fptr = str;
8584 }
8585
8586 /* Display '*' instead of what the user typed */
8587 for (i = 0; i < p->text->length; i++)
8588 p->text->ptr[i] = '*';
8589
8590 p->doit = True;
8591 }
8592 else if (p->text->length == 0) {
8593 /* backspace */
8594 if (*fptr != NULL && (*fptr)[0] != '\0') {
8595 len = strlen(*fptr);
8596
8597 start = (p->startPos < len) ? p->startPos : (len - 1);
8598 tp = *fptr + ((p->endPos > len) ? len : p->endPos);
8599
8600 (*fptr)[start] = '\0';
8601 (void) strcat(*fptr, tp);
8602 }
8603
8604 p->doit = True;
8605 }
8606 }
8607
8608
8609 /*
8610 * dbprog_auth_ok
8611 * Callback for the proxy-authorization OK button
8612 */
8613 /*ARGSUSED*/
8614 void
dbprog_auth_ok(Widget w,XtPointer client_data,XtPointer call_data)8615 dbprog_auth_ok(Widget w, XtPointer client_data, XtPointer call_data)
8616 {
8617 curstat_t *s = (curstat_t *)(void *) client_data;
8618 char *name;
8619
8620 if (XtIsManaged(widgets.auth.form)) {
8621 XtUnmapWidget(XtParent(widgets.auth.form));
8622 XtUnmanageChild(widgets.auth.form);
8623 }
8624
8625 auth_initted = TRUE;
8626
8627 /* Set proxy auth user name */
8628 name = get_text_string(widgets.auth.name_txt, TRUE);
8629 if (!util_newstr(&dbp->proxy_user, name)) {
8630 CD_FATAL(app_data.str_nomemory);
8631 return;
8632 }
8633 XtFree(name);
8634
8635 /* Re-load CD information */
8636 dbprog_dbget(s);
8637 }
8638
8639
8640 /*
8641 * dbprog_auth_cancel
8642 * Callback for the proxy-authorization Cancel button
8643 */
8644 /*ARGSUSED*/
8645 void
dbprog_auth_cancel(Widget w,XtPointer client_data,XtPointer call_data)8646 dbprog_auth_cancel(Widget w, XtPointer client_data, XtPointer call_data)
8647 {
8648 if (XtIsManaged(widgets.auth.form)) {
8649 XtUnmapWidget(XtParent(widgets.auth.form));
8650 XtUnmanageChild(widgets.auth.form);
8651 }
8652
8653 dbprog_auth_retry(w, NULL, call_data);
8654 }
8655
8656
8657 /*
8658 * dbprog_dlist_cancel
8659 * Callback for the Disc List Cancel button
8660 */
8661 /*ARGSUSED*/
8662 void
dbprog_dlist_cancel(Widget w,XtPointer client_data,XtPointer call_data)8663 dbprog_dlist_cancel(Widget w, XtPointer client_data, XtPointer call_data)
8664 {
8665 XtUnmapWidget(XtParent(widgets.dlist.form));
8666 XtUnmanageChild(widgets.dlist.form);
8667 }
8668
8669
8670 /*
8671 * dbprog_dlist
8672 * Pop up/down the disc list window.
8673 */
8674 /*ARGSUSED*/
8675 void
dbprog_dlist(Widget w,XtPointer client_data,XtPointer call_data)8676 dbprog_dlist(Widget w, XtPointer client_data, XtPointer call_data)
8677 {
8678 curstat_t *s = (curstat_t *)(void *) client_data;
8679 static bool_t first = TRUE;
8680
8681 if (XtIsManaged(widgets.dlist.form)) {
8682 /* Pop down the Disc List window */
8683 dbprog_dlist_cancel(w, client_data, call_data);
8684 return;
8685 }
8686
8687 if (dlist_mode == DLIST_HIST) {
8688 if (!hist_initted) {
8689 /* Display the in-core history list */
8690 dbprog_hist_addall(s);
8691
8692 hist_initted = TRUE;
8693 }
8694 }
8695
8696 /* Pop up the Disc List window.
8697 * The dialog has mappedWhenManaged set to False,
8698 * so we have to map/unmap explicitly. The reason
8699 * for this is we want to avoid a screen glitch when
8700 * we move the window in cd_dialog_setpos(), so we
8701 * map the window afterwards.
8702 */
8703 XtManageChild(widgets.dlist.form);
8704 if (first) {
8705 first = FALSE;
8706 /* Set window position */
8707 cd_dialog_setpos(XtParent(widgets.dlist.form));
8708 }
8709 XtMapWidget(XtParent(widgets.dlist.form));
8710
8711 /* Put input focus on the cancel button */
8712 XmProcessTraversal(widgets.dlist.cancel_btn, XmTRAVERSE_CURRENT);
8713 }
8714
8715
8716 /*
8717 * dbprog_dlist_mode
8718 * Disc list type selector menu callback
8719 */
8720 /*ARGSUSED*/
8721 void
dbprog_dlist_mode(Widget w,XtPointer client_data,XtPointer call_data)8722 dbprog_dlist_mode(Widget w, XtPointer client_data, XtPointer call_data)
8723 {
8724 curstat_t *s = (curstat_t *)(void *) client_data;
8725 int newmode;
8726
8727 if (w == widgets.dlist.hist_btn)
8728 newmode = DLIST_HIST;
8729 else
8730 newmode = DLIST_CHGR;
8731
8732 DBGPRN(DBG_UI)(errfp, "\n* DISC LIST: %s mode selected\n",
8733 (newmode == DLIST_HIST) ? "History" : "CD Changer");
8734
8735 if (newmode == dlist_mode)
8736 /* No change */
8737 return;
8738
8739 dlist_mode = newmode;
8740
8741 /* Clean up the disc list widget */
8742 XmListDeleteAllItems(widgets.dlist.disc_list);
8743 dlist_pos = -1;
8744 hist_cnt = 0;
8745
8746 XtSetSensitive(widgets.dlist.show_btn, False);
8747 XtSetSensitive(widgets.dlist.goto_btn, False);
8748 XtSetSensitive(widgets.dlist.del_btn, False);
8749 XtSetSensitive(widgets.dlist.delall_btn, False);
8750
8751 if (dlist_mode == DLIST_HIST) {
8752 /* Display the in-core history list on the
8753 * disc list widget
8754 */
8755 dbprog_hist_addall(s);
8756 }
8757 else {
8758 /* Display the in-core CD changer list on the
8759 * disc list widget.
8760 */
8761 dbprog_chgr_addall(s);
8762 }
8763 }
8764
8765
8766 /*
8767 * dbprog_dlist_select
8768 * Disc list browse selection callback
8769 */
8770 /*ARGSUSED*/
8771 void
dbprog_dlist_select(Widget w,XtPointer client_data,XtPointer call_data)8772 dbprog_dlist_select(Widget w, XtPointer client_data, XtPointer call_data)
8773 {
8774 XmListCallbackStruct *p =
8775 (XmListCallbackStruct *)(void *) call_data;
8776 cdinfo_dlist_t *dp;
8777 int i;
8778
8779 if (dlist_pos == p->item_position) {
8780 /* Already selected: de-select it */
8781 XmListDeselectPos(w, dlist_pos);
8782 dlist_pos = -1;
8783
8784 XtSetSensitive(widgets.dlist.show_btn, False);
8785 if (dlist_mode == DLIST_HIST)
8786 XtSetSensitive(widgets.dlist.del_btn, False);
8787 else if (dlist_mode == DLIST_CHGR)
8788 XtSetSensitive(widgets.dlist.goto_btn, False);
8789 }
8790 else {
8791 dlist_pos = p->item_position;
8792
8793 dp = (dlist_mode == DLIST_HIST) ?
8794 cdinfo_hist_list() : cdinfo_chgr_list();
8795 for (i = 1; dp != NULL; dp = dp->next, i++) {
8796 if (i == dlist_pos)
8797 break;
8798 }
8799 if (dp != NULL && dp->device != NULL)
8800 XtSetSensitive(widgets.dlist.show_btn, True);
8801
8802 if (dlist_mode == DLIST_HIST)
8803 XtSetSensitive(widgets.dlist.del_btn, True);
8804 else if (dlist_mode == DLIST_CHGR)
8805 XtSetSensitive(widgets.dlist.goto_btn, True);
8806 }
8807 }
8808
8809
8810 /*
8811 * dbprog_dlist_show
8812 * Show selected disc list entry
8813 */
8814 /*ARGSUSED*/
8815 void
dbprog_dlist_show(Widget w,XtPointer client_data,XtPointer call_data)8816 dbprog_dlist_show(Widget w, XtPointer client_data, XtPointer call_data)
8817 {
8818 int i;
8819 cdinfo_dlist_t *dp;
8820 struct tm *tm;
8821 char typstr[16],
8822 str[FILE_PATH_SZ * 4];
8823
8824 if (dlist_pos <= 0) {
8825 /* User has not selected a show target yet */
8826 cd_beep();
8827 return;
8828 }
8829
8830 dp = (dlist_mode == DLIST_HIST) ?
8831 cdinfo_hist_list() : cdinfo_chgr_list();
8832 for (i = 1; dp != NULL; dp = dp->next, i++) {
8833 if (i == dlist_pos)
8834 break;
8835 }
8836
8837 XmListDeselectPos(widgets.dlist.disc_list, dlist_pos);
8838 dlist_pos = -1;
8839 XtSetSensitive(widgets.dlist.show_btn, False);
8840 XtSetSensitive(widgets.dlist.goto_btn, False);
8841 XtSetSensitive(widgets.dlist.del_btn, False);
8842
8843 /* Put input focus on the cancel button */
8844 XmProcessTraversal(widgets.dlist.cancel_btn, XmTRAVERSE_CURRENT);
8845
8846 if (dp != NULL) {
8847 #if !defined(__VMS) || ((__VMS_VER >= 70000000) && (__DECC_VER > 50230003))
8848 tzset();
8849 #endif
8850 tm = localtime(&dp->time);
8851
8852 switch (dp->type) {
8853 case CDINFO_DLIST_LOCAL:
8854 (void) sprintf(typstr, " (%.10s):",
8855 app_data.str_local);
8856 break;
8857 case CDINFO_DLIST_REMOTE:
8858 case CDINFO_DLIST_REMOTE1:
8859 (void) sprintf(typstr, " (%.10s):",
8860 app_data.str_cddb);
8861 break;
8862 default:
8863 (void) strcpy(typstr, ":");
8864 break;
8865 }
8866
8867 (void) sprintf(str,
8868 "%.127s%s%.127s\n\n%s %.63s\n%s %.255s\n%s %d\n%s%s %s",
8869 (dp->artist == NULL) ? "" : dp->artist,
8870 (dp->artist != NULL && dp->title != NULL) ? " / " : "",
8871 (dp->title == NULL) ?
8872 app_data.str_unkndisc : dp->title,
8873 "Genre:",
8874 dp->genre == NULL ?
8875 "-" : cdinfo_genre_name(dp->genre),
8876 "Device:",
8877 dp->device == NULL ? "-" : dp->device,
8878 "Disc:", dp->discno,
8879 "Last loaded", typstr,
8880 dp->time == 0 ? "-" : asctime(tm)
8881 );
8882 }
8883 else {
8884 /* Error: shouldn't get here */
8885 cd_beep();
8886 return;
8887 }
8888
8889 /* Pop up information dialog */
8890 CD_INFO(str);
8891 }
8892
8893
8894 /*
8895 * dbprog_dlist_goto
8896 * Cange to the selected disc list entry
8897 */
8898 /*ARGSUSED*/
8899 void
dbprog_dlist_goto(Widget w,XtPointer client_data,XtPointer call_data)8900 dbprog_dlist_goto(Widget w, XtPointer client_data, XtPointer call_data)
8901 {
8902 curstat_t *s = (curstat_t *)(void *) client_data;
8903 int newdisc;
8904
8905 if (dlist_pos <= 0 || dlist_mode != DLIST_CHGR) {
8906 /* User has not selected a goto target yet,
8907 * or wrong list mode.
8908 */
8909 cd_beep();
8910 return;
8911 }
8912
8913 newdisc = dlist_pos;
8914 XmListDeselectPos(widgets.dlist.disc_list, dlist_pos);
8915 dlist_pos = -1;
8916 XtSetSensitive(widgets.dlist.show_btn, False);
8917 XtSetSensitive(widgets.dlist.goto_btn, False);
8918
8919 /* Put input focus on the cancel button */
8920 XmProcessTraversal(widgets.dlist.cancel_btn, XmTRAVERSE_CURRENT);
8921
8922 if (newdisc == s->cur_disc)
8923 return; /* Nothing to do */
8924
8925 s->prev_disc = s->cur_disc;
8926 s->cur_disc = newdisc;
8927
8928 s->flags |= STAT_CHGDISC;
8929
8930 /* Ask the user if the changed CD information
8931 * should be submitted to CDDB.
8932 */
8933 if (!dbprog_chgsubmit(s))
8934 return;
8935
8936 s->flags &= ~STAT_CHGDISC;
8937
8938 /* Change to watch cursor */
8939 cd_busycurs(TRUE, CURS_ALL);
8940
8941 /* Do the disc change */
8942 di_chgdisc(s);
8943
8944 /* Update display */
8945 dpy_dbmode(s, FALSE);
8946 dpy_playmode(s, FALSE);
8947
8948 /* Change to normal cursor */
8949 cd_busycurs(FALSE, CURS_ALL);
8950 }
8951
8952
8953 /*
8954 * dbprog_dlist_delete
8955 * Delete selected disc list entry
8956 */
8957 /*ARGSUSED*/
8958 void
dbprog_dlist_delete(Widget w,XtPointer client_data,XtPointer call_data)8959 dbprog_dlist_delete(Widget w, XtPointer client_data, XtPointer call_data)
8960 {
8961 int i;
8962 cdinfo_dlist_t *hp;
8963
8964 if (dlist_pos <= 0 || dlist_mode != DLIST_HIST) {
8965 /* User has not selected a delete target yet,
8966 * or the list mode is not the history list.
8967 */
8968 cd_beep();
8969 return;
8970 }
8971
8972 /* Delete item from list widget */
8973 XmListDeletePos(widgets.dlist.disc_list, dlist_pos);
8974
8975 /* Delete item from in-core history list */
8976 for (i = 1, hp = cdinfo_hist_list(); hp != NULL; hp = hp->next, i++) {
8977 if (i == dlist_pos) {
8978 (void) cdinfo_hist_delent(hp, TRUE);
8979 break;
8980 }
8981 }
8982
8983 if (hist_cnt > 0)
8984 hist_cnt--;
8985
8986 XtSetSensitive(widgets.dlist.show_btn, False);
8987 XtSetSensitive(widgets.dlist.del_btn, False);
8988 if (cdinfo_hist_list() == NULL)
8989 XtSetSensitive(widgets.dlist.delall_btn, False);
8990
8991 dlist_pos = -1;
8992
8993 /* Put input focus on the cancel button */
8994 XmProcessTraversal(widgets.dlist.cancel_btn, XmTRAVERSE_CURRENT);
8995 }
8996
8997
8998 /*
8999 * dbprog_dlist_delall
9000 * Delete all disc list entries
9001 */
9002 /*ARGSUSED*/
9003 void
dbprog_dlist_delall(Widget w,XtPointer client_data,XtPointer call_data)9004 dbprog_dlist_delall(Widget w, XtPointer client_data, XtPointer call_data)
9005 {
9006 char *str;
9007 size_t len;
9008
9009 if (dlist_mode != DLIST_HIST) {
9010 /* Delete is supported only for the history list */
9011 cd_beep();
9012 return;
9013 }
9014
9015 /* Put input focus on the cancel button */
9016 XmProcessTraversal(widgets.dlist.cancel_btn, XmTRAVERSE_CURRENT);
9017
9018 len = strlen(app_data.str_dlist_delall) +
9019 strlen(app_data.str_askproceed) + 2;
9020 if ((str = (char *) MEM_ALLOC("msg", len)) == NULL) {
9021 CD_FATAL(app_data.str_nomemory);
9022 return;
9023 }
9024 (void) sprintf(str, "%s\n%s",
9025 app_data.str_dlist_delall,
9026 app_data.str_askproceed
9027 );
9028
9029 /* Pop up confirm dialog */
9030 (void) cd_confirm_popup(
9031 app_data.str_confirm,
9032 str,
9033 (XtCallbackProc) dbprog_dlist_delall_yes, client_data,
9034 (XtCallbackProc) NULL, NULL
9035 );
9036
9037 MEM_FREE(str);
9038 }
9039
9040
9041 /*
9042 * dbprog_dlist_delall_yes
9043 * Delete all confirmation dialog "Yes" button callback.
9044 */
9045 /*ARGSUSED*/
9046 void
dbprog_dlist_delall_yes(Widget w,XtPointer client_data,XtPointer call_data)9047 dbprog_dlist_delall_yes(Widget w, XtPointer client_data, XtPointer call_data)
9048 {
9049 XtSetSensitive(widgets.dlist.show_btn, False);
9050 XtSetSensitive(widgets.dlist.del_btn, False);
9051 XtSetSensitive(widgets.dlist.delall_btn, False);
9052
9053 /* Delete all items in the list widget */
9054 XmListDeleteAllItems(widgets.dlist.disc_list);
9055
9056 /* Delete in-core history list and history file */
9057 cdinfo_hist_delall(TRUE);
9058
9059 dlist_pos = -1;
9060 hist_cnt = 0;
9061 }
9062
9063
9064 /*
9065 * dbprog_dlist_rescan
9066 * Re-scan and re-display disc list entries
9067 */
9068 /*ARGSUSED*/
9069 void
dbprog_dlist_rescan(Widget w,XtPointer client_data,XtPointer call_data)9070 dbprog_dlist_rescan(Widget w, XtPointer client_data, XtPointer call_data)
9071 {
9072 curstat_t *s = (curstat_t *)(void *) client_data;
9073
9074 if (dlist_mode == DLIST_HIST) {
9075 /* Change to watch cursor */
9076 cd_busycurs(TRUE, CURS_DLIST);
9077
9078 XtSetSensitive(widgets.dlist.show_btn, False);
9079 XtSetSensitive(widgets.dlist.goto_btn, False);
9080 XtSetSensitive(widgets.dlist.del_btn, False);
9081 XtSetSensitive(widgets.dlist.delall_btn, False);
9082
9083 /* Delete all items in the list widget */
9084 XmListDeleteAllItems(widgets.dlist.disc_list);
9085
9086 /* Delete in-core history list */
9087 cdinfo_hist_delall(FALSE);
9088
9089 dlist_pos = -1;
9090 hist_cnt = 0;
9091
9092 /* Re-load history file */
9093 cdinfo_hist_init();
9094
9095 /* Update list widget */
9096 dbprog_hist_addall(s);
9097
9098 /* Change to normal cursor */
9099 cd_busycurs(FALSE, CURS_DLIST);
9100 }
9101 else {
9102 if (app_data.numdiscs == 1)
9103 /* Not a changer: nothing to do */
9104 return;
9105
9106 switch (s->mode) {
9107 case MOD_PLAY:
9108 case MOD_PAUSE:
9109 case MOD_SAMPLE:
9110 /* Stop playing first */
9111 di_stop(s, FALSE);
9112 break;
9113 default:
9114 break;
9115 }
9116
9117 /* Make the re-scan button insensitive for now */
9118 XtSetSensitive(w, False);
9119
9120 /* Put input focus on the cancel button */
9121 XmProcessTraversal(
9122 widgets.dlist.cancel_btn,
9123 XmTRAVERSE_CURRENT
9124 );
9125
9126 /* Change to watch cursor */
9127 cd_busycurs(TRUE, CURS_ALL);
9128
9129 if (app_data.numdiscs >= 3) {
9130 /* Pop up the working dialog */
9131 cd_working_popup(
9132 app_data.str_working,
9133 app_data.str_chgrscan,
9134 (XtCallbackProc) dbprog_scan_stop_btn,
9135 (XtPointer) s
9136 );
9137 }
9138
9139 /* Set multiplay mode so we don't block on an empty slot.
9140 * Also force reverse mode to be false for scanning.
9141 */
9142 sav_mplay = app_data.multi_play;
9143 sav_rev = app_data.reverse;
9144 app_data.multi_play = TRUE;
9145 app_data.reverse = FALSE;
9146
9147 /* Start scanning */
9148 start_slot = s->cur_disc;
9149 scan_slot = 0;
9150 s->chgrscan = TRUE;
9151 dbprog_chgr_scan_next(s);
9152 }
9153 }
9154
9155
9156 /*
9157 * dbprog_scan_stop_btn
9158 * Rescan working dialog box stop button callback function
9159 */
9160 /*ARGSUSED*/
9161 void
dbprog_scan_stop_btn(Widget w,XtPointer client_data,XtPointer call_data)9162 dbprog_scan_stop_btn(Widget w, XtPointer client_data, XtPointer call_data)
9163 {
9164 curstat_t *s = (curstat_t *)(void *) client_data;
9165
9166 dbprog_chgr_scan_stop(s);
9167 }
9168
9169
9170 /**************** ^^ Callback routines ^^ ****************/
9171
9172
9173