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