1 /*
2  * Tlf - contest logging program for amateur radio operators
3  * Copyright (C) 2001-2005 Rein Couperus <pa0r@eudxf.org>
4  *               2009-2016 Thomas Beierlein <tb@forth-ev.de>
5  *               2013      Ervin Hegedüs - HA2OS <airween@gmail.com>
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 
22 /* ------------------------------------------------------------
23  *        Callinput handles the call input field
24  *
25  *--------------------------------------------------------------*/
26 
27 
28 #include <ctype.h>
29 #include <fcntl.h>
30 #include <math.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 
35 #include "addspot.h"
36 #include "autocq.h"
37 #include "bandmap.h"
38 #include "calledit.h"
39 #include "callinput.h"
40 #include "changefreq.h"
41 #include "changepars.h"
42 #include "clear_display.h"
43 #include "cleanup.h"
44 #include "cw_utils.h"
45 #include "edit_last.h"
46 #include "err_utils.h"
47 #include "deleteqso.h"
48 #include "getctydata.h"
49 #include "gettxinfo.h"
50 #include "grabspot.h"
51 #include "ignore_unused.h"
52 #include "keystroke_names.h"
53 #include "lancode.h"
54 #include "muf.h"
55 #include "netkeyer.h"
56 #include "nicebox.h"		// Includes curses.h
57 #include "note.h"
58 #include "prevqso.h"
59 #include "printcall.h"
60 #include "qtcvars.h"		// Includes globalvars.h
61 #include "qtcwin.h"
62 #include "rtty.h"
63 #include "searchlog.h"		// Includes glib.h
64 #include "sendbuf.h"
65 #include "sendspcall.h"
66 #include "show_help.h"
67 #include "showinfo.h"
68 #include "showpxmap.h"
69 #include "speedupndown.h"
70 #include "splitscreen.h"
71 #include "stoptx.h"
72 #include "time_update.h"
73 #include "trx_memory.h"
74 #include "ui_utils.h"
75 #include "writeparas.h"
76 #include "showzones.h"
77 #include "bands.h"
78 #include "fldigixmlrpc.h"
79 
80 #define TUNE_UP 6	/* tune up for 6 s (no more than 10) */
81 
82 typedef enum { STORE_OR_POP, POP, SWAP } memory_op_t;
83 
84 void send_bandswitch(freq_t freq);
85 int autosend(void);
86 int plain_number(char *str);
87 void handle_bandswitch(int direction);
88 void handle_memory_operation(memory_op_t op);
89 
90 extern int no_arrows;
91 extern char hiscall[];
92 extern int bandinx;
93 extern char band[NBANDS][4];
94 extern int contest;
95 extern int dxped;
96 extern freq_t freq;
97 extern int trx_control;
98 extern freq_t bandfrequency[];
99 
100 
101 /** callsign input loop
102  *
103  * \return code of last typed character */
callinput(void)104 int callinput(void) {
105 
106     extern int itumult;
107     extern int wazmult;
108     extern int isdupe;		// LZ3NY auto-b4 patch
109     extern int cwstart;
110     extern int early_started;
111     extern char hiscall_sent[];
112     extern char comment[];
113     extern cqmode_t cqmode;
114     extern int trxmode;
115     extern char lastcall[];
116     extern int cqdelay;
117     extern char his_rst[];
118     extern int cluster;
119     extern int announcefilter;
120     extern char ph_message[14][80];
121     extern SCREEN *mainscreen;
122     extern int simulator;
123     extern int simulator_mode;
124     extern char talkarray[5][62];
125     extern int lan_active;
126     extern int zonedisplay;
127     extern int showscore_flag;
128     extern int searchflg;
129     extern int cqww;
130     extern char cqzone[];
131     extern char ituzone[];
132     extern int ctcomp;
133     extern int nob4;
134     extern int change_rst;
135     extern int weight;
136     extern int k_pin14;
137     extern int k_ptt;
138     extern int noautocq;
139     extern int miniterm;
140     extern int no_rst;
141 
142     extern int bmautoadd;
143     extern int bmautograb;
144     extern int digikeyer;
145 
146     static freq_t freqstore;		/* qrg during last callsign input
147 					   character, 0 if grabbed,
148 					   used to decide if a call in input
149 					   field was entered by hand or
150 					   grabbed from spot list */
151 
152     enum grabstate_t { NONE, IN_PROGRESS, REACHED };
153 
154     struct grab_t {
155 	enum grabstate_t state;
156 	freq_t spotfreq;
157 	char call[15];
158     };
159 
160     static struct grab_t grab =  { .state = NONE };
161 
162 
163     int cury, curx;
164     int j, t, x = 0;
165     char instring[2] = { '\0', '\0' };
166     static int lastwindow;
167 
168 
169     attron(modify_attr(COLOR_PAIR(NORMCOLOR)));
170 
171     printcall();	/* print call input field */
172     searchlog();
173 
174     while (strlen(hiscall) <= 13) {
175 
176 	show_zones(bandinx);
177 	printcall();
178 
179 	/* wait for next char pressed, but update time, cluster and TRX qrg */
180 	/* main loop waiting for input */
181 	x = -1;
182 	while (x < 1) {
183 
184 	    usleep(10000);
185 
186 	    time_update();
187 
188 	    if (trxmode == DIGIMODE) {
189 		show_rtty();
190 	    }
191 
192 	    if (digikeyer == FLDIGI && fldigi_set_callfield == 1 && hiscall[0] != '\0') {
193 		freqstore = freq;
194 		fldigi_set_callfield = 0;
195 	    }
196 
197 	    /* if BMAUTOADD is active and user has input a call sign
198 	     * (indicated by non-zero freqstore) check if he turns away
199 	     * from frequency and if so add call to spot list */
200 	    if (bmautoadd != 0 && freqstore != 0) {
201 		if (strlen(hiscall) >= 3) {
202 		    if (fabs(freq - freqstore) > 500) {
203 			add_to_spots(hiscall, freqstore);
204 			hiscall[0] = '\0';
205 			HideSearchPanel();
206 			freqstore = 0;
207 		    }
208 		}
209 	    }
210 
211 	    /* if BMAUTOGRAB is active in S&P mode and input field is empty and a spot has
212 	     * not already been grabbed here check if a spot is on freq
213 	     * and pick it up if one found */
214 	    if (bmautograb && cqmode == S_P && *hiscall == '\0' && grab.state == NONE) {
215 		get_spot_on_qrg(grab.call, freq);
216 		if (strlen(grab.call) >= 3) {
217 		    strncpy(hiscall, grab.call, sizeof(hiscall));
218 		    grab.state = REACHED;
219 		    grab.spotfreq = freq;
220 
221 		    showinfo(getctydata_pfx(hiscall));
222 		    printcall();
223 		    searchlog();
224 		    freqstore = 0;
225 		}
226 	    }
227 
228 	    /* wait till freq of grabbed spot is reported back from rig.
229 	     * Then go to 'reached' state' */
230 	    if (grab.state == IN_PROGRESS && fabs(freq - grab.spotfreq) <= 100)
231 		grab.state = REACHED;
232 
233 	    /* Check if we tune away from old freq before a grabbed spot is
234 	     * reached. If so stop grab process */
235 
236 	    /* if we have grabbed a call from spot list and tune away
237 	     * then forget about it */
238 	    if (fabs(freq - grab.spotfreq) > 500 && grab.state == REACHED) {
239 		grab.state = NONE;
240 		hiscall[0] = '\0';
241 		printcall();
242 		HideSearchPanel();
243 		showinfo(SHOWINFO_DUMMY);
244 	    }
245 
246 
247 	    /* make sure that the wrefresh() inside getch() shows the cursor
248 	     * in the input field */
249 	    wmove(stdscr, 12, 29 + strlen(hiscall));
250 	    x = key_poll();
251 
252 	}
253 
254 	/* special handling of some keycodes if call field is empty */
255 	if (*hiscall == '\0') {
256 	    // <Enter>, sends CQ message (F1), starts autoCQ, or sends S&P message.
257 	    if ((x == '\n' || x == KEY_ENTER) && *hiscall == '\0') {
258 		if (cqmode == CQ) {
259 		    if (noautocq != 1)
260 			x = auto_cq();
261 		} else {
262 		    sendspcall();
263 		    break;
264 		}
265 	    }
266 
267 	    // Up Arrow or Alt-e, edit last QSO
268 	    if (x == KEY_UP || x == ALT_E) {
269 		edit_last();
270 		break;
271 	    }
272 
273 	    // Equals, confirms last callsign already logged if call field is empty.
274 	    if (x == '=' && *hiscall == '\0') {
275 		char *str = g_strdup_printf("%s TU ", lastcall);
276 		sendmessage(str);
277 		g_free(str);
278 		break;
279 	    }
280 	}
281 
282 	// Shift-F1: switch back (swap) to CQ frequency if it's in memory
283 	//           and start a CQ
284 	if (x == SHIFT_F(1)) {
285 	    if (cqmode == S_P && memory_get_cqmode() == CQ) {
286 		handle_memory_operation(SWAP);
287 	    }
288 
289 	    x = KEY_F(1);   // continue as F1
290 	}
291 
292 	switch (x) {
293 
294 	    // Plus (+)
295 	    // - in non-CT mode switch to other mode (CQ <-> S&P)
296 	    // - in CT mode send TU and log QSO.
297 	    case '+': {
298 		if (!ctcomp) {
299 
300 		    /* switch to other mode */
301 		    if (cqmode == CQ) {
302 			cqmode = S_P;
303 		    } else
304 			cqmode = CQ;
305 
306 		    /* and show new mode */
307 		    show_header_line();
308 
309 		} else {
310 
311 		    if (strlen(hiscall) > 2) {
312 			if (((cqww == 1) || (wazmult == 1))
313 				&& (*comment == '\0'))
314 			    strcpy(comment, cqzone);
315 
316 			if ((itumult == 1) && (*comment == '\0'))
317 			    strcpy(comment, ituzone);
318 
319 			if (*comment == '\0') {
320 			    x = -1;
321 			} else {
322 			    /* F4 (TU macro) */
323 			    send_standard_message(3);
324 
325 			    x = '\\';   // key for logging QSO without message
326 			}
327 		    }
328 		}
329 		break;
330 	    }
331 
332 	    // Ctrl-Q (^Q), open QTC window for receiving or sending QTCs.
333 	    case CTRL_Q: {
334 		if (qtcdirection == 1 || qtcdirection == 3) {	// in case of QTC=RECV or QTC=BOTH
335 		    qtc_main_panel(RECV);
336 		}
337 		if (qtcdirection == 2) {			// in case of QTC=SEND
338 		    qtc_main_panel(SEND);
339 		}
340 		x = KEY_LEFT;
341 		continue;
342 	    }
343 
344 	    // Ctrl-S (^S), open QTC window for sending QTCs.
345 	    case CTRL_S: {
346 		if (qtcdirection == 2 || qtcdirection == 3) {	// in case of QTC=SEND ot QTC=BOTH
347 		    qtc_main_panel(SEND);
348 		}
349 		x = KEY_LEFT;
350 		continue;
351 	    }
352 
353 	    // <Home>, enter call edit when call field is not empty.
354 	    case KEY_HOME: {
355 		if ((*hiscall != '\0') && (ungetch(x) == OK)) {
356 		    calledit();
357 		}
358 
359 		break;
360 	    }
361 
362 	    // Left Arrow, enter call edit when call field is not empty, or band down.
363 	    case KEY_LEFT: {
364 		if (*hiscall != '\0') {
365 		    calledit();
366 		} else {
367 		    handle_bandswitch(BAND_DOWN);
368 		}
369 
370 		break;
371 	    }
372 
373 	    // Right Arrow, band up when call field is empty.
374 	    case KEY_RIGHT: {
375 		handle_bandswitch(BAND_UP);
376 		break;
377 	    }
378 
379 	    // Alt-w (M-w), set Morse weight.
380 	    case ALT_W: {
381 		char weightbuf[5] = "";
382 		char *end;
383 
384 		mvprintw(12, 29, "Wght: -50..50");
385 
386 		nicebox(1, 1, 2, 12, "Cw");
387 		attron(COLOR_PAIR(C_LOG) | A_STANDOUT);
388 		mvprintw(2, 2, "Speed:   %2u ", GetCWSpeed());
389 		mvprintw(3, 2, "Weight: %3d ", weight);
390 		refreshp();
391 
392 		usleep(800000);
393 		mvprintw(3, 10, "   ");
394 
395 		echo();
396 		mvgetnstr(3, 10, weightbuf, 3);
397 		noecho();
398 
399 		g_strchomp(weightbuf);
400 
401 		int tmp = strtol(weightbuf, &end, 10);
402 
403 		if ((weightbuf[0] != '\0') && (*end == '\0')) {
404 		    /* successful conversion */
405 
406 		    if (tmp > -51 && tmp < 51) {
407 			weight = tmp;
408 			netkeyer(K_WEIGHT, weightbuf);
409 		    }
410 		}
411 		clear_display();
412 		printcall();
413 
414 		break;
415 	    }
416 
417 	    // Alt-v (M-v), change Morse speed in CW mode, else band down.
418 	    case ALT_V: {
419 		if (ctcomp == 1) {
420 		    while (x != ESCAPE) {
421 			nicebox(1, 1, 2, 12, "Cw");
422 			attron(COLOR_PAIR(C_LOG) | A_STANDOUT);
423 			mvprintw(2, 2, "Speed:   %2u ", GetCWSpeed());
424 			mvprintw(3, 2, "Weight: %3d ", weight);
425 			printcall();
426 			refreshp();
427 
428 			x = key_get();
429 			if (x == KEY_UP) {
430 			    speedup();
431 			    attron(COLOR_PAIR(C_HEADER) | A_STANDOUT);
432 
433 			    mvprintw(0, 14, "%2u", GetCWSpeed());
434 
435 			} else if (x == KEY_DOWN) {
436 			    speeddown();
437 			    attron(COLOR_PAIR(C_HEADER) | A_STANDOUT);
438 			    mvprintw(0, 14, "%2u", GetCWSpeed());
439 
440 			} else
441 			    x = ESCAPE;
442 
443 			clear_display();
444 		    }
445 		} else {	// trlog compatible, band switch
446 		    handle_bandswitch(BAND_DOWN);
447 		}
448 		x = -1;
449 
450 		break;
451 	    }
452 
453 	    // <Page-Up>, change RST if call field not empty, else increase CW speed.
454 	    case KEY_PPAGE: {
455 		if ((change_rst == 1) && (strlen(hiscall) != 0)) {	// change RST
456 
457 		    if (his_rst[1] <= 56) {
458 
459 			his_rst[1]++;
460 
461 			if (!no_rst)
462 			    mvprintw(12, 44, his_rst);
463 			mvprintw(12, 29, hiscall);
464 		    }
465 
466 		} else {	// change cw speed
467 		    speedup();
468 
469 		    attron(COLOR_PAIR(C_HEADER) | A_STANDOUT);
470 		    mvprintw(0, 14, "%2u", GetCWSpeed());
471 		}
472 
473 		break;
474 	    }
475 
476 
477 	    // <Page-Down>, change RST if call field not empty, else decrease CW speed.
478 	    case KEY_NPAGE: {
479 		if ((change_rst == 1) && (strlen(hiscall) != 0)) {
480 
481 		    if (his_rst[1] > 49) {
482 			his_rst[1]--;
483 
484 			if (!no_rst)
485 			    mvprintw(12, 44, his_rst);
486 			mvprintw(12, 29, hiscall);
487 		    }
488 
489 		} else {
490 
491 		    speeddown();
492 
493 		    attron(COLOR_PAIR(C_HEADER) | A_STANDOUT);
494 		    mvprintw(0, 14, "%2u", GetCWSpeed());
495 		}
496 		break;
497 	    }
498 
499 	    // <Enter>, log QSO in CT mode, else test if B4 message should be sent.
500 	    case '\n':
501 	    case KEY_ENTER: {
502 		if (strlen(hiscall) > 2 && ctcomp == 1) {
503 		    /* There seems to be a call, log it in CT mode but only if
504 		     * the exchange field is not empty.
505 		     */
506 		    if (comment[0] == '\0') {
507 			x = -1;
508 			break;
509 		    } else {
510 			/* Log without sending message. */
511 			x = BACKSLASH;
512 			break;
513 		    }
514 		}
515 
516 		if (strlen(hiscall) < 3 || nob4 == 1)
517 		    break;
518 
519 		/* check b4 QSO if call is long enough and 'nob4' off */
520 		isdupe = 0;	// LZ3NY  auto-b4 patch
521 
522 		searchlog();
523 
524 		if (isdupe != 0) {
525 		    // XXX: Before digi_message, SSB mode sent CW here. - W8BSD
526 		    send_standard_message(6);	/* as with F7 */
527 		    cleanup();
528 		    clear_display();
529 		}
530 		break;
531 	    }
532 
533 	    /* <Insert>, send exchange in CT mode */
534 	    case KEY_IC: {
535 		if (ctcomp != 0) {
536 		    /* F3 (RST macro) */
537 		    send_standard_message(2);
538 		    /* Set to space to move cursor to exchange field
539 		     * which will trigger autofill if available.
540 		     */
541 		    x = ' ';
542 		}
543 		break;
544 	    }
545 
546 	    // Colon, prefix for entering commands or changing parameters.
547 	    case ':': {
548 		changepars();
549 		hiscall[0] = '\0';
550 		x = 0;
551 		clear_display();
552 		attron(COLOR_PAIR(C_LOG) | A_STANDOUT);
553 
554 		for (j = 13; j <= 23; j++) {
555 		    mvprintw(j, 0, backgrnd_str);
556 		}
557 
558 		attron(modify_attr(COLOR_PAIR(NORMCOLOR)));
559 
560 		mvprintw(12, 29, "            ");
561 		mvprintw(12, 29, "");
562 		refreshp();
563 		break;
564 	    }
565 
566 	    // Hash, save xcvr freq to mem or restore mem to xcvr.
567 	    case '#': {
568 		handle_memory_operation(STORE_OR_POP);
569 		break;
570 	    }
571 	    // Dollar, pop memory (MEM -> TRX)
572 	    case '$': {
573 		handle_memory_operation(POP);
574 		break;
575 	    }
576 	    // Percent, swap memory (MEM <-> TRX)
577 	    case '%': {
578 		handle_memory_operation(SWAP);
579 		break;
580 	    }
581 
582 	    // Minus, delete previous QSO from log.
583 	    case '-': {
584 		delete_qso();
585 		break;
586 	    }
587 
588 	    // Semicolon or Alt-n (M-n), insert note in log.
589 	    case ';':
590 	    case ALT_N: {
591 		include_note();
592 		x = -1;
593 		break;
594 	    }
595 
596 	    // Alt-0 to Alt-9 (M-0...M-9), send CW/Digimode messages 15-24.
597 	    case 176 ... 185: {
598 		send_standard_message(x - 162);	/* alt-0 to alt-9 */
599 
600 		break;
601 	    }
602 
603 	    // F1, send CQ or S&P call message.
604 	    case KEY_F(1): {
605 		if (trxmode == CWMODE || trxmode == DIGIMODE) {
606 
607 		    if (cqmode == S_P) {
608 			sendspcall();
609 		    } else {
610 			send_standard_message(0);	/* CQ */
611 		    }
612 
613 
614 		    if (simulator != 0) {
615 			simulator_mode = 1;
616 		    }
617 		} else {
618 
619 		    if (cqmode == S_P)
620 			play_file(ph_message[5]);	/* S&P */
621 		    else
622 			play_file(ph_message[0]);
623 		}
624 		break;
625 	    }
626 
627 	    // F2-F11, send messages 2 through 11.
628 	    case KEY_F(2) ... KEY_F(11): {
629 		send_standard_message(x - KEY_F(1));	// F2...F11 - F1 = 1...10
630 
631 		break;
632 	    }
633 
634 	    // F12, activate autocq timing and message.
635 	    case KEY_F(12): {
636 		x = auto_cq();
637 		break;
638 	    }
639 
640 	    // Query, send call with " ?" appended or F5 message in voice mode.
641 	    case '?': {
642 		if (*hiscall != '\0') {
643 		    strcat(hiscall, " ?");
644 		    send_standard_message(4);
645 		    hiscall[strlen(hiscall) - 2] = '\0';
646 		}
647 		x = -1;
648 		break;
649 	    }
650 
651 	    // <Backspace>, remove chracter left of cursor, move cursor left one position.
652 	    case KEY_BACKSPACE: {
653 		if (*hiscall != '\0') {
654 		    getyx(stdscr, cury, curx);
655 		    mvprintw(cury, curx - 1, " ");
656 		    mvprintw(cury, curx - 1, "");
657 		    hiscall[strlen(hiscall) - 1] = '\0';
658 
659 		    if (atoi(hiscall) < 1800) {	/*  no frequency */
660 			showinfo(getctydata_pfx(hiscall));
661 			searchlog();
662 			refreshp();
663 		    }
664 
665 		    x = -1;
666 		    break;
667 		}
668 		break;
669 	    }
670 
671 	    // Alt-r (M-r) or Alt-s (M-s), toggle score window.
672 	    case ALT_R:
673 	    case ALT_S: {
674 		if (showscore_flag == 0)
675 		    showscore_flag = 1;
676 		else {
677 		    showscore_flag = 0;
678 		}
679 		clear_display();
680 		break;
681 	    }
682 
683 	    // Alt-k (M-k), synonym for Ctrl-K (^K).
684 	    case ALT_K: {
685 		x = CTRL_K;		// Ctrl-K
686 		break;
687 	    }
688 
689 	    // Alt-a (M-a), cycle cluster window.
690 	    case ALT_A: {
691 		if (cluster == NOCLUSTER) {
692 		    cluster = CLUSTER;	// alt-A
693 		    announcefilter = FILTER_ALL;
694 		} else if (cluster == CLUSTER) {
695 		    cluster = MAP;
696 		} else if (cluster == MAP) {
697 		    cluster = NOCLUSTER;
698 		}
699 
700 		break;
701 	    }
702 
703 	    // Alt-b (M-b), band-up for TR-Log mode.
704 	    case ALT_B: {
705 		if (ctcomp == 0) {
706 		    handle_bandswitch(BAND_UP);
707 		}
708 		break;
709 	    }
710 
711 	    // Alt-j (M-j), show station frequencies.
712 	    case ALT_J: {
713 		if (cluster != FREQWINDOW) {
714 		    lastwindow = cluster;
715 		    cluster = FREQWINDOW;
716 		} else
717 		    cluster = lastwindow;
718 
719 		break;
720 	    }
721 
722 	    // Alt-h (M-h), show help.
723 	    case ALT_H: {
724 		show_help();
725 		break;
726 	    }
727 
728 	    // Alt-, (M-,) or period, change bandmap filter config.
729 	    case 172:
730 	    case '.': {
731 		bm_menu();
732 		break;
733 	    }
734 
735 	    // Alt-c (M-c), toggle check window.
736 	    case ALT_C: {
737 		if (searchflg != SEARCHWINDOW)
738 		    searchflg = SEARCHWINDOW;
739 		else
740 		    searchflg = 0;
741 		break;
742 	    }
743 
744 	    // Alt-m (M-m), show multipliers.
745 	    case ALT_M: {
746 		show_mults();
747 		refreshp();
748 		break;
749 	    }
750 
751 	    // Alt-p (M-p), toggle PTT via cwdaemon
752 	    case ALT_P: {
753 		if (k_ptt == 0) {
754 		    k_ptt = 1;
755 		    attron(COLOR_PAIR(C_HEADER) | A_STANDOUT);
756 		    mvprintw(0, 2, "PTT on   ");
757 		    mvprintw(12, 29, "");
758 		    refreshp();
759 		    netkeyer(K_PTT, "1");	// ptt on
760 		    x = key_get();	// any character to stop tuning
761 		    if (x == ALT_P)	// Alt-P (M-p)
762 			netkeyer(K_PTT, "0");	// ptt off
763 		    k_ptt = 0;
764 		    show_header_line();
765 		    refreshp();
766 		} else
767 		    netkeyer(K_PTT, "0");	// ptt off in any case.
768 
769 		break;
770 	    }
771 
772 	    // Alt-t (M-t), tune xcvr via cwdaemon.
773 	    case ALT_T: {
774 		int count;
775 		gchar *buff;
776 
777 		attron(COLOR_PAIR(C_HEADER) | A_STANDOUT);
778 		mvprintw(0, 2, "Tune     ");
779 		mvprintw(12, 29, "");
780 		refreshp();
781 
782 		buff = g_strdup_printf("%d", TUNE_UP);
783 		netkeyer(K_TUNE, buff);	// cw on
784 		g_free(buff);
785 
786 		count = (int)(TUNE_UP / 0.25);
787 
788 		while (count != 0) {
789 		    usleep(250000);
790 		    if (key_poll() != -1)	// any key pressed ?
791 			break;
792 		    count--;
793 		}
794 
795 		netkeyer(K_ABORT, "");	// cw abort
796 
797 		show_header_line();
798 		refreshp();
799 
800 		break;
801 	    }
802 
803 	    // Alt-z (M-z), show zones worked.
804 	    case ALT_Z: {
805 		if (cqww == 1) {
806 		    if (zonedisplay == 0)
807 			zonedisplay = 1;
808 		    else {
809 			zonedisplay = 0;
810 			clear_display();
811 		    }
812 		} else {
813 		    multiplierinfo();
814 		}
815 
816 		break;
817 	    }
818 
819 	    // Alt-q (M-q) or Alt-x (M-x), Exit
820 	    case ALT_Q:
821 	    case ALT_X: {
822 		mvprintw(13, 29, "You want to leave tlf? (y/n): ");
823 		while (x != 'n' && x != 'N') {
824 
825 		    x = key_get();
826 
827 		    if (x == 'y' || x == 'Y') {
828 			writeparas();
829 			cleanup_telnet();
830 			endwin();
831 
832 			puts("\n\nThanks for using TLF.. 73\n");
833 			exit(0);
834 		    }
835 		}
836 		x = ESCAPE;
837 		break;
838 	    }
839 
840 	    // <Escape>, clear call input or stop sending.
841 	    case ESCAPE: {
842 		if (early_started == 0) {
843 		    /* if CW not started early drop call and start anew */
844 		    cleanup();
845 		    clear_display();
846 		} else {
847 		    /* otherwise just stop sending */
848 		    stoptx();
849 		    *hiscall_sent = '\0';
850 		    early_started = 0;
851 		}
852 
853 		freqstore = 0;
854 		break;
855 	    }
856 
857 	    // Underscore, confirm last exchange.
858 	    case '_': {
859 		prev_qso();
860 
861 		break;
862 	    }
863 
864 	    // Exclamation, open a new shell.
865 	    case '!': {
866 		const char *shell = getenv("SHELL");
867 		if (shell == NULL) {
868 		    shell = "sh";
869 		}
870 		endwin();
871 		IGNORE(system("clear"));;
872 		IGNORE(system(shell));;
873 		IGNORE(system("clear"));;
874 		set_term(mainscreen);
875 		clear_display();
876 
877 		break;
878 	    }
879 
880 	    // Ctrl-L (^L), resets screen.
881 	    case CTRL_L: {
882 		endwin();
883 		set_term(mainscreen);
884 		clear_display();
885 
886 		break;
887 	    }
888 
889 	    // Ctrl-P (^P), show MUF display.
890 	    case CTRL_P: {
891 		int currentterm = miniterm;
892 		miniterm = 0;
893 		muf();
894 		miniterm = currentterm;
895 		clear_display();
896 
897 		break;
898 	    }
899 
900 	    // Ctrl-A (^A), add a spot and share on LAN.
901 	    case CTRL_A: {
902 		addspot();
903 		HideSearchPanel();
904 		showinfo(SHOWINFO_DUMMY);
905 
906 		grab.state = REACHED;
907 		grab.spotfreq = freq;
908 		break;
909 	    }
910 
911 	    // Ctrl-B (^B), send spot to DX cluster.
912 	    case CTRL_B: {
913 		announcefilter = 0;
914 		cluster = CLUSTER;
915 		send_cluster();
916 
917 		break;
918 	    }
919 
920 	    // Ctrl-F (^F), change frequency dialog.
921 	    case CTRL_F: {
922 		change_freq();
923 
924 		break;
925 	    }
926 
927 	    // Ctrl-G (^G), grab next DX spot from bandmap.
928 	    case CTRL_G: {
929 		freq_t f = grab_next();
930 		if (f > 0.0) {
931 		    grab.state = IN_PROGRESS;
932 		    grab.spotfreq = f;
933 		    show_header_line();
934 		    freqstore = 0;
935 		}
936 
937 		break;
938 	    }
939 
940 	    // Alt-g (M-g), grab first spot matching call field chars.
941 	    case ALT_G: {
942 		double f = grabspot();
943 		if (f > 0.0) {
944 		    grab.state = IN_PROGRESS;
945 		    grab.spotfreq = f;
946 		    show_header_line();
947 		    freqstore = 0;
948 		}
949 
950 		break;
951 	    }
952 
953 	    // Double quote, send talk message to other nodes.
954 	    case '\"': {
955 		if (lan_active != 0)
956 		    talk();
957 
958 		break;
959 	    }
960 
961 	    // Ctrl-R (^R), toogle trx1, trx2 via lp0 pin 14.
962 	    case CTRL_R: {
963 		if (k_pin14 == 0) {
964 		    k_pin14 = 1;
965 		    netkeyer(K_SET14, "1");
966 		} else {
967 		    k_pin14 = 0;
968 		    netkeyer(K_SET14, "0");
969 		}
970 		break;
971 	    }
972 
973 	    // Ctrl-T (^T) or Alt-i (M-i), show talk messages.
974 	    case CTRL_T:
975 	    case ALT_I: {
976 		if (lan_active != 0) {
977 
978 		    for (t = 0; t <= 5; t++)
979 			mvprintw(14 + t, 1,
980 				 "                                                            ");
981 		    for (t = 0; t <= 4; t++)
982 			mvprintw(15 + t, 1, talkarray[t]);
983 		    nicebox(14, 0, 5, 59, "Messages");
984 
985 		    refreshp();
986 		    key_get();
987 		    attron(COLOR_PAIR(C_LOG) | A_STANDOUT);
988 		    for (t = 0; t <= 6; t++)
989 			mvprintw(14 + t, 0,
990 				 "                                                             ");
991 
992 		    clear_display();
993 		}
994 		break;
995 	    }
996 
997 	}	/* end switch */
998 
999 
1000 	// Ctrl-<Page-Up>, increase cqdelay by 1/2 second.
1001 	// Alt-<Page-Up>, same for terminals that consume Ctrl-<Page-Up>.
1002 	if ((key_kPRV3 && x == key_kPRV3)
1003 		|| (key_kPRV5 && x == key_kPRV5)) {
1004 
1005 	    if (cqdelay <= 60) {
1006 		cqdelay++;
1007 
1008 		attron(COLOR_PAIR(C_HEADER) | A_STANDOUT);
1009 		mvprintw(0, 19, "  ");
1010 		mvprintw(0, 19, "%i", cqdelay);
1011 	    }
1012 
1013 	    break;
1014 	}
1015 
1016 
1017 	// Ctrl-<Page-Down>, decrease cqdelay by 1/2 Second.
1018 	// Alt-<Page-Down>, same for terminals that consume Ctrl-<Page-Down>.
1019 	if ((key_kNXT3 && x == key_kNXT3)
1020 		|| (key_kNXT5 && x == key_kNXT5)) {
1021 
1022 	    if (cqdelay >= 4) {
1023 		cqdelay--;
1024 
1025 		attron(COLOR_PAIR(C_HEADER) | A_STANDOUT);
1026 		mvprintw(0, 19, "  ");
1027 		mvprintw(0, 19, "%i", cqdelay);
1028 	    }
1029 
1030 	    break;
1031 	}
1032 
1033 
1034 	/* Convert to upper case */
1035 	if (x >= 'a' && x <= 'z')
1036 	    x = x - 32;
1037 
1038 	/* Add character to call input field. */
1039 	if (x >= '/' && x <= 'Z') {
1040 
1041 	    if (strlen(hiscall) < 13) {
1042 		instring[0] = x;
1043 		instring[1] = '\0';
1044 		addch(x);
1045 		strcat(hiscall, instring);
1046 		if (cqmode == CQ && cwstart > 0 &&
1047 			trxmode == CWMODE && contest == 1) {
1048 		    /* early start keying after 'cwstart' characters but only
1049 		     * if input field contains at least one nondigit */
1050 		    if (strlen(hiscall) == cwstart && !plain_number(hiscall)) {
1051 			x = autosend();
1052 		    }
1053 		}
1054 	    }
1055 
1056 	    if (atoi(hiscall) < 1800) {	/*  no frequency */
1057 
1058 		showinfo(getctydata_pfx(hiscall));
1059 		searchlog();
1060 	    }
1061 
1062 	    refreshp();
1063 
1064 	    freqstore = freq;
1065 
1066 	}
1067 
1068 	if (cqmode == CQ && cwstart < 0 && trxmode == CWMODE &&
1069 		contest == 1) {
1070 	    if (x == '\n' || x == KEY_ENTER) {
1071 		/* early start keying after 'Enter' but only if input field
1072 		 * contains at least two chars, one or more of it nondigit */
1073 		if (strlen(hiscall) >= 2 && !plain_number(hiscall)) {
1074 		    x = autosend();
1075 		}
1076 	    }
1077 	}
1078 
1079 	if ((x == '\n' || x == KEY_ENTER) || x == SPACE || x == TAB
1080 		|| x == CTRL_K || x == 44 || x == BACKSLASH) {
1081 	    break;
1082 	}
1083 
1084     }
1085 
1086     return (x);
1087 }
1088 
1089 /** check if string is plain number
1090  *
1091  * Check if string contains only digits
1092  * \param str    the string to check
1093  * \return true  if only digits inside
1094  *         false at least one none digit
1095  */
plain_number(char * str)1096 int plain_number(char *str) {
1097     int i;
1098 
1099     for (i = 0; i < strlen(str); i++) {
1100 	if (!isdigit(str[i])) {
1101 	    return false;
1102 	}
1103     }
1104 
1105     return true;
1106 }
1107 
1108 
1109 /** autosend function
1110  *
1111  * autosend allow an operator in RUN mode to just enter the call of the
1112  * other station. TLF will start sending the call and switch automatically
1113  * to sending the exchange when typing stops.
1114  *  - starts after 2..5 characters
1115  *  - shorter calls have to be finished with ENTER key
1116  *  - as soon as autosend starts only alfanumerical keys are accepted
1117  *  - no edit after input possible
1118  *  - calculates expected time to send call from cw speed and
1119  *  - switches to sending exchange after that time is reached
1120  *
1121  *  \return last typed key, ESC or \n
1122  *          ESC - transmission has stopped
1123  *          \n  - timeout or CR pressed -> send exchange
1124  */
autosend()1125 int autosend() {
1126 
1127     extern int early_started;
1128     extern int sending_call;
1129     extern char hiscall_sent[];
1130     extern char hiscall[];
1131 
1132     GTimer *timer;
1133     double timeout, timeout_sent;
1134     int x;
1135     int char_sent;
1136 
1137     early_started = 1;
1138     sending_call = 1;
1139     sendmessage(hiscall);
1140     sending_call = 0;
1141     strcpy(hiscall_sent, hiscall);
1142 
1143     char_sent = 0; 			/* no char sent so far */
1144     timeout_sent = (1.2 / GetCWSpeed()) * getCWdots(hiscall[char_sent]);
1145 
1146     timer = g_timer_new();
1147     timeout = (1.2 / GetCWSpeed()) * cw_message_length(hiscall);
1148 
1149     x = -1;
1150     while ((x != ESCAPE) && (x != '\n' && x != KEY_ENTER)) {
1151 	x = -1;
1152 	while ((x == -1) && (g_timer_elapsed(timer, NULL) < timeout)) {
1153 
1154 	    highlightCall(char_sent + 1);
1155 
1156 	    usleep(10000);
1157 
1158 	    if (g_timer_elapsed(timer, NULL) > timeout_sent) {
1159 		/* one char sent - display and set new timeout */
1160 		char_sent ++;
1161 		timeout_sent +=
1162 		    (1.2 / GetCWSpeed()) * getCWdots(hiscall[char_sent]);
1163 
1164 	    }
1165 
1166 	    /* make sure that the wrefresh() inside getch() shows the cursor
1167 	     * in the input field */
1168 	    wmove(stdscr, 12, 29 + strlen(hiscall));
1169 	    x = key_poll();
1170 
1171 	}
1172 
1173 	if (x == -1) { 		/* timeout */
1174 	    x = '\n';
1175 	    continue;
1176 	}
1177 
1178 	// <Escape>
1179 	if (x == ESCAPE) {
1180 	    stoptx();
1181 	    *hiscall_sent = '\0';
1182 	    early_started = 0;
1183 	    continue;
1184 	}
1185 
1186 	/* convert to upper case */
1187 	if (x >= 'a' && x <= 'z')
1188 	    x = x - 32;
1189 
1190 	int len = strlen(hiscall);
1191 	if (len < 13 && x >= '/' && x <= 'Z') {
1192 	    char append[2];
1193 
1194 	    /* insert into hiscall */
1195 	    hiscall[len] = x;
1196 	    hiscall[len + 1] = '\0';
1197 
1198 	    /* display it  */
1199 	    printcall();
1200 
1201 	    /* send it to cw */
1202 	    append[0] = x;
1203 	    append[1] = '\0';
1204 	    sendmessage(append);
1205 
1206 	    /* add char length to timeout */
1207 	    timeout += (1.2 / GetCWSpeed()) * getCWdots((char) x);
1208 
1209 	    len = strlen(hiscall_sent);
1210 	    hiscall_sent[len] = x;
1211 	    hiscall_sent[len + 1] = '\0';
1212 	}
1213     }
1214 
1215     g_timer_destroy(timer);
1216     return x;
1217 }
1218 
1219 
play_file(char * audiofile)1220 int play_file(char *audiofile) {
1221 
1222     extern int txdelay;
1223     extern unsigned char rigptt;
1224 
1225     int fd;
1226     char playcommand[120];
1227 
1228     if (*audiofile == '\0')
1229 	return (0);
1230 
1231     if ((fd = open(audiofile, O_RDONLY, 0664)) < 0) {
1232 	TLF_LOG_INFO("cannot open sound file %s!", audiofile);
1233     } else {
1234 	close(fd);
1235 	if (access("./play_vk", X_OK) == 0) {
1236 	    sprintf(playcommand, "./play_vk %s", audiofile);
1237 	} else {
1238 	    sprintf(playcommand, "play_vk %s", audiofile);
1239 	}
1240 	/* CAT PTT wanted and available, use it. */
1241 	if (rigptt == 0x03) {
1242 	    /* Request PTT On */
1243 	    rigptt |= (1 << 3);		/* 0x0b */
1244 	} else {		/* Fall back to netkeyer interface */
1245 	    netkeyer(K_PTT, "1");	// ptt on
1246 	}
1247 
1248 	usleep(txdelay * 1000);
1249 	IGNORE(system(playcommand));;
1250 	printcall();
1251 
1252 	/* CAT PTT wanted, available, and active. */
1253 	if (rigptt == 0x07) {
1254 
1255 	    /* Request PTT Off */
1256 	    rigptt |= (1 << 4);		/* 0x17 */
1257 	} else {		/* Fall back to netkeyer interface */
1258 	    netkeyer(K_PTT, "0");	// ptt off
1259 	}
1260     }
1261 
1262     return 0;
1263 }
1264 
1265 
send_bandswitch(freq_t freq)1266 void send_bandswitch(freq_t freq) {
1267 
1268     extern int use_bandoutput;
1269     extern int bandinx;
1270     extern int bandindexarray[];
1271 
1272     char outnibble[3];
1273     int bandswitch = 0;
1274 
1275     if (!use_bandoutput) {
1276 	return;
1277     }
1278 
1279     const int khz = (int)(freq / 1000.0);
1280     if (khz > 15) {	// looks like a freq...
1281 	switch (khz) {
1282 	    case 1800 ... 2000:
1283 		bandswitch = 1;
1284 		break;
1285 	    case 3500 ... 4000:
1286 		bandswitch = 2;
1287 		break;
1288 	    case 7000 ... 7300:
1289 		bandswitch = 3;
1290 		break;
1291 	    case 10100 ... 10150:
1292 		bandswitch = 4;
1293 		break;
1294 	    case 14000 ... 14350:
1295 		bandswitch = 5;
1296 		break;
1297 	    case 18068 ... 18168:
1298 		bandswitch = 6;
1299 		break;
1300 	    case 21000 ... 21450:
1301 		bandswitch = 7;
1302 		break;
1303 	    case 24890 ... 24990:
1304 		bandswitch = 8;
1305 		break;
1306 	    case 28000 ... 29700:
1307 		bandswitch = 9;
1308 	}
1309     } else			// use the bandinx
1310 	bandswitch = bandinx + 1;
1311 
1312     bandswitch = bandindexarray[bandswitch];
1313 
1314     sprintf(outnibble, "%d", bandswitch);
1315     netkeyer(K_SWITCH, outnibble);
1316 }
1317 
1318 
1319 /** handle bandswitch from keyboard
1320  *
1321  **/
handle_bandswitch(int direction)1322 void handle_bandswitch(int direction) {
1323     // make sure call field is empty and arrows are enabled
1324     if (*hiscall != '\0' || no_arrows) {
1325 	return;
1326     }
1327 
1328     next_band(direction);
1329 
1330     if (contest == 1 && dxped == 0) {
1331 	while (IsWarcIndex(bandinx)) {	/* loop till next contest band */
1332 	    next_band(direction);
1333 	}
1334     }
1335 
1336     attron(COLOR_PAIR(C_WINDOW) | A_STANDOUT);
1337     mvprintw(12, 0, band[bandinx]);
1338 
1339     if (trx_control) {
1340 	freq = bandfrequency[bandinx]; // TODO: is this needed?
1341 	set_outfreq(bandfrequency[bandinx]);
1342     }
1343 
1344     send_bandswitch(bandinx);
1345 }
1346 
1347 /** handle TRX memory operation and update screen
1348  *
1349  **/
handle_memory_operation(memory_op_t op)1350 void handle_memory_operation(memory_op_t op) {
1351     const freq_t newfreq = memory_get_freq();
1352 
1353     switch (op) {
1354 	case STORE_OR_POP:
1355 	    memory_store_or_pop();
1356 	    break;
1357 	case POP:
1358 	    memory_pop();
1359 	    break;
1360 	case SWAP:
1361 	    memory_swap();
1362 	    break;
1363     }
1364 
1365     show_header_line();
1366     showinfo(getctydata_pfx(hiscall));
1367     searchlog();
1368     move(12, 29 + strlen(hiscall));
1369 
1370     if (newfreq <= 0) {
1371 	return;     // freq not changed
1372     }
1373 
1374     // wait until change is reported back from the rig (timeout: 1 sec)
1375     for (int i = 0; i < 20; i++) {
1376 	if (fabs(freq - newfreq) < 100) {
1377 	    break;
1378 	}
1379 	usleep(50000);
1380     }
1381 }
1382