diff --git a/src/Makefile.am b/src/Makefile.am index b8e76e50..8f520c77 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,7 +6,7 @@ bin_PROGRAMS = tlf tlf_SOURCES = \ addcall.c addmult.c addpfx.c addspot.c audio.c autocq.c \ background_process.c bandmap.c bands.c \ - cabrillo_utils.c calledit.c callinput.c changefreq.c changepars.c \ + cabrillo_utils.c callinput.c changefreq.c changepars.c \ change_rst.c checklogfile.c checkqtclogfile.c \ cleanup.c clear_display.c clusterinfo.c \ cqww_simulator.c cw_utils.c \ @@ -43,7 +43,7 @@ tlf_LDADD = @LIBM_LIB@ @PTHREAD_LIBS@ @GLIB_LIBS@ @PANEL_LIBS@ @CURSES_LIBS@ \ noinst_HEADERS = \ addcall.h addmult.h addpfx.h addspot.h audio.h autocq.h \ background_process.h bandmap.h bands.h \ - cabrillo_utils.h calledit.h callinput.h changefreq.h changepars.h \ + cabrillo_utils.h callinput.h changefreq.h changepars.h \ change_rst.h checklogfile.h checkqtclogfile.h \ cleanup.h clear_display.h clusterinfo.h \ cqww_simulator.h cw_utils.h \ diff --git a/src/calledit.c b/src/calledit.c deleted file mode 100644 index f4531189..00000000 --- a/src/calledit.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Tlf - contest logging program for amateur radio operators - * Copyright (C) 2001-2002-2003 Rein Couperus - * 2011 Thomas Beierlein - -#include "callinput.h" -#include "getctydata.h" -#include "globalvars.h" -#include "keystroke_names.h" -#include "searchlog.h" // Includes glib.h -#include "showinfo.h" -#include "tlf.h" -#include "tlf_curses.h" -#include "ui_utils.h" - - -void calledit(void) { - - int i = 0, l, b; - int j = 0; - int x = 0; - int cnt = 0, insertflg = 0; - char call1[30], call2[10]; - - l = strlen(current_qso.call); - b = l - 1; - - - while ((i != ESCAPE) && (b <= strlen(current_qso.call))) { - - attroff(A_STANDOUT); - attron(COLOR_PAIR(C_HEADER)); - - mvaddstr(12, 29, " "); - mvaddstr(12, 29, current_qso.call); - move(12, 29 + b); - /* no refreshp() here as getch() calls wrefresh() for the - * panel with last output (where the cursor should go */ - - i = key_get(); - - // or - if ((i == KEY_DC) || (i == KEY_IC)) - cnt++; - else { - if (i != ESCAPE) - cnt = 0; - } - - // - if (i == TAB) - block_part = 1; - else - block_part = 0; - - // Ctrl-A (^A) or , move to head of callsign field. - if (i == CTRL_A || i == KEY_HOME) { - b = 0; - x = 0; - } - - // Ctrl-E (^E) or , move to end of callsign field, exit edit mode. - if (i == CTRL_E || i == KEY_END) { - break; /* stop edit */ - } - - // Left arrow - if (i == KEY_LEFT) { - - if (b > 0) - b--; - - // Right arrow - } else if (i == KEY_RIGHT) { - if (b < strlen(current_qso.call) - 1) { - b++; - } else - break; /* stop edit */ - - // - } else if (i == KEY_DC) { - - l = strlen(current_qso.call); - - for (j = b; j <= l; j++) { - current_qso.call[j] = current_qso.call[j + 1]; /* move to left incl. \0 */ - } - - update_info_line(); - - if (cnt > 1) - searchlog(); - - // - } else if (i == KEY_BACKSPACE) { - - if (b > 0) { - - b--; - - l = strlen(current_qso.call); - - for (j = b; j <= l; j++) { - current_qso.call[j] = current_qso.call[j + 1]; - } - - update_info_line(); - - if (cnt > 1) - searchlog(); - } - - // - } else if (i == KEY_IC) { - if (insertflg == 0) - insertflg = 1; - else - insertflg = 0; - - // Any character left other than . - } else if (i != ESCAPE) { - - - if (valid_call_char(i)) { - - call2[0] = '\0'; - - // Promote lower case to upper case. - i = g_ascii_toupper(i); - - if (b <= 12) { - strncpy(call1, current_qso.call, b); - strncpy(call2, current_qso.call + b, strlen(current_qso.call) - (b - 1)); - } - - if (strlen(current_qso.call) + 1 == 12) - break; // leave insert mode - - call1[b] = i; - call1[b + 1] = '\0'; - if ((strlen(call1) + strlen(call2)) < 12) { - strcat(call1, call2); - if (strlen(call1) >= 12) - break; - strcpy(current_qso.call, call1); - } - - if ((b < strlen(current_qso.call) - 1) && (b <= 12)) - b++; - else - break; - - update_info_line(); - - searchlog(); - - } else if (x != 0) - i = ESCAPE; - - } else - i = ESCAPE; - - } - - attroff(A_STANDOUT); - attron(COLOR_PAIR(C_HEADER)); - - mvaddstr(12, 29, " "); - mvaddstr(12, 29, current_qso.call); - refreshp(); - - attron(A_STANDOUT); - searchlog(); -} - -int insert_char(int curposition) { - - char call1[30], call2[10]; - int ichr = 0; - - attroff(A_STANDOUT); - attron(COLOR_PAIR(C_HEADER)); - - call1[0] = '\0'; - call2[0] = '\0'; - - while (ichr != ESCAPE) { - - ichr = key_get(); - - // Leave insert mode if , , or are received. - if ((ichr == TAB) || (ichr == '\n') || (ichr == KEY_ENTER) || (ichr == DELETE)) - break; - - // Promote lower case to upper case. - if ((ichr >= 97) && (ichr <= 122)) - ichr = ichr - 32; - - if (curposition <= 10) { - strncpy(call1, current_qso.call, curposition); - } - - if (curposition <= 10) { - strncpy(call2, current_qso.call + curposition, - strlen(current_qso.call) - (curposition - 1)); - } - - // Too long! - if (strlen(current_qso.call) + 1 == MAX_CALL_LENGTH) - break; // leave insert mode - - // Accept A-Z or / and 1-9 - if (((ichr >= 65) && (ichr <= 90)) - || ((ichr >= 47) && (ichr <= 57))) { - call1[curposition] = ichr; - call1[curposition + 1] = '\0'; - if ((strlen(call1) + strlen(call2)) < 12) { - strcat(call1, call2); - if (strlen(call1) + strlen(current_qso.call) >= 12) - break; - strcpy(current_qso.call, call1); - } - } else - break; - - attroff(A_STANDOUT); - attron(COLOR_PAIR(C_HEADER)); - - mvaddstr(12, 29, current_qso.call); - curposition++; - move(12, 29 + curposition); - refreshp(); - - } - ichr = ESCAPE; - - return (ichr); -} diff --git a/src/calledit.h b/src/calledit.h deleted file mode 100644 index 867d279f..00000000 --- a/src/calledit.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Tlf - contest logging program for amateur radio operators - * Copyright (C) 2001-2002-2003 Rein Couperus - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#ifndef CALLEDIT_H -#define CALLEDIT_H - -void calledit(void); -int insert_char(int); - -#endif /* CALLEDIT_H */ diff --git a/src/callinput.c b/src/callinput.c index c9eac563..acdfc468 100644 --- a/src/callinput.c +++ b/src/callinput.c @@ -36,7 +36,6 @@ #include "audio.h" #include "autocq.h" #include "bandmap.h" -#include "calledit.h" #include "callinput.h" #include "change_rst.h" #include "changefreq.h" @@ -113,21 +112,52 @@ int callinput(void) { static struct grab_t grab = { .state = NONE }; - int cury, curx; int j, t, x = 0; - char instring[2] = { '\0', '\0' }; + int pos = strlen(current_qso.call); + int saved_len; + int use_part_pos = 0; // position after filling callsign by USEPARTIALS static int lastwindow; attron(modify_attr(COLOR_PAIR(NORMCOLOR))); - printcall(); /* print call input field */ + saved_len = strlen(current_qso.call); searchlog(); + if (strlen(current_qso.call) > saved_len) { // check for autofill + pos = strlen(current_qso.call); + use_part_pos = pos; + } + + printcall(); /* print call input field */ while (strlen(current_qso.call) <= MAX_CALL_LENGTH) { + // try to keep cursor position, update it only if call got shorter + if (pos > strlen(current_qso.call)) { + pos = strlen(current_qso.call); + } + + // release blocking of partials if call is empty + // (e.g. cleared to enter a different call) + if (strlen(current_qso.call) == 0) { + use_part_pos = 0; + block_part = false; + } + + // block use of partials if we edit a previously autofilled call + if (use_part_pos > 0 && pos < use_part_pos) { + block_part = true; + } + show_zones(bandinx); update_info_line(); + + saved_len = strlen(current_qso.call); searchlog(); + if (strlen(current_qso.call) > saved_len) { // check for autofill + pos = strlen(current_qso.call); + use_part_pos = pos; + } + printcall(); // note: calls refreshp() /* wait for next char pressed, but update time, cluster and TRX qrg */ @@ -147,6 +177,7 @@ int callinput(void) { && current_qso.call[0] != '\0') { freqstore = freq; fldigi_set_callfield = false; + pos = strlen(current_qso.call); // call has been just set, restart outer loop to update display break; } @@ -173,6 +204,7 @@ int callinput(void) { get_spot_on_qrg(grab.call, freq); if (strlen(grab.call) >= 3) { g_strlcpy(current_qso.call, grab.call, CALL_SIZE); + pos = strlen(current_qso.call); grab.state = REACHED; grab.spotfreq = freq; freqstore = 0; @@ -199,7 +231,7 @@ int callinput(void) { /* make sure that the wrefresh() inside getch() shows the cursor * in the input field */ - wmove(stdscr, 12, 29 + strlen(current_qso.call)); + wmove(stdscr, 12, 29 + pos); x = key_poll(); } @@ -309,19 +341,31 @@ int callinput(void) { continue; } - // , enter call edit when call field is not empty. + // , move cursor to the beginning of callsign field. case KEY_HOME: { - if ((*current_qso.call != '\0') && (ungetch(x) == OK)) { - calledit(); - } + pos = 0; + break; + } + + // Ctrl-E (^E) or , move to the end of callsign field + case CTRL_E: + case KEY_END: { + pos = strlen(current_qso.call); + break; + } + // + case KEY_DC: { + delete_char(current_qso.call, pos); break; } - // Left Arrow, enter call edit when call field is not empty, or band down. + // Left Arrow, move cursor left; or band down when call field is empty. case KEY_LEFT: { if (*current_qso.call != '\0') { - calledit(); + if (pos > 0) { + --pos; + } } else { handle_bandswitch(BAND_DOWN); } @@ -329,9 +373,15 @@ int callinput(void) { break; } - // Right Arrow, band up when call field is empty. + // Right Arrow, move cursor right; or band up when call field is empty. case KEY_RIGHT: { - handle_bandswitch(BAND_UP); + if (*current_qso.call != '\0') { + if (pos < strlen(current_qso.call)) { + ++pos; + } + } else { + handle_bandswitch(BAND_UP); + } break; } @@ -399,6 +449,7 @@ int callinput(void) { // XXX: Before digi_message, SSB mode sent CW here. - W8BSD send_standard_message(6); /* as with F7 */ cleanup(); + pos = 0; clear_display(); } break; @@ -487,11 +538,9 @@ int callinput(void) { // , remove character left of cursor, move cursor left one position. case KEY_BACKSPACE: { - if (*current_qso.call != '\0') { - getyx(stdscr, cury, curx); - mvaddstr(cury, curx - 1, " "); - move(cury, curx - 1); - current_qso.call[strlen(current_qso.call) - 1] = '\0'; + if (pos >= 1) { + --pos; + delete_char(current_qso.call, pos); } break; } @@ -636,6 +685,8 @@ int callinput(void) { restore_comment(); } + pos = strlen(current_qso.call); + clear_display(); break; @@ -647,6 +698,8 @@ int callinput(void) { restore_hiscall(); } + pos = strlen(current_qso.call); + break; // , clear call input or stop sending. @@ -655,6 +708,7 @@ int callinput(void) { if (!early_started) { /* if CW not started early drop call and start anew */ cleanup(); + pos = 0; clear_display(); } freqstore = 0; @@ -726,6 +780,7 @@ int callinput(void) { // Ctrl-G (^G), grab next DX spot from bandmap. case CTRL_G: { freq_t f = grab_next(); + pos = strlen(current_qso.call); if (f > 0.0) { grab.state = IN_PROGRESS; grab.spotfreq = f; @@ -739,6 +794,7 @@ int callinput(void) { // Alt-g (M-g), grab first spot matching call field chars. case ALT_G: { double f = grabspot(); + pos = strlen(current_qso.call); if (f > 0.0) { grab.state = IN_PROGRESS; grab.spotfreq = f; @@ -799,19 +855,19 @@ int callinput(void) { if (valid_call_char(x)) { x = g_ascii_toupper(x); - if (strlen(current_qso.call) < MAX_CALL_LENGTH) { - instring[0] = x; - instring[1] = '\0'; - addch(x); - strcat(current_qso.call, instring); - if (cqmode == CQ && cwstart > 0 && - trxmode == CWMODE && iscontest) { - /* early start keying after 'cwstart' characters but only - * if input field contains at least one nondigit */ - if (strlen(current_qso.call) == cwstart && !plain_number(current_qso.call)) { - x = autosend(); - } - } + pos = insert_char(x, current_qso.call, pos, MAX_CALL_LENGTH); + + /* early start keying after 'cwstart' characters, but only if + * - cursor is at the end of the input field + * - input field contains at least one nondigit character + * - we are in CW mode and CQing in a contest + */ + if (cwstart > 0 && pos == cwstart + && pos == strlen(current_qso.call) + && !plain_number(current_qso.call) + && trxmode == CWMODE && cqmode == CQ && iscontest) { + + x = autosend(); } freqstore = freq; @@ -882,7 +938,10 @@ int autosend() { timeout = (1.2 / speed) * cw_message_length(current_qso.call); x = -1; - while ((x != ESCAPE) && (x != '\n' && x != KEY_ENTER)) { + while (x != ESCAPE && x != '\n' && x != KEY_ENTER) { + + printcall(); + x = -1; while ((x == -1) && (g_timer_elapsed(timer, NULL) < timeout)) { @@ -918,32 +977,23 @@ int autosend() { continue; } - int len = strlen(current_qso.call); - if (len < 13 && valid_call_char(x)) { - char append[2]; - + if (len < MAX_CALL_LENGTH && valid_call_char(x)) { /* convert to upper case */ x = g_ascii_toupper(x); - /* insert into current_qso.call */ - current_qso.call[len] = x; - current_qso.call[len + 1] = '\0'; + char append[] = {x, 0}; - /* display it */ - printcall(); + /* insert into current_qso.call */ + strcat(current_qso.call, append); /* send it to cw */ - append[0] = x; - append[1] = '\0'; sendmessage(append); /* add char length to timeout */ timeout += (1.2 / speed) * getCWdots((char) x); - len = strlen(hiscall_sent); - hiscall_sent[len] = x; - hiscall_sent[len + 1] = '\0'; + strcat(hiscall_sent, append); } } diff --git a/src/globalvars.h b/src/globalvars.h index b6f8f4ac..d90681e5 100644 --- a/src/globalvars.h +++ b/src/globalvars.h @@ -198,7 +198,7 @@ extern int my_country_points; extern bool lowband_point_mult; extern bool landebug; extern int dupe; -extern int block_part; +extern bool block_part; extern int miniterm; extern int announcefilter; extern int nr_of_spots; diff --git a/src/log_to_disk.c b/src/log_to_disk.c index eec8a798..c8b24c1d 100644 --- a/src/log_to_disk.c +++ b/src/log_to_disk.c @@ -168,7 +168,7 @@ void log_to_disk(int from_lan) { set_outfreq(RESETRIT); } - block_part = 0; /* unblock use partials */ + block_part = false; /* unblock use partials */ pthread_mutex_unlock(&disk_mutex); } diff --git a/src/main.c b/src/main.c index a53f457f..143d4b1a 100644 --- a/src/main.c +++ b/src/main.c @@ -301,10 +301,7 @@ int qso_points; /**< number of points for last qso */ int qsos_per_band[NBANDS]; bool partials = false; /**< show partial calls */ bool use_part = false; /**< use automatically found partial call */ -int block_part = 0; /**< if 1 block the call autocompletion - for these QSO */ -char para_word[80] = "LODNCFS:3C\n"; /* longcw, cluster, search, DE, - contest, filter, speed, delay */ +bool block_part = false;/**< true: block call autocompletion for this QSO */ char lastmsg[1000] = ""; /*-------------------------------------keyer------------------------------*/ diff --git a/src/printcall.c b/src/printcall.c index 1c6abfad..eae615ff 100644 --- a/src/printcall.c +++ b/src/printcall.c @@ -42,7 +42,7 @@ void printcall(void) { mvaddstr(12, 29, spaces(MAX_CALL_LENGTH)); mvaddstr(12, 29, current_qso.call); if ((cqmode == CQ) && (cwstart > 0)) - mvchgat(12, 29 + cwstart, 12 - cwstart, + mvchgat(12, 29 + cwstart, MAX_CALL_LENGTH - cwstart, attrib | A_UNDERLINE, C_INPUT, NULL); refreshp(); diff --git a/src/searchlog.c b/src/searchlog.c index 41a07d6b..05560fa0 100644 --- a/src/searchlog.c +++ b/src/searchlog.c @@ -319,10 +319,12 @@ void handlePartials(void) { nr_suggested = displayPartials(suggested_call); /* If only one partial call found and USEPARTIALS set, - * use that call for auto-completion. Can be blocked by - * pressing tab in calledit() function + * use that call for auto-completion. It is blocked by + * subsequent editing the call. The blocking is released + * after logging the QSO or clearing the call field. */ if ((nr_suggested == 1) && use_part && !block_part + && !plain_number(current_qso.call) && strlen(suggested_call) > strlen(current_qso.call)) { strcpy(current_qso.call, suggested_call); diff --git a/src/utils.c b/src/utils.c index 74f2e85b..0b0fb7d8 100644 --- a/src/utils.c +++ b/src/utils.c @@ -269,3 +269,50 @@ double get_current_seconds() { return tv.tv_sec + tv.tv_usec / 1e6; } +/* + * insert a character into a string at given position + * if maximal length would be reached then no action is performed + * + * \returns the new position + */ +int insert_char(char c, char *buf, int pos, int maxlength) { + if (pos < 0) { + return 0; + } + int len = strlen(buf); + if (pos > len) { + return len; + } + if (len >= maxlength) { // buffer is full + return pos; + } + + GString *str = g_string_new_len(buf, maxlength + 1); + g_string_insert_c(str, pos, c); + char *s = g_string_free(str, FALSE); + strcpy(buf, s); + g_free(s); + + return pos + 1; +} + +/* + * delete the character from a string at given position + */ +void delete_char(char *buf, int pos) { + if (pos < 0) { + return; + } + int len = strlen(buf); + if (pos >= len) { + return; + } + + GString *str = g_string_new(buf); + g_string_erase(str, pos, 1); + char *s = g_string_free(str, FALSE); + strcpy(buf, s); + g_free(s); + +} + diff --git a/src/utils.h b/src/utils.h index 033ca4a1..4aec559d 100644 --- a/src/utils.h +++ b/src/utils.h @@ -28,5 +28,7 @@ char *find_available(char *filename); void get_partial_callsign(char *call1, char *call2, char *partial); bool plain_number(char *str); double get_current_seconds(); +int insert_char(char c, char *buf, int pos, int maxlength); +void delete_char(char *buf, int pos); #endif /* UTILS_H */ diff --git a/test/data.c b/test/data.c index cf37eb8c..c284d2ad 100644 --- a/test/data.c +++ b/test/data.c @@ -262,10 +262,7 @@ int qsos_per_band[NBANDS]; int dupe = 0; bool partials = false; /**< show partial calls */ bool use_part = false; /**< if 1 use automatically found partial call */ -int block_part = 0; /**< if 1 block the call autocompletion - for these QSO */ -char para_word[80] = - "LODNCFS:3C\n"; /* longcw, cluster, search, DE, contest, filter, speed, delay */ +bool block_part = false;/**< true: block call autocompletion for this QSO */ char lastmsg[1000] = ""; int scale_values[20] = { 40, 38, 36, 34, 32, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, diff --git a/test/test_utils.c b/test/test_utils.c index 600625cb..f31830dd 100644 --- a/test/test_utils.c +++ b/test/test_utils.c @@ -119,3 +119,56 @@ void test_get_partial_callsign(void **state) { } } +void test_insert_char_append(void **state) { + char buffer[20]; + buffer[0] = 0; + int pos = 0; + + pos = insert_char('A', buffer, pos, 3); + assert_int_equal(1, pos); + assert_string_equal("A", buffer); + + pos = insert_char('B', buffer, pos, 3); + assert_int_equal(2, pos); + assert_string_equal("AB", buffer); + + pos = insert_char('C', buffer, pos, 3); + assert_int_equal(3, pos); + assert_string_equal("ABC", buffer); + + pos = insert_char('D', buffer, pos, 3); // no action is expected + assert_int_equal(3, pos); + assert_string_equal("ABC", buffer); +} + +void test_insert_char_middle(void **state) { + char buffer[20]; + strcpy(buffer, "AB"); + int pos = 0; + + pos = insert_char('C', buffer, pos, 5); + assert_int_equal(1, pos); + assert_string_equal("CAB", buffer); + + pos = 2; // points to B + pos = insert_char('D', buffer, pos, 5); + assert_int_equal(3, pos); + assert_string_equal("CADB", buffer); +} + +void test_delete_char(void **state) { + char buffer[20]; + strcpy(buffer, "ABCDE"); + + delete_char(buffer, 0); + assert_string_equal("BCDE", buffer); + + delete_char(buffer, 1); + assert_string_equal("BDE", buffer); + + delete_char(buffer, 2); + assert_string_equal("BD", buffer); + + delete_char(buffer, 3); // no change, pos >= length + assert_string_equal("BD", buffer); +} diff --git a/tlf.1.in b/tlf.1.in index dd144d87..9c8f207a 100644 --- a/tlf.1.in +++ b/tlf.1.in @@ -962,16 +962,17 @@ Call input field is empty: change to next band lower in frequency or wrap to highest band if already on the lowest band. . .IP -Characters in the call input field: enter edit mode while moving the cursor -to the left onto the rightmost character. +Characters in the call input field: move the cursor to the left. . -In edit mode successive presses will move the cursor left until the first +Successive presses will move the cursor left until the first character is reached. . .TP .BR \(->\ (Right-Arrow) Change to next band higher in frequency or wrap to the lowest band if already on the highest band when call input field is empty. +.IP +Characters in the call input field: move the cursor to the right. . .TP .B F1 @@ -1212,6 +1213,8 @@ See .I @prefix@/share/doc/@PACKAGE@/Manual.md for more information. . +.IP +In the call input field: move the cursor after the call. .TP .B Ctrl-F Set frequency. @@ -3640,7 +3643,7 @@ The return value is a string specifying the minimal required .TP \fBdef setup() -> None:\fR . -Called whenever the QSO list is initialized (e.g. on inital loading of +Called whenever the QSO list is initialized (e.g. on initial loading of the log file or before rescoring it). It shall reset any internal structures related to QSOs. .