From ef0a495ee63a98acd8a6e503bd74eaec6be83906 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Deuc=D0=B5?= <shurd@sasktel.net>
Date: Wed, 17 Mar 2021 18:45:26 -0400
Subject: [PATCH] First cut at adding a comment field to listings.

The jury is still out on some details...
1) If you press ESC while editing the comment, it currently cancels
   the edit.  Would people expect ESC to exit the program instead?
2) The format/colours of the displayed comment... right now it's
   using the UIFC background colours, and not draing a border or
   window.
3) Just how long should the comment field be, and how should it be
   edited.
---
 src/syncterm/bbslist.c | 192 ++++++++++++++++++++++++++++++++++-------
 src/syncterm/bbslist.h |   1 +
 2 files changed, 163 insertions(+), 30 deletions(-)

diff --git a/src/syncterm/bbslist.c b/src/syncterm/bbslist.c
index 4fe5b12f30..e754d8a3c7 100644
--- a/src/syncterm/bbslist.c
+++ b/src/syncterm/bbslist.c
@@ -32,7 +32,7 @@ struct sort_order_info {
 #define SORT_ORDER_REVERSED     (1<<0)
 #define SORT_ORDER_STRING       (1<<1)
 
-struct sort_order_info sort_order[] = {
+static struct sort_order_info sort_order[] = {
      {
          NULL
         ,0
@@ -187,18 +187,16 @@ struct sort_order_info sort_order[] = {
 
 int sortorder[sizeof(sort_order)/sizeof(struct sort_order_info)];
 
-char *sort_orders[]={"Entry Name","Address","Connection Type","Port","Date Added","Date Last Connected"};
-
 char *screen_modes[]={     "Current", "80x25", "80x28", "80x30", "80x43", "80x50", "80x60", "132x37 (16:9)", "132x52 (5:4)", "132x25", "132x28", "132x30", "132x34", "132x43", "132x50", "132x60", "C64", "C128 (40col)", "C128 (80col)", "Atari", "Atari XEP80", "Custom", "EGA 80x25", NULL};
-char *screen_modes_enum[]={"Current", "80x25", "80x28", "80x30", "80x43", "80x50", "80x60", "132x37",        "132x52",       "132x25", "132x28", "132x30", "132x34", "132x43", "132x50", "132x60", "C64", "C128-40col",   "C128-80col",   "Atari", "Atari-XEP80", "Custom", "EGA80x25", NULL};
+static char *screen_modes_enum[]={"Current", "80x25", "80x28", "80x30", "80x43", "80x50", "80x60", "132x37",        "132x52",       "132x25", "132x28", "132x30", "132x34", "132x43", "132x50", "132x60", "C64", "C128-40col",   "C128-80col",   "Atari", "Atari-XEP80", "Custom", "EGA80x25", NULL};
 char *log_levels[]={"Emergency", "Alert", "Critical", "Error", "Warning", "Notice", "Info", "Debug", NULL};
-char *log_level_desc[]={"None", "Alerts", "Critical Errors", "Errors", "Warnings", "Notices", "Normal", "All (Debug)", NULL};
+static char *log_level_desc[]={"None", "Alerts", "Critical Errors", "Errors", "Warnings", "Notices", "Normal", "All (Debug)", NULL};
 
 char *rate_names[]={"300", "600", "1200", "2400", "4800", "9600", "19200", "38400", "57600", "76800", "115200", "Current", NULL};
 int rates[]={300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 0};
 
-const char *fc_names[] = {"RTS/CTS", "XON/XOFF", "RTS/CTS and XON/XOFF", "None", NULL};
-const char *fc_enum[] = {"RTSCTS", "XONXOFF", "RTSCTS_XONXOFF", "None", NULL};
+static char *fc_names[] = {"RTS/CTS", "XON/XOFF", "RTS/CTS and XON/XOFF", "None", NULL};
+static char *fc_enum[] = {"RTSCTS", "XONXOFF", "RTSCTS_XONXOFF", "None", NULL};
 
 char *music_names[]={"ESC [ | only", "BANSI Style", "All ANSI Music enabled", NULL};
 char music_helpbuf[] = "`ANSI Music Setup`\n\n"
@@ -222,22 +220,22 @@ char music_helpbuf[] = "`ANSI Music Setup`\n\n"
                         "SyncTERM has now defined a third ANSI music sequence which *IS* legal\n"
                         "according to the ANSI spec.  Specifically ESC[|.";
 
-char *address_families[]={"PerDNS", "IPv4", "IPv6", NULL};
-char *address_family_names[]={"As per DNS", "IPv4 only", "IPv6 only", NULL};
+static char *address_families[]={"PerDNS", "IPv4", "IPv6", NULL};
+static char *address_family_names[]={"As per DNS", "IPv4 only", "IPv6 only", NULL};
 
-char *address_family_help = "`Address Family`\n\n"
+static char *address_family_help = "`Address Family`\n\n"
                             "Select the address family to resolve\n\n"
                             "`As per DNS`..: Uses what is in the DNS system\n"
                             "`IPv4 only`...: Only uses IPv4 addresses.\n"
                             "`IPv6 only`...: Only uses IPv6 addresses.\n";
 
-char *address_help=
+static char *address_help=
                     "`Address`, `Phone Number`, `Serial Port`, or `Command`\n\n"
                     "Enter the hostname, IP address, phone number, or serial port device of\n"
                     "the system to connect to. Example: `nix.synchro.net`\n\n"
                     "In the case of the Shell type, enter the command to run.\n"
                     "Shell types are only functional under *nix\n";
-char *conn_type_help=           "`Connection Type`\n\n"
+static char *conn_type_help=           "`Connection Type`\n\n"
                                 "Select the type of connection you wish to make:\n\n"
                                 "`RLogin`...........: Auto-login with RLogin protocol\n"
                                 "`RLogin Reversed`..: RLogin using reversed username/password parameters\n"
@@ -250,6 +248,8 @@ char *conn_type_help=           "`Connection Type`\n\n"
                                 "`MBBS GHost`.......: Communicate using the Major BBS 'GHost' protocol\n";
                                 ;
 
+static char *YesNo[3]={"Yes","No",""};
+
 ini_style_t ini_style = {
     /* key_len */ 15,
     /* key_prefix */ "\t",
@@ -701,6 +701,7 @@ void read_item(str_list_t listfile, struct bbslist *entry, char *bbsname, int id
     entry->music=iniGetInteger(section,NULL,"ANSIMusic",CTERM_MUSIC_BANSI);
     entry->address_family=iniGetEnum(section,NULL,"AddressFamily",address_families, ADDRESS_FAMILY_UNSPEC);
     iniGetString(section,NULL,"Font","Codepage 437 English",entry->font);
+    iniGetString(section,NULL,"Comment","",entry->comment);
     entry->type=type;
     entry->id=id;
 
@@ -920,7 +921,6 @@ int edit_list(struct bbslist **list, struct bbslist *item,char *listpath,int isd
     str_list_t  inifile;
     char    tmp[LIST_NAME_MAX+1];
     char    *itemname;
-    char    *YesNo[3]={"Yes","No",""};
 
     for(i=0;i<sizeof(opt)/sizeof(opt[0]);i++)
         opts[i]=opt[i];
@@ -1468,6 +1468,7 @@ void add_bbs(char *listpath, struct bbslist *bbs)
     iniSetString(&inifile,bbs->name,"Font",bbs->font,&ini_style);
     iniSetBool(&inifile,bbs->name,"HidePopups",bbs->hidepopups,&ini_style);
     iniSetBool(&inifile,bbs->name,"RIP",bbs->rip,&ini_style);
+    iniSetString(&inifile,bbs->name,"Comment",bbs->comment,&ini_style);
     if((listfile=fopen(listpath,"w"))!=NULL) {
         iniWriteFile(listfile,inifile);
         fclose(listfile);
@@ -1960,6 +1961,111 @@ void load_bbslist(struct bbslist **list, size_t listsize, struct bbslist *defaul
         free(current);
 }
 
+/*
+ * Note that any time it's drawn, it's inactive...
+ */
+static void draw_comment(struct bbslist *list)
+{
+	int lpad;
+	int rpad;
+	int clen;
+	int remain;
+	char *comment;
+
+	if (list == NULL)
+		comment = "";
+	else
+		comment = list->comment;
+	gotoxy(1, uifc.scrn_len);
+	textattr(uifc.lclr|(uifc.cclr<<4));
+	// Calculator how to centre.
+	clen = strlen(comment);
+	if (clen > uifc.scrn_width - 4) {
+		lpad = 0;
+		rpad = 0;
+	}
+	else {
+		remain = uifc.scrn_width - 4 - clen;
+		rpad = remain / 2 + (remain % 2);
+		lpad = remain - rpad;
+	}
+	cprintf("  %*s%-.*s%*s  ", lpad, "", uifc.scrn_width - 4, comment, rpad, "");
+}
+
+/*
+ * Return value indicates if focus should return to list (true) or move
+ * to settings (false)
+ * 
+ * TODO: ESC in edit box doesn't exit program... good or bad?
+ */
+static bool edit_comment(struct bbslist *list, char *listpath)
+{
+	FILE *listfile;
+	str_list_t inifile = NULL;
+	int ch;
+	bool ret = false;
+	char *old;
+	int i;
+
+	if (list == NULL)
+		goto done;
+	if (safe_mode)
+		goto done;
+	// Open with write permissions so it fails if you can't edit.
+	if ((listfile = fopen(listpath,"r+")) != NULL) {
+		inifile = iniReadFile(listfile);
+		fclose(listfile);
+	}
+	else {
+		goto done;
+	}
+
+	old = strdup(list->comment);
+	if (!old)
+		goto done;
+	textattr(uifc.lclr|(uifc.bclr<<4));
+	gotoxy(1, uifc.scrn_len);
+	clreol();
+	uifc.getstrxy(3, uifc.scrn_len, uifc.scrn_width - 4, list->comment, sizeof(list->comment), K_LINE|K_EDIT|K_NOCRLF|K_TABEXIT|K_MOUSEEXIT|K_TABEXIT, &ch);
+	switch(ch) {
+		case '\x1b':
+			strcpy(list->comment, old);
+			ret = true;
+			goto done;
+		case '\t':
+			ret = false;
+			break;
+		default:
+			ret = true;
+			break;
+	}
+
+	if (strcmp(old, list->comment)) {
+		iniSetString(&inifile, list->name, "Comment", list->comment, &ini_style);
+		if (list->type==SYSTEM_BBSLIST) {
+			uifc.helpbuf = "`Copy from system directory`\n\n"
+			               "This entry was loaded from the system directory.  In order to edit it, it\n"
+			               "must be copied into your personal directory.\n";
+			i = 0;
+			if (uifc.list(WIN_MID|WIN_SAV, 0, 0, 0, &i, NULL, "Copy from system directory?", YesNo) != 0)
+				goto done;
+			list->type = USER_BBSLIST;
+			add_bbs(listpath, list);
+		}
+	}
+
+done:
+	if (inifile != NULL) {
+		if ((listfile = fopen(listpath,"w")) != NULL) {
+			iniWriteFile(listfile, inifile);
+			fclose(listfile);
+		}
+		strListFree(&inifile);
+	}
+	draw_comment(list);
+	return ret;
+}
+
 /*
  * Displays the BBS list and allows edits to user BBS list
  * Mode is one of BBSLIST_SELECT or BBSLIST_EDIT
@@ -1976,7 +2082,6 @@ struct bbslist *show_bbslist(char *current, int connected)
     int     val;
     int     listcount=0;
     char    str[128];
-    char    *YesNo[3]={"Yes","No",""};
     char    title[1024];
     char    *p;
     char    addy[LIST_ADDR_MAX+1];
@@ -2044,7 +2149,7 @@ struct bbslist *show_bbslist(char *current, int connected)
                                     "~ CTRL-E ~ to edit the selected entry\n"
                                     "~ CTRL-S ~ to modify the sort order\n"
                                     "~ ENTER ~ to connect to the selected entry";
-                else
+                else {
                     uifc.helpbuf=   "`SyncTERM Directory`\n\n"
                                     "Commands:\n\n"
                                     "~ CTRL-D ~ Quick-connect to a URL\n"
@@ -2061,6 +2166,7 @@ struct bbslist *show_bbslist(char *current, int connected)
                             "`UIFC List Keys`\n\n"
                                     "~ CTRL-F ~ find text in current menu options\n"
                                     "~ CTRL-G ~ repeat last search\n";
+		}
                 if(opt != oldopt) {
                     if(list[opt]!=NULL && list[opt]->name[0]) {
                         sprintf(title, "%s - %s (%d calls / Last: %s", syncterm_version, (char *)(list[opt]), list[opt]->calls, list[opt]->connected?ctime(&list[opt]->connected):"Never\n");
@@ -2073,28 +2179,32 @@ struct bbslist *show_bbslist(char *current, int connected)
                     settitle(title);
                 }
                 oldopt=opt;
+                uifc.list_height = listcount + 5;
+                if (uifc.list_height > (uifc.scrn_len - 4))
+			uifc.list_height = uifc.scrn_len - 4;
                 val=uifc.list((listcount<MAX_OPTS?WIN_XTR:0)
                     |WIN_ACT|WIN_INSACT|WIN_DELACT|WIN_UNGETMOUSE|WIN_SAV|WIN_ESC
-                    |WIN_T2B|WIN_INS|WIN_DEL|WIN_EDIT|WIN_EXTKEYS|WIN_DYN
-                    ,0,0,0,&opt,&bar,list_title,(char **)list);
+                    |WIN_INS|WIN_DEL|WIN_EDIT|WIN_EXTKEYS|WIN_DYN|WIN_FIXEDHEIGHT
+                    ,0,(uifc.scrn_len-(uifc.list_height)+1)/2-4,0,&opt,&bar,list_title,(char **)list);
                 if(val==listcount)
                     val=listcount|MSK_INS;
                 if(val==-7) { /* CTRL-E */
                     uifc.list((listcount<MAX_OPTS?WIN_XTR:0)
                         |WIN_ACT|WIN_INSACT|WIN_DELACT|WIN_SAV|WIN_ESC
-                        |WIN_T2B|WIN_INS|WIN_DEL|WIN_EDIT|WIN_EXTKEYS|WIN_DYN
-                        |WIN_SEL
-                        ,0,0,0,&opt,&bar,list_title,(char **)list);
+                        |WIN_INS|WIN_DEL|WIN_EDIT|WIN_EXTKEYS|WIN_DYN
+                        |WIN_SEL|WIN_FIXEDHEIGHT
+                        ,0,(uifc.scrn_len-(uifc.list_height)+1)/2-4,0,&opt,&bar,list_title,(char **)list);
                     val=opt|MSK_EDIT;
                 }
+                draw_comment(list[opt]);
                 if(val<0) {
                     switch(val) {
                         case -2-0x13:   /* CTRL-S - Sort */
                             uifc.list((listcount<MAX_OPTS?WIN_XTR:0)
                                 |WIN_ACT|WIN_INSACT|WIN_DELACT|WIN_SAV|WIN_ESC
-                                |WIN_T2B|WIN_INS|WIN_DEL|WIN_EDIT|WIN_EXTKEYS|WIN_DYN
-                                |WIN_SEL
-                                ,0,0,0,&opt,&bar,list_title,(char **)list);
+                                |WIN_INS|WIN_DEL|WIN_EDIT|WIN_EXTKEYS|WIN_DYN
+                                |WIN_SEL|WIN_FIXEDHEIGHT
+                                ,0,(uifc.scrn_len-(uifc.list_height)+1)/2-4,0,&opt,&bar,list_title,(char **)list);
                             edit_sorting(list,&listcount, &opt, &bar, list[opt]?list[opt]->name:NULL);
                             break;
                         case -2-0x3000: /* ALT-B - Scrollback */
@@ -2104,18 +2214,30 @@ struct bbslist *show_bbslist(char *current, int connected)
                                     ,0,0,0,&sopt,&sbar,"SyncTERM Settings",settings_menu);
                             }
                             break;
+                        case -11:       /* TAB */
+                            if (val == -11) {
+                                uifc.list((listcount<MAX_OPTS?WIN_XTR:0)
+                                    |WIN_ACT|WIN_INSACT|WIN_DELACT|WIN_SAV|WIN_ESC
+                                    |WIN_INS|WIN_DEL|WIN_EDIT|WIN_EXTKEYS|WIN_DYN
+                                    |WIN_SEL|WIN_FIXEDHEIGHT
+                                    ,0,(uifc.scrn_len-(uifc.list_height)+1)/2-4,0,&opt,&bar,list_title,(char **)list);
+				if (edit_comment(list[opt], settings.list_path))
+				    break;
+                                at_settings=!at_settings;
+                                break;
+                            }
+                            /* Fall-through */
                         case -2-CIO_KEY_MOUSE:  /* Clicked outside of window... */
                             getmouse(&mevent);
                             /* Fall-through */
                         case -2-0x0f00: /* Backtab */
                         case -2-0x4b00: /* Left Arrow */
                         case -2-0x4d00: /* Right Arrow */
-                        case -11:       /* TAB */
                             uifc.list((listcount<MAX_OPTS?WIN_XTR:0)
                                 |WIN_ACT|WIN_INSACT|WIN_DELACT|WIN_SAV|WIN_ESC
-                                |WIN_T2B|WIN_INS|WIN_DEL|WIN_EDIT|WIN_EXTKEYS|WIN_DYN
-                                |WIN_SEL
-                                ,0,0,0,&opt,&bar,list_title,(char **)list);
+                                |WIN_INS|WIN_DEL|WIN_EDIT|WIN_EXTKEYS|WIN_DYN
+                                |WIN_SEL|WIN_FIXEDHEIGHT
+                                ,0,(uifc.scrn_len-(uifc.list_height)+1)/2-4,0,&opt,&bar,list_title,(char **)list);
                             at_settings=!at_settings;
                             break;
                         case -6:        /* CTRL-D */
@@ -2133,9 +2255,9 @@ struct bbslist *show_bbslist(char *current, int connected)
                                                 "[(rlogin|telnet|ssh)://][user[:password]@]domainname[:port]\n";
                                 uifc.list((listcount<MAX_OPTS?WIN_XTR:0)
                                     |WIN_ACT|WIN_INSACT|WIN_DELACT|WIN_SAV|WIN_ESC
-                                    |WIN_T2B|WIN_INS|WIN_DEL|WIN_EDIT|WIN_EXTKEYS|WIN_DYN
-                                    |WIN_SEL
-                                    ,0,0,0,&opt,&bar,list_title,(char **)list);
+                                    |WIN_INS|WIN_DEL|WIN_EDIT|WIN_EXTKEYS|WIN_DYN
+                                    |WIN_SEL|WIN_FIXEDHEIGHT
+                                    ,0,(uifc.scrn_len-(uifc.list_height)+1)/2-4,0,&opt,&bar,list_title,(char **)list);
                                 uifc.input(WIN_MID|WIN_SAV,0,0,"Address",addy,LIST_ADDR_MAX,0);
                                 memcpy(&retlist, &defaults, sizeof(defaults));
                                 if(uifc.changes) {
@@ -2364,6 +2486,16 @@ struct bbslist *show_bbslist(char *current, int connected)
                         getmouse(&mevent);
                         /* Fall-through */
                     case -2-0x0f00: /* Backtab */
+			if (val == -2-0x0f00) {
+                                uifc.list((listcount<MAX_OPTS?WIN_XTR:0)
+                                    |WIN_ACT|WIN_INSACT|WIN_DELACT|WIN_SAV|WIN_ESC
+                                    |WIN_T2B|WIN_INS|WIN_DEL|WIN_EDIT|WIN_EXTKEYS|WIN_DYN
+                                    |WIN_SEL|WIN_INACT
+                                    ,0,0,0,&opt,&bar,list_title,(char **)list);
+				if (!edit_comment(list[opt], settings.list_path))
+					break;
+			}
+			/* Fall-through */
                     case -2-0x4b00: /* Left Arrow */
                     case -2-0x4d00: /* Right Arrow */
                     case -11:       /* TAB */
diff --git a/src/syncterm/bbslist.h b/src/syncterm/bbslist.h
index 05fc9c3d83..3adc488afc 100644
--- a/src/syncterm/bbslist.h
+++ b/src/syncterm/bbslist.h
@@ -96,6 +96,7 @@ struct bbslist {
     char            ghost_program[9]; /* GHost program can only be 8 chars max. */
     int             rip;
     int             flow_control;
+    char            comment[1024];
 };
 
 extern char *music_names[];
-- 
GitLab